Browse Source

refactor KernelMap initialization

pull/781/head
Anton Firszov 8 years ago
parent
commit
70206614fc
  1. 94
      src/ImageSharp/Processing/Processors/Transforms/KernelMap.cs
  2. 4
      tests/ImageSharp.Tests/Processing/Processors/Transforms/KernelMapTests.ReferenceKernelMap.cs
  3. 18
      tests/ImageSharp.Tests/Processing/Processors/Transforms/KernelMapTests.cs
  4. 31
      tests/ImageSharp.Tests/Processing/Processors/Transforms/ResizeTests.cs

94
src/ImageSharp/Processing/Processors/Transforms/KernelMap.cs

@ -15,28 +15,38 @@ namespace SixLabors.ImageSharp.Processing.Processors.Transforms
/// </summary>
internal class KernelMap : IDisposable
{
private readonly Buffer2D<float> data;
private readonly IResampler sampler;
private readonly ResizeKernel[] kernels;
private readonly int sourceSize;
private readonly float ratio;
private int period;
private readonly float scale;
private int radius;
private readonly int radius;
private int periodicRegionMin;
private readonly Buffer2D<float> data;
private int periodicRegionMax;
private readonly ResizeKernel[] kernels;
private KernelMap(MemoryAllocator memoryAllocator, int destinationSize, int radius, int period)
private KernelMap(
MemoryAllocator memoryAllocator,
IResampler sampler,
int sourceSize,
int destinationSize,
int bufferHeight,
float ratio,
float scale,
int radius)
{
this.DestinationSize = destinationSize;
this.period = period;
this.sampler = sampler;
this.ratio = ratio;
this.scale = scale;
this.radius = radius;
this.periodicRegionMin = period + radius;
this.periodicRegionMax = destinationSize - radius;
int width = (radius * 2) + 1;
this.data = memoryAllocator.Allocate2D<float>(width, destinationSize, AllocationOptions.Clean);
this.sourceSize = sourceSize;
this.DestinationSize = destinationSize;
int maxWidth = (radius * 2) + 1;
this.data = memoryAllocator.Allocate2D<float>(maxWidth, bufferHeight, AllocationOptions.Clean);
this.kernels = new ResizeKernel[destinationSize];
}
@ -79,61 +89,69 @@ namespace SixLabors.ImageSharp.Processing.Processors.Transforms
}
int period = ImageMaths.LeastCommonMultiple(sourceSize, destinationSize) / sourceSize;
int radius = (int)MathF.Ceiling(scale * sampler.Radius);
var result = new KernelMap(memoryAllocator, destinationSize, radius, period);
for (int i = 0; i < destinationSize; i++)
var result = new KernelMap(
memoryAllocator,
sampler,
sourceSize,
destinationSize,
destinationSize,
ratio,
scale,
radius);
result.BasicInit();
return result;
}
private void BasicInit()
{
for (int i = 0; i < this.DestinationSize; i++)
{
float center = ((i + .5F) * ratio) - .5F;
float center = ((i + .5F) * this.ratio) - .5F;
// Keep inside bounds.
int left = (int)MathF.Ceiling(center - radius);
int left = (int)MathF.Ceiling(center - this.radius);
if (left < 0)
{
left = 0;
}
int right = (int)MathF.Floor(center + radius);
if (right > sourceSize - 1)
int right = (int)MathF.Floor(center + this.radius);
if (right > this.sourceSize - 1)
{
right = sourceSize - 1;
right = this.sourceSize - 1;
}
float sum = 0;
ResizeKernel ws = result.CreateKernel(i, left, right);
result.kernels[i] = ws;
ResizeKernel kernel = this.CreateKernel(i, left, right);
this.kernels[i] = kernel;
ref float weightsBaseRef = ref MemoryMarshal.GetReference(ws.GetValues());
ref float kernelBaseRef = ref MemoryMarshal.GetReference(kernel.GetValues());
for (int j = left; j <= right; j++)
{
float weight = sampler.GetValue((j - center) / scale);
sum += weight;
float value = this.sampler.GetValue((j - center) / this.scale);
sum += value;
// weights[j - left] = weight:
Unsafe.Add(ref weightsBaseRef, j - left) = weight;
Unsafe.Add(ref kernelBaseRef, j - left) = value;
}
// Normalize, best to do it here rather than in the pixel loop later on.
if (sum > 0)
{
for (int w = 0; w < ws.Length; w++)
for (int w = 0; w < kernel.Length; w++)
{
// weights[w] = weights[w] / sum:
ref float wRef = ref Unsafe.Add(ref weightsBaseRef, w);
wRef /= sum;
ref float kRef = ref Unsafe.Add(ref kernelBaseRef, w);
kRef /= sum;
}
}
}
return result;
}
private int ReduceIndex(int destIndex)
{
return destIndex;
}
/// <summary>

4
tests/ImageSharp.Tests/Processing/Processors/Transforms/KernelMapTests.ReferenceKernelMap.cs

@ -23,7 +23,7 @@ namespace SixLabors.ImageSharp.Tests.Processing.Processors.Transforms
public ReferenceKernel GetKernel(int destinationIndex) => this.kernels[destinationIndex];
public static ReferenceKernelMap Calculate(IResampler sampler, int destinationSize, int sourceSize)
public static ReferenceKernelMap Calculate(IResampler sampler, int destinationSize, int sourceSize, bool normalize = true)
{
float ratio = (float)sourceSize / destinationSize;
float scale = ratio;
@ -68,7 +68,7 @@ namespace SixLabors.ImageSharp.Tests.Processing.Processors.Transforms
result.Add(new ReferenceKernel(left, values));
if (sum > 0)
if (sum > 0 && normalize)
{
for (int w = 0; w < values.Length; w++)
{

18
tests/ImageSharp.Tests/Processing/Processors/Transforms/KernelMapTests.cs

@ -35,6 +35,8 @@ namespace SixLabors.ImageSharp.Tests.Processing.Processors.Transforms
{ nameof(KnownResamplers.Bicubic), 500, 200 },
{ nameof(KnownResamplers.Bicubic), 200, 500 },
{ nameof(KnownResamplers.Bicubic), 10, 25 },
{ nameof(KnownResamplers.Lanczos3), 16, 12 },
{ nameof(KnownResamplers.Lanczos3), 12, 16 },
{ nameof(KnownResamplers.Lanczos3), 12, 9 },
@ -43,12 +45,26 @@ namespace SixLabors.ImageSharp.Tests.Processing.Processors.Transforms
{ nameof(KnownResamplers.Lanczos3), 8, 6 },
{ nameof(KnownResamplers.Lanczos3), 20, 12 },
{ nameof(KnownResamplers.Lanczos3), 5, 25 },
{ nameof(KnownResamplers.Lanczos3), 5, 50 },
{ nameof(KnownResamplers.Lanczos8), 500, 200 },
{ nameof(KnownResamplers.Lanczos8), 100, 10 },
{ nameof(KnownResamplers.Lanczos8), 100, 80 },
{ nameof(KnownResamplers.Lanczos8), 10, 100 },
};
[Theory]
[MemberData(nameof(KernelMapData))]
public void PrintNonNormalizedKernelMap(string resamplerName, int srcSize, int destSize)
{
IResampler resampler = TestUtils.GetResampler(resamplerName);
var kernelMap = ReferenceKernelMap.Calculate(resampler, destSize, srcSize, false);
this.Output.WriteLine($"Actual KernelMap:\n{PrintKernelMap(kernelMap)}\n");
}
[Theory]
[MemberData(nameof(KernelMapData))]
public void KernelMapContentIsCorrect(string resamplerName, int srcSize, int destSize)
@ -60,7 +76,7 @@ namespace SixLabors.ImageSharp.Tests.Processing.Processors.Transforms
#if DEBUG
this.Output.WriteLine($"Actual KernelMap:\n{PrintKernelMap(kernelMap)}\n");
this.Output.WriteLine($"Reference KernelMap:\n{PrintKernelMap(referenceMap)}\n");
// this.Output.WriteLine($"Reference KernelMap:\n{PrintKernelMap(referenceMap)}\n");
#endif
for (int i = 0; i < kernelMap.DestinationSize; i++)

31
tests/ImageSharp.Tests/Processing/Processors/Transforms/ResizeTests.cs

@ -11,6 +11,7 @@ using SixLabors.ImageSharp.Tests.TestUtilities.ImageComparison;
using SixLabors.Primitives;
using Xunit;
// ReSharper disable InconsistentNaming
namespace SixLabors.ImageSharp.Tests.Processing.Processors.Transforms
{
@ -20,7 +21,7 @@ namespace SixLabors.ImageSharp.Tests.Processing.Processors.Transforms
private static readonly ImageComparer ValidatorComparer = ImageComparer.TolerantPercentage(0.07F);
public static readonly TheoryData<string, IResampler> AllReSamplers =
public static readonly TheoryData<string, IResampler> AllResamplers =
new TheoryData<string, IResampler>
{
{ "Bicubic", KnownResamplers.Bicubic },
@ -40,9 +41,9 @@ namespace SixLabors.ImageSharp.Tests.Processing.Processors.Transforms
};
[Theory]
[WithTestPatternImages(nameof(AllReSamplers), 100, 100, DefaultPixelType, 0.5f)]
[WithFileCollection(nameof(CommonTestImages), nameof(AllReSamplers), DefaultPixelType, 0.5f)]
[WithFileCollection(nameof(CommonTestImages), nameof(AllReSamplers), DefaultPixelType, 0.3f)]
[WithTestPatternImages(nameof(AllResamplers), 100, 100, DefaultPixelType, 0.5f)]
[WithFileCollection(nameof(CommonTestImages), nameof(AllResamplers), DefaultPixelType, 0.5f)]
[WithFileCollection(nameof(CommonTestImages), nameof(AllResamplers), DefaultPixelType, 0.3f)]
public void Resize_WorksWithAllResamplers<TPixel>(TestImageProvider<TPixel> provider, string name, IResampler sampler, float ratio)
where TPixel : struct, IPixel<TPixel>
{
@ -57,6 +58,28 @@ namespace SixLabors.ImageSharp.Tests.Processing.Processors.Transforms
}
}
[Theory]
[WithTestPatternImages(100, 100, PixelTypes.Rgba32, nameof(KnownResamplers.Bicubic), 1)]
[WithTestPatternImages(100, 100, PixelTypes.Rgba32, nameof(KnownResamplers.Bicubic), 10)]
[WithTestPatternImages(100, 100, PixelTypes.Rgba32, nameof(KnownResamplers.Lanczos3), 10)]
[WithTestPatternImages(100, 100, PixelTypes.Rgba32, nameof(KnownResamplers.Lanczos8), 10)]
[WithTestPatternImages(100, 100, PixelTypes.Rgba32, nameof(KnownResamplers.NearestNeighbor), 10)]
[WithFile(TestImages.Png.CalliphoraPartial, PixelTypes.Rgba32, nameof(KnownResamplers.NearestNeighbor), 1)]
[WithFile(TestImages.Png.CalliphoraPartial, PixelTypes.Rgba32, nameof(KnownResamplers.NearestNeighbor), 5)]
[WithFile(TestImages.Png.CalliphoraPartial, PixelTypes.Rgba32, nameof(KnownResamplers.Bicubic), 5)]
public void ScaleUp<TPixel>(TestImageProvider<TPixel> provider, string samplerName, float ratio)
where TPixel : struct, IPixel<TPixel>
{
using (Image<TPixel> image = provider.GetImage())
{
SizeF newSize = image.Size() * ratio;
image.Mutate(x => x.Resize((Size)newSize, TestUtils.GetResampler(samplerName), false));
FormattableString details = $"{samplerName}_{ratio.ToString(System.Globalization.CultureInfo.InvariantCulture)}";
image.DebugSave(provider, details);
}
}
[Theory]
[WithFileCollection(nameof(CommonTestImages), DefaultPixelType, 1)]
[WithFileCollection(nameof(CommonTestImages), DefaultPixelType, 4)]

Loading…
Cancel
Save