mirror of https://github.com/SixLabors/ImageSharp
3 changed files with 188 additions and 1 deletions
@ -0,0 +1,77 @@ |
|||
// Copyright (c) Six Labors.
|
|||
// Licensed under the Apache License, Version 2.0.
|
|||
|
|||
using System; |
|||
using System.Buffers; |
|||
using SixLabors.ImageSharp.Advanced; |
|||
using SixLabors.ImageSharp.Memory; |
|||
using SixLabors.ImageSharp.PixelFormats; |
|||
|
|||
namespace SixLabors.ImageSharp.Processing |
|||
{ |
|||
/// <summary>
|
|||
/// Defines extensions that allow the computation of image integrals on an <see cref="Image"/>
|
|||
/// </summary>
|
|||
public static partial class ProcessingExtensions |
|||
{ |
|||
/// <summary>
|
|||
/// Apply an image integral. <See href="https://en.wikipedia.org/wiki/Summed-area_table"/>
|
|||
/// </summary>
|
|||
/// <param name="source">The image on which to apply the integral.</param>
|
|||
/// <typeparam name="TPixel">The type of the pixel.</typeparam>
|
|||
/// <returns>The <see cref="Buffer2D{T}"/> containing all the sums.</returns>
|
|||
public static Buffer2D<ulong> CalculateIntegralImage<TPixel>(this Image<TPixel> source) |
|||
where TPixel : unmanaged, IPixel<TPixel> |
|||
{ |
|||
Configuration configuration = source.GetConfiguration(); |
|||
|
|||
int endY = source.Height; |
|||
int endX = source.Width; |
|||
|
|||
Buffer2D<ulong> intImage = configuration.MemoryAllocator.Allocate2D<ulong>(source.Width, source.Height); |
|||
ulong sumX0 = 0; |
|||
|
|||
using (IMemoryOwner<L8> tempRow = configuration.MemoryAllocator.Allocate<L8>(source.Width)) |
|||
{ |
|||
Span<L8> tempSpan = tempRow.GetSpan(); |
|||
Span<TPixel> sourceRow = source.GetPixelRowSpan(0); |
|||
Span<ulong> destRow = intImage.GetRowSpan(0); |
|||
|
|||
PixelOperations<TPixel>.Instance.ToL8(configuration, sourceRow, tempSpan); |
|||
|
|||
// First row
|
|||
for (int x = 0; x < endX; x++) |
|||
{ |
|||
sumX0 += tempSpan[x].PackedValue; |
|||
destRow[x] = sumX0; |
|||
} |
|||
|
|||
Span<ulong> previousDestRow = destRow; |
|||
|
|||
// All other rows
|
|||
for (int y = 1; y < endY; y++) |
|||
{ |
|||
sourceRow = source.GetPixelRowSpan(y); |
|||
destRow = intImage.GetRowSpan(y); |
|||
|
|||
PixelOperations<TPixel>.Instance.ToL8(configuration, sourceRow, tempSpan); |
|||
|
|||
// Process first column
|
|||
sumX0 = tempSpan[0].PackedValue; |
|||
destRow[0] = sumX0 + previousDestRow[0]; |
|||
|
|||
// Process all other colmns
|
|||
for (int x = 1; x < endX; x++) |
|||
{ |
|||
sumX0 += tempSpan[x].PackedValue; |
|||
destRow[x] = sumX0 + previousDestRow[x]; |
|||
} |
|||
|
|||
previousDestRow = destRow; |
|||
} |
|||
} |
|||
|
|||
return intImage; |
|||
} |
|||
} |
|||
} |
|||
@ -0,0 +1,110 @@ |
|||
// Copyright (c) Six Labors.
|
|||
// Licensed under the Apache License, Version 2.0.
|
|||
|
|||
using SixLabors.ImageSharp.Memory; |
|||
using SixLabors.ImageSharp.PixelFormats; |
|||
using SixLabors.ImageSharp.Processing; |
|||
using Xunit; |
|||
|
|||
namespace SixLabors.ImageSharp.Tests.Processing.Transforms |
|||
{ |
|||
public class IntegralImageTests : BaseImageOperationsExtensionTest |
|||
{ |
|||
[Theory] |
|||
[WithFile(TestImages.Png.Bradley01, PixelTypes.Rgba32)] |
|||
[WithFile(TestImages.Png.Bradley02, PixelTypes.Rgba32)] |
|||
[WithFile(TestImages.Png.Ducky, PixelTypes.Rgba32)] |
|||
public void CalculateIntegralImage_Rgba32Works(TestImageProvider<Rgba32> provider) |
|||
{ |
|||
using Image<Rgba32> image = provider.GetImage(); |
|||
|
|||
// Act:
|
|||
Buffer2D<ulong> integralBuffer = image.CalculateIntegralImage(); |
|||
|
|||
// Assert:
|
|||
VerifySumValues(provider, integralBuffer, (Rgba32 pixel) => |
|||
{ |
|||
L8 outputPixel = default; |
|||
|
|||
outputPixel.FromRgba32(pixel); |
|||
|
|||
return outputPixel.PackedValue; |
|||
}); |
|||
} |
|||
|
|||
[Theory] |
|||
[WithFile(TestImages.Png.Bradley01, PixelTypes.L8)] |
|||
[WithFile(TestImages.Png.Bradley02, PixelTypes.L8)] |
|||
public void CalculateIntegralImage_L8Works(TestImageProvider<L8> provider) |
|||
{ |
|||
using Image<L8> image = provider.GetImage(); |
|||
|
|||
// Act:
|
|||
Buffer2D<ulong> integralBuffer = image.CalculateIntegralImage(); |
|||
|
|||
// Assert:
|
|||
VerifySumValues(provider, integralBuffer, (L8 pixel) => { return pixel.PackedValue; }); |
|||
} |
|||
|
|||
private static void VerifySumValues<TPixel>( |
|||
TestImageProvider<TPixel> provider, |
|||
Buffer2D<ulong> integralBuffer, |
|||
System.Func<TPixel, ulong> getPixel) |
|||
where TPixel : unmanaged, IPixel<TPixel> |
|||
{ |
|||
Image<TPixel> image = provider.GetImage(); |
|||
|
|||
// Check top-left corner
|
|||
Assert.Equal(getPixel(image[0, 0]), integralBuffer[0, 0]); |
|||
|
|||
ulong pixelValues = 0; |
|||
|
|||
pixelValues += getPixel(image[0, 0]); |
|||
pixelValues += getPixel(image[1, 0]); |
|||
pixelValues += getPixel(image[0, 1]); |
|||
pixelValues += getPixel(image[1, 1]); |
|||
|
|||
// Check top-left 2x2 pixels
|
|||
Assert.Equal(pixelValues, integralBuffer[1, 1]); |
|||
|
|||
pixelValues = 0; |
|||
|
|||
pixelValues += getPixel(image[image.Width - 3, 0]); |
|||
pixelValues += getPixel(image[image.Width - 2, 0]); |
|||
pixelValues += getPixel(image[image.Width - 1, 0]); |
|||
pixelValues += getPixel(image[image.Width - 3, 1]); |
|||
pixelValues += getPixel(image[image.Width - 2, 1]); |
|||
pixelValues += getPixel(image[image.Width - 1, 1]); |
|||
|
|||
// Check top-right 3x2 pixels
|
|||
Assert.Equal(pixelValues, integralBuffer[image.Width - 1, 1] + 0 - 0 - integralBuffer[image.Width - 4, 1]); |
|||
|
|||
pixelValues = 0; |
|||
|
|||
pixelValues += getPixel(image[0, image.Height - 3]); |
|||
pixelValues += getPixel(image[0, image.Height - 2]); |
|||
pixelValues += getPixel(image[0, image.Height - 1]); |
|||
pixelValues += getPixel(image[1, image.Height - 3]); |
|||
pixelValues += getPixel(image[1, image.Height - 2]); |
|||
pixelValues += getPixel(image[1, image.Height - 1]); |
|||
|
|||
// Check bottom-left 2x3 pixels
|
|||
Assert.Equal(pixelValues, integralBuffer[1, image.Height - 1] + 0 - integralBuffer[1, image.Height - 4] - 0); |
|||
|
|||
pixelValues = 0; |
|||
|
|||
pixelValues += getPixel(image[image.Width - 3, image.Height - 3]); |
|||
pixelValues += getPixel(image[image.Width - 2, image.Height - 3]); |
|||
pixelValues += getPixel(image[image.Width - 1, image.Height - 3]); |
|||
pixelValues += getPixel(image[image.Width - 3, image.Height - 2]); |
|||
pixelValues += getPixel(image[image.Width - 2, image.Height - 2]); |
|||
pixelValues += getPixel(image[image.Width - 1, image.Height - 2]); |
|||
pixelValues += getPixel(image[image.Width - 3, image.Height - 1]); |
|||
pixelValues += getPixel(image[image.Width - 2, image.Height - 1]); |
|||
pixelValues += getPixel(image[image.Width - 1, image.Height - 1]); |
|||
|
|||
// Check bottom-right 3x3 pixels
|
|||
Assert.Equal(pixelValues, integralBuffer[image.Width - 1, image.Height - 1] + integralBuffer[image.Width - 4, image.Height - 4] - integralBuffer[image.Width - 1, image.Height - 4] - integralBuffer[image.Width - 4, image.Height - 1]); |
|||
} |
|||
} |
|||
} |
|||
Loading…
Reference in new issue