public PixelWorker(IList <TColorStorage> sourceImage, int sourceX, int sourceY, int sourceWidth, int sourceHeight, OutOfBoundsUtils.OutOfBoundsHandler sourceXWrapper, OutOfBoundsUtils.OutOfBoundsHandler sourceYWrapper, IList <TColorStorage> targetImage, int targetX, int targetY, int targetWidth)
        {
            Contract.Requires(sourceX >= 0 && sourceX < sourceWidth && sourceY >= 0 && sourceY < sourceHeight);
            this._sourceImage  = sourceImage;
            this._sourceX      = sourceX;
            this._sourceY      = sourceY;
            this._sourceWidth  = sourceWidth;
            this._sourceHeight = sourceHeight;

            // we can safely calculate this offset, because we assume that the central source pixel is never out of bounds
            this._sourceOffset = sourceWidth * sourceY + sourceX;

            // we only check pixels in a row or column once for over-/underflow, to avoid calling the wrappers over and over again
            this._sourceXWrapper = sourceXWrapper;
            this._sourceYWrapper = sourceYWrapper;

            // we calculate a delta offset for pixels around the center and store these independent X from Y
            this._sourceOffsetM2X = this._CalculateOffsetM2X;
            this._sourceOffsetM1X = this._CalculateOffsetM1X;
            this._sourceOffsetP1X = this._CalculateOffsetP1X;
            this._sourceOffsetP2X = this._CalculateOffsetP2X;
            this._sourceOffsetM2Y = this._CalculateOffsetM2Y;
            this._sourceOffsetM1Y = this._CalculateOffsetM1Y;
            this._sourceOffsetP1Y = this._CalculateOffsetP1Y;
            this._sourceOffsetP2Y = this._CalculateOffsetP2Y;

            this._targetImage  = targetImage;
            this._targetOffset = targetWidth * targetY + targetX;

            // pre-calculating the row offset for target image, because they surely get used
            this._targetOffsetM1Y = targetWidth * -1;                 // for nx0 filters
            this._targetOffsetP1Y = targetWidth;                      // for nx2 filters
            this._targetOffsetP2Y = targetWidth << 1;                 // for nx3 filters
            this._targetOffsetP3Y = targetWidth * 3;                  // for nx4 filters
        }
Пример #2
0
        /// <summary>
        /// Scale image with a compact support interpolation kernel
        /// This is a generic linear interpolation routine to scale an image using any
        /// compactly supported interpolation kernel.  The kernel is applied separably
        /// along both dimensions.  Half-sample even symmetric extension is used to
        /// handle the boundaries.
        ///
        /// The interpolation is computed so that Dest[m + DestWidth*n] is the
        /// interpolation of Src at sampling location
        ///     (XStart + m*XStep, YStart + n*YStep)
        /// for m = 0, ..., DestWidth - 1, n = 0, ..., DestHeight - 1, where the
        /// pixels of Src are located at the integers.
        ///
        /// The implementation follows the approach taken in ffmpeg's swscale library.
        /// First a "scanline filter" is constructed, a sparse matrix such that
        /// multiplying with a row of the input image produces an interpolated row in
        /// the output image.  Similarly a second matrix is constructed for
        /// interpolating columns.  The interpolation itself is then essentially two
        /// sparse matrix times dense matrix multiplies.
        /// </summary>
        /// <param name="destination">pointer to memory for holding the interpolated image</param>
        /// <param name="xStart">leftmost sampling location (in input coordinates)</param>
        /// <param name="xStep">the length between successive samples (in input coordinates)</param>
        /// <param name="yStart">uppermost sampling location (in input coordinates)</param>
        /// <param name="yStep">the length between successive samples (in input coordinates)</param>
        /// <param name="kernel">interpolation kernel function to use</param>
        /// <param name="kernelRadius">kernel support radius</param>
        /// <param name="kernelNormalize">if set to <c>true</c> filter rows are normalized to sum to 1</param>
        /// <param name="horizontalOutOfBoundsHandler">The horizontal out of bounds handler.</param>
        /// <param name="verticalOutOfBoundsHandler">The vertical out of bounds handler.</param>
        private void _LinScale2D(FloatImage destination, float xStart, float xStep, float yStart, float yStep, Kernels.FixedRadiusKernelMethod kernel, float kernelRadius, bool kernelNormalize, OutOfBoundsUtils.OutOfBoundsHandler horizontalOutOfBoundsHandler, OutOfBoundsUtils.OutOfBoundsHandler verticalOutOfBoundsHandler)
        {
            Contract.Requires(destination != null);
            Contract.Requires(kernel != null);
            Contract.Requires(!(kernelRadius < 0));

            var srcWidth  = this.Width;
            var srcHeight = this.Height;

            var destHeight = destination.Height;
            var destWidth  = destination.Width;

            var buffer  = new float[srcWidth * destHeight];
            var hFilter = _MakeScaleScanFilter(destWidth, xStart, xStep, srcWidth, kernel, kernelRadius, kernelNormalize, horizontalOutOfBoundsHandler);
            var vFilter = _MakeScaleScanFilter(destHeight, yStart, yStep, srcHeight, kernel, kernelRadius, kernelNormalize, verticalOutOfBoundsHandler);

            foreach (var plane in new[] {
                Tuple.Create(this._redPlane, destination._redPlane),
                Tuple.Create(this._greenPlane, destination._greenPlane),
                Tuple.Create(this._bluePlane, destination._bluePlane),
                Tuple.Create(this._alphaPlane, destination._alphaPlane),
            })
            {
                for (var x = 0; x < srcWidth; x++)
                {
                    _ScaleScan(buffer, x, srcWidth, destHeight, plane.Item1, x, srcWidth, vFilter);
                }

                for (var y = 0; y < destHeight; y++)
                {
                    _ScaleScan(plane.Item2, y * destWidth, 1, destWidth, buffer, y * srcWidth, 1, hFilter);
                }
            }
        }
