Ejemplo n.º 1
0
        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);
        }
Ejemplo n.º 2
0
            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");
            }
Ejemplo n.º 3
0
        /// <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);
        }
Ejemplo n.º 4
0
        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;
        }
Ejemplo n.º 5
0
        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);
        }
Ejemplo n.º 6
0
            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);
                }
            }
        }
Ejemplo n.º 8
0
        /// <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);
        }
Ejemplo n.º 9
0
        /// <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));
        }
Ejemplo n.º 10
0
        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);
        }
Ejemplo n.º 11
0
        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;
            }
        }
Ejemplo n.º 12
0
        /// <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);
                }
            }
        }
Ejemplo n.º 14
0
        /// <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);
        }
Ejemplo n.º 15
0
        /// <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);
        }
Ejemplo n.º 16
0
        /// <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;
        }
Ejemplo n.º 17
0
        /// <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);
        }
Ejemplo n.º 18
0
        /// <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);
        }
Ejemplo n.º 19
0
        /// <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);
        }
Ejemplo n.º 20
0
        /// <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);
        }
Ejemplo n.º 21
0
        /// <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);
        }
Ejemplo n.º 22
0
        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;
            }
        }
Ejemplo n.º 23
0
                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);
                }
Ejemplo n.º 24
0
        /// <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);
            }
        }
Ejemplo n.º 25
0
        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);
        }
Ejemplo n.º 26
0
        /// <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);
        }
Ejemplo n.º 27
0
        /// <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);
        }