Tôi đã viết thuật toán thay đổi kích thước sau đây có thể điều chỉnh hình ảnh lên hoặc xuống một cách chính xác. Nó quá chậm mặc dù do lặp đi lặp lại bên trong thông qua mảng trọng số trên mỗi vòng lặp.Thuật toán chia nhỏ kích thước chia thành hai lần
Tôi khá chắc chắn tôi sẽ có thể tách thuật toán thành hai lần, giống như bạn làm với hiệu ứng Gaussian blur mà sẽ làm giảm độ phức tạp của hoạt động và tăng tốc hiệu suất. Thật không may tôi không thể làm cho nó hoạt động. Có ai có thể giúp được không?
Parallel.For(
startY,
endY,
y =>
{
if (y >= targetY && y < targetBottom)
{
Weight[] verticalValues = this.verticalWeights[y].Values;
for (int x = startX; x < endX; x++)
{
Weight[] horizontalValues = this.horizontalWeights[x].Values;
// Destination color components
Color destination = new Color();
// This is where there is too much operation complexity.
foreach (Weight yw in verticalValues)
{
int originY = yw.Index;
foreach (Weight xw in horizontalValues)
{
int originX = xw.Index;
Color sourceColor = Color.Expand(source[originX, originY]);
float weight = yw.Value * xw.Value;
destination += sourceColor * weight;
}
}
destination = Color.Compress(destination);
target[x, y] = destination;
}
}
});
Trọng số và chỉ số được tính như sau. Một cho mỗi thứ nguyên:
/// <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>
private Weights[] PrecomputeWeights(int destinationSize, int sourceSize)
{
IResampler sampler = this.Sampler;
float ratio = sourceSize/(float)destinationSize;
float scale = ratio;
// When shrinking, broaden the effective kernel support so that we still
// visit every source pixel.
if (scale < 1)
{
scale = 1;
}
float scaledRadius = (float)Math.Ceiling(scale * sampler.Radius);
Weights[] result = new Weights[destinationSize];
// Make the weights slices, one source for each column or row.
Parallel.For(
0,
destinationSize,
i =>
{
float center = ((i + .5f) * ratio) - 0.5f;
int start = (int)Math.Ceiling(center - scaledRadius);
if (start < 0)
{
start = 0;
}
int end = (int)Math.Floor(center + scaledRadius);
if (end > sourceSize)
{
end = sourceSize;
if (end < start)
{
end = start;
}
}
float sum = 0;
result[i] = new Weights();
List<Weight> builder = new List<Weight>();
for (int a = start; a < end; a++)
{
float w = sampler.GetValue((a - center)/scale);
if (w < 0 || w > 0)
{
sum += w;
builder.Add(new Weight(a, w));
}
}
// Normalise the values
if (sum > 0 || sum < 0)
{
builder.ForEach(w => w.Value /= sum);
}
result[i].Values = builder.ToArray();
result[i].Sum = sum;
});
return result;
}
/// <summary>
/// Represents the weight to be added to a scaled pixel.
/// </summary>
protected class Weight
{
/// <summary>
/// The pixel index.
/// </summary>
public readonly int Index;
/// <summary>
/// Initializes a new instance of the <see cref="Weight"/> class.
/// </summary>
/// <param name="index">The index.</param>
/// <param name="value">The value.</param>
public Weight(int index, float value)
{
this.Index = index;
this.Value = value;
}
/// <summary>
/// Gets or sets the result of the interpolation algorithm.
/// </summary>
public float Value { get; set; }
}
/// <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; }
/// <summary>
/// Gets or sets the sum.
/// </summary>
public float Sum { get; set; }
}
Mỗi IResampler cung cấp chuỗi trọng số phù hợp dựa trên chỉ mục đã cho. các resampler bicubic hoạt động như sau.
/// <summary>
/// The function implements the bicubic kernel algorithm W(x) as described on
/// <see href="https://en.wikipedia.org/wiki/Bicubic_interpolation#Bicubic_convolution_algorithm">Wikipedia</see>
/// A commonly used algorithm within imageprocessing that preserves sharpness better than triangle interpolation.
/// </summary>
public class BicubicResampler : IResampler
{
/// <inheritdoc/>
public float Radius => 2;
/// <inheritdoc/>
public float GetValue(float x)
{
// The coefficient.
float a = -0.5f;
if (x < 0)
{
x = -x;
}
float result = 0;
if (x <= 1)
{
result = (((1.5f * x) - 2.5f) * x * x) + 1;
}
else if (x < 2)
{
result = (((((a * x) + 2.5f) * x) - 4) * x) + 2;
}
return result;
}
}
Đây là ví dụ về hình ảnh được thay đổi kích thước bằng thuật toán hiện có. Đầu ra là chính xác (lưu ý ánh bạc được bảo quản).
ảnh gốc
hình ảnh một nửa kích thước bằng cách sử dụng lấy mẫu lại bicubic.
Mã này là một phần của một lớn hơn nhiều library mà tôi viết thư này để thêm xử lý hình ảnh để corefx.
các chỉ mục và giá trị trọng số là gì? mà không có nó không có cách nào để tính toán phân vùng đệ quy ... khác thì sức mạnh vũ phu ... và chỉ khi một số thuộc tính được đáp ứng ... cũng là một ý tưởng tốt là chia sẻ hình ảnh mẫu trước và sau khi lọc để có cái gì đó so sánh với ... – Spektre
@Spektre Tôi đã thêm nhiều thông tin hơn vào câu hỏi. Nếu có bất cứ điều gì khác bạn cần xin vui lòng cho tôi biết. –
cũng vì đây là bi-cubic (không phải là một convolution tùy ý những gì tôi giả định từ mã của bạn lúc đầu không nhìn quá sâu) sau đó điều này là không thể giải quyết bởi phân khu đệ quy. Thay vào đó điều này có thể được tách thành vấn đề 2x1D như bạn dự định nhưng tôi không chắc chắn nó sẽ nhanh hơn (trên máy tính ngày nay) sau đó trực tiếp 2D lấy mẫu lại. Phải thực hiện một số xét nghiệm về nó nhưng sẽ không có thời gian cho đến thứ hai này. – Spektre