Esempio n. 1
0
 /// <summary>
 /// Initializes a new instance of the <see cref="FrameQuantizer{TPixel}"/> class.
 /// </summary>
 /// <param name="configuration">The configuration which allows altering default behaviour or extending the library.</param>
 /// <param name="diffuser">The diffuser</param>
 /// <param name="singlePass">
 /// If true, the quantization process only needs to loop through the source pixels once
 /// </param>
 /// <remarks>
 /// If you construct this class with a <value>true</value> for <paramref name="singlePass"/>, then the code will
 /// only call the <see cref="SecondPass(ImageFrame{TPixel}, Span{byte}, ReadOnlySpan{TPixel},  int, int)"/> method.
 /// If two passes are required, the code will also call <see cref="FirstPass(ImageFrame{TPixel}, int, int)"/>.
 /// </remarks>
 protected FrameQuantizer(Configuration configuration, IErrorDiffuser diffuser, bool singlePass)
 {
     this.Configuration = configuration;
     this.Diffuser      = diffuser;
     this.Dither        = this.Diffuser != null;
     this.singlePass    = singlePass;
 }
Esempio n. 2
0
 /// <summary>
 /// Dithers the image reducing it to the given palette using error diffusion.
 /// </summary>
 /// <param name="source">The image this method extends.</param>
 /// <param name="diffuser">The diffusion algorithm to apply.</param>
 /// <param name="threshold">The threshold to apply binarization of the image. Must be between 0 and 1.</param>
 /// <param name="palette">The palette to select substitute colors from.</param>
 /// <param name="rectangle">
 /// The <see cref="Rectangle"/> structure that specifies the portion of the image object to alter.
 /// </param>
 /// <returns>The <see cref="IImageProcessingContext"/> to allow chaining of operations.</returns>
 public static IImageProcessingContext Diffuse(
     this IImageProcessingContext source,
     IErrorDiffuser diffuser,
     float threshold,
     ReadOnlyMemory <Color> palette,
     Rectangle rectangle) =>
 source.ApplyProcessor(new ErrorDiffusionPaletteProcessor(diffuser, threshold, palette), rectangle);
Esempio n. 3
0
        /// <summary>
        /// Initializes a new instance of the <see cref="WuQuantizer"/> class.
        /// </summary>
        /// <param name="diffuser">The error diffusion algorithm, if any, to apply to the output image</param>
        /// <param name="maxColors">The maximum number of colors to hold in the color palette</param>
        public WuQuantizer(IErrorDiffuser diffuser, int maxColors)
        {
            Guard.MustBeBetweenOrEqualTo(maxColors, 1, 255, nameof(maxColors));

            this.Diffuser  = diffuser;
            this.MaxColors = maxColors;
        }
Esempio n. 4
0
 /// <summary>
 /// Dithers the image reducing it to two colors using error diffusion.
 /// </summary>
 /// <param name="source">The image this method extends.</param>
 /// <param name="diffuser">The diffusion algorithm to apply.</param>
 /// <param name="threshold">The threshold to apply binarization of the image. Must be between 0 and 1.</param>
 /// <param name="upperColor">The color to use for pixels that are above the threshold.</param>
 /// <param name="lowerColor">The color to use for pixels that are below the threshold</param>
 /// <returns>The <see cref="IImageProcessingContext"/> to allow chaining of operations.</returns>
 public static IImageProcessingContext BinaryDiffuse(
     this IImageProcessingContext source,
     IErrorDiffuser diffuser,
     float threshold,
     Color upperColor,
     Color lowerColor) =>
 source.ApplyProcessor(new BinaryErrorDiffusionProcessor(diffuser, threshold, upperColor, lowerColor));
