Quale rappresentazione Haskell è consigliata per array di pixel 2D senza scatola con milioni di pixel?


117

Voglio affrontare alcuni problemi di elaborazione delle immagini in Haskell. Sto lavorando sia con immagini bitonali (bitmap) che a colori con milioni di pixel. Ho una serie di domande:

  1. Su quale base dovrei scegliere tra Vector.Unboxede UArray? Sono entrambi array unboxed, ma l' Vectorastrazione sembra fortemente pubblicizzata, in particolare intorno alla fusione di loop. È Vectorsempre meglio? In caso contrario, quando dovrei usare quale rappresentazione?

  2. Per le immagini a colori, vorrei memorizzare triple di interi a 16 bit o triple di numeri in virgola mobile a precisione singola. A tal fine, è Vectoro UArraypiù facile da usare? Più performante?

  3. Per le immagini bitonali dovrò memorizzare solo 1 bit per pixel. Esiste un tipo di dati predefinito che può aiutarmi qui comprimendo più pixel in una parola o sono da solo?

  4. Infine, i miei array sono bidimensionali. Suppongo di poter gestire l'indirizzamento extra imposto da una rappresentazione come "array di array" (o vettore di vettori), ma preferirei un'astrazione che abbia il supporto per la mappatura dell'indice. Qualcuno può consigliare qualcosa da una libreria standard o da Hackage?

Sono un programmatore funzionale e non ho bisogno di mutazioni :-)


2
Penso che ci sia solo Repa che soddisfa il numero 4, vedi cse.unsw.edu.au/~chak/papers/repa.pdf .
stephen tetley

5
@stephen: l' Arrayinterfaccia standard supporta array multidimensionali. Puoi semplicemente usare una tupla per l'indice.
John L

13
Il fatto che questa domanda sia altamente votata e favorita (incluso da me) sembra indicare che la gestione degli array da parte di Haskell non è molto ben documentata.
Alexandre C.

2
@Alexandre C .: La gestione degli array quotidiani di base è ben documentata; gestire grandi blocchi di memoria che contengono dati mutabili è semplice come lo sarebbe in C; gestire grandi array multidimensionali immutabili nel modo più efficiente possibile è un po 'meno ovvio. Si tratta di ottimizzare le prestazioni di uno scenario in cui dettagli sottili e meno documentati rappresenterebbero un problema in qualsiasi lingua.
CA McCann

1
@Alexandre C .: Per la maggior parte delle applicazioni, è senza soluzione di continuità. E non è davvero Haskell stesso in questione, sono la libreria e il compilatore. Una pianura UArrayindicizzata da una tupla di Ints è semplice da lavorare e spesso abbastanza buona, ma anche la magia profonda di GHC non ottimizzerà il codice utilizzando la sua API minima in qualcosa di competitivo con una libreria ottimizzata per una rapida elaborazione di dati di massa parallelizzata.
CA McCann

Risposte:


89

Per gli array multidimensionali, la migliore opzione corrente in Haskell, a mio avviso, è repa .

Repa fornisce array paralleli polimorfici di forma regolare, multidimensionali e ad alte prestazioni. Tutti i dati numerici vengono memorizzati senza scatola. Le funzioni scritte con i combinatori Repa sono automaticamente parallele a condizione di fornire + RTS -N qualunque cosa sulla riga di comando quando si esegue il programma.

Recentemente, è stato utilizzato per alcuni problemi di elaborazione delle immagini:

Ho iniziato a scrivere un tutorial sull'uso di repa , che è un buon punto di partenza se conosci già gli array Haskell o la libreria vettoriale. Il trampolino di lancio chiave è l'uso di tipi di forma invece di semplici tipi di indice, per indirizzare gli indici multidimensionali (e persino gli stampini).

Il repa-io pacchetto include il supporto per la lettura e la scrittura di file immagine .bmp, sebbene sia necessario il supporto per più formati.

Affrontando le tue domande specifiche, ecco un grafico, con discussione:


