#if __GLASGOW_HASKELL__ >= 800
#endif
#ifndef ENABLE_INTERNAL_DOCUMENTATION
#endif
module OpenCV.Internal.Core.Types.Mat
(
Mat(..)
, typeCheckMat
, relaxMat
, coerceMat
, unsafeCoerceMat
, keepMatAliveDuring
, newEmptyMat
, newMat
, withMatData
, matElemAddress
, mkMat
, cloneMat
, typeCheckMatM
, relaxMatM
, coerceMatM
, unsafeCoerceMatM
, mkMatM
, createMat
, withMatM
, cloneMatM
, deallocateMatM
, MatInfo(..)
, matInfo
, dimPositions
, Depth(..)
, marshalDepth
, unmarshalDepth
, marshalFlags
, unmarshalFlags
, ShapeT
, ChannelsT
, DepthT
, StaticDepthT
, ToShape(toShape)
, ToShapeDS(toShapeDS)
, ToChannels, toChannels
, ToChannelsDS, toChannelsDS
, ToDepth(toDepth)
, ToDepthDS(toDepthDS)
) where
import "base" Control.Exception ( throwIO )
import "base" Control.Monad.ST ( ST )
import "base" Data.Int
import "base" Data.Maybe
import "base" Data.Monoid ( (<>) )
import "base" Data.Proxy
import "base" Data.Word
import "base" Foreign.C.Types
import "base" Foreign.ForeignPtr ( ForeignPtr, withForeignPtr, touchForeignPtr )
import "base" Foreign.Marshal.Alloc ( alloca )
import "base" Foreign.Marshal.Array ( allocaArray, peekArray )
import "base" Foreign.Ptr ( Ptr, plusPtr )
import "base" Foreign.Storable ( Storable(..), peek )
import "base" GHC.TypeLits
import "base" System.IO.Unsafe ( unsafePerformIO )
import "base" Unsafe.Coerce ( unsafeCoerce )
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 "primitive" Control.Monad.Primitive ( PrimMonad, PrimState, unsafePrimToPrim )
import "this" OpenCV.Internal
import "this" OpenCV.Internal.C.Inline ( openCvCtx )
import "this" OpenCV.Internal.C.Types
import "this" OpenCV.Internal.C.PlacementNew.TH
import "this" OpenCV.Internal.Core.Types
import "this" OpenCV.Internal.Core.Types.Mat.Depth
import "this" OpenCV.Internal.Core.Types.Mat.Marshal
import "this" OpenCV.Internal.Exception
import "this" OpenCV.Internal.Mutable
import "this" OpenCV.TypeLevel
import "transformers" Control.Monad.Trans.Except
import qualified "vector" Data.Vector as V
import qualified "vector" Data.Vector.Generic as VG
C.context openCvCtx
C.include "opencv2/core.hpp"
C.using "namespace cv"
newtype Mat (shape :: DS [DS Nat])
(channels :: DS Nat)
(depth :: DS *)
= Mat {unMat :: ForeignPtr (C (Mat shape channels depth))}
type instance C (Mat shape channels depth) = C'Mat
type instance Mutable (Mat shape channels depth) = Mut (Mat shape channels depth)
instance WithPtr (Mat shape channels depth) where
withPtr = withForeignPtr . unMat
instance FromPtr (Mat shape channels depth) where
fromPtr = objFromPtr Mat $ \ptr ->
[CU.exp| void { delete $(Mat * ptr) }|]
instance FreezeThaw (Mat shape channels depth) where
freeze = cloneMatM . unMut
thaw = fmap Mut . cloneMatM
unsafeFreeze = pure . unMut
unsafeThaw = pure . Mut
typeCheckMat
:: forall shape channels depth
. ( ToShapeDS (Proxy shape)
, ToChannelsDS (Proxy channels)
, ToDepthDS (Proxy depth)
)
=> Mat shape channels depth
-> [CoerceMatError]
typeCheckMat mat =
fromMaybe [] (checkShape <$> dsToMaybe dsExpectedShape)
<> maybeToList (dsToMaybe dsExpectedNumChannels >>= checkNumChannels)
<> maybeToList (dsToMaybe dsExpectedDepth >>= checkDepth)
where
mi = matInfo mat
dsExpectedShape :: DS [DS Int32]
dsExpectedShape = toShapeDS (Proxy :: Proxy shape)
dsExpectedNumChannels :: DS Int32
dsExpectedNumChannels = toChannelsDS (Proxy :: Proxy channels)
dsExpectedDepth :: DS Depth
dsExpectedDepth = toDepthDS (Proxy :: Proxy depth)
checkShape :: [DS Int32] -> [CoerceMatError]
checkShape expectedShape = maybe checkSizes (:[]) dimCheck
where
dimCheck :: Maybe CoerceMatError
dimCheck | expectedDim == actualDim = Nothing
| otherwise = Just $ ShapeError $ ExpectationError expectedDim actualDim
where
expectedDim = length expectedShape
actualDim = length (miShape mi)
checkSizes :: [CoerceMatError]
checkSizes = catMaybes $ zipWith3 checkSize [1..] expectedShape (miShape mi)
where
checkSize :: Int -> DS Int32 -> Int32 -> Maybe CoerceMatError
checkSize dimIx dsExpected actual = dsToMaybe dsExpected >>= \expected ->
if expected == actual
then Nothing
else Just $ SizeError dimIx
$ fromIntegral
<$> ExpectationError expected actual
checkNumChannels :: Int32 -> Maybe CoerceMatError
checkNumChannels expectedNumChannels
| miChannels mi == expectedNumChannels = Nothing
| otherwise = Just $ ChannelError
$ fromIntegral
<$> ExpectationError expectedNumChannels (miChannels mi)
checkDepth :: Depth -> Maybe CoerceMatError
checkDepth expectedDepth
| miDepth mi == expectedDepth = Nothing
| otherwise = Just $ DepthError
$ ExpectationError expectedDepth (miDepth mi)
relaxMat
:: ( MayRelax shapeIn shapeOut
, MayRelax channelsIn channelsOut
, MayRelax depthIn depthOut
)
=> Mat shapeIn channelsIn depthIn
-> Mat shapeOut channelsOut depthOut
relaxMat = unsafeCoerce
coerceMat
:: ( ToShapeDS (Proxy shapeOut)
, ToChannelsDS (Proxy channelsOut)
, ToDepthDS (Proxy depthOut)
)
=> Mat shapeIn channelsIn depthIn
-> CvExcept (Mat shapeOut channelsOut depthOut)
coerceMat matIn | null errors = pure matOut
| otherwise = throwE $ CoerceMatError errors
where
matOut = unsafeCoerceMat matIn
errors = typeCheckMat matOut
unsafeCoerceMat
:: Mat shapeIn channelsIn depthIn
-> Mat shapeOut channelsOut depthOut
unsafeCoerceMat = unsafeCoerce
keepMatAliveDuring :: Mat shape channels depth -> IO a -> IO a
keepMatAliveDuring mat m = do
x <- m
touchForeignPtr $ unMat mat
pure x
newEmptyMat :: IO (Mat ('S '[]) ('S 1) ('S Word8))
newEmptyMat = unsafeCoerceMat <$> fromPtr [CU.exp|Mat * { new Mat() }|]
newMat
:: ( ToShape shape
, ToChannels channels
, ToDepth depth
, ToScalar scalar
)
=> shape
-> channels
-> depth
-> scalar
-> CvExceptT IO (Mat (ShapeT shape) (ChannelsT channels) (DepthT depth))
newMat shape channels depth defValue = ExceptT $ do
dst <- newEmptyMat
handleCvException (pure $ unsafeCoerceMat dst) $
withVector shape' $ \shapePtr ->
withPtr (toScalar defValue) $ \scalarPtr ->
withPtr dst $ \dstPtr ->
[cvExcept|
*$(Mat * dstPtr) =
Mat( $(int32_t c'ndims)
, $(int32_t * shapePtr)
, $(int32_t c'type)
, *$(Scalar * scalarPtr)
);
|]
where
c'ndims = fromIntegral $ VG.length shape'
c'type = marshalFlags depth' channels'
shape' = toShape shape
channels' = toChannels channels
depth' = toDepth depth
withVector
:: (VG.Vector v a, Storable a)
=> v a
-> (Ptr a -> IO b)
-> IO b
withVector v f =
allocaArray n $ \ptr ->
let go !ix
| ix < n = do
pokeElemOff ptr ix (VG.unsafeIndex v ix)
go (ix+1)
| otherwise = f ptr
in go 0
where
n = VG.length v
withMatData
:: Mat shape channels depth
-> ([CSize] -> Ptr Word8 -> IO a)
-> IO a
withMatData mat f = withPtr mat $ \matPtr ->
alloca $ \(dimsPtr :: Ptr Int32 ) ->
alloca $ \(stepPtr2 :: Ptr (Ptr CSize)) ->
alloca $ \(dataPtr2 :: Ptr (Ptr Word8)) -> do
[CU.block|void {
const Mat * const matPtr = $(Mat * matPtr);
*$(int32_t * const dimsPtr ) = matPtr->dims;
*$(size_t * * const stepPtr2) = matPtr->step.p;
*$(uint8_t * * const dataPtr2) = matPtr->data;
}|]
dims <- peek dimsPtr
stepPtr <- peek stepPtr2
dataPtr <- peek dataPtr2
step <- peekArray (fromIntegral dims) stepPtr
f step dataPtr
matElemAddress :: Ptr Word8 -> [Int] -> [Int] -> Ptr a
matElemAddress dataPtr step pos = dataPtr `plusPtr` offset
where
offset = sum $ zipWith (*) step pos
mkMat
:: ( ToShape shape
, ToChannels channels
, ToDepth depth
, ToScalar scalar
)
=> shape
-> channels
-> depth
-> scalar
-> CvExcept (Mat (ShapeT shape) (ChannelsT channels) (DepthT depth))
mkMat shape channels depth defValue =
unsafeCvExcept $ newMat shape channels depth defValue
cloneMat :: Mat shape channels depth
-> Mat shape channels depth
cloneMat = unsafePerformIO . cloneMatIO
cloneMatIO :: Mat shape channels depth
-> IO (Mat shape channels depth)
cloneMatIO mat =
fmap unsafeCoerceMat $ fromPtr $ withPtr mat $ \matPtr ->
[C.exp|Mat * { new Mat($(Mat * matPtr)->clone()) }|]
typeCheckMatM
:: forall shape channels depth s
. ( ToShapeDS (Proxy shape)
, ToChannelsDS (Proxy channels)
, ToDepthDS (Proxy depth)
)
=> Mut (Mat shape channels depth) s
-> [CoerceMatError]
typeCheckMatM = typeCheckMat . unMut
relaxMatM
:: ( MayRelax shapeIn shapeOut
, MayRelax channelsIn channelsOut
, MayRelax depthIn depthOut
)
=> Mut (Mat shapeIn channelsIn depthIn ) s
-> Mut (Mat shapeOut channelsOut depthOut) s
relaxMatM = unsafeCoerce
coerceMatM
:: ( ToShapeDS (Proxy shapeOut)
, ToChannelsDS (Proxy channelsOut)
, ToDepthDS (Proxy depthOut)
)
=> Mut (Mat shapeIn channelsIn depthIn) s
-> CvExcept (Mut (Mat shapeOut channelsOut depthOut) s)
coerceMatM = fmap Mut . coerceMat . unMut
unsafeCoerceMatM
:: Mut (Mat shapeIn channelsIn depthIn ) s
-> Mut (Mat shapeOut channelsOut depthOut) s
unsafeCoerceMatM = unsafeCoerce
mkMatM
:: ( PrimMonad m
, ToShape shape
, ToChannels channels
, ToDepth depth
, ToScalar scalar
)
=> shape
-> channels
-> depth
-> scalar
-> CvExceptT m (Mut (Mat (ShapeT shape) (ChannelsT channels) (DepthT depth)) (PrimState m))
mkMatM shape channels depth defValue = do
mat <- mapExceptT unsafePrimToPrim $ newMat shape channels depth defValue
unsafeThaw mat
createMat
:: (forall s. CvExceptT (ST s) (Mut (Mat shape channels depth) s))
-> CvExcept (Mat shape channels depth)
createMat mk = runCvExceptST $ unsafeFreeze =<< mk
withMatM
:: ( ToShape shape
, ToChannels channels
, ToDepth depth
, ToScalar scalar
)
=> shape
-> channels
-> depth
-> scalar
-> ( forall s
. Mut (Mat (ShapeT shape) (ChannelsT channels) (DepthT depth)) (PrimState (ST s))
-> CvExceptT (ST s) ()
)
-> CvExcept (Mat (ShapeT shape) (ChannelsT channels) (DepthT depth))
withMatM shape channels depth defValue f = createMat $ do
matM <- mkMatM shape channels depth defValue
f matM
pure matM
cloneMatM :: (PrimMonad m)
=> Mat shape channels depth
-> m (Mat shape channels depth)
cloneMatM = unsafePrimToPrim . cloneMatIO
deallocateMatM
:: (PrimMonad m)
=> Mut (Mat shape channels depth) (PrimState m)
-> m ()
deallocateMatM mutMat = unsafePrimToPrim $ do
e <- handleCvException (pure ()) $
withPtr mutMat $ \mutMatPtr ->
[cvExcept| $(Mat * mutMatPtr)->deallocate(); |]
either throwIO pure e
data MatInfo
= MatInfo
{ miShape :: ![Int32]
, miDepth :: !Depth
, miChannels :: !Int32
} deriving (Show, Eq)
matInfo :: Mat shape channels depth -> MatInfo
matInfo mat = unsafePerformIO $
withPtr mat $ \matPtr ->
alloca $ \(flagsPtr :: Ptr Int32) ->
alloca $ \(dimsPtr :: Ptr Int32) ->
alloca $ \(sizePtr :: Ptr (Ptr Int32)) -> do
[CU.block|void {
const Mat * const matPtr = $(Mat * matPtr);
*$(int32_t * const flagsPtr) = matPtr->flags;
*$(int32_t * const dimsPtr ) = matPtr->dims;
*$(int32_t * * const sizePtr ) = matPtr->size.p;
}|]
(depth, channels) <- unmarshalFlags <$> peek flagsPtr
dims <- peek dimsPtr
size <- peek sizePtr
shape <- peekArray (fromIntegral dims) size
pure MatInfo
{ miShape = shape
, miDepth = depth
, miChannels = channels
}
dimPositions :: (Num a, Enum a) => [a] -> [[a]]
dimPositions = traverse (enumFromTo 0 . pred)
type family ShapeT (a :: ka) :: DS [DS Nat] where
ShapeT [Int32] = 'D
ShapeT (V.Vector Int32) = 'D
ShapeT (x ::: xs) = 'S (DSNats (x ::: xs))
ShapeT (xs :: [Nat]) = 'S (DSNats xs)
ShapeT (Proxy a) = ShapeT a
type ChannelsT a = DSNat a
class ToShape a where
toShape :: a -> V.Vector Int32
instance ToShape (V.Vector Int32) where
toShape = id
instance ToShape [Int32] where
toShape = V.fromList
instance ToShape (Proxy '[]) where
toShape _proxy = V.empty
instance (ToInt32 (Proxy a), ToShape (Proxy as))
=> ToShape (Proxy (a ': as)) where
toShape _proxy =
V.cons
(toInt32 (Proxy :: Proxy a))
(toShape (Proxy :: Proxy as))
instance ToShape Z where
toShape Z = V.empty
instance (ToInt32 a, ToShape as) => ToShape (a ::: as) where
toShape (a ::: as) = V.cons (toInt32 a) (toShape as)
class ToShapeDS a where
toShapeDS :: a -> DS [DS Int32]
instance ToShapeDS (proxy 'D) where
toShapeDS _proxy = D
instance (ToNatListDS (Proxy as)) => ToShapeDS (Proxy ('S as)) where
toShapeDS _proxy = S $ toNatListDS (Proxy :: Proxy as)
type ToChannels a = ToInt32 a
toChannels :: (ToInt32 a) => a -> Int32
toChannels = toInt32
type ToChannelsDS a = ToNatDS a
toChannelsDS :: (ToChannelsDS a) => a -> DS Int32
toChannelsDS = toNatDS
mkPlacementNewInstance ''Mat