Browse Source

Resize experiments.

Former-commit-id: 9d1f60a253be59b797be35618a0159dd6ca3b13b
Former-commit-id: c36b547bcc7372a0961397c20fdcdb6fee96070d
Former-commit-id: 4e5618575a0a923629e85880dd75f358d8bfe6eb
pull/1/head
James Jackson-South 10 years ago
parent
commit
4e7e7f98e8
  1. 181
      src/ImageProcessorCore/Samplers/Resampler.cs
  2. 23
      src/ImageProcessorCore/Samplers/Resize.cs
  3. 2
      tests/ImageProcessorCore.Tests/Processors/Samplers/SamplerTests.cs

181
src/ImageProcessorCore/Samplers/Resampler.cs

@ -11,6 +11,8 @@ namespace ImageProcessorCore.Samplers
/// <summary> /// <summary>
/// Provides methods that allow the resampling of images using various algorithms. /// Provides methods that allow the resampling of images using various algorithms.
/// <see href="http://www.realtimerendering.com/resources/GraphicsGems/category.html#Image Processing_link"/>
/// <see href="http://www.realtimerendering.com/resources/GraphicsGems/gemsiii/filter_rcg.c"/>
/// </summary> /// </summary>
public abstract class Resampler : ImageSampler public abstract class Resampler : ImageSampler
{ {
@ -52,74 +54,171 @@ namespace ImageProcessorCore.Samplers
/// </returns> /// </returns>
protected Weights[] PrecomputeWeights(int destinationSize, int sourceSize) protected Weights[] PrecomputeWeights(int destinationSize, int sourceSize)
{ {
float xscale = destinationSize / (float)sourceSize;
float width;
IResampler sampler = this.Sampler; IResampler sampler = this.Sampler;
float ratio = sourceSize / (float)destinationSize; float fwidth = sampler.Radius;
float scale = ratio; float fscale;
double left;
double right;
double weight = 0;
int n = 0;
int k;
// When shrinking, broaden the effective kernel support so that we still Weights[] result = new Weights[destinationSize];
// When expanding, broaden the effective kernel support so that we still
// visit every source pixel. // visit every source pixel.
if (scale < 1) if (xscale < 0)
{ {
scale = 1; width = sampler.Radius / xscale;
} fscale = 1 / xscale;
float scaledRadius = (float)Math.Ceiling(scale * sampler.Radius); // Make the weights slices, one source for each column or row.
Weights[] result = new Weights[destinationSize]; for (int i = 0; i < destinationSize; i++)
// Make the weights slices, one source for each column or row.
Parallel.For(
0,
destinationSize,
i =>
{ {
float center = ((i + .5f) * ratio) - 0.5f; float centre = i / xscale;
int start = (int)Math.Ceiling(center - scaledRadius); left = Math.Ceiling(centre - width);
right = Math.Floor(centre + width);
if (start < 0) float sum = 0;
{ result[i] = new Weights();
start = 0; List<Weight> builder = new List<Weight>();
} for (double j = left; j <= right; j++)
int end = (int)Math.Floor(center + scaledRadius);
if (end > sourceSize)
{ {
end = sourceSize; weight = centre - j;
weight = sampler.GetValue((float)weight / fscale) / fscale;
if (end < start) if (j < 0)
{
n = (int)-j;
}
else if (j >= sourceSize)
{
n = (int)((sourceSize - j) + sourceSize - 1);
}
else
{ {
end = start; n = (int)j;
} }
sum++;
builder.Add(new Weight(n, (float)weight));
} }
result[i].Values = builder.ToArray();
result[i].Sum = sum;
}
}
else
{
// Make the weights slices, one source for each column or row.
for (int i = 0; i < destinationSize; i++)
{
float centre = i / xscale;
left = Math.Ceiling(centre - fwidth);
right = Math.Floor(centre + fwidth);
float sum = 0; float sum = 0;
result[i] = new Weights(); result[i] = new Weights();
List<Weight> builder = new List<Weight>(); List<Weight> builder = new List<Weight>();
for (int a = start; a < end; a++) for (double j = left; j <= right; j++)
{ {
float w = sampler.GetValue((a - center) / scale); weight = centre - j;
weight = sampler.GetValue((float)weight);
if (w < 0 || w > 0) if (j < 0)
{ {
sum += w; n = (int)-j;
builder.Add(new Weight(a, w)); }
else if (j >= sourceSize)
{
n = (int)((sourceSize - j) + sourceSize - 1);
}
else
{
n = (int)j;
} }
}
// Normalise the values sum++;
if (sum > 0 || sum < 0) builder.Add(new Weight(n, (float)weight));
{
builder.ForEach(w => w.Value /= sum);
} }
result[i].Values = builder.ToArray(); result[i].Values = builder.ToArray();
result[i].Sum = sum; result[i].Sum = sum;
}); }
}
return result; return result;
} }
//protected Weights[] PrecomputeWeights(int destinationSize, int sourceSize)
//{
// IResampler sampler = this.Sampler;
// float ratio = sourceSize / (float)destinationSize;
// float scale = ratio;
// // When shrinking, broaden the effective kernel support so that we still
// // visit every source pixel.
// if (scale < 1)
// {
// scale = 1;
// }
// float scaledRadius = (float)Math.Ceiling(scale * sampler.Radius);
// Weights[] result = new Weights[destinationSize];
// // Make the weights slices, one source for each column or row.
// Parallel.For(
// 0,
// destinationSize,
// i =>
// {
// float center = ((i + .5f) * ratio) - 0.5f;
// int start = (int)Math.Ceiling(center - scaledRadius);
// if (start < 0)
// {
// start = 0;
// }
// int end = (int)Math.Floor(center + scaledRadius);
// if (end > sourceSize)
// {
// end = sourceSize;
// if (end < start)
// {
// end = start;
// }
// }
// float sum = 0;
// result[i] = new Weights();
// List<Weight> builder = new List<Weight>();
// for (int a = start; a < end; a++)
// {
// float w = sampler.GetValue((a - center) / scale);
// if (w < 0 || w > 0)
// {
// sum += w;
// builder.Add(new Weight(a, w));
// }
// }
// // Normalise the values
// if (sum > 0 || sum < 0)
// {
// builder.ForEach(w => w.Value /= sum);
// }
// result[i].Values = builder.ToArray();
// result[i].Sum = sum;
// });
// return result;
//}
/// <summary> /// <summary>
/// Represents the weight to be added to a scaled pixel. /// Represents the weight to be added to a scaled pixel.
/// </summary> /// </summary>

23
src/ImageProcessorCore/Samplers/Resize.cs

@ -100,18 +100,27 @@ namespace ImageProcessorCore.Samplers
{ {
for (int x = startX; x < endX; x++) for (int x = startX; x < endX; x++)
{ {
float sum = this.HorizontalWeights[x].Sum;
Weight[] horizontalValues = this.HorizontalWeights[x].Values; Weight[] horizontalValues = this.HorizontalWeights[x].Values;
// Destination color components // Destination color components
Color destination = new Color(); Color destination = new Color();
foreach (Weight xw in horizontalValues) for (int i = 0; i < sum; i++)
{ {
Weight xw = horizontalValues[i];
int originX = xw.Index; int originX = xw.Index;
Color sourceColor = compand ? Color.Expand(source[originX, y]) : source[originX, y]; Color sourceColor = compand ? Color.Expand(source[originX, y]) : source[originX, y];
destination += sourceColor * xw.Value; destination += sourceColor * xw.Value;
} }
//foreach (Weight xw in horizontalValues)
//{
// int originX = xw.Index;
// Color sourceColor = compand ? Color.Expand(source[originX, y]) : source[originX, y];
// destination += sourceColor * xw.Value;
//}
if (compand) if (compand)
{ {
destination = Color.Compress(destination); destination = Color.Compress(destination);
@ -129,6 +138,7 @@ namespace ImageProcessorCore.Samplers
{ {
if (y >= targetY && y < targetBottom) if (y >= targetY && y < targetBottom)
{ {
float sum = this.VerticalWeights[y].Sum;
Weight[] verticalValues = this.VerticalWeights[y].Values; Weight[] verticalValues = this.VerticalWeights[y].Values;
for (int x = startX; x < endX; x++) for (int x = startX; x < endX; x++)
@ -136,14 +146,23 @@ namespace ImageProcessorCore.Samplers
// Destination color components // Destination color components
Color destination = new Color(); Color destination = new Color();
foreach (Weight yw in verticalValues) for (int i = 0; i < sum; i++)
{ {
Weight yw = verticalValues[i];
int originY = yw.Index; int originY = yw.Index;
int originX = x; int originX = x;
Color sourceColor = compand ? Color.Expand(this.firstPass[originX, originY]) : this.firstPass[originX, originY]; Color sourceColor = compand ? Color.Expand(this.firstPass[originX, originY]) : this.firstPass[originX, originY];
destination += sourceColor * yw.Value; destination += sourceColor * yw.Value;
} }
//foreach (Weight yw in verticalValues)
//{
// int originY = yw.Index;
// int originX = x;
// Color sourceColor = compand ? Color.Expand(this.firstPass[originX, originY]) : this.firstPass[originX, originY];
// destination += sourceColor * yw.Value;
//}
if (compand) if (compand)
{ {
destination = Color.Compress(destination); destination = Color.Compress(destination);

2
tests/ImageProcessorCore.Tests/Processors/Samplers/SamplerTests.cs

@ -92,7 +92,7 @@
string filename = Path.GetFileNameWithoutExtension(file) + "-" + name + Path.GetExtension(file); string filename = Path.GetFileNameWithoutExtension(file) + "-" + name + Path.GetExtension(file);
using (FileStream output = File.OpenWrite($"TestOutput/Resize/{filename}")) using (FileStream output = File.OpenWrite($"TestOutput/Resize/{filename}"))
{ {
image.Resize(image.Width / 2, image.Height / 2, sampler, false, this.ProgressUpdate) image.Resize(image.Width * 2, image.Height * 2, sampler, false, this.ProgressUpdate)
.Save(output); .Save(output);
} }

Loading…
Cancel
Save