Browse Source

Cleanup and remove double cast.

pull/644/head
James Jackson-South 8 years ago
parent
commit
0da5e68854
  1. 18
      src/ImageSharp/Processing/HistogramEqualizationExtension.cs
  2. 38
      src/ImageSharp/Processing/Processors/Normalization/HistogramEqualizationProcessor.cs
  3. 7
      tests/ImageSharp.Tests/Processing/Normalization/HistogramEqualizationTests.cs

18
src/ImageSharp/Processing/HistogramEqualizationExtension.cs

@ -7,19 +7,29 @@ using SixLabors.ImageSharp.Processing.Processors.Normalization;
namespace SixLabors.ImageSharp.Processing namespace SixLabors.ImageSharp.Processing
{ {
/// <summary> /// <summary>
/// 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.
/// </summary> /// </summary>
public static class HistogramEqualizationExtension public static class HistogramEqualizationExtension
{ {
/// <summary>
/// Equalizes the histogram of an image to increases the global contrast using 65536 luminance levels.
/// </summary>
/// <typeparam name="TPixel">The pixel format.</typeparam>
/// <param name="source">The image this method extends.</param>
/// <returns>The <see cref="Image{TPixel}"/>.</returns>
public static IImageProcessingContext<TPixel> HistogramEqualization<TPixel>(this IImageProcessingContext<TPixel> source)
where TPixel : struct, IPixel<TPixel>
=> HistogramEqualization(source, 65536);
/// <summary> /// <summary>
/// Equalizes the histogram of an image to increases the global contrast. /// Equalizes the histogram of an image to increases the global contrast.
/// </summary> /// </summary>
/// <typeparam name="TPixel">The pixel format.</typeparam> /// <typeparam name="TPixel">The pixel format.</typeparam>
/// <param name="source">The image this method extends.</param> /// <param name="source">The image this method extends.</param>
/// <param name="luminanceLevels">The number of different luminance levels. Typical values are 256 for 8-bit grayscale images /// <param name="luminanceLevels">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.</param> /// or 65536 for 16-bit grayscale images.</param>
/// <returns>A histogram equalized grayscale image.</returns> /// <returns>The <see cref="Image{TPixel}"/>.</returns>
public static IImageProcessingContext<TPixel> HistogramEqualization<TPixel>(this IImageProcessingContext<TPixel> source, int luminanceLevels = 65536) public static IImageProcessingContext<TPixel> HistogramEqualization<TPixel>(this IImageProcessingContext<TPixel> source, int luminanceLevels)
where TPixel : struct, IPixel<TPixel> where TPixel : struct, IPixel<TPixel>
=> source.ApplyProcessor(new HistogramEqualizationProcessor<TPixel>(luminanceLevels)); => source.ApplyProcessor(new HistogramEqualizationProcessor<TPixel>(luminanceLevels));
} }

38
src/ImageSharp/Processing/Processors/Normalization/HistogramEqualizationProcessor.cs

@ -3,6 +3,7 @@
using System; using System;
using System.Numerics; using System.Numerics;
using System.Runtime.CompilerServices;
using SixLabors.ImageSharp.Advanced; using SixLabors.ImageSharp.Advanced;
using SixLabors.ImageSharp.PixelFormats; using SixLabors.ImageSharp.PixelFormats;
using SixLabors.Memory; using SixLabors.Memory;
@ -21,8 +22,8 @@ namespace SixLabors.ImageSharp.Processing.Processors.Normalization
/// Initializes a new instance of the <see cref="HistogramEqualizationProcessor{TPixel}"/> class. /// Initializes a new instance of the <see cref="HistogramEqualizationProcessor{TPixel}"/> class.
/// </summary> /// </summary>
/// <param name="luminanceLevels">The number of different luminance levels. Typical values are 256 for 8-bit grayscale images /// <param name="luminanceLevels">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.</param> /// or 65536 for 16-bit grayscale images.</param>
public HistogramEqualizationProcessor(int luminanceLevels = 65536) public HistogramEqualizationProcessor(int luminanceLevels)
{ {
Guard.MustBeGreaterThan(luminanceLevels, 0, nameof(luminanceLevels)); Guard.MustBeGreaterThan(luminanceLevels, 0, nameof(luminanceLevels));
@ -30,7 +31,7 @@ namespace SixLabors.ImageSharp.Processing.Processors.Normalization
} }
/// <summary> /// <summary>
/// Gets the luminance levels. /// Gets the number of luminance levels.
/// </summary> /// </summary>
public int LuminanceLevels { get; } public int LuminanceLevels { get; }
@ -41,7 +42,7 @@ namespace SixLabors.ImageSharp.Processing.Processors.Normalization
int numberOfPixels = source.Width * source.Height; int numberOfPixels = source.Width * source.Height;
Span<TPixel> pixels = source.GetPixelSpan(); Span<TPixel> pixels = source.GetPixelSpan();
// build the histogram of the grayscale levels // Build the histogram of the grayscale levels.
using (IBuffer<int> histogramBuffer = memoryAllocator.AllocateClean<int>(this.LuminanceLevels)) using (IBuffer<int> histogramBuffer = memoryAllocator.AllocateClean<int>(this.LuminanceLevels))
using (IBuffer<int> cdfBuffer = memoryAllocator.AllocateClean<int>(this.LuminanceLevels)) using (IBuffer<int> cdfBuffer = memoryAllocator.AllocateClean<int>(this.LuminanceLevels))
{ {
@ -53,33 +54,33 @@ namespace SixLabors.ImageSharp.Processing.Processors.Normalization
histogram[luminance]++; 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<int> cdf = cdfBuffer.GetSpan(); Span<int> cdf = cdfBuffer.GetSpan();
int cdfMin = this.CaluclateCdf(cdf, histogram); int cdfMin = this.CalculateCdf(cdf, histogram);
// apply the cdf to each pixel of the image // Apply the cdf to each pixel of the image
double numberOfPixelsMinusCdfMin = (double)(numberOfPixels - cdfMin); float numberOfPixelsMinusCdfMin = numberOfPixels - cdfMin;
for (int i = 0; i < pixels.Length; i++) for (int i = 0; i < pixels.Length; i++)
{ {
TPixel sourcePixel = pixels[i]; TPixel sourcePixel = pixels[i];
int luminance = this.GetLuminance(sourcePixel, this.LuminanceLevels); 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));
} }
} }
} }
/// <summary> /// <summary>
/// Calculate the cumulative distribution function /// Calculates the cumulative distribution function.
/// </summary> /// </summary>
/// <param name="cdf">The array holding the cdf</param> /// <param name="cdf">The array holding the cdf.</param>
/// <param name="histogram">The histogram of the input image</param> /// <param name="histogram">The histogram of the input image.</param>
/// <returns>The first none zero value of the cdf</returns> /// <returns>The first none zero value of the cdf.</returns>
private int CaluclateCdf(Span<int> cdf, Span<int> histogram) private int CalculateCdf(Span<int> cdf, Span<int> histogram)
{ {
// calculate the cumulative histogram // Calculate the cumulative histogram
int histSum = 0; int histSum = 0;
for (int i = 0; i < histogram.Length; i++) for (int i = 0; i < histogram.Length; i++)
{ {
@ -87,7 +88,7 @@ namespace SixLabors.ImageSharp.Processing.Processors.Normalization
cdf[i] = histSum; 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; int cdfMin = 0;
for (int i = 0; i < histogram.Length; i++) 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++) for (int i = 0; i < histogram.Length; i++)
{ {
cdf[i] = Math.Max(0, cdf[i] - cdfMin); cdf[i] = Math.Max(0, cdf[i] - cdfMin);
@ -112,6 +113,7 @@ namespace SixLabors.ImageSharp.Processing.Processors.Normalization
/// </summary> /// </summary>
/// <param name="sourcePixel">The pixel to get the luminance from</param> /// <param name="sourcePixel">The pixel to get the luminance from</param>
/// <param name="luminanceLevels">The number of luminance levels (256 for 8 bit, 65536 for 16 bit grayscale images)</param> /// <param name="luminanceLevels">The number of luminance levels (256 for 8 bit, 65536 for 16 bit grayscale images)</param>
[MethodImpl(InliningOptions.ShortMethod)]
private int GetLuminance(TPixel sourcePixel, int luminanceLevels) private int GetLuminance(TPixel sourcePixel, int luminanceLevels)
{ {
// Convert to grayscale using ITU-R Recommendation BT.709 // Convert to grayscale using ITU-R Recommendation BT.709

7
tests/ImageSharp.Tests/Processing/Normalization/HistogramEqualizationTests.cs

@ -14,7 +14,7 @@ namespace SixLabors.ImageSharp.Tests.Processing.Normalization
[InlineData(65536)] [InlineData(65536)]
public void HistogramEqualizationTest(int luminanceLevels) public void HistogramEqualizationTest(int luminanceLevels)
{ {
// arrange // Arrange
byte[] pixels = new byte[] byte[] pixels = new byte[]
{ {
52, 55, 61, 59, 70, 61, 76, 61, 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, 69, 85, 64, 58, 55, 61, 65, 83,
70, 87, 69, 68, 65, 73, 78, 90 70, 87, 69, 68, 65, 73, 78, 90
}; };
var image = new Image<Rgba32>(8, 8); var image = new Image<Rgba32>(8, 8);
for (int y = 0; y < 8; y++) 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 146, 206, 130, 117, 85, 166, 182, 215
}; };
// act // Act
image.Mutate(x => x.HistogramEqualization(luminanceLevels)); image.Mutate(x => x.HistogramEqualization(luminanceLevels));
// assert // Assert
for (int y = 0; y < 8; y++) for (int y = 0; y < 8; y++)
{ {
for (int x = 0; x < 8; x++) for (int x = 0; x < 8; x++)

Loading…
Cancel
Save