Example #1
0
 public ConvolutionRowOperation(
     Rectangle bounds,
     Buffer2D <TPixel> targetPixels,
     Buffer2D <TPixel> sourcePixels,
     KernelSamplingMap map,
     DenseMatrix <float> kernelMatrix,
     Configuration configuration,
     bool preserveAlpha)
 {
     this.bounds        = bounds;
     this.targetPixels  = targetPixels;
     this.sourcePixels  = sourcePixels;
     this.map           = map;
     this.kernelMatrix  = kernelMatrix;
     this.configuration = configuration;
     this.preserveAlpha = preserveAlpha;
 }
        /// <inheritdoc/>
        protected override void OnFrameApply(ImageFrame <TPixel> source)
        {
            using Buffer2D <TPixel> firstPassPixels = this.Configuration.MemoryAllocator.Allocate2D <TPixel>(source.Size());

            var interest = Rectangle.Intersect(this.SourceRectangle, source.Bounds());

            // We use a rectangle 2x the interest width to allocate a buffer big enough
            // for source and target bulk pixel conversion.
            var operationBounds = new Rectangle(interest.X, interest.Y, interest.Width * 2, interest.Height);

            // We can create a single sampling map with the size as if we were using the non separated 2D kernel
            // the two 1D kernels represent, and reuse it across both convolution steps, like in the bokeh blur.
            using var mapXY = new KernelSamplingMap(this.Configuration.MemoryAllocator);

            mapXY.BuildSamplingOffsetMap(this.Kernel.Length, this.Kernel.Length, interest);

            // Horizontal convolution
            var horizontalOperation = new HorizontalConvolutionRowOperation(
                interest,
                firstPassPixels,
                source.PixelBuffer,
                mapXY,
                this.Kernel,
                this.Configuration,
                this.PreserveAlpha);

            ParallelRowIterator.IterateRows <HorizontalConvolutionRowOperation, Vector4>(
                this.Configuration,
                operationBounds,
                in horizontalOperation);

            // Vertical convolution
            var verticalOperation = new VerticalConvolutionRowOperation(
                interest,
                source.PixelBuffer,
                firstPassPixels,
                mapXY,
                this.Kernel,
                this.Configuration,
                this.PreserveAlpha);

            ParallelRowIterator.IterateRows <VerticalConvolutionRowOperation, Vector4>(
                this.Configuration,
                operationBounds,
                in verticalOperation);
        }
 public SecondPassConvolutionRowOperation(
     Rectangle bounds,
     Buffer2D <Vector4> targetValues,
     Buffer2D <ComplexVector4> sourceValues,
     KernelSamplingMap map,
     Complex64[] kernel,
     float z,
     float w)
 {
     this.bounds       = bounds;
     this.targetValues = targetValues;
     this.sourceValues = sourceValues;
     this.map          = map;
     this.kernel       = kernel;
     this.z            = z;
     this.w            = w;
 }
 public HorizontalConvolutionRowOperation(
     Rectangle bounds,
     Buffer2D <TPixel> targetPixels,
     Buffer2D <TPixel> sourcePixels,
     KernelSamplingMap map,
     float[] kernel,
     Configuration configuration,
     bool preserveAlpha)
 {
     this.bounds        = bounds;
     this.targetPixels  = targetPixels;
     this.sourcePixels  = sourcePixels;
     this.map           = map;
     this.kernel        = kernel;
     this.configuration = configuration;
     this.preserveAlpha = preserveAlpha;
 }
        /// <summary>
        /// Computes and aggregates the convolution for each complex kernel component in the processor.
        /// </summary>
        /// <param name="source">The source image. Cannot be null.</param>
        /// <param name="sourceRectangle">The <see cref="Rectangle" /> structure that specifies the portion of the image object to draw.</param>
        /// <param name="configuration">The configuration.</param>
        /// <param name="processingBuffer">The buffer with the raw pixel data to use to aggregate the results of each convolution.</param>
        private void OnFrameApplyCore(
            ImageFrame <TPixel> source,
            Rectangle sourceRectangle,
            Configuration configuration,
            Buffer2D <Vector4> processingBuffer)
        {
            // Allocate the buffer with the intermediate convolution results
            using Buffer2D <ComplexVector4> firstPassBuffer = configuration.MemoryAllocator.Allocate2D <ComplexVector4>(source.Size());

            // Unlike in the standard 2 pass convolution processor, we use a rectangle of 1x the interest width
            // to speedup the actual convolution, by applying bulk pixel conversion and clamping calculation.
            // The second half of the buffer will just target the temporary buffer of complex pixel values.
            // This is needed because the bokeh blur operates as TPixel -> complex -> TPixel, so we cannot
            // convert back to standard pixels after each separate 1D convolution pass. Like in the gaussian
            // blur though, we preallocate and compute the kernel sampling maps before processing each complex
            // component, to avoid recomputing the same sampling map once per convolution pass. Since we are
            // doing two 1D convolutions with the same kernel, we can use a single kernel sampling map as if
            // we were using a 2D kernel with each dimension being the same as the length of our kernel, and
            // use the two sampling offset spans resulting from this same map. This saves some extra work.
            using var mapXY = new KernelSamplingMap(configuration.MemoryAllocator);

            mapXY.BuildSamplingOffsetMap(this.kernelSize, this.kernelSize, sourceRectangle);

            ref Complex64[] baseRef   = ref MemoryMarshal.GetReference(this.kernels.AsSpan());