diff --git a/src/ImageSharp/Processing/HistogramEqualizationExtension.cs b/src/ImageSharp/Processing/HistogramEqualizationExtension.cs index b472a2750..8dabfcc05 100644 --- a/src/ImageSharp/Processing/HistogramEqualizationExtension.cs +++ b/src/ImageSharp/Processing/HistogramEqualizationExtension.cs @@ -7,19 +7,29 @@ using SixLabors.ImageSharp.Processing.Processors.Normalization; namespace SixLabors.ImageSharp.Processing { /// - /// Adds extension that allows applying an HistogramEqualization to the image. + /// Adds extension that allow the adjustment of the contrast of an image via its histogram. /// public static class HistogramEqualizationExtension { + /// + /// Equalizes the histogram of an image to increases the global contrast using 65536 luminance levels. + /// + /// The pixel format. + /// The image this method extends. + /// The . + public static IImageProcessingContext HistogramEqualization(this IImageProcessingContext source) + where TPixel : struct, IPixel + => HistogramEqualization(source, 65536); + /// /// Equalizes the histogram of an image to increases the global contrast. /// /// The pixel format. /// The image this method extends. /// The number of different luminance levels. Typical values are 256 for 8-bit grayscale images - /// or 65536 for 16-bit grayscale images. Defaults to 65536. - /// A histogram equalized grayscale image. - public static IImageProcessingContext HistogramEqualization(this IImageProcessingContext source, int luminanceLevels = 65536) + /// or 65536 for 16-bit grayscale images. + /// The . + public static IImageProcessingContext HistogramEqualization(this IImageProcessingContext source, int luminanceLevels) where TPixel : struct, IPixel => source.ApplyProcessor(new HistogramEqualizationProcessor(luminanceLevels)); } diff --git a/src/ImageSharp/Processing/Processors/Normalization/HistogramEqualizationProcessor.cs b/src/ImageSharp/Processing/Processors/Normalization/HistogramEqualizationProcessor.cs index fdd439a33..ba56e392f 100644 --- a/src/ImageSharp/Processing/Processors/Normalization/HistogramEqualizationProcessor.cs +++ b/src/ImageSharp/Processing/Processors/Normalization/HistogramEqualizationProcessor.cs @@ -3,6 +3,7 @@ using System; using System.Numerics; +using System.Runtime.CompilerServices; using SixLabors.ImageSharp.Advanced; using SixLabors.ImageSharp.PixelFormats; using SixLabors.Memory; @@ -21,8 +22,8 @@ namespace SixLabors.ImageSharp.Processing.Processors.Normalization /// Initializes a new instance of the class. /// /// The number of different luminance levels. Typical values are 256 for 8-bit grayscale images - /// or 65536 for 16-bit grayscale images. Defaults to 65536. - public HistogramEqualizationProcessor(int luminanceLevels = 65536) + /// or 65536 for 16-bit grayscale images. + public HistogramEqualizationProcessor(int luminanceLevels) { Guard.MustBeGreaterThan(luminanceLevels, 0, nameof(luminanceLevels)); @@ -30,7 +31,7 @@ namespace SixLabors.ImageSharp.Processing.Processors.Normalization } /// - /// Gets the luminance levels. + /// Gets the number of luminance levels. /// public int LuminanceLevels { get; } @@ -41,7 +42,7 @@ namespace SixLabors.ImageSharp.Processing.Processors.Normalization int numberOfPixels = source.Width * source.Height; Span pixels = source.GetPixelSpan(); - // build the histogram of the grayscale levels + // Build the histogram of the grayscale levels. using (IBuffer histogramBuffer = memoryAllocator.AllocateClean(this.LuminanceLevels)) using (IBuffer cdfBuffer = memoryAllocator.AllocateClean(this.LuminanceLevels)) { @@ -53,33 +54,33 @@ namespace SixLabors.ImageSharp.Processing.Processors.Normalization histogram[luminance]++; } - // calculate the cumulative distribution function, which will map each input pixel to a new value + // Calculate the cumulative distribution function, which will map each input pixel to a new value. Span cdf = cdfBuffer.GetSpan(); - int cdfMin = this.CaluclateCdf(cdf, histogram); + int cdfMin = this.CalculateCdf(cdf, histogram); - // apply the cdf to each pixel of the image - double numberOfPixelsMinusCdfMin = (double)(numberOfPixels - cdfMin); + // Apply the cdf to each pixel of the image + float numberOfPixelsMinusCdfMin = numberOfPixels - cdfMin; for (int i = 0; i < pixels.Length; i++) { TPixel sourcePixel = pixels[i]; int luminance = this.GetLuminance(sourcePixel, this.LuminanceLevels); - double luminanceEqualized = cdf[luminance] / numberOfPixelsMinusCdfMin; + float luminanceEqualized = cdf[luminance] / numberOfPixelsMinusCdfMin; - pixels[i].PackFromVector4(new Vector4((float)luminanceEqualized)); + pixels[i].PackFromVector4(new Vector4(luminanceEqualized)); } } } /// - /// Calculate the cumulative distribution function + /// Calculates the cumulative distribution function. /// - /// The array holding the cdf - /// The histogram of the input image - /// The first none zero value of the cdf - private int CaluclateCdf(Span cdf, Span histogram) + /// The array holding the cdf. + /// The histogram of the input image. + /// The first none zero value of the cdf. + private int CalculateCdf(Span cdf, Span histogram) { - // calculate the cumulative histogram + // Calculate the cumulative histogram int histSum = 0; for (int i = 0; i < histogram.Length; i++) { @@ -87,7 +88,7 @@ namespace SixLabors.ImageSharp.Processing.Processors.Normalization cdf[i] = histSum; } - // get the first none zero value of the cumulative histogram + // Get the first none zero value of the cumulative histogram int cdfMin = 0; for (int i = 0; i < histogram.Length; i++) { @@ -98,7 +99,7 @@ namespace SixLabors.ImageSharp.Processing.Processors.Normalization } } - // creating the lookup table: subtracting cdf min, so we do not need to do that inside the for loop + // Creating the lookup table: subtracting cdf min, so we do not need to do that inside the for loop for (int i = 0; i < histogram.Length; i++) { cdf[i] = Math.Max(0, cdf[i] - cdfMin); @@ -112,6 +113,7 @@ namespace SixLabors.ImageSharp.Processing.Processors.Normalization /// /// The pixel to get the luminance from /// The number of luminance levels (256 for 8 bit, 65536 for 16 bit grayscale images) + [MethodImpl(InliningOptions.ShortMethod)] private int GetLuminance(TPixel sourcePixel, int luminanceLevels) { // Convert to grayscale using ITU-R Recommendation BT.709 diff --git a/tests/ImageSharp.Tests/Processing/Normalization/HistogramEqualizationTests.cs b/tests/ImageSharp.Tests/Processing/Normalization/HistogramEqualizationTests.cs index ebecfec5a..1595ed32c 100644 --- a/tests/ImageSharp.Tests/Processing/Normalization/HistogramEqualizationTests.cs +++ b/tests/ImageSharp.Tests/Processing/Normalization/HistogramEqualizationTests.cs @@ -14,7 +14,7 @@ namespace SixLabors.ImageSharp.Tests.Processing.Normalization [InlineData(65536)] public void HistogramEqualizationTest(int luminanceLevels) { - // arrange + // Arrange byte[] pixels = new byte[] { 52, 55, 61, 59, 70, 61, 76, 61, @@ -26,6 +26,7 @@ namespace SixLabors.ImageSharp.Tests.Processing.Normalization 69, 85, 64, 58, 55, 61, 65, 83, 70, 87, 69, 68, 65, 73, 78, 90 }; + var image = new Image(8, 8); for (int y = 0; y < 8; y++) { @@ -48,10 +49,10 @@ namespace SixLabors.ImageSharp.Tests.Processing.Normalization 146, 206, 130, 117, 85, 166, 182, 215 }; - // act + // Act image.Mutate(x => x.HistogramEqualization(luminanceLevels)); - // assert + // Assert for (int y = 0; y < 8; y++) { for (int x = 0; x < 8; x++)