public void Gray16_FromRgba32() { // Arrange Gray16 gray = default; const byte rgb = 128; ushort scaledRgb = ImageMaths.UpscaleFrom8BitTo16Bit(rgb); ushort expected = ImageMaths.Get16BitBT709Luminance(scaledRgb, scaledRgb, scaledRgb); // Act gray.FromRgba32(new Rgba32(rgb, rgb, rgb)); ushort actual = gray.PackedValue; // Assert Assert.Equal(expected, actual); }
public void RotationZoomIsCalculated(int imageWidth, int imageHeight, float angle, float expected) { float result = ImageMaths.ZoomAfterRotation(imageWidth, imageHeight, angle); result.Should() .BeApproximately( expected, 0.01f, "because the zoom level after rotation should have been calculated"); result.Should() .BePositive("because we're always zooming in so the zoom level should always be positive"); result.Should() .BeGreaterOrEqualTo(1, "because the zoom should always increase the size and not reduce it"); }
/// <summary> /// The position in the original string where the first character of the captured substring was found. /// </summary> /// <param name="queryString">The query string to search.</param> /// <returns> /// The zero-based starting position in the original string where the captured substring was found. /// </returns> public int MatchRegexIndex(string queryString) { this.SortOrder = int.MaxValue; Match match = this.RegexPattern.Match(queryString); if (match.Success) { this.SortOrder = match.Index; NameValueCollection queryCollection = HttpUtility.ParseQueryString(queryString); int percentage = QueryParamParser.Instance.ParseValue <int>(queryCollection["brightness"]); percentage = ImageMaths.Clamp(percentage, -100, 100); this.Processor.DynamicParameter = percentage; } return(this.SortOrder); }
public OctreeQuantizer(Configuration configuration, QuantizerOptions options) { Guard.NotNull(configuration, nameof(configuration)); Guard.NotNull(options, nameof(options)); this.Configuration = configuration; this.Options = options; this.maxColors = this.Options.MaxColors; this.octree = new Octree(ImageMaths.GetBitsNeededForColorDepth(this.maxColors).Clamp(1, 8)); this.paletteOwner = configuration.MemoryAllocator.Allocate <TPixel>(this.maxColors, AllocationOptions.Clean); this.palette = default; this.pixelMap = default; this.isDithering = !(this.Options.Dither is null); this.isDisposed = false; }
public void La32_FromRgba32() { // Arrange La32 gray = default; const byte rgb = 128; ushort scaledRgb = ImageMaths.UpscaleFrom8BitTo16Bit(rgb); ushort expected = ImageMaths.Get16BitBT709Luminance(scaledRgb, scaledRgb, scaledRgb); // Act gray.FromRgba32(new Rgba32(rgb, rgb, rgb)); ushort actual = gray.L; // Assert Assert.Equal(expected, actual); Assert.Equal(ushort.MaxValue, gray.A); }
public void ThenShouldBeAt00And5By5GivenPoints5UnitsApart( int pointX1, int pointY1, int pointX2, int pointY2) { // Arrange // Act var rectangle = ImageMaths.GetBoundingRectangle( new Point(pointX1, pointY1), new Point(pointX2, pointY2)); // Assert Assert.That( rectangle, Is.EqualTo(new Rectangle(pointX1, pointY1, pointX2 - pointX1, pointY2 - pointY1))); }
/// <inheritdoc/> protected override void OnFrameApply(ImageFrame <TPixel> source) { IOrderedDither dither = this.definition.Dither; TPixel upperColor = this.definition.UpperColor.ToPixel <TPixel>(); TPixel lowerColor = this.definition.LowerColor.ToPixel <TPixel>(); bool isAlphaOnly = typeof(TPixel) == typeof(A8); var interest = Rectangle.Intersect(this.SourceRectangle, source.Bounds()); int startY = interest.Y; int endY = interest.Bottom; int startX = interest.X; int endX = interest.Right; // Collect the values before looping so we can reduce our calculation count for identical sibling pixels TPixel sourcePixel = source[startX, startY]; TPixel previousPixel = sourcePixel; Rgba32 rgba = default; sourcePixel.ToRgba32(ref rgba); // Convert to grayscale using ITU-R Recommendation BT.709 if required byte luminance = isAlphaOnly ? rgba.A : ImageMaths.Get8BitBT709Luminance(rgba.R, rgba.G, rgba.B); for (int y = startY; y < endY; y++) { Span <TPixel> row = source.GetPixelRowSpan(y); for (int x = startX; x < endX; x++) { sourcePixel = row[x]; // Check if this is the same as the last pixel. If so use that value // rather than calculating it again. This is an inexpensive optimization. if (!previousPixel.Equals(sourcePixel)) { sourcePixel.ToRgba32(ref rgba); luminance = isAlphaOnly ? rgba.A : ImageMaths.Get8BitBT709Luminance(rgba.R, rgba.G, rgba.B); // Setup the previous pointer previousPixel = sourcePixel; } dither.Dither(source, sourcePixel, upperColor, lowerColor, luminance, x, y); } } }
/// <summary> /// Encodes the image to the specified stream from the <see cref="Image{TPixel}"/>. /// </summary> /// <typeparam name="TPixel">The pixel format.</typeparam> /// <param name="image">The <see cref="Image{TPixel}"/> to encode from.</param> /// <param name="stream">The <see cref="Stream"/> to encode the image data to.</param> public void Encode <TPixel>(Image <TPixel> image, Stream stream) where TPixel : struct, IPixel <TPixel> { Guard.NotNull(image, nameof(image)); Guard.NotNull(stream, nameof(stream)); // Quantize the image returning a palette. QuantizedFrame <TPixel> quantized = this.quantizer.CreateFrameQuantizer <TPixel>().QuantizeFrame(image.Frames.RootFrame); // Get the number of bits. this.bitDepth = ImageMaths.GetBitsNeededForColorDepth(quantized.Palette.Length).Clamp(1, 8); int index = this.GetTransparentIndex(quantized); // Write the header. this.WriteHeader(stream); // Write the LSD. We'll use local color tables for now. this.WriteLogicalScreenDescriptor(image, stream, index); // Write the first frame. this.WriteComments(image.MetaData, stream); // Write additional frames. if (image.Frames.Count > 1) { this.WriteApplicationExtension(stream, image.MetaData.RepeatCount); } foreach (ImageFrame <TPixel> frame in image.Frames) { if (quantized == null) { quantized = this.quantizer.CreateFrameQuantizer <TPixel>().QuantizeFrame(frame); } this.WriteGraphicalControlExtension(frame.MetaData, stream, this.GetTransparentIndex(quantized)); this.WriteImageDescriptor(frame, stream); this.WriteColorTable(quantized, stream); this.WriteImageData(quantized, stream); quantized = null; // So next frame can regenerate it } // TODO: Write extension etc stream.WriteByte(GifConstants.EndIntroducer); }
/// <summary> /// Gets the bounding rectangle of the image based on the rotating angles. /// </summary> /// <param name="width"> /// The width of the image. /// </param> /// <param name="height"> /// The height of the image. /// </param> /// <returns> /// The <see cref="Rectangle"/>. /// </returns> private Rectangle GetBoundingRectangle(int width, int height) { int maxWidth = 0; int maxHeight = 0; List <float> angles = new List <float> { this.CyanAngle, this.MagentaAngle, this.YellowAngle, this.KeylineAngle }; foreach (float angle in angles) { Size rotatedSize = ImageMaths.GetBoundingRotatedRectangle(width, height, angle).Size; maxWidth = Math.Max(maxWidth, rotatedSize.Width); maxHeight = Math.Max(maxHeight, rotatedSize.Height); } return(new Rectangle(0, 0, maxWidth, maxHeight)); }
public void L16_ToRgba32(ushort input) { // Arrange ushort expected = ImageMaths.DownScaleFrom16BitTo8Bit(input); var gray = new L16(input); // Act Rgba32 actual = default; gray.ToRgba32(ref actual); // Assert Assert.Equal(expected, actual.R); Assert.Equal(expected, actual.G); Assert.Equal(expected, actual.B); Assert.Equal(byte.MaxValue, actual.A); }
private void EncodeLocal <TPixel>(Image <TPixel> image, IndexedImageFrame <TPixel> quantized, Stream stream) where TPixel : unmanaged, IPixel <TPixel> { ImageFrame <TPixel> previousFrame = null; GifFrameMetadata previousMeta = null; for (int i = 0; i < image.Frames.Count; i++) { ImageFrame <TPixel> frame = image.Frames[i]; ImageFrameMetadata metadata = frame.Metadata; GifFrameMetadata frameMetadata = metadata.GetGifMetadata(); if (quantized is null) { // Allow each frame to be encoded at whatever color depth the frame designates if set. if (previousFrame != null && previousMeta.ColorTableLength != frameMetadata.ColorTableLength && frameMetadata.ColorTableLength > 0) { var options = new QuantizerOptions { Dither = this.quantizer.Options.Dither, DitherScale = this.quantizer.Options.DitherScale, MaxColors = frameMetadata.ColorTableLength }; using IFrameQuantizer <TPixel> frameQuantizer = this.quantizer.CreateFrameQuantizer <TPixel>(this.configuration, options); quantized = frameQuantizer.QuantizeFrame(frame, frame.Bounds()); } else { using IFrameQuantizer <TPixel> frameQuantizer = this.quantizer.CreateFrameQuantizer <TPixel>(this.configuration); quantized = frameQuantizer.QuantizeFrame(frame, frame.Bounds()); } } this.bitDepth = ImageMaths.GetBitsNeededForColorDepth(quantized.Palette.Length); this.WriteGraphicalControlExtension(frameMetadata, this.GetTransparentIndex(quantized), stream); this.WriteImageDescriptor(frame, true, stream); this.WriteColorTable(quantized, stream); this.WriteImageData(quantized, stream); quantized.Dispose(); quantized = null; // So next frame can regenerate it previousFrame = frame; previousMeta = frameMetadata; } }
/// <inheritdoc/> protected override void OnApply(ImageBase <TColor, TPacked> target, ImageBase <TColor, TPacked> source, Rectangle targetRectangle, Rectangle sourceRectangle) { ImageBase <TColor, TPacked> temp = new Image <TColor, TPacked>(source.Width, source.Height); // Detect the edges. new SobelProcessor <TColor, TPacked>().Apply(temp, source, sourceRectangle); // Apply threshold binarization filter. new BinaryThresholdProcessor <TColor, TPacked>(.5f).Apply(temp, sourceRectangle); // Search for the first white pixels Rectangle rectangle = ImageMaths.GetFilteredBoundingRectangle(temp, 0); // Reset the target pixel to the correct size. target.SetPixels(rectangle.Width, rectangle.Height, new TColor[rectangle.Width * rectangle.Height]); this.cropRectangle = rectangle; }
/// <inheritdoc/> protected override void OnFrameApply(ImageFrame <TPixel> source, Rectangle sourceRectangle, Configuration configuration) { byte threshold = (byte)MathF.Round(this.Threshold * 255F); bool isAlphaOnly = typeof(TPixel) == typeof(Alpha8); var interest = Rectangle.Intersect(sourceRectangle, source.Bounds()); int startY = interest.Y; int endY = interest.Bottom; int startX = interest.X; int endX = interest.Right; // Collect the values before looping so we can reduce our calculation count for identical sibling pixels TPixel sourcePixel = source[startX, startY]; TPixel previousPixel = sourcePixel; Rgba32 rgba = default; sourcePixel.ToRgba32(ref rgba); // Convert to grayscale using ITU-R Recommendation BT.709 if required byte luminance = isAlphaOnly ? rgba.A : ImageMaths.Get8BitBT709Luminance(rgba.R, rgba.G, rgba.B); for (int y = startY; y < endY; y++) { Span <TPixel> row = source.GetPixelRowSpan(y); for (int x = startX; x < endX; x++) { sourcePixel = row[x]; // Check if this is the same as the last pixel. If so use that value // rather than calculating it again. This is an inexpensive optimization. if (!previousPixel.Equals(sourcePixel)) { sourcePixel.ToRgba32(ref rgba); luminance = isAlphaOnly ? rgba.A : ImageMaths.Get8BitBT709Luminance(rgba.R, rgba.G, rgba.B); // Setup the previous pointer previousPixel = sourcePixel; } TPixel transformedPixel = luminance >= threshold ? this.UpperColor : this.LowerColor; this.Diffuser.Dither(source, sourcePixel, transformedPixel, x, y, startX, startY, endX, endY); } } }
/// <summary> /// The position in the original string where the first character of the captured substring was found. /// </summary> /// <param name="queryString">The query string to search.</param> /// <returns> /// The zero-based starting position in the original string where the captured substring was found. /// </returns> public int MatchRegexIndex(string queryString) { this.SortOrder = int.MaxValue; Match match = this.RegexPattern.Match(queryString); if (match.Success) { this.SortOrder = match.Index; NameValueCollection queryCollection = HttpUtility.ParseQueryString(queryString); Color[] colors = QueryParamParser.Instance.ParseValue <Color[]>(queryCollection["replace"]); int fuzziness = QueryParamParser.Instance.ParseValue <int>(queryCollection["fuzziness"]); fuzziness = ImageMaths.Clamp(fuzziness, 0, 128); this.Processor.DynamicParameter = new Tuple <Color, Color, int>(colors[0], colors[1], fuzziness); } return(this.SortOrder); }
/// <summary> /// Create a 1 dimensional Gaussian kernel using the Gaussian G(x) function /// </summary> /// <param name="horizontal">Whether to calculate a horizontal kernel.</param> /// <returns>The <see cref="Fast2DArray{T}"/></returns> private Fast2DArray <float> CreateGaussianKernel(bool horizontal) { int size = this.kernelSize; float weight = this.sigma; Fast2DArray <float> kernel = horizontal ? new Fast2DArray <float>(size, 1) : new Fast2DArray <float>(1, size); float sum = 0F; float midpoint = (size - 1) / 2F; for (int i = 0; i < size; i++) { float x = i - midpoint; float gx = ImageMaths.Gaussian(x, weight); sum += gx; if (horizontal) { kernel[0, i] = gx; } else { kernel[i, 0] = gx; } } // Normalize kernel so that the sum of all weights equals 1 if (horizontal) { for (int i = 0; i < size; i++) { kernel[0, i] = kernel[0, i] / sum; } } else { for (int i = 0; i < size; i++) { kernel[i, 0] = kernel[i, 0] / sum; } } return(kernel); }
/// <summary> /// Initializes a new instance of the <see cref="HueProcessor{TColor}"/> class. /// </summary> /// <param name="angle">The new brightness of the image. Must be between -100 and 100.</param> public HueProcessor(float angle) { // Wrap the angle round at 360. angle = angle % 360; // Make sure it's not negative. while (angle < 0) { angle += 360; } this.Angle = angle; float radians = ImageMaths.DegreesToRadians(angle); double cosradians = Math.Cos(radians); double sinradians = Math.Sin(radians); float lumR = .213f; float lumG = .715f; float lumB = .072f; float oneMinusLumR = 1 - lumR; float oneMinusLumG = 1 - lumG; float oneMinusLumB = 1 - lumB; // The matrix is set up to preserve the luminance of the image. // See http://graficaobscura.com/matrix/index.html // Number are taken from https://msdn.microsoft.com/en-us/library/jj192162(v=vs.85).aspx Matrix4x4 matrix4X4 = new Matrix4x4() { M11 = (float)(lumR + (cosradians * oneMinusLumR) - (sinradians * lumR)), M12 = (float)(lumR - (cosradians * lumR) - (sinradians * 0.143)), M13 = (float)(lumR - (cosradians * lumR) - (sinradians * oneMinusLumR)), M21 = (float)(lumG - (cosradians * lumG) - (sinradians * lumG)), M22 = (float)(lumG + (cosradians * oneMinusLumG) + (sinradians * 0.140)), M23 = (float)(lumG - (cosradians * lumG) + (sinradians * lumG)), M31 = (float)(lumB - (cosradians * lumB) + (sinradians * oneMinusLumB)), M32 = (float)(lumB - (cosradians * lumB) - (sinradians * 0.283)), M33 = (float)(lumB + (cosradians * oneMinusLumB) + (sinradians * lumB)), M44 = 1 }; this.Matrix = matrix4X4; }
/// <summary> /// Create a 1 dimensional Gaussian kernel using the Gaussian G(x) function /// </summary> /// <returns>The <see cref="DenseMatrix{T}"/></returns> private DenseMatrix <float> CreateGaussianKernel() { int size = this.kernelSize; float weight = this.Sigma; var kernel = new DenseMatrix <float>(size, 1); float sum = 0; float midpoint = (size - 1) / 2F; for (int i = 0; i < size; i++) { float x = i - midpoint; float gx = ImageMaths.Gaussian(x, weight); sum += gx; kernel[0, i] = gx; } // Invert the kernel for sharpening. int midpointRounded = (int)midpoint; for (int i = 0; i < size; i++) { if (i == midpointRounded) { // Calculate central value kernel[0, i] = (2F * sum) - kernel[0, i]; } else { // invert value kernel[0, i] = -kernel[0, i]; } } // Normalize kernel so that the sum of all weights equals 1 for (int i = 0; i < size; i++) { kernel[0, i] /= sum; } return(kernel); }
/// <summary> /// Sets the resolution of the image. /// <remarks> /// This method sets both the bitmap data and EXIF resolution if available. /// </remarks> /// </summary> /// <param name="horizontal">The horizontal resolution.</param> /// <param name="vertical">The vertical resolution.</param> /// <param name="unit"> /// The unit of measure for the horizontal resolution and the vertical resolution. /// Defaults to inches /// </param> /// <returns> /// The <see cref="ImageFactory"/>. /// </returns> public ImageFactory Resolution(int horizontal, int vertical, PropertyTagResolutionUnit unit = PropertyTagResolutionUnit.Inch) { if (this.ShouldProcess) { // Sanitize the input. horizontal = ImageMaths.Clamp(horizontal, 0, int.MaxValue); vertical = ImageMaths.Clamp(vertical, 0, int.MaxValue); Tuple <int, int, PropertyTagResolutionUnit> resolution = new Tuple <int, int, PropertyTagResolutionUnit>(horizontal, vertical, unit); Resolution dpi = new Resolution { DynamicParameter = resolution }; this.CurrentImageFormat.ApplyProcessor(dpi.ProcessImage, this); } return(this); }
/// <summary> /// The position in the original string where the first character of the captured substring was found. /// </summary> /// <param name="queryString">The query string to search.</param> /// <returns> /// The zero-based starting position in the original string where the captured substring was found. /// </returns> public int MatchRegexIndex(string queryString) { this.SortOrder = int.MaxValue; Match match = this.RegexPattern.Match(queryString); if (match.Success) { this.SortOrder = match.Index; NameValueCollection queryCollection = HttpUtility.ParseQueryString(queryString); int degrees = QueryParamParser.Instance.ParseValue <int>(queryCollection["hue"]); bool rotate = QueryParamParser.Instance.ParseValue <bool>(queryCollection["hue.rotate"]); degrees = ImageMaths.Clamp(degrees, 0, 360); this.Processor.DynamicParameter = new Tuple <int, bool>(degrees, rotate); } return(this.SortOrder); }
/// <summary> /// Computes a simple linear function of the three neighboring pixels (left, above, upper left), then chooses /// as predictor the neighboring pixel closest to the computed value. /// </summary> /// <param name="left">The left neighbor pixel.</param> /// <param name="above">The above neighbor pixel.</param> /// <param name="upperLeft">The upper left neighbor pixel.</param> /// <returns> /// The <see cref="byte"/>. /// </returns> private static byte PaethPredicator(byte left, byte above, byte upperLeft) { int p = left + above - upperLeft; int pa = ImageMaths.FastAbs(p - left); int pb = ImageMaths.FastAbs(p - above); int pc = ImageMaths.FastAbs(p - upperLeft); if (pa <= pb && pa <= pc) { return(left); } if (pb <= pc) { return(above); } return(upperLeft); }
/// <summary> /// Create a 1 dimensional Gaussian kernel using the Gaussian G(x) function /// </summary> /// <param name="horizontal">Whether to calculate a horizontal kernel.</param> /// <returns>The <see cref="T:float[,]"/></returns> private float[,] CreateGaussianKernel(bool horizontal) { int size = this.kernelSize; float weight = this.sigma; float[,] kernel = horizontal ? new float[1, size] : new float[size, 1]; float sum = 0.0f; float midpoint = (size - 1) / 2f; for (int i = 0; i < size; i++) { float x = i - midpoint; float gx = ImageMaths.Gaussian(x, weight); sum += gx; if (horizontal) { kernel[0, i] = gx; } else { kernel[i, 0] = gx; } } // Normalise kernel so that the sum of all weights equals 1 if (horizontal) { for (int i = 0; i < size; i++) { kernel[0, i] = kernel[0, i] / sum; } } else { for (int i = 0; i < size; i++) { kernel[i, 0] = kernel[i, 0] / sum; } } return(kernel); }
private void EncodeLocal <TPixel>(Image <TPixel> image, QuantizedFrame <TPixel> quantized, Stream stream) where TPixel : struct, IPixel <TPixel> { ImageFrame <TPixel> previousFrame = null; GifFrameMetadata previousMeta = null; foreach (ImageFrame <TPixel> frame in image.Frames) { ImageFrameMetadata metadata = frame.Metadata; GifFrameMetadata frameMetadata = metadata.GetFormatMetadata(GifFormat.Instance); if (quantized is null) { // Allow each frame to be encoded at whatever color depth the frame designates if set. if (previousFrame != null && previousMeta.ColorTableLength != frameMetadata.ColorTableLength && frameMetadata.ColorTableLength > 0) { using (IFrameQuantizer <TPixel> frameQuantizer = this.quantizer.CreateFrameQuantizer <TPixel>(image.GetConfiguration(), frameMetadata.ColorTableLength)) { quantized = frameQuantizer.QuantizeFrame(frame); } } else { using (IFrameQuantizer <TPixel> frameQuantizer = this.quantizer.CreateFrameQuantizer <TPixel>(image.GetConfiguration())) { quantized = frameQuantizer.QuantizeFrame(frame); } } } this.bitDepth = ImageMaths.GetBitsNeededForColorDepth(quantized.Palette.Length).Clamp(1, 8); this.WriteGraphicalControlExtension(frameMetadata, this.GetTransparentIndex(quantized), stream); this.WriteImageDescriptor(frame, true, stream); this.WriteColorTable(quantized, stream); this.WriteImageData(quantized, stream); quantized?.Dispose(); quantized = null; // So next frame can regenerate it previousFrame = frame; previousMeta = frameMetadata; } }
public void ThenShouldConvertTo4PointsGivenRectangle(int x, int y, int width, int height) { // Arrange var rectangle = new Rectangle(x, y, width, height); // Act var points = ImageMaths.ToPoints(rectangle); // Assert var collectionEquivalentConstraint = Is.EquivalentTo( new[] { new Point(x, y), new Point(x + width, y), new Point(x, y + height), new Point(x + width, y + height), }); Assert.That( points, collectionEquivalentConstraint); }
/// <inheritdoc/> protected override void OnApply(ImageBase <TPixel> source, Rectangle sourceRectangle) { using (ImageBase <TPixel> temp = new Image <TPixel>(source)) { // Detect the edges. new SobelProcessor <TPixel>().Apply(temp, sourceRectangle); // Apply threshold binarization filter. new BinaryThresholdProcessor <TPixel>(this.Value).Apply(temp, sourceRectangle); // Search for the first white pixels Rectangle rectangle = ImageMaths.GetFilteredBoundingRectangle(temp, 0); if (rectangle == sourceRectangle) { return; } new CropProcessor <TPixel>(rectangle).Apply(source, sourceRectangle); } }
public void ApplyPaletteDither <TPixel>( Configuration configuration, ReadOnlyMemory <TPixel> palette, ImageFrame <TPixel> source, Rectangle bounds, float scale) where TPixel : struct, IPixel <TPixel> { var ditherOperation = new PaletteDitherRowIntervalOperation <TPixel>( in Unsafe.AsRef(this), source, bounds, palette, scale, ImageMaths.GetBitsNeededForColorDepth(palette.Span.Length)); ParallelRowIterator.IterateRows( configuration, bounds, in ditherOperation); }
/// <inheritdoc/> protected override void OnApply(ImageBase <TColor, TPacked> source, Rectangle sourceRectangle) { ImageBase <TColor, TPacked> temp = new Image <TColor, TPacked>(source.Width, source.Height); temp.ClonePixels(source.Width, source.Height, source.Pixels); // Detect the edges. new SobelProcessor <TColor, TPacked>().Apply(temp, sourceRectangle); // Apply threshold binarization filter. new BinaryThresholdProcessor <TColor, TPacked>(this.Value).Apply(temp, sourceRectangle); // Search for the first white pixels Rectangle rectangle = ImageMaths.GetFilteredBoundingRectangle(temp, 0); if (rectangle == sourceRectangle) { return; } new CropProcessor <TColor, TPacked>(rectangle).Apply(source, sourceRectangle); }
/// <summary> /// Calculates the bit depth value. /// </summary> /// <typeparam name="TPixel">The type of the pixel.</typeparam> /// <param name="options">The options.</param> /// <param name="image">The image.</param> /// <param name="quantizedFrame">The quantized frame.</param> public static byte CalculateBitDepth <TPixel>( PngEncoderOptions options, Image <TPixel> image, IndexedImageFrame <TPixel> quantizedFrame) where TPixel : unmanaged, IPixel <TPixel> { byte bitDepth; if (options.ColorType == PngColorType.Palette) { byte quantizedBits = (byte)ImageMaths.GetBitsNeededForColorDepth(quantizedFrame.Palette.Length).Clamp(1, 8); byte bits = Math.Max((byte)options.BitDepth, quantizedBits); // Png only supports in four pixel depths: 1, 2, 4, and 8 bits when using the PLTE chunk // We check again for the bit depth as the bit depth of the color palette from a given quantizer might not // be within the acceptable range. if (bits == 3) { bits = 4; } else if (bits >= 5 && bits <= 7) { bits = 8; } bitDepth = bits; } else { bitDepth = (byte)options.BitDepth; } if (Array.IndexOf(PngConstants.ColorTypes[options.ColorType.Value], bitDepth) == -1) { throw new NotSupportedException("Bit depth is not supported or not valid."); } return(bitDepth); }
/// <summary> /// Create a 1 dimensional Gaussian kernel using the Gaussian G(x) function. /// </summary> /// <returns>The <see cref="DenseMatrix{T}"/>.</returns> internal static DenseMatrix <float> CreateGaussianBlurKernel(int size, float weight) { var kernel = new DenseMatrix <float>(size, 1); float sum = 0F; float midpoint = (size - 1) / 2F; for (int i = 0; i < size; i++) { float x = i - midpoint; float gx = ImageMaths.Gaussian(x, weight); sum += gx; kernel[0, i] = gx; } // Normalize kernel so that the sum of all weights equals 1 for (int i = 0; i < size; i++) { kernel[0, i] /= sum; } return(kernel); }