Browse Source

Added image integral algorithm.

pull/1561/head
David Rolland 5 years ago
parent
commit
8147ec3bfc
  1. 77
      src/ImageSharp/Processing/Extensions/ProcessingExtensions.IntegralImage.cs
  2. 2
      src/ImageSharp/Processing/Extensions/ProcessingExtensions.cs
  3. 110
      tests/ImageSharp.Tests/Processing/IntegralImageTests.cs

77
src/ImageSharp/Processing/Extensions/ProcessingExtensions.IntegralImage.cs

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

2
src/ImageSharp/Processing/Extensions/ProcessingExtensions.cs

@ -12,7 +12,7 @@ namespace SixLabors.ImageSharp.Processing
/// <summary>
/// Adds extensions that allow the processing of images to the <see cref="Image{TPixel}"/> type.
/// </summary>
public static class ProcessingExtensions
public static partial class ProcessingExtensions
{
/// <summary>
/// Mutates the source image by applying the image operation to it.

110
tests/ImageSharp.Tests/Processing/IntegralImageTests.cs

@ -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…
Cancel
Save