Browse Source

Bulk pixel conversion

pull/2241/head
Ynse Hoornenborg 4 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); using IMemoryOwner<int> histogramBuffer = memoryAllocator.Allocate<int>(this.LuminanceLevels, AllocationOptions.Clean);
// Build the histogram of the grayscale levels. // Build the histogram of the grayscale levels.
var grayscaleOperation = new GrayscaleLevelsRowOperation<TPixel>(interest, histogramBuffer, source.PixelBuffer, this.LuminanceLevels); var grayscaleOperation = new GrayscaleLevelsRowOperation<TPixel>(this.Configuration, interest, histogramBuffer, source.PixelBuffer, this.LuminanceLevels);
ParallelRowIterator.IterateRows( ParallelRowIterator.IterateRows<GrayscaleLevelsRowOperation<TPixel>, Vector4>(
this.Configuration, this.Configuration,
interest, interest,
in grayscaleOperation); in grayscaleOperation);
@ -83,16 +83,16 @@ internal class AutoLevelProcessor<TPixel> : HistogramEqualizationProcessor<TPixe
if (this.SyncChannels) if (this.SyncChannels)
{ {
var cdfOperation = new SynchronizedChannelsRowOperation(interest, cdfBuffer, source.PixelBuffer, this.LuminanceLevels, numberOfPixelsMinusCdfMin); var cdfOperation = new SynchronizedChannelsRowOperation(this.Configuration, interest, cdfBuffer, source.PixelBuffer, this.LuminanceLevels, numberOfPixelsMinusCdfMin);
ParallelRowIterator.IterateRows( ParallelRowIterator.IterateRows<SynchronizedChannelsRowOperation, Vector4>(
this.Configuration, this.Configuration,
interest, interest,
in cdfOperation); in cdfOperation);
} }
else else
{ {
var cdfOperation = new SeperateChannelsRowOperation(interest, cdfBuffer, source.PixelBuffer, this.LuminanceLevels, numberOfPixelsMinusCdfMin); var cdfOperation = new SeperateChannelsRowOperation(this.Configuration, interest, cdfBuffer, source.PixelBuffer, this.LuminanceLevels, numberOfPixelsMinusCdfMin);
ParallelRowIterator.IterateRows( ParallelRowIterator.IterateRows<SeperateChannelsRowOperation, Vector4>(
this.Configuration, this.Configuration,
interest, interest,
in cdfOperation); in cdfOperation);
@ -102,8 +102,9 @@ internal class AutoLevelProcessor<TPixel> : HistogramEqualizationProcessor<TPixe
/// <summary> /// <summary>
/// A <see langword="struct"/> implementing the cdf logic for synchronized color channels. /// A <see langword="struct"/> implementing the cdf logic for synchronized color channels.
/// </summary> /// </summary>
private readonly struct SynchronizedChannelsRowOperation : IRowOperation private readonly struct SynchronizedChannelsRowOperation : IRowOperation<Vector4>
{ {
private readonly Configuration configuration;
private readonly Rectangle bounds; private readonly Rectangle bounds;
private readonly IMemoryOwner<int> cdfBuffer; private readonly IMemoryOwner<int> cdfBuffer;
private readonly Buffer2D<TPixel> source; private readonly Buffer2D<TPixel> source;
@ -112,12 +113,14 @@ internal class AutoLevelProcessor<TPixel> : HistogramEqualizationProcessor<TPixe
[MethodImpl(InliningOptions.ShortMethod)] [MethodImpl(InliningOptions.ShortMethod)]
public SynchronizedChannelsRowOperation( public SynchronizedChannelsRowOperation(
Configuration configuration,
Rectangle bounds, Rectangle bounds,
IMemoryOwner<int> cdfBuffer, IMemoryOwner<int> cdfBuffer,
Buffer2D<TPixel> source, Buffer2D<TPixel> source,
int luminanceLevels, int luminanceLevels,
float numberOfPixelsMinusCdfMin) float numberOfPixelsMinusCdfMin)
{ {
this.configuration = configuration;
this.bounds = bounds; this.bounds = bounds;
this.cdfBuffer = cdfBuffer; this.cdfBuffer = cdfBuffer;
this.source = source; this.source = source;
@ -127,33 +130,42 @@ internal class AutoLevelProcessor<TPixel> : HistogramEqualizationProcessor<TPixe
/// <inheritdoc/> /// <inheritdoc/>
[MethodImpl(InliningOptions.ShortMethod)] [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()); ref int cdfBase = ref MemoryMarshal.GetReference(this.cdfBuffer.GetSpan());
var sourceAccess = new PixelAccessor<TPixel>(this.source); var sourceAccess = new PixelAccessor<TPixel>(this.source);
Span<TPixel> pixelRow = sourceAccess.GetRowSpan(y);
int levels = this.luminanceLevels; int levels = this.luminanceLevels;
float noOfPixelsMinusCdfMin = this.numberOfPixelsMinusCdfMin; 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++) for (int x = 0; x < this.bounds.Width; x++)
{ {
// TODO: We should bulk convert here. var vector = Unsafe.Add(ref vectorRef, x);
ref TPixel pixel = ref pixelRow[x];
var vector = pixel.ToVector4();
int luminance = ColorNumerics.GetBT709Luminance(ref vector, levels); int luminance = ColorNumerics.GetBT709Luminance(ref vector, levels);
float scaledLuminance = Unsafe.Add(ref cdfBase, luminance) / noOfPixelsMinusCdfMin; float scaledLuminance = Unsafe.Add(ref cdfBase, luminance) / noOfPixelsMinusCdfMin;
float scalingFactor = scaledLuminance * levels / luminance; float scalingFactor = scaledLuminance * levels / luminance;
Vector4 scaledVector = new Vector4(scalingFactor * vector.X, scalingFactor * vector.Y, scalingFactor * vector.Z, vector.W); 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> /// <summary>
/// A <see langword="struct"/> implementing the cdf logic for separate color channels. /// A <see langword="struct"/> implementing the cdf logic for separate color channels.
/// </summary> /// </summary>
private readonly struct SeperateChannelsRowOperation : IRowOperation private readonly struct SeperateChannelsRowOperation : IRowOperation<Vector4>
{ {
private readonly Configuration configuration;
private readonly Rectangle bounds; private readonly Rectangle bounds;
private readonly IMemoryOwner<int> cdfBuffer; private readonly IMemoryOwner<int> cdfBuffer;
private readonly Buffer2D<TPixel> source; private readonly Buffer2D<TPixel> source;
@ -162,12 +174,14 @@ internal class AutoLevelProcessor<TPixel> : HistogramEqualizationProcessor<TPixe
[MethodImpl(InliningOptions.ShortMethod)] [MethodImpl(InliningOptions.ShortMethod)]
public SeperateChannelsRowOperation( public SeperateChannelsRowOperation(
Configuration configuration,
Rectangle bounds, Rectangle bounds,
IMemoryOwner<int> cdfBuffer, IMemoryOwner<int> cdfBuffer,
Buffer2D<TPixel> source, Buffer2D<TPixel> source,
int luminanceLevels, int luminanceLevels,
float numberOfPixelsMinusCdfMin) float numberOfPixelsMinusCdfMin)
{ {
this.configuration = configuration;
this.bounds = bounds; this.bounds = bounds;
this.cdfBuffer = cdfBuffer; this.cdfBuffer = cdfBuffer;
this.source = source; this.source = source;
@ -177,19 +191,25 @@ internal class AutoLevelProcessor<TPixel> : HistogramEqualizationProcessor<TPixe
/// <inheritdoc/> /// <inheritdoc/>
[MethodImpl(InliningOptions.ShortMethod)] [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()); ref int cdfBase = ref MemoryMarshal.GetReference(this.cdfBuffer.GetSpan());
var sourceAccess = new PixelAccessor<TPixel>(this.source); var sourceAccess = new PixelAccessor<TPixel>(this.source);
Span<TPixel> pixelRow = sourceAccess.GetRowSpan(y);
int levelsMinusOne = this.luminanceLevels - 1; int levelsMinusOne = this.luminanceLevels - 1;
float noOfPixelsMinusCdfMin = this.numberOfPixelsMinusCdfMin; 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++) for (int x = 0; x < this.bounds.Width; x++)
{ {
// TODO: We should bulk convert here. var vector = Unsafe.Add(ref vectorRef, x) * levelsMinusOne;
ref TPixel pixel = ref pixelRow[x];
var vector = pixel.ToVector4() * levelsMinusOne;
uint originalX = (uint)MathF.Round(vector.X); uint originalX = (uint)MathF.Round(vector.X);
float scaledX = Unsafe.Add(ref cdfBase, originalX) / noOfPixelsMinusCdfMin; 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; float scaledY = Unsafe.Add(ref cdfBase, originalY) / noOfPixelsMinusCdfMin;
uint originalZ = (uint)MathF.Round(vector.Z); uint originalZ = (uint)MathF.Round(vector.Z);
float scaledZ = Unsafe.Add(ref cdfBase, originalZ) / noOfPixelsMinusCdfMin; 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); using IMemoryOwner<int> histogramBuffer = memoryAllocator.Allocate<int>(this.LuminanceLevels, AllocationOptions.Clean);
// Build the histogram of the grayscale levels. // Build the histogram of the grayscale levels.
var grayscaleOperation = new GrayscaleLevelsRowOperation<TPixel>(interest, histogramBuffer, source.PixelBuffer, this.LuminanceLevels); var grayscaleOperation = new GrayscaleLevelsRowOperation<TPixel>(this.Configuration, interest, histogramBuffer, source.PixelBuffer, this.LuminanceLevels);
ParallelRowIterator.IterateRows( ParallelRowIterator.IterateRows<GrayscaleLevelsRowOperation<TPixel>, Vector4>(
this.Configuration, this.Configuration,
interest, interest,
in grayscaleOperation); in grayscaleOperation);
@ -74,8 +74,8 @@ internal class GlobalHistogramEqualizationProcessor<TPixel> : HistogramEqualizat
float numberOfPixelsMinusCdfMin = numberOfPixels - cdfMin; float numberOfPixelsMinusCdfMin = numberOfPixels - cdfMin;
// Apply the cdf to each pixel of the image // Apply the cdf to each pixel of the image
var cdfOperation = new CdfApplicationRowOperation(interest, cdfBuffer, source.PixelBuffer, this.LuminanceLevels, numberOfPixelsMinusCdfMin); var cdfOperation = new CdfApplicationRowOperation(this.Configuration, interest, cdfBuffer, source.PixelBuffer, this.LuminanceLevels, numberOfPixelsMinusCdfMin);
ParallelRowIterator.IterateRows( ParallelRowIterator.IterateRows<CdfApplicationRowOperation, Vector4>(
this.Configuration, this.Configuration,
interest, interest,
in cdfOperation); in cdfOperation);
@ -84,8 +84,9 @@ internal class GlobalHistogramEqualizationProcessor<TPixel> : HistogramEqualizat
/// <summary> /// <summary>
/// A <see langword="struct"/> implementing the cdf application levels logic for <see cref="GlobalHistogramEqualizationProcessor{TPixel}"/>. /// A <see langword="struct"/> implementing the cdf application levels logic for <see cref="GlobalHistogramEqualizationProcessor{TPixel}"/>.
/// </summary> /// </summary>
private readonly struct CdfApplicationRowOperation : IRowOperation private readonly struct CdfApplicationRowOperation : IRowOperation<Vector4>
{ {
private readonly Configuration configuration;
private readonly Rectangle bounds; private readonly Rectangle bounds;
private readonly IMemoryOwner<int> cdfBuffer; private readonly IMemoryOwner<int> cdfBuffer;
private readonly Buffer2D<TPixel> source; private readonly Buffer2D<TPixel> source;
@ -94,12 +95,14 @@ internal class GlobalHistogramEqualizationProcessor<TPixel> : HistogramEqualizat
[MethodImpl(InliningOptions.ShortMethod)] [MethodImpl(InliningOptions.ShortMethod)]
public CdfApplicationRowOperation( public CdfApplicationRowOperation(
Configuration configuration,
Rectangle bounds, Rectangle bounds,
IMemoryOwner<int> cdfBuffer, IMemoryOwner<int> cdfBuffer,
Buffer2D<TPixel> source, Buffer2D<TPixel> source,
int luminanceLevels, int luminanceLevels,
float numberOfPixelsMinusCdfMin) float numberOfPixelsMinusCdfMin)
{ {
this.configuration = configuration;
this.bounds = bounds; this.bounds = bounds;
this.cdfBuffer = cdfBuffer; this.cdfBuffer = cdfBuffer;
this.source = source; this.source = source;
@ -109,22 +112,30 @@ internal class GlobalHistogramEqualizationProcessor<TPixel> : HistogramEqualizat
/// <inheritdoc/> /// <inheritdoc/>
[MethodImpl(InliningOptions.ShortMethod)] [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()); ref int cdfBase = ref MemoryMarshal.GetReference(this.cdfBuffer.GetSpan());
Span<TPixel> pixelRow = this.source.DangerousGetRowSpan(y);
int levels = this.luminanceLevels; int levels = this.luminanceLevels;
float noOfPixelsMinusCdfMin = this.numberOfPixelsMinusCdfMin; 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++) for (int x = 0; x < this.bounds.Width; x++)
{ {
// TODO: We should bulk convert here. var vector = Unsafe.Add(ref vectorRef, x);
ref TPixel pixel = ref pixelRow[x];
var vector = pixel.ToVector4();
int luminance = ColorNumerics.GetBT709Luminance(ref vector, levels); int luminance = ColorNumerics.GetBT709Luminance(ref vector, levels);
float luminanceEqualized = Unsafe.Add(ref cdfBase, luminance) / noOfPixelsMinusCdfMin; 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. // Licensed under the Six Labors Split License.
using System.Buffers; using System.Buffers;
using System.Numerics;
using System.Runtime.CompilerServices; using System.Runtime.CompilerServices;
using System.Runtime.InteropServices; using System.Runtime.InteropServices;
using SixLabors.ImageSharp.Advanced; using SixLabors.ImageSharp.Advanced;
@ -11,11 +12,12 @@ using SixLabors.ImageSharp.PixelFormats;
namespace SixLabors.ImageSharp.Processing.Processors.Normalization; namespace SixLabors.ImageSharp.Processing.Processors.Normalization;
/// <summary> /// <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> /// </summary>
internal readonly struct GrayscaleLevelsRowOperation<TPixel> : IRowOperation internal readonly struct GrayscaleLevelsRowOperation<TPixel> : IRowOperation<Vector4>
where TPixel : unmanaged, IPixel<TPixel> where TPixel : unmanaged, IPixel<TPixel>
{ {
private readonly Configuration configuration;
private readonly Rectangle bounds; private readonly Rectangle bounds;
private readonly IMemoryOwner<int> histogramBuffer; private readonly IMemoryOwner<int> histogramBuffer;
private readonly Buffer2D<TPixel> source; private readonly Buffer2D<TPixel> source;
@ -23,11 +25,13 @@ internal readonly struct GrayscaleLevelsRowOperation<TPixel> : IRowOperation
[MethodImpl(InliningOptions.ShortMethod)] [MethodImpl(InliningOptions.ShortMethod)]
public GrayscaleLevelsRowOperation( public GrayscaleLevelsRowOperation(
Configuration configuration,
Rectangle bounds, Rectangle bounds,
IMemoryOwner<int> histogramBuffer, IMemoryOwner<int> histogramBuffer,
Buffer2D<TPixel> source, Buffer2D<TPixel> source,
int luminanceLevels) int luminanceLevels)
{ {
this.configuration = configuration;
this.bounds = bounds; this.bounds = bounds;
this.histogramBuffer = histogramBuffer; this.histogramBuffer = histogramBuffer;
this.source = source; this.source = source;
@ -36,16 +40,23 @@ internal readonly struct GrayscaleLevelsRowOperation<TPixel> : IRowOperation
/// <inheritdoc/> /// <inheritdoc/>
[MethodImpl(InliningOptions.ShortMethod)] [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()); ref int histogramBase = ref MemoryMarshal.GetReference(this.histogramBuffer.GetSpan());
Span<TPixel> pixelRow = this.source.DangerousGetRowSpan(y);
int levels = this.luminanceLevels; 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++) for (int x = 0; x < this.bounds.Width; x++)
{ {
// TODO: We should bulk convert here. var vector = Unsafe.Add(ref vectorRef, x);
var vector = pixelRow[x].ToVector4();
int luminance = ColorNumerics.GetBT709Luminance(ref vector, levels); int luminance = ColorNumerics.GetBT709Luminance(ref vector, levels);
Interlocked.Increment(ref Unsafe.Add(ref histogramBase, luminance)); Interlocked.Increment(ref Unsafe.Add(ref histogramBase, luminance));
} }

Loading…
Cancel
Save