packages feed

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)