From 1ec6da892ab1d28fc2f14e348c90575cb802cf76 Mon Sep 17 00:00:00 2001 From: Ynse Hoornenborg Date: Mon, 26 Sep 2022 22:01:35 +0200 Subject: [PATCH] Bulk pixel conversion --- .../AutoLevelProcessor{TPixel}.cs | 62 +++++++++++++------ ...lHistogramEqualizationProcessor{TPixel}.cs | 33 ++++++---- .../GrayscaleLevelsRowOperation{TPixel}.cs | 23 +++++-- 3 files changed, 81 insertions(+), 37 deletions(-) diff --git a/src/ImageSharp/Processing/Processors/Normalization/AutoLevelProcessor{TPixel}.cs b/src/ImageSharp/Processing/Processors/Normalization/AutoLevelProcessor{TPixel}.cs index 856fba3dc..c07ac3aa3 100644 --- a/src/ImageSharp/Processing/Processors/Normalization/AutoLevelProcessor{TPixel}.cs +++ b/src/ImageSharp/Processing/Processors/Normalization/AutoLevelProcessor{TPixel}.cs @@ -59,8 +59,8 @@ internal class AutoLevelProcessor : HistogramEqualizationProcessor histogramBuffer = memoryAllocator.Allocate(this.LuminanceLevels, AllocationOptions.Clean); // Build the histogram of the grayscale levels. - var grayscaleOperation = new GrayscaleLevelsRowOperation(interest, histogramBuffer, source.PixelBuffer, this.LuminanceLevels); - ParallelRowIterator.IterateRows( + var grayscaleOperation = new GrayscaleLevelsRowOperation(this.Configuration, interest, histogramBuffer, source.PixelBuffer, this.LuminanceLevels); + ParallelRowIterator.IterateRows, Vector4>( this.Configuration, interest, in grayscaleOperation); @@ -83,16 +83,16 @@ internal class AutoLevelProcessor : HistogramEqualizationProcessor( 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( this.Configuration, interest, in cdfOperation); @@ -102,8 +102,9 @@ internal class AutoLevelProcessor : HistogramEqualizationProcessor /// A implementing the cdf logic for synchronized color channels. /// - private readonly struct SynchronizedChannelsRowOperation : IRowOperation + private readonly struct SynchronizedChannelsRowOperation : IRowOperation { + private readonly Configuration configuration; private readonly Rectangle bounds; private readonly IMemoryOwner cdfBuffer; private readonly Buffer2D source; @@ -112,12 +113,14 @@ internal class AutoLevelProcessor : HistogramEqualizationProcessor cdfBuffer, Buffer2D source, int luminanceLevels, float numberOfPixelsMinusCdfMin) { + this.configuration = configuration; this.bounds = bounds; this.cdfBuffer = cdfBuffer; this.source = source; @@ -127,33 +130,42 @@ internal class AutoLevelProcessor : HistogramEqualizationProcessor [MethodImpl(InliningOptions.ShortMethod)] - public void Invoke(int y) + public int GetRequiredBufferLength(Rectangle bounds) => bounds.Width; + + /// + [MethodImpl(InliningOptions.ShortMethod)] + public void Invoke(int y, Span span) { + Span 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(this.source); - Span pixelRow = sourceAccess.GetRowSpan(y); int levels = this.luminanceLevels; float noOfPixelsMinusCdfMin = this.numberOfPixelsMinusCdfMin; + Span pixelRow = sourceAccess.GetRowSpan(y).Slice(this.bounds.X, this.bounds.Width); + PixelOperations.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.Instance.FromVector4Destructive(this.configuration, vectorBuffer, pixelRow); } } /// /// A implementing the cdf logic for separate color channels. /// - private readonly struct SeperateChannelsRowOperation : IRowOperation + private readonly struct SeperateChannelsRowOperation : IRowOperation { + private readonly Configuration configuration; private readonly Rectangle bounds; private readonly IMemoryOwner cdfBuffer; private readonly Buffer2D source; @@ -162,12 +174,14 @@ internal class AutoLevelProcessor : HistogramEqualizationProcessor cdfBuffer, Buffer2D source, int luminanceLevels, float numberOfPixelsMinusCdfMin) { + this.configuration = configuration; this.bounds = bounds; this.cdfBuffer = cdfBuffer; this.source = source; @@ -177,19 +191,25 @@ internal class AutoLevelProcessor : HistogramEqualizationProcessor [MethodImpl(InliningOptions.ShortMethod)] - public void Invoke(int y) + public int GetRequiredBufferLength(Rectangle bounds) => bounds.Width; + + /// + [MethodImpl(InliningOptions.ShortMethod)] + public void Invoke(int y, Span span) { + Span 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(this.source); - Span pixelRow = sourceAccess.GetRowSpan(y); int levelsMinusOne = this.luminanceLevels - 1; float noOfPixelsMinusCdfMin = this.numberOfPixelsMinusCdfMin; + Span pixelRow = sourceAccess.GetRowSpan(y); + PixelOperations.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 : HistogramEqualizationProcessor.Instance.FromVector4Destructive(this.configuration, vectorBuffer, pixelRow); } } } diff --git a/src/ImageSharp/Processing/Processors/Normalization/GlobalHistogramEqualizationProcessor{TPixel}.cs b/src/ImageSharp/Processing/Processors/Normalization/GlobalHistogramEqualizationProcessor{TPixel}.cs index d506777be..7e9e06464 100644 --- a/src/ImageSharp/Processing/Processors/Normalization/GlobalHistogramEqualizationProcessor{TPixel}.cs +++ b/src/ImageSharp/Processing/Processors/Normalization/GlobalHistogramEqualizationProcessor{TPixel}.cs @@ -51,8 +51,8 @@ internal class GlobalHistogramEqualizationProcessor : HistogramEqualizat using IMemoryOwner histogramBuffer = memoryAllocator.Allocate(this.LuminanceLevels, AllocationOptions.Clean); // Build the histogram of the grayscale levels. - var grayscaleOperation = new GrayscaleLevelsRowOperation(interest, histogramBuffer, source.PixelBuffer, this.LuminanceLevels); - ParallelRowIterator.IterateRows( + var grayscaleOperation = new GrayscaleLevelsRowOperation(this.Configuration, interest, histogramBuffer, source.PixelBuffer, this.LuminanceLevels); + ParallelRowIterator.IterateRows, Vector4>( this.Configuration, interest, in grayscaleOperation); @@ -74,8 +74,8 @@ internal class GlobalHistogramEqualizationProcessor : 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( this.Configuration, interest, in cdfOperation); @@ -84,8 +84,9 @@ internal class GlobalHistogramEqualizationProcessor : HistogramEqualizat /// /// A implementing the cdf application levels logic for . /// - private readonly struct CdfApplicationRowOperation : IRowOperation + private readonly struct CdfApplicationRowOperation : IRowOperation { + private readonly Configuration configuration; private readonly Rectangle bounds; private readonly IMemoryOwner cdfBuffer; private readonly Buffer2D source; @@ -94,12 +95,14 @@ internal class GlobalHistogramEqualizationProcessor : HistogramEqualizat [MethodImpl(InliningOptions.ShortMethod)] public CdfApplicationRowOperation( + Configuration configuration, Rectangle bounds, IMemoryOwner cdfBuffer, Buffer2D source, int luminanceLevels, float numberOfPixelsMinusCdfMin) { + this.configuration = configuration; this.bounds = bounds; this.cdfBuffer = cdfBuffer; this.source = source; @@ -109,22 +112,30 @@ internal class GlobalHistogramEqualizationProcessor : HistogramEqualizat /// [MethodImpl(InliningOptions.ShortMethod)] - public void Invoke(int y) + public int GetRequiredBufferLength(Rectangle bounds) => bounds.Width; + + /// + [MethodImpl(InliningOptions.ShortMethod)] + public void Invoke(int y, Span span) { + Span vectorBuffer = span.Slice(0, this.bounds.Width); + ref Vector4 vectorRef = ref MemoryMarshal.GetReference(vectorBuffer); ref int cdfBase = ref MemoryMarshal.GetReference(this.cdfBuffer.GetSpan()); - Span pixelRow = this.source.DangerousGetRowSpan(y); int levels = this.luminanceLevels; float noOfPixelsMinusCdfMin = this.numberOfPixelsMinusCdfMin; + Span pixelRow = this.source.DangerousGetRowSpan(y); + PixelOperations.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.Instance.FromVector4Destructive(this.configuration, vectorBuffer, pixelRow); } } } diff --git a/src/ImageSharp/Processing/Processors/Normalization/GrayscaleLevelsRowOperation{TPixel}.cs b/src/ImageSharp/Processing/Processors/Normalization/GrayscaleLevelsRowOperation{TPixel}.cs index f4fcd1578..8895fdc61 100644 --- a/src/ImageSharp/Processing/Processors/Normalization/GrayscaleLevelsRowOperation{TPixel}.cs +++ b/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; /// -/// A implementing the grayscale levels logic as . +/// A implementing the grayscale levels logic as . /// -internal readonly struct GrayscaleLevelsRowOperation : IRowOperation +internal readonly struct GrayscaleLevelsRowOperation : IRowOperation where TPixel : unmanaged, IPixel { + private readonly Configuration configuration; private readonly Rectangle bounds; private readonly IMemoryOwner histogramBuffer; private readonly Buffer2D source; @@ -23,11 +25,13 @@ internal readonly struct GrayscaleLevelsRowOperation : IRowOperation [MethodImpl(InliningOptions.ShortMethod)] public GrayscaleLevelsRowOperation( + Configuration configuration, Rectangle bounds, IMemoryOwner histogramBuffer, Buffer2D source, int luminanceLevels) { + this.configuration = configuration; this.bounds = bounds; this.histogramBuffer = histogramBuffer; this.source = source; @@ -36,16 +40,23 @@ internal readonly struct GrayscaleLevelsRowOperation : IRowOperation /// [MethodImpl(InliningOptions.ShortMethod)] - public void Invoke(int y) + public int GetRequiredBufferLength(Rectangle bounds) => bounds.Width; + + /// + [MethodImpl(InliningOptions.ShortMethod)] + public void Invoke(int y, Span span) { + Span vectorBuffer = span.Slice(0, this.bounds.Width); + ref Vector4 vectorRef = ref MemoryMarshal.GetReference(vectorBuffer); ref int histogramBase = ref MemoryMarshal.GetReference(this.histogramBuffer.GetSpan()); - Span pixelRow = this.source.DangerousGetRowSpan(y); int levels = this.luminanceLevels; + Span pixelRow = this.source.DangerousGetRowSpan(y); + PixelOperations.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)); }