Tutti e tre UArray, Vector e Repa supportano l'unboxing.  Vector e Repa hanno un'API ricca e flessibile, ma UArray no.  UArray e Repa hanno l'indicizzazione multidimensionale, ma Vector no.  Tutti supportano il bit-packing, sebbene Vector e Repa abbiano alcuni avvertimenti a tale riguardo.  Vector e Repa interagiscono con dati e codice C, ma UArray no.  Solo Repa supporta gli stampini.


Su quale base dovrei scegliere tra Vector.Unboxed e UArray?

Hanno approssimativamente la stessa rappresentazione sottostante, tuttavia, la differenza principale è l'ampiezza dell'API per lavorare con i vettori: hanno quasi tutte le operazioni che normalmente assoceresti agli elenchi (con un framework di ottimizzazione basato sulla fusione), mentre UArrayhanno quasi nessuna API.

Per le immagini a colori, vorrei memorizzare triple di interi a 16 bit o triple di numeri in virgola mobile a precisione singola.

UArrayha un supporto migliore per i dati multidimensionali, in quanto può utilizzare tipi di dati arbitrari per l'indicizzazione. Sebbene ciò sia possibile in Vector(scrivendo un'istanza di UAper il tipo di elemento), non è l'obiettivo principale di Vector- invece, è qui che Repaentra in gioco, rendendo molto facile l'uso di tipi di dati personalizzati memorizzati in modo efficiente, grazie all'indicizzazione della forma .

In Repa, il tuo triplo di pantaloncini avrebbe il tipo:

Array DIM3 Word16

Cioè, una matrice 3D di Word16.

Per le immagini bitonali dovrò memorizzare solo 1 bit per pixel.

Gli UArrays impacchettano Bools come bit, Vector usa l'istanza per Bool che esegue la compressione dei bit, invece utilizzando una rappresentazione basata su Word8. Tuttavia, è facile scrivere un'implementazione di pacchetti di bit per i vettori: eccone una , dalla libreria uvector (obsoleta). Sotto il cofano, RepausiVectors , quindi penso che erediti le scelte di rappresentazione delle biblioteche.

C'è un tipo di dati predefinito che può aiutarmi qui comprimendo più pixel in una parola

È possibile utilizzare le istanze esistenti per qualsiasi libreria, per diversi tipi di parole, ma potrebbe essere necessario scrivere alcuni helper utilizzando Data.Bits per eseguire il roll e lo srotolamento dei dati compressi.

Infine, i miei array sono bidimensionali

UArray e Repa supportano array multidimensionali efficienti. Repa ha anche una ricca interfaccia per farlo. Il vettore da solo non lo fa.


Menzioni notevoli:

  • hmatrix , un tipo di array personalizzato con estesi collegamenti a pacchetti di algebra lineare. Dovrebbe essere vincolato a utilizzare i tipi vectoro repa.
  • ix-shapeable , ottenendo un'indicizzazione più flessibile da array regolari
  • lavagna , la libreria di Andy Gill per la manipolazione di immagini 2D
  • codec-image-devil , legge e scrive vari formati di immagine su UArray

5
Inoltre, ora puoi eseguire l'IO dell'immagine di array di repa 3D in molti formati, grazie a repa-devil .
Don Stewart

2
Potresti spiegare come Repa può interagire con il codice C? Non ho trovato istanze memorizzabili per Data.Array.Repa ...
sastanin

2
La copia nei puntatori è probabilmente il percorso più semplice per i dati memorizzabili, ma chiaramente non è una soluzione a lungo termine. Per questo avremo bisogno di vettori memorizzabili sotto il cofano.
Don Stewart,


17

Una volta ho esaminato le funzionalità delle librerie di array Haskell che sono importanti per me e ho compilato una tabella di confronto (solo foglio di calcolo: collegamento diretto ). Quindi cercherò di rispondere.

