mirror of https://github.com/SixLabors/ImageSharp
Browse Source
If Parallelism > 1 then something goes wrong with the pixel sampling. It could be as simple as a rounding error but I'm struggling to see what the bug cause is. Former-commit-id: 222bb4f5982a37ad03dc1ddd47a7d437699ba6b6 Former-commit-id: 1e85ef9bfa7f17bb703c84dee5b6b18734ca7401 Former-commit-id: f6bbb2fbae3df6e9753e16dbcf80c75c7d7f93a3af/merge-core
6 changed files with 282 additions and 247 deletions
@ -1,180 +0,0 @@ |
|||||
// <copyright file="Resize.cs" company="James South">
|
|
||||
// Copyright © James South and contributors.
|
|
||||
// Licensed under the Apache License, Version 2.0.
|
|
||||
// </copyright>
|
|
||||
|
|
||||
namespace ImageProcessor.Samplers |
|
||||
{ |
|
||||
using System; |
|
||||
using System.Collections.Generic; |
|
||||
|
|
||||
/// <summary>
|
|
||||
/// Provides methods that allow the resizing of images using various resampling algorithms.
|
|
||||
/// </summary>
|
|
||||
public class Resize : ParallelImageProcessor |
|
||||
{ |
|
||||
/// <summary>
|
|
||||
/// The epsilon for comparing floating point numbers.
|
|
||||
/// </summary>
|
|
||||
private const float Epsilon = 0.0001f; |
|
||||
|
|
||||
/// <summary>
|
|
||||
/// Initializes a new instance of the <see cref="Resize"/> class.
|
|
||||
/// </summary>
|
|
||||
/// <param name="sampler">
|
|
||||
/// The sampler to perform the resize operation.
|
|
||||
/// </param>
|
|
||||
public Resize(IResampler sampler) |
|
||||
{ |
|
||||
Guard.NotNull(sampler, nameof(sampler)); |
|
||||
|
|
||||
this.Sampler = sampler; |
|
||||
} |
|
||||
|
|
||||
/// <summary>
|
|
||||
/// Gets the sampler to perform the resize operation.
|
|
||||
/// </summary>
|
|
||||
public IResampler Sampler { get; } |
|
||||
|
|
||||
/// <inheritdoc/>
|
|
||||
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<Weight> verticalValues = verticalWeights[y - startY].Values; |
|
||||
double verticalSum = verticalWeights[y - startY].Sum; |
|
||||
|
|
||||
for (int x = startX; x < endX; x++) |
|
||||
{ |
|
||||
List<Weight> 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<Weight>(); |
|
||||
} |
|
||||
|
|
||||
public List<Weight> Values { get; set; } |
|
||||
|
|
||||
public double Sum { get; set; } |
|
||||
} |
|
||||
} |
|
||||
} |
|
||||
@ -0,0 +1,147 @@ |
|||||
|
// <copyright file="Resize.cs" company="James South">
|
||||
|
// Copyright © James South and contributors.
|
||||
|
// Licensed under the Apache License, Version 2.0.
|
||||
|
// </copyright>
|
||||
|
|
||||
|
namespace ImageProcessor.Samplers |
||||
|
{ |
||||
|
using System; |
||||
|
|
||||
|
/// <summary>
|
||||
|
/// Provides methods that allow the resizing of images using various resampling algorithms.
|
||||
|
/// </summary>
|
||||
|
public class ResizeB : ParallelImageProcessor |
||||
|
{ |
||||
|
/// <summary>
|
||||
|
/// The epsilon for comparing floating point numbers.
|
||||
|
/// </summary>
|
||||
|
private const float Epsilon = 0.0001f; |
||||
|
|
||||
|
/// <summary>
|
||||
|
/// Initializes a new instance of the <see cref="Resize"/> class.
|
||||
|
/// </summary>
|
||||
|
/// <param name="sampler">
|
||||
|
/// The sampler to perform the resize operation.
|
||||
|
/// </param>
|
||||
|
public ResizeB(IResampler sampler) |
||||
|
{ |
||||
|
Guard.NotNull(sampler, nameof(sampler)); |
||||
|
|
||||
|
this.Sampler = sampler; |
||||
|
} |
||||
|
|
||||
|
/// <summary>
|
||||
|
/// Gets the sampler to perform the resize operation.
|
||||
|
/// </summary>
|
||||
|
public IResampler Sampler { get; } |
||||
|
|
||||
|
/// <inheritdoc/>
|
||||
|
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; |
||||
|
} |
||||
|
} |
||||
|
} |
||||
|
} |
||||
|
} |
||||
|
} |
||||
|
} |
||||
Loading…
Reference in new issue