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>
/// <typeparam name="TPixel">The pixel format.</typeparam>
/// <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)
where TPixel : struct, IPixel<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
{
/// <summary>
/// Applies a global histogram equalization to the image.
/// </summary>
/// <typeparam name="TPixel">The pixel format.</typeparam>
internal class HistogramEqualizationProcessor<TPixel> : ImageProcessor<TPixel>
where TPixel : struct, IPixel<TPixel>
{
/// <inheritdoc/>
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;
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));
}
}
}
}
/// <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[]
{
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;
}
}
}

Loading…
Cancel
Save