Browse Source

Merge remote-tracking branch 'upstream/master' into LightnessProcessor

af/merge-core
James Jackson-South 7 years ago
parent
commit
68bf2ed650
  1. 8
      src/ImageSharp/Processing/Processors/Normalization/AdaptiveHistogramEqualizationProcessor.cs
  2. 8
      src/ImageSharp/Processing/Processors/Normalization/AdaptiveHistogramEqualizationProcessor{TPixel}.cs
  3. 8
      src/ImageSharp/Processing/Processors/Normalization/AdaptiveHistogramEqualizationSlidingWindowProcessor.cs
  4. 8
      src/ImageSharp/Processing/Processors/Normalization/AdaptiveHistogramEqualizationSlidingWindowProcessor{TPixel}.cs
  5. 8
      src/ImageSharp/Processing/Processors/Normalization/GlobalHistogramEqualizationProcessor.cs
  6. 8
      src/ImageSharp/Processing/Processors/Normalization/GlobalHistogramEqualizationProcessor{TPixel}.cs
  7. 18
      src/ImageSharp/Processing/Processors/Normalization/HistogramEqualizationOptions.cs
  8. 18
      src/ImageSharp/Processing/Processors/Normalization/HistogramEqualizationProcessor.cs
  9. 30
      src/ImageSharp/Processing/Processors/Normalization/HistogramEqualizationProcessor{TPixel}.cs
  10. 6
      tests/ImageSharp.Tests/Processing/Normalization/HistogramEqualizationTests.cs

8
src/ImageSharp/Processing/Processors/Normalization/AdaptiveHistogramEqualizationProcessor.cs

@ -17,14 +17,14 @@ namespace SixLabors.ImageSharp.Processing.Processors.Normalization
/// <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.</param> /// or 65536 for 16-bit grayscale images.</param>
/// <param name="clipHistogram">Indicating whether to clip the histogram bins at a specific value.</param> /// <param name="clipHistogram">Indicating whether to clip the histogram bins at a specific value.</param>
/// <param name="clipLimitPercentage">Histogram clip limit in percent of the total pixels in the tile. Histogram bins which exceed this limit, will be capped at this value.</param> /// <param name="clipLimit">The histogram clip limit. Histogram bins which exceed this limit, will be capped at this value.</param>
/// <param name="numberOfTiles">The number of tiles the image is split into (horizontal and vertically). Minimum value is 2. Maximum value is 100.</param> /// <param name="numberOfTiles">The number of tiles the image is split into (horizontal and vertically). Minimum value is 2. Maximum value is 100.</param>
public AdaptiveHistogramEqualizationProcessor( public AdaptiveHistogramEqualizationProcessor(
int luminanceLevels, int luminanceLevels,
bool clipHistogram, bool clipHistogram,
float clipLimitPercentage, int clipLimit,
int numberOfTiles) int numberOfTiles)
: base(luminanceLevels, clipHistogram, clipLimitPercentage) : base(luminanceLevels, clipHistogram, clipLimit)
{ {
this.NumberOfTiles = numberOfTiles; this.NumberOfTiles = numberOfTiles;
} }
@ -40,7 +40,7 @@ namespace SixLabors.ImageSharp.Processing.Processors.Normalization
return new AdaptiveHistogramEqualizationProcessor<TPixel>( return new AdaptiveHistogramEqualizationProcessor<TPixel>(
this.LuminanceLevels, this.LuminanceLevels,
this.ClipHistogram, this.ClipHistogram,
this.ClipLimitPercentage, this.ClipLimit,
this.NumberOfTiles, this.NumberOfTiles,
source, source,
sourceRectangle); sourceRectangle);

8
src/ImageSharp/Processing/Processors/Normalization/AdaptiveHistogramEqualizationProcessor{TPixel}.cs

