accelerate-examples-0.2.0.0: tests/image-processing/Canny.hs
{-# LANGUAGE TypeOperators #-}
module Canny where
import PGM
import Data.Array.Accelerate hiding (zipWith, unindex2)
import qualified Data.Array.Accelerate as Acc
type Image a = Array DIM2 a
type Stencil7x1 a = (Stencil3 a, Stencil7 a, Stencil3 a)
type Stencil1x7 a = (Stencil3 a, Stencil3 a, Stencil3 a, Stencil3 a, Stencil3 a, Stencil3 a, Stencil3 a)
convolve7x1 :: (Elt a, IsNum a) => [Exp a] -> Stencil7x1 a -> Exp a
convolve7x1 kernel (_, (a,b,c,d,e,f,g), _) =
sum $ zipWith (*) kernel [a,b,c,d,e,f,g]
convolve1x7 :: (Elt a, IsNum a) => [Exp a] -> Stencil1x7 a -> Exp a
convolve1x7 kernel ((_,a,_), (_,b,_), (_,c,_), (_,d,_), (_,e,_), (_,f,_), (_,g,_)) =
sum $ zipWith (*) kernel [a,b,c,d,e,f,g]
-- Gaussian smoothing
--
gaussian :: (Elt a, IsFloating a) => [Exp a]
gaussian = [ 0.00442012927963
, 0.05384819825462
, 0.24133088157513
, 0.39788735772974
, 0.24133088157513
, 0.05384819825462
, 0.00442012927963 ]
gaussianX :: (Elt a, IsFloating a) => Acc (Image a) -> Acc (Image a)
gaussianX = stencil (convolve7x1 gaussian) (Constant 0)
gaussianY :: (Elt a, IsFloating a) => Acc (Image a) -> Acc (Image a)
gaussianY = stencil (convolve1x7 gaussian) (Constant 0)
-- Gaussian derivative and gradient quantisation
--
gaussian' :: (Elt a, IsFloating a) => [Exp a]
gaussian' = [ 0.02121662054222
, 0.17231423441479
, 0.38612941052022
, 0.0
,-0.38612941052022
,-0.17231423441479
,-0.02121662054222 ]
gradientX :: (Elt a, IsFloating a) => Acc (Image a) -> Acc (Image a)
gradientX = stencil (convolve7x1 gaussian') (Constant 0)
gradientY :: (Elt a, IsFloating a) => Acc (Image a) -> Acc (Image a)
gradientY = stencil (convolve1x7 gaussian') (Constant 0)
gradientMagnitude :: (Elt a, IsFloating a) => Acc (Image a) -> Acc (Image a) -> Acc (Image a)
gradientMagnitude = Acc.zipWith magdir
where
magdir dx dy = let mag = sqrt (dx*dx + dy*dy)
-- dir = atan2 dy dx
in mag -- lift (mag, dir)
-- Non-maximum suppression
--
nonMaximumSuppression
:: (Elt a, IsFloating a)
=> Exp a
-> Acc (Image a)
-> Acc (Image a)
-> Acc (Image a)
-> Acc (Image a)
nonMaximumSuppression threshold gradX gradY gradM =
generate (shape gradX) $ \ix ->
let dx = gradX ! ix
dy = gradY ! ix
mag = gradM ! ix
alpha = 1.3065629648763766 -- 0.5 / sin (pi / 8.0)
offsetx = Acc.round (alpha * dx / mag)
offsety = Acc.round (alpha * dy / mag)
--
(m,n) = unindex2 (shape gradX)
(x,y) = unindex2 ix
fwd = gradM ! lift (clamp (x+offsetx, y+offsety))
rev = gradM ! lift (clamp (x-offsetx, y-offsety))
--
unindex2 uv = let Z:.u:.v = unlift uv in (u,v)
clamp (u,v) = lift (Z:. 0 `Acc.max` u `Acc.min` (m-1) :. 0 `Acc.max` v `Acc.min` (n-1))
in
(mag <* threshold ||* fwd >* mag ||* rev >* mag) ? (0, 1)
-- Canny edge detection
--
canny :: (Elt a, IsFloating a) => Image a -> Acc (Image a)
canny img =
let smooth = gaussianX . gaussianY $ use img
gradX = gradientX smooth
gradY = gradientY smooth
gradMag = gradientMagnitude gradX gradY
in
nonMaximumSuppression 0.1 gradX gradY gradMag
-- Main
-- ----
-- TLM: should compare to a pre-saved reference image
run :: FilePath -> IO (() -> Acc (Array DIM2 Float))
run file = do
pgm <- readPGM file
return (\() -> canny pgm)