Biểu diễn Haskell nào được khuyến nghị cho mảng pixel 2D, không hộp với hàng triệu pixel?


117

Tôi muốn giải quyết một số vấn đề về xử lý hình ảnh trong Haskell. Tôi đang làm việc với cả ảnh bitonal (ảnh bitmap) và ảnh màu với hàng triệu pixel. Tôi có một số câu hỏi:

  1. Trên cơ sở nào tôi nên chọn giữa Vector.UnboxedUArray? Cả hai đều là các mảng không được đóng hộp, nhưng phần Vectortrừu tượng có vẻ được quảng cáo nhiều, đặc biệt là xung quanh sự kết hợp vòng lặp. Là Vectorluôn luôn tốt hơn? Nếu không, khi nào tôi nên sử dụng biểu diễn nào?

  2. Đối với hình ảnh màu, tôi sẽ muốn lưu trữ bộ ba số nguyên 16 bit hoặc bộ ba số dấu phẩy động chính xác đơn. Với mục đích này, sử dụng một trong hai Vectorhoặc UArraydễ dàng hơn? Biểu diễn hơn?

  3. Đối với hình ảnh bitonal, tôi sẽ chỉ cần lưu trữ 1 bit trên mỗi pixel. Có loại dữ liệu được xác định trước có thể giúp tôi ở đây bằng cách đóng gói nhiều pixel thành một từ hay tôi tự làm không?

  4. Cuối cùng, mảng của tôi là hai chiều. Tôi cho rằng tôi có thể đối phó với việc bổ sung hướng dẫn được áp đặt bởi một biểu diễn là "mảng của mảng" (hoặc vectơ của vectơ), nhưng tôi thích một phần trừu tượng có hỗ trợ ánh xạ chỉ mục. Bất cứ ai có thể giới thiệu bất cứ điều gì từ một thư viện tiêu chuẩn hoặc từ Hackage?

Tôi là một lập trình viên chức năng và không có nhu cầu đột biến :-)


2
Tôi nghĩ chỉ có Repa đáp ứng được số 4, xem cse.unsw.edu.au/~chak/papers/repa.pdf .
stephen tetley

5
@stephen: Arraygiao diện tiêu chuẩn hỗ trợ mảng đa chiều. Bạn chỉ có thể sử dụng một bộ giá trị cho chỉ mục.
John L

13
Thực tế là câu hỏi này được ủng hộ cao và được yêu thích (bao gồm cả tôi) dường như chỉ ra rằng việc xử lý các mảng của Haskell không được ghi chép đầy đủ.
Alexandre C.

2
@Alexandre C.: Việc xử lý các mảng cơ bản hàng ngày đã được ghi chép đầy đủ; việc xử lý các khối bộ nhớ lớn chứa dữ liệu có thể thay đổi cũng đơn giản như trong C; việc xử lý các mảng đa chiều lớn bất biến một cách hiệu quả nhất có thể là điều ít rõ ràng hơn. Đây là về việc điều chỉnh hiệu suất một kịch bản trong đó các chi tiết tinh tế, ít được ghi chép lại sẽ là một vấn đề trong bất kỳ ngôn ngữ nào.
CA McCann

1
@Alexandre C.: Đối với hầu hết các ứng dụng, nó liền mạch. Và bản thân nó không thực sự là Haskell, đó là thư viện và trình biên dịch. Một UArraychỉ mục đơn giản được lập chỉ mục bởi một bộ Intrất đơn giản để làm việc và thường đủ tốt, nhưng ngay cả phép thuật sâu sắc của GHC sẽ không tối ưu hóa mã bằng cách sử dụng API tối thiểu của nó thành một thứ gì đó cạnh tranh với một thư viện được tinh chỉnh để xử lý dữ liệu hàng loạt song song nhanh chóng.
CA McCann

Câu trả lời:


89

Đối với mảng đa chiều, tùy chọn tốt nhất hiện tại trong Haskell, theo quan điểm của tôi, là repa .

