Browse Source

Fix resampler accuracy

Former-commit-id: 35f3471c93647fb9cf7927cd758321154f06cdbb
Former-commit-id: d90a5442769e89c655a4ada4a6ca36d07fa4bac5
Former-commit-id: 813a7f137bc4badeae043123dd51d02c8812e217
af/merge-core
James Jackson-South 10 years ago
parent
commit
cb6e3145ed
  1. 6
      src/ImageProcessorCore/Samplers/Processors/CompandingResizeProcessor.cs
  2. 124
      src/ImageProcessorCore/Samplers/Processors/ResamplingWeightedProcessor.cs
  3. 6
      src/ImageProcessorCore/Samplers/Processors/ResizeProcessor.cs
  4. 10
      tests/ImageProcessorCore.Tests/FileTestBase.cs
  5. 4
      tests/ImageProcessorCore.Tests/Processors/Samplers/ResizeTests.cs

6
src/ImageProcessorCore/Samplers/Processors/CompandingResizeProcessor.cs

@ -112,13 +112,12 @@ namespace ImageProcessorCore.Processors
{
// Ensure offsets are normalised for cropping and padding.
int offsetX = x - startX;
double sum = this.HorizontalWeights[offsetX].Sum;
Weight[] horizontalValues = this.HorizontalWeights[offsetX].Values;
// Destination color components
Vector4 destination = Vector4.Zero;
for (int i = 0; i < sum; i++)
for (int i = 0; i < horizontalValues.Length; i++)
{
Weight xw = horizontalValues[i];
int originX = xw.Index;
@ -145,7 +144,6 @@ namespace ImageProcessorCore.Processors
{
// Ensure offsets are normalised for cropping and padding.
int offsetY = y - startY;
double sum = this.VerticalWeights[offsetY].Sum;
Weight[] verticalValues = this.VerticalWeights[offsetY].Values;
for (int x = 0; x < width; x++)
@ -153,7 +151,7 @@ namespace ImageProcessorCore.Processors
// Destination color components
Vector4 destination = Vector4.Zero;
for (int i = 0; i < sum; i++)
for (int i = 0; i < verticalValues.Length; i++)
{
Weight yw = verticalValues[i];
int originY = yw.Index;

124
src/ImageProcessorCore/Samplers/Processors/ResamplingWeightedProcessor.cs

@ -6,6 +6,8 @@
namespace ImageProcessorCore.Processors
{
using System;
using System.Collections.Generic;
using System.Linq;
/// <summary>
/// Provides methods that allow the resizing of images using various algorithms.
@ -65,6 +67,7 @@ namespace ImageProcessorCore.Processors
}
}
/// <summary>
/// Computes the weights to apply at each pixel when resizing.
/// </summary>
@ -75,90 +78,56 @@ namespace ImageProcessorCore.Processors
/// </returns>
protected Weights[] PrecomputeWeights(int destinationSize, int sourceSize)
{
float scale = (float)destinationSize / sourceSize;
IResampler sampler = this.Sampler;
float radius = sampler.Radius;
int left;
int right;
float weight;
int index;
int sum;
float ratio = (float)sourceSize / destinationSize;
float scale = ratio;
if (scale < 1F)
{
scale = 1F;
}
IResampler sampler = this.Sampler;
float radius = (float)Math.Ceiling(scale * sampler.Radius);
Weights[] result = new Weights[destinationSize];
// When shrinking, broaden the effective kernel support so that we still
// visit every source pixel.
if (scale < 1F)
for (int i = 0; i < destinationSize; i++)
{
float width = radius / scale;
float filterScale = 1F / scale;
float center = ((i + .5F) * ratio) - .5F;
// Make the weights slices, one source for each column or row.
for (int i = 0; i < destinationSize; i++)
// Keep inside bounds.
int left = (int)Math.Ceiling(center - radius);
if (left < 0)
{
float centre = i / scale;
left = (int)Math.Ceiling(centre - width);
right = (int)Math.Floor(centre + width);
result[i] = new Weights
{
Values = new Weight[(right - left + 1)]
};
left = 0;
}
for (int j = left; j <= right; j++)
{
weight = sampler.GetValue((centre - j) / filterScale) / filterScale;
if (j < 0)
{
index = -j;
}
else if (j >= sourceSize)
{
index = (int)((sourceSize - (float)j) + sourceSize - 1);
}
else
{
index = j;
}
sum = (int)result[i].Sum++;
result[i].Values[sum] = new Weight(index, weight);
}
int right = (int)Math.Floor(center + radius);
if (right > sourceSize - 1)
{
right = sourceSize - 1;
}
}
else
{
// Make the weights slices, one source for each column or row.
for (int i = 0; i < destinationSize; i++)
float sum = 0;
result[i] = new Weights();
Weight[] weights = new Weight[right - left + 1];
for (int j = left; j <= right; j++)
{
float centre = i / scale;
left = (int)Math.Ceiling(centre - radius);
right = (int)Math.Floor(centre + radius);
result[i] = new Weights
{
Values = new Weight[(right - left + 1)]
};
float weight = sampler.GetValue((j - center) / scale);
sum += weight;
weights[j - left] = new Weight(j, weight);
}
for (int j = left; j <= right; j++)
// Normalise, best to do it here rather than in the pixel loop later on.
if (sum > 0)
{
for (int w = 0; w < weights.Length; w++)
{
weight = sampler.GetValue(centre - (float)j);
if (j < 0)
{
index = -j;
}
else if (j >= sourceSize)
{
index = (sourceSize - j) + sourceSize - 1;
}
else
{
index = j;
}
sum = (int)result[i].Sum++;
result[i].Values[sum] = new Weight(index, weight);
weights[w].Value = weights[w].Value / sum;
}
}
result[i].Values = weights;
}
return result;
@ -167,10 +136,10 @@ namespace ImageProcessorCore.Processors
/// <summary>
/// Represents the weight to be added to a scaled pixel.
/// </summary>
protected struct Weight
protected class Weight
{
/// <summary>
/// Initializes a new instance of the <see cref="Weight"/> struct.
/// Initializes a new instance of the <see cref="Weight"/> class.
/// </summary>
/// <param name="index">The index.</param>
/// <param name="value">The value.</param>
@ -186,9 +155,9 @@ namespace ImageProcessorCore.Processors
public int Index { get; }
/// <summary>
/// Gets the result of the interpolation algorithm.
/// Gets or sets the result of the interpolation algorithm.
/// </summary>
public float Value { get; }
public float Value { get; set; }
}
/// <summary>
@ -200,11 +169,6 @@ namespace ImageProcessorCore.Processors
/// Gets or sets the values.
/// </summary>
public Weight[] Values { get; set; }
/// <summary>
/// Gets or sets the sum.
/// </summary>
public float Sum { get; set; }
}
}
}

6
src/ImageProcessorCore/Samplers/Processors/ResizeProcessor.cs

@ -110,13 +110,12 @@ namespace ImageProcessorCore.Processors
{
// Ensure offsets are normalised for cropping and padding.
int offsetX = x - startX;
double sum = this.HorizontalWeights[offsetX].Sum;
Weight[] horizontalValues = this.HorizontalWeights[offsetX].Values;
// Destination color components
Vector4 destination = Vector4.Zero;
for (int i = 0; i < sum; i++)
for (int i = 0; i < horizontalValues.Length; i++)
{
Weight xw = horizontalValues[i];
int originX = xw.Index;
@ -143,7 +142,6 @@ namespace ImageProcessorCore.Processors
{
// Ensure offsets are normalised for cropping and padding.
int offsetY = y - startY;
double sum = this.VerticalWeights[offsetY].Sum;
Weight[] verticalValues = this.VerticalWeights[offsetY].Values;
for (int x = 0; x < width; x++)
@ -151,7 +149,7 @@ namespace ImageProcessorCore.Processors
// Destination color components
Vector4 destination = Vector4.Zero;
for (int i = 0; i < sum; i++)
for (int i = 0; i < verticalValues.Length; i++)
{
Weight yw = verticalValues[i];
int originY = yw.Index;

10
tests/ImageProcessorCore.Tests/FileTestBase.cs

@ -19,20 +19,20 @@ namespace ImageProcessorCore.Tests
/// </summary>
protected static readonly List<string> Files = new List<string>
{
"TestImages/Formats/Png/pl.png",
"TestImages/Formats/Png/pd.png",
//"TestImages/Formats/Png/pl.png",
//"TestImages/Formats/Png/pd.png",
//"TestImages/Formats/Jpg/Floorplan.jpeg", // Perf: Enable for local testing only
//"TestImages/Formats/Jpg/Calliphora.jpg",
"TestImages/Formats/Jpg/Calliphora.jpg",
//"TestImages/Formats/Jpg/turtle.jpg",
//"TestImages/Formats/Jpg/fb.jpg", // Perf: Enable for local testing only
//"TestImages/Formats/Jpg/progress.jpg", // Perf: Enable for local testing only
//"TestImages/Formats/Jpg/gamma_dalai_lama_gray.jpg", // Perf: Enable for local testing only
//"TestImages/Formats/Bmp/Car.bmp",
"TestImages/Formats/Bmp/Car.bmp",
// "TestImages/Formats/Bmp/neg_height.bmp", // Perf: Enable for local testing only
//"TestImages/Formats/Png/blur.png", // Perf: Enable for local testing only
//"TestImages/Formats/Png/indexed.png", // Perf: Enable for local testing only
//TestImages/Formats/Png/splash.png",
//"TestImages/Formats/Gif/rings.gif",
"TestImages/Formats/Gif/rings.gif",
//"TestImages/Formats/Gif/giphy.gif" // Perf: Enable for local testing only
};

4
tests/ImageProcessorCore.Tests/Processors/Samplers/ResizeTests.cs

@ -51,8 +51,8 @@ namespace ImageProcessorCore.Tests
Image image = new Image(stream);
using (FileStream output = File.OpenWrite($"{path}/{filename}"))
{
//image.Resize(image.Width / 2, image.Height / 2, sampler, true, this.ProgressUpdate)
image.Resize(555, 15, sampler, true, this.ProgressUpdate)
image.Resize(image.Width / 2, image.Height / 2, sampler, true, this.ProgressUpdate)
//image.Resize(555, 275, sampler, false, this.ProgressUpdate)
.Save(output);
}
}

Loading…
Cancel
Save