Browse Source

Merge pull request #1641 from SixLabors/js/fix-1640

AdaptiveHistogramEqualizationProcessor. Fix for large images
pull/1643/head
James Jackson-South 5 years ago
committed by GitHub
parent
commit
841f8965be
No known key found for this signature in database GPG Key ID: 4AEE18F83AFDEB23
  1. 9
      src/ImageSharp/Processing/Processors/Normalization/AdaptiveHistogramEqualizationProcessor.cs
  2. 9
      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. 2
      tests/ImageSharp.Tests/Formats/Jpg/JpegEncoderTests.cs
  6. 39
      tests/ImageSharp.Tests/Processing/Normalization/HistogramEqualizationTests.cs

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);
}
}
}

9
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();

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);
}

2
tests/ImageSharp.Tests/Formats/Jpg/JpegEncoderTests.cs

@ -309,7 +309,7 @@ namespace SixLabors.ImageSharp.Tests.Formats.Jpg
Assert.Equal(values.Entries, actual.Entries);
}
[Theory]
[Theory(Skip = "TODO: Too Flaky")]
[InlineData(JpegSubsample.Ratio420, 0)]
[InlineData(JpegSubsample.Ratio420, 3)]
[InlineData(JpegSubsample.Ratio420, 10)]

39
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,43 @@ 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>
{
if (!TestEnvironment.Is64BitProcess)
{
return;
}
// https://github.com/SixLabors/ImageSharp/discussions/1640
// Test using isolated memory to ensure clean buffers for reference
provider.Configuration = Configuration.CreateDefaultInstance();
var options = new HistogramEqualizationOptions
{
Method = HistogramEqualizationMethod.AdaptiveTileInterpolation,
LuminanceLevels = 4096,
ClipHistogram = false,
ClipLimit = 350,
NumberOfTiles = 8
};
using Image<TPixel> image = provider.GetImage();
using Image<TPixel> referenceResult = image.Clone(ctx =>
{
ctx.HistogramEqualization(options);
ctx.Resize(image.Width / 4, image.Height / 4, KnownResamplers.Bicubic);
});
using Image<TPixel> processed = image.Clone(ctx =>
{
ctx.HistogramEqualization(options);
ctx.Resize(image.Width / 4, image.Height / 4, KnownResamplers.Bicubic);
});
ValidatorComparer.VerifySimilarity(referenceResult, processed);
}
}
}

Loading…
Cancel
Save