2012-11-08 3 views
8

Ich lade ein RGB-Bild von der Festplatte mit JuicyPixels-repa. Leider ist die Array-Darstellung des Bildes Array F DIM3 Word8, wobei die innere Dimension die RGB-Pixel ist. Das ist ein bisschen inkompatibel mit existierenden repa Bildverarbeitungsalgorithmen, wo ein RGB Bild Array U DIM2 (Word8, Word8, Word8) ist.Berechnen Sie ein Bildhistogramm mit Repa

ich die RGB-Histogramme des Bildes berechnet werden soll, bin ich eine Funktion mit der Signatur der Suche:

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

wie kann ich meine 3D-Array falten für jede colorchannel ein 1D-Array zu bekommen?

Edit:

Das Hauptproblem ist nicht, dass ich nicht in der Lage bin DIM3-DIM2 für jeden Kanal zu konvertieren (leicht mit Slicing getan). Das Problem ist, dass ich das Quellbild DIM2 oder DIM3 iterieren muss und zu einem DIM1 Array von einem anderen Shapeund Umfang akkumulieren muss. So kann ich repas foldS nicht verwenden, wie es die Dimension um eins verringert, aber mit dem gleichen Umfang.

Ich experimentierte auch mit traverse, aber es iteriert über die Ausdehnung des Zielbildes und bietet eine Funktion, um Pixel aus dem Quellbild zu erhalten, was zu sehr ineffizientem Code führen würde, der die gleichen Pixel für jeden Farbwert zählt.

Eine gute Möglichkeit wäre ein einfaches Falten über ein Vector mit dem Histogramm-Typ als Akkumulator sein, aber leider habe ich keinen U (unboxed) oder V (Vektor) basierten Array, aus dem ich effizient ein Vector bekommen kann. Ich habe eine Array F (Fremdzeiger).

+3

Was haben Sie versucht? (Könnten Sie es tun, wenn das Array den Typ 'Array U DIM2 (Word8, Word8, Word8) 'hätte? Wenn ja, können Sie eine Konvertierungsfunktion' Array U DIM3 Word8 -> Array U DIM2 (Word8, Word8, Word8) 'schreiben ?) – huon

+1

Ich werde versuchen, das morgen zu sehen. In der Zwischenzeit, wenn es grundlegende Konvertierungen zwischen Bilddarstellungen gibt, von denen Sie denken, dass sie in JP-repa nützlich wären, dann senden Sie bitte ein Patch. –

+0

@FalcoHirschenberger Hast du jemals Otsu Thresholding gemacht? Ich würde es mögen, aber ich möchte doppelte Arbeit vermeiden. –

Antwort

7

Ok, ich fand ein paar Minuten. Im Folgenden behandle ich vier Lösungen und habe die schlechtesten Lösungen (die mittleren zwei mit O (n) Datenkonvertierung) wirklich einfach für Sie gemacht.

Lets Bestätigen Sie die Dumb Lösung

Es ist vernünftig mit dem Offensichtlichen zu starten. Sie Data.List.foldl verwenden könnten die Zeilen und Spalten, den Aufbau Ihres Histogramme von Erstnullabgleich Arrays (ungetestet/Teil Code folgt) zu durchqueren:

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 

Ich bin nicht sicher, wie effizient dies sein wird, aber vermuten, dass es wird Überprüfe zu viele Grenzen. Der Wechsel zu unsafeIndex sollte vernünftigerweise tun, unter der Annahme, dass die verzögerten Arrays hist* gut funktionieren, obwohl Sie sich dafür entscheiden, incElem zu implementieren.

Sie können den Array Sie

Wollen Sie bauen

traverse Verwenden Sie tatsächlich konvertieren JP-Repa Stil Arrays in DIM2 Arrays mit Tupeln für Elemente:

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)) 

Können Sie mir auf den Körper zeigen von Code, über den Sie gesprochen haben, der dieses Format verwendet? Es wäre einfach, JP-Repa mit einer Funktion dieses Typs zu versehen.

Sie können die Unboxed Vector bauen Sie Erwähnt

Sie haben erwähnt, eine einfache Lösung zu falten unboxed Vektoren, aber beklagte, dass JP-repa keine unboxed Array liefert. Glücklicherweise Umwandlung ist einfach:

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

Wir können Patch-Repa

Das ist wirklich nur ein Problem, weil Repa hat nicht das, was ich eine normale traverse Funktion prüfen. Repas Traverse ist eher eine Array-Konstruktion, die zufällig eine Indizierungsfunktion in einem anderen Array bereitstellt. Wir wollen Traverse in der Form:

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

aber von grob ist dies eigentlich nur eine missgebildete Falte. So lässt es umbenennen und neu anordnen, die Argumente:

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

, die gut mit dem (bereits existierenden) foldAllS Betrieb kontrastiert:

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

Beachten Sie, wie unsere neue Falte zwei kritische Eigenschaften aufweist. Der Ergebnistyp muss nicht mit dem Elementtyp übereinstimmen, daher könnten wir mit einem Histogramm-Tupel beginnen. Zweitens übergibt unsere Version von fold den Index, mit dem Sie auswählen können, welches Histogramm im Tupel aktualisiert werden soll (falls vorhanden).

Sie können lazily verwenden die neuesten JuicyPixels-Repa

Um Ihre bevorzugte Repa Array-Format zu erwerben oder eine unboxed Vektor zu erwerben, können Sie die neu hochgeladen JuicyPixels-Repa-0.6 verwenden Sie einfach.

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

Jetzt können Sie den Vektor umklappen oder das Tupel-Array direkt verwenden, wie Sie ursprünglich gewünscht haben.

Ich habe auch einen hässlichen Stich in die erste, schrecklich naiv, Lösung konkretisiert:

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] ] 

ich zu vorsichtig bin von der Leistung dieses Code (3 Querungen pro Index ... Ich muß es sein müde), um es in JP-Repa zu werfen, aber wenn Sie finden, dass es gut funktioniert, lassen Sie mich wissen.

+0

Danke für deine tolle Antwort. Ich werde deine Vorschläge so schnell wie möglich versuchen. Ich versuche, den automatischen Schwellenwertalgorithmus von Otsu zu implementieren. Zu Ihrer Frage stellen die Algorithmen in 'repa-algorithms' ein Multichannel-Bild in der Form' Array U DIM2a' dar und stellen Pixelkonvertierungsfunktionen der Form 'rgb8OfFloat :: (Float, Float, Float) -> (Word8, Word8 , Word8) '. –