Repa cung cấp hiệu suất cao, đều đặn, đa chiều, các mảng song song đa hình. Tất cả dữ liệu số được lưu trữ trong hộp. Các hàm được viết bằng tổ hợp Repa sẽ tự động song song với điều kiện bạn cung cấp + RTS - Bất cứ thứ gì trên dòng lệnh khi chạy chương trình.

Gần đây, nó đã được sử dụng cho một số vấn đề xử lý hình ảnh:

Tôi đã bắt đầu viết một hướng dẫn về cách sử dụng repa , đây là một nơi tốt để bắt đầu nếu bạn đã biết về mảng Haskell hoặc thư viện vectơ. Bước đệm quan trọng là việc sử dụng các loại hình dạng thay vì các loại chỉ mục đơn giản, để giải quyết các chỉ số đa chiều (và thậm chí cả giấy nến).

Gói repa-io bao gồm hỗ trợ đọc và ghi tệp hình ảnh .bmp, mặc dù cần hỗ trợ nhiều định dạng hơn.

Giải quyết các câu hỏi cụ thể của bạn, đây là một hình ảnh, với cuộc thảo luận:


Cả ba UArray, Vector và Repa đều hỗ trợ tính năng mở hộp.  Vector và Repa có một API phong phú, linh hoạt, nhưng UArray thì không.  UArray và Repa có lập chỉ mục đa chiều, nhưng Vector thì không.  Tất cả chúng đều có hỗ trợ đóng gói bit, mặc dù Vector và Repa có một số lưu ý về vấn đề đó.  Vector và Repa tương tác với dữ liệu và mã C, nhưng UArray thì không.  Chỉ Repa hỗ trợ giấy nến.


Tôi nên chọn giữa Vector.Unboxed và UArray trên cơ sở nào?

Chúng có biểu diễn cơ bản gần giống nhau, tuy nhiên, điểm khác biệt chính là bề rộng của API để làm việc với vectơ: chúng có hầu hết tất cả các hoạt động mà bạn thường liên kết với danh sách (với khung tối ưu hóa theo hướng kết hợp), trong khi UArrayhầu như có không có API.

Đối với hình ảnh màu, tôi sẽ muốn lưu trữ bộ ba số nguyên 16 bit hoặc bộ ba số dấu phẩy động chính xác đơn.

UArraycó hỗ trợ tốt hơn cho dữ liệu đa chiều, vì nó có thể sử dụng các kiểu dữ liệu tùy ý để lập chỉ mục. Mặc dù điều này có thể thực hiện được Vector(bằng cách viết một phiên bản UAcho loại phần tử của bạn), nhưng đó không phải là mục tiêu chính của Vector- thay vào đó, đây là Repabước tiến vào, giúp bạn rất dễ sử dụng các kiểu dữ liệu tùy chỉnh được lưu trữ theo cách hiệu quả, nhờ vào chỉ mục hình dạng .

Trong Repa, bộ ba quần đùi của bạn sẽ có kiểu:

Array DIM3 Word16

Đó là, một mảng 3D của Word16s.

Đối với hình ảnh bitonal, tôi sẽ chỉ cần lưu trữ 1 bit trên mỗi pixel.

UArrays đóng gói Bools dưới dạng bit, Vector sử dụng ví dụ cho Bool thực hiện đóng gói bit, thay vì sử dụng một biểu diễn dựa trên Word8. Dù sao đi nữa, thật dễ dàng để viết một triển khai đóng gói bit cho các vectơ - đây là một , từ thư viện uvector (lỗi thời). Dưới mui xe, Repasử dụng Vectors, vì vậy tôi nghĩ rằng nó kế thừa các lựa chọn đại diện thư viện.

Có loại dữ liệu xác định trước có thể giúp tôi ở đây bằng cách đóng gói nhiều pixel thành một từ không

