2016-01-14 18 views
26

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

Original unscaled image

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.

Image halved in size

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.

+0

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

+0

@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. –

+0

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

Trả lời

0

Ok vì vậy, đây là cách tôi thực hiện.

Bí quyết trước tiên phải đổi kích thước chỉ chiều rộng của hình ảnh giữ chiều cao giống như hình ảnh gốc. Chúng tôi lưu trữ các pixel kết quả trong một hình ảnh tạm thời.

Sau đó, đó là trường hợp thay đổi kích thước hình ảnh đó xuống đầu ra cuối cùng của chúng tôi.

Như bạn có thể thấy chúng tôi không còn lặp qua cả hai tập hợp trọng lượng trên mỗi pixel. Mặc dù phải lặp lại mặc dù vòng lặp pixel bên ngoài hai lần thuật toán nhanh hơn nhiều trong hoạt động của nó trung bình nhanh hơn khoảng 25% trên hình ảnh thử nghiệm của tôi.

// Interpolate the image using the calculated weights. 
// First process the columns. 
Parallel.For(
    0, 
    sourceBottom, 
    y => 
    { 
     for (int x = startX; x < endX; x++) 
     { 
      Weight[] horizontalValues = this.HorizontalWeights[x].Values; 

      // Destination color components 
      Color destination = new Color(); 

      foreach (Weight xw in horizontalValues) 
      { 
       int originX = xw.Index; 
       Color sourceColor = Color.Expand(source[originX, y]); 
       destination += sourceColor * xw.Value; 
      } 

      destination = Color.Compress(destination); 
      this.firstPass[x, y] = destination; 
     } 
    }); 

// Now process the rows. 
Parallel.For(
    startY, 
    endY, 
    y => 
    { 
     if (y >= targetY && y < targetBottom) 
     { 
      Weight[] verticalValues = this.VerticalWeights[y].Values; 

      for (int x = startX; x < endX; x++) 
      { 
       // Destination color components 
       Color destination = new Color(); 

       foreach (Weight yw in verticalValues) 
       { 
        int originY = yw.Index; 
        int originX = x; 
        Color sourceColor = Color.Expand(this.firstPass[originX, originY]); 
        destination += sourceColor * yw.Value; 
       } 

       destination = Color.Compress(destination); 
       target[x, y] = destination; 
      } 
     } 
    }); 
+0

Tốc độ tăng lớn là khi bạn độc quyền đọc các vùng liên tiếp của bộ nhớ. Để làm điều đó, bạn phải chuyển đổi ma trận hai lần - một lần sau khi chia tỷ lệ X, sau đó sau khi chia tỷ lệ Y. Nhược điểm là bạn có thể chuyển đổi trong quá trình ghi, mà CPU có thể làm không đồng bộ nếu không có phụ thuộc đọc trên vùng bộ nhớ đó . –

+0

Điều này sẽ đơn giản hóa mã của bạn - bây giờ bạn chỉ biết cách chia tỷ lệ theo một hướng và bạn luôn chuyển đổi khi viết. Bạn gọi Scale1D hai lần, truyền theo chiều rộng/chiều cao và cấu trúc trọng số khác nhau mỗi lần. Bây giờ tôi đã chỉ thử nghiệm này trong C, vì vậy kiểm tra giới hạn mảng có thể làm chậm mọi thứ xuống, nhưng tôi nghĩ rằng các lợi ích liên kết bộ nhớ cache cơ bản nên làm việc trong JIT CLR là tốt. Cách tiếp cận transpose-when-writing, đọc-liên tiếp một mình dường như cung cấp gần với một thứ tự của cường độ trong lợi nhuận hiệu suất. –

1

Bạn có thể thử biểu đồ voronoi có trọng số. Hãy thử ngẫu nhiên một tập hợp các điểm và tính toán sơ đồ voronoi. Làm mịn các đa giác bằng thuật toán của Lloyd và nội suy màu của đa giác. Với trọng lượng tính toán sơ đồ voronoi trọng số. Ví dụ voronoi stippling và mosaic: http://www.mrl.nyu.edu/~ajsecord/stipples.htmlhttp://www.evilmadscientist.com/2012/stipplegen-weighted-voronoi-stippling-and-tsp-paths-in-processing/.

+0

Cảm ơn tôi sẽ xem xét điều này. –

1

Đánh giá từ quan điểm trừu tượng (không biết nhiều về thao tác hình ảnh) Tôi nghĩ có vẻ như bạn đang tính toán các giá trị cho trọng lượng và màu chua (trong vòng lặp forermermost) nhiều lần (bất cứ khi nào cùng một cặp chỉ mục) cây trồng lại một lần nữa); nó sẽ khả thi để đơn giản là tính toán trước chúng? Bạn sẽ cần tính toán ma trận 'sản phẩm trực tiếp' cho ma trận HorizontalWeight và VerticalWeight của bạn (chỉ cần nhân giá trị cho mỗi cặp chỉ mục (x, y)) và cũng có thể áp dụng Color.Expand cho nguồn trước. Các tác vụ này có thể được thực hiện song song và 'sản phẩm trực tiếp' (xin lỗi, tôi không biết Tên chính xác cho con thú đó) sẽ có sẵn trong nhiều thư viện.

+0

Tôi nghĩ rằng tôi làm theo những gì bạn đang nói cảm ơn. Tôi sẽ thử nghiệm với cách tiếp cận đó. –

Các vấn đề liên quan