Яке представлення Haskell рекомендується використовувати для двовимірних, незрощених піксельних масивів з мільйонами пікселів?


117

Я хочу вирішити деякі проблеми обробки зображень в Haskell. Я працюю як з бітональними (растровими), так і з кольоровими зображеннями з мільйонами пікселів. У мене є ряд питань:

  1. На якій підставі я повинен вибрати між Vector.Unboxedі UArray? Вони обидва нерозбірні масиви, але Vectorабстракція здається сильно рекламованою, зокрема навколо синтезу циклу. Чи Vectorзавжди краще? Якщо ні, то коли я повинен використовувати яке представлення?

  2. Для кольорових зображень я хотів би зберегти тричі 16-бітових цілих чи трійки одноточних чисел з плаваючою комою. З цією метою застосовується Vectorабо UArrayпростіше? Більш виконавський?

  3. Для бітональних зображень мені потрібно буде зберігати лише 1 біт на піксель. Чи є заздалегідь визначений тип даних, який може мені тут допомогти, упакувавши кілька пікселів у слово, чи я самостійно?

  4. Нарешті, мої масиви є двовимірними. Я думаю, я міг би мати справу з додатковою непрямістю, накладеною поданням як "масив масивів" (або вектор векторів), але я вважаю за краще абстракцію, яка має підтримку відображення індексів. Хтось може порекомендувати що-небудь із стандартної бібліотеки чи з Hackage?

Я функціональний програміст і не потребую мутацій :-)


2
Я думаю, що є лише Репа, яка відповідає номеру 4, див. Cse.unsw.edu.au/~chak/papers/repa.pdf .
Стівен Тетлі

5
@stephen: стандартний Arrayінтерфейс підтримує багатовимірні масиви. Ви можете просто використовувати кортеж для індексу.
Джон Л

13
Факт, що це питання є високооціненим та прихильним (включаючи мене), свідчить про те, що поводження з масивами Haskell не дуже добре зафіксовано.
Олександр К.

2
@Alexandre C.: Обробка базових повсякденних масивів добре задокументована; поводження з великими блоками пам’яті, що містять зміни даних, настільки ж просто, як і в C; якомога ефективніше обробляти великі незмінні багатовимірні масиви дещо менш очевидно. Йдеться про налаштування ефективності, коли сценарій, коли тонкі, менш задокументовані деталі будуть проблемою будь-якою мовою.
CA McCann

1
@Alexandre C.: Для більшості застосунків це безшовно. І це насправді не сам Haskell, це бібліотека та компілятор. З простою UArrayіндексованою сумою Ints просто працювати і часто досить добре, але навіть глибока магія GHC не збирається оптимізувати код за допомогою свого мінімального API в щось конкурентоспроможне з бібліотекою, створеною для швидкої паралельної масової обробки даних.
CA McCann

Відповіді:


89

Для моїх багатовимірних масивів, на мою думку, найкращим варіантом в Haskell є, на мій погляд, ремонт .

Repa забезпечує високоефективні, регулярні, багатовимірні, форми поліморфних паралельних масивів. Всі числові дані зберігаються без коробки. Функції, записані комбінаторами Repa, автоматично паралельно, якщо ви надаєте + RTS -Небач у командному рядку під час запуску програми.

Останнім часом його використовували для деяких проблем із обробкою зображень:

Я почав писати підручник з використання repa , який є гарним місцем для початку, якщо ви вже знаєте масиви Haskell або векторну бібліотеку. Ключовим кроком є ​​використання типів фігур замість простих індексних типів для вирішення багатовимірних індексів (і навіть трафаретів).

Пакет repa-io включає підтримку читання та запису файлів зображень .bmp, хоча необхідна підтримка більшої кількості форматів.

Вирішуючи ваші конкретні запитання, ось графічний, із обговоренням:


Усі три програми UArray, Vector та Repa підтримують розблокування.  Vector і Repa мають багатий, гнучкий API, але UArray цього не робить.  UArray та Repa мають багатовимірну індексацію, але вектор ні.  Всі вони мають підтримку пакування бітів, хоча Vector і Repa мають певні застереження щодо цього.  Vector та Repa взаємодіють із даними та кодом C, але UArray цього не робить.  Тільки Repa підтримує трафарети.


На основі якої я повинен вибрати між Vector.Unboxed та UArray?

Вони мають приблизно однакове базове представлення, проте головна відмінність полягає в широті API для роботи з векторами: вони мають майже всі операції, які ви зазвичай асоціюєте зі списками (з оптимізацією, керованою синтезом), при UArrayцьому майже немає API.

Для кольорових зображень я хотів би зберегти тричі 16-бітових цілих чи трійки одноточних чисел з плаваючою комою.

UArrayмає кращу підтримку багатовимірних даних, оскільки може використовувати довільні типи даних для індексації. Хоча це можливо Vector(написавши екземпляр UAдля свого типу елемента), це не є основною метою Vector- натомість, саме тут вводяться Repaкроки, що дозволяє дуже просто використовувати власні типи даних, які зберігаються ефективно, завдяки індексації форм