Esempio n. 5
0
        /// <summary>
        /// Initializes a new instance of the <see cref="ErrorDiffusionPaletteProcessor{TPixel}"/> class.
        /// </summary>
        /// <param name="diffuser">The error diffuser</param>
        /// <param name="threshold">The threshold to split the image. Must be between 0 and 1.</param>
        /// <param name="palette">The palette to select substitute colors from.</param>
        public ErrorDiffusionPaletteProcessor(IErrorDiffuser diffuser, float threshold, TPixel[] palette)
            : base(palette)
        {
            Guard.NotNull(diffuser, nameof(diffuser));
            Guard.MustBeBetweenOrEqualTo(threshold, 0, 1, nameof(threshold));

            this.Diffuser  = diffuser;
            this.Threshold = threshold;
        }
        /// <summary>
        /// Initializes a new instance of the <see cref="BinaryErrorDiffusionProcessor{TPixel}"/> class.
        /// </summary>
        /// <param name="diffuser">The error diffuser</param>
        /// <param name="threshold">The threshold to split the image. Must be between 0 and 1.</param>
        /// <param name="upperColor">The color to use for pixels that are above the threshold.</param>
        /// <param name="lowerColor">The color to use for pixels that are below the threshold.</param>
        public BinaryErrorDiffusionProcessor(IErrorDiffuser diffuser, float threshold, TPixel upperColor, TPixel lowerColor)
        {
            Guard.NotNull(diffuser, nameof(diffuser));
            Guard.MustBeBetweenOrEqualTo(threshold, 0, 1, nameof(threshold));

            this.Diffuser   = diffuser;
            this.Threshold  = threshold;
            this.UpperColor = upperColor;
            this.LowerColor = lowerColor;
        }
        /// <summary>
        /// Initializes a new instance of the <see cref="ErrorDiffusionDitherProcessor{TPixel}"/> class.
        /// </summary>
        /// <param name="diffuser">The error diffuser</param>
        /// <param name="threshold">The threshold to split the image. Must be between 0 and 1.</param>
        public ErrorDiffusionDitherProcessor(IErrorDiffuser diffuser, float threshold)
        {
            Guard.NotNull(diffuser, nameof(diffuser));

            this.Diffuser  = diffuser;
            this.Threshold = threshold;

            // Default to white/black for upper/lower.
            this.UpperColor = NamedColors <TPixel> .White;
            this.LowerColor = NamedColors <TPixel> .Black;
        }
Esempio n. 8
0
        protected override void ProcessRecord()
        {
            if (!ShouldProcess(FileImage.FileInfo.ToString(), "Quantize Image"))
            {
                return;
            }
            IErrorDiffuser diffuser = Dither ? KnownDiffusers.FloydSteinberg : null;

            FileImage.Image.Mutate(im => im.Quantize(new WuQuantizer(diffuser, MaxColors[0])));
            WriteObject(FileImage);
        }
Esempio n. 9
0
        public void ImageShouldApplyDiffusionFilterInBox(string name, IErrorDiffuser diffuser)
        {
            string path = this.CreateOutputDirectory("Dither", "Diffusion");

            foreach (TestFile file in Files)
            {
                string filename = file.GetFileName($"{name}-InBox");
                using (Image <Rgba32> image = file.CreateImage())
                    using (FileStream output = File.OpenWrite($"{path}/{filename}"))
                    {
                        image.Dither(diffuser, .5F, new Rectangle(10, 10, image.Width / 2, image.Height / 2)).Save(output);
                    }
            }
        }
Esempio n. 10
0
        public void ImageShouldApplyDiffusionFilter(string name, IErrorDiffuser diffuser)
        {
            string path = this.CreateOutputDirectory("Dither", "Diffusion");

            foreach (TestFile file in Files)
            {
                string filename = file.GetFileName(name);
                using (Image <Rgba32> image = file.CreateImage())
                    using (FileStream output = File.OpenWrite($"{path}/{filename}"))
                    {
                        image.Dither(diffuser, .5F).Save(output);
                    }
            }
        }
        /// <inheritdoc/>
        protected override void OnFrameApply(ImageFrame <TPixel> source)
        {
            TPixel         upperColor = this.definition.UpperColor.ToPixel <TPixel>();
            TPixel         lowerColor = this.definition.LowerColor.ToPixel <TPixel>();
            IErrorDiffuser diffuser   = this.definition.Diffuser;

            byte threshold   = (byte)MathF.Round(this.definition.Threshold * 255F);
            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;
                    }

                    TPixel transformedPixel = luminance >= threshold ? upperColor : lowerColor;
                    diffuser.Dither(source, sourcePixel, transformedPixel, x, y, startX, endX, endY);
                }
            }
        }
