Browse Source

Ensure cdfMinSpan is cleared before use.

pull/1641/head
James Jackson-South 5 years ago
parent
commit
5ceba7116c
  1. 9
      src/ImageSharp/Processing/Processors/Normalization/AdaptiveHistogramEqualizationProcessor.cs
  2. 13
      src/ImageSharp/Processing/Processors/Normalization/AdaptiveHistogramEqualizationProcessor{TPixel}.cs
  3. 44
      src/ImageSharp/Processing/Processors/Normalization/HistogramEqualizationProcessor.cs
  4. 1
      src/ImageSharp/Processing/Processors/Normalization/HistogramEqualizationProcessor{TPixel}.cs
  5. 31
      tests/ImageSharp.Tests/Processing/Normalization/HistogramEqualizationTests.cs
  6. 3
      tests/Images/External/ReferenceOutput/HistogramEqualizationTests/Issue1640_L16_TestPattern5120x9234.png

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

@ -22,10 +22,7 @@ namespace SixLabors.ImageSharp.Processing.Processors.Normalization
bool clipHistogram,
int clipLimit,
int numberOfTiles)
: base(luminanceLevels, clipHistogram, clipLimit)
{
this.NumberOfTiles = numberOfTiles;
}
: base(luminanceLevels, clipHistogram, clipLimit) => this.NumberOfTiles = numberOfTiles;
/// <summary>
/// Gets the number of tiles the image is split into (horizontal and vertically) for the adaptive histogram equalization.
@ -34,8 +31,7 @@ namespace SixLabors.ImageSharp.Processing.Processors.Normalization
/// <inheritdoc />
public override IImageProcessor<TPixel> CreatePixelSpecificProcessor<TPixel>(Configuration configuration, Image<TPixel> source, Rectangle sourceRectangle)
{
return new AdaptiveHistogramEqualizationProcessor<TPixel>(
=> new AdaptiveHistogramEqualizationProcessor<TPixel>(
configuration,
this.LuminanceLevels,
this.ClipHistogram,
@ -43,6 +39,5 @@ namespace SixLabors.ImageSharp.Processing.Processors.Normalization
this.NumberOfTiles,
source,
sourceRectangle);
}
}
}

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

@ -459,10 +459,14 @@ namespace SixLabors.ImageSharp.Processing.Processors.Normalization
private readonly Configuration configuration;
private readonly MemoryAllocator memoryAllocator;
// Used for storing the minimum value for each CDF entry.
/// <summary>
/// Used for storing the minimum value for each CDF entry.
/// </summary>
private readonly Buffer2D<int> cdfMinBuffer2D;
// Used for storing the LUT for each CDF entry.
/// <summary>
/// Used for storing the LUT for each CDF entry.
/// </summary>
private readonly Buffer2D<int> cdfLutBuffer2D;
private readonly int pixelsInTile;
private readonly int sourceWidth;
@ -596,6 +600,7 @@ namespace SixLabors.ImageSharp.Processing.Processors.Normalization
int y = this.tileYStartPositions[index].y;
int endY = Math.Min(y + this.tileHeight, this.sourceHeight);
Span<int> cdfMinSpan = this.cdfMinBuffer2D.GetRowSpan(cdfY);
cdfMinSpan.Clear();
using IMemoryOwner<int> histogramBuffer = this.allocator.Allocate<int>(this.luminanceLevels);
Span<int> histogram = histogramBuffer.GetSpan();
@ -614,7 +619,9 @@ namespace SixLabors.ImageSharp.Processing.Processors.Normalization
for (int dx = x; dx < xlimit; dx++)
{
int luminance = GetLuminance(rowSpan[dx], this.luminanceLevels);
histogram[luminance]++;
// This is safe. The index maxes out to the span length.
Unsafe.Add(ref histogramBase, luminance)++;
}
}

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

