/// <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; }
/// <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);
/// <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; }
/// <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));
/// <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; }
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); }
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); } } }
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); } } }
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; }
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); } }
/// <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; }
/// <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;
/// <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); }
/// <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) { }
public BinaryDitherTest() { this.orderedDither = KnownDitherers.BayerDither4x4; this.errorDiffuser = KnownDiffusers.FloydSteinberg; }
/// <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) { }
/// <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) { }
/// <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) { }
/// <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); }
/// <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)); }
/// <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) { }
/// <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) { }