{-# LINE 1 "src/OpenCV/ImgProc/ImgFiltering.hsc" #-}
{-# language CPP #-}
{-# LINE 2 "src/OpenCV/ImgProc/ImgFiltering.hsc" #-}
{-# language QuasiQuotes #-}
{-# language TemplateHaskell #-}


{-# LINE 6 "src/OpenCV/ImgProc/ImgFiltering.hsc" #-}
{-# options_ghc -Wno-redundant-constraints #-}

{-# LINE 8 "src/OpenCV/ImgProc/ImgFiltering.hsc" #-}

{- |

Functions and classes described in this section are used to perform various
linear or non-linear filtering operations on 2D images (represented as
@'Mat'@\'s). It means that for each pixel location @(x,y)@ in the source image
(normally, rectangular), its neighborhood is considered and used to compute the
response. In case of a linear filter, it is a weighted sum of pixel values. In
case of morphological operations, it is the minimum or maximum values, and so
on. The computed response is stored in the destination image at the same
location @(x,y)@. It means that the output image will be of the same size as the
input image. Normally, the functions support multi-channel arrays, in which case
every channel is processed independently. Therefore, the output image will also
have the same number of channels as the input one.

Another common feature of the functions and classes described in this section is
that, unlike simple arithmetic functions, they need to extrapolate values of
some non-existing pixels. For example, if you want to smooth an image using a
Gaussian @3x3@ filter, then, when processing the left-most pixels in each
row, you need pixels to the left of them, that is, outside of the image. You can
let these pixels be the same as the left-most image pixels ("replicated border"
extrapolation method), or assume that all the non-existing pixels are zeros
("constant border" extrapolation method), and so on. OpenCV enables you to
specify the extrapolation method.
-}
module OpenCV.ImgProc.ImgFiltering
    ( MorphShape(..)
    , MorphOperation(..)

    , laplacian
    , medianBlur
    , erode
    , dilate
    , filter2D
    , morphologyEx
    , getStructuringElement
    , blur
    , gaussianBlur
    ) where

import "base" Data.Int
import "base" Data.Maybe
import "base" Data.Proxy
import "base" Data.Word
import qualified "inline-c" Language.C.Inline as C
import qualified "inline-c-cpp" Language.C.Inline.Cpp as C
import "linear" Linear.V2 ( V2(..) )
import "this" OpenCV.Core.Types
import "this" OpenCV.ImgProc.Types
import "this" OpenCV.Internal.C.Inline ( openCvCtx )
import "this" OpenCV.Internal.C.Types
import "this" OpenCV.Internal.Core.Types.Mat
import "this" OpenCV.Internal.Exception
import "this" OpenCV.Internal.ImgProc.Types ( marshalBorderMode )
import "this" OpenCV.TypeLevel

--------------------------------------------------------------------------------

C.context openCvCtx

C.include "opencv2/core.hpp"
C.include "opencv2/imgproc.hpp"
C.using "namespace cv"


{-# LINE 73 "src/OpenCV/ImgProc/ImgFiltering.hsc" #-}

{-# LINE 74 "src/OpenCV/ImgProc/ImgFiltering.hsc" #-}

{-# LINE 75 "src/OpenCV/ImgProc/ImgFiltering.hsc" #-}


{-# LINE 77 "src/OpenCV/ImgProc/ImgFiltering.hsc" #-}

{-# LINE 78 "src/OpenCV/ImgProc/ImgFiltering.hsc" #-}


--------------------------------------------------------------------------------
-- Constants
--------------------------------------------------------------------------------

defaultAnchor :: Point2i
defaultAnchor = toPoint (pure (-1) :: V2 Int32)


--------------------------------------------------------------------------------
-- Types
--------------------------------------------------------------------------------

data MorphShape
   = MorphRect -- ^ A rectangular structuring element.
   | MorphEllipse
     -- ^ An elliptic structuring element, that is, a filled ellipse inscribed
     -- into the rectangle Rect(0, 0, esize.width, 0.esize.height).
   | MorphCross !Point2i -- ^ A cross-shaped structuring element.

c'MORPH_RECT = 0
c'MORPH_RECT :: (Num a) => a

{-# LINE 100 "src/OpenCV/ImgProc/ImgFiltering.hsc" #-}
c'MORPH_ELLIPSE = 2
c'MORPH_ELLIPSE :: (Num a) => a

{-# LINE 101 "src/OpenCV/ImgProc/ImgFiltering.hsc" #-}
c'MORPH_CROSS = 1
c'MORPH_CROSS :: (Num a) => a

{-# LINE 102 "src/OpenCV/ImgProc/ImgFiltering.hsc" #-}

marshalMorphShape :: MorphShape -> (Int32, Point2i)
marshalMorphShape = \case
    MorphRect         -> (c'MORPH_RECT   , defaultAnchor)
    MorphEllipse      -> (c'MORPH_ELLIPSE, defaultAnchor)
    MorphCross anchor -> (c'MORPH_CROSS  , anchor)

data MorphOperation
   = MorphOpen     -- ^ An opening operation: dilate . erode
   | MorphClose    -- ^ A closing operation: erode . dilate
   | MorphGradient -- ^ A morphological gradient: dilate - erode
   | MorphTopHat   -- ^ "top hat": src - open
   | MorphBlackHat -- ^ "black hat": close - src

c'MORPH_OPEN = 2
c'MORPH_OPEN :: (Num a) => a

{-# LINE 117 "src/OpenCV/ImgProc/ImgFiltering.hsc" #-}
c'MORPH_CLOSE = 3
c'MORPH_CLOSE :: (Num a) => a

{-# LINE 118 "src/OpenCV/ImgProc/ImgFiltering.hsc" #-}
c'MORPH_GRADIENT = 4
c'MORPH_GRADIENT :: (Num a) => a

{-# LINE 119 "src/OpenCV/ImgProc/ImgFiltering.hsc" #-}
c'MORPH_TOPHAT = 5
c'MORPH_TOPHAT :: (Num a) => a

{-# LINE 120 "src/OpenCV/ImgProc/ImgFiltering.hsc" #-}
c'MORPH_BLACKHAT = 6
c'MORPH_BLACKHAT :: (Num a) => a

{-# LINE 121 "src/OpenCV/ImgProc/ImgFiltering.hsc" #-}

marshalMorphOperation :: MorphOperation -> Int32
marshalMorphOperation = \case
    MorphOpen     -> c'MORPH_OPEN
    MorphClose    -> c'MORPH_CLOSE
    MorphGradient -> c'MORPH_GRADIENT
    MorphTopHat   -> c'MORPH_TOPHAT
    MorphBlackHat -> c'MORPH_BLACKHAT


--------------------------------------------------------------------------------
-- Image Filtering
--------------------------------------------------------------------------------

{- | Calculates the Laplacian of an image

The function calculates the Laplacian of the source image by adding up
the second x and y derivatives calculated using the Sobel operator.

Example:

@
laplacianImg
    :: forall shape channels depth
     . (Mat shape channels depth ~ Birds_512x341)
    => Mat shape ('S 1) ('S Double)
laplacianImg = exceptError $ do
    imgG <- cvtColor bgr gray birds_512x341
    laplacian Nothing Nothing Nothing Nothing imgG
@

<<doc/generated/examples/laplacianImg.png laplacianImg>>

<http://docs.opencv.org/3.0-last-rst/modules/imgproc/doc/filtering.html#laplacian OpenCV Sphinx doc>
-}
laplacian
    :: forall shape channels srcDepth dstDepth
     . (ToDepth (Proxy dstDepth))
    => Maybe Int32
       -- ^ Aperture size used to compute the second-derivative filters. The
       -- size must be positive and odd. Default value is 1.
    -> Maybe Double
       -- ^ Optional scale factor for the computed Laplacian values. Default
       -- value is 1.
    -> Maybe Double
       -- ^ Optional delta value that is added to the results. Default value is
       -- 0.
    -> Maybe BorderMode
       -- ^ Pixel extrapolation method.
    -> Mat shape channels srcDepth
    -> CvExcept (Mat shape channels ('S dstDepth))
laplacian ksize scale delta borderType src = unsafeWrapException $ do
    dst <- newEmptyMat
    handleCvException (pure $ unsafeCoerceMat dst) $
      withPtr src $ \srcPtr ->
      withPtr dst $ \dstPtr ->
      [cvExcept|
        cv::Laplacian
        ( *$(Mat *   srcPtr      )
        , *$(Mat *   dstPtr      )
        ,  $(int32_t c'ddepth    )
        ,  $(int32_t c'ksize     )
        ,  $(double  c'scale     )
        ,  $(double  c'delta     )
        ,  $(int32_t c'borderType)
        );
      |]
  where
    c'ksize = fromMaybe 1 ksize
    c'scale = maybe 1 realToFrac scale
    c'delta = maybe 0 realToFrac delta
    c'ddepth = marshalDepth $ toDepth (Proxy :: Proxy dstDepth)
    c'borderType = fst $ marshalBorderMode $ fromMaybe BorderReflect101 borderType

{- | Blurs an image using the median filter

Example:

@
medianBlurImg
    :: forall (width    :: Nat)
              (width2   :: Nat)
              (height   :: Nat)
              (channels :: Nat)
              (depth    :: *)
     . ( Mat (ShapeT [height, width]) ('S channels) ('S depth) ~ Birds_512x341
       , width2 ~ ((*) width 2) -- TODO (RvD): HSE parse error with infix type operator
       )
    => Mat (ShapeT [height, width2]) ('S channels) ('S depth)
medianBlurImg = exceptError $
    withMatM (Proxy :: Proxy [height, width2])
             (Proxy :: Proxy channels)
             (Proxy :: Proxy depth)
             white $ \\imgM -> do
      birdsBlurred <- pureExcept $ medianBlur birds_512x341 13
      matCopyToM imgM (V2 0 0) birds_512x341 Nothing
      matCopyToM imgM (V2 w 0) birdsBlurred  Nothing
  where
    w = fromInteger $ natVal (Proxy :: Proxy width)
@

<<doc/generated/examples/medianBlurImg.png medianBlurImg>>

<http://docs.opencv.org/3.0-last-rst/modules/imgproc/doc/filtering.html#medianblur OpenCV Sphinx doc>
-}
-- TODO (Rvd): make ksize a type level argument
-- if ksize in [3, 5] then depth in [Word8, Int16, Float) else depth ~ Word8
medianBlur
    :: ( depth    `In` '[Word8, Word16, Float]
       , channels `In` '[1, 3, 4]
       -- , Length shape <= 2
       )
    => Mat shape ('S channels) ('S depth)
       -- ^ Input 1-, 3-, or 4-channel image; when ksize is 3 or 5, the image
       -- depth should be 'Word8', 'Word16', or 'Float', for
       -- larger aperture sizes, it can only be 'Word8'.
    -> Int32
       -- ^ Aperture linear size; it must be odd and greater than 1, for
       -- example: 3, 5, 7...
    -> CvExcept (Mat shape ('S channels) ('S depth))
medianBlur matIn ksize = unsafeWrapException $ do
    matOut <- newEmptyMat
    handleCvException (pure $ unsafeCoerceMat matOut) $
      withPtr matOut $ \matOutPtr ->
      withPtr matIn $ \matInPtr ->
        [cvExcept| cv::medianBlur(*$(Mat * matInPtr), *$(Mat * matOutPtr), $(int32_t ksize)); |]

{- | Blurs an image using a box filter.

Example:

@
boxBlurImg
    :: forall (width    :: Nat)
              (width2   :: Nat)
              (height   :: Nat)
              (channels :: Nat)
              (depth    :: *)
     . ( Mat (ShapeT [height, width]) ('S channels) ('S depth) ~ Birds_512x341
       , width2 ~ ((*) width 2) -- TODO (RvD): HSE parse error with infix type operator
       )
    => Mat (ShapeT [height, width2]) ('S channels) ('S depth)
boxBlurImg = exceptError $
    withMatM (Proxy :: Proxy [height, width2])
             (Proxy :: Proxy channels)
             (Proxy :: Proxy depth)
             white $ \\imgM -> do
      birdsBlurred <- pureExcept $ blur (V2 13 13 :: V2 Int32) birds_512x341
      matCopyToM imgM (V2 0 0) birds_512x341 Nothing
      matCopyToM imgM (V2 w 0) birdsBlurred  Nothing
  where
    w = fromInteger $ natVal (Proxy :: Proxy width)
@

<<doc/generated/examples/boxBlurImg.png boxBlurImg>>

<http://docs.opencv.org/3.0-last-rst/modules/imgproc/doc/filtering.html#blur OpenCV Sphinx doc>
-}
blur
  :: ( depth `In` '[Word8, Word16, Int16, Float, Double]
     , IsSize  size  Int32
     )
  => size  Int32 -- ^ Blurring kernel size.
  -> Mat shape ('S channels) ('S depth)
  -> CvExcept (Mat shape ('S channels) ('S depth))
blur size matIn =
  unsafeWrapException $
  do matOut <- newEmptyMat
     handleCvException (pure $ unsafeCoerceMat matOut) $
       withPtr ksize $ \ksizePtr ->
       withPtr matIn $ \matInPtr ->
       withPtr matOut $ \matOutPtr ->
       [cvExcept|
           cv::blur
           ( *$(Mat * matInPtr)
           , *$(Mat * matOutPtr)
           , *$(Size2i * ksizePtr)
           );
       |]
  where ksize :: Size2i
        ksize = toSize size

gaussianBlur
  :: ( depth `In` '[Word8, Word16, Float, Double]
     , IsSize size Int32
     )
  => size Int32 -- ^ Blurring kernel size.
  -> Double -- ^ sigmaX
  -> Double -- ^ sigmaY
  -> Mat shape ('S channels) ('S depth)
  -> CvExcept (Mat shape ('S channels) ('S depth))
gaussianBlur size sigmaX sigmaY matIn =
  unsafeWrapException $
  do matOut <- newEmptyMat
     handleCvException (pure $ unsafeCoerceMat matOut) $
       withPtr ksize $ \ksizePtr ->
       withPtr matIn $ \matInPtr ->
       withPtr matOut $ \matOutPtr ->
       [cvExcept|
           cv::GaussianBlur
           ( *$(Mat * matInPtr)
           , *$(Mat * matOutPtr)
           , *$(Size2i * ksizePtr)
           , $(double c'sigmaX)
           , $(double c'sigmaY)
           );
       |]
  where
    ksize :: Size2i
    ksize = toSize size

    c'sigmaX = realToFrac sigmaX
    c'sigmaY = realToFrac sigmaY

{- | Erodes an image by using a specific structuring element

Example:

@
erodeImg
    :: forall (width    :: Nat)
              (width2   :: Nat)
              (height   :: Nat)
              (channels :: Nat)
              (depth    :: *)
     . ( Mat (ShapeT [height, width]) ('S channels) ('S depth) ~ Lambda
       , width2 ~ ((*) width 2) -- TODO (RvD): HSE parse error with infix type operator
       )
    => Mat (ShapeT [height, width2]) ('S channels) ('S depth)
erodeImg = exceptError $
    withMatM (Proxy :: Proxy [height, width2])
             (Proxy :: Proxy channels)
             (Proxy :: Proxy depth)
             white $ \\imgM -> do
      erodedLambda <-
        pureExcept $ erode lambda Nothing (Nothing :: Maybe Point2i) 5 BorderReplicate
      matCopyToM imgM (V2 0 0) lambda Nothing
      matCopyToM imgM (V2 w 0) erodedLambda Nothing
  where
    w = fromInteger $ natVal (Proxy :: Proxy width)
@

<<doc/generated/examples/erodeImg.png erodeImg>>

<http://docs.opencv.org/3.0-last-rst/modules/imgproc/doc/filtering.html#erode OpenCV Sphinx doc>
-}
erode
    :: ( IsPoint2 point2 Int32
       , depth `In` [Word8, Word16, Int16, Float, Double]
       )
    => Mat shape channels ('S depth) -- ^ Input image.
    -> Maybe (Mat ('S [sh, sw]) ('S 1) ('S Word8))
       -- ^ Structuring element used for erosion. If `emptyMat` is
       -- used a @3x3@ rectangular structuring element is used. Kernel
       -- can be created using `getStructuringElement`.
    -> Maybe (point2 Int32) -- ^ anchor
    -> Int -- ^ iterations
    -> BorderMode
    -> CvExcept (Mat shape channels ('S depth))
erode src mbKernel mbAnchor iterations borderMode = unsafeWrapException $ do
    dst <- newEmptyMat
    handleCvException (pure $ unsafeCoerceMat dst) $
      withPtr src    $ \srcPtr    ->
      withPtr dst    $ \dstPtr    ->
      withPtr kernel $ \kernelPtr ->
      withPtr anchor $ \anchorPtr ->
      withPtr borderValue $ \borderValuePtr ->
        [cvExcept|
          cv::erode
          ( *$(Mat     * srcPtr        )
          , *$(Mat     * dstPtr        )
          , *$(Mat     * kernelPtr     )
          , *$(Point2i * anchorPtr     )
          ,  $(int32_t   c'iterations  )
          ,  $(int32_t   c'borderType  )
          , *$(Scalar  * borderValuePtr)
          );
        |]
  where
    kernel :: Mat 'D 'D 'D
    kernel = maybe (relaxMat emptyMat) unsafeCoerceMat mbKernel

    anchor :: Point2i
    anchor = maybe defaultAnchor toPoint mbAnchor

    c'iterations = fromIntegral iterations
    (c'borderType, borderValue) = marshalBorderMode borderMode

{- | Convolves an image with the kernel.

Example:

@
filter2DImg
    :: forall (width    :: Nat)
              (width2   :: Nat)
              (height   :: Nat)
              (channels :: Nat)
              (depth    :: *)
     . ( Mat (ShapeT [height, width]) ('S channels) ('S depth) ~ Birds_512x341
       , width2 ~ ((*) width 2) -- TODO (RvD): HSE parse error with infix type operator
       )
    => Mat (ShapeT [height, width2]) ('S channels) ('S depth)
filter2DImg = exceptError $
    withMatM (Proxy :: Proxy [height, width2])
             (Proxy :: Proxy channels)
             (Proxy :: Proxy depth)
             white $ \\imgM -> do
      filteredBird <-
        pureExcept $ filter2D birds_512x341 kernel (Nothing :: Maybe Point2i) 0 BorderReplicate
      matCopyToM imgM (V2 0 0) birds_512x341 Nothing
      matCopyToM imgM (V2 w 0) filteredBird Nothing
  where
    w = fromInteger $ natVal (Proxy :: Proxy width)
    kernel =
      exceptError $
      withMatM (Proxy :: Proxy [3, 3])
               (Proxy :: Proxy 1)
               (Proxy :: Proxy Double)
               black $ \\imgM -> do
        lift $ line imgM (V2 0 0 :: V2 Int32) (V2 0 0 :: V2 Int32) (V4 (-2) (-2) (-2) 1 :: V4 Double) 0 LineType_8 0
        lift $ line imgM (V2 1 0 :: V2 Int32) (V2 0 1 :: V2 Int32) (V4 (-1) (-1) (-1) 1 :: V4 Double) 0 LineType_8 0
        lift $ line imgM (V2 1 1 :: V2 Int32) (V2 1 1 :: V2 Int32) (V4   1    1    1  1 :: V4 Double) 0 LineType_8 0
        lift $ line imgM (V2 1 2 :: V2 Int32) (V2 2 1 :: V2 Int32) (V4   1    1    1  1 :: V4 Double) 0 LineType_8 0
        lift $ line imgM (V2 2 2 :: V2 Int32) (V2 2 2 :: V2 Int32) (V4   2    2    2  1 :: V4 Double) 0 LineType_8 0
@

<<doc/generated/examples/filter2DImg.png filter2DImg>>


<http://docs.opencv.org/3.0-last-rst/modules/imgproc/doc/filtering.html#filter2d OpenCV Sphinx doc>
-}
filter2D
    :: ( IsPoint2 point2 Int32
       , depth `In` [Word8, Word16, Int16, Float, Double]
       )
    => Mat shape channels ('S depth) -- ^ Input image.
    -> Mat ('S [sh, sw]) ('S 1) ('S Double)
       -- ^ convolution kernel (or rather a correlation kernel),
       -- a single-channel floating point matrix; if you want to
       -- apply different kernels to different channels, split the
       -- image into separate color planes using split and process
       -- them individually.
    -> Maybe (point2 Int32) -- ^ anchor
    -> Double -- ^ delta
    -> BorderMode
    -> CvExcept (Mat shape channels ('S depth))
filter2D src kernel mbAnchor delta borderMode = unsafeWrapException $ do
    dst <- newEmptyMat
    handleCvException (pure $ unsafeCoerceMat dst) $
      withPtr src    $ \srcPtr    ->
      withPtr dst    $ \dstPtr    ->
      withPtr kernel $ \kernelPtr ->
      withPtr anchor $ \anchorPtr ->
        [cvExcept|
          cv::filter2D
          ( *$(Mat     * srcPtr        )
          , *$(Mat     * dstPtr        )
          , -1
          , *$(Mat     * kernelPtr     )
          , *$(Point2i * anchorPtr     )
          ,  $(double    c'delta       )
          ,  $(int32_t   c'borderType  )
          );
        |]
  where
    anchor :: Point2i
    anchor = maybe defaultAnchor toPoint mbAnchor

    c'delta = realToFrac delta
    (c'borderType, _) = marshalBorderMode borderMode

{- | Dilates an image by using a specific structuring element

Example:

@
dilateImg
    :: forall (width    :: Nat)
              (width2   :: Nat)
              (height   :: Nat)
              (channels :: Nat)
              (depth    :: *)
     . ( Mat (ShapeT [height, width]) ('S channels) ('S depth) ~ Lambda
       , width2 ~ ((*) width 2) -- TODO (RvD): HSE parse error with infix type operator
       )
    => Mat (ShapeT [height, width2]) ('S channels) ('S depth)
dilateImg = exceptError $
    withMatM (Proxy :: Proxy [height, width2])
             (Proxy :: Proxy channels)
             (Proxy :: Proxy depth)
             white $ \\imgM -> do
      dilatedLambda <-
        pureExcept $ dilate lambda Nothing (Nothing :: Maybe Point2i) 3 BorderReplicate
      matCopyToM imgM (V2 0 0) lambda Nothing
      matCopyToM imgM (V2 w 0) dilatedLambda Nothing
  where
    w = fromInteger $ natVal (Proxy :: Proxy width)
@

<<doc/generated/examples/dilateImg.png dilateImg>>


<http://docs.opencv.org/3.0-last-rst/modules/imgproc/doc/filtering.html#dilate OpenCV Sphinx doc>
-}
dilate
    :: ( IsPoint2 point2 Int32
       , depth `In` [Word8, Word16, Int16, Float, Double]
       )
    => Mat shape channels ('S depth) -- ^ Input image.
    -> Maybe (Mat ('S [sh, sw]) ('S 1) ('S Word8))
       -- ^ Structuring element used for dilation. If `emptyMat` is
       -- used a @3x3@ rectangular structuring element is used. Kernel
       -- can be created using `getStructuringElement`.
    -> Maybe (point2 Int32) -- ^ anchor
    -> Int -- ^ iterations
    -> BorderMode
    -> CvExcept (Mat shape channels ('S depth))
dilate src mbKernel mbAnchor iterations borderMode = unsafeWrapException $ do
    dst <- newEmptyMat
    handleCvException (pure $ unsafeCoerceMat dst) $
      withPtr src    $ \srcPtr    ->
      withPtr dst    $ \dstPtr    ->
      withPtr kernel $ \kernelPtr ->
      withPtr anchor $ \anchorPtr ->
      withPtr borderValue $ \borderValuePtr ->
        [cvExcept|
          cv::dilate
          ( *$(Mat     * srcPtr        )
          , *$(Mat     * dstPtr        )
          , *$(Mat     * kernelPtr     )
          , *$(Point2i * anchorPtr     )
          ,  $(int32_t   c'iterations  )
          ,  $(int32_t   c'borderType  )
          , *$(Scalar  * borderValuePtr)
          );
        |]
  where
    kernel :: Mat 'D 'D 'D
    kernel = maybe (relaxMat emptyMat) unsafeCoerceMat mbKernel

    anchor :: Point2i
    anchor = maybe defaultAnchor toPoint mbAnchor

    c'iterations = fromIntegral iterations
    (c'borderType, borderValue) = marshalBorderMode borderMode

{- | Performs advanced morphological transformations

<http://docs.opencv.org/3.0-last-rst/modules/imgproc/doc/filtering.html#morphologyex OpenCV Sphinx doc>
-}
morphologyEx
    :: ( IsPoint2 point2 Int32
       , depth `In` [Word8, Word16, Int16, Float, Double]
       )
     => Mat shape channels ('S depth) -- ^ Source image.
    -> MorphOperation -- ^ Type of a morphological operation.
    -> Mat 'D 'D 'D -- ^ Structuring element.
    -> Maybe (point2 Int32) -- ^ Anchor position with the kernel.
    -> Int -- ^ Number of times erosion and dilation are applied.
    -> BorderMode
    -> CvExcept (Mat shape channels ('S depth))
morphologyEx src op kernel mbAnchor iterations borderMode = unsafeWrapException $ do
    dst <- newEmptyMat
    handleCvException (pure $ unsafeCoerceMat dst) $
      withPtr src    $ \srcPtr    ->
      withPtr dst    $ \dstPtr    ->
      withPtr kernel $ \kernelPtr ->
      withPtr anchor $ \anchorPtr ->
      withPtr borderValue $ \borderValuePtr ->
        [cvExcept|
          cv::morphologyEx
          ( *$(Mat     * srcPtr        )
          , *$(Mat     * dstPtr        )
          ,  $(int32_t   c'op          )
          , *$(Mat     * kernelPtr     )
          , *$(Point2i * anchorPtr     )
          ,  $(int32_t   c'iterations  )
          ,  $(int32_t   c'borderType  )
          , *$(Scalar  * borderValuePtr)
          );
        |]

  where
    c'op = marshalMorphOperation op

    anchor :: Point2i
    anchor = maybe defaultAnchor toPoint mbAnchor

    c'iterations = fromIntegral iterations
    (c'borderType, borderValue) = marshalBorderMode borderMode


{- | Returns a structuring element of the specified size and shape for
morphological operations

Example:

@
type StructureImg = Mat (ShapeT [128, 128]) ('S 1) ('S Word8)

structureImg :: MorphShape -> StructureImg
structureImg shape = exceptError $ do
    mat <- getStructuringElement shape (Proxy :: Proxy 128) (Proxy :: Proxy 128)
    img <- matConvertTo (Just 255) Nothing mat
    bitwiseNot img

morphRectImg :: StructureImg
morphRectImg = structureImg MorphRect

morphEllipseImg :: StructureImg
morphEllipseImg = structureImg MorphEllipse

morphCrossImg :: StructureImg
morphCrossImg = structureImg $ MorphCross $ toPoint (pure (-1) :: V2 Int32)
@

<<doc/generated/examples/morphRectImg.png morphRectImg>>
<<doc/generated/examples/morphEllipseImg.png morphEllipseImg>>
<<doc/generated/examples/morphCrossImg.png morphCrossImg>>

<http://docs.opencv.org/3.0-last-rst/modules/imgproc/doc/filtering.html#getstructuringelement OpenCV Sphinx doc>
-}
getStructuringElement
    :: (ToInt32 height, ToInt32 width)
    => MorphShape -- ^
    -> height
    -> width
    -> CvExcept (Mat (ShapeT (height ::: width ::: Z)) ('S 1) ('S Word8))
getStructuringElement morphShape height width = unsafeWrapException $ do
    element <- newEmptyMat
    handleCvException (pure $ unsafeCoerceMat element) $
      withPtr ksize   $ \ksizePtr   ->
      withPtr anchor  $ \anchorPtr  ->
      withPtr element $ \elementPtr ->
       [cvExcept|
         *$(Mat * elementPtr) =
           cv::getStructuringElement
           ( $(int32_t c'morphShape)
           , *$(Size2i * ksizePtr)
           , *$(Point2i * anchorPtr)
           );
       |]
  where
    ksize :: Size2i
    ksize = toSize $ V2 (toInt32 width) (toInt32 height)
    (c'morphShape, anchor) = marshalMorphShape morphShape