packages feed

cv-combinators 0.1.2.1 → 0.1.2.2

raw patch · 6 files changed

+155/−371 lines, 6 filesdep +SDLdep +allocated-processordep +graphics-drawingcombinatorsdep ~HOpenCVdep ~basenew-component:exe:test-cv-combinators2PVP: major bump suggested

API removals or changes: PVP suggests a major version bump

Dependencies added: SDL, allocated-processor, graphics-drawingcombinators, vector-space

Dependency ranges changed: HOpenCV, base

API changes (from Hackage documentation)

- AI.CV.Processor: (--<) :: (Functor (cat a), Category cat) => cat a a1 -> cat (a1, a1) c -> cat a c
- AI.CV.Processor: Processor :: (a -> x -> m x) -> (a -> m x) -> (x -> m b) -> (x -> m ()) -> Processor m a b
- AI.CV.Processor: chain :: Processor m a b' -> Processor m b' b -> Processor m a b
- AI.CV.Processor: data Processor m a b
- AI.CV.Processor: differentiate :: (Real b) => Clock IO -> IOSource a b -> IOSource a Double
- AI.CV.Processor: empty :: (Monad m) => Processor m a a
- AI.CV.Processor: forkJoin :: Processor m a b -> Processor m a b' -> Processor m a (b, b')
- AI.CV.Processor: instance (Monad m) => Applicative (Processor m a)
- AI.CV.Processor: instance (Monad m) => Arrow (Processor m)
- AI.CV.Processor: instance (Monad m) => Category (Processor m)
- AI.CV.Processor: instance (Monad m) => Functor (Processor m a)
- AI.CV.Processor: integrate :: (Real b) => Clock IO -> IOSource a b -> IOSource a Double
- AI.CV.Processor: max_ :: (Ord b) => Clock IO -> b -> IOSource a b -> IOSource a b
- AI.CV.Processor: min_ :: (Ord b) => Clock IO -> b -> IOSource a b -> IOSource a b
- AI.CV.Processor: parallel :: Processor m a b -> Processor m c d -> Processor m (a, c) (b, d)
- AI.CV.Processor: processor :: (Monad m) => (a -> x -> m x) -> (a -> m x) -> (x -> m b) -> (x -> m ()) -> Processor m a b
- AI.CV.Processor: run :: (Monad m) => Processor m a b -> a -> m b
- AI.CV.Processor: runUntil :: (Monad m) => Processor m a b -> a -> (b -> m Bool) -> m b
- AI.CV.Processor: runWith :: (Monad m) => (m b -> m b') -> Processor m a b -> a -> m b'
- AI.CV.Processor: scanlT :: Clock IO -> (b -> b -> DTime -> c -> c) -> c -> IOSource a b -> IOSource a c
- AI.CV.Processor: split :: (Functor f) => f a -> f (a, a)
- AI.CV.Processor: type Clock m = m Double
- AI.CV.Processor: type DTime = Double
- AI.CV.Processor: type IOProcessor a b = Processor IO a b
- AI.CV.Processor: type IOSink a = IOProcessor a ()
- AI.CV.Processor: type IOSource a b = Processor IO a b

Files

cv-combinators.cabal view
@@ -1,5 +1,5 @@ name: cv-combinators-version: 0.1.2.1+version: 0.1.2.2 license: BSD3 maintainer: Noam Lewis <jones.noamle@gmail.com> bug-reports: mailto:jones.noamle@gmail.com@@ -8,7 +8,7 @@ description:    Initial version; using the "HOpenCV" package as a backend.    .-   Provides a functional combinator library, naturally expressed as Arrow instances (but also Category, Functor and Applicative). +   Provides a functional combinator library for computer vision, based on the "allocated-processor" package.    .    Online documentation, if not built below, can be found at <http://www.ee.bgu.ac.il/~noamle/>.    .@@ -21,20 +21,26 @@ Tested-With:   GHC == 6.10.4  library-   exposed-modules: AI.CV.Processor,-                    AI.CV.ImageProcessors+   exposed-modules: AI.CV.ImageProcessors    hs-Source-Dirs: src-   build-depends: base >= 3 && < 5, HOpenCV >= 0.1.2+   build-depends: base >=4 && <5, HOpenCV >= 0.1.2.1, allocated-processor, vector-space    ghc-options: -Wall  executable test-cv-combinators   hs-source-dirs:  src-  Build-Depends: base >= 4+  Build-Depends: base >=4 && <5, HOpenCV >= 0.1.2.1, allocated-processor, vector-space   main-is: Test.hs   Ghc-Options: -Wall    Ghc-Prof-Options:  -prof -auto-all -  other-modules: AI.CV.Processor, AI.CV.ImageProcessors+  other-modules: AI.CV.ImageProcessors +executable test-cv-combinators2+  hs-source-dirs:  src+  Build-Depends: base >=4 && <5, HOpenCV >= 0.1.2.1, allocated-processor, SDL, graphics-drawingcombinators, vector-space+  main-is: IntegratedTest.hs+  Ghc-Options: -Wall +  Ghc-Prof-Options:  -prof -auto-all +  other-modules: AI.CV.ImageProcessors, Graphics.GraphicsProcessors  -- source-repository head --  type: git
src/AI/CV/ImageProcessors.hs view
@@ -46,7 +46,7 @@      runTill, runTillKeyPressed, keyPressed) where  -import AI.CV.Processor+import Control.Processor(runUntil, IOSink, IOSource, IOProcessor, processor)  import AI.CV.OpenCV.Types(PImage) import qualified AI.CV.OpenCV.CV as CV@@ -55,7 +55,7 @@ import AI.CV.OpenCV.CxCore(IplImage, CvSize, CvRect, CvMemStorage) import AI.CV.OpenCV.CV(CvHaarClassifierCascade) -import Foreign.Ptr+import Foreign.Ptr(Ptr)   type Image = PImage@@ -71,8 +71,7 @@  -- | Predicate for pressed keys keyPressed :: Show a => a -> IO Bool-keyPressed _ = do-  fmap (/= -1) $ HighGui.waitKey 3+keyPressed _ = fmap (/= -1) $ HighGui.waitKey 3 -- todo wrap waitKey more generally for the API  -- | Runs the processor until a predicate is true, for predicates, and processors that take () as input -- (such as chains that start with a camera).@@ -81,7 +80,7 @@  -- | Name (and type) says it all. runTillKeyPressed :: (Show a) => IOProcessor () a -> IO ()-runTillKeyPressed f = (f `runTill` keyPressed) >> (return ())+runTillKeyPressed f = f `runTill` keyPressed >> (return ())  ------------------------------------------------------------------ capture :: IO (Ptr HighGui.CvCapture) -> ImageSource@@ -89,7 +88,7 @@     where processQueryFrame :: () -> (Ptr CxCore.IplImage, Ptr HighGui.CvCapture)                                 -> IO (Ptr CxCore.IplImage, Ptr HighGui.CvCapture)           processQueryFrame _ (_, cap) = do-            newFrame <- HighGui.cvQueryFrame $ cap+            newFrame <- HighGui.cvQueryFrame cap             return (newFrame, cap)                      allocateCamera :: () -> IO (Ptr CxCore.IplImage, Ptr HighGui.CvCapture)@@ -98,11 +97,9 @@             newFrame <- HighGui.cvQueryFrame cap             return (newFrame, cap)           -          fromState (image, _) = do -            return image+          fromState (image, _) = return image           -          releaseNext (_, cap) = do-            HighGui.cvReleaseCapture $ cap+          releaseNext (_, cap) = HighGui.cvReleaseCapture cap   -- | A capture device, using OpenCV's HighGui lib's cvCreateCameraCapture@@ -120,9 +117,9 @@ -- | A window that displays images. -- Note: windows with the same index will be the same window....is this ok? window :: Int -> ImageSink-window num = processor procFunc allocFunc (do return) (do return)+window num = processor procFunc allocFunc return return     where procFunc :: (Image -> () -> IO ())-          procFunc src x = (HighGui.showImage (fromIntegral num) src) >> (return x)+          procFunc src x = HighGui.showImage (fromIntegral num) src >> return x                      allocFunc :: (Image -> IO ())           allocFunc _ = HighGui.newWindow (fromIntegral num) True@@ -131,7 +128,7 @@ -- | A convenience function for constructing a common type of processors that work exclusively on images imageProcessor :: (Image -> Image -> IO Image) -> (Image -> IO Image)                 -> ImageProcessor-imageProcessor procFunc allocFunc = processor procFunc allocFunc (do return) (CxCore.cvReleaseImage)+imageProcessor procFunc allocFunc = processor procFunc allocFunc return CxCore.cvReleaseImage  -- | OpenCV's cvResize resize :: Int -- Width@@ -172,7 +169,7 @@             gray <- CxCore.cvCreateImage (CxCore.cvGetSize src) 1 CxCore.iplDepth8u             return (gray, target)             -          convertState = do return . snd+          convertState = return . snd                                        releaseState (gray, target) = do             CxCore.cvReleaseImage gray@@ -202,7 +199,7 @@             print name -- todo verify that this is a haar cascade             return ([], (cascade, storage))           -          convFunc = do return . fst+          convFunc = return . fst                      freeFunc (_, (_, storage)) = do             CxCore.cvReleaseMemStorage storage@@ -216,7 +213,7 @@              -- | OpenCV's cvRectangle, currently without width, color or line type control drawRects :: IOProcessor (Image, [CvRect]) Image-drawRects = processor procFunc (CxCore.cvCloneImage . fst) (do return) CxCore.cvReleaseImage+drawRects = processor procFunc (CxCore.cvCloneImage . fst) return CxCore.cvReleaseImage     where procFunc (src,rects) dst = do             CxCore.cvCopy src dst             mapM_ (CxCore.cvRectangle dst) rects
− src/AI/CV/Processor.hs
@@ -1,344 +0,0 @@-{-# LANGUAGE RankNTypes, GADTs, NoMonomorphismRestriction #-}--- | --- Module      : AI.CV.Processor--- Copyright   : (c) Noam Lewis, 2010--- License     : BSD3------ Maintainer  : Noam Lewis <jones.noamle@gmail.com>--- Stability   : experimental--- Portability : tested on GHC only------ Framework for expressing IO actions that require initialization and finalizers.--- This module provides a *functional* interface for defining and chaining a series of processors.------ Motivating example: bindings to C libraries that use functions such as: f(foo *src, foo *dst),--- where the pointer `dst` must be pre-allocated. In this case we normally do:------   > foo *dst = allocateFoo();---   > ... ---   > while (something) {---   >    f(src, dst);---   >    ...---   > }---   > releaseFoo(dst);------ You can use the 'runUntil' function below to emulate that loop.------ Processor is an instance of Category, Functor, Applicative and Arrow. ------ In addition to the general type @'Processor' m a b@, this module also defines the semantic model--- for @'Processor' IO a b@, which has synonym @'IOProcessor' a b@.--module AI.CV.Processor where--import Prelude hiding ((.),id)--import Control.Category-import Control.Applicative hiding (empty)-import Control.Arrow--import Control.Monad(liftM, join)---- | The type of Processors------    * a, b = the input and output types of the processor (think a -> b)------    * x = type of internal state (existentially quantified)------ The arguments to the constructor are:------    1. Processing function: Takes input and internal state, and returns new internal state.------    2. Allocator for internal state (this is run only once): Takes (usually the first) input, and returns initial internal state.------    3. Convertor from state x to output b: Takes internal state and returns the output.------    4. Releaser for internal state (finalizer, run once): Run after processor is done being used, to release the internal state.----data Processor m a b where-    Processor :: Monad m => (a -> x -> m x) -> (a -> m x) -> (x -> m b) -> (x -> m ()) -> (Processor m a b)-    --- | The semantic model for 'IOProcessor' is a function:------ > [[ 'IOProcessor' a b ]] = a -> b------ And the following laws:------    1. The processing function (@a -> x -> m x@) must act as if purely, so that indeed for a given input the---       output is always the same. One particular thing to be careful with is that the output does not depend---       on time (for example, you shouldn't use IOProcessor to implement an input device). The @IOSource@ type---       is defined exactly for time-dependent processors. For pointer typed inputs and outputs, see next law.------    2. For processors that work on pointers, @[[ Ptr t ]] = t@. This is guaranteed by the following---       implementation constraints for @IOProcessor a b@:------       1. If `a` is a pointer type (@a = Ptr p@), then the processor must NOT write (modify) the referenced data.------       2. If `b` is a pointer, the memory it points to (and its allocation status) is only allowed to change---          by the processor that created it (in the processing and releasing functions). In a way this---          generalizes the first constraint.------ Note, that unlike "Yampa", this model does not allow transformations of the type @(Time -> a) -> (Time ->--- b)@. The reason is that I want to prevent arbitrary time access (whether causal or not). This limitation--- means that everything is essentially "point-wise" in time. To allow memory-full operations under this--- model, 'scanlT' is defined. See <http://www.ee.bgu.ac.il/~noamle/_downloads/gaccum.pdf> for more about--- arbitrary time access.-type IOProcessor a b = Processor IO a b---- | @'IOSource' a b@ is the type of time-dependent processors, such that:------ > [[ 'IOSource' a b ]] = (a, Time) -> b------ Thus, it is ok to implement a processing action that outputs arbitrary time-dependent values during runtime--- regardless of input. (Although the more useful case is to calculate something from the input @a@ that is--- also time-dependent. The @a@ input is often not required and in those cases @a = ()@ is used.------ Notice that this means that IOSource doesn't qualify as an 'IOProcessor'. However, currently the--- implementation /does NOT/ enforce this, i.e. IOSource is not a newtype; I don't know how to implement it--- correctly. Also, one question is whether primitives like "chain" will have to disallow placing 'IOSource'--- as the second element in a chain. Maybe they should, maybe they shouldn't.-type IOSource a b = Processor IO a b---- | TODO: What's the semantic model for @'IOSink' a@?-type IOSink a = IOProcessor a ()---- | TODO: do we need this? we're exporting the data constructor anyway for now, so maybe we don't.-processor :: Monad m =>-             (a -> x -> m x) -> (a -> m x) -> (x -> m b) -> (x -> m ())-          -> Processor m a b-processor = Processor---- | Chains two processors serially, so one feeds the next.-chain :: Processor m a b'  -> Processor m b' b -> Processor m a b-chain (Processor pf1 af1 cf1 rf1) (Processor pf2 af2 cf2 rf2) = processor pf3 af3 cf3 rf3-    where pf3 a (x1,x2) = do-            x1' <- pf1 a x1-            b'  <- cf1 x1-            x2' <- pf2 b' x2-            return (x1', x2')-            -          af3 a = do-            x1 <- af1 a-            b' <- cf1 x1-            x2 <- af2 b'-            return (x1,x2)-            -          cf3 (_,x2) = do-            b <- cf2 x2-            return b-            -          rf3 (x1,x2) = do-            rf2 x2-            rf1 x1-  --- | A processor that represents two sub-processors in parallel (although the current implementation runs them--- sequentially, but that may change in the future)-parallel :: Processor m a b -> Processor m c d -> Processor m (a,c) (b,d)-parallel (Processor pf1 af1 cf1 rf1) (Processor pf2 af2 cf2 rf2) = processor pf3 af3 cf3 rf3-    where pf3 (a,c) (x1,x2) = do-            x1' <- pf1 a x1-            x2' <- pf2 c x2-            return (x1', x2')-            -          af3 (a,c) = do-            x1 <- af1 a-            x2 <- af2 c-            return (x1,x2)-            -          cf3 (x1,x2) = do-            b  <- cf1 x1-            d <- cf2 x2-            return (b,d)-            -          rf3 (x1,x2) = do-            rf2 x2-            rf1 x1---- | Constructs a processor that: given two processors, gives source as input to both processors and runs them--- independently, and after both have have finished, outputs their combined outputs.--- --- Semantic meaning, using Arrow's (&&&) operator:--- [[ forkJoin ]] = &&& --- Or, considering the Applicative instance of functions (which are the semantic meanings of a processor):--- [[ forkJoin ]] = liftA2 (,)--- Alternative implementation to consider: f &&& g = (,) <&> f <*> g-forkJoin :: Processor m a b  -> Processor m a b' -> Processor m a (b,b')-forkJoin (Processor pf1 af1 cf1 rf1) (Processor pf2 af2 cf2 rf2) = processor pf3 af3 cf3 rf3-    where --pf3 :: a -> (x1,x2) -> m (x1,x2)-          pf3 a (x1,x2) = do-            x1' <- pf1 a x1-            x2' <- pf2 a x2-            return (x1', x2')-            -          --af3 :: a -> m (x1, x2)-          af3 a = do-            x1 <- af1 a-            x2 <- af2 a-            return (x1,x2)-          -          --cf3 :: (x1,x2) -> m (b,b')-          cf3 (x1,x2) = do-            b <- cf1 x1-            b' <- cf2 x2-            return (b,b')-          -          --rf3 :: (x1,x2) -> m ()-          rf3 (x1,x2) = rf2 x2 >> rf1 x1------------------------------------------------------------------- | The identity processor: output = input. Semantically, [[ empty ]] = id-empty :: Monad m => Processor m a a-empty = processor pf af cf rf-    where pf _ = do return-          af   = do return-          cf   = do return-          rf _ = do return ()-               -instance Monad m => Category (Processor m) where-  (.) = flip chain-  id  = empty-  -instance Monad m => Functor (Processor m a) where-  -- |-  -- > [[ fmap ]] = (.)-  ---  -- This could have used fmap internally as a Type Class Morphism, but monads-  -- don't neccesary implement the obvious: fmap = liftM.-  fmap f (Processor pf af cf rf) = processor pf af cf' rf-    where cf' x = liftM f (cf x) --instance Monad m => Applicative (Processor m a) where-  -- | -  -- > [[ pure ]] = const-  pure b = processor pf af cf rf-    where pf _ = do return-          af _ = do return ()-          cf _ = do return b-          rf _ = do return ()-            -  -- |-  -- [[ pf <*> px ]] = \a -> ([[ pf ]] a) ([[ px ]] a)-  -- (same as '(<*>)' on functions)-  (<*>) (Processor pf af cf rf) (Processor px ax cx rx) = processor py ay cy ry-    where py a (stateF, stateX) = do-            f' <- pf a stateF-            x' <- px a stateX-            return (f', x')-            -          ay a = do-            stateF <- af a-            stateX <- ax a-            return (stateF, stateX)-            -          -- this is the only part that seems specific to <*>-          cy (stateF, stateX) = do-            b2c <- cf stateF-            b <- cx stateX-            return (b2c b)-            -          ry (stateF, stateX) = do-            rx stateX-            rf stateF-  --- | A few tricks by Saizan from #haskell to perhaps use here:---  first f = (,) <$> (arr fst >>> f) <*> arr snd---  arr f = f <$> id---  f *** g = (arr fst >>> f) &&& (arr snd >>> g)-instance Monad m => Arrow (Processor m) where-  arr = flip liftA id-  (&&&) = forkJoin-  (***) = parallel-  first = (*** id)-  second = (id ***)-  ------------------------------------------------------------------- | Splits (duplicates) the output of a functor, or on this case a processor.-split :: Functor f => f a -> f (a,a)-split = (join (,) <$>)---- | 'f --< g' means: split f and feed it into g. Useful for feeding parallelized (***'d) processors.--- For example, a --< (b *** c) = a >>> (b &&& c)-(--<) :: (Functor (cat a), Category cat) => cat a a1 -> cat (a1, a1) c -> cat a c-f --< g = split f >>> g-infixr 1 --<-----------------------------------------------------------------            --- | Runs the processor once: allocates, processes, converts to output, and deallocates.-run :: Monad m => Processor m a b -> a -> m b-run = runWith id---- | Keeps running the processing function in a loop until a predicate on the output is true.--- Useful for processors whose main function is after the allocation and before deallocation.-runUntil :: Monad m => Processor m a b -> a -> (b -> m Bool) -> m b-runUntil (Processor pf af cf rf) a untilF = do-  x <- af a-  let repeatF y = do-        y' <- pf a y-        b <- cf y'-        b' <- untilF b-        if b' then return b else repeatF y'-  d <- repeatF x-  rf x-  return d----- | Runs the processor once, but passes the processing + conversion action to the given function.-runWith :: Monad m => (m b -> m b') -> Processor m a b -> a -> m b'-runWith f (Processor pf af cf rf) a = do-        x <- af a-        b' <- f (pf a x >>= cf)-        rf x-        return b'-----------------------------------------------------------------type DTime = Double--type Clock m = m Double---- | scanlT provides the primitive for performing memory-full operations on time-dependent processors, as described in <http://www.ee.bgu.ac.il/~noamle/_downloads/gaccum.pdf>.------ /Untested/.-scanlT :: Clock IO -> (b -> b -> DTime -> c -> c) -> c -> IOSource a b -> IOSource a c-scanlT clock transFunc initOut (Processor pf af cf rf) = processor procFunc allocFunc convFunc releaseFunc-    where procFunc curIn' (prevIn, prevTime, prevOut, x) = do-            x' <- pf curIn' x-            curIn <- cf x'-            curTime <- clock-            let dtime = curTime - prevTime-                curOut = transFunc prevIn curIn dtime prevOut-            return (curIn, curTime, curOut, x')-          -          allocFunc firstIn' = do-            x <- af firstIn'-            firstIn <- cf x-            curTime <- clock-            return (firstIn, curTime, initOut, x)-          -          convFunc (_, _, curOut, _) = return curOut-          -          releaseFunc (_, _, _, x') = rf x'-          -          --- | Differentiate using scanlT. TODO: test, and also generalize for any monad (trivial change of types).-differentiate :: (Real b) => Clock IO -> IOSource a b -> IOSource a Double-differentiate clock = scanlT clock diffFunc 0-    where diffFunc y' y dt _ = (realToFrac (y' - y)) / dt -- horrible approximation!-          -integrate :: (Real b) => Clock IO -> IOSource a b -> IOSource a Double-integrate clock p = scanlT clock intFunc 0 p-    where intFunc y' y dt prevSum = prevSum + (realToFrac (y' + y)) * dt / 2 -- horrible approximation!--max_ :: Ord b => Clock IO -> b -> IOSource a b -> IOSource a b-max_ clock minVal = scanlT clock maxFunc minVal-    where maxFunc y' y _ _ = max y' y-          -min_ :: Ord b => Clock IO -> b -> IOSource a b -> IOSource a b-min_ clock maxVal = scanlT clock minFunc maxVal-    where minFunc y' y _ _ = min y' y-
+ src/Graphics/GraphicsProcessors.hs view
@@ -0,0 +1,43 @@++--------------------------------------------------------------+-- | +-- Module      : AI.CV.ImageProcessors+-- Copyright   : (c) Noam Lewis 2010+-- License     : BSD3+--+-- Maintainer  : Noam Lewis <jones.noamle@gmail.com>+-- Stability   : experimental+-- Portability : tested on GHC only+--+--------------------------------------------------------------+module Graphics.GraphicsProcessors where+++import Control.Processor+import qualified Graphics.DrawingCombinators as Draw+import qualified Graphics.UI.SDL as SDL++type DrawRenderer a = IOSink (Draw.Image a)+++------------------------------------------------------------------+-- | A window that displays images.+sdlWindow :: Int -> Int -> DrawRenderer a+sdlWindow width height = processor procFunc allocFunc return return+    where procFunc :: (Draw.Image a -> () -> IO ())+          procFunc image _ = do+            Draw.clearRender image >> SDL.glSwapBuffers+            return ()+          +          allocFunc :: (Draw.Image a -> IO ())+          allocFunc firstImage = do+            -- TODO: doh, this is global state! the processor should handle individual windows.+            SDL.init [SDL.InitTimer, SDL.InitVideo]+            -- resolution & color depth+            SDL.setVideoMode width height 32 [SDL.OpenGL]+            Draw.clearRender firstImage +            SDL.glSwapBuffers+            return ()+++
+ src/IntegratedTest.hs view
@@ -0,0 +1,82 @@+{-# LANGUAGE FlexibleContexts #-}++module Main where+++import qualified AI.CV.ImageProcessors as IP++import qualified Graphics.DrawingCombinators as Draw+import Graphics.DrawingCombinators((%%))+import qualified Graphics.GraphicsProcessors as GP++import qualified AI.CV.OpenCV.CV as CV+import qualified Control.Processor as Processor+import Control.Processor(DTime, DClock, scanlT, IOSource, IOProcessor)+import AI.CV.OpenCV.CxCore(CvRect(..), CvSize(..))++import Data.VectorSpace((*^), zeroV, (^+^), Scalar, VectorSpace)++import Prelude hiding ((.),id)+import Control.Arrow+import Control.Category+import Data.Monoid++resX, resY :: Num a => a+resX = 160+resY = 120++resizer :: IP.ImageProcessor+resizer = IP.resize resX resY CV.CV_INTER_LINEAR++faceDetect :: IOProcessor IP.Image [CvRect]+faceDetect = IP.haarDetect "/usr/share/opencv/haarcascades/haarcascade_frontalface_alt.xml" 1.1 3 CV.cvHaarFlagNone (CvSize 20 20)+  +captureDev :: IP.ImageSource+--captureDev = videoFile "/tmp/video.flv" -- Many formats are supported, not just flv (FFMPEG-based, normally).++-- If you have a webcam, uncomment this, and comment the other definition.+captureDev = IP.camera 0++square :: Draw.Image Any+square = Draw.rotate (pi/4) %% Draw.regularPoly (4 :: Int)++drawCvRect :: CvRect -> Draw.Image Any+drawCvRect (CvRect x y w h) = tr %% Draw.tint (Draw.Color 0 1 0 0.5) square+    where tr = Draw.translate (1 - (2*x'/resX + w'/resX), 1 - (2*y'/resY + h'/resY))+               `mappend` (Draw.scale (2*w'/resX) (2*h'/resY))+          w' = fromIntegral w+          h' = fromIntegral h+          x' = fromIntegral x+          y' = fromIntegral y+  +--drawRects :: IP.ImageSink+--drawRects = arr (mconcat . drawCvRects) --fmap (mconcat . map drawCvRect) id+--    where drawCvRects = map drawCvRect+++clock :: IO Double -- = DClock Double+clock = return 1 -- todo implement really in some module that wraps SDL, GLUT or whatever.++-- todo: ins't this just an n-step past memory? generalize a bit and move to Processor package?+movingAverage :: Int -> ([(DTime, b)] -> c) -> (DTime, b) -> c -> IOSource a b -> IOProcessor a c+movingAverage n f initA initB p = (scanlT clock f' (take n . repeat $ initA, initB) p) >>> arr snd+    where f' _ y2 dt (lastNSamps, _) = (nextSamps, f nextSamps )+              where nextSamps = (dt, y2) : (tail lastNSamps)++-- todo: this is a general function, perhaps move to a module?+averageV :: (Fractional (Scalar a), VectorSpace a) => [Scalar a] -> [a] -> a+averageV weights samps = ((1/n) *^) . foldr (^+^) zeroV $ zipWith (*^) weights samps+    where n = fromIntegral (length weights)+          +  +-- todo: this is a general function, perhaps move to Processor package?+movingCvRectAverage :: (Fractional (Scalar v), VectorSpace v) => [Scalar v] -> IOProcessor a [v] -> IOProcessor a v+movingCvRectAverage weights pIn = movingAverage (length weights) (averageV weights . map snd) (0, zeroV) zeroV pIn'+    where pIn' = pIn >>> arr headOrZero+          headOrZero [] = zeroV+          headOrZero xs = head xs++main :: IO ()+main = Processor.runUntil (captureDev >>> resizer >>> avgRect faceDetect >>> arr drawCvRect >>> sdlWindow) () (const . return $ False)+    where avgRect = movingCvRectAverage [2,1,1,0]+          sdlWindow = GP.sdlWindow resX resY  
src/Test.hs view
@@ -4,8 +4,8 @@ import AI.CV.ImageProcessors  import qualified AI.CV.OpenCV.CV as CV-import qualified AI.CV.Processor as Processor-import AI.CV.Processor((--<))+import qualified Control.Processor as Processor+import Control.Processor((--<)) import AI.CV.OpenCV.Types import AI.CV.OpenCV.CxCore(CvRect(..), CvSize(..)) @@ -23,10 +23,10 @@ faceDetect = haarDetect "/usr/share/opencv/haarcascades/haarcascade_frontalface_alt.xml" 1.1 3 CV.cvHaarFlagNone (CvSize 20 20)    captureDev :: ImageSource-captureDev = videoFile "/tmp/video.flv" -- Many formats are supported, not just flv (FFMPEG-based, normally).+--captureDev = videoFile "/tmp/video.flv" -- Many formats are supported, not just flv (FFMPEG-based, normally).  -- If you have a webcam, uncomment this, and comment the other definition.--- captureDev = camera 0+captureDev = camera 0  main :: IO () main = runTillKeyPressed (captureDev >>> resizer --< (faces *** edges) >>> (window 0 *** window 1))