2012-01-24 42 views
5

Tôi có một công cụ với các điều khiển trackbar trượt sử dụng để điều chỉnh của hình ảnh độ sáng, độ tương phản, gamma vvnhanh hơn thuật toán độ tương phản cho một bitmap

tôi đang cố gắng để có được cập nhật theo thời gian thực cho hình ảnh của tôi trong khi người dùng kéo thanh trượt. Thuật toán độ sáng và gamma là tốc độ chấp nhận được (khoảng 170ms). Nhưng thuật toán tương phản là khoảng 380ms.

Về cơ bản, biểu mẫu của tôi là cửa sổ công cụ có thanh trượt. Mỗi lần hình ảnh được cập nhật, nó sẽ gửi một sự kiện đến cha mẹ sẽ vẽ lại hình ảnh mới. Cửa sổ công cụ giữ hình ảnh chưa sửa đổi ban đầu bị khóa trong bộ nhớ vì vậy tôi luôn có quyền truy cập vào các byte của nó. Vì vậy, về cơ bản tôi làm điều này mỗi khi sự kiện ValueChanged cho một thanh trượt (chẳng hạn như thanh trượt Contrast) được thay đổi.

  • LockBits của bitmap làm việc (đích) như Format24bppRgb (bitmap gốc là trong Format32bppPArgb)
  • Marshal.Copy các bit vào một mảng byte []
  • Kiểm tra hoạt động mà tôi đang làm (trong đó thanh trượt được chọn)
  • Sử dụng đoạn mã sau cho Contrast:

code:

double newValue = 0; 
double c = (100.0 + contrast)/100.0; 

c *= c; 

for (int i = 0; i < sourcePixels.Length; i++) 
{ 
    newValue = sourcePixels[i]; 

    newValue /= 255.0; 
    newValue -= 0.5; 
    newValue *= c; 
    newValue += 0.5; 
    newValue *= 255; 

    if (newValue < 0) 
     newValue = 0; 
    if (newValue > 255) 
     newValue = 255; 

    destPixels[i] = (byte)newValue; 
} 

Tôi đọc một lần về cách sử dụng số nguyên thay vì giá trị dấu phẩy động để tăng tốc độ tương phản, nhưng tôi không thể tìm thấy bài viết đó một lần nữa.

Tôi đã thử sử dụng mã không an toàn (con trỏ) nhưng thực sự nhận thấy tốc độ giảm. Tôi cho rằng đó là vì mã được sử dụng lồng nhau cho các vòng lặp để lặp x và y thay vì một vòng lặp đơn.

+0

