Browse Source

Bulk pixel conversion

pull/2241/head
Ynse Hoornenborg 3 years ago
parent
commit
1ec6da892a
  1. 62
      src/ImageSharp/Processing/Processors/Normalization/AutoLevelProcessor{TPixel}.cs
  2. 33
      src/ImageSharp/Processing/Processors/Normalization/GlobalHistogramEqualizationProcessor{TPixel}.cs
  3. 23
      src/ImageSharp/Processing/Processors/Normalization/GrayscaleLevelsRowOperation{TPixel}.cs

62
src/ImageSharp/Processing/Processors/Normalization/AutoLevelProcessor{TPixel}.cs

@ -59,8 +59,8 @@ internal class AutoLevelProcessor<TPixel> : HistogramEqualizationProcessor<TPixe
using IMemoryOwner<int> histogramBuffer = memoryAllocator.Allocate<int>(this.LuminanceLevels, AllocationOptions.Clean);
// Build the histogram of the grayscale levels.
var grayscaleOperation = new GrayscaleLevelsRowOperation<TPixel>(interest, histogramBuffer, source.PixelBuffer, this.LuminanceLevels);
ParallelRowIterator.IterateRows(
var grayscaleOperation = new GrayscaleLevelsRowOperation<TPixel>(this.Configuration, interest, histogramBuffer, source.PixelBuffer, this.LuminanceLevels);
ParallelRowIterator.IterateRows<GrayscaleLevelsRowOperation<TPixel>, Vector4>(
this.Configuration,
interest,
in grayscaleOperation);
@ -83,16 +83,16 @@ internal class AutoLevelProcessor<TPixel> : HistogramEqualizationProcessor<TPixe
if (this.SyncChannels)
{
var cdfOperation = new SynchronizedChannelsRowOperation(interest, cdfBuffer, source.PixelBuffer, this.LuminanceLevels, numberOfPixelsMinusCdfMin);
ParallelRowIterator.IterateRows(
var cdfOperation = new SynchronizedChannelsRowOperation(this.Configuration, interest, cdfBuffer, source.PixelBuffer, this.LuminanceLevels, numberOfPixelsMinusCdfMin);
ParallelRowIterator.IterateRows<SynchronizedChannelsRowOperation, Vector4>(
this.Configuration,
interest,
in cdfOperation);
}
else
{
var cdfOperation = new SeperateChannelsRowOperation(interest, cdfBuffer, source.PixelBuffer, this.LuminanceLevels, numberOfPixelsMinusCdfMin);
ParallelRowIterator.IterateRows(
var cdfOperation = new SeperateChannelsRowOperation(this.Configuration, interest, cdfBuffer, source.PixelBuffer, this.LuminanceLevels, numberOfPixelsMinusCdfMin);
ParallelRowIterator.IterateRows<SeperateChannelsRowOperation, Vector4>(
this.Configuration,
interest,
in cdfOperation);
@ -102,8 +102,9 @@ internal class AutoLevelProcessor<TPixel> : HistogramEqualizationProcessor<TPixe
/// <summary>
/// A <see langword="struct"/> implementing the cdf logic for synchronized color channels.
/// </summary>
private readonly struct SynchronizedChannelsRowOperation : IRowOperation
private readonly struct SynchronizedChannelsRowOperation : IRowOperation<Vector4>
{
private readonly Configuration configuration;
private readonly Rectangle bounds;
private readonly IMemoryOwner<int> cdfBuffer;
private readonly Buffer2D<TPixel> source;
@ -112,12 +113,14 @@ internal class AutoLevelProcessor<TPixel> : HistogramEqualizationProcessor<TPixe
[MethodImpl(InliningOptions.ShortMethod)]
public SynchronizedChannelsRowOperation(
Configuration configuration,
Rectangle bounds,
IMemoryOwner<int> cdfBuffer,
Buffer2D<TPixel> source,
int luminanceLevels,
float numberOfPixelsMinusCdfMin)
{
this.configuration = configuration;
this.bounds = bounds;
this.cdfBuffer = cdfBuffer;
this.source = source;
@ -127,33 +130,42 @@ internal class AutoLevelProcessor<TPixel> : HistogramEqualizationProcessor<TPixe
/// <inheritdoc/>
[MethodImpl(InliningOptions.ShortMethod)]
public void Invoke(int y)
public int GetRequiredBufferLength(Rectangle bounds) => bounds.Width;
/// <inheritdoc/>
[MethodImpl(InliningOptions.ShortMethod)]
public void Invoke(int y, Span<Vector4> span)
{
Span<Vector4> vectorBuffer = span.Slice(0, this.bounds.Width);
ref Vector4 vectorRef = ref MemoryMarshal.GetReference(vectorBuffer);
ref int cdfBase = ref MemoryMarshal.GetReference(this.cdfBuffer.GetSpan());
var sourceAccess = new PixelAccessor<TPixel>(this.source);
Span<TPixel> pixelRow = sourceAccess.GetRowSpan(y);
int levels = this.luminanceLevels;
float noOfPixelsMinusCdfMin = this.numberOfPixelsMinusCdfMin;
Span<TPixel> pixelRow = sourceAccess.GetRowSpan(y).Slice(this.bounds.X, this.bounds.Width);
PixelOperations<TPixel>.Instance.ToVector4(this.configuration, pixelRow, vectorBuffer);
for (int x = 0; x < this.bounds.Width; x++)
{
// TODO: We should bulk convert here.
ref TPixel pixel = ref pixelRow[x];
var vector = pixel.ToVector4();
var vector = Unsafe.Add(ref vectorRef, x);
int luminance = ColorNumerics.GetBT709Luminance(ref vector, levels);
float scaledLuminance = Unsafe.Add(ref cdfBase, luminance) / noOfPixelsMinusCdfMin;
float scalingFactor = scaledLuminance * levels / luminance;
Vector4 scaledVector = new Vector4(scalingFactor * vector.X, scalingFactor * vector.Y, scalingFactor * vector.Z, vector.W);
pixel.FromVector4(scaledVector);
Unsafe.Add(ref vectorRef, x) = scaledVector;
}
PixelOperations<TPixel>.Instance.FromVector4Destructive(this.configuration, vectorBuffer, pixelRow);
}
}
/// <summary>
/// A <see langword="struct"/> implementing the cdf logic for separate color channels.
/// </summary>
private readonly struct SeperateChannelsRowOperation : IRowOperation
private readonly struct SeperateChannelsRowOperation : IRowOperation<Vector4>
{
private readonly Configuration configuration;
private readonly Rectangle bounds;
private readonly IMemoryOwner<int> cdfBuffer;
private readonly Buffer2D<TPixel> source;
@ -162,12 +174,14 @@ internal class AutoLevelProcessor<TPixel> : HistogramEqualizationProcessor<TPixe
[MethodImpl(InliningOptions.ShortMethod)]
public SeperateChannelsRowOperation(
Configuration configuration,
Rectangle bounds,
IMemoryOwner<int> cdfBuffer,
Buffer2D<TPixel> source,
int luminanceLevels,
float numberOfPixelsMinusCdfMin)
{
this.configuration = configuration;
this.bounds = bounds;
this.cdfBuffer = cdfBuffer;
this.source = source;
@ -177,19 +191,25 @@ internal class AutoLevelProcessor<TPixel> : HistogramEqualizationProcessor<TPixe
/// <inheritdoc/>
[MethodImpl(InliningOptions.ShortMethod)]
public void Invoke(int y)
public int GetRequiredBufferLength(Rectangle bounds) => bounds.Width;
/// <inheritdoc/>
[MethodImpl(InliningOptions.ShortMethod)]
public void Invoke(int y, Span<Vector4> span)
{
Span<Vector4> vectorBuffer = span.Slice(0, this.bounds.Width);
ref Vector4 vectorRef = ref MemoryMarshal.GetReference(vectorBuffer);
ref int cdfBase = ref MemoryMarshal.GetReference(this.cdfBuffer.GetSpan());
var sourceAccess = new PixelAccessor<TPixel>(this.source);
Span<TPixel> pixelRow = sourceAccess.GetRowSpan(y);
int levelsMinusOne = this.luminanceLevels - 1;
float noOfPixelsMinusCdfMin = this.numberOfPixelsMinusCdfMin;
Span<TPixel> pixelRow = sourceAccess.GetRowSpan(y);
PixelOperations<TPixel>.Instance.ToVector4(this.configuration, pixelRow, vectorBuffer);
for (int x = 0; x < this.bounds.Width; x++)
{
// TODO: We should bulk convert here.
ref TPixel pixel = ref pixelRow[x];
var vector = pixel.ToVector4() * levelsMinusOne;
var vector = Unsafe.Add(ref vectorRef, x) * levelsMinusOne;
uint originalX = (uint)MathF.Round(vector.X);
float scaledX = Unsafe.Add(ref cdfBase, originalX) / noOfPixelsMinusCdfMin;
@ -197,8 +217,10 @@ internal class AutoLevelProcessor<TPixel> : HistogramEqualizationProcessor<TPixe
float scaledY = Unsafe.Add(ref cdfBase, originalY) / noOfPixelsMinusCdfMin;
uint originalZ = (uint)MathF.Round(vector.Z);
float scaledZ = Unsafe.Add(ref cdfBase, originalZ) / noOfPixelsMinusCdfMin;
pixel.FromVector4(new Vector4(scaledX, scaledY, scaledZ, vector.W));
Unsafe.Add(ref vectorRef, x) = new Vector4(scaledX, scaledY, scaledZ, vector.W);
}
PixelOperations<TPixel>.Instance.FromVector4Destructive(this.configuration, vectorBuffer, pixelRow);
}
}
}

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

@ -51,8 +51,8 @@ internal class GlobalHistogramEqualizationProcessor<TPixel> : HistogramEqualizat
using IMemoryOwner<int> histogramBuffer = memoryAllocator.Allocate<int>(this.LuminanceLevels, AllocationOptions.Clean);
// Build the histogram of the grayscale levels.
var grayscaleOperation = new GrayscaleLevelsRowOperation<TPixel>(interest, histogramBuffer, source.PixelBuffer, this.LuminanceLevels);
ParallelRowIterator.IterateRows(
var grayscaleOperation = new GrayscaleLevelsRowOperation<TPixel>(this.Configuration, interest, histogramBuffer, source.PixelBuffer, this.LuminanceLevels);
ParallelRowIterator.IterateRows<GrayscaleLevelsRowOperation<TPixel>, Vector4>(
this.Configuration,
interest,
in grayscaleOperation);
@ -74,8 +74,8 @@ internal class GlobalHistogramEqualizationProcessor<TPixel> : HistogramEqualizat
float numberOfPixelsMinusCdfMin = numberOfPixels - cdfMin;
// Apply the cdf to each pixel of the image
var cdfOperation = new CdfApplicationRowOperation(interest, cdfBuffer, source.PixelBuffer, this.LuminanceLevels, numberOfPixelsMinusCdfMin);
ParallelRowIterator.IterateRows(
var cdfOperation = new CdfApplicationRowOperation(this.Configuration, interest, cdfBuffer, source.PixelBuffer, this.LuminanceLevels, numberOfPixelsMinusCdfMin);
ParallelRowIterator.IterateRows<CdfApplicationRowOperation, Vector4>(
this.Configuration,
interest,
in cdfOperation);
@ -84,8 +84,9 @@ internal class GlobalHistogramEqualizationProcessor<TPixel> : HistogramEqualizat
/// <summary>
/// A <see langword="struct"/> implementing the cdf application levels logic for <see cref="GlobalHistogramEqualizationProcessor{TPixel}"/>.
/// </summary>
private readonly struct CdfApplicationRowOperation : IRowOperation
private readonly struct CdfApplicationRowOperation : IRowOperation<Vector4>
{
private readonly Configuration configuration;
private readonly Rectangle bounds;
private readonly IMemoryOwner<int> cdfBuffer;
private readonly Buffer2D<TPixel> source;
@ -94,12 +95,14 @@ internal class GlobalHistogramEqualizationProcessor<TPixel> : HistogramEqualizat
[MethodImpl(InliningOptions.ShortMethod)]
public CdfApplicationRowOperation(
Configuration configuration,
Rectangle bounds,
IMemoryOwner<int> cdfBuffer,
Buffer2D<TPixel> source,
int luminanceLevels,
float numberOfPixelsMinusCdfMin)
{
this.configuration = configuration;
this.bounds = bounds;
this.cdfBuffer = cdfBuffer;
this.source = source;
@ -109,22 +112,30 @@ internal class GlobalHistogramEqualizationProcessor<TPixel> : HistogramEqualizat
/// <inheritdoc/>
[MethodImpl(InliningOptions.ShortMethod)]
public void Invoke(int y)
public int GetRequiredBufferLength(Rectangle bounds) => bounds.Width;
/// <inheritdoc/>
[MethodImpl(InliningOptions.ShortMethod)]
public void Invoke(int y, Span<Vector4> span)
{
Span<Vector4> vectorBuffer = span.Slice(0, this.bounds.Width);
ref Vector4 vectorRef = ref MemoryMarshal.GetReference(vectorBuffer);
ref int cdfBase = ref MemoryMarshal.GetReference(this.cdfBuffer.GetSpan());
Span<TPixel> pixelRow = this.source.DangerousGetRowSpan(y);
int levels = this.luminanceLevels;
float noOfPixelsMinusCdfMin = this.numberOfPixelsMinusCdfMin;
Span<TPixel> pixelRow = this.source.DangerousGetRowSpan(y);
PixelOperations<TPixel>.Instance.ToVector4(this.configuration, pixelRow, vectorBuffer);
for (int x = 0; x < this.bounds.Width; x++)
{
// TODO: We should bulk convert here.
ref TPixel pixel = ref pixelRow[x];
var vector = pixel.ToVector4();
var vector = Unsafe.Add(ref vectorRef, x);
int luminance = ColorNumerics.GetBT709Luminance(ref vector, levels);
float luminanceEqualized = Unsafe.Add(ref cdfBase, luminance) / noOfPixelsMinusCdfMin;
pixel.FromVector4(new Vector4(luminanceEqualized, luminanceEqualized, luminanceEqualized, vector.W));
Unsafe.Add(ref vectorRef, x) = new Vector4(luminanceEqualized, luminanceEqualized, luminanceEqualized, vector.W);
}
PixelOperations<TPixel>.Instance.FromVector4Destructive(this.configuration, vectorBuffer, pixelRow);
}
}
}

23
src/ImageSharp/Processing/Processors/Normalization/GrayscaleLevelsRowOperation{TPixel}.cs

@ -2,6 +2,7 @@
// Licensed under the Six Labors Split License.
using System.Buffers;
using System.Numerics;
using System.Runtime.CompilerServices;
using System.Runtime.InteropServices;
using SixLabors.ImageSharp.Advanced;
@ -11,11 +12,12 @@ using SixLabors.ImageSharp.PixelFormats;
namespace SixLabors.ImageSharp.Processing.Processors.Normalization;
/// <summary>
/// A <see langword="struct"/> implementing the grayscale levels logic as <see cref="IRowOperation"/>.
/// A <see langword="struct"/> implementing the grayscale levels logic as <see cref="IRowOperation{Vector4}"/>.
/// </summary>
internal readonly struct GrayscaleLevelsRowOperation<TPixel> : IRowOperation
internal readonly struct GrayscaleLevelsRowOperation<TPixel> : IRowOperation<Vector4>
where TPixel : unmanaged, IPixel<TPixel>
{
private readonly Configuration configuration;
private readonly Rectangle bounds;
private readonly IMemoryOwner<int> histogramBuffer;
private readonly Buffer2D<TPixel> source;
@ -23,11 +25,13 @@ internal readonly struct GrayscaleLevelsRowOperation<TPixel> : IRowOperation
[MethodImpl(InliningOptions.ShortMethod)]
public GrayscaleLevelsRowOperation(
Configuration configuration,
Rectangle bounds,
IMemoryOwner<int> histogramBuffer,
Buffer2D<TPixel> source,
int luminanceLevels)
{
this.configuration = configuration;
this.bounds = bounds;
this.histogramBuffer = histogramBuffer;
this.source = source;
@ -36,16 +40,23 @@ internal readonly struct GrayscaleLevelsRowOperation<TPixel> : IRowOperation
/// <inheritdoc/>
[MethodImpl(InliningOptions.ShortMethod)]
public void Invoke(int y)
public int GetRequiredBufferLength(Rectangle bounds) => bounds.Width;
/// <inheritdoc/>
[MethodImpl(InliningOptions.ShortMethod)]
public void Invoke(int y, Span<Vector4> span)
{
Span<Vector4> vectorBuffer = span.Slice(0, this.bounds.Width);
ref Vector4 vectorRef = ref MemoryMarshal.GetReference(vectorBuffer);
ref int histogramBase = ref MemoryMarshal.GetReference(this.histogramBuffer.GetSpan());
Span<TPixel> pixelRow = this.source.DangerousGetRowSpan(y);
int levels = this.luminanceLevels;
Span<TPixel> pixelRow = this.source.DangerousGetRowSpan(y);
PixelOperations<TPixel>.Instance.ToVector4(this.configuration, pixelRow, vectorBuffer);
for (int x = 0; x < this.bounds.Width; x++)
{
// TODO: We should bulk convert here.
var vector = pixelRow[x].ToVector4();
var vector = Unsafe.Add(ref vectorRef, x);
int luminance = ColorNumerics.GetBT709Luminance(ref vector, levels);
Interlocked.Increment(ref Unsafe.Add(ref histogramBase, luminance));
}

Loading…
Cancel
Save