8

を使用して、ディスクから RGB イメージをロードしていJuicyPixels-repaます。残念ながら、画像の配列表現はArray F DIM3 Word8、内側の次元が RGB ピクセルである場所です。これrepaは、RGB 画像がArray U DIM2 (Word8, Word8, Word8).

画像の RGB ヒストグラムを計算したいので、シグネチャを使用して関数を検索しています。

type Hist = Array U DIM1 Int
histogram:: Array F DIM3  Word8 -> (Hist, Hist, Hist)

各カラーチャネルの 1 次元配列を取得するために 3 次元配列を折りたたむにはどうすればよいですか?

編集:

DIM3主な問題は、各チャネルを からに変換できないことではありませんDIM2(スライスで簡単に実行できます)。問題は、ソース イメージを反復処理するDIM2異なる範囲の配列DIM3に蓄積する必要があることです。したがって、次元を1つ減らすため、repa'sは使用できませんが、同じ程度です。DIM1Shape (Z:.256) foldS

私も実験しましtraverseたが、ソース画像からピクセルを取得する関数を提供して、宛先画像の範囲を反復処理します。これは、各カラー値に対して同じピクセルをカウントする非常に非効率的なコードにつながります。

良い方法はVector、アキュムレータとしてヒストグラム タイプを使用して を単純に折りたたむことですが、残念ながら、効率的に を取得できる (ボックス化されていないU) またはV(ベクトル) ベースの配列がありませんVector。私はArray F(外部ポインタ)を持っています。

4

1 に答える 1

7

わかりました、数分見つかりました。以下では、4 つの解決策について説明し、最悪の解決策 (O(n) データ変換を含む真ん中の 2 つ) を簡単に説明します。

愚かな解決策を認めましょう

明白なことから始めるのが合理的です。Data.List.foldl行と列をトラバースし、最初のゼロ配列からヒストグラムを構築するために使用できます (テストされていない/部分的なコードが続きます)。

foldl (\(histR, histG, histB) (row,col) ->
            let r = arr ! (Z:.row:.col:.0)
                g = arr ! (Z:.row:.col:.1)
                b = arr ! (Z:.row:.col:.2)
            in (incElem r histR, incElem g histG, incElem b histB)
         (zero,zero,zero)
         [ (row,col) | row <- [0..nrRow-1], col <- [0..nrCol-1] ]
 ...
 where (Z:.nrRow:.nrCol:._) = extent arr

これがどれほど効率的かはわかりませんが、境界チェックが多すぎると思われます。unsafeIndex への切り替えは、遅延配列hist*がうまく機能すると仮定すると、適切に機能するはずですincElem

必要なアレイを構築できます

を使用すると、実際に JP-Repa スタイルの配列を要素のタプルを持つtraverse配列に変換できます。DIM2

main = do
    let arr = R.fromFunction (Z:.a:.b:.c) (\(Z:.i:.j:.k) -> i+j-k)
        a =4 :: Int
        b = 4 :: Int
        c= 4 :: Int
        new = R.traverse arr
                       (\(Z:.r:.c:._) -> Z:.r:.c) -- the extent
                       (\l idx -> (l (idx:.0)
                                  ,l (idx:.1)
                                  ,l (idx :. 2)))
    print (R.computeS new :: R.Array R.U DIM2 (Int,Int,Int))

この形式を使用する、あなたが話したコードの本体を教えてもらえますか? このタイプの関数を含めるように JP-Repa にパッチを当てるのは簡単です。

あなたが言及したボックス化されていないベクトルを構築できます

簡単な解決策はボックス化されていないベクトルを折りたたむことだとおっしゃいましたが、JP-repa がボックス化されていない配列を提供していないことを嘆いています。幸いなことに、変換は簡単です。

toUnboxed :: Img a -> VU.Vector Word8
toUnboxed = R.toUnboxed . R.computeUnboxedS . R.delay . imgData

Repaにパッチを当てることができました

Repa には、私が通常の機能と見なす機能がないため、これは実際には単なる問題traverseです。Repa のトラバースは、たまたま別の配列にインデックス関数を提供する配列構築に近いものです。次の形式のトラバースが必要です。

newTraverse :: Array r sh e -> a -> (a -> sh -> e -> a) -> a

しかし、大雑把に言えば、これは実際には単なる不正な折り目です。名前を変更して、引数を並べ替えます。

foldAllIdxS :: (sh -> a - > e -> a) -> a -> Array r sh e -> a

foldAllS(既存の)操作とは対照的です。

foldAllS :: (a -> a -> a) -> a -> Array r sh a -> a

新しい折りたたみには 2 つの重要な特徴があることに注目してください。結果の型は要素の型と一致する必要がないため、ヒストグラムのタプルから始めることができます。次に、フォールドのバージョンはインデックスを渡します。これにより、タプル内のどのヒストグラムを更新するかを選択できます (存在する場合)。

最新の JuicyPixels-Repa を怠惰に使用できます

好みの Repa 配列形式を取得するか、ボックス化されていないベクターを取得するには、新しくアップロードされた JuicyPixels-Repa-0.6 を使用するだけです。

someImg <- readImage path :: IO (Either String (Img RGBA))
let img = either (error "Blah") id someImg
    uvec = toUnboxed img
    tupleArr = collapseColorChannel img

これで、最初に望んでいたように、ベクトルを折りたたむか、タプル配列を直接使用できます。

私はまた、最初の恐ろしく素朴な解決策を具体化することに醜い刺し傷を負いました。

histograms :: Img a -> (Histogram, Histogram, Histogram, Histogram)
histograms (Img arr) =
    let (Z:.nrRow:.nrCol:._) = R.extent arr
        zero = R.fromFunction (Z:.256) (\_ -> 0 :: Word8)
        incElem idx x = RU.unsafeTraverse x id (\l i -> l i + if i==(Z:.fromIntegral idx) then 1 else 0)
    in Prelude.foldl (\(hR, hG, hB, hA) (row,col) ->
             let r = R.unsafeIndex arr (Z:.row:.col:.0)
                 g = R.unsafeIndex arr (Z:.row:.col:.1)
                 b = R.unsafeIndex arr (Z:.row:.col:.2)
                 a = R.unsafeIndex arr (Z:.row:.col:.3)
             in (incElem r hR, incElem g hG, incElem b hB, incElem a hA))
          (zero,zero,zero,zero)
          [ (row,col) | row <- [0..nrRow-1], col <- [0..nrCol-1] ]

このコードのパフォーマンス (インデックスあたり 3 回のトラバーサル... 疲れているに違いない) を JP-Repa に投入するにはあまりにも警戒していますが、うまく機能することがわかったらお知らせください。

于 2012-11-10T07:40:10.443 に答える