/* * This region is a C++ -> C# conversion from the original sources of Pascal Getreuer <*****@*****.**> */ /// <summary> /// Resizes the image to the specified dimensions using the given kernel type. /// </summary> /// <param name="destWidth">Width of the destination.</param> /// <param name="destHeight">Height of the destination.</param> /// <param name="interpolationInfo">The interpolation method info.</param> /// <param name="centeredGrid">if set to <c>true</c> using a centered grid; otherwise, using top-left aligned.</param> /// <returns> /// The resized image /// </returns> private FloatImage _Resize(int destWidth, int destHeight, Kernels.FixedRadiusKernelInfo interpolationInfo, bool centeredGrid) { var xScale = (float)destWidth / this.Width; var yScale = (float)destHeight / this.Height; var xStep = 1 / xScale; var yStep = 1 / yScale; float xStart, yStart; if (centeredGrid) { xStart = (xStep - 1) / 2; yStart = (yStep - 1) / 2; } else { xStart = yStart = 0; } var result = new FloatImage(destWidth, destHeight, this._horizontalOutOfBoundsMode, this._verticalOutOfBoundsMode); // prefilter image if necessary if (interpolationInfo.PrefilterAlpha != null && interpolationInfo.PrefilterAlpha.Length > 0) { this._PrefilterImage(interpolationInfo.PrefilterAlpha, interpolationInfo.PrefilterScale); } // resample this._LinScale2D( result, xStart, xStep, yStart, yStep, interpolationInfo.Kernel, interpolationInfo.KernelRadius, interpolationInfo.KernelNormalize, OutOfBoundsUtils.GetHandlerOrCrash(this.HorizontalOutOfBoundsMode), OutOfBoundsUtils.GetHandlerOrCrash(this.VerticalOutOfBoundsMode) ); return(result); }
/// <summary> /// Converts a given image into a floating point one. /// </summary> /// <param name="image">The image.</param> /// <param name="filterRegion">The filter region.</param> /// <returns></returns> public static FloatImage FromImage(cImage image, Rectangle?filterRegion) { Contract.Requires(image != null); var startX = filterRegion == null ? 0 : Math.Max(0, filterRegion.Value.Left); var startY = filterRegion == null ? 0 : Math.Max(0, filterRegion.Value.Top); var endX = filterRegion == null ? image.Width : Math.Min(image.Width, filterRegion.Value.Right); var endY = filterRegion == null ? image.Height : Math.Min(image.Height, filterRegion.Value.Bottom); var width = endX - startX; var height = endY - startY; var result = new FloatImage(width, height, image.HorizontalOutOfBoundsMode, image.VerticalOutOfBoundsMode); // copy image data Parallel.ForEach( Partitioner.Create(startY, endY), () => 0, (range, _, threadStorage) => { var i = (range.Item1 - startY) * width; for (var y = range.Item1; y < range.Item2; ++y) { for (var x = startX; x < endX; ++x) { var color = image[x, y]; result._redPlane[i] = color.SingleRed; result._greenPlane[i] = color.SingleGreen; result._bluePlane[i] = color.SingleBlue; result._alphaPlane[i] = color.SingleAlpha; ++i; } } return(threadStorage); }, _ => { } ); return(result); }
/* * This region is a C++ -> C# conversion from the original sources of Pascal Getreuer <*****@*****.**> */ /// <summary> /// Resizes the image to the specified dimensions using the given kernel type. /// </summary> /// <param name="destWidth">Width of the destination.</param> /// <param name="destHeight">Height of the destination.</param> /// <param name="interpolationInfo">The interpolation method info.</param> /// <param name="centeredGrid">if set to <c>true</c> using a centered grid; otherwise, using top-left aligned.</param> /// <returns> /// The resized image /// </returns> private FloatImage _Resize(int destWidth, int destHeight, Kernels.FixedRadiusKernelInfo interpolationInfo, bool centeredGrid) { var xScale = (float)destWidth / this.Width; var yScale = (float)destHeight / this.Height; var xStep = 1 / xScale; var yStep = 1 / yScale; float xStart, yStart; if (centeredGrid) { xStart = (xStep - 1) / 2; yStart = (yStep - 1) / 2; } else { xStart = yStart = 0; } var result = new FloatImage(destWidth, destHeight, this._horizontalOutOfBoundsMode, this._verticalOutOfBoundsMode); // prefilter image if necessary if (interpolationInfo.PrefilterAlpha != null && interpolationInfo.PrefilterAlpha.Length > 0) this._PrefilterImage(interpolationInfo.PrefilterAlpha, interpolationInfo.PrefilterScale); // resample this._LinScale2D( result, xStart, xStep, yStart, yStep, interpolationInfo.Kernel, interpolationInfo.KernelRadius, interpolationInfo.KernelNormalize, OutOfBoundsUtils.GetHandlerOrCrash(this.HorizontalOutOfBoundsMode), OutOfBoundsUtils.GetHandlerOrCrash(this.VerticalOutOfBoundsMode) ); return (result); }
/// <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); } }
/// <summary> /// Converts a given image into a floating point one. /// </summary> /// <param name="image">The image.</param> /// <param name="filterRegion">The filter region.</param> /// <returns></returns> public static FloatImage FromImage(cImage image, Rectangle? filterRegion) { Contract.Requires(image != null); var startX = filterRegion == null ? 0 : Math.Max(0, filterRegion.Value.Left); var startY = filterRegion == null ? 0 : Math.Max(0, filterRegion.Value.Top); var endX = filterRegion == null ? image.Width : Math.Min(image.Width, filterRegion.Value.Right); var endY = filterRegion == null ? image.Height : Math.Min(image.Height, filterRegion.Value.Bottom); var width = endX - startX; var height = endY - startY; var result = new FloatImage(width, height, image.HorizontalOutOfBoundsMode, image.VerticalOutOfBoundsMode); // copy image data Parallel.ForEach( Partitioner.Create(startY, endY), () => 0, (range, _, threadStorage) => { var i = (range.Item1 - startY) * width; for (var y = range.Item1; y < range.Item2; ++y) { for (var x = startX; x < endX; ++x) { var color = image[x, y]; result._redPlane[i] = color.SingleRed; result._greenPlane[i] = color.SingleGreen; result._bluePlane[i] = color.SingleBlue; result._alphaPlane[i] = color.SingleAlpha; ++i; } } return (threadStorage); }, _ => { } ); return (result); }