Browse Source

added support for 16 bit greyscale

af/merge-core
popow 8 years ago
parent
commit
72a4ee4828
  1. 2
      src/ImageSharp/Processing/Contrast/HistogramEqualizationExtension.cs
  2. 56
      src/ImageSharp/Processing/Contrast/HistogramEqualizationProcessor.cs
  3. 17
      tests/ImageSharp.Tests/Processing/Contrast/HistogramEqualizationTests.cs

2
src/ImageSharp/Processing/Contrast/HistogramEqualizationExtension.cs

@ -15,7 +15,7 @@ namespace SixLabors.ImageSharp.Processing.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>
/// <returns>The <see cref="Image{TPixel}"/>.</returns> /// <returns>A histogram equalized grayscale image.</returns>
public static IImageProcessingContext<TPixel> HistogramEqualization<TPixel>(this IImageProcessingContext<TPixel> source) public static IImageProcessingContext<TPixel> HistogramEqualization<TPixel>(this IImageProcessingContext<TPixel> source)
where TPixel : struct, IPixel<TPixel> where TPixel : struct, IPixel<TPixel>
=> source.ApplyProcessor(new HistogramEqualizationProcessor<TPixel>()); => source.ApplyProcessor(new HistogramEqualizationProcessor<TPixel>());

56
src/ImageSharp/Processing/Contrast/HistogramEqualizationProcessor.cs

@ -9,17 +9,23 @@ using SixLabors.Primitives;
namespace SixLabors.ImageSharp.Processing.Contrast namespace SixLabors.ImageSharp.Processing.Contrast
{ {
/// <summary>
/// Applies a global histogram equalization to the image.
/// </summary>
/// <typeparam name="TPixel">The pixel format.</typeparam>
internal class HistogramEqualizationProcessor<TPixel> : ImageProcessor<TPixel> internal class HistogramEqualizationProcessor<TPixel> : ImageProcessor<TPixel>
where TPixel : struct, IPixel<TPixel> where TPixel : struct, IPixel<TPixel>
{ {
/// <inheritdoc/> /// <inheritdoc/>
protected override void OnFrameApply(ImageFrame<TPixel> source, Rectangle sourceRectangle, Configuration configuration) protected override void OnFrameApply(ImageFrame<TPixel> source, Rectangle sourceRectangle, Configuration configuration)
{ {
var rgb = default(Rgb24); var rgb48 = default(Rgb48);
var rgb24 = default(Rgb24);
int numberOfPixels = source.Width * source.Height; int numberOfPixels = source.Width * source.Height;
bool is16bitPerChannel = typeof(TPixel) == typeof(Rgb48) || typeof(TPixel) == typeof(Rgba64);
// build the histogram of the grayscale levels // build the histogram of the grayscale levels
int luminanceLevels = 256; int luminanceLevels = is16bitPerChannel ? 65536 : 256;
int[] histogram = new int[luminanceLevels]; int[] histogram = new int[luminanceLevels];
for (int y = 0; y < source.Height; y++) 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++) for (int x = 0; x < source.Width; x++)
{ {
TPixel sourcePixel = row[x]; TPixel sourcePixel = row[x];
sourcePixel.ToRgb24(ref rgb); int luminance = this.GetLuminance(sourcePixel, is16bitPerChannel, ref rgb24, ref rgb48);
// Convert to grayscale using ITU-R Recommendation BT.709 if required
int luminance = (int)((.2126F * rgb.R) + (.7152F * rgb.G) + (.0722F * rgb.B));
histogram[luminance]++; 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[] cdf = new int[luminanceLevels];
int histSum = 0; int histSum = 0;
for (int i = 0; i < histogram.Length; i++) 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++) for (int x = 0; x < source.Width; x++)
{ {
TPixel sourcePixel = row[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; double luminanceEqualized = (lut[luminance] / numberOfPixelsMinusCdfMin) * luminanceLevelsMinusOne;
luminanceEqualized = Math.Round(luminanceEqualized); 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));
}
} }
} }
} }
/// <summary>
/// Convert the pixel values to grayscale using ITU-R Recommendation BT.709.
/// </summary>
/// <param name="sourcePixel">The pixel to get the luminance from</param>
/// <param name="is16bitPerChannel">Flag indicates, if its 16 bits per channel, otherwise its 8</param>
/// <param name="rgb24">Will store the pixel values in case of 8 bit per channel</param>
/// <param name="rgb48">Will store the pixel values in case of 16 bit per channel</param>
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;
}
} }
} }

17
tests/ImageSharp.Tests/Processing/Contrast/HistogramEqualizationTests.cs

@ -37,14 +37,14 @@ namespace SixLabors.ImageSharp.Tests.Processing.Contrast
byte[] expected = new byte[] byte[] expected = new byte[]
{ {
0, 12, 53, 32, 146, 53, 174, 53, 0, 12, 53, 32, 146, 53, 174, 53,
57, 32, 12, 227, 219, 202, 32, 154, 57, 32, 12, 227, 219, 202, 32, 154,
65, 85, 93, 239, 251, 227, 65, 158, 65, 85, 93, 239, 251, 227, 65, 158,
73, 146, 146, 247, 255, 235, 154, 130, 73, 146, 146, 247, 255, 235, 154, 130,
97, 166, 117, 231, 243, 210, 117, 117, 97, 166, 117, 231, 243, 210, 117, 117,
117, 190, 36, 190, 178, 93, 20, 170, 117, 190, 36, 190, 178, 93, 20, 170,
130, 202, 73, 20, 12, 53, 85, 194, 130, 202, 73, 20, 12, 53, 85, 194,
146, 206, 130, 117, 85, 166, 182, 215 146, 206, 130, 117, 85, 166, 182, 215
}; };
// act // act
@ -58,7 +58,6 @@ namespace SixLabors.ImageSharp.Tests.Processing.Contrast
Rgba32 actual = image[x, y]; Rgba32 actual = image[x, y];
int diff = expected[y * 8 + x] - actual.R; int diff = expected[y * 8 + x] - actual.R;
Assert.True(diff == 0); Assert.True(diff == 0);
int foo = 2;
} }
} }
} }

Loading…
Cancel
Save