Esempio n. 12
0
        public void DiffusionFilter_WorksWithAllErrorDiffusers <TPixel>(
            TestImageProvider <TPixel> provider,
            IErrorDiffuser diffuser)
            where TPixel : struct, IPixel <TPixel>
        {
            if (SkipAllDitherTests)
            {
                return;
            }

            provider.RunValidatingProcessorTest(
                x => x.Diffuse(diffuser, 0.5f),
                testOutputDetails: diffuser.GetType().Name,
                comparer: ValidatorComparer,
                appendPixelTypeToFileName: false);
        }
        /// <summary>
        /// Initializes a new instance of the <see cref="ErrorDiffusionDitherProcessor{TColor}"/> class.
        /// </summary>
        /// <param name="diffuser">The error diffuser</param>
        /// <param name="threshold">The threshold to split the image. Must be between 0 and 1.</param>
        public ErrorDiffusionDitherProcessor(IErrorDiffuser diffuser, float threshold)
        {
            Guard.NotNull(diffuser, nameof(diffuser));

            this.Diffuser  = diffuser;
            this.Threshold = threshold;

            // Default to white/black for upper/lower.
            TColor upper = default(TColor);

            upper.PackFromBytes(255, 255, 255, 255);
            this.UpperColor = upper;

            TColor lower = default(TColor);

            lower.PackFromBytes(0, 0, 0, 255);
            this.LowerColor = lower;
        }
Esempio n. 14
0
 public void DiffusionFilter_WorksWithAllErrorDiffusers <TPixel>(TestImageProvider <TPixel> provider, string name, IErrorDiffuser diffuser)
     where TPixel : struct, IPixel <TPixel>
 {
     using (Image <TPixel> image = provider.GetImage())
     {
         image.Mutate(x => x.Diffuse(diffuser, .5F));
         image.DebugSave(provider, name);
     }
 }
Esempio n. 15
0
 /// <summary>
 /// Initializes a new instance of the <see cref="PaletteQuantizer"/> class.
 /// </summary>
 /// <param name="diffuser">The error diffusion algorithm, if any, to apply to the output image</param>
 public PaletteQuantizer(IErrorDiffuser diffuser)
 {
     this.Diffuser = diffuser;
 }
Esempio n. 16
0
 /// <summary>
 /// Initializes a new instance of the <see cref="PaletteFrameQuantizer{TPixel}"/> class.
 /// </summary>
 /// <param name="diffuser">The palette quantizer.</param>
 /// <param name="colors">An array of all colors in the palette.</param>
 public PaletteFrameQuantizer(IErrorDiffuser diffuser, ReadOnlyMemory <TPixel> colors)
     : base(diffuser, true) => this.palette = colors;
Esempio n. 17
0
 /// <summary>
 /// Initializes a new instance of the <see cref="WuQuantizer"/> class.
 /// </summary>
 /// <param name="diffuser">The error diffusion algorithm, if any, to apply to the output image</param>
 /// <param name="maxColors">The maximum number of colors to hold in the color palette</param>
 public WuQuantizer(IErrorDiffuser diffuser, int maxColors)
 {
     this.Diffuser  = diffuser;
     this.MaxColors = maxColors.Clamp(QuantizerConstants.MinColors, QuantizerConstants.MaxColors);
 }
Esempio n. 18
0
 /// <summary>
 /// Initializes a new instance of the <see cref="WuQuantizer"/> class.
 /// </summary>
 /// <param name="diffuser">The error diffusion algorithm, if any, to apply to the output image</param>
 public WuQuantizer(IErrorDiffuser diffuser)
     : this(diffuser, QuantizerConstants.MaxColors)
 {
 }
Esempio n. 19
0
 public BinaryDitherTest()
 {
     this.orderedDither = KnownDitherers.BayerDither4x4;
     this.errorDiffuser = KnownDiffusers.FloydSteinberg;
 }
Esempio n. 20
0
 /// <summary>
 /// Initializes a new instance of the <see cref="ErrorDiffusionPaletteProcessor{TPixel}"/> class.
 /// </summary>
 /// <param name="diffuser">The error diffuser</param>
 public ErrorDiffusionPaletteProcessor(IErrorDiffuser diffuser)
     : this(diffuser, .5F)
 {
 }
Esempio n. 21
0
 /// <summary>
 /// Initializes a new instance of the <see cref="ErrorDiffusionPaletteProcessor{TPixel}"/> class.
 /// </summary>
 /// <param name="diffuser">The error diffuser</param>
 /// <param name="threshold">The threshold to split the image. Must be between 0 and 1.</param>
 public ErrorDiffusionPaletteProcessor(IErrorDiffuser diffuser, float threshold)
     : this(diffuser, threshold, NamedColors <TPixel> .WebSafePalette)
 {
 }
 /// <summary>
 /// Initializes a new instance of the <see cref="BinaryErrorDiffusionProcessor{TPixel}"/> class.
 /// </summary>
 /// <param name="diffuser">The error diffuser</param>
 /// <param name="threshold">The threshold to split the image. Must be between 0 and 1.</param>
 public BinaryErrorDiffusionProcessor(IErrorDiffuser diffuser, float threshold)
     : this(diffuser, threshold, NamedColors <TPixel> .White, NamedColors <TPixel> .Black)
 {
 }
