flawr
flawr

Reputation: 11628

Efficiently accessing Pixels

I just discovered this post and was wondering how to do something like that in Haskell. In the following I just simplify the part that I had difficulties with:

I have a list with a large number of 2D coordinates. For simplicitys sake let us assume that the list contains Int coordinates that are all in the range 0 - 1023

points :: [(Int,Int)]  -- long list of points

The goal is now having an image (let us say img :: [[Int]] where img and each entry thereof has length 1024) where (img!!i)!!j is the number of occurences of (i,j) in points (which would get mapped to some gray values).

What I tried so far:

  1. If I just use the imperative approach of looping over points and trying to incerment the entry i,j of some 2d array, the part about picking some entry i,j and incrementing it is very cumbersome and probably not very efficient on the one hand, and on the other hand using !! is prone to index too large errors.
  2. A slightly more efficinet approach: For each pairf of coordinates (i,j) use partition to filter out the (i,j) entries and count them, and pass the rest of the list to the next pair of coordnates. This way we get a list of (i,j, count) and we just have to plug count into each pixel i,j once, but this is still not very elegant in my opinion.

So can you suggest any solutions on how to do this in more efficient and elegant functional way?

This is just meant as an exemplary problem, because I frequently ran into similar problems and only found unsatisfactory solutions.


EDIT: As requested here is an example of such code:


main = putStrLn "before:" >> print myImg  >> putStrLn "after:" >> print myImg2
       >> putStrLn "output of our \"image\":" >> print outImg

n :: Int
n = 4

-- example for "increment"
myImg = [[n..n+3]|n<-[1,5..16]] :: [[Int]]
myImg2 = increment myImg 2 3 10000

-- example for our application
zeroImg = [[0,0,0,0]|_<-[0,0,0,0]]
outImg = foldl (\img (i,j) -> increment img i j 1 ) zeroImg points

-- our "data" (here im just filling this with arbitrary numbers)
points = [(i `mod` n,j `mod` n)|i <-[1..14],j<-[i..77+i]] 


-- not very elegant code
increment :: [[Int]] -> Int -> Int -> Int -> [[Int]]
increment img i j v = a ++ [x ++ [y + v] ++ z] ++  c -- increments the "pixel" (i,j) by v
  where 
   a = take i img  
   b = img !! i
   c = drop (i+1) img
   x = take j b
   y = b !! j
   z = drop (j+1) b

Try it online!

Upvotes: 2

Views: 109

Answers (2)

freestyle
freestyle

Reputation: 3790

You can use, as one of variants, Data.Array:

import Data.Array

main = do
    putStrLn "before:" >> print myArr
    putStrLn "after:" >> print myArr2
    putStrLn "output of our \"image\":" >> print outArr

n :: Int
n = 4

-- example for "increment"
myArr = listArray ((0,0), (n-1,n-1)) [1..]
myArr2 = accum (+) myArr [((2, 3), 1000)]

-- example for our application
outArr = accumArray (+) 0 ((0,0), (n-1,n-1)) [ (p, 1) | p <- points ]

-- our "data" (here im just filling this with arbitrary numbers)
points = [(i `mod` n,j `mod` n)|i <-[1..14],j<-[i..77+i]]

Using repa with vector:

import Control.Monad
import Data.Array.Repa
import qualified Data.Vector.Generic as V
import qualified Data.Vector.Generic.Mutable as MV

main = putStrLn "output of our \"image\":" >> print outArr

n :: Int
n = 1024

-- example for our application
outArr :: Array U DIM2 Int
outArr = fromUnboxed (Z :. n :. n) $ V.create $ do
    v <- MV.new (n * n)
    MV.set v 0
    forM_ points $ \(i, j) -> do
        let idx = i * n + j
        MV.modify v (+1) idx
    pure v

-- our "data" (here im just filling this with arbitrary numbers)
points = [(i `mod` n,j `mod` n)|i <-[1..14],j<-[i..77+i]]

Upvotes: 2

Jogger
Jogger

Reputation: 1709

One possibility to do image processing in Haskell is with Comonads. It allows to specify image processing algorithms in an elegant fuctional way. A good and accessible introduction is https://jaspervdj.be/posts/2014-11-27-comonads-image-processing.html.

Upvotes: 1

Related Questions