diff --git a/src/ImageSharp/Processing/Contrast/HistogramEqualizationExtension.cs b/src/ImageSharp/Processing/Contrast/HistogramEqualizationExtension.cs
index 1cd29f8b4..a7b59cedd 100644
--- a/src/ImageSharp/Processing/Contrast/HistogramEqualizationExtension.cs
+++ b/src/ImageSharp/Processing/Contrast/HistogramEqualizationExtension.cs
@@ -15,7 +15,7 @@ namespace SixLabors.ImageSharp.Processing.Contrast
///
/// The pixel format.
/// The image this method extends.
- /// The .
+ /// A histogram equalized grayscale image.
public static IImageProcessingContext HistogramEqualization(this IImageProcessingContext source)
where TPixel : struct, IPixel
=> source.ApplyProcessor(new HistogramEqualizationProcessor());
diff --git a/src/ImageSharp/Processing/Contrast/HistogramEqualizationProcessor.cs b/src/ImageSharp/Processing/Contrast/HistogramEqualizationProcessor.cs
index f23382d20..df1737455 100644
--- a/src/ImageSharp/Processing/Contrast/HistogramEqualizationProcessor.cs
+++ b/src/ImageSharp/Processing/Contrast/HistogramEqualizationProcessor.cs
@@ -9,17 +9,23 @@ using SixLabors.Primitives;
namespace SixLabors.ImageSharp.Processing.Contrast
{
+ ///
+ /// Applies a global histogram equalization to the image.
+ ///
+ /// The pixel format.
internal class HistogramEqualizationProcessor : ImageProcessor
where TPixel : struct, IPixel
{
///
protected override void OnFrameApply(ImageFrame source, Rectangle sourceRectangle, Configuration configuration)
{
- var rgb = default(Rgb24);
+ var rgb48 = default(Rgb48);
+ var rgb24 = default(Rgb24);
int numberOfPixels = source.Width * source.Height;
+ bool is16bitPerChannel = typeof(TPixel) == typeof(Rgb48) || typeof(TPixel) == typeof(Rgba64);
// build the histogram of the grayscale levels
- int luminanceLevels = 256;
+ int luminanceLevels = is16bitPerChannel ? 65536 : 256;
int[] histogram = new int[luminanceLevels];
for (int y = 0; y < source.Height; y++)
{
@@ -27,15 +33,12 @@ namespace SixLabors.ImageSharp.Processing.Contrast
for (int x = 0; x < source.Width; x++)
{
TPixel sourcePixel = row[x];
- sourcePixel.ToRgb24(ref rgb);
-
- // Convert to grayscale using ITU-R Recommendation BT.709 if required
- int luminance = (int)((.2126F * rgb.R) + (.7152F * rgb.G) + (.0722F * rgb.B));
+ int luminance = this.GetLuminance(sourcePixel, is16bitPerChannel, ref rgb24, ref rgb48);
histogram[luminance]++;
}
}
- // calculate the cumulative distribution function which will be the cumulative histogram
+ // calculate the cumulative distribution function (which will be the cumulative histogram)
int[] cdf = new int[luminanceLevels];
int histSum = 0;
for (int i = 0; i < histogram.Length; i++)
@@ -69,13 +72,46 @@ namespace SixLabors.ImageSharp.Processing.Contrast
for (int x = 0; x < source.Width; x++)
{
TPixel sourcePixel = row[x];
- sourcePixel.ToRgb24(ref rgb);
- int luminance = (int)((.2126F * rgb.R) + (.7152F * rgb.G) + (.0722F * rgb.B));
+
+ int luminance = this.GetLuminance(sourcePixel, is16bitPerChannel, ref rgb24, ref rgb48);
double luminanceEqualized = (lut[luminance] / numberOfPixelsMinusCdfMin) * luminanceLevelsMinusOne;
luminanceEqualized = Math.Round(luminanceEqualized);
- row[x].PackFromRgba32(new Rgba32((byte)luminanceEqualized, (byte)luminanceEqualized, (byte)luminanceEqualized));
+
+ if (is16bitPerChannel)
+ {
+ row[x].PackFromRgb48(new Rgb48((ushort)luminanceEqualized, (ushort)luminanceEqualized, (ushort)luminanceEqualized));
+ }
+ else
+ {
+ row[x].PackFromRgba32(new Rgba32((byte)luminanceEqualized, (byte)luminanceEqualized, (byte)luminanceEqualized));
+ }
}
}
}
+
+ ///
+ /// Convert the pixel values to grayscale using ITU-R Recommendation BT.709.
+ ///
+ /// The pixel to get the luminance from
+ /// Flag indicates, if its 16 bits per channel, otherwise its 8
+ /// Will store the pixel values in case of 8 bit per channel
+ /// Will store the pixel values in case of 16 bit per channel
+ private int GetLuminance(TPixel sourcePixel, bool is16bitPerChannel, ref Rgb24 rgb24, ref Rgb48 rgb48)
+ {
+ // Convert to grayscale using ITU-R Recommendation BT.709
+ int luminance;
+ if (is16bitPerChannel)
+ {
+ sourcePixel.ToRgb48(ref rgb48);
+ luminance = (int)((.2126F * rgb48.R) + (.7152F * rgb48.G) + (.0722F * rgb48.B));
+ }
+ else
+ {
+ sourcePixel.ToRgb24(ref rgb24);
+ luminance = (int)((.2126F * rgb24.R) + (.7152F * rgb24.G) + (.0722F * rgb24.B));
+ }
+
+ return luminance;
+ }
}
}
diff --git a/tests/ImageSharp.Tests/Processing/Contrast/HistogramEqualizationTests.cs b/tests/ImageSharp.Tests/Processing/Contrast/HistogramEqualizationTests.cs
index b5c584a55..db2282ccd 100644
--- a/tests/ImageSharp.Tests/Processing/Contrast/HistogramEqualizationTests.cs
+++ b/tests/ImageSharp.Tests/Processing/Contrast/HistogramEqualizationTests.cs
@@ -37,14 +37,14 @@ namespace SixLabors.ImageSharp.Tests.Processing.Contrast
byte[] expected = new byte[]
{
- 0, 12, 53, 32, 146, 53, 174, 53,
- 57, 32, 12, 227, 219, 202, 32, 154,
- 65, 85, 93, 239, 251, 227, 65, 158,
- 73, 146, 146, 247, 255, 235, 154, 130,
- 97, 166, 117, 231, 243, 210, 117, 117,
- 117, 190, 36, 190, 178, 93, 20, 170,
- 130, 202, 73, 20, 12, 53, 85, 194,
- 146, 206, 130, 117, 85, 166, 182, 215
+ 0, 12, 53, 32, 146, 53, 174, 53,
+ 57, 32, 12, 227, 219, 202, 32, 154,
+ 65, 85, 93, 239, 251, 227, 65, 158,
+ 73, 146, 146, 247, 255, 235, 154, 130,
+ 97, 166, 117, 231, 243, 210, 117, 117,
+ 117, 190, 36, 190, 178, 93, 20, 170,
+ 130, 202, 73, 20, 12, 53, 85, 194,
+ 146, 206, 130, 117, 85, 166, 182, 215
};
// act
@@ -58,7 +58,6 @@ namespace SixLabors.ImageSharp.Tests.Processing.Contrast
Rgba32 actual = image[x, y];
int diff = expected[y * 8 + x] - actual.R;
Assert.True(diff == 0);
- int foo = 2;
}
}
}