Esempio n. 23
0
 /// <summary>
 /// Initializes a new instance of the <see cref="OctreeQuantizer"/> class.
 /// </summary>
 /// <param name="diffuser">The error diffusion algorithm, if any, to apply to the output image</param>
 public OctreeQuantizer(IErrorDiffuser diffuser)
     : this(diffuser, DefaultMaxColors)
 {
 }
 /// <summary>
 /// Initializes a new instance of the <see cref="BinaryErrorDiffusionProcessor{TPixel}"/> class.
 /// </summary>
 /// <param name="diffuser">The error diffuser</param>
 public BinaryErrorDiffusionProcessor(IErrorDiffuser diffuser)
     : this(diffuser, .5F)
 {
 }
Esempio n. 25
0
 /// <summary>
 /// Initializes a new instance of the <see cref="OctreeQuantizer"/> class.
 /// </summary>
 /// <param name="diffuser">The error diffusion algorithm, if any, to apply to the output image</param>
 /// <param name="maxColors">The maximum number of colors to hold in the color palette</param>
 public OctreeQuantizer(IErrorDiffuser diffuser, int maxColors)
 {
     this.Diffuser  = diffuser;
     this.MaxColors = maxColors.Clamp(1, DefaultMaxColors);
 }
Esempio n. 26
0
 /// <summary>
 /// Dithers the image reducing it to two colors using error diffusion.
 /// </summary>
 /// <typeparam name="TPixel">The pixel format.</typeparam>
 /// <param name="source">The image this method extends.</param>
 /// <param name="diffuser">The diffusion algorithm to apply.</param>
 /// <param name="threshold">The threshold to apply binarization of the image. Must be between 0 and 1.</param>
 /// <returns>The <see cref="Image{TPixel}"/>.</returns>
 public static Image <TPixel> Dither <TPixel>(this Image <TPixel> source, IErrorDiffuser diffuser, float threshold)
     where TPixel : struct, IPixel <TPixel>
 {
     return(Dither(source, diffuser, threshold, source.Bounds));
 }
Esempio n. 27
0
 /// <summary>
 /// Initializes a new instance of the <see cref="WebSafePaletteQuantizer" /> class.
 /// </summary>
 /// <param name="diffuser">The error diffusion algorithm, if any, to apply to the output image</param>
 public WebSafePaletteQuantizer(IErrorDiffuser diffuser)
     : base(Color.WebSafePalette, diffuser)
 {
 }
 /// <summary>
 /// Initializes a new instance of the <see cref="WernerPaletteQuantizer" /> class.
 /// </summary>
 /// <param name="diffuser">The error diffusion algorithm, if any, to apply to the output image</param>
 public WernerPaletteQuantizer(IErrorDiffuser diffuser)
     : base(diffuser)
 {
 }
Esempio n. 29
0
 /// <summary>
 /// Dithers the image reducing it to two colors using error diffusion.
 /// </summary>
 /// <typeparam name="TPixel">The pixel format.</typeparam>
 /// <param name="source">The image this method extends.</param>
 /// <param name="diffuser">The diffusion algorithm to apply.</param>
 /// <param name="threshold">The threshold to apply binarization of the image. Must be between 0 and 1.</param>
 /// <param name="rectangle">
 /// The <see cref="Rectangle"/> structure that specifies the portion of the image object to alter.
 /// </param>
 /// <returns>The <see cref="Image{TPixel}"/>.</returns>
 public static Image <TPixel> Dither <TPixel>(this Image <TPixel> source, IErrorDiffuser diffuser, float threshold, Rectangle rectangle)
     where TPixel : struct, IPixel <TPixel>
 {
     source.ApplyProcessor(new ErrorDiffusionDitherProcessor <TPixel>(diffuser, threshold), rectangle);
     return(source);
 }
 /// <summary>
 /// Initializes a new instance of the <see cref="BinaryErrorDiffusionProcessor"/> class.
 /// </summary>
 /// <param name="diffuser">The error diffuser</param>
 /// <param name="threshold">The threshold to split the image. Must be between 0 and 1.</param>
 public BinaryErrorDiffusionProcessor(IErrorDiffuser diffuser, float threshold)
     : this(diffuser, threshold, Color.White, Color.Black)
 {
 }