У Repaвашому трійці шортів буде тип:

Array DIM3 Word16

Тобто, 3D-масив Word16.

Для бітональних зображень мені потрібно буде зберігати лише 1 біт на піксель.

UArrays пакує Bools як біти, Vector використовує екземпляр для Bool, який робить бітове пакування, замість цього використовуючи представлення на основі Word8. Як би там не було, легко створити реалізацію пакунків для векторів - ось один , із (застарілої) бібліотеки увекторів. Під кришкою Repaвикористовується Vectors, тому я вважаю, що це успадковує вибір бібліотечних представлень.

Чи є заздалегідь визначений тип даних, який може допомогти мені тут, упакувавши кілька пікселів у слово

Ви можете використовувати наявні екземпляри для будь-якої з бібліотек, для різних типів слів, але вам може знадобитися написати кілька помічників за допомогою Data.Bits для прокатки та розгортання упакованих даних.

Нарешті, мої масиви є двовимірними

UArray і Repa підтримують ефективні багатовимірні масиви. Репа також має багатий інтерфейс для цього. Вектор сам по собі не має.


Помітні згадки:

  • hmatrix , тип користувальницького масиву з великими прив’язками до лінійних пакетів алгебри. Повинні бути пов'язані з використанням vectorабо repaтипів.
  • ix-формується , отримуючи більш гнучку індексацію від регулярних масивів
  • дошка , бібліотека Енді Гілла для маніпулювання двовимірними зображеннями
  • codec-image-devil , читати та записувати різні формати зображень в UArray

5
Крім того, тепер ви можете робити IO зображення з 3D-масивів репарації у багатьох форматах, завдяки repa-devil .
Дон Стюарт

2
Чи можете ви поясніть, як Repa може взаємодіяти з кодом C? Я не знайшов зберігаються екземпляри для Data.Array.Repa ...
зустріч

2
Копіювання в покажчики - це, мабуть, найпростіший шлях до даних, що зберігаються, але явно не є довгостроковим рішенням. Для цього нам знадобляться вектори, що зберігаються під капотом.
Дон Стюарт


17

Одного разу я ознайомився з особливостями важливих для мене бібліотек Haskell і склав таблицю порівняння (лише електронна таблиця: пряме посилання ). Тому я спробую відповісти.

На основі якої я повинен вибрати між Vector.Unboxed та UArray? Вони обидва неосмислені масиви, але абстракція Vector здається сильно рекламованою, зокрема навколо злиття циклу. Чи вектор завжди кращий? Якщо ні, то коли я повинен використовувати яке представлення?

UArray може віддавати перевагу над Vector, якщо потрібні двовимірні або багатовимірні масиви. Але Vector має приємніший API для маніпулювання, ну, вектори. Взагалі, вектор не дуже підходить для імітації багатовимірних масивів.

Vector.Unboxed не можна використовувати з паралельними стратегіями. Я підозрюю, що UArray також не може бути використаний, але, принаймні, дуже просто перейти з UArray на бокс-масив і побачити, чи може паралелізація виграти на боксерські витрати.

Для кольорових зображень я хотів би зберегти тричі 16-бітових цілих чи трійки одноточних чисел з плаваючою комою. З цією метою чи простіший у використанні вектор, чи UArray? Більш виконавський?

Я спробував використовувати масиви для зображення зображень (хоча мені потрібні були лише зображення сірого масштабу). Для кольорових зображень я використовував бібліотеку Codec-Image-DevIL для читання / запису зображень (прив'язки до бібліотеки DevIL), для зображень у градаціях сірого я використовував бібліотеку pgm (чистий Haskell).

Моя основна проблема Array полягала в тому, що він забезпечує лише зберігання у довільному доступі, але він не забезпечує багато засобів побудови алгоритмів масиву, а також не має готових до використання бібліотек підпрограм масиву (не взаємодіє з лінійними лінійками алгебри, не так не дозволяють виражати згортки, ффти та інші перетворення).

Майже щоразу, коли новий масив повинен бути побудований з існуючого, повинен бути складений проміжний список значень (як при множенні матриць із "Ніжного вступу"). Вартість побудови масиву часто переважає переваги швидшого випадкового доступу до того, що представлення на основі списку швидше в деяких випадках мого використання.

STUArray міг би мені допомогти, але мені не сподобалось битися з криптовалютними помилками та зусиллями, необхідними для написання поліморфного коду за допомогою STUArray .

Отже, проблема з масивами полягає в тому, що вони не дуже підходять для чисельних обчислень. Hmatrix 'Data.Packed.Vector і Data.Packed.Matrix кращі в цьому відношенні, оскільки вони поставляються разом із суцільною бібліотекою матриць (увага: ліцензія GPL). Під час множення матриці hmatrix була достатньо швидкою ( лише трохи повільніше, ніж Octave ), але дуже голодною до пам'яті (споживалася в кілька разів більше, ніж Python / SciPy).

Існує також бібліотека blas для матриць, але вона не побудована на GHC7.

Я ще не мав великого досвіду роботи з Repa, і я не добре розумію код ремонту. Як я бачу, у нього є дуже обмежений діапазон готових до використання матричних і масивних алгоритмів, написаних поверх нього, але принаймні можна виразити важливі алгоритми засобами бібліотеки. Наприклад, вже є підпрограми для множення матриць і для згортання в алгоритмах репарації. На жаль, здається, що згортання зараз обмежене 7 × 7 ядрами (мені це недостатньо, але їх вистачить для багатьох застосувань).

Я не пробував прив'язки Haskell OpenCV. Вони повинні бути швидкими, оскільки OpenCV дійсно швидкий, але я не впевнений, чи прив'язки завершені та якісні, щоб бути зручними. Також OpenCV за своєю природою є дуже необхідним, наповненим руйнівними оновленнями. Я думаю, важко розробити приємний та ефективний функціональний інтерфейс. Якщо хтось піде шляхом OpenCV, він, швидше за все, буде використовувати зображення представлення OpenCV скрізь, і використовувати OpenCV підпрограми для маніпулювання ними.

Для бітональних зображень мені потрібно буде зберігати лише 1 біт на піксель. Чи є заздалегідь визначений тип даних, який може мені тут допомогти, упакувавши кілька пікселів у слово, чи я самостійно?

Наскільки я знаю, Unboxed масиви Bools піклуються про упаковку та розпакування бітових векторів. Я пам'ятаю, як дивився на реалізацію масивів Bools в інших бібліотеках, і не бачив цього в іншому місці.

Нарешті, мої масиви є двовимірними. Я думаю, я міг би мати справу з додатковою непрямістю, накладеною поданням як "масив масивів" (або вектор векторів), але я вважаю за краще абстракцію, яка має підтримку відображення індексів. Хтось може порекомендувати що-небудь із стандартної бібліотеки чи з Hackage?

Окрім Vector (та простих списків), всі інші бібліотеки масивів здатні представляти двовимірні масиви чи матриці. Я гадаю, вони уникають несподіваних непрямих дій.


Згадані нижче прив'язки opencv є неповними. Дійсно неможливо, щоб одна людина створила та підтримувала повний набір для такої величезної бібліотеки. Однак використання opencv все ще економічно навіть у тому випадку, якщо вам доведеться створити обгортку для потрібної вам функції, оскільки вона реалізує деякі дійсно складні речі.
алеатор

@aleator Так, я розумію, що це дійсно величезна кількість роботи для однієї людини. BTW, якщо ви є обслуговуючим персоналом, чи можете ви будь-де опублікувати документи пікша, щоб можна було оцінити охоплення бібліотеки та прив’язок, не встановлюючи локально? (Документи недоступні в Hackage через помилку збірки; і для мене це не створюється, як ні GHC 6.12.1, ні GHC 7.0.2 через M_PIнезадекларовані).
зустріч

@jextee Ей, дякую за пораду! Я завантажив нову версію, яка може виправити обидві проблеми.
алеатор

@aleator Дякуємо, тепер він будується чисто.
зустріч

5

Хоча це не відповідає точно на ваше запитання і насправді навіть не є haskell як такою, я б рекомендував поглянути на CV або CV-комбінатори бібліотек при злому. Вони пов'язують безліч корисних операторів обробки зображень та зору з бібліотеки opencv і значно швидше працюють з проблемами машинного зору.

Було б досить чудово, якби хтось з'ясував, як репа або якусь подібну бібліотеку масиву можна було б безпосередньо використовувати з opencv.


0

Ось нова бібліотека обробки зображень Haskell, яка може впоратися з усіма розглянутими завданнями та багато іншого. В даний час він використовує пакети Repa і Vector для базових уявлень, які, отже, успадковують синтез, паралельні обчислення, мутації та більшість інших приємностей, які поставляються з цими бібліотеками. Він забезпечує простий у користуванні інтерфейс, який є природним для маніпулювання зображеннями:

  • 2D індексації та Unboxed пікселів з довільною точністю ( Double, Float, Word16і т.д ..)
  • всі основні функції , такі як map, fold, zipWith, traverse...
  • підтримка різних кольорових просторів: RGB, HSI, сіра гамма, двотоннальна, складна тощо.
  • загальна функціональність обробки зображень:
    • Двійкова морфологія
    • Звитий
    • Інтерполяція
    • Перетворення Фур'є
    • Складання графіків гістограми
    • тощо.
  • Можливість обробляти пікселі та зображення як звичайні номери.
  • Читання та запис загальних форматів зображень через бібліотеку JuicyPixels

Найголовніше, що це чиста бібліотека Haskell, тому вона не залежить від жодних зовнішніх програм. Він також дуже розширюється, можна ввести нові кольорові простори та зображення зображень.

Одне, що він не робить - це упаковка декількох двійкових пікселів у Word, а замість цього використовується Wordдвійковий піксель, можливо, у майбутньому ...

Використовуючи наш веб-сайт, ви визнаєте, що прочитали та зрозуміли наші Політику щодо файлів cookie та Політику конфіденційності.
Licensed under cc by-sa 3.0 with attribution required.