Reputation: 11628
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:
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.(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
Upvotes: 2
Views: 109
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
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