Chcę rozwiązać kilka problemów z przetwarzaniem obrazu w Haskell. Pracuję zarówno z obrazami bitonalnymi (bitmapowymi), jak i kolorowymi z milionami pikseli. Mam kilka pytań:
Na jakiej podstawie mam wybierać między
Vector.Unboxed
aUArray
? Obie są tablicami rozpakowanymi, aleVector
abstrakcja wydaje się mocno reklamowana, szczególnie w przypadku fuzji pętli. CzyVector
zawsze jest lepsze? Jeśli nie, kiedy należy użyć której reprezentacji?W przypadku obrazów kolorowych będę chciał przechowywać trójek 16-bitowych liczb całkowitych lub trójek liczb zmiennoprzecinkowych o pojedynczej precyzji. W tym celu jest albo
Vector
lubUArray
łatwiejsze w użyciu? Bardziej wydajne?W przypadku obrazów bitonalnych będę musiał przechowywać tylko 1 bit na piksel. Czy istnieje predefiniowany typ danych, który może mi pomóc, pakując wiele pikseli w słowo, czy też jestem sam?
Wreszcie moje tablice są dwuwymiarowe. Przypuszczam, że mógłbym poradzić sobie z dodatkowym kierunkiem narzuconym przez reprezentację jako „tablica tablic” (lub wektor wektorów), ale wolałbym abstrakcję, która obsługuje mapowanie indeksów. Czy ktoś może polecić coś ze standardowej biblioteki lub z Hackage?
Jestem programistą funkcjonalnym i nie potrzebuję mutacji :-)
źródło
Array
interfejs obsługuje wielowymiarowe tablice. Możesz po prostu użyć krotki jako indeksu.UArray
indeksowany przez krotkęInt
s jest prosty w obsłudze i często wystarczająco dobry, ale nawet głęboka magia GHC nie zoptymalizuje kodu przy użyciu jego minimalnego interfejsu API do czegoś konkurencyjnego z biblioteką dostosowaną do szybkiego równoległego masowego przetwarzania danych.Odpowiedzi:
Moim zdaniem w przypadku tablic wielowymiarowych obecnie najlepszą opcją w Haskell jest repa .
Ostatnio był używany do niektórych problemów z przetwarzaniem obrazu:
Zacząłem pisać samouczek dotyczący korzystania z repa , który jest dobrym miejscem do rozpoczęcia, jeśli znasz już tablice Haskell lub bibliotekę wektorów. Kluczowym krokiem jest użycie typów kształtów zamiast prostych typów indeksowych w celu uwzględnienia wielowymiarowych indeksów (a nawet szablonów).
Pakiet repa-io zawiera obsługę odczytu i zapisu plików obrazów .bmp, chociaż potrzebna jest obsługa większej liczby formatów.
W odpowiedzi na Twoje konkretne pytania, oto grafika z omówieniem:
Na jakiej podstawie mam wybierać między Vector.Unboxed a UArray?
Mają w przybliżeniu tę samą podstawową reprezentację, jednak podstawową różnicą jest szerokość interfejsu API do pracy z wektorami: mają prawie wszystkie operacje, które normalnie kojarzysz z listami (z platformą optymalizacji opartą na fuzji), podczas gdy
UArray
prawie brak API.W przypadku obrazów kolorowych będę chciał przechowywać trójek 16-bitowych liczb całkowitych lub trójek liczb zmiennoprzecinkowych o pojedynczej precyzji.
UArray
ma lepszą obsługę danych wielowymiarowych, ponieważ może używać dowolnych typów danych do indeksowania. Chociaż jest to możliwe wVector
(pisząc wystąpienieUA
dla typu elementu), nie jest to głównym celemVector
- zamiast tego jest to miejsce, w którymRepa
wkracza, co bardzo ułatwia korzystanie z niestandardowych typów danych przechowywanych w efektywny sposób, dzięki indeksowaniu kształtu .W
Repa
twoim potrójnym spodenku będzie typ:Oznacza to trójwymiarową tablicę Word16.
W przypadku obrazów bitonalnych będę musiał przechowywać tylko 1 bit na piksel.
UArrays pakują Bools jako bity, Vector używa instancji dla Bool, która wykonuje pakowanie bitów, zamiast tego używa reprezentacji opartej na
Word8
. Jakkolwiek, łatwo jest napisać implementację pakowania bitów dla wektorów - oto jedna z (przestarzałej) biblioteki uvector. Pod maskąRepa
używaVectors
, więc myślę, że dziedziczy wybory reprezentacji bibliotek.Czy istnieje predefiniowany typ danych, który może mi tutaj pomóc, pakując wiele pikseli w słowo
Możesz użyć istniejących instancji dla dowolnej biblioteki, dla różnych typów słów, ale może być konieczne napisanie kilku pomocników przy użyciu Data.Bits do przewijania i rozwijania spakowanych danych.
Wreszcie moje tablice są dwuwymiarowe
UArray i Repa obsługują wydajne tablice wielowymiarowe. Repa ma również bogaty interfejs do tego. Sam Vector tego nie robi.
Wybitne wzmianki:
vector
lubrepa
.źródło
Kiedyś przejrzałem funkcje bibliotek tablicowych Haskell, które są dla mnie ważne, i skompilowałem tabelę porównawczą (tylko arkusz kalkulacyjny: bezpośredni link ). Więc spróbuję odpowiedzieć.
UArray może być preferowany względem Vector, jeśli potrzebne są tablice dwuwymiarowe lub wielowymiarowe. Ale Vector ma lepszy interfejs API do manipulowania, no cóż, wektorami. Ogólnie rzecz biorąc, Vector nie nadaje się dobrze do symulowania tablic wielowymiarowych.
Vector.Unboxed nie może być używany ze strategiami równoległymi. Podejrzewam, że UArray również nie może być używany, ale przynajmniej bardzo łatwo jest przejść z UArray na boxed Array i zobaczyć, czy korzyści z równoległości przewyższają koszty pakowania.
Próbowałem używać tablic do reprezentowania obrazów (chociaż potrzebowałem tylko obrazów w skali szarości). Do obrazów kolorowych użyłem biblioteki Codec-Image-DevIL do odczytu / zapisu obrazów (powiązania z biblioteką DevIL), dla obrazów w skali szarości użyłem biblioteki pgm (czysty Haskell).
Moim głównym problemem związanym z Arrayem było to, że zapewnia on tylko pamięć o dostępie swobodnym, ale nie zapewnia wielu sposobów budowania algorytmów Array ani nie zawiera gotowych do użycia bibliotek procedur tablicowych (nie współpracuje z bibliotekami algebry liniowej, nie nie pozwalają na wyrażanie splotów, fft i innych przekształceń).
Prawie za każdym razem, gdy trzeba zbudować nową tablicę z istniejącej, należy skonstruować pośrednią listę wartości (jak w mnożeniu macierzy z Łagodnego wprowadzenia). Koszt budowy tablicy często przewyższa korzyści płynące z szybszego losowego dostępu do tego stopnia, że reprezentacja oparta na liście jest szybsza w niektórych moich przypadkach użycia.
STUArray mógł mi pomóc, ale nie lubiłem walczyć z tajemniczymi błędami typu i wysiłkami niezbędnymi do napisania kodu polimorficznego za pomocą STUArray .
Problem z tablicami polega na tym, że nie nadają się one dobrze do obliczeń numerycznych. Hmatrix 'Data.Packed.Vector i Data.Packed.Matrix są lepsze pod tym względem, ponieważ są dostarczane wraz z solidną biblioteką macierzy (uwaga: licencja GPL). Pod względem wydajności, przy mnożeniu macierzy, hmatrix była wystarczająco szybka ( tylko nieznacznie wolniejsza niż Octave ), ale bardzo wymagała pamięci (zużywała kilka razy więcej niż Python / SciPy).
Istnieje również biblioteka blas dla macierzy, ale nie jest oparta na GHC7.
Nie miałem jeszcze dużego doświadczenia z Repą i nie rozumiem dobrze kodu repa. Z tego co widzę ma bardzo ograniczony zakres gotowych do użycia algorytmów macierzowych i tablicowych napisanych na wierzchu, ale przynajmniej da się wyrazić za pomocą biblioteki ważne algorytmy. Na przykład, istnieją już procedury mnożenia macierzy i splotu w algorytmach repa. Niestety wydaje się, że splot jest teraz ograniczony do jądra 7 × 7 (dla mnie to nie wystarcza, ale powinno wystarczyć do wielu zastosowań).
Nie próbowałem powiązań Haskell OpenCV. Powinny być szybkie, ponieważ OpenCV jest naprawdę szybkie, ale nie jestem pewien, czy wiązania są kompletne i wystarczająco dobre, aby nadawały się do użytku. Ponadto OpenCV ze swej natury jest bardzo niezbędny, pełen destrukcyjnych aktualizacji. Podejrzewam, że ciężko jest zaprojektować na dodatek ładny i wydajny funkcjonalny interfejs. Jeśli ktoś pójdzie drogą OpenCV, prawdopodobnie użyje reprezentacji obrazu OpenCV wszędzie i użyje procedur OpenCV do manipulowania nimi.
O ile wiem, Unboxed tablice Bools zajmują się pakowaniem i rozpakowywaniem wektorów bitowych. Pamiętam, jak patrzyłem na implementację tablic Bools w innych bibliotekach i nie widziałem tego nigdzie indziej.
Oprócz Vector (i prostych list), wszystkie inne biblioteki tablic mogą reprezentować dwuwymiarowe tablice lub macierze. Przypuszczam, że unikają niepotrzebnego pośrednictwa.
źródło
M_PI
niezadeklarowanego).Chociaż nie jest to dokładna odpowiedź na twoje pytanie i tak naprawdę nie jest nawet haskellem jako takim, polecam przyjrzeć się CV lub kombinatorom CV bibliotekom podczas hackage. Łączą one wielu dość użytecznych operatorów przetwarzania obrazu i wizyjnych z biblioteki opencv i znacznie przyspieszają pracę z problemami z widzeniem maszynowym.
Byłoby raczej wspaniale, gdyby ktoś odkrył, w jaki sposób repa lub jakaś taka biblioteka tablic może być bezpośrednio używana z opencv.
źródło
Oto nowa biblioteka Haskell Image Processing, która może obsłużyć wszystkie wymienione zadania i wiele więcej. Obecnie używa pakietów Repa i Vector do reprezentacji bazowych, co w konsekwencji dziedziczy fuzję, obliczenia równoległe, mutację i większość innych dodatków, które są dostarczane z tymi bibliotekami. Zapewnia łatwy w użyciu interfejs, który jest naturalny do manipulacji obrazem:
Double
,Float
,Word16
itd ..)map
,fold
,zipWith
,traverse
...Co najważniejsze, jest to czysta biblioteka Haskella, więc nie jest zależna od żadnych zewnętrznych programów. Jest również wysoce rozszerzalny, można wprowadzić nowe przestrzenie kolorów i reprezentacje obrazów.
Jedyną rzeczą, której nie robi, jest pakowanie wielu pikseli binarnych w a
Word
, zamiast tego używaWord
jednego piksela binarnego, być może w przyszłości ...źródło