Browse Source

merged ResizeProcessor and CompandingResizeProcessor

af/merge-core
Anton Firszov 9 years ago
parent
commit
740411a684
  1. 177
      src/ImageSharp/Processing/Processors/Transforms/CompandingResizeProcessor.cs
  2. 24
      src/ImageSharp/Processing/Processors/Transforms/ResamplingWeightedProcessor.Weights.cs
  3. 1
      src/ImageSharp/Processing/Processors/Transforms/ResamplingWeightedProcessor.cs
  4. 48
      src/ImageSharp/Processing/Processors/Transforms/ResizeProcessor.cs
  5. 12
      src/ImageSharp/Processing/Transforms/Resize.cs
  6. 26
      tests/ImageSharp.Tests/Processors/Filters/ResizeTests.cs

177
src/ImageSharp/Processing/Processors/Transforms/CompandingResizeProcessor.cs

@ -1,177 +0,0 @@
// <copyright file="CompandingResizeProcessor.cs" company="James Jackson-South">
// Copyright (c) James Jackson-South and contributors.
// Licensed under the Apache License, Version 2.0.
// </copyright>
namespace ImageSharp.Processing.Processors
{
using System;
using System.Numerics;
using System.Threading.Tasks;
/// <summary>
/// Provides methods that allow the resizing of images using various algorithms.
/// This version will expand and compress the image to and from a linear color space during processing.
/// </summary>
/// <typeparam name="TColor">The pixel format.</typeparam>
internal class CompandingResizeProcessor<TColor> : ResamplingWeightedProcessor<TColor>
where TColor : struct, IPixel<TColor>
{
/// <summary>
/// Initializes a new instance of the <see cref="CompandingResizeProcessor{TColor}"/> class.
/// </summary>
/// <param name="sampler">The sampler to perform the resize operation.</param>
/// <param name="width">The target width.</param>
/// <param name="height">The target height.</param>
public CompandingResizeProcessor(IResampler sampler, int width, int height)
: base(sampler, width, height, new Rectangle(0, 0, width, height))
{
}
/// <summary>
/// Initializes a new instance of the <see cref="CompandingResizeProcessor{TColor}"/> class.
/// </summary>
/// <param name="sampler">The sampler to perform the resize operation.</param>
/// <param name="width">The target width.</param>
/// <param name="height">The target height.</param>
/// <param name="resizeRectangle">
/// The <see cref="Rectangle"/> structure that specifies the portion of the target image object to draw to.
/// </param>
public CompandingResizeProcessor(IResampler sampler, int width, int height, Rectangle resizeRectangle)
: base(sampler, width, height, resizeRectangle)
{
}
/// <inheritdoc/>
public override bool Compand { get; set; } = true;
/// <inheritdoc/>
protected override unsafe void OnApply(ImageBase<TColor> source, Rectangle sourceRectangle)
{
// Jump out, we'll deal with that later.
if (source.Width == this.Width && source.Height == this.Height && sourceRectangle == this.ResizeRectangle)
{
return;
}
int width = this.Width;
int height = this.Height;
int sourceX = sourceRectangle.X;
int sourceY = sourceRectangle.Y;
int startY = this.ResizeRectangle.Y;
int endY = this.ResizeRectangle.Bottom;
int startX = this.ResizeRectangle.X;
int endX = this.ResizeRectangle.Right;
int minX = Math.Max(0, startX);
int maxX = Math.Min(width, endX);
int minY = Math.Max(0, startY);
int maxY = Math.Min(height, endY);
if (this.Sampler is NearestNeighborResampler)
{
// Scaling factors
float widthFactor = sourceRectangle.Width / (float)this.ResizeRectangle.Width;
float heightFactor = sourceRectangle.Height / (float)this.ResizeRectangle.Height;
using (PixelAccessor<TColor> targetPixels = new PixelAccessor<TColor>(width, height))
{
using (PixelAccessor<TColor> sourcePixels = source.Lock())
{
Parallel.For(
minY,
maxY,
this.ParallelOptions,
y =>
{
// Y coordinates of source points
int originY = (int)(((y - startY) * heightFactor) + sourceY);
for (int x = minX; x < maxX; x++)
{
// X coordinates of source points
targetPixels[x, y] = sourcePixels[(int)(((x - startX) * widthFactor) + sourceX), originY];
}
});
}
// Break out now.
source.SwapPixelsBuffers(targetPixels);
return;
}
}
// Interpolate the image using the calculated weights.
// A 2-pass 1D algorithm appears to be faster than splitting a 1-pass 2D algorithm
// First process the columns. Since we are not using multiple threads startY and endY
// are the upper and lower bounds of the source rectangle.
using (PixelAccessor<TColor> targetPixels = new PixelAccessor<TColor>(width, height))
{
using (PixelAccessor<TColor> sourcePixels = source.Lock())
using (PixelAccessor<TColor> firstPassPixels = new PixelAccessor<TColor>(width, source.Height))
{
Parallel.For(
0,
sourceRectangle.Bottom,
this.ParallelOptions,
y =>
{
for (int x = minX; x < maxX; x++)
{
// Ensure offsets are normalised for cropping and padding.
WeightsWindow ws = this.HorizontalWeights.Weights[x - startX];
float* horizontalValues = ws.Ptr;
int left = ws.Left;
// Destination color components
Vector4 destination = Vector4.Zero;
for (int i = 0; i < ws.Length; i++)
{
float xw = horizontalValues[i];
int index = left + i;
destination += sourcePixels[index, y].ToVector4().Expand() * xw;
}
TColor d = default(TColor);
d.PackFromVector4(destination.Compress());
firstPassPixels[x, y] = d;
}
});
// Now process the rows.
Parallel.For(
minY,
maxY,
this.ParallelOptions,
y =>
{
// Ensure offsets are normalised for cropping and padding.
WeightsWindow ws = this.VerticalWeights.Weights[y - startY];
float* verticalValues = ws.Ptr;
int left = ws.Left;
for (int x = 0; x < width; x++)
{
// Destination color components
Vector4 destination = Vector4.Zero;
for (int i = 0; i < ws.Length; i++)
{
float yw = verticalValues[i];
int index = left + i;
destination += firstPassPixels[x, index].ToVector4().Expand() * yw;
}
TColor d = default(TColor);
d.PackFromVector4(destination.Compress());
targetPixels[x, y] = d;
}
});
}
source.SwapPixelsBuffers(targetPixels);
}
}
}
}

24
src/ImageSharp/Processing/Processors/Transforms/ResamplingWeightedProcessor.Weights.cs

@ -69,6 +69,30 @@ namespace ImageSharp.Processing.Processors
return result;
}
/// <summary>
/// Computes the sum of vectors in 'rowSpan' weighted by weight values, pointed by this <see cref="WeightsWindow"/> instance.
/// Applies <see cref="Vector4Extensions.Expand(float)"/> to all input vectors.
/// </summary>
/// <param name="rowSpan">The input span of vectors</param>
/// <returns>The weighted sum</returns>
public Vector4 ComputeExpandedWeightedRowSum(BufferSpan<Vector4> rowSpan)
{
float* horizontalValues = this.Ptr;
int left = this.Left;
// Destination color components
Vector4 result = Vector4.Zero;
for (int i = 0; i < this.Length; i++)
{
float xw = horizontalValues[i];
int index = left + i;
result += rowSpan[index].Expand() * xw;
}
return result;
}
/// <summary>
/// Computes the sum of vectors in 'firstPassPixels' at a column pointed by 'x',
/// weighted by weight values, pointed by this <see cref="WeightsWindow"/> instance.

1
src/ImageSharp/Processing/Processors/Transforms/ResamplingWeightedProcessor.cs

@ -156,6 +156,5 @@ namespace ImageSharp.Processing.Processors
return result;
}
}
}

48
src/ImageSharp/Processing/Processors/Transforms/ResizeProcessor.cs

@ -12,9 +12,6 @@ namespace ImageSharp.Processing.Processors
/// <summary>
/// Provides methods that allow the resizing of images using various algorithms.
/// </summary>
/// <remarks>
/// This version and the <see cref="CompandingResizeProcessor{TColor}"/> have been separated out to improve performance.
/// </remarks>
/// <typeparam name="TColor">The pixel format.</typeparam>
internal class ResizeProcessor<TColor> : ResamplingWeightedProcessor<TColor>
where TColor : struct, IPixel<TColor>
@ -123,15 +120,27 @@ namespace ImageSharp.Processing.Processors
using (PinnedBuffer<Vector4> tempRowBuffer = new PinnedBuffer<Vector4>(sourcePixels.Width))
{
BufferSpan<TColor> sourceRow = sourcePixels.GetRowSpan(y);
BulkPixelOperations<TColor>.Instance.ToVector4(
sourceRow,
tempRowBuffer,
sourceRow.Length);
for (int x = minX; x < maxX; x++)
if (this.Compand)
{
for (int x = minX; x < maxX; x++)
{
WeightsWindow window = this.HorizontalWeights.Weights[x - startX];
firstPassPixels[x, y] = window.ComputeExpandedWeightedRowSum(tempRowBuffer);
}
}
else
{
WeightsWindow window = this.HorizontalWeights.Weights[x - startX];
firstPassPixels[x, y] = window.ComputeWeightedRowSum(tempRowBuffer);
for (int x = minX; x < maxX; x++)
{
WeightsWindow window = this.HorizontalWeights.Weights[x - startX];
firstPassPixels[x, y] = window.ComputeWeightedRowSum(tempRowBuffer);
}
}
}
});
@ -146,14 +155,29 @@ namespace ImageSharp.Processing.Processors
// Ensure offsets are normalised for cropping and padding.
WeightsWindow window = this.VerticalWeights.Weights[y - startY];
for (int x = 0; x < width; x++)
if (this.Compand)
{
// Destination color components
Vector4 destination = window.ComputeWeightedColumnSum(firstPassPixels, x);
for (int x = 0; x < width; x++)
{
// Destination color components
Vector4 destination = window.ComputeWeightedColumnSum(firstPassPixels, x);
destination = destination.Compress();
TColor d = default(TColor);
d.PackFromVector4(destination);
targetPixels[x, y] = d;
}
}
else
{
for (int x = 0; x < width; x++)
{
// Destination color components
Vector4 destination = window.ComputeWeightedColumnSum(firstPassPixels, x);
TColor d = default(TColor);
d.PackFromVector4(destination);
targetPixels[x, y] = d;
TColor d = default(TColor);
d.PackFromVector4(destination);
targetPixels[x, y] = d;
}
}
});
}

12
src/ImageSharp/Processing/Transforms/Resize.cs

@ -156,16 +156,8 @@ namespace ImageSharp
Guard.MustBeGreaterThan(width, 0, nameof(width));
Guard.MustBeGreaterThan(height, 0, nameof(height));
ResamplingWeightedProcessor<TColor> processor;
if (compand)
{
processor = new CompandingResizeProcessor<TColor>(sampler, width, height, targetRectangle);
}
else
{
processor = new ResizeProcessor<TColor>(sampler, width, height, targetRectangle);
}
ResizeProcessor<TColor> processor =
new ResizeProcessor<TColor>(sampler, width, height, targetRectangle) { Compand = compand };
source.ApplyProcessor(processor, sourceRectangle);
return source;

26
tests/ImageSharp.Tests/Processors/Filters/ResizeTests.cs

@ -8,6 +8,32 @@ namespace ImageSharp.Tests
using System.IO;
using Processing;
using Xunit;
using Xunit.Abstractions;
public class ResizeProfilingBenchmarks : MeasureFixture
{
public ResizeProfilingBenchmarks(ITestOutputHelper output)
: base(output)
{
}
public int ExecutionCount { get; set; } = 50;
[Theory]
[InlineData(100, 100)]
[InlineData(1000, 1000)]
public void ResizeBicubic(int width, int height)
{
this.Measure(this.ExecutionCount,
() =>
{
using (Image image = new Image(width, height))
{
image.Resize(width / 4, height / 4);
}
});
}
}
public class ResizeTests : FileTestBase
{

Loading…
Cancel
Save