数百万のピクセルを持つ2Dボックス化されていないピクセル配列には、どのHaskell表現が推奨されますか?


117

Haskellの画像処理の問題に取り組みたい。私は、数百万ピクセルのモノクロ(ビットマップ)とカラー画像の両方を扱っています。いくつか質問があります。

  1. Vector.Unboxedとの間でどちらを選択すればよいUArrayですか?どちらもボックス化されていない配列ですが、Vector抽象化は特にループフュージョンを中心に宣伝されているようです。でVector常により良いですか?そうでない場合、いつどの表現を使用すればよいですか?

  2. カラー画像の場合、16ビット整数のトリプルまたは単精度浮動小数点数のトリプルを格納したいと思います。この目的のために、どちらVectorUArray使いやすいですか?より高性能ですか?

  3. モノクロイメージの場合、ピクセルごとに1ビットのみを保存する必要があります。複数のピクセルを1つの単語にパックすることによってここで私を助けることができる事前定義されたデータ型はありますか、それとも私一人でですか?

  4. 最後に、私の配列は2次元です。「配列の配列」(またはベクトルのベクトル)としての表現によって課される追加の間接参照を処理できると思いますが、インデックスマッピングをサポートする抽象化を好みます。誰でも標準ライブラリやHackageから何かを推薦できますか?

私は関数型プログラマーであり、突然変異の必要はありません:-)


2
4 番目に該当する Repaだけがあると思います。cse.unsw.edu.au/~chak/papers/repa.pdfを参照してください。
スティーブンテトリー

5
@stephen:標準Arrayインターフェースは多次元配列をサポートします。単純にタプルをインデックスに使用できます。
ジョンL

13
この質問が非常に支持され、好まれているという事実(私も含む)は、Haskellの配列の処理が十分に文書化されていないことを示しているようです。
アレクサンドルC.

2
@Alexandre C .:基本的な日常の配列の処理は十分に文書化されています。変更可能なデータを保持するメモリの大きなブロックの処理は、Cの場合と同じくらい簡単です。大規模な不変の多次元配列を可能な限り効率的に処理することは、ややわかりにくいです。これは、微妙で十分に文書化されていない詳細がどの言語でも問題になるシナリオのパフォーマンスチューニングに関するものです。
CAマッキャン、

1
@Alexandre C .:ほとんどのアプリケーションでは、シームレスです。そして、問題となっているのは実際にはHaskell自体ではなく、ライブラリとコンパイラです。sのUArrayタプルによってインデックス付けされたプレーンIntは、操作が簡単で十分なことがよくありますが、GHCの深いマジックでさえ、その最小APIを使用して、高速並列化されたバルクデータ処理用に調整されたライブラリと競合するものにコードを最適化することはできません。
CAマッキャン、

回答:


89

多次元配列の場合、私の考えでは、Haskellの現在の最良のオプションはrepaです。

Repaは、高性能で規則的な多次元形状多形並列配列を提供します。すべての数値データはボックス化されずに保存されます。プログラムの実行時にコマンドラインで+ RTS -Nwhateverを指定すると、Repaコンビネーターで作成された関数は自動的に並列になります。

最近、いくつかの画像処理の問題に使用されています。

repaの使用に関するチュートリアルを書き始めまし。これは、Haskellの配列またはベクトルライブラリをすでに知っている場合は、このページから始めるのに適しています。重要な足がかりは、単純なインデックスタイプではなくシェイプタイプを使用して、多次元インデックス(およびステンシル)に対処することです。

REPA-IOパッケージには、より多くのフォーマットのサポートが必要とされているものの、読み、.BMPイメージファイルを書き込むためのサポートが含まれています。

あなたの特定の質問に対処すること、これが議論のあるグラフィックです:


UArray、Vector、およびRepaの3つすべてがボックス化解除をサポートしています。 VectorとRepaには豊富で柔軟なAPIがありますが、UArrayにはありません。 UArrayとRepaには多次元のインデックスがありますが、Vectorにはありません。 ベクターとRepaにはその点でいくつかの警告がありますが、それらはすべてビットパッキングをサポートしています。 VectorおよびRepaはCデータおよびコードと相互運用しますが、UArrayは相互運用しません。 Repaのみがステンシルをサポートしています。


Vector.UnboxedとUArrayのどちらを選択する必要がありますか?

これらの基本的な表現はほぼ同じですが、主な違いは、ベクターを操作するためのAPIの幅です。通常、リストに関連付けるほとんどすべての操作(フュージョン駆動の最適化フレームワークを使用)があり、 UArray、ほとんど持っていますAPIはありません。

カラー画像の場合、16ビット整数のトリプルまたは単精度浮動小数点数のトリプルを格納したいと思います。

UArray索引付けに任意のデータ型を使用できるため、多次元データのサポートが向上しています。これはVectorUA要素タイプののインスタンスを作成することで)可能ですが、その主な目的ではありません。Vector代わりに、これがRepaステップインして、効率的な方法で格納されたカスタムデータタイプを非常に簡単に使用できるようにします。形のおかげでインデックスの。

ではRepa、ショートのトリプルは次のタイプになります。

Array DIM3 Word16

つまり、Word16の3D配列です。

モノクロイメージの場合、ピクセルごとに1ビットのみを保存する必要があります。

UArrayはBoolをビットとしてパックします。Vectorは、ビットパッキングを行うBoolのインスタンスを使用し、代わりにに基づく表現を使用しWord8ます。- Howver、ベクトルのビットパッキングの実装を書くのは簡単です、ここでひとつです(廃止)ユニフォームベクタライブラリから、。フードの下で、Repa使用Vectorsしているため、そのライブラリ表現の選択を継承していると思います。

複数のピクセルを1つの単語にパックすることにより、ここで私に役立つ事前定義されたデータ型はありますか

既存のインスタンスを任意のライブラリのさまざまな単語タイプに使用できますが、Data.Bitsを使用していくつかのヘルパーを記述し、パックされたデータをロールおよびアンロールする必要がある場合があります。

最後に、私の配列は2次元です

UArrayとRepaは効率的な多次元配列をサポートしています。Repaには、そうするための豊富なインターフェースもあります。ベクター自体にはありません。


注目すべき言及:

  • hmatrix、線形代数パッケージへの広範なバインディングを持つカスタム配列型。vectorまたはrepaタイプを使用するようにバインドする必要があります。
  • ix-shapeable、通常の配列からより柔軟なインデックス付け
  • 黒板、2D画像を操作するためのアンディギルのライブラリ
  • codec-image-devil、さまざまな画像形式の読み取りとUArrayへの書き込み

5
また、repa-devilのおかげで、3D repa配列の画像IOをさまざまな形式で実行できるようになりました。
Don Stewart、

2
RepaがCコードと相互運用する方法を説明していただけませんか?Data.Array.Repa ...のStorableインスタンスが見つかりませんでした
sastanin

2
ポインタへのコピーはおそらく保存可能なデータへの最も簡単な道ですが、明らかに長期的な解決策ではありません。そのためには、内部に格納可能なベクターが必要です。
Don Stewart、


17

自分にとって重要なHaskell配列ライブラリの機能を確認し、比較表を作成しました(スプレッドシートのみ:直接リンク)。だから答えようと思います。

Vector.UnboxedとUArrayのどちらを選択する必要がありますか?どちらもボックス化されていない配列ですが、ベクターの抽象化は、特にループフュージョンを中心に宣伝されているようです。ベクターは常に優れていますか?そうでない場合、いつどの表現を使用すればよいですか?

2次元または多次元配列が必要な場合は、UArrayがVectorよりも優先される場合があります。しかし、Vectorには、ベクターを操作するためのより優れたAPIがあります。一般に、Vectorは多次元配列のシミュレーションにはあまり適していません。

Vector.Unboxedは、並列戦略では使用できません。UArrayもどちらも使用できないと思いますが、少なくともUArrayからボックス化配列に切り替えて、並列化の利点がボクシングコストを上回るかどうかを確認するのは非常に簡単です。

カラー画像の場合、16ビット整数のトリプルまたは単精度浮動小数点数のトリプルを格納したいと思います。この目的のために、VectorまたはUArrayを使用する方が簡単ですか?より高性能ですか?

配列を使用して画像を表現しようとしました(ただし、グレースケール画像のみが必要でした)。カラー画像の場合、Codec-Image-DevILライブラリを使用して画像(DevILライブラリへのバインディング)を読み書きし、グレースケール画像の場合、pgmライブラリ(純粋なHaskell)を使用しました。

配列に関する私の主な問題は、ランダムアクセスストレージしか提供しないことですが、配列アルゴリズムを構築する多くの手段を提供せず、配列ルーチンのライブラリをすぐに使用できるようになりません(線形代数ライブラリーとのインターフェースはありません、畳み込み、FFT、その他の変換を表現することはできません)。

ほとんどの場合、既存の配列から新しい配列を作成する必要があるたびに、値の中間リストを作成する必要があります(行列乗算など) Gentle Introductionの)。多くの場合、配列の構築コストは、ランダムアクセスが高速であることの利点を上回っており、リストベースの表現の方が私のユースケースの一部で高速であるという点にまで達しています。

STUArrayは私を助けることができたかもしれませんが、私はSTUArrayでポリモーフィックなコードを書くために必要な暗号タイプのエラーや努力と戦うのが好きではありませんでした

したがって、配列の問題は、配列が数値計算にあまり適していないことです。HmatrixのData.Packed.VectorとData.Packed.Matrixは、固体マトリックスライブラリ(注意:GPLライセンス)が付属しているため、この点で優れています。パフォーマンスの観点から、行列の乗算では、hmatrixは十分に高速でしたが(Octaveよりわずかに遅い)、非常にメモリを消費しました(Python / SciPyの数倍以上消費されました)。

マトリックス用のblasライブラリもありますが、GHC7に基づいて構築されていません。

私はまだRepaの経験があまりなく、repaコードをよく理解していません。私が見るところから見ると、その上に書かれたすぐに使用できる行列および配列アルゴリズムの範囲は非常に限られていますが、少なくともライブラリを使用して重要なアルゴリズムを表現することは可能です。たとえば、行列の乗算と反復アルゴリズムの畳み込みのルーチンは既に存在します。残念ながら、畳み込みは現在7×7カーネルに制限されているようです(私には十分ではありませんが、多くの用途には十分です)。

Haskell OpenCVバインディングは試していません。OpenCVは本当に高速であるため、高速である必要がありますが、バインディングが完全であり、使用できるほど十分であるかどうかはわかりません。また、OpenCVはその性質上、破壊的な更新に満ちた非常に不可欠です。その上に素敵で効率的な機能的インターフェースを設計するのは難しいと思います。OpenCVの方法を採用する場合、彼はどこでもOpenCV画像表現を使用し、OpenCVルーチンを使用してそれらを操作する可能性があります。

モノクロイメージの場合、ピクセルごとに1ビットのみを保存する必要があります。複数のピクセルを1つの単語にパックすることによってここで私を助けることができる事前定義されたデータ型はありますか、それとも私一人でですか?

私の知る限り、Boolのボックス化されていない配列は、ビットベクトルのパックとアンパックを処理します。他のライブラリでのBoolsの配列の実装を見た覚えがありますが、他の場所では見られませんでした。

最後に、私の配列は2次元です。「配列の配列」(またはベクトルのベクトル)としての表現によって課される追加の間接参照を処理できると思いますが、インデックスマッピングをサポートする抽象化を好みます。誰でも標準ライブラリやHackageから何かを推薦できますか?

ベクトル(および単純なリスト)を除いて、他のすべての配列ライブラリは2次元配列または行列を表すことができます。彼らは不必要な間接参照を避けていると思います。


下記のopencvバインディングは不完全です。このような巨大なライブラリの完全なセットを1人で作成して維持することは実際には不可能です。ただし、必要な関数のラッパーを作成する必要がある場合でも、opencvを使用すると、非常に複雑なものが実装されるため、コスト効率がよくなります。
エアレーター

@aleatorはい、私は一人のために本当に膨大な量の仕事であることを理解しています。ところで、あなたがメンテナーなら、hadockのドキュメントをどこかに公開してもらえますか?ローカルにインストールせずにライブラリとバインディングのカバレッジを評価することができましたか?(ビルドエラーのため、ドキュメントはHackageで利用できません。また、宣言されていないため、GHC 6.12.1とGHC 7.0.2のどちらでもビルドできませんM_PI)。
サスタニン

@jexteeこんにちは、ヒントをありがとう!両方の問題を修正する可能性のある新しいバージョンをアップロードしました。
アリレーター

@aleatorおかげで、きれいにビルドできるようになりました。
サスタニン

5

これはあなたの質問に正確に答えているわけではなく、実際にはそれ自体はハッケルすらしていませんが、ハックでCVまたはCV-combinatorsライブラリを確認することをお勧めします。それらは、opencv-libraryからの多くのかなり有用な画像処理およびビジョンオペレーターをバインドし、マシンビジョンの問題をはるかに高速に処理します。

repaまたはそのような配列ライブラリをopencvで直接使用する方法を誰かが理解できれば、それはかなり素晴らしいことです。


0

問題となっているすべてのタスクを処理できる新しいHaskell画像処理ライブラリがあります。現在、基礎となる表現にRepaおよびVectorパッケージを使用しています。その結果、融合、並列計算、突然変異、およびこれらのライブラリに付属するその他のほとんどの機能が継承されます。これは、画像操作に自然な使いやすいインターフェースを提供します。

  • 任意の精度で2Dインデックスとアンボックス化画素(DoubleFloatWord16、など。)
  • すべての重要な機能が好きmapfoldzipWithtraverse...
  • さまざまなカラースペースのサポート:RGB、HSI、グレースケール、バイトーナル、コンプレックスなど
  • 一般的な画像処理機能:
    • バイナリ形態
    • たたみ込み
    • 補間
    • フーリエ変換
    • ヒストグラムのプロット
  • ピクセルと画像を通常の数値として扱う機能。
  • JuicyPixelsライブラリを介した一般的な画像形式の読み取りと書き込み

最も重要なのは、それが純粋なHaskellライブラリであるため、外部プログラムに依存しないことです。また、拡張性が高く、新しい色空間と画像表現を導入できます。

これが行わないことの1つは、複数のバイナリピクセルをにパックすることですWord。代わりに、Wordバイナリピクセルごとに使用することになるでしょう。

弊社のサイトを使用することにより、あなたは弊社のクッキーポリシーおよびプライバシーポリシーを読み、理解したものとみなされます。
Licensed under cc by-sa 3.0 with attribution required.