{-# LANGUAGE BangPatterns, FlexibleContexts, MultiParamTypeClasses , TypeFamilies #-} module Vision.Image.Class ( -- * Classes Pixel (..), MaskedImage (..), Image (..), ImageChannel, FromFunction (..) , FunctorImage (..) -- * Functions , (!), (!?), nChannels, pixel -- * Conversion , Convertible (..), convert ) where import Data.Convertible (Convertible (..), convert) import Data.Int import Data.Vector.Storable (Vector, generate, unfoldr) import Data.Word import Foreign.Storable (Storable) import Prelude hiding (map, read) import Vision.Primitive ( Z (..), (:.) (..), Point, Size , fromLinearIndex, toLinearIndex, shapeLength ) -- Classes --------------------------------------------------------------------- -- | Determines the number of channels and the type of each pixel of the image -- and how images are represented. class Pixel p where type PixelChannel p -- | Returns the number of channels of the pixel. -- -- Must not consume 'p' (could be 'undefined'). pixNChannels :: p -> Int pixIndex :: p -> Int -> PixelChannel p instance Pixel Int16 where type PixelChannel Int16 = Int16 pixNChannels _ = 1 pixIndex p _ = p instance Pixel Int32 where type PixelChannel Int32 = Int32 pixNChannels _ = 1 pixIndex p _ = p instance Pixel Int where type PixelChannel Int = Int pixNChannels _ = 1 pixIndex p _ = p instance Pixel Word8 where type PixelChannel Word8 = Word8 pixNChannels _ = 1 pixIndex p _ = p instance Pixel Word16 where type PixelChannel Word16 = Word16 pixNChannels _ = 1 pixIndex p _ = p instance Pixel Word32 where type PixelChannel Word32 = Word32 pixNChannels _ = 1 pixIndex p _ = p instance Pixel Word where type PixelChannel Word = Word pixNChannels _ = 1 pixIndex p _ = p instance Pixel Float where type PixelChannel Float = Float pixNChannels _ = 1 pixIndex p _ = p instance Pixel Double where type PixelChannel Double = Double pixNChannels _ = 1 pixIndex p _ = p instance Pixel Bool where type PixelChannel Bool = Bool pixNChannels _ = 1 pixIndex p _ = p -- | Provides an abstraction for images which are not defined for each of their -- pixels. The interface is similar to 'Image' except that indexing functions -- don't always return. -- -- Image origin (@'ix2' 0 0@) is located in the upper left corner. class Storable (ImagePixel i) => MaskedImage i where type ImagePixel i shape :: i -> Size -- | Returns the pixel\'s value at 'Z :. y, :. x'. maskedIndex :: i -> Point -> Maybe (ImagePixel i) maskedIndex img = (img `maskedLinearIndex`) . toLinearIndex (shape img) {-# INLINE maskedIndex #-} -- | Returns the pixel\'s value as if the image was a single dimension -- vector (row-major representation). maskedLinearIndex :: i -> Int -> Maybe (ImagePixel i) maskedLinearIndex img = (img `maskedIndex`) . fromLinearIndex (shape img) {-# INLINE maskedLinearIndex #-} -- | Returns the non-masked values of the image. values :: i -> Vector (ImagePixel i) values !img = unfoldr step 0 where !n = shapeLength (shape img) step !i | i >= n = Nothing | Just p <- img `maskedLinearIndex` i = Just (p, i + 1) | otherwise = step (i + 1) {-# INLINE values #-} {-# MINIMAL shape, (maskedIndex | maskedLinearIndex) #-} type ImageChannel i = PixelChannel (ImagePixel i) -- | Provides an abstraction over the internal representation of an image. -- -- Image origin is located in the lower left corner. class MaskedImage i => Image i where -- | Returns the pixel value at 'Z :. y :. x'. index :: i -> Point -> ImagePixel i index img = (img `linearIndex`) . toLinearIndex (shape img) {-# INLINE index #-} -- | Returns the pixel value as if the image was a single dimension vector -- (row-major representation). linearIndex :: i -> Int -> ImagePixel i linearIndex img = (img `index`) . fromLinearIndex (shape img) {-# INLINE linearIndex #-} -- | Returns every pixel values as if the image was a single dimension -- vector (row-major representation). vector :: i -> Vector (ImagePixel i) vector img = generate (shapeLength $ shape img) (img `linearIndex`) {-# INLINE vector #-} {-# MINIMAL index | linearIndex #-} -- | Provides ways to construct an image from a function. class FromFunction i where type FromFunctionPixel i -- | Generates an image by calling the given function for each pixel of the -- constructed image. fromFunction :: Size -> (Point -> FromFunctionPixel i) -> i -- | Generates an image by calling the last function for each pixel of the -- constructed image. -- -- The first function is called for each line, generating a line invariant -- value. -- -- This function is faster for some image representations as some recurring -- computation can be cached. fromFunctionLine :: Size -> (Int -> a) -> (a -> Point -> FromFunctionPixel i) -> i fromFunctionLine size line f = fromFunction size (\pt@(Z :. y :. _) -> f (line y) pt) {-# INLINE fromFunctionLine #-} -- | Generates an image by calling the last function for each pixel of the -- constructed image. -- -- The first function is called for each column, generating a column -- invariant value. -- -- This function *can* be faster for some image representations as some -- recurring computations can be cached. However, it may requires a vector -- allocation for these values. If the column invariant is cheap to -- compute, prefer 'fromFunction'. fromFunctionCol :: Storable b => Size -> (Int -> b) -> (b -> Point -> FromFunctionPixel i) -> i fromFunctionCol size col f = fromFunction size (\pt@(Z :. _ :. x) -> f (col x) pt) {-# INLINE fromFunctionCol #-} -- | Generates an image by calling the last function for each pixel of the -- constructed image. -- -- The two first functions are called for each line and for each column, -- respectively, generating common line and column invariant values. -- -- This function is faster for some image representations as some recurring -- computation can be cached. However, it may requires a vector -- allocation for column values. If the column invariant is cheap to -- compute, prefer 'fromFunctionLine'. fromFunctionCached :: Storable b => Size -> (Int -> a) -- ^ Line function -> (Int -> b) -- ^ Column function -> (a -> b -> Point -> FromFunctionPixel i) -- ^ Pixel function -> i fromFunctionCached size line col f = fromFunction size (\pt@(Z :. y :. x) -> f (line y) (col x) pt) {-# INLINE fromFunctionCached #-} {-# MINIMAL fromFunction #-} -- | Defines a class for images on which a function can be applied. The class is -- different from 'Functor' as there could be some constraints and -- transformations the pixel and image types. class (MaskedImage src, MaskedImage res) => FunctorImage src res where map :: (ImagePixel src -> ImagePixel res) -> src -> res -- Functions ------------------------------------------------------------------- -- | Alias of 'maskedIndex'. (!?) :: MaskedImage i => i -> Point -> Maybe (ImagePixel i) (!?) = maskedIndex {-# INLINE (!?) #-} -- | Alias of 'index'. (!) :: Image i => i -> Point -> ImagePixel i (!) = index {-# INLINE (!) #-} -- | Returns the number of channels of an image. nChannels :: (Pixel (ImagePixel i), MaskedImage i) => i -> Int nChannels img = pixNChannels (pixel img) {-# INLINE nChannels #-} -- | Returns an 'undefined' instance of a pixel of the image. This is sometime -- useful to satisfy the type checker as in a call to 'pixNChannels' : -- -- > nChannels img = pixNChannels (pixel img) pixel :: MaskedImage i => i -> ImagePixel i pixel _ = undefined