2

私はHaskellで画像処理プログラムに取り組んでいます。Repa-DevIL ライブラリは、画像処理に適したライブラリです。ただし、処理中の画像をリアルタイムで表示できる GUI が必要です。gtkImage を使用して Repa.Array タイプの画像を表示するにはどうすればよいですか?

4

1 に答える 1

1

RepaArrayPixbufgtkの に変換するのImageはかなり簡単です。Arrayこの関数は、 がすでに 32 ビット RGBA データを保持していることを前提としています。

-- full source with language extensions and includes is below
pixbufNewFromArray :: (Source r Word32) => Array r DIM2 Word32 -> IO Pixbuf
pixbufNewFromArray array = do
    let (Z:. width :. height) = extent array
    pixbuf <- pixbufNew ColorspaceRgb True 8 width height
    rowStrideBytes <- pixbufGetRowstride pixbuf
    let rowStride = rowStrideBytes `quot` 4
    pixbufPixels <- pixbufGetPixels pixbuf
    let copyPixel (x, y) = do
        writeArray pixbufPixels (y * rowStride + x) (index array (Z:. x :. y))
    mapM_ copyPixel $ range ((0, 0), (width-1, height-1))
    return pixbuf

残念ながら、これは Repa を gtk2hs で整数化するために必要なすべてではありません。メインの gtk スレッドまたはそのイベント ハンドラーのコンテキストで Repa 計算を実行すると、gtk がロックされます。これに対する解決策は、すべての Repa 計算をバックグラウンド スレッドで実行し、UI の更新をメイン スレッドに送信して実行することです。これの本質はforkIO、メイン スレッドで、UI の更新を で送り返すことpostGUIAsyncです。

完全な例

完全な例は 3 つの部分に分かれています。Repa を gtk2hs に接続する前に、リアルタイムで表示するデータが必要です。次に、Repa の例を gtk2hs に接続します。最後に、データを表示するための最小限の適切な gtk アプリケーションを提供します。以下は、この例に必要なすべてのディレクティブimportとディレクティブです。例の問題のステンシルにのみ必要です。LANGUAGEQuasiQuotes

{-# LANGUAGE QuasiQuotes #-}
{-# LANGUAGE DataKinds #-}
{-# LANGUAGE TypeOperators #-}
{-# LANGUAGE FlexibleContexts #-}

-- Repa life example
import Data.Array.Repa.Stencil
import Data.Array.Repa.Stencil.Dim2
import Data.Vector.Unboxed (Unbox)

-- Repa
import Data.Word
import Data.Array.Repa hiding (map)
import qualified Data.Array.Repa as A

-- GTK
import Graphics.UI.Gtk
import Data.Ix (range)
import Data.Array.MArray (writeArray)
import Control.Concurrent

気晴らし - 人生のゲーム

データの例として、面白いメトセラを使ったライフ ゲーム シミュレーションを使用します。ライフ ゲームは、ピクセルの近傍に適用される単純なルールです。各ピクセルは、生命が存在しない場合は 0、生命が存在する場合は 1 です。このルールは、近傍の数とピクセルの値のみに依存します。適切なステンシルを使用した畳み込みによって、近隣から必要なすべての情報を 1 つの数値にまとめることができます。

lifeStep :: (Source r1 a, Num a, Eq a) => Array r1 DIM2 a -> Array (TR PC5) DIM2 a
lifeStep = smap rule . mapStencil2 (BoundConst 0)
    [stencil2| 1 1  1
               1 16 1
               1 1  1 |]
    where
        {-# INLINE rule #-}
        rule 19 = 1
        rule 3  = 1
        rule 18 = 1
        rule _  = 0

まず興味深いサンプル データは、「r」ペントミノの形をした生命です。

rPentomino :: (Num a, Unbox a) => Array U DIM2 a
rPentomino = fromListUnboxed (Z :. 3 :. 3) 
    [0, 1, 1,
     1, 1, 0,
     0, 1, 0]

この人生は、その周りの世界に拡大しようとしています。拡大する余地を与えるために、追加の余地を埋めることができればいいと思います。

pad2 :: (Source r1 a) => a -> Int -> Int -> Int -> Int -> Array r1 DIM2 a -> Array D DIM2 a
pad2 a left right bottom top middle =
    traverse middle shape fill
    where
        extents = extent middle
        (Z :. width :. height) = extents
        shape = const (Z :. left + width + right :. bottom + height + top)
        {-# INLINE fill #-}
        fill lookup (Z :. x :. y) =
            if inShape extents newPoint
            then lookup newPoint
            else a
                where
                    newPoint = (Z :. x - left :. y - bottom)

pentominoWorld0 :: (Num a, Unbox a) => Array D DIM2 a
pentominoWorld0 = pad2 0 100 100 100 100 rPentomino

私たちは自分の人生を白い背景に黒いピクセルとして描くつもりです。以下は、生細胞または死細胞から RGBA のこれらの色へのマッピングを実装します。

lifeBonW :: (Source r1 a, Num a, Eq a, Shape sh) => Array r1 sh a -> Array D sh Word32
lifeBonW = A.map color
    where
        {-# INLINE color #-}
        color 0 = 0xFFFFFFFF
        color _ = 0xFF000000

gtk2hsにリパ

repa を gtk2hs に接続するには、 Repa を gtk2hs で使用される に変換できる必要がありArrayますPixbuf。次にPixbuf、画像に を描画する必要があります。以下は RGBAの をArrayRGBAに変換します。RGBAは、一度に 1 つのピクセルのチャネルだけではなく、一度に 1 つのピクセルを書き込むことができるため、望ましいものです。Word32PixbufPixbuf

pixbufNewFromArray :: (Source r Word32) => Array r DIM2 Word32 -> IO Pixbuf
pixbufNewFromArray array = do
    let (Z:. width :. height) = extent array
    pixbuf <- pixbufNew ColorspaceRgb True 8 width height
    rowStrideBytes <- pixbufGetRowstride pixbuf
    let rowStride = rowStrideBytes `quot` 4
    pixbufPixels <- pixbufGetPixels pixbuf
    let copyPixel (x, y) = do
        writeArray pixbufPixels (y * rowStride + x) (index array (Z:. x :. y))
    mapM_ copyPixel $ range ((0, 0), (width-1, height-1))
    return pixbuf

sをレンダリングする方法が与えられるとPixbuf、次のコード片は人生ゲームのシミュレーションを実行し、Arrays をに変換しPixbuf、フレーム間で待機します。

renderThread :: (Pixbuf -> IO ()) -> IO ()
renderThread draw =
    do
        world0 <- computeP . ofWord8s $ pentominoWorld0
        go world0
    where
        go world = do
            pixbuf <- pixbufNewFromArray . lifeBonW . unboxed $ world
            draw pixbuf
            nextWorld <- computeP . lifeStep $ world
            threadDelay 50000 -- microseconds
            go nextWorld

unboxedおよびofWord8sは、そうでなければ非常に多様な RepaArrayの型を指定する便利な方法です。

unboxed :: Array U sh a -> Array U sh a
unboxed = id

ofWord8s :: Array r sh Word8 -> Array r sh Word8
ofWord8s = id

GTK

GTK コードは最小限で、単一のWindowImage. 重要なことはすべてラインで行われforkIO . renderThread $ postGUIAsync . imageSetFromPixbuf imageます。前述の を開始し、正しい gtk スレッドで表示内容を確実に設定renderThreadする表示手段を提供します。PixbufImage

main = do
    initGUI
    window <- windowNew
    image <- imageNew
    set window [containerChild := image]
    onDestroy window mainQuit
    widgetShowAll window
    forkIO . renderThread $ postGUIAsync . imageSetFromPixbuf image
    mainGUI

スレッド化されたコンパイルおよびランタイム フラグ

ほとんどの Repa プログラムと同様に、これは次のようにコンパイルする必要があります (-fllvmお持ちの場合)。

-Odph -rtsopts -threaded -fno-liberate-case -funfolding-use-threshold1000 -funfolding-keeness-factor1000 -optlo-O3

実行時に、次のフラグを渡す必要があります

+RTS -N
于 2014-08-22T21:14:36.933 に答える