Browse Source

refactor ResamplingWeightedProcessor.Weights

af/merge-core
Anton Firszov 9 years ago
parent
commit
d983797baa
  1. 11
      src/ImageSharp/Image/PixelAccessor{TColor}.cs
  2. 24
      src/ImageSharp/Processing/Processors/Transforms/CompandingResizeProcessor.cs
  3. 117
      src/ImageSharp/Processing/Processors/Transforms/ResamplingWeightedProcessor.cs
  4. 63
      src/ImageSharp/Processing/Processors/Transforms/ResizeProcessor.cs

11
src/ImageSharp/Image/PixelAccessor{TColor}.cs

@ -241,17 +241,6 @@ namespace ImageSharp
this.CopyTo(area, sourceX, sourceY, width, height);
}
///// <summary>
///// Gets a <see cref="BufferSpan{T}"/> to the row 'y' beginning from the pixel at 'x'.
///// </summary>
///// <param name="x">The x coordinate</param>
///// <param name="y">The y coordinate</param>
///// <returns>The <see cref="BufferSpan{T}"/></returns>
//internal BufferSpan<TColor> GetRowSpan(int x, int y)
//{
// return this.pixelBuffer.Slice((y * this.Width) + x, this.Width - x);
//}
/// <summary>
/// Sets the pixel buffer in an unsafe manner. This should not be used unless you know what its doing!!!
/// </summary>

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

@ -46,7 +46,7 @@ namespace ImageSharp.Processing.Processors
public override bool Compand { get; set; } = true;
/// <inheritdoc/>
protected override void OnApply(ImageBase<TColor> source, Rectangle sourceRectangle)
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)
@ -119,15 +119,18 @@ namespace ImageSharp.Processing.Processors
for (int x = minX; x < maxX; x++)
{
// Ensure offsets are normalised for cropping and padding.
Weight[] horizontalValues = this.HorizontalWeights[x - startX].Values;
Weights 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 < horizontalValues.Length; i++)
for (int i = 0; i < ws.Length; i++)
{
Weight xw = horizontalValues[i];
destination += sourcePixels[xw.Index + sourceX, y].ToVector4().Expand() * xw.Value;
float xw = horizontalValues[i];
int index = left + i;
destination += sourcePixels[index, y].ToVector4().Expand() * xw;
}
TColor d = default(TColor);
@ -144,17 +147,20 @@ namespace ImageSharp.Processing.Processors
y =>
{
// Ensure offsets are normalised for cropping and padding.
Weight[] verticalValues = this.VerticalWeights[y - startY].Values;
Weights 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 < verticalValues.Length; i++)
for (int i = 0; i < ws.Length; i++)
{
Weight yw = verticalValues[i];
destination += firstPassPixels[x, yw.Index + sourceY].ToVector4().Expand() * yw.Value;
float yw = verticalValues[i];
int index = left + i;
destination += firstPassPixels[x, index].ToVector4().Expand() * yw;
}
TColor d = default(TColor);

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

@ -6,6 +6,9 @@
namespace ImageSharp.Processing.Processors
{
using System;
using System.Buffers;
using System.Runtime.CompilerServices;
using System.Runtime.InteropServices;
/// <summary>
/// Provides methods that allow the resizing of images using various algorithms.
@ -59,32 +62,41 @@ namespace ImageSharp.Processing.Processors
/// <summary>
/// Gets or sets the horizontal weights.
/// </summary>
protected Weights[] HorizontalWeights { get; set; }
protected Weights.Buffer HorizontalWeights { get; set; }
/// <summary>
/// Gets or sets the vertical weights.
/// </summary>
protected Weights[] VerticalWeights { get; set; }
protected Weights.Buffer VerticalWeights { get; set; }
/// <inheritdoc/>
protected override void BeforeApply(ImageBase<TColor> source, Rectangle sourceRectangle)
{
if (!(this.Sampler is NearestNeighborResampler))
{
this.HorizontalWeights = this.PrecomputeWeights(this.ResizeRectangle.Width, sourceRectangle.Width);
this.VerticalWeights = this.PrecomputeWeights(this.ResizeRectangle.Height, sourceRectangle.Height);
this.HorizontalWeights = this.PrecomputeWeights(
this.ResizeRectangle.Width,
sourceRectangle.Width);
this.VerticalWeights = this.PrecomputeWeights(
this.ResizeRectangle.Height,
sourceRectangle.Height);
}
}
protected override void AfterApply(ImageBase<TColor> source, Rectangle sourceRectangle)
{
base.AfterApply(source, sourceRectangle);
this.HorizontalWeights?.Dispose();
this.HorizontalWeights = null;
this.VerticalWeights?.Dispose();
this.VerticalWeights = null;
}
/// <summary>
/// Computes the weights to apply at each pixel when resizing.
/// </summary>
/// <param name="destinationSize">The destination section size.</param>
/// <param name="sourceSize">The source section size.</param>
/// <returns>
/// The <see cref="T:Weights[]"/>.
/// </returns>
protected Weights[] PrecomputeWeights(int destinationSize, int sourceSize)
protected unsafe Weights.Buffer PrecomputeWeights(int destinationSize, int sourceSize)
{
float ratio = (float)sourceSize / destinationSize;
float scale = ratio;
@ -96,8 +108,8 @@ namespace ImageSharp.Processing.Processors
IResampler sampler = this.Sampler;
float radius = (float)Math.Ceiling(scale * sampler.Radius);
Weights[] result = new Weights[destinationSize];
Weights.Buffer result = new Weights.Buffer(sourceSize, destinationSize);
for (int i = 0; i < destinationSize; i++)
{
float center = ((i + .5F) * ratio) - .5F;
@ -116,67 +128,82 @@ namespace ImageSharp.Processing.Processors
}
float sum = 0;
result[i] = new Weights();
Weight[] weights = new Weight[right - left + 1];
result.Weights[i] = result.Slice(i, left, right);
Weights ws = result.Weights[i];
float* weights = ws.Ptr;
for (int j = left; j <= right; j++)
{
float weight = sampler.GetValue((j - center) / scale);
sum += weight;
weights[j - left] = new Weight(j, weight);
weights[j - left] = weight;
}
// Normalise, best to do it here rather than in the pixel loop later on.
if (sum > 0)
{
for (int w = 0; w < weights.Length; w++)
for (int w = 0; w < ws.Length; w++)
{
weights[w].Value = weights[w].Value / sum;
weights[w] = weights[w] / sum;
}
}
result[i].Values = weights;
}
return result;
}
/// <summary>
/// Represents the weight to be added to a scaled pixel.
/// Represents a collection of weights and their sum.
/// </summary>
protected struct Weight
protected unsafe struct Weights
{
/// <summary>
/// Initializes a new instance of the <see cref="Weight"/> struct.
/// The local left index position
/// </summary>
/// <param name="index">The index.</param>
/// <param name="value">The value.</param>
public Weight(int index, float value)
public int Left;
public BufferSpan<float> Span;
public float* Ptr => (float*)Span.PointerAtOffset;
public int Length => Span.Length;
[MethodImpl(MethodImplOptions.AggressiveInlining)]
private Weights(int left, BufferSpan<float> span)
{
this.Index = index;
this.Value = value;
this.Left = left;
this.Span = span;
}
/// <summary>
/// Gets the pixel index.
/// </summary>
public int Index { get; }
internal unsafe class Buffer : IDisposable
{
private PinnedImageBuffer<float> dataBuffer;
/// <summary>
/// Gets or sets the result of the interpolation algorithm.
/// </summary>
public float Value { get; set; }
}
public Weights[] Weights { get; }
/// <summary>
/// Represents a collection of weights and their sum.
/// </summary>
protected class Weights
{
/// <summary>
/// Gets or sets the values.
/// </summary>
public Weight[] Values { get; set; }
public float* DataPtr { get; }
internal Weights Slice(int i, int leftIdx, int rightIdx)
{
var span = dataBuffer.GetRowSpan(i).Slice(leftIdx, rightIdx - leftIdx);
return new Weights(leftIdx, span);
}
public Buffer(int sourceSize, int destinationSize)
{
this.dataBuffer = new PinnedImageBuffer<float>(sourceSize, destinationSize);
this.dataBuffer.Clear();
this.DataPtr = (float*)this.dataBuffer.Pointer;
this.Weights = new Weights[destinationSize];
}
public void Dispose()
{
this.dataBuffer.Dispose();
}
}
}
}
}

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

@ -45,7 +45,7 @@ namespace ImageSharp.Processing.Processors
}
/// <inheritdoc/>
protected override void OnApply(ImageBase<TColor> source, Rectangle sourceRectangle)
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)
@ -107,33 +107,47 @@ namespace ImageSharp.Processing.Processors
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))
using (PinnedImageBuffer<Vector4> firstPassPixels = new PinnedImageBuffer<Vector4>(width, source.Height))
{
firstPassPixels.Clear();
Parallel.For(
0,
sourceRectangle.Bottom,
this.ParallelOptions,
y =>
{
for (int x = minX; x < maxX; x++)
{
// Ensure offsets are normalised for cropping and padding.
Weight[] horizontalValues = this.HorizontalWeights[x - startX].Values;
// Destination color components
Vector4 destination = Vector4.Zero;
for (int i = 0; i < horizontalValues.Length; i++)
// TODO: Without Parallel.For() this buffer object could be reused:
using (PinnedBuffer<Vector4> tempRowBuffer = new PinnedBuffer<Vector4>(sourcePixels.Width))
{
Weight xw = horizontalValues[i];
destination += sourcePixels[xw.Index + sourceX, y].ToVector4() * xw.Value;
BufferSpan<TColor> sourceRow = sourcePixels.GetRowSpan(y);
BulkPixelOperations<TColor>.Instance.ToVector4(
sourceRow,
tempRowBuffer,
sourceRow.Length);
for (int x = minX; x < maxX; x++)
{
// Ensure offsets are normalised for cropping and padding.
Weights 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 += tempRowBuffer[index] * xw;
}
firstPassPixels[x, y] = destination;
}
}
TColor d = default(TColor);
d.PackFromVector4(destination);
firstPassPixels[x, y] = d;
}
});
});
// Now process the rows.
Parallel.For(
@ -143,17 +157,20 @@ namespace ImageSharp.Processing.Processors
y =>
{
// Ensure offsets are normalised for cropping and padding.
Weight[] verticalValues = this.VerticalWeights[y - startY].Values;
Weights 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 < verticalValues.Length; i++)
for (int i = 0; i < ws.Length; i++)
{
Weight yw = verticalValues[i];
destination += firstPassPixels[x, yw.Index + sourceY].ToVector4() * yw.Value;
float yw = verticalValues[i];
int index = left + i;
destination += firstPassPixels[x, index] * yw;
}
TColor d = default(TColor);

Loading…
Cancel
Save