Пример #3
0
        /// <summary>
        /// Create scanline interpolation filter to be applied with ScaleScan
        /// This routine creates a scalescanfilter for 1-D interpolation of samples at
        /// the locations
        ///    XStart + n*XStep, n = 0, ..., DestWidth - 1,
        /// where the pixels of the source are logically located at the integers.  Half-
        /// sample even symmetric extension is used to handle the boundaries.
        /// </summary>
        /// <param name="destWidth">width after interpolation</param>
        /// <param name="xstart">leftmost sampling location (in input coordinates)</param>
        /// <param name="xstep">the length between successive samples (in input coordinates)</param>
        /// <param name="srcWidth">width of the input</param>
        /// <param name="kernel">interpolation kernel function to use</param>
        /// <param name="kernelRadius">kernel support radius</param>
        /// <param name="kernelNormalize">if set to <c>true</c> filter rows are normalized to sum to 1</param>
        /// <param name="boundary">boundary handling</param>
        /// <returns></returns>
        ///
        ///
        private static ScaleScanFilter _MakeScaleScanFilter(int destWidth, float xstart, float xstep, int srcWidth, Kernels.FixedRadiusKernelMethod kernel, float kernelRadius, bool kernelNormalize, OutOfBoundsUtils.OutOfBoundsHandler boundary)
        {
            Contract.Requires(kernel != null);

            var kernelWidth = (int)Math.Ceiling(2 * kernelRadius);
            var filterWidth = (srcWidth < kernelWidth) ? srcWidth : kernelWidth;
            var filterCoeff = new float[filterWidth * destWidth];
            var filterPos   = new short[destWidth];

            var result = new ScaleScanFilter {
                Coeff = filterCoeff,
                Pos   = filterPos,
                Width = filterWidth
            };

            var maxPos = srcWidth - filterWidth;

            var coeffIndex = 0;

            for (var destX = 0; destX < destWidth; destX++)
            {
                var srcX = xstart + xstep * destX;
                var pos  = (int)Math.Ceiling(srcX - kernelRadius);

                if (pos < 0 || maxPos < pos)
                {
                    filterPos[destX] = (short)(pos <0 ? 0 : pos> maxPos ? maxPos : pos);

                    for (var n = 0; n < filterWidth; n++)
                    {
                        filterCoeff[coeffIndex + n] = 0;
                    }

                    for (var n = 0; n < kernelWidth; n++)
                    {
                        var index = pos + n;
                        if (index < 0 || index >= srcWidth)
                        {
                            index = boundary(index, srcWidth, index < 0);
                        }

                        filterCoeff[coeffIndex + index - filterPos[destX]]
                            += (float)kernel(srcX - index);
                    }
                }
                else
                {
                    filterPos[destX] = (short)pos;

                    for (var n = 0; n < filterWidth; n++)
                    {
                        filterCoeff[coeffIndex + n] = (float)kernel(srcX - (pos + n));
                    }
                }

                if (kernelNormalize) /* Normalize */
                {
                    var sum = 0f;

                    for (var n = 0; n < filterWidth; n++)
                    {
                        sum += filterCoeff[coeffIndex + n];
                    }

                    for (var n = 0; n < filterWidth; n++)
                    {
                        filterCoeff[coeffIndex + n] /= sum;
                    }
                }

                coeffIndex += filterWidth;
            }

            return(result);
        }
Пример #4
0
        private static float _GetValueFromPlane(float[] plane, int x, int y, int width, int height, OutOfBoundsUtils.OutOfBoundsHandler horizontalOutOfBoundsHandler, OutOfBoundsUtils.OutOfBoundsHandler verticalOutOfBoundsHandler)
        {
            if (x < 0 || x >= width)
            {
                x = horizontalOutOfBoundsHandler(x, width, x < 0);
            }

            if (y < 0 || y >= height)
            {
                y = verticalOutOfBoundsHandler(y, height, y < 0);
            }

            return(plane[y * width + x]);
        }