Bạn có thể sử dụng các phiên bản hiện có cho bất kỳ thư viện nào, cho các loại từ khác nhau, nhưng bạn có thể cần viết một vài trợ giúp bằng Data.Bits để cuộn và bỏ cuộn dữ liệu đã đóng gói.

Cuối cùng, mảng của tôi là hai chiều

UArray và Repa hỗ trợ các mảng đa chiều hiệu quả. Repa cũng có một giao diện phong phú để làm như vậy. Vector riêng của nó thì không.


Đề cập đáng chú ý:

  • hmatrix , một kiểu mảng tùy chỉnh với các liên kết mở rộng với các gói đại số tuyến tính. Nên bị ràng buộc để sử dụng vectorhoặc repacác loại.
  • ix-shapable , nhận được lập chỉ mục linh hoạt hơn từ các mảng thông thường
  • bảng đen , thư viện của Andy Gill để xử lý hình ảnh 2D
  • codec-image-devil , đọc và ghi các định dạng hình ảnh khác nhau vào UArray

5
Ngoài ra, bây giờ bạn có thể làm IO hình ảnh của các mảng repa 3D ở nhiều định dạng, nhờ repa-devil .
Don Stewart

2
Bạn có thể vui lòng giải thích cách Repa có thể tương tác với mã C không? Tôi không tìm thấy phiên bản Đáng lưu ý cho Data.Array.Repa ...
sastanin

2
Sao chép vào con trỏ có lẽ là cách dễ nhất để lưu trữ dữ liệu, nhưng rõ ràng không phải là một giải pháp lâu dài. Để làm được điều đó, chúng ta sẽ cần các vectơ đáng yêu.
Don Stewart


17

Sau khi tôi xem xét các tính năng của thư viện mảng Haskell quan trọng đối với tôi và biên soạn một bảng so sánh (chỉ bảng tính: liên kết trực tiếp ). Vì vậy, tôi sẽ cố gắng trả lời.

Tôi nên chọn giữa Vector.Unboxed và UArray trên cơ sở nào? Cả hai đều là mảng không được đóng hộp, nhưng phần trừu tượng Vector có vẻ được quảng cáo rất nhiều, đặc biệt là xung quanh phép hợp vòng lặp. Vector luôn tốt hơn? Nếu không, khi nào tôi nên sử dụng biểu diễn nào?

UArray có thể được ưu tiên hơn Vector nếu người ta cần mảng hai chiều hoặc nhiều chiều. Nhưng Vector có API đẹp hơn để thao tác, tốt, vectơ. Nói chung, Vector không thích hợp để mô phỏng mảng nhiều chiều.

Vector.Unboxed không thể được sử dụng với các chiến lược song song. Tôi nghi ngờ rằng UArray không thể được sử dụng, nhưng ít nhất rất dễ dàng để chuyển từ UArray sang Mảng đóng hộp và xem liệu việc song song có lợi hơn so với chi phí quyền anh hay không.

Đối với hình ảnh màu, tôi sẽ muốn lưu trữ bộ ba số nguyên 16 bit hoặc bộ ba số dấu phẩy động chính xác đơn. Với mục đích này, Vector hoặc UArray có dễ sử dụng hơn không? Biểu diễn hơn?

Tôi đã thử sử dụng Mảng để biểu diễn hình ảnh (mặc dù tôi chỉ cần hình ảnh thang độ xám). Đối với hình ảnh màu, tôi đã sử dụng thư viện Codec-Image-DevIL để đọc / ghi hình ảnh (liên kết với thư viện DevIL), đối với hình ảnh thang độ xám, tôi đã sử dụng thư viện pgm (Haskell thuần túy).

