Browse Source

Merge remote-tracking branch 'refs/remotes/origin/Core-Resize' into Core

Former-commit-id: ede8c124ed2ed8b0a85359e316b4955e24dd7cf2
Former-commit-id: 7c4912636e4455d06e6ae41d18dd33722b4b05a4
Former-commit-id: 4148ad3ba0358e007f10a5303671fef7d041249d
af/merge-core
James South 10 years ago
parent
commit
54bb22419a
  1. 29
      src/ImageProcessorCore/Samplers/ImageSamplerExtensions.cs
  2. 164
      src/ImageProcessorCore/Samplers/Resampler.cs
  3. 7
      src/ImageProcessorCore/Samplers/Resamplers/BoxResampler.cs
  4. 104
      src/ImageProcessorCore/Samplers/Rotate.cs

29
src/ImageProcessorCore/Samplers/ImageSamplerExtensions.cs

@ -176,34 +176,7 @@ namespace ImageProcessorCore.Samplers
/// <returns>The <see cref="Image"/></returns>
public static Image Rotate(this Image source, float degrees, ProgressEventHandler progressHandler = null)
{
return Rotate(source, degrees, new BicubicResampler(), false, progressHandler);
}
/// <summary>
/// Rotates an image by the given angle in degrees.
/// </summary>
/// <param name="source">The image to resize.</param>
/// <param name="degrees">The angle in degrees to perform the rotation.</param>
/// <param name="compand">Whether to compress and expand the image color-space to gamma correct the image during processing.</param>
/// <param name="progressHandler">A delegate which is called as progress is made processing the image.</param>
/// <returns>The <see cref="Image"/></returns>
public static Image Rotate(this Image source, float degrees, bool compand, ProgressEventHandler progressHandler = null)
{
return Rotate(source, degrees, new BicubicResampler(), compand, progressHandler);
}
/// <summary>
/// Rotates an image by the given angle in degrees.
/// </summary>
/// <param name="source">The image to resize.</param>
/// <param name="degrees">The angle in degrees to perform the rotation.</param>
/// <param name="sampler">The <see cref="IResampler"/> to perform the resampling.</param>
/// <param name="compand">Whether to compress and expand the image color-space to gamma correct the image during processing.</param>
/// <param name="progressHandler">A delegate which is called as progress is made processing the image.</param>
/// <returns>The <see cref="Image"/></returns>
public static Image Rotate(this Image source, float degrees, IResampler sampler, bool compand, ProgressEventHandler progressHandler = null)
{
Rotate processor = new Rotate(sampler) { Angle = degrees, Compand = compand };
Rotate processor = new Rotate { Angle = degrees };
processor.OnProgress += progressHandler;
try

164
src/ImageProcessorCore/Samplers/Resampler.cs

@ -6,8 +6,6 @@
namespace ImageProcessorCore.Samplers
{
using System;
using System.Collections.Generic;
using System.Threading.Tasks;
/// <summary>
/// Provides methods that allow the resampling of images using various algorithms.
@ -54,58 +52,57 @@ namespace ImageProcessorCore.Samplers
/// </returns>
protected Weights[] PrecomputeWeights(int destinationSize, int sourceSize)
{
float xscale = destinationSize / (float)sourceSize;
float width;
float scale = (float)destinationSize / sourceSize;
IResampler sampler = this.Sampler;
float fwidth = sampler.Radius;
float fscale;
float radius = sampler.Radius;
double left;
double right;
double weight = 0;
int n = 0;
int k;
double weight;
int index;
int sum;
Weights[] result = new Weights[destinationSize];
// When expanding, broaden the effective kernel support so that we still
// When shrinking, broaden the effective kernel support so that we still
// visit every source pixel.
if (xscale < 0)
if (scale < 1)
{
width = sampler.Radius / xscale;
fscale = 1 / xscale;
float width = radius / scale;
float filterScale = 1 / scale;
// Make the weights slices, one source for each column or row.
for (int i = 0; i < destinationSize; i++)
{
float centre = i / xscale;
float centre = i / scale;
left = Math.Ceiling(centre - width);
right = Math.Floor(centre + width);
float sum = 0;
result[i] = new Weights();
List<Weight> builder = new List<Weight>();
result[i] = new Weights
{
Sum = 0,
Values = new Weight[(int)Math.Floor(2 * width + 1)]
};
for (double j = left; j <= right; j++)
{
weight = centre - j;
weight = sampler.GetValue((float)weight / fscale) / fscale;
weight = sampler.GetValue((float)(weight / filterScale)) / filterScale;
if (j < 0)
{
n = (int)-j;
index = (int)-j;
}
else if (j >= sourceSize)
{
n = (int)((sourceSize - j) + sourceSize - 1);
index = (int)((sourceSize - j) + sourceSize - 1);
}
else
{
n = (int)j;
index = (int)j;
}
sum++;
builder.Add(new Weight(n, (float)weight));
sum = (int)result[i].Sum++;
result[i].Values[sum] = new Weight(index, (float)weight);
}
result[i].Values = builder.ToArray();
result[i].Sum = sum;
}
}
else
@ -113,122 +110,46 @@ namespace ImageProcessorCore.Samplers
// 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;
result[i] = new Weights();
float centre = i / scale;
left = Math.Ceiling(centre - radius);
right = Math.Floor(centre + radius);
result[i] = new Weights
{
Sum = 0,
Values = new Weight[(int)(radius * 2 + 1)]
};
List<Weight> builder = new List<Weight>();
for (double j = left; j <= right; j++)
{
weight = centre - j;
weight = sampler.GetValue((float)weight);
if (j < 0)
{
n = (int)-j;
index = (int)-j;
}
else if (j >= sourceSize)
{
n = (int)((sourceSize - j) + sourceSize - 1);
index = (int)((sourceSize - j) + sourceSize - 1);
}
else
{
n = (int)j;
index = (int)j;
}
sum++;
builder.Add(new Weight(n, (float)weight));
sum = (int)result[i].Sum++;
result[i].Values[sum] = new Weight(index, (float)weight);
}
result[i].Values = builder.ToArray();
result[i].Sum = sum;
}
}
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>
/// Represents the weight to be added to a scaled pixel.
/// </summary>
protected class Weight
protected struct Weight
{
/// <summary>
/// The pixel index.
/// </summary>
public readonly int Index;
/// <summary>
/// Initializes a new instance of the <see cref="Weight"/> class.
/// </summary>
@ -241,9 +162,14 @@ namespace ImageProcessorCore.Samplers
}
/// <summary>
/// Gets or sets the result of the interpolation algorithm.
/// Gets the pixel index.
/// </summary>
public int Index { get; }
/// <summary>
/// Gets the result of the interpolation algorithm.
/// </summary>
public float Value { get; set; }
public float Value { get; }
}
/// <summary>
@ -262,4 +188,4 @@ namespace ImageProcessorCore.Samplers
public float Sum { get; set; }
}
}
}
}

7
src/ImageProcessorCore/Samplers/Resamplers/BoxResampler.cs

@ -17,12 +17,7 @@ namespace ImageProcessorCore.Samplers
/// <inheritdoc/>
public float GetValue(float x)
{
if (x < 0)
{
x = -x;
}
if (x <= 0.5)
if (x > -0.5 && x <= 0.5)
{
return 1;
}

104
src/ImageProcessorCore/Samplers/Rotate.cs

@ -10,24 +10,13 @@ namespace ImageProcessorCore.Samplers
/// <summary>
/// Provides methods that allow the rotating of images using various algorithms.
/// </summary>
public class Rotate : Resampler
public class Rotate : ImageSampler
{
/// <summary>
/// The angle of rotation.
/// </summary>
private float angle;
/// <summary>
/// Initializes a new instance of the <see cref="Rotate"/> class.
/// </summary>
/// <param name="sampler">
/// The sampler to perform the resize operation.
/// </param>
public Rotate(IResampler sampler)
: base(sampler)
{
}
/// <summary>
/// Gets or sets the angle of rotation.
/// </summary>
@ -54,16 +43,6 @@ namespace ImageProcessorCore.Samplers
}
}
/// <inheritdoc/>
protected override void OnApply(ImageBase source, ImageBase target, Rectangle targetRectangle, Rectangle sourceRectangle)
{
if (!(this.Sampler is NearestNeighborResampler))
{
this.HorizontalWeights = this.PrecomputeWeights(targetRectangle.Width, sourceRectangle.Width);
this.VerticalWeights = this.PrecomputeWeights(targetRectangle.Height, sourceRectangle.Height);
}
}
/// <inheritdoc/>
protected override void Apply(ImageBase target, ImageBase source, Rectangle targetRectangle, Rectangle sourceRectangle, int startY, int endY)
{
@ -73,45 +52,11 @@ namespace ImageProcessorCore.Samplers
int endX = targetRectangle.Right;
float negativeAngle = -this.angle;
Point centre = Rectangle.Center(sourceRectangle);
bool compand = this.Compand;
if (this.Sampler is NearestNeighborResampler)
{
// Scaling factors
float widthFactor = source.Width / (float)target.Width;
float heightFactor = source.Height / (float)target.Height;
Parallel.For(
startY,
endY,
y =>
{
if (y >= targetY && y < targetBottom)
{
// Y coordinates of source points
int originY = (int)((y - targetY) * heightFactor);
// Scaling factors
float widthFactor = source.Width / (float)target.Width;
float heightFactor = source.Height / (float)target.Height;
for (int x = startX; x < endX; x++)
{
// X coordinates of source points
int originX = (int)((x - startX) * widthFactor);
// Rotate at the centre point
Point rotated = Point.Rotate(new Point(originX, originY), centre, negativeAngle);
if (sourceRectangle.Contains(rotated.X, rotated.Y))
{
target[x, y] = source[rotated.X, rotated.Y];
}
}
this.OnRowProcessed();
}
});
// Break out now.
return;
}
// Interpolate the image using the calculated weights.
Parallel.For(
startY,
endY,
@ -119,46 +64,21 @@ namespace ImageProcessorCore.Samplers
{
if (y >= targetY && y < targetBottom)
{
Weight[] verticalValues = this.VerticalWeights[y].Values;
// Y coordinates of source points
int originY = (int)((y - targetY) * heightFactor);
for (int x = startX; x < endX; x++)
{
Weight[] horizontalValues = this.HorizontalWeights[x].Values;
// X coordinates of source points
int originX = (int)((x - startX) * widthFactor);
// Destination color components
Color destination = new Color();
foreach (Weight yw in verticalValues)
// Rotate at the centre point
Point rotated = Point.Rotate(new Point(originX, originY), centre, negativeAngle);
if (sourceRectangle.Contains(rotated.X, rotated.Y))
{
int originY = yw.Index;
foreach (Weight xw in horizontalValues)
{
int originX = xw.Index;
// Rotate at the centre point
Point rotated = Point.Rotate(new Point(originX, originY), centre, negativeAngle);
if (sourceRectangle.Contains(rotated.X, rotated.Y))
{
target[x, y] = source[rotated.X, rotated.Y];
}
if (sourceRectangle.Contains(rotated.X, rotated.Y))
{
Color sourceColor = compand ? Color.Expand(source[rotated.X, rotated.Y]) : source[rotated.X, rotated.Y];
destination += sourceColor * yw.Value * xw.Value;
}
}
target[x, y] = source[rotated.X, rotated.Y];
}
if (compand)
{
destination = Color.Compress(destination);
}
target[x, y] = destination;
}
this.OnRowProcessed();
}
});

Loading…
Cancel
Save