Browse Source

Fix Resizer

Fix #394


Former-commit-id: 490723c7f33cbab80264901ad252541a44b1dc73
Former-commit-id: 60edb602bc6236d29495081697e2f385aaa84320
Former-commit-id: abf91473873cfad473325c0af36a7026f5e40709
af/merge-core
James Jackson-South 10 years ago
parent
commit
2771f593f6
  1. 2
      src/ImageProcessorCore/Samplers/Resampler.cs
  2. 108
      src/ImageProcessorCore/Samplers/Resize.cs
  3. 4
      src/ImageProcessorCore/Samplers/ResizeHelper.cs
  4. 47
      tests/ImageProcessorCore.Tests/Processors/Samplers/SamplerTests.cs

2
src/ImageProcessorCore/Samplers/Resampler.cs

@ -151,7 +151,7 @@ namespace ImageProcessorCore.Samplers
protected struct Weight protected struct Weight
{ {
/// <summary> /// <summary>
/// Initializes a new instance of the <see cref="Weight"/> class. /// Initializes a new instance of the <see cref="Weight"/> struct.
/// </summary> /// </summary>
/// <param name="index">The index.</param> /// <param name="index">The index.</param>
/// <param name="value">The value.</param> /// <param name="value">The value.</param>

108
src/ImageProcessorCore/Samplers/Resize.cs

@ -47,7 +47,6 @@ namespace ImageProcessorCore.Samplers
protected override void Apply(ImageBase target, ImageBase source, Rectangle targetRectangle, Rectangle sourceRectangle, int startY, int endY) protected override void Apply(ImageBase target, ImageBase source, Rectangle targetRectangle, Rectangle sourceRectangle, int startY, int endY)
{ {
// Jump out, we'll deal with that later. // Jump out, we'll deal with that later.
// TODO: Add rectangle comparison.
if (source.Bounds == target.Bounds && sourceRectangle == targetRectangle) if (source.Bounds == target.Bounds && sourceRectangle == targetRectangle)
{ {
return; return;
@ -104,80 +103,77 @@ namespace ImageProcessorCore.Samplers
// First process the columns. Since we are not using multiple threads startY and endY // First process the columns. Since we are not using multiple threads startY and endY
// are the upper and lower bounds of the source rectangle. // are the upper and lower bounds of the source rectangle.
Parallel.For( Parallel.For(
startY, 0,
endY, sourceHeight,
y => y =>
{
// Ensure offsets are normalised for cropping and padding.
int offsetY = y - startY;
for (int x = startX; x < endX; x++)
{ {
int offsetX = x - startX; for (int x = startX; x < endX; x++)
{
float sum = this.HorizontalWeights[offsetX].Sum; if (x >= 0 && x < width)
Weight[] horizontalValues = this.HorizontalWeights[offsetX].Values; {
// Ensure offsets are normalised for cropping and padding.
int offsetX = x - startX;
float sum = this.HorizontalWeights[offsetX].Sum;
Weight[] horizontalValues = this.HorizontalWeights[offsetX].Values;
// Destination color components // Destination color components
Color destination = new Color(); Color destination = new Color();
for (int i = 0; i < sum; i++) for (int i = 0; i < sum; i++)
{ {
Weight xw = horizontalValues[i]; Weight xw = horizontalValues[i];
int originX = xw.Index; int originX = xw.Index;
Color sourceColor = compand ? Color.Expand(source[originX, offsetY]) : source[originX, offsetY]; Color sourceColor = compand ? Color.Expand(source[originX, y]) : source[originX, y];
destination += sourceColor * xw.Value; destination += sourceColor * xw.Value;
} }
if (compand) if (compand)
{ {
destination = Color.Compress(destination); destination = Color.Compress(destination);
} }
if (x >= 0 && x < width && offsetY >= 0 && offsetY < sourceHeight) this.firstPass[x, y] = destination;
{ }
this.firstPass[x, offsetY] = destination;
} }
} });
});
// Now process the rows. // Now process the rows.
Parallel.For( Parallel.For(
startY, startY,
endY, endY,
y => y =>
{
// Ensure offsets are normalised for cropping and padding.
int offsetY = y - startY;
float sum = this.VerticalWeights[offsetY].Sum;
Weight[] verticalValues = this.VerticalWeights[offsetY].Values;
for (int x = 0; x < width; x++)
{ {
// Destination color components if (y >= 0 && y < height)
Color destination = new Color();
for (int i = 0; i < sum; i++)
{ {
Weight yw = verticalValues[i]; // Ensure offsets are normalised for cropping and padding.
int originY = yw.Index; int offsetY = y - startY;
Color sourceColor = compand ? Color.Expand(this.firstPass[x, originY]) : this.firstPass[x, originY]; float sum = this.VerticalWeights[offsetY].Sum;
destination += sourceColor * yw.Value; Weight[] verticalValues = this.VerticalWeights[offsetY].Values;
}
if (compand) for (int x = 0; x < width; x++)
{ {
destination = Color.Compress(destination); // Destination color components
} Color destination = new Color();
if (y >= 0 && y < height) for (int i = 0; i < sum; i++)
{ {
target[x, y] = destination; Weight yw = verticalValues[i];
int originY = yw.Index;
Color sourceColor = compand ? Color.Expand(this.firstPass[x, originY]) : this.firstPass[x, originY];
destination += sourceColor * yw.Value;
}
if (compand)
{
destination = Color.Compress(destination);
}
target[x, y] = destination;
}
} }
}
this.OnRowProcessed(); this.OnRowProcessed();
}); });
} }
/// <inheritdoc/> /// <inheritdoc/>

4
src/ImageProcessorCore/Samplers/ResizeHelper.cs

@ -394,12 +394,14 @@ namespace ImageProcessorCore.Samplers
if (widthDiff < heightDiff) if (widthDiff < heightDiff)
{ {
destinationHeight = Convert.ToInt32(width * sourceRatio); destinationHeight = Convert.ToInt32(width * sourceRatio);
height = destinationHeight;
destinationWidth = width; destinationWidth = width;
} }
else if (widthDiff > heightDiff) else if (widthDiff > heightDiff)
{ {
destinationHeight = height;
destinationWidth = Convert.ToInt32(height / sourceRatio); destinationWidth = Convert.ToInt32(height / sourceRatio);
destinationHeight = height;
width = destinationWidth;
} }
else else
{ {

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

@ -182,7 +182,7 @@
{ {
ResizeOptions options = new ResizeOptions() ResizeOptions options = new ResizeOptions()
{ {
Size = new Size(image.Width , image.Height / 2) Size = new Size(image.Width / 2, image.Height)
}; };
image.Resize(options, this.ProgressUpdate) image.Resize(options, this.ProgressUpdate)
@ -214,9 +214,8 @@
{ {
ResizeOptions options = new ResizeOptions() ResizeOptions options = new ResizeOptions()
{ {
Size = new Size(image.Width , image.Height + 200), Size = new Size(image.Width + 200, image.Height),
Mode = ResizeMode.Pad, Mode = ResizeMode.Pad
Sampler = new NearestNeighborResampler()
}; };
image.Resize(options, this.ProgressUpdate) image.Resize(options, this.ProgressUpdate)
@ -281,8 +280,9 @@
{ {
ResizeOptions options = new ResizeOptions() ResizeOptions options = new ResizeOptions()
{ {
Size = new Size(image.Width + 200, image.Height), Size = new Size(300, 300),
Mode = ResizeMode.Max Mode = ResizeMode.Max,
//Sampler = new NearestNeighborResampler()
}; };
image.Resize(options, this.ProgressUpdate) image.Resize(options, this.ProgressUpdate)
@ -314,7 +314,7 @@
{ {
ResizeOptions options = new ResizeOptions() ResizeOptions options = new ResizeOptions()
{ {
Size = new Size(image.Width + 200, image.Height), Size = new Size(image.Width - 50, image.Height - 25),
Mode = ResizeMode.Min Mode = ResizeMode.Min
}; };
@ -327,6 +327,39 @@
} }
} }
[Fact]
public void ImageShouldResizeWithStretchMode()
{
if (!Directory.Exists("TestOutput/ResizeStretch"))
{
Directory.CreateDirectory("TestOutput/ResizeStretch");
}
foreach (string file in Files)
{
using (FileStream stream = File.OpenRead(file))
{
Stopwatch watch = Stopwatch.StartNew();
string filename = Path.GetFileName(file);
using (Image image = new Image(stream))
using (FileStream output = File.OpenWrite($"TestOutput/ResizeStretch/{filename}"))
{
ResizeOptions options = new ResizeOptions()
{
Size = new Size(image.Width - 200, image.Height),
Mode = ResizeMode.Stretch
};
image.Resize(options, this.ProgressUpdate)
.Save(output);
}
Trace.WriteLine($"{filename}: {watch.ElapsedMilliseconds}ms");
}
}
}
[Theory] [Theory]
[MemberData("RotateFlips")] [MemberData("RotateFlips")]
public void ImageShouldRotateFlip(RotateType rotateType, FlipType flipType) public void ImageShouldRotateFlip(RotateType rotateType, FlipType flipType)

Loading…
Cancel
Save