Vấn đề lớn của tôi với Array là nó chỉ cung cấp lưu trữ truy cập ngẫu nhiên, nhưng nó không cung cấp nhiều phương tiện để xây dựng thuật toán Array cũng như không đi kèm với các thư viện sẵn sàng sử dụng các quy trình mảng (không giao diện với lib đại số tuyến tính, không 'không cho phép thể hiện các biến đổi chập, fft và các biến đổi khác).

Hầu như mỗi khi Mảng mới phải được tạo từ Mảng hiện có, một danh sách các giá trị trung gian phải được xây dựng (giống như trong phép nhân ma trận từ Giới thiệu Nhẹ nhàng). Chi phí xây dựng mảng thường lớn hơn lợi ích của việc truy cập ngẫu nhiên nhanh hơn, đến mức biểu diễn dựa trên danh sách nhanh hơn trong một số trường hợp sử dụng của tôi.

STUArray có thể đã giúp tôi, nhưng tôi không thích đấu tranh với các lỗi kiểu khó hiểu và những nỗ lực cần thiết để viết mã đa hình với STUArray .

Vì vậy, vấn đề với Mảng là chúng không phù hợp cho các phép tính số. Về mặt này, Hmatrix 'Data.Packed.Vector và Data.Packed.Matrix tốt hơn vì chúng đi kèm với một thư viện ma trận vững chắc (chú ý: giấy phép GPL). Về hiệu suất, trên phép nhân ma trận, hmatrix đủ nhanh ( chỉ chậm hơn Octave một chút ), nhưng rất ngốn bộ nhớ (tiêu thụ nhiều hơn Python / SciPy vài lần).

Ngoài ra còn có thư viện blas cho ma trận, nhưng nó không xây dựng trên GHC7.

Tôi chưa có nhiều kinh nghiệm về Repa và tôi không hiểu rõ về mã repa. Từ những gì tôi thấy, nó có rất hạn chế về các thuật toán ma trận và mảng sẵn sàng sử dụng được viết trên đó, nhưng ít nhất nó có thể diễn đạt các thuật toán quan trọng bằng phương tiện của thư viện. Ví dụ, đã có các quy trình cho phép nhân ma trận và cho phép tích chập trong các thuật toán lặp lại. Thật không may, có vẻ như tích chập bây giờ bị giới hạn ở các hạt nhân 7 × 7 (đối với tôi nó không đủ, nhưng sẽ đủ cho nhiều mục đích sử dụng).

Tôi đã không thử liên kết Haskell OpenCV. Chúng phải nhanh, vì OpenCV thực sự nhanh, nhưng tôi không chắc liệu các ràng buộc có hoàn chỉnh và đủ tốt để có thể sử dụng được hay không. Ngoài ra, OpenCV về bản chất của nó là rất bắt buộc, chứa đầy các bản cập nhật phá hoại. Tôi cho rằng thật khó để thiết kế một giao diện chức năng đẹp mắt và hiệu quả. Nếu một người đi theo cách OpenCV, anh ta có thể sử dụng biểu diễn hình ảnh OpenCV ở mọi nơi và sử dụng các quy trình OpenCV để thao tác chúng.

Đối với hình ảnh bitonal, tôi sẽ chỉ cần lưu trữ 1 bit trên mỗi pixel. Có loại dữ liệu được xác định trước có thể giúp tôi ở đây bằng cách đóng gói nhiều pixel thành một từ hay tôi tự làm không?

Theo như tôi biết, các mảng Bools Unboxed đảm nhận việc đóng gói và giải nén các vectơ bit. Tôi nhớ đã xem xét việc triển khai các mảng Bools trong các thư viện khác và không thấy điều này ở nơi khác.

Cuối cùng, mảng của tôi là hai chiều. Tôi cho rằng tôi có thể đối phó với việc bổ sung hướng dẫn được áp đặt bởi một biểu diễn là "mảng của mảng" (hoặc vectơ của vectơ), nhưng tôi thích một phần trừu tượng có hỗ trợ ánh xạ chỉ mục. Bất cứ ai có thể giới thiệu bất cứ điều gì từ một thư viện tiêu chuẩn hoặc từ Hackage?

Ngoài Vector (và các danh sách đơn giản), tất cả các thư viện mảng khác đều có khả năng biểu diễn các mảng hoặc ma trận hai chiều. Tôi cho rằng họ tránh chuyển hướng không cần thiết.


Các ràng buộc opencv được đề cập bên dưới là không đầy đủ. Thực sự là không thể cho một người tạo và duy trì một bộ hoàn chỉnh cho một thư viện khổng lồ như vậy. Tuy nhiên, sử dụng opencv vẫn tiết kiệm chi phí ngay cả khi bạn phải tự tạo một trình bao bọc cho chức năng bạn cần, vì nó thực hiện một số thứ thực sự phức tạp.
aleator

@aleator Vâng, tôi hiểu rằng đó là khối lượng công việc thực sự lớn đối với một người. BTW, nếu bạn là người bảo trì, bạn có thể vui lòng xuất bản tài liệu về hasdock ở đâu đó để có thể đánh giá mức độ phù hợp của thư viện và các ràng buộc mà không cần cài đặt cục bộ không? (tài liệu không có sẵn trên Hackage do lỗi xây dựng; và tài liệu không tạo cho tôi với cả GHC 6.12.1 và GHC 7.0.2 do M_PIkhông được khai báo).
sastanin

@jextee Này, cảm ơn vì mẹo! Tôi đã tải lên một phiên bản mới có thể khắc phục được cả hai vấn đề.
aleator

@aleator Cảm ơn, bây giờ nó được xây dựng sạch sẽ.
sastanin

5

Mặc dù, điều này không trả lời chính xác câu hỏi của bạn và thậm chí không thực sự là haskell như vậy, tôi khuyên bạn nên xem qua các thư viện CV hoặc CV-combinators tại hackage. Chúng liên kết nhiều toán tử thị giác và xử lý hình ảnh khá hữu ích từ thư viện opencv và giúp làm việc với các vấn đề về thị giác máy nhanh hơn nhiều.

Sẽ thật tuyệt nếu ai đó tìm ra cách repa hoặc một số thư viện mảng như vậy có thể được sử dụng trực tiếp với opencv.


0

Đây là thư viện Xử lý hình ảnh Haskell mới có thể xử lý tất cả các tác vụ được đề cập và hơn thế nữa. Hiện tại, nó sử dụng các gói RepaVector cho các biểu diễn cơ bản, do đó kế thừa sự hợp nhất, tính toán song song, đột biến và hầu hết các tính năng bổ sung khác đi kèm với các thư viện đó. Nó cung cấp một giao diện dễ sử dụng, tự nhiên cho việc thao tác hình ảnh:

  • 2D lập chỉ mục và không có hộp bọc pixel với độ chính xác tùy ý ( Double, Float, Word16, vv ..)
  • tất cả các chức năng cần thiết như map, fold, zipWith, traverse...
  • hỗ trợ cho các không gian màu khác nhau: RGB, HSI, thang màu xám, Bi-tonal, Complex, v.v.
  • chức năng xử lý hình ảnh phổ biến:
    • Hình thái nhị phân
    • Convolution
    • Nội suy
    • Biến đổi Fourier
    • Lập biểu đồ
    • Vân vân.
  • Khả năng coi pixel và hình ảnh là số thông thường.
  • Đọc và ghi các định dạng hình ảnh phổ biến thông qua thư viện JuicyPixels

Quan trọng nhất, nó là một thư viện Haskell thuần túy, vì vậy nó không phụ thuộc vào bất kỳ chương trình bên ngoài nào. Nó cũng có khả năng mở rộng cao, có thể giới thiệu các không gian màu và hình ảnh mới.

Một điều mà nó không làm là đóng gói nhiều pixel nhị phân trong một Word, thay vào đó nó sử dụng một Wordpixel trên mỗi pixel nhị phân, có thể trong tương lai ...

Khi sử dụng trang web của chúng tôi, bạn xác nhận rằng bạn đã đọc và hiểu Chính sách cookieChính sách bảo mật của chúng tôi.
Licensed under cc by-sa 3.0 with attribution required.