diff --git a/src/ImageProcessor/ImageProcessor.csproj b/src/ImageProcessor/ImageProcessor.csproj
index af3cae8ebc..a7ec6d89a8 100644
--- a/src/ImageProcessor/ImageProcessor.csproj
+++ b/src/ImageProcessor/ImageProcessor.csproj
@@ -196,8 +196,8 @@
-
+
diff --git a/src/ImageProcessor/ParallelImageProcessor.cs b/src/ImageProcessor/ParallelImageProcessor.cs
index ea913776c9..fa224b44ae 100644
--- a/src/ImageProcessor/ParallelImageProcessor.cs
+++ b/src/ImageProcessor/ParallelImageProcessor.cs
@@ -16,7 +16,7 @@ namespace ImageProcessor
///
/// Gets or sets the count of workers to run the process in parallel.
///
- public int Parallelism { get; set; } = Environment.ProcessorCount;
+ public virtual int Parallelism { get; set; } = Environment.ProcessorCount;
///
public void Apply(ImageBase target, ImageBase source, Rectangle sourceRectangle)
@@ -67,7 +67,7 @@ namespace ImageProcessor
{
sourceRectangle = source.Bounds;
}
-
+ this.Parallelism = 1;
if (this.Parallelism > 1)
{
int partitionCount = this.Parallelism;
diff --git a/src/ImageProcessor/Samplers/Resize - Copy.cs b/src/ImageProcessor/Samplers/Resize - Copy.cs
deleted file mode 100644
index 65a07933f3..0000000000
--- a/src/ImageProcessor/Samplers/Resize - Copy.cs
+++ /dev/null
@@ -1,180 +0,0 @@
-//
-// Copyright © James South and contributors.
-// Licensed under the Apache License, Version 2.0.
-//
-
-namespace ImageProcessor.Samplers
-{
- using System;
- using System.Collections.Generic;
-
- ///
- /// Provides methods that allow the resizing of images using various resampling algorithms.
- ///
- public class Resize : ParallelImageProcessor
- {
- ///
- /// The epsilon for comparing floating point numbers.
- ///
- private const float Epsilon = 0.0001f;
-
- ///
- /// Initializes a new instance of the class.
- ///
- ///
- /// The sampler to perform the resize operation.
- ///
- public Resize(IResampler sampler)
- {
- Guard.NotNull(sampler, nameof(sampler));
-
- this.Sampler = sampler;
- }
-
- ///
- /// Gets the sampler to perform the resize operation.
- ///
- public IResampler Sampler { get; }
-
- ///
- protected override void Apply(ImageBase target, ImageBase source, Rectangle targetRectangle, Rectangle sourceRectangle, int startY, int endY)
- {
- int sourceWidth = source.Width;
- int sourceHeight = source.Height;
-
- int width = target.Width;
- int height = target.Height;
-
- int targetY = targetRectangle.Y;
- int startX = targetRectangle.X;
- int endX = targetRectangle.Right;
-
- // Scaling factors
- double widthFactor = sourceWidth / (double)targetRectangle.Width;
- double heightFactor = sourceHeight / (double)targetRectangle.Height;
- int sectionHeight = endY - startY;
-
- Weights[] horizontalWeights = this.PrecomputeWeights(targetRectangle.Width, sourceRectangle.Width, this.Sampler);
- Weights[] verticalWeights = this.PrecomputeWeights(sectionHeight, (int)((sectionHeight * heightFactor) + .5), this.Sampler);
-
- // Width and height decreased by 1
- int maxHeight = sourceHeight - 1;
- int maxWidth = sourceWidth - 1;
-
- for (int y = startY; y < endY; y++)
- {
- List verticalValues = verticalWeights[y - startY].Values;
- double verticalSum = verticalWeights[y - startY].Sum;
-
- for (int x = startX; x < endX; x++)
- {
- List horizontalValues = horizontalWeights[x - startX].Values;
- double horizontalSum = horizontalWeights[x - startX].Sum;
-
-
- // Destination color components
- double r = 0;
- double g = 0;
- double b = 0;
- double a = 0;
-
- foreach (Weight yw in verticalValues)
- {
- int originY = yw.Index + startY; //- targetY;//(int)(((y - targetY) * heightFactor) - 0.5);
- originY = originY.Clamp(0, maxHeight);
-
- foreach (Weight xw in horizontalValues)
- {
- int originX = xw.Index;//(int)(((x - startX) * widthFactor) - 0.5);
- originX = originX.Clamp(0, maxWidth);
-
- Bgra sourceColor = source[originX, originY];
- r += sourceColor.R * (yw.Value / verticalSum) * (xw.Value / horizontalSum);
- g += sourceColor.G * (yw.Value / verticalSum) * (xw.Value / horizontalSum);
- b += sourceColor.B * (yw.Value / verticalSum) * (xw.Value / horizontalSum);
- a += sourceColor.A * (yw.Value / verticalSum) * (xw.Value / horizontalSum);
- }
- }
-
- Bgra destinationColor = new Bgra(b.ToByte(), g.ToByte(), r.ToByte(), a.ToByte());
- target[x, y] = destinationColor;
- }
- }
- }
-
- private Weights[] PrecomputeWeights(int dstSize, int srcSize, IResampler sampler)
- {
- float du = srcSize / (float)dstSize;
- float scale = du;
-
- if (scale < 1)
- {
- scale = 1;
- }
-
- double ru = Math.Ceiling(scale * sampler.Radius);
- Weights[] result = new Weights[dstSize];
-
- for (int i = 0; i < dstSize; i++)
- {
- double fu = ((i + .5) * du) - 0.5;
- int startU = (int)Math.Ceiling(fu - ru);
-
- if (startU < 0)
- {
- startU = 0;
- }
-
- int endU = (int)Math.Floor(fu + ru);
-
- if (endU > srcSize - 1)
- {
- endU = srcSize - 1;
- }
-
- double sum = 0;
- result[i] = new Weights();
-
- for (int a = startU; a <= endU; a++)
- {
- double w = 255 * sampler.GetValue((a - fu) / scale);
-
- if (Math.Abs(w) > Epsilon)
- {
- sum += w;
- result[i].Values.Add(new Weight(a, w));
- }
- }
-
- result[i].Sum = sum;
- }
-
- return result;
- }
-
- public struct Weight
- {
- public Weight(int index, double value)
- {
- this.Index = index;
- this.Value = value;
- }
-
- public readonly int Index;
-
- public readonly double Value;
- }
-
- public class Weights
- {
- public Weights()
- {
- this.Values = new List();
- }
-
- public List Values { get; set; }
-
- public double Sum { get; set; }
- }
- }
-}
diff --git a/src/ImageProcessor/Samplers/Resize.cs b/src/ImageProcessor/Samplers/Resize.cs
index 47125cfd3d..4ed61dc6a4 100644
--- a/src/ImageProcessor/Samplers/Resize.cs
+++ b/src/ImageProcessor/Samplers/Resize.cs
@@ -6,11 +6,12 @@
namespace ImageProcessor.Samplers
{
using System;
+ using System.Collections.Generic;
///
/// Provides methods that allow the resizing of images using various resampling algorithms.
///
- public class ResizeB : ParallelImageProcessor
+ public class Resize : ParallelImageProcessor
{
///
/// The epsilon for comparing floating point numbers.
@@ -23,13 +24,16 @@ namespace ImageProcessor.Samplers
///
/// The sampler to perform the resize operation.
///
- public ResizeB(IResampler sampler)
+ public Resize(IResampler sampler)
{
Guard.NotNull(sampler, nameof(sampler));
this.Sampler = sampler;
}
+ ///
+ public override int Parallelism => 1; // Uncomment this to see bug.
+
///
/// Gets the sampler to perform the resize operation.
///
@@ -47,12 +51,14 @@ namespace ImageProcessor.Samplers
int targetY = targetRectangle.Y;
int startX = targetRectangle.X;
int endX = targetRectangle.Right;
- int right = (int)(this.Sampler.Radius + .5);
- int left = (-right) + 1;
// Scaling factors
- double widthFactor = sourceWidth / (double)targetRectangle.Width;
double heightFactor = sourceHeight / (double)targetRectangle.Height;
+ int targetSectionHeight = endY - startY;
+ int sourceSectionHeight = (int)((targetSectionHeight * heightFactor) + .5);
+
+ Weights[] horizontalWeights = this.PrecomputeWeights(targetRectangle.Width, sourceRectangle.Width, this.Sampler);
+ Weights[] verticalWeights = this.PrecomputeWeights(targetSectionHeight, sourceSectionHeight, this.Sampler);
// Width and height decreased by 1
int maxHeight = sourceHeight - 1;
@@ -62,20 +68,15 @@ namespace ImageProcessor.Samplers
{
if (y >= 0 && y < height)
{
- // Y coordinates of source points.
- double originY = ((y - targetY) * heightFactor) - 0.5;
- int originY1 = (int)originY;
- double dy = originY - originY1;
+ List verticalValues = verticalWeights[y - startY].Values;
+ double verticalSum = verticalWeights[y - startY].Sum;
- // For each row.
for (int x = startX; x < endX; x++)
{
if (x >= 0 && x < width)
{
- // X coordinates of source points.
- double originX = ((x - startX) * widthFactor) - 0.5f;
- int originX1 = (int)originX;
- double dx = originX - originX1;
+ List horizontalValues = horizontalWeights[x - startX].Values;
+ double horizontalSum = horizontalWeights[x - startX].Sum;
// Destination color components
double r = 0;
@@ -83,65 +84,132 @@ namespace ImageProcessor.Samplers
double b = 0;
double a = 0;
- for (int yy = left; yy <= right; yy++)
+ foreach (Weight yw in verticalValues)
{
- // Get Y cooefficient
- double kernel1 = this.Sampler.GetValue(yy - dy);
-
- if (Math.Abs(kernel1) < Epsilon)
+ if (Math.Abs(yw.Value) < Epsilon)
{
continue;
}
- int originY2 = originY1 + yy;
- if (originY2 < 0)
- {
- originY2 = 0;
- }
-
- if (originY2 > maxHeight)
- {
- originY2 = maxHeight;
- }
+ // TODO: This is wrong. Adding (int)((startY * heightFactor) - .5) gets close but no cigar.
+ int originY = yw.Index + (int)((startY * heightFactor) - .5);
+ originY = originY.Clamp(0, maxHeight);
- for (int xx = left; xx <= right; xx++)
+ foreach (Weight xw in horizontalValues)
{
- // Get X cooefficient
- double kernel2 = kernel1 * this.Sampler.GetValue(xx - dx);
-
- if (Math.Abs(kernel2) < Epsilon)
+ if (Math.Abs(xw.Value) < Epsilon)
{
continue;
}
- int originX2 = originX1 + xx;
- if (originX2 < 0)
- {
- originX2 = 0;
- }
-
- if (originX2 > maxWidth)
- {
- originX2 = maxWidth;
- }
-
- Bgra sourceColor = source[originX2, originY2];
- // sourceColor = PixelOperations.ToLinear(sourceColor);
+ // TODO: This need updating to take into account the target rectangle.
+ int originX = xw.Index;
+ originX = originX.Clamp(0, maxWidth);
- r += kernel2 * sourceColor.R;
- g += kernel2 * sourceColor.G;
- b += kernel2 * sourceColor.B;
- a += kernel2 * sourceColor.A;
+ Bgra sourceColor = source[originX, originY];
+ r += sourceColor.R * (yw.Value / verticalSum) * (xw.Value / horizontalSum);
+ g += sourceColor.G * (yw.Value / verticalSum) * (xw.Value / horizontalSum);
+ b += sourceColor.B * (yw.Value / verticalSum) * (xw.Value / horizontalSum);
+ a += sourceColor.A * (yw.Value / verticalSum) * (xw.Value / horizontalSum);
}
}
Bgra destinationColor = new Bgra(b.ToByte(), g.ToByte(), r.ToByte(), a.ToByte());
- // destinationColor = PixelOperations.ToSrgb(destinationColor);
target[x, y] = destinationColor;
}
}
}
}
}
+
+ ///
+ /// Computes the weights to apply at each pixel when resizing.
+ ///
+ ///
+ /// The destination section size.
+ ///
+ ///
+ /// The source section size.
+ ///
+ ///
+ /// The containing the resampling algorithm.
+ ///
+ ///
+ /// The .
+ ///
+ private Weights[] PrecomputeWeights(int destinationSize, int sourceSize, IResampler sampler)
+ {
+ float du = sourceSize / (float)destinationSize;
+ float scale = du;
+
+ if (scale < 1)
+ {
+ scale = 1;
+ }
+
+ double ru = Math.Ceiling(scale * sampler.Radius);
+ Weights[] result = new Weights[destinationSize];
+
+ for (int i = 0; i < destinationSize; i++)
+ {
+ double fu = ((i + .5) * du) - 0.5;
+ int startU = (int)Math.Ceiling(fu - ru);
+
+ if (startU < 0)
+ {
+ startU = 0;
+ }
+
+ int endU = (int)Math.Floor(fu + ru);
+
+ if (endU > sourceSize - 1)
+ {
+ endU = sourceSize - 1;
+ }
+
+ double sum = 0;
+ result[i] = new Weights();
+
+ for (int a = startU; a <= endU; a++)
+ {
+ double w = 255 * sampler.GetValue((a - fu) / scale);
+
+ if (Math.Abs(w) > Epsilon)
+ {
+ sum += w;
+ result[i].Values.Add(new Weight(a, w));
+ }
+ }
+
+ result[i].Sum = sum;
+ }
+
+ return result;
+ }
+
+ protected struct Weight
+ {
+ public Weight(int index, double value)
+ {
+ this.Index = index;
+ this.Value = value;
+ }
+
+ public readonly int Index;
+
+ public readonly double Value;
+ }
+
+ protected class Weights
+ {
+ public Weights()
+ {
+ this.Values = new List();
+ }
+
+ public List Values { get; set; }
+
+ public double Sum { get; set; }
+ }
}
}
diff --git a/src/ImageProcessor/Samplers/ResizeOld.cs b/src/ImageProcessor/Samplers/ResizeOld.cs
new file mode 100644
index 0000000000..47125cfd3d
--- /dev/null
+++ b/src/ImageProcessor/Samplers/ResizeOld.cs
@@ -0,0 +1,147 @@
+//
+// Copyright © James South and contributors.
+// Licensed under the Apache License, Version 2.0.
+//
+
+namespace ImageProcessor.Samplers
+{
+ using System;
+
+ ///
+ /// Provides methods that allow the resizing of images using various resampling algorithms.
+ ///
+ public class ResizeB : ParallelImageProcessor
+ {
+ ///
+ /// The epsilon for comparing floating point numbers.
+ ///
+ private const float Epsilon = 0.0001f;
+
+ ///
+ /// Initializes a new instance of the class.
+ ///
+ ///
+ /// The sampler to perform the resize operation.
+ ///
+ public ResizeB(IResampler sampler)
+ {
+ Guard.NotNull(sampler, nameof(sampler));
+
+ this.Sampler = sampler;
+ }
+
+ ///
+ /// Gets the sampler to perform the resize operation.
+ ///
+ public IResampler Sampler { get; }
+
+ ///
+ protected override void Apply(ImageBase target, ImageBase source, Rectangle targetRectangle, Rectangle sourceRectangle, int startY, int endY)
+ {
+ int sourceWidth = source.Width;
+ int sourceHeight = source.Height;
+
+ int width = target.Width;
+ int height = target.Height;
+
+ int targetY = targetRectangle.Y;
+ int startX = targetRectangle.X;
+ int endX = targetRectangle.Right;
+ int right = (int)(this.Sampler.Radius + .5);
+ int left = (-right) + 1;
+
+ // Scaling factors
+ double widthFactor = sourceWidth / (double)targetRectangle.Width;
+ double heightFactor = sourceHeight / (double)targetRectangle.Height;
+
+ // Width and height decreased by 1
+ int maxHeight = sourceHeight - 1;
+ int maxWidth = sourceWidth - 1;
+
+ for (int y = startY; y < endY; y++)
+ {
+ if (y >= 0 && y < height)
+ {
+ // Y coordinates of source points.
+ double originY = ((y - targetY) * heightFactor) - 0.5;
+ int originY1 = (int)originY;
+ double dy = originY - originY1;
+
+ // For each row.
+ for (int x = startX; x < endX; x++)
+ {
+ if (x >= 0 && x < width)
+ {
+ // X coordinates of source points.
+ double originX = ((x - startX) * widthFactor) - 0.5f;
+ int originX1 = (int)originX;
+ double dx = originX - originX1;
+
+ // Destination color components
+ double r = 0;
+ double g = 0;
+ double b = 0;
+ double a = 0;
+
+ for (int yy = left; yy <= right; yy++)
+ {
+ // Get Y cooefficient
+ double kernel1 = this.Sampler.GetValue(yy - dy);
+
+ if (Math.Abs(kernel1) < Epsilon)
+ {
+ continue;
+ }
+
+ int originY2 = originY1 + yy;
+ if (originY2 < 0)
+ {
+ originY2 = 0;
+ }
+
+ if (originY2 > maxHeight)
+ {
+ originY2 = maxHeight;
+ }
+
+ for (int xx = left; xx <= right; xx++)
+ {
+ // Get X cooefficient
+ double kernel2 = kernel1 * this.Sampler.GetValue(xx - dx);
+
+ if (Math.Abs(kernel2) < Epsilon)
+ {
+ continue;
+ }
+
+ int originX2 = originX1 + xx;
+ if (originX2 < 0)
+ {
+ originX2 = 0;
+ }
+
+ if (originX2 > maxWidth)
+ {
+ originX2 = maxWidth;
+ }
+
+ Bgra sourceColor = source[originX2, originY2];
+ // sourceColor = PixelOperations.ToLinear(sourceColor);
+
+ r += kernel2 * sourceColor.R;
+ g += kernel2 * sourceColor.G;
+ b += kernel2 * sourceColor.B;
+ a += kernel2 * sourceColor.A;
+ }
+ }
+
+ Bgra destinationColor = new Bgra(b.ToByte(), g.ToByte(), r.ToByte(), a.ToByte());
+ // destinationColor = PixelOperations.ToSrgb(destinationColor);
+ target[x, y] = destinationColor;
+ }
+ }
+ }
+ }
+ }
+ }
+}
diff --git a/tests/ImageProcessor.Tests/Processors/Samplers/SamplerTests.cs b/tests/ImageProcessor.Tests/Processors/Samplers/SamplerTests.cs
index 759095b336..881e45bdb3 100644
--- a/tests/ImageProcessor.Tests/Processors/Samplers/SamplerTests.cs
+++ b/tests/ImageProcessor.Tests/Processors/Samplers/SamplerTests.cs
@@ -13,18 +13,18 @@ namespace ImageProcessor.Tests
public static readonly TheoryData Samplers =
new TheoryData
{
- //{ "Bicubic", new BicubicResampler() },
- //{ "Bilinear", new TriangleResampler() },
- { "NearestNeighbour", new BoxResampler() },
- //{ "Lanczos3", new Lanczos3Resampler() },
+ { "Bicubic", new BicubicResampler() },
+ { "Triangle", new TriangleResampler() },
+ { "Box", new BoxResampler() },
+ { "Lanczos3", new Lanczos3Resampler() },
{ "Lanczos8", new Lanczos8Resampler() },
- //{ "MitchellNetravali", new MitchellNetravaliResampler() },
- //{ "Hermite", new HermiteResampler() },
- //{ "Spline", new SplineResampler() },
- //{ "Robidoux", new RobidouxResampler() },
- //{ "RobidouxSharp", new RobidouxSharpResampler() },
- //{ "RobidouxSoft", new RobidouxSoftResampler() },
- //{ "Welch", new WelchResampler() }
+ { "MitchellNetravali", new MitchellNetravaliResampler() },
+ { "Hermite", new HermiteResampler() },
+ { "Spline", new SplineResampler() },
+ { "Robidoux", new RobidouxResampler() },
+ { "RobidouxSharp", new RobidouxSharpResampler() },
+ { "RobidouxSoft", new RobidouxSoftResampler() },
+ { "Welch", new WelchResampler() }
};
[Theory]
@@ -45,7 +45,7 @@ namespace ImageProcessor.Tests
string filename = Path.GetFileNameWithoutExtension(file) + "-" + name + Path.GetExtension(file);
using (FileStream output = File.OpenWrite($"Resized/{filename}"))
{
- image.Resize(500, 500, sampler).Save(output);
+ image.Resize(100, 100, sampler).Save(output);
}
Trace.WriteLine($"{name}: {watch.ElapsedMilliseconds}ms");