bản sao có thể có của [Điều chỉnh độ tương phản của hình ảnh trong C# một cách hiệu quả] (http://stackoverflow.com/questions/3115076/adjust-the-contrast-of-an-image-in-c-sharp-efficiently) – Magnus

+0

Mã không an toàn trong câu hỏi mà bạn đã liên kết, khi tôi thử nghiệm nó với cùng một hình ảnh như tôi đang sử dụng với mã của tôi, mất hơn 900 mili giây cho mỗi thường trình. Tất nhiên tôi đã sửa đổi nó để nó không sao chép hoặc tạo bitmap mới, nó chỉ là vòng lặp lồng nhau với con trỏ và toán học dấu phẩy động. Nó quá chậm. –

+1

Có lẽ bạn có thể sửa đổi mã của mình để sử dụng con trỏ thay vì mã không an toàn. – Magnus

Trả lời

10

Tùy thuộc vào máy bạn đang chạy tính năng này, kỹ thuật của bạn có thể khá chậm. Nếu bạn đang sử dụng một hệ thống ARM không có FPU, mỗi hoạt động đó sẽ mất khá nhiều thời gian. Vì bạn đang áp dụng cùng một hoạt động cho mỗi byte, một kỹ thuật nhanh hơn sẽ là tạo một bảng tra cứu 256-entry cho mức độ tương phản và sau đó dịch từng byte hình ảnh thông qua bảng. vòng lặp của bạn sau đó sẽ như thế nào:

byte contrast_lookup[256]; 
double newValue = 0; 
double c = (100.0 + contrast)/100.0; 

c *= c; 

for (int i = 0; i < 256; i++) 
{ 
    newValue = (double)i; 
    newValue /= 255.0; 
    newValue -= 0.5; 
    newValue *= c; 
    newValue += 0.5; 
    newValue *= 255; 

    if (newValue < 0) 
     newValue = 0; 
    if (newValue > 255) 
     newValue = 255; 
    contrast_lookup[i] = (byte)newValue; 
} 

for (int i = 0; i < sourcePixels.Length; i++) 
{ 
    destPixels[i] = contrast_lookup[sourcePixels[i]]; 
} 
+1

Nhanh hơn nhiều. Điều đó có tốc độ xuống từ khoảng 380ms đến khoảng 155ms. Cảm ơn bạn! –

+1

Cần lưu ý rằng bạn cũng có thể áp dụng cách tiếp cận tương tự này cho các thuật toán về độ sáng và gamma để làm cho chúng nhanh hơn. – Seph

+0

Tôi nghĩ vậy và tôi sẽ thử nó. Phương pháp này cân đối thực sự tốt với bitmap độ phân giải cao như các tập tin 2976x1536 tôi đang xử lý. –

3

@BitBank trả lời câu hỏi của bạn như hỏi, tôi muốn nói thêm rằng nếu bạn là sau khi thực hiện bạn nên xem xét mã của bạn mà là nhận được dữ liệu pixel và đặt nó sau đó.

mã làm việc đầy đủ sử dụng con trỏ (đạo cụ để @BitBank trên mã for loop):

private unsafe void ApplyContrast(double contrast, Bitmap bmp) 
{ 
    byte[] contrast_lookup = new byte[256]; 
    double newValue = 0; 
    double c = (100.0 + contrast)/100.0; 

    c *= c; 

    for (int i = 0; i < 256; i++) 
    { 
     newValue = (double)i; 
     newValue /= 255.0; 
     newValue -= 0.5; 
     newValue *= c; 
     newValue += 0.5; 
     newValue *= 255; 

     if (newValue < 0) 
      newValue = 0; 
     if (newValue > 255) 
      newValue = 255; 
     contrast_lookup[i] = (byte)newValue; 
    } 

    var bitmapdata = bmp.LockBits(new Rectangle(0, 0, bmp.Width, bmp.Height), 
     System.Drawing.Imaging.ImageLockMode.ReadWrite, System.Drawing.Imaging.PixelFormat.Format32bppArgb); 

    int PixelSize = 4; 

    for (int y = 0; y < bitmapdata.Height; y++) 
    { 
     byte* destPixels = (byte*)bitmapdata.Scan0 + (y * bitmapdata.Stride); 
     for (int x = 0; x < bitmapdata.Width; x++) 
     { 
      destPixels[x * PixelSize] = contrast_lookup[destPixels[x * PixelSize]]; // B 
      destPixels[x * PixelSize + 1] = contrast_lookup[destPixels[x * PixelSize + 1]]; // G 
      destPixels[x * PixelSize + 2] = contrast_lookup[destPixels[x * PixelSize + 2]]; // R 
      //destPixels[x * PixelSize + 3] = contrast_lookup[destPixels[x * PixelSize + 3]]; //A 
     } 
    } 
    bmp.UnlockBits(bitmapdata); 
} 

Nếu bạn đang thiết dữ liệu pixel hình ảnh của bạn sử dụng Marshal.Copy bạn sẽ tìm thấy điều này thực hiện tốt hơn.

Điều này sẽ hoạt động nhanh hơn mã hiện tại của bạn và bạn cũng giảm được dung lượng bộ nhớ tốt khi xử lý các hình ảnh rất lớn.

+0

Nó cũng có thể nhanh hơn một chút nếu bạn không quy mô nó từ và trở về 255. I.E. newValue = (double) i; newValue - = 128; newValue * = c; newValue + = 128; – DkAngelito

+0

Một cải tiến lớn khác là tính x * PixelSize một lần thay vì 6 lần ở bên trong cho – DkAngelito

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