Su quale base dovrei scegliere tra Vector.Unboxed e UArray? Sono entrambi array unboxed, ma l'astrazione Vector sembra fortemente pubblicizzata, in particolare intorno alla fusione di loop. Vector è sempre migliore? In caso contrario, quando dovrei usare quale rappresentazione?

UArray può essere preferito a Vector se sono necessari array bidimensionali o multidimensionali. Ma Vector ha API migliori per manipolare, beh, i vettori. In generale, Vector non è adatto per la simulazione di array multidimensionali.

Vector.Unboxed non può essere utilizzato con strategie parallele. Sospetto che nemmeno UArray possa essere utilizzato, ma almeno è molto facile passare da UArray a boxed Array e vedere se i vantaggi della parallelizzazione superano i costi di boxing.

Per le immagini a colori, vorrei memorizzare triple di interi a 16 bit o triple di numeri in virgola mobile a precisione singola. A tal fine, Vector o UArray sono più facili da usare? Più performante?

Ho provato a utilizzare gli array per rappresentare le immagini (anche se avevo bisogno solo di immagini in scala di grigi). Per le immagini a colori ho utilizzato la libreria Codec-Image-DevIL per leggere / scrivere immagini (collegamenti alla libreria DevIL), per le immagini in scala di grigi ho usato la libreria pgm (Haskell puro).

Il mio problema principale con Array era che fornisce solo archiviazione ad accesso casuale, ma non fornisce molti mezzi per costruire algoritmi Array né viene fornito con librerie pronte per l'uso di routine di array (non si interfaccia con le librerie di algebra lineare, non non consentono di esprimere convoluzioni, fft e altre trasformazioni).

Quasi ogni volta che si deve costruire un nuovo Array da quello esistente, deve essere costruito un elenco intermedio di valori (come nella moltiplicazione di matrici dalla Gentle Introduction). Il costo della costruzione dell'array spesso supera i vantaggi di un accesso casuale più veloce, al punto che una rappresentazione basata su elenchi è più veloce in alcuni dei miei casi d'uso.

STUArray avrebbe potuto aiutarmi, ma non mi piaceva combattere con errori di tipo criptici e gli sforzi necessari per scrivere codice polimorfico con STUArray .

Quindi il problema con gli array è che non sono adatti per i calcoli numerici. Data.Packed.Vector e Data.Packed.Matrix di Hmatrix sono migliori sotto questo aspetto, perché sono dotati di una solida libreria a matrice (attenzione: licenza GPL). Dal punto di vista delle prestazioni, sulla moltiplicazione di matrici, hmatrix era sufficientemente veloce ( solo leggermente più lento di Octave ), ma molto affamato di memoria (consumato molte volte di più di Python / SciPy).

C'è anche la libreria blas per le matrici, ma non si basa su GHC7.

Non avevo ancora molta esperienza con Repa e non capisco bene il codice repa. Da quello che vedo ha una gamma molto limitata di algoritmi di matrice e array pronti all'uso scritti sopra, ma almeno è possibile esprimere algoritmi importanti per mezzo della libreria. Ad esempio, esistono già routine per la moltiplicazione di matrici e per la convoluzione negli algoritmi repa. Sfortunatamente, sembra che la convoluzione sia ora limitata a kernel 7 × 7 (non è abbastanza per me, ma dovrebbe essere sufficiente per molti usi).

Non ho provato i collegamenti Haskell OpenCV. Dovrebbero essere veloci, perché OpenCV è davvero veloce, ma non sono sicuro che i collegamenti siano completi e abbastanza buoni da essere utilizzabili. Inoltre, OpenCV per sua natura è molto imperativo, pieno di aggiornamenti distruttivi. Suppongo che sia difficile progettare un'interfaccia funzionale bella ed efficiente sopra di essa. Se uno usa OpenCV, è probabile che utilizzi la rappresentazione di immagini OpenCV ovunque e utilizzi le routine OpenCV per manipolarle.

Per le immagini bitonali dovrò memorizzare solo 1 bit per pixel. Esiste un tipo di dati predefinito che può aiutarmi qui comprimendo più pixel in una parola o sono da solo?

Per quanto ne so, gli array Unboxed di Bools si occupano di impacchettare e decomprimere i vettori di bit. Ricordo di aver guardato l'implementazione di array di Bools in altre librerie, e non l'ho visto altrove.

Infine, i miei array sono bidimensionali. Suppongo di poter gestire l'indirizzamento extra imposto da una rappresentazione come "array di array" (o vettore di vettori), ma preferirei un'astrazione che abbia il supporto per la mappatura dell'indice. Qualcuno può consigliare qualcosa da una libreria standard o da Hackage?

A parte Vector (e semplici elenchi), tutte le altre librerie di array sono in grado di rappresentare array o matrici bidimensionali. Suppongo che evitino indirette indirette inutili.


Le associazioni opencv menzionate di seguito sono incomplete. Non è davvero possibile per una persona creare e mantenere un set completo per una libreria così vasta. Tuttavia, è ancora conveniente usare opencv anche se devi creare un wrapper per la funzione di cui hai bisogno, dal momento che implementa alcune cose davvero complesse.
aleator

@aleator Sì, capisco che è davvero un'enorme quantità di lavoro per una persona. A proposito, se sei un manutentore, potresti pubblicare documenti di eglefino da qualche parte, in modo che fosse possibile valutare la copertura della libreria e dei collegamenti senza installarli localmente? (i documenti non sono disponibili su Hackage a causa di un errore di compilazione; e non viene compilato per me né con GHC 6.12.1 né GHC 7.0.2 a causa di elementi M_PInon dichiarati).
sastanin

@jextee Hey, grazie per il suggerimento! Ho caricato una nuova versione che potrebbe risolvere entrambi i problemi.
aleator

@aleator Grazie, ora si costruisce in modo pulito.
sastanin

5

Anche se questo non risponde esattamente alla tua domanda e in realtà non è nemmeno haskell in quanto tale, consiglierei di dare un'occhiata alle librerie di CV o combinatori di CV su hackage. Associano i molti operatori di elaborazione delle immagini e di visione piuttosto utili dalla libreria opencv e rendono molto più veloce il lavoro con i problemi di visione artificiale.

Sarebbe piuttosto bello se qualcuno capisse come repa o alcune di queste librerie di array potrebbero essere utilizzate direttamente con opencv.


0

Ecco una nuova libreria Haskell Image Processing in grado di gestire tutte le attività in questione e molto altro ancora. Attualmente utilizza i pacchetti Repa e Vector per le rappresentazioni sottostanti, che di conseguenza eredita la fusione, il calcolo parallelo, la mutazione e la maggior parte delle altre chicche fornite con quelle librerie. Fornisce un'interfaccia facile da usare che è naturale per la manipolazione delle immagini:

  • 2D indicizzazione e disimballati pixel con precisione arbitraria ( Double, Float, Word16, ecc ..)
  • tutte le funzioni essenziali come map, fold, zipWith, traverse...
  • supporto per vari spazi colore: RGB, HSI, scala di grigi, Bi-tonale, Complesso, ecc.
  • funzionalità di elaborazione delle immagini comuni:
    • Morfologia binaria
    • circonvoluzione
    • interpolazione
    • trasformata di Fourier
    • Tracciamento dell'istogramma
    • eccetera.
  • Capacità di trattare pixel e immagini come numeri regolari.
  • Lettura e scrittura di formati di immagini comuni tramite la libreria JuicyPixels

Ancora più importante, è una libreria Haskell pura, quindi non dipende da programmi esterni. È anche altamente estendibile, è possibile introdurre nuovi spazi colore e rappresentazioni di immagini.

Una cosa che non fa è impacchettare più pixel binari in un Word, invece utilizza un Wordpixel binario per pixel, forse in futuro ...

Utilizzando il nostro sito, riconosci di aver letto e compreso le nostre Informativa sui cookie e Informativa sulla privacy.
Licensed under cc by-sa 3.0 with attribution required.