friday-0.1: src/Vision/Image/Type.hs
{-# LANGUAGE BangPatterns, FlexibleContexts, FlexibleInstances
, MultiParamTypeClasses, PatternGuards, TypeFamilies
, UndecidableInstances #-}
{-# OPTIONS_GHC -fno-warn-orphans #-}
module Vision.Image.Type (
-- * Classes
Pixel (..), MaskedImage (..), Image (..), ImageChannel, FromFunction (..)
, FunctorImage (..)
-- * Manifest images
, Manifest (..)
-- * Delayed images
, Delayed (..)
-- * Delayed masked images
, DelayedMask (..)
-- * Functions
, nChannels, pixel
-- * Conversion
, Convertible (..), convert, delay, compute
-- * Types helpers
, delayed, manifest
) where
import Control.Applicative ((<$>))
import Data.Convertible (Convertible (..), convert)
import Data.Int
import Data.Vector.Storable (
Vector, (!), create, enumFromN, forM_, generate, unfoldr
)
import Data.Vector.Storable.Mutable (new, write)
import Data.Word
import Foreign.Storable (Storable)
import Prelude hiding (map, read)
import Vision.Primitive (
Z (..), (:.) (..), Point, Size
, ix2, fromLinearIndex, toLinearIndex, shapeLength
)
-- Classes ---------------------------------------------------------------------
-- | Determines the number of channels and the type of each pixel of the image
-- and how images are represented.
class Storable p => 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 is located in the lower left corner.
class Pixel (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
-- Manifest images -------------------------------------------------------------
-- | Stores the image content in a 'Vector'.
data Storable p => Manifest p = Manifest {
manifestSize :: !Size
, manifestVector :: !(Vector p)
} deriving (Eq, Ord, Show)
instance Pixel p => MaskedImage (Manifest p) where
type ImagePixel (Manifest p) = p
shape = manifestSize
{-# INLINE shape #-}
Manifest _ vec `maskedLinearIndex` ix = Just $! vec ! ix
{-# INLINE maskedLinearIndex #-}
values = manifestVector
{-# INLINE values #-}
instance Pixel p => Image (Manifest p) where
Manifest _ vec `linearIndex` ix = vec ! ix
{-# INLINE linearIndex #-}
vector = manifestVector
{-# INLINE vector #-}
instance Storable p => FromFunction (Manifest p) where
type FromFunctionPixel (Manifest p) = p
fromFunction !size@(Z :. h :. w) f =
Manifest size $ create $ do
arr <- new (h * w)
forM_ (enumFromN 0 h) $ \y -> do
let !lineOffset = y * w
forM_ (enumFromN 0 w) $ \x -> do
let !offset = lineOffset + x
!val = f (ix2 y x)
write arr offset val
return arr
{-# INLINE fromFunction #-}
fromFunctionLine !size@(Z :. h :. w) line f =
Manifest size $ create $ do
-- Note: create is faster than unfoldrN.
arr <- new (h * w)
forM_ (enumFromN 0 h) $ \y -> do
let !lineVal = line y
!lineOffset = y * w
forM_ (enumFromN 0 w) $ \x -> do
let !offset = lineOffset + x
!val = f lineVal (ix2 y x)
write arr offset val
return arr
{-# INLINE fromFunctionLine #-}
fromFunctionCol !size@(Z :. h :. w) col f =
Manifest size $ create $ do
-- Note: create is faster than unfoldrN.
arr <- new (h * w)
forM_ (enumFromN 0 h) $ \y -> do
let !lineOffset = y * w
forM_ (enumFromN 0 w) $ \x -> do
let !offset = lineOffset + x
!val = f (cols ! x) (ix2 y x)
write arr offset val
return arr
where
!cols = generate w col
{-# INLINE fromFunctionCol #-}
fromFunctionCached !size@(Z :. h :. w) line col f =
Manifest size $ create $ do
-- Note: create is faster than unfoldrN.
arr <- new (h * w)
forM_ (enumFromN 0 h) $ \y -> do
let !lineVal = line y
!lineOffset = y * w
forM_ (enumFromN 0 w) $ \x -> do
let !offset = lineOffset + x
!val = f lineVal (cols ! x) (ix2 y x)
write arr offset val
return arr
where
!cols = generate w col
{-# INLINE fromFunctionCached #-}
instance (Image src, Pixel p) => FunctorImage src (Manifest p) where
map f img = fromFunction (shape img) (f . (img `index`))
{-# INLINE map #-}
-- Delayed images --------------------------------------------------------------
-- | A delayed image is an image which is constructed using a function.
--
-- Usually, a delayed image maps each of its pixels over another image.
-- Delayed images are useful by avoiding intermediate images in a
-- transformation pipeline of images or by avoiding the computation of the whole
-- resulting image when only a portion of its pixels will be accessed.
data Delayed p = Delayed {
delayedSize :: !Size
, delayedFun :: !(Point -> p)
}
instance Pixel p => MaskedImage (Delayed p) where
type ImagePixel (Delayed p) = p
shape = delayedSize
{-# INLINE shape #-}
maskedIndex img = Just . delayedFun img
{-# INLINE maskedIndex #-}
instance Pixel p => Image (Delayed p) where
index = delayedFun
{-# INLINE index #-}
instance FromFunction (Delayed p) where
type FromFunctionPixel (Delayed p) = p
fromFunction = Delayed
{-# INLINE fromFunction #-}
instance (Image src, Pixel p) => FunctorImage src (Delayed p) where
map f img = fromFunction (shape img) (f . (img `index`))
{-# INLINE map #-}
-- Masked delayed images -------------------------------------------------------
data DelayedMask p = DelayedMask {
delayedMaskSize :: !Size
, delayedMaskFun :: !(Point -> Maybe p)
}
instance Pixel p => MaskedImage (DelayedMask p) where
type ImagePixel (DelayedMask p) = p
shape = delayedMaskSize
{-# INLINE shape #-}
maskedIndex = delayedMaskFun
{-# INLINE maskedIndex #-}
instance Pixel p => FromFunction (DelayedMask p) where
type FromFunctionPixel (DelayedMask p) = Maybe p
fromFunction = DelayedMask
{-# INLINE fromFunction #-}
instance (MaskedImage src, Pixel p) => FunctorImage src (DelayedMask p) where
map f img = fromFunction (shape img) (\pt -> f <$> (img `maskedIndex` pt))
{-# INLINE map #-}
-- Functions -------------------------------------------------------------------
-- | Returns the number of channels of an image.
nChannels :: 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
-- Conversion ------------------------------------------------------------------
-- | Delays an image in its delayed representation.
delay :: Image i => i -> Delayed (ImagePixel i)
delay = map id
{-# INLINE delay #-}
-- | Computes the value of an image into a manifest representation.
compute :: (Image i, Storable (ImagePixel i)) => i -> Manifest (ImagePixel i)
compute = map id
{-# INLINE compute #-}
instance (Pixel p1, Pixel p2, Storable p1, Storable p2, Convertible p1 p2)
=> Convertible (Manifest p1) (Manifest p2) where
safeConvert = Right . map convert
{-# INLINE safeConvert #-}
instance (Pixel p1, Pixel p2, Convertible p1 p2)
=> Convertible (Delayed p1) (Delayed p2) where
safeConvert = Right . map convert
{-# INLINE safeConvert #-}
instance (Pixel p1, Pixel p2, Storable p2, Convertible p1 p2)
=> Convertible (Delayed p1) (Manifest p2) where
safeConvert = Right . map convert
{-# INLINE safeConvert #-}
instance (Pixel p1, Pixel p2, Storable p1, Convertible p1 p2)
=> Convertible (Manifest p1) (Delayed p2) where
safeConvert = Right . map convert
{-# INLINE safeConvert #-}
-- Types helpers ---------------------------------------------------------------------
-- | Forces an image to be in its delayed represenation. Does nothing.
delayed :: Delayed p -> Delayed p
delayed = id
-- | Forces an image to be in its delayed represenation. Does nothing.
manifest :: Manifest p -> Manifest p
manifest = id