@ -30,18 +30,18 @@ namespace SixLabors.ImageSharp.Processing.Processors.Normalization
/// <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.</param> /// or 65536 for 16-bit grayscale images.</param>
/// <param name="clipHistogram">Indicating whether to clip the histogram bins at a specific value.</param> /// <param name="clipHistogram">Indicating whether to clip the histogram bins at a specific value.</param>
/// <param name="clipLimitPercentage">Histogram clip limit in percent of the total pixels in the tile. Histogram bins which exceed this limit, will be capped at this value.</param> /// <param name="clipLimit">The histogram clip limit. Histogram bins which exceed this limit, will be capped at this value.</param>
/// <param name="tiles">The number of tiles the image is split into (horizontal and vertically). Minimum value is 2. Maximum value is 100.</param> /// <param name="tiles">The number of tiles the image is split into (horizontal and vertically). Minimum value is 2. Maximum value is 100.</param>
/// <param name="source">The source <see cref="Image{TPixel}"/> for the current processor instance.</param> /// <param name="source">The source <see cref="Image{TPixel}"/> for the current processor instance.</param>
/// <param name="sourceRectangle">The source area to process for the current processor instance.</param> /// <param name="sourceRectangle">The source area to process for the current processor instance.</param>
public AdaptiveHistogramEqualizationProcessor( public AdaptiveHistogramEqualizationProcessor(
int luminanceLevels, int luminanceLevels,
bool clipHistogram, bool clipHistogram,
float clipLimitPercentage, int clipLimit,
int tiles, int tiles,
Image<TPixel> source, Image<TPixel> source,
Rectangle sourceRectangle) Rectangle sourceRectangle)
: base(luminanceLevels, clipHistogram, clipLimitPercentage, source, sourceRectangle) : base(luminanceLevels, clipHistogram, clipLimit, source, sourceRectangle)
{ {
Guard.MustBeGreaterThanOrEqualTo(tiles, 2, nameof(tiles)); Guard.MustBeGreaterThanOrEqualTo(tiles, 2, nameof(tiles));
Guard.MustBeLessThanOrEqualTo(tiles, 100, nameof(tiles)); Guard.MustBeLessThanOrEqualTo(tiles, 100, nameof(tiles));
@ -512,7 +512,7 @@ namespace SixLabors.ImageSharp.Processing.Processors.Normalization
if (processor.ClipHistogramEnabled) if (processor.ClipHistogramEnabled)
{ {
processor.ClipHistogram(histogram, processor.ClipLimitPercentage, this.pixelsInTile); processor.ClipHistogram(histogram, processor.ClipLimit);
} }
Unsafe.Add(ref cdfMinBase, cdfX) = processor.CalculateCdf(ref cdfBase, ref histogramBase, histogram.Length - 1); Unsafe.Add(ref cdfMinBase, cdfX) = processor.CalculateCdf(ref cdfBase, ref histogramBase, histogram.Length - 1);

8
src/ImageSharp/Processing/Processors/Normalization/AdaptiveHistogramEqualizationSlidingWindowProcessor.cs

@ -16,14 +16,14 @@ namespace SixLabors.ImageSharp.Processing.Processors.Normalization
/// <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.</param> /// or 65536 for 16-bit grayscale images.</param>
/// <param name="clipHistogram">Indicating whether to clip the histogram bins at a specific value.</param> /// <param name="clipHistogram">Indicating whether to clip the histogram bins at a specific value.</param>
/// <param name="clipLimitPercentage">Histogram clip limit in percent of the total pixels in the tile. Histogram bins which exceed this limit, will be capped at this value.</param> /// <param name="clipLimit">The histogram clip limit. Histogram bins which exceed this limit, will be capped at this value.</param>
/// <param name="numberOfTiles">The number of tiles the image is split into (horizontal and vertically). Minimum value is 2. Maximum value is 100.</param> /// <param name="numberOfTiles">The number of tiles the image is split into (horizontal and vertically). Minimum value is 2. Maximum value is 100.</param>
public AdaptiveHistogramEqualizationSlidingWindowProcessor( public AdaptiveHistogramEqualizationSlidingWindowProcessor(
int luminanceLevels, int luminanceLevels,
bool clipHistogram, bool clipHistogram,
float clipLimitPercentage, int clipLimit,
int numberOfTiles) int numberOfTiles)
: base(luminanceLevels, clipHistogram, clipLimitPercentage) : base(luminanceLevels, clipHistogram, clipLimit)
{ {
this.NumberOfTiles = numberOfTiles; this.NumberOfTiles = numberOfTiles;
} }
@ -39,7 +39,7 @@ namespace SixLabors.ImageSharp.Processing.Processors.Normalization
return new AdaptiveHistogramEqualizationSlidingWindowProcessor<TPixel>( return new AdaptiveHistogramEqualizationSlidingWindowProcessor<TPixel>(
this.LuminanceLevels, this.LuminanceLevels,
this.ClipHistogram, this.ClipHistogram,
this.ClipLimitPercentage, this.ClipLimit,
this.NumberOfTiles, this.NumberOfTiles,
source, source,
sourceRectangle); sourceRectangle);

8
src/ImageSharp/Processing/Processors/Normalization/AdaptiveHistogramEqualizationSlidingWindowProcessor{TPixel}.cs

@ -29,18 +29,18 @@ namespace SixLabors.ImageSharp.Processing.Processors.Normalization
/// <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.</param> /// or 65536 for 16-bit grayscale images.</param>
/// <param name="clipHistogram">Indicating whether to clip the histogram bins at a specific value.</param> /// <param name="clipHistogram">Indicating whether to clip the histogram bins at a specific value.</param>
/// <param name="clipLimitPercentage">Histogram clip limit in percent of the total pixels in the tile. Histogram bins which exceed this limit, will be capped at this value.</param> /// <param name="clipLimit">The histogram clip limit. Histogram bins which exceed this limit, will be capped at this value.</param>
/// <param name="tiles">The number of tiles the image is split into (horizontal and vertically). Minimum value is 2. Maximum value is 100.</param> /// <param name="tiles">The number of tiles the image is split into (horizontal and vertically). Minimum value is 2. Maximum value is 100.</param>
/// <param name="source">The source <see cref="Image{TPixel}"/> for the current processor instance.</param> /// <param name="source">The source <see cref="Image{TPixel}"/> for the current processor instance.</param>
/// <param name="sourceRectangle">The source area to process for the current processor instance.</param> /// <param name="sourceRectangle">The source area to process for the current processor instance.</param>
public AdaptiveHistogramEqualizationSlidingWindowProcessor( public AdaptiveHistogramEqualizationSlidingWindowProcessor(
int luminanceLevels, int luminanceLevels,
bool clipHistogram, bool clipHistogram,
float clipLimitPercentage, int clipLimit,
int tiles, int tiles,
Image<TPixel> source, Image<TPixel> source,
Rectangle sourceRectangle) Rectangle sourceRectangle)
: base(luminanceLevels, clipHistogram, clipLimitPercentage, source, sourceRectangle) : base(luminanceLevels, clipHistogram, clipLimit, source, sourceRectangle)
{ {
Guard.MustBeGreaterThanOrEqualTo(tiles, 2, nameof(tiles)); Guard.MustBeGreaterThanOrEqualTo(tiles, 2, nameof(tiles));
Guard.MustBeLessThanOrEqualTo(tiles, 100, nameof(tiles)); Guard.MustBeLessThanOrEqualTo(tiles, 100, nameof(tiles));
@ -210,7 +210,7 @@ namespace SixLabors.ImageSharp.Processing.Processors.Normalization
{ {
// Clipping the histogram, but doing it on a copy to keep the original un-clipped values for the next iteration. // Clipping the histogram, but doing it on a copy to keep the original un-clipped values for the next iteration.
histogram.CopyTo(histogramCopy); histogram.CopyTo(histogramCopy);
this.ClipHistogram(histogramCopy, this.ClipLimitPercentage, swInfos.PixelInTile); this.ClipHistogram(histogramCopy, this.ClipLimit);
} }
// Calculate the cumulative distribution function, which will map each input pixel in the current tile to a new value. // Calculate the cumulative distribution function, which will map each input pixel in the current tile to a new value.

8
src/ImageSharp/Processing/Processors/Normalization/GlobalHistogramEqualizationProcessor.cs

@ -15,9 +15,9 @@ namespace SixLabors.ImageSharp.Processing.Processors.Normalization
/// </summary> /// </summary>
/// <param name="luminanceLevels">The number of luminance levels.</param> /// <param name="luminanceLevels">The number of luminance levels.</param>
/// <param name="clipHistogram">A value indicating whether to clip the histogram bins at a specific value.</param> /// <param name="clipHistogram">A value indicating whether to clip the histogram bins at a specific value.</param>
/// <param name="clipLimitPercentage">The histogram clip limit in percent of the total pixels in the tile. Histogram bins which exceed this limit, will be capped at this value.</param> /// <param name="clipLimit">The histogram clip limit. Histogram bins which exceed this limit, will be capped at this value.</param>
public GlobalHistogramEqualizationProcessor(int luminanceLevels, bool clipHistogram, float clipLimitPercentage) public GlobalHistogramEqualizationProcessor(int luminanceLevels, bool clipHistogram, int clipLimit)
: base(luminanceLevels, clipHistogram, clipLimitPercentage) : base(luminanceLevels, clipHistogram, clipLimit)
{ {
} }
@ -27,7 +27,7 @@ namespace SixLabors.ImageSharp.Processing.Processors.Normalization
return new GlobalHistogramEqualizationProcessor<TPixel>( return new GlobalHistogramEqualizationProcessor<TPixel>(
this.LuminanceLevels, this.LuminanceLevels,
this.ClipHistogram, this.ClipHistogram,
this.ClipLimitPercentage, this.ClipLimit,
source, source,
sourceRectangle); sourceRectangle);
} }

8
src/ImageSharp/Processing/Processors/Normalization/GlobalHistogramEqualizationProcessor{TPixel}.cs

@ -31,16 +31,16 @@ namespace SixLabors.ImageSharp.Processing.Processors.Normalization
/// or 65536 for 16-bit grayscale images. /// or 65536 for 16-bit grayscale images.
/// </param> /// </param>
/// <param name="clipHistogram">Indicating whether to clip the histogram bins at a specific value.</param> /// <param name="clipHistogram">Indicating whether to clip the histogram bins at a specific value.</param>
/// <param name="clipLimitPercentage">Histogram clip limit in percent of the total pixels. Histogram bins which exceed this limit, will be capped at this value.</param> /// <param name="clipLimit">The histogram clip limit. Histogram bins which exceed this limit, will be capped at this value.</param>
/// <param name="source">The source <see cref="Image{TPixel}"/> for the current processor instance.</param> /// <param name="source">The source <see cref="Image{TPixel}"/> for the current processor instance.</param>
/// <param name="sourceRectangle">The source area to process for the current processor instance.</param> /// <param name="sourceRectangle">The source area to process for the current processor instance.</param>
public GlobalHistogramEqualizationProcessor( public GlobalHistogramEqualizationProcessor(
int luminanceLevels, int luminanceLevels,
bool clipHistogram, bool clipHistogram,
float clipLimitPercentage, int clipLimit,
Image<TPixel> source, Image<TPixel> source,
Rectangle sourceRectangle) Rectangle sourceRectangle)
: base(luminanceLevels, clipHistogram, clipLimitPercentage, source, sourceRectangle) : base(luminanceLevels, clipHistogram, clipLimit, source, sourceRectangle)
{ {
} }
@ -76,7 +76,7 @@ namespace SixLabors.ImageSharp.Processing.Processors.Normalization
Span<int> histogram = histogramBuffer.GetSpan(); Span<int> histogram = histogramBuffer.GetSpan();
if (this.ClipHistogramEnabled) if (this.ClipHistogramEnabled)
{ {
this.ClipHistogram(histogram, this.ClipLimitPercentage, numberOfPixels); this.ClipHistogram(histogram, this.ClipLimit);
} }
// 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.

18
src/ImageSharp/Processing/Processors/Normalization/HistogramEqualizationOptions.cs

@ -20,7 +20,8 @@ namespace SixLabors.ImageSharp.Processing.Processors.Normalization
/// <summary> /// <summary>
/// Gets or sets the number of different luminance levels. Typical values are 256 for 8-bit grayscale images /// Gets or sets the number of different luminance levels. Typical values are 256 for 8-bit grayscale images
/// or 65536 for 16-bit grayscale images. Defaults to 256. /// or 65536 for 16-bit grayscale images.
/// Defaults to 256.
/// </summary> /// </summary>
public int LuminanceLevels { get; set; } = 256; public int LuminanceLevels { get; set; } = 256;
@ -32,14 +33,19 @@ namespace SixLabors.ImageSharp.Processing.Processors.Normalization
public bool ClipHistogram { get; set; } = false; public bool ClipHistogram { get; set; } = false;
/// <summary> /// <summary>
/// Gets or sets the histogram clip limit in percent of the total pixels in a tile. Histogram bins which exceed this limit, will be capped at this value. /// Gets or sets the histogram clip limit. Adaptive histogram equalization may cause noise to be amplified in near constant
/// Defaults to 0.035f. /// regions. To reduce this problem, histogram bins which exceed a given limit will be capped at this value. The exceeding values
/// will be redistributed equally to all other bins. The clipLimit depends on the size of the tiles the image is split into
/// and therefore the image size itself.
/// Defaults to 350.
/// </summary> /// </summary>
public float ClipLimitPercentage { get; set; } = 0.035f; /// <remarks>For more information, see also: https://en.wikipedia.org/wiki/Adaptive_histogram_equalization#Contrast_Limited_AHE</remarks>
public int ClipLimit { get; set; } = 350;
/// <summary> /// <summary>
/// Gets or sets the number of tiles the image is split into (horizontal and vertically) for the adaptive histogram equalization. Defaults to 10. /// Gets or sets the number of tiles the image is split into (horizontal and vertically) for the adaptive histogram equalization.
/// Defaults to 8.
/// </summary> /// </summary>
public int NumberOfTiles { get; set; } = 10; public int NumberOfTiles { get; set; } = 8;
} }
} }

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

@ -17,12 +17,12 @@ namespace SixLabors.ImageSharp.Processing.Processors.Normalization
/// <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.</param> /// or 65536 for 16-bit grayscale images.</param>
/// <param name="clipHistogram">Indicates, if histogram bins should be clipped.</param> /// <param name="clipHistogram">Indicates, if histogram bins should be clipped.</param>
/// <param name="clipLimitPercentage">Histogram clip limit in percent of the total pixels in the tile. Histogram bins which exceed this limit, will be capped at this value.</param> /// <param name="clipLimit">The histogram clip limit. Histogram bins which exceed this limit, will be capped at this value.</param>
protected HistogramEqualizationProcessor(int luminanceLevels, bool clipHistogram, float clipLimitPercentage) protected HistogramEqualizationProcessor(int luminanceLevels, bool clipHistogram, int clipLimit)
{ {
this.LuminanceLevels = luminanceLevels; this.LuminanceLevels = luminanceLevels;
this.ClipHistogram = clipHistogram; this.ClipHistogram = clipHistogram;
this.ClipLimitPercentage = clipLimitPercentage; this.ClipLimit = clipLimit;
} }
/// <summary> /// <summary>
@ -36,9 +36,9 @@ namespace SixLabors.ImageSharp.Processing.Processors.Normalization
public bool ClipHistogram { get; } public bool ClipHistogram { get; }
/// <summary> /// <summary>
/// Gets the histogram clip limit in percent of the total pixels in the tile. Histogram bins which exceed this limit, will be capped at this value. /// Gets the histogram clip limit. Histogram bins which exceed this limit, will be capped at this value.
/// </summary> /// </summary>
public float ClipLimitPercentage { get; } public int ClipLimit { get; }
/// <inheritdoc /> /// <inheritdoc />
public abstract IImageProcessor<TPixel> CreatePixelSpecificProcessor<TPixel>(Image<TPixel> source, Rectangle sourceRectangle) public abstract IImageProcessor<TPixel> CreatePixelSpecificProcessor<TPixel>(Image<TPixel> source, Rectangle sourceRectangle)
@ -60,14 +60,14 @@ namespace SixLabors.ImageSharp.Processing.Processors.Normalization
processor = new GlobalHistogramEqualizationProcessor( processor = new GlobalHistogramEqualizationProcessor(
options.LuminanceLevels, options.LuminanceLevels,
options.ClipHistogram, options.ClipHistogram,
options.ClipLimitPercentage); options.ClipLimit);
break; break;
case HistogramEqualizationMethod.AdaptiveTileInterpolation: case HistogramEqualizationMethod.AdaptiveTileInterpolation:
processor = new AdaptiveHistogramEqualizationProcessor( processor = new AdaptiveHistogramEqualizationProcessor(
options.LuminanceLevels, options.LuminanceLevels,
options.ClipHistogram, options.ClipHistogram,
options.ClipLimitPercentage, options.ClipLimit,
options.NumberOfTiles); options.NumberOfTiles);
break; break;
@ -75,7 +75,7 @@ namespace SixLabors.ImageSharp.Processing.Processors.Normalization
processor = new AdaptiveHistogramEqualizationSlidingWindowProcessor( processor = new AdaptiveHistogramEqualizationSlidingWindowProcessor(
options.LuminanceLevels, options.LuminanceLevels,
options.ClipHistogram, options.ClipHistogram,
options.ClipLimitPercentage, options.ClipLimit,
options.NumberOfTiles); options.NumberOfTiles);
break; break;
@ -83,7 +83,7 @@ namespace SixLabors.ImageSharp.Processing.Processors.Normalization
processor = new GlobalHistogramEqualizationProcessor( processor = new GlobalHistogramEqualizationProcessor(
options.LuminanceLevels, options.LuminanceLevels,
options.ClipHistogram, options.ClipHistogram,
options.ClipLimitPercentage); options.ClipLimit);
break; break;
} }

30
src/ImageSharp/Processing/Processors/Normalization/HistogramEqualizationProcessor{TPixel}.cs

@ -26,24 +26,24 @@ namespace SixLabors.ImageSharp.Processing.Processors.Normalization
/// <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.</param> /// or 65536 for 16-bit grayscale images.</param>
/// <param name="clipHistogram">Indicates, if histogram bins should be clipped.</param> /// <param name="clipHistogram">Indicates, if histogram bins should be clipped.</param>
/// <param name="clipLimitPercentage">Histogram clip limit in percent of the total pixels in the tile. Histogram bins which exceed this limit, will be capped at this value.</param> /// <param name="clipLimit">The histogram clip limit. Histogram bins which exceed this limit, will be capped at this value.</param>
/// <param name="source">The source <see cref="Image{TPixel}"/> for the current processor instance.</param> /// <param name="source">The source <see cref="Image{TPixel}"/> for the current processor instance.</param>
/// <param name="sourceRectangle">The source area to process for the current processor instance.</param> /// <param name="sourceRectangle">The source area to process for the current processor instance.</param>
protected HistogramEqualizationProcessor( protected HistogramEqualizationProcessor(
int luminanceLevels, int luminanceLevels,
bool clipHistogram, bool clipHistogram,
float clipLimitPercentage, int clipLimit,
Image<TPixel> source, Image<TPixel> source,
Rectangle sourceRectangle) Rectangle sourceRectangle)
: base(source, sourceRectangle) : base(source, sourceRectangle)
{ {
Guard.MustBeGreaterThan(luminanceLevels, 0, nameof(luminanceLevels)); Guard.MustBeGreaterThan(luminanceLevels, 0, nameof(luminanceLevels));
Guard.MustBeGreaterThan(clipLimitPercentage, 0F, nameof(clipLimitPercentage)); Guard.MustBeGreaterThan(clipLimit, 1, nameof(clipLimit));
this.LuminanceLevels = luminanceLevels; this.LuminanceLevels = luminanceLevels;
this.luminanceLevelsFloat = luminanceLevels; this.luminanceLevelsFloat = luminanceLevels;
this.ClipHistogramEnabled = clipHistogram; this.ClipHistogramEnabled = clipHistogram;
this.ClipLimitPercentage = clipLimitPercentage; this.ClipLimit = clipLimit;
} }
/// <summary> /// <summary>
@ -57,9 +57,9 @@ namespace SixLabors.ImageSharp.Processing.Processors.Normalization
public bool ClipHistogramEnabled { get; } public bool ClipHistogramEnabled { get; }
/// <summary> /// <summary>
/// Gets the histogram clip limit in percent of the total pixels in the tile. Histogram bins which exceed this limit, will be capped at this value. /// Gets the histogram clip limit. Histogram bins which exceed this limit, will be capped at this value.
/// </summary> /// </summary>
public float ClipLimitPercentage { get; } public int ClipLimit { get; }
/// <summary> /// <summary>
/// Calculates the cumulative distribution function. /// Calculates the cumulative distribution function.
@ -96,11 +96,9 @@ namespace SixLabors.ImageSharp.Processing.Processors.Normalization
/// the values over the clip limit to all other bins equally. /// the values over the clip limit to all other bins equally.
/// </summary> /// </summary>
/// <param name="histogram">The histogram to apply the clipping.</param> /// <param name="histogram">The histogram to apply the clipping.</param>
/// <param name="clipLimitPercentage">Histogram clip limit in percent of the total pixels in the tile. Histogram bins which exceed this limit, will be capped at this value.</param> /// <param name="clipLimit">Histogram clip limit. Histogram bins which exceed this limit, will be capped at this value.</param>
/// <param name="pixelCount">The numbers of pixels inside the tile.</param> public void ClipHistogram(Span<int> histogram, int clipLimit)
public void ClipHistogram(Span<int> histogram, float clipLimitPercentage, int pixelCount)
{ {
int clipLimit = (int)MathF.Round(pixelCount * clipLimitPercentage);
int sumOverClip = 0; int sumOverClip = 0;
ref int histogramBase = ref MemoryMarshal.GetReference(histogram); ref int histogramBase = ref MemoryMarshal.GetReference(histogram);
@ -114,6 +112,7 @@ namespace SixLabors.ImageSharp.Processing.Processors.Normalization
} }
} }
// Redistribute the clipped pixels over all bins of the histogram.
int addToEachBin = sumOverClip > 0 ? (int)MathF.Floor(sumOverClip / this.luminanceLevelsFloat) : 0; int addToEachBin = sumOverClip > 0 ? (int)MathF.Floor(sumOverClip / this.luminanceLevelsFloat) : 0;
if (addToEachBin > 0) if (addToEachBin > 0)
{ {
@ -122,6 +121,17 @@ namespace SixLabors.ImageSharp.Processing.Processors.Normalization
Unsafe.Add(ref histogramBase, i) += addToEachBin; Unsafe.Add(ref histogramBase, i) += addToEachBin;
} }
} }
int residual = sumOverClip - (addToEachBin * this.LuminanceLevels);
if (residual != 0)
{
int residualStep = Math.Max(this.LuminanceLevels / residual, 1);
for (int i = 0; i < this.LuminanceLevels && residual > 0; i += residualStep, residual--)
{
ref int histogramLevel = ref Unsafe.Add(ref histogramBase, i);
histogramLevel++;
}
}
} }
/// <summary> /// <summary>

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

@ -121,8 +121,8 @@ namespace SixLabors.ImageSharp.Tests.Processing.Normalization
/// See: https://github.com/SixLabors/ImageSharp/pull/984 /// See: https://github.com/SixLabors/ImageSharp/pull/984
/// </summary> /// </summary>
[Theory] [Theory]
[WithTestPatternImages(110, 110, PixelTypes.Rgba32)] [WithTestPatternImages(110, 110, PixelTypes.Rgb24)]
[WithTestPatternImages(170, 170, PixelTypes.Rgba32)] [WithTestPatternImages(170, 170, PixelTypes.Rgb24)]
public void Issue984<TPixel>(TestImageProvider<TPixel> provider) public void Issue984<TPixel>(TestImageProvider<TPixel> provider)
where TPixel : struct, IPixel<TPixel> where TPixel : struct, IPixel<TPixel>
{ {
@ -133,10 +133,12 @@ namespace SixLabors.ImageSharp.Tests.Processing.Normalization
Method = HistogramEqualizationMethod.AdaptiveTileInterpolation, Method = HistogramEqualizationMethod.AdaptiveTileInterpolation,
LuminanceLevels = 256, LuminanceLevels = 256,
ClipHistogram = true, ClipHistogram = true,
ClipLimit = 5,
NumberOfTiles = 10 NumberOfTiles = 10
}; };
image.Mutate(x => x.HistogramEqualization(options)); image.Mutate(x => x.HistogramEqualization(options));
image.DebugSave(provider); image.DebugSave(provider);
image.CompareToReferenceOutput(ValidatorComparer, provider);
} }
} }
} }

Loading…
Cancel
Save