From e3a2334edebd4925c636077d0eeed17cdd0d83c6 Mon Sep 17 00:00:00 2001 From: James Jackson-South Date: Thu, 22 Oct 2015 00:09:57 +1100 Subject: [PATCH] Resizer updates Getting closer but still not quite right. Former-commit-id: 7df2d4c45591ba8e71e073c29d61b73e25c38cf0 Former-commit-id: cb63ea1d1341f15a3b6d82fb6b0a6c842107bcf9 Former-commit-id: e3a69acf2949c0081381ca71214c673581712e93 --- .../Common/Helpers/PixelOperations.cs | 4 +- src/ImageProcessor/ParallelImageProcessor.cs | 2 +- src/ImageProcessor/Samplers/Resize.cs | 107 ++++++++++++++---- .../Processors/ProcessorTestBase.cs | 12 +- .../Processors/Samplers/SamplerTests.cs | 2 +- .../gamma_dalai_lama_gray.jpg.REMOVED.git-id | 1 + .../Formats/Png/gamma-1.0-or-2.2.png | Bin 0 -> 4195 bytes 7 files changed, 97 insertions(+), 31 deletions(-) create mode 100644 tests/ImageProcessor.Tests/TestImages/Formats/Jpg/gamma_dalai_lama_gray.jpg.REMOVED.git-id create mode 100644 tests/ImageProcessor.Tests/TestImages/Formats/Png/gamma-1.0-or-2.2.png diff --git a/src/ImageProcessor/Common/Helpers/PixelOperations.cs b/src/ImageProcessor/Common/Helpers/PixelOperations.cs index 0c4197de96..6352025d52 100644 --- a/src/ImageProcessor/Common/Helpers/PixelOperations.cs +++ b/src/ImageProcessor/Common/Helpers/PixelOperations.cs @@ -38,7 +38,7 @@ namespace ImageProcessor // Create only once and lazily. byte[] ramp = LinearBytes.Value; - return new Bgra(composite.B, ramp[composite.G], ramp[composite.R], ramp[composite.A]); + return new Bgra(ramp[composite.B], ramp[composite.G], ramp[composite.R], composite.A); } /// @@ -55,7 +55,7 @@ namespace ImageProcessor // Create only once and lazily. byte[] ramp = SrgbBytes.Value; - return new Bgra(linear.B, ramp[linear.G], ramp[linear.R], ramp[linear.A]); + return new Bgra(ramp[linear.B], ramp[linear.G], ramp[linear.R], linear.A); } /// diff --git a/src/ImageProcessor/ParallelImageProcessor.cs b/src/ImageProcessor/ParallelImageProcessor.cs index fa224b44ae..72722dd916 100644 --- a/src/ImageProcessor/ParallelImageProcessor.cs +++ b/src/ImageProcessor/ParallelImageProcessor.cs @@ -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.cs b/src/ImageProcessor/Samplers/Resize.cs index cdcdd4f46e..da62075666 100644 --- a/src/ImageProcessor/Samplers/Resize.cs +++ b/src/ImageProcessor/Samplers/Resize.cs @@ -63,8 +63,10 @@ namespace ImageProcessor.Samplers 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); + int offsetY = this.CalculateOffset(startY, targetSectionHeight, sourceSectionHeight); + int offsetX = this.CalculateOffset(startX, targetRectangle.Width, sourceRectangle.Width); + Weights[] horizontalWeights = this.PrecomputeWeights(targetRectangle.Width, sourceRectangle.Width); + Weights[] verticalWeights = this.PrecomputeWeights(targetSectionHeight, sourceSectionHeight); // Width and height decreased by 1 int maxHeight = sourceHeight - 1; @@ -97,8 +99,8 @@ namespace ImageProcessor.Samplers continue; } - // TODO: This is wrong. Adding (int)((startY * heightFactor) - .5) gets close but no cigar. - int originY = yw.Index + (int)((startY * heightFactor) - .5); + // TODO: This offset is wrong. + int originY = offsetY == 0 ? yw.Index : yw.Index + offsetY; originY = originY.Clamp(0, maxHeight); foreach (Weight xw in horizontalValues) @@ -108,11 +110,13 @@ namespace ImageProcessor.Samplers continue; } - // TODO: This need updating to take into account the target rectangle. - int originX = xw.Index; + // TODO: This offset is wrong. + int originX = xw.Index + offsetX; originX = originX.Clamp(0, maxWidth); Bgra sourceColor = source[originX, originY]; + sourceColor = PixelOperations.ToLinear(sourceColor); + 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); @@ -121,6 +125,7 @@ namespace ImageProcessor.Samplers } Bgra destinationColor = new Bgra(b.ToByte(), g.ToByte(), r.ToByte(), a.ToByte()); + destinationColor = PixelOperations.ToSrgb(destinationColor); target[x, y] = destinationColor; } } @@ -131,22 +136,16 @@ namespace ImageProcessor.Samplers /// /// Computes the weights to apply at each pixel when resizing. /// - /// - /// The destination section size. - /// - /// - /// The source section size. - /// - /// - /// The containing the resampling algorithm. - /// + /// The destination section size. + /// The source section size. /// /// The . /// - private Weights[] PrecomputeWeights(int destinationSize, int sourceSize, IResampler sampler) + private Weights[] PrecomputeWeights(int destinationSize, int sourceSize) { - float du = sourceSize / (float)destinationSize; - float scale = du; + IResampler sampler = this.Sampler; + double du = sourceSize / (double)destinationSize; + double scale = du; if (scale < 1) { @@ -193,28 +192,92 @@ namespace ImageProcessor.Samplers return result; } - protected struct Weight + /// + /// Calculates the scaled offset caused by parallelism. + /// + /// The offset position. + /// The destination size. + /// The source size. + /// + /// The . + /// + private int CalculateOffset(int offset, int destinationSize, int sourceSize) { - public Weight(int index, double value) + if (offset == 0) { - this.Index = index; - this.Value = value; + return 0; + } + + IResampler sampler = this.Sampler; + double du = sourceSize / (double)destinationSize; + double scale = du; + + if (scale < 1) + { + scale = 1; + } + + double ru = Math.Ceiling(scale * sampler.Radius); + + double fu = ((offset + .5) * du) - 0.5; + int result = (int)Math.Ceiling(fu - ru); + + if (result < 0) + { + return 0; } + return result; + } + + /// + /// Represents the weight to be added to a scaled pixel. + /// + protected struct Weight + { + /// + /// The pixel index. + /// public readonly int Index; + /// + /// The result of the interpolation algorithm. + /// public readonly double Value; + + /// + /// Initializes a new instance of the struct. + /// + /// The index. + /// The value. + public Weight(int index, double value) + { + this.Index = index; + this.Value = value; + } } + /// + /// Represents a collection of weights and their sum. + /// protected class Weights { + /// + /// Initializes a new instance of the class. + /// public Weights() { this.Values = new List(); } + /// + /// Gets or sets the values. + /// public List Values { get; set; } + /// + /// Gets or sets the sum. + /// public double Sum { get; set; } } } diff --git a/tests/ImageProcessor.Tests/Processors/ProcessorTestBase.cs b/tests/ImageProcessor.Tests/Processors/ProcessorTestBase.cs index da88b7c94f..01c38bd436 100644 --- a/tests/ImageProcessor.Tests/Processors/ProcessorTestBase.cs +++ b/tests/ImageProcessor.Tests/Processors/ProcessorTestBase.cs @@ -19,11 +19,13 @@ namespace ImageProcessor.Tests /// public static readonly List Files = new List { - //"../../TestImages/Formats/Jpg/Backdrop.jpg", - //"../../TestImages/Formats/Jpg/Calliphora.jpg", - //"../../TestImages/Formats/Bmp/Car.bmp", - //"../../TestImages/Formats/Png/cmyk.png", - //"../../TestImages/Formats/Gif/leaf.gif" + "../../TestImages/Formats/Jpg/Backdrop.jpg", + "../../TestImages/Formats/Jpg/Calliphora.jpg", + "../../TestImages/Formats/Jpg/gamma_dalai_lama_gray.jpg", + "../../TestImages/Formats/Bmp/Car.bmp", + "../../TestImages/Formats/Png/cmyk.png", + "../../TestImages/Formats/Png/gamma-1.0-or-2.2.png", + "../../TestImages/Formats/Gif/leaf.gif", "../../TestImages/Formats/Gif/rings.gif" // { "../../TestImages/Formats/Gif/ani.gif" }, diff --git a/tests/ImageProcessor.Tests/Processors/Samplers/SamplerTests.cs b/tests/ImageProcessor.Tests/Processors/Samplers/SamplerTests.cs index 881e45bdb3..0ce2733468 100644 --- a/tests/ImageProcessor.Tests/Processors/Samplers/SamplerTests.cs +++ b/tests/ImageProcessor.Tests/Processors/Samplers/SamplerTests.cs @@ -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(100, 100, sampler).Save(output); + image.Resize(500, 500, sampler).Save(output); } Trace.WriteLine($"{name}: {watch.ElapsedMilliseconds}ms"); diff --git a/tests/ImageProcessor.Tests/TestImages/Formats/Jpg/gamma_dalai_lama_gray.jpg.REMOVED.git-id b/tests/ImageProcessor.Tests/TestImages/Formats/Jpg/gamma_dalai_lama_gray.jpg.REMOVED.git-id new file mode 100644 index 0000000000..c6c87c96c7 --- /dev/null +++ b/tests/ImageProcessor.Tests/TestImages/Formats/Jpg/gamma_dalai_lama_gray.jpg.REMOVED.git-id @@ -0,0 +1 @@ +56cbc3371def2882d1ead5d4d2456550f2b8d72c \ No newline at end of file diff --git a/tests/ImageProcessor.Tests/TestImages/Formats/Png/gamma-1.0-or-2.2.png b/tests/ImageProcessor.Tests/TestImages/Formats/Png/gamma-1.0-or-2.2.png new file mode 100644 index 0000000000000000000000000000000000000000..6d2ba91fb1c35b2f90f94a38e22886262efb2432 GIT binary patch literal 4195 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 YKJ%G><%)?9tOxMi@8j`sZ_uUx00Ck-?f?J) literal 0 HcmV?d00001