@ -49,44 +49,18 @@ namespace SixLabors.ImageSharp.Processing.Processors.Normalization
/// </summary>
/// <param name="options">The <see cref="HistogramEqualizationOptions"/>.</param>
/// <returns>The <see cref="HistogramEqualizationProcessor"/>.</returns>
public static HistogramEqualizationProcessor FromOptions(HistogramEqualizationOptions options)
public static HistogramEqualizationProcessor FromOptions(HistogramEqualizationOptions options) => options.Method switch
{
HistogramEqualizationProcessor processor;
HistogramEqualizationMethod.Global
=> new GlobalHistogramEqualizationProcessor(options.LuminanceLevels, options.ClipHistogram, options.ClipLimit),
switch (options.Method)
{
case HistogramEqualizationMethod.Global:
processor = new GlobalHistogramEqualizationProcessor(
options.LuminanceLevels,
options.ClipHistogram,
options.ClipLimit);
break;
HistogramEqualizationMethod.AdaptiveTileInterpolation
=> new AdaptiveHistogramEqualizationProcessor(options.LuminanceLevels, options.ClipHistogram, options.ClipLimit, options.NumberOfTiles),
case HistogramEqualizationMethod.AdaptiveTileInterpolation:
processor = new AdaptiveHistogramEqualizationProcessor(
options.LuminanceLevels,
options.ClipHistogram,
options.ClipLimit,
options.NumberOfTiles);
break;
HistogramEqualizationMethod.AdaptiveSlidingWindow
=> new AdaptiveHistogramEqualizationSlidingWindowProcessor(options.LuminanceLevels, options.ClipHistogram, options.ClipLimit, options.NumberOfTiles),
case HistogramEqualizationMethod.AdaptiveSlidingWindow:
processor = new AdaptiveHistogramEqualizationSlidingWindowProcessor(
options.LuminanceLevels,
options.ClipHistogram,
options.ClipLimit,
options.NumberOfTiles);
break;
default:
processor = new GlobalHistogramEqualizationProcessor(
options.LuminanceLevels,
options.ClipHistogram,
options.ClipLimit);
break;
}
return processor;
}
_ => new GlobalHistogramEqualizationProcessor(options.LuminanceLevels, options.ClipHistogram, options.ClipLimit),
};
}
}

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

@ -142,6 +142,7 @@ namespace SixLabors.ImageSharp.Processing.Processors.Normalization
[MethodImpl(InliningOptions.ShortMethod)]
public static int GetLuminance(TPixel sourcePixel, int luminanceLevels)
{
// TODO: We need a bulk per span equivalent.
var vector = sourcePixel.ToVector4();
return ColorNumerics.GetBT709Luminance(ref vector, luminanceLevels);
}

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

@ -141,6 +141,7 @@ namespace SixLabors.ImageSharp.Tests.Processing.Normalization
/// See: https://github.com/SixLabors/ImageSharp/pull/984
/// </summary>
/// <typeparam name="TPixel">The pixel type of the image.</typeparam>
/// <param name="provider">The test image provider.</param>
[Theory]
[WithTestPatternImages(110, 110, PixelTypes.Rgb24)]
[WithTestPatternImages(170, 170, PixelTypes.Rgb24)]
@ -162,5 +163,35 @@ namespace SixLabors.ImageSharp.Tests.Processing.Normalization
image.CompareToReferenceOutput(ValidatorComparer, provider);
}
}
[Theory]
[WithTestPatternImages(5120, 9234, PixelTypes.L16)]
public unsafe void Issue1640<TPixel>(TestImageProvider<TPixel> provider)
where TPixel : unmanaged, IPixel<TPixel>
{
using Image<TPixel> image = provider.GetImage();
// https://github.com/SixLabors/ImageSharp/discussions/1640
for (int i = 0; i < 2; i++)
{
var options = new HistogramEqualizationOptions
{
Method = HistogramEqualizationMethod.AdaptiveTileInterpolation,
LuminanceLevels = 4096,
ClipHistogram = false,
ClipLimit = 350,
NumberOfTiles = 8
};
Image<TPixel> processed = image.Clone(ctx =>
{
ctx.HistogramEqualization(options);
ctx.Resize(image.Width / 4, image.Height / 4, KnownResamplers.Bicubic);
});
processed.DebugSave(provider);
processed.CompareToReferenceOutput(ValidatorComparer, provider);
}
}
}
}

3
tests/Images/External/ReferenceOutput/HistogramEqualizationTests/Issue1640_L16_TestPattern5120x9234.png

@ -0,0 +1,3 @@
version https://git-lfs.github.com/spec/v1
oid sha256:2e6bff82eaedcd43932a5bd11d1feeea2143f00ab2ee5fe0654a403bba9ba2de
size 424844
Loading…
Cancel
Save