#if __GLASGOW_HASKELL__ >= 800
#endif
module OpenCV.ImgProc.FeatureDetection
( canny
, goodFeaturesToTrack
, houghCircles
, houghLinesP
, GoodFeaturesToTrackDetectionMethod(..)
, CannyNorm(..)
, Circle(..)
, LineSegment(..)
) where
import "base" Control.Exception ( mask_ )
import "base" Data.Int
import "base" Data.Maybe
import qualified "vector" Data.Vector as V
import "base" Data.Word
import "base" Foreign.Marshal.Alloc ( alloca )
import "base" Foreign.Marshal.Array ( peekArray )
import "base" Foreign.Marshal.Utils ( fromBool )
import "base" Foreign.Ptr ( Ptr )
import "base" Foreign.Storable ( peek )
import "base" Prelude hiding ( lines )
import "base" System.IO.Unsafe ( unsafePerformIO )
import qualified "inline-c" Language.C.Inline as C
import qualified "inline-c" Language.C.Inline.Unsafe as CU
import qualified "inline-c-cpp" Language.C.Inline.Cpp as C
import "linear" Linear ( V2(..), V3(..), V4(..) )
import "primitive" Control.Monad.Primitive ( PrimMonad, PrimState, unsafePrimToPrim )
import "this" OpenCV.Core.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.TypeLevel
#if MIN_VERSION_base(4,9,0)
import "base" Data.Foldable ( Foldable )
import "base" Data.Traversable ( Traversable )
#endif
C.context openCvCtx
C.include "opencv2/core.hpp"
C.include "opencv2/imgproc.hpp"
C.using "namespace cv"
canny
:: Double
-> Double
-> Maybe Int32
-> CannyNorm
-> Mat ('S [h, w]) channels ('S Word8)
-> CvExcept (Mat ('S [h, w]) ('S 1) ('S Word8))
canny threshold1 threshold2 apertureSize norm src = unsafeWrapException $ do
dst <- newEmptyMat
handleCvException (pure $ unsafeCoerceMat dst) $
withPtr src $ \srcPtr ->
withPtr dst $ \dstPtr ->
[cvExcept|
cv::Canny
( *$(Mat * srcPtr)
, *$(Mat * dstPtr)
, $(double c'threshold1)
, $(double c'threshold2)
, $(int32_t c'apertureSize)
, $(bool c'l2Gradient)
);
|]
where
c'threshold1 = realToFrac threshold1
c'threshold2 = realToFrac threshold2
c'apertureSize = fromMaybe 3 apertureSize
c'l2Gradient =
fromBool $
case norm of
CannyNormL1 -> False
CannyNormL2 -> True
data CannyNorm
= CannyNormL1
| CannyNormL2
deriving (Show, Eq)
data Circle
= Circle
{ circleCenter :: V2 Float
, circleRadius :: Float
} deriving (Show)
goodFeaturesToTrack
:: (depth `In` ['S Word8, 'S Float, 'D])
=> Mat ('S [h, w]) ('S 1) depth
-> Int32
-> Double
-> Double
-> Maybe (Mat ('S [h, w]) ('S 1) ('S Word8))
-> Maybe Int32
-> GoodFeaturesToTrackDetectionMethod
-> V.Vector (V2 Float)
goodFeaturesToTrack src maxCorners qualityLevel minDistance mbMask blockSize detector = unsafePerformIO $ do
withPtr src $ \srcPtr ->
withPtr mbMask $ \mskPtr ->
alloca $ \(cornersLengthsPtr :: Ptr Int32) ->
alloca $ \(cornersPtrPtr :: Ptr (Ptr (Ptr C'Point2f))) -> mask_ $ do
[C.block| void {
std::vector<cv::Point2f> corners;
Mat * mskPtr = $(Mat * mskPtr);
cv::goodFeaturesToTrack
( *$(Mat * srcPtr)
, corners
, $(int32_t maxCorners)
, $(double c'qualityLevel)
, $(double c'minDistance)
, mskPtr ? _InputArray(*mskPtr) : _InputArray(cv::noArray())
, $(int32_t c'blockSize)
, $(bool c'useHarrisDetector)
, $(double c'harrisK)
);
cv::Point2f * * * cornersPtrPtr = $(Point2f * * * cornersPtrPtr);
cv::Point2f * * cornersPtr = new cv::Point2f * [corners.size()];
*cornersPtrPtr = cornersPtr;
*$(int32_t * cornersLengthsPtr) = corners.size();
for (std::vector<cv::Point2f>::size_type i = 0; i != corners.size(); i++) {
cornersPtr[i] = new cv::Point2f( corners[i] );
}
}|]
numCorners <- fromIntegral <$> peek cornersLengthsPtr
cornersPtr <- peek cornersPtrPtr
(corners :: [V2 Float]) <-
peekArray numCorners cornersPtr >>=
mapM (fmap (fmap fromCFloat . fromPoint) . fromPtr . pure)
[CU.block| void {
delete [] *$(Point2f * * * cornersPtrPtr);
}|]
pure (V.fromList corners)
where
c'qualityLevel = realToFrac qualityLevel
c'minDistance = realToFrac minDistance
c'blockSize = fromMaybe 3 blockSize
c'useHarrisDetector =
fromBool $
case detector of
HarrisDetector _kValue -> True
CornerMinEigenVal -> False
c'harrisK =
realToFrac $
case detector of
HarrisDetector kValue -> kValue
CornerMinEigenVal -> 0.04
data GoodFeaturesToTrackDetectionMethod
= HarrisDetector Double
| CornerMinEigenVal
deriving (Show, Eq)
houghCircles
:: Double
-> Double
-> Maybe Double
-> Maybe Double
-> Maybe Int32
-> Maybe Int32
-> Mat ('S [h, w]) ('S 1) ('S Word8)
-> V.Vector Circle
houghCircles dp minDist param1 param2 minRadius maxRadius src = unsafePerformIO $
withPtr src $ \srcPtr ->
alloca $ \(circleLengthsPtr :: Ptr Int32) ->
alloca $ \(circlesPtrPtr :: Ptr (Ptr (Ptr C'Vec3f))) -> mask_ $ do
_ <- [cvExcept|
std::vector<cv::Vec3f> circles;
cv::HoughCircles(
*$(Mat * srcPtr),
circles,
CV_HOUGH_GRADIENT,
$(double c'dp),
$(double c'minDist),
$(double c'param1),
$(double c'param2),
$(int32_t c'minRadius),
$(int32_t c'maxRadius)
);
cv::Vec3f * * * circlesPtrPtr = $(Vec3f * * * circlesPtrPtr);
cv::Vec3f * * circlesPtr = new cv::Vec3f * [circles.size()];
*circlesPtrPtr = circlesPtr;
*$(int32_t * circleLengthsPtr) = circles.size();
for (std::vector<cv::Vec3f>::size_type i = 0; i != circles.size(); i++) {
circlesPtr[i] = new cv::Vec3f( circles[i] );
}
|]
numCircles <- fromIntegral <$> peek circleLengthsPtr
circlesPtr <- peek circlesPtrPtr
(circles :: [V3 Float]) <-
peekArray numCircles circlesPtr >>=
mapM (fmap (fmap fromCFloat . fromVec) . fromPtr . pure)
[CU.block| void { delete [] *$(Vec3f * * * circlesPtrPtr); }|]
pure (V.fromList (map (\(V3 x y r) -> Circle (V2 x y) r) circles))
where c'dp = realToFrac dp
c'minDist = realToFrac minDist
c'param1 = realToFrac (fromMaybe 100 param1)
c'param2 = realToFrac (fromMaybe 100 param2)
c'minRadius = fromIntegral (fromMaybe 0 minRadius)
c'maxRadius = fromIntegral (fromMaybe 0 maxRadius)
data LineSegment depth
= LineSegment
{ lineSegmentStart :: !(V2 depth)
, lineSegmentStop :: !(V2 depth)
} deriving (Foldable, Functor, Traversable, Show)
type instance VecDim LineSegment = 4
instance (IsVec V4 depth) => IsVec LineSegment depth where
toVec (LineSegment (V2 x1 y1) (V2 x2 y2)) =
toVec (V4 x1 y1 x2 y2)
fromVec vec =
LineSegment
{ lineSegmentStart = V2 x1 y1
, lineSegmentStop = V2 x2 y2
}
where
V4 x1 y1 x2 y2 = fromVec vec
houghLinesP
:: (PrimMonad m)
=> Double
-> Double
-> Int32
-> Maybe Double
-> Maybe Double
-> Mut (Mat ('S [h, w]) ('S 1) ('S Word8)) (PrimState m)
-> m (V.Vector (LineSegment Int32))
houghLinesP rho theta threshold minLineLength maxLineGap src = unsafePrimToPrim $
withPtr src $ \srcPtr ->
alloca $ \(numLinesPtr :: Ptr Int32) ->
alloca $ \(linesPtrPtr :: Ptr (Ptr (Ptr C'Vec4i))) -> mask_ $ do
[C.block| void {
std::vector<cv::Vec4i> lines = std::vector<cv::Vec4i>();
cv::HoughLinesP
( *$(Mat * srcPtr)
, lines
, $(double c'rho)
, $(double c'theta)
, $(int32_t threshold)
, $(double c'minLineLength)
, $(double c'maxLineGap)
);
*$(int32_t * numLinesPtr) = lines.size();
cv::Vec4i * * * linesPtrPtr = $(Vec4i * * * linesPtrPtr);
cv::Vec4i * * linesPtr = new cv::Vec4i * [lines.size()];
*linesPtrPtr = linesPtr;
for (std::vector<cv::Vec4i>::size_type ix = 0; ix != lines.size(); ix++)
{
cv::Vec4i & org = lines[ix];
cv::Vec4i * newLine = new cv::Vec4i(org[0], org[1], org[2], org[3]);
linesPtr[ix] = newLine;
}
}|]
numLines <- fromIntegral <$> peek numLinesPtr
linesPtr <- peek linesPtrPtr
lineSegments <- mapM (fmap fromVec . fromPtr . pure) =<< peekArray numLines linesPtr
[CU.block| void {
delete [] *$(Vec4i * * * linesPtrPtr);
}|]
pure $ V.fromList lineSegments
where
c'rho = realToFrac rho
c'theta = realToFrac theta
c'minLineLength = maybe 0 realToFrac minLineLength
c'maxLineGap = maybe 0 realToFrac maxLineGap