diff --git a/README.md b/README.md index 516d5cbc..4fe14442 100644 --- a/README.md +++ b/README.md @@ -1,10 +1,13 @@ -[![Hackage](https://img.shields.io/hackage/v/opencv.svg)](https://hackage.haskell.org/package/opencv) [![Build Status](https://travis-ci.org/LumiGuide/haskell-opencv.svg)](https://travis-ci.org/LumiGuide/haskell-opencv) +[opencv ![Hackage](https://img.shields.io/hackage/v/opencv.svg)](https://hackage.haskell.org/package/opencv) + +[opencv-extra ![Hackage](https://img.shields.io/hackage/v/opencv-extra.svg)](https://hackage.haskell.org/package/opencv-extra) + Haskell OpenCV-3.x binding ========================== -Haskell OpenCV-3.x logo +Haskell OpenCV-3.x logo This is a Haskell library providing a binding to OpenCV-3.x. It binds directly @@ -42,11 +45,11 @@ break between minor releases, so be careful. Development ----------- -We use Nix to enter an environment containing all the needed dependencies. For -the moment the following commands only work on Linux. The Nix expression for -opencv-extra currently fails to build on OS X. +To get into an environment that contains all the needed dependencies we use Nix. +The following commands work both on Linux and OS X: curl https://nixos.org/nix/install | sh # Only execute this if you haven't installed Nix yet. + cd opencv[-extra] nix-shell Then you should be able to use `cabal` as normal. diff --git a/data/haskell-opencv-logo-200x82.png b/data/haskell-opencv-logo-200x82.png new file mode 100644 index 00000000..ec63774b Binary files /dev/null and b/data/haskell-opencv-logo-200x82.png differ diff --git a/nixpkgs-config.nix b/nixpkgs-config.nix index 351c40fe..2bee0e7c 100644 --- a/nixpkgs-config.nix +++ b/nixpkgs-config.nix @@ -15,7 +15,7 @@ opencv3 = pkgs.opencv3.override { enableIpp = !osx; - enableContrib = !osx; + enableContrib = true; enableGtk2 = true; enableFfmpeg = !osx; enableGStreamer = true; diff --git a/nixpkgs.nix b/nixpkgs.nix index 1804c3cf..bfe5c0f0 100644 --- a/nixpkgs.nix +++ b/nixpkgs.nix @@ -2,6 +2,6 @@ let pkgs = import {}; in pkgs.fetchFromGitHub { owner = "NixOS"; repo = "nixpkgs"; - rev = "0011f9065a1ad1da4db67bec8d535d91b0a78fba"; - sha256 = "0m662mibyxqmp83zdhsi084p2h90268h3i8bfk3b2q8pbjz89yx2"; + rev = "89e02c7516ff301d28301926e9d7b373f29836fe"; + sha256 = "0v28hyyn1hw9951w1zvq5s40bbmk3gpjsmhq0cyp7lvs7d1aq0z7"; } diff --git a/opencv-examples/opencv-examples.cabal b/opencv-examples/opencv-examples.cabal index 350da0f1..e1d8e22f 100644 --- a/opencv-examples/opencv-examples.cabal +++ b/opencv-examples/opencv-examples.cabal @@ -14,6 +14,11 @@ extra-source-files: data/*.jpg data/*.xml +source-repository head + type: git + location: git://github.com/LumiGuide/haskell-opencv.git + subdir: opencv-examples + library hs-source-dirs: lib exposed-modules: diff --git a/opencv-extra-examples/opencv-extra-examples.cabal b/opencv-extra-examples/opencv-extra-examples.cabal index cd1ace4e..40e0469c 100644 --- a/opencv-extra-examples/opencv-extra-examples.cabal +++ b/opencv-extra-examples/opencv-extra-examples.cabal @@ -13,6 +13,11 @@ extra-source-files: data/*.jpg data/*.xml +source-repository head + type: git + location: git://github.com/LumiGuide/haskell-opencv.git + subdir: opencv-extra-examples + executable tracker main-is: tracker.hs hs-source-dirs: src diff --git a/opencv-extra/CHANGELOG.md b/opencv-extra/CHANGELOG.md new file mode 100644 index 00000000..e2e5c7b2 --- /dev/null +++ b/opencv-extra/CHANGELOG.md @@ -0,0 +1,14 @@ +## [0.0.0.1] - 2017-06-20 + +### Changed + +- Fix build on OS X. +- Add source repo to cabal file. +- Added a Cabal package description. + +## 0.0.0.0 - 2017-06-11 + +- Initial version + + +[0.0.0.1]: https://github.com/LumiGuide/haskell-opencv/compare/opencv-extra-0.0.0.0...opencv-extra-0.0.0.1 diff --git a/opencv-extra/Setup.hs b/opencv-extra/Setup.hs index 031daa10..6a35c073 100644 --- a/opencv-extra/Setup.hs +++ b/opencv-extra/Setup.hs @@ -3,6 +3,6 @@ import System.Environment ( getArgs ) main = do args <- getArgs - let args' | "configure" `elem` args = args ++ ["--with-gcc","g++", "--with-ld","g++"] + let args' | "configure" `elem` args = args ++ ["--with-gcc","c++", "--with-ld","c++"] | otherwise = args defaultMainArgs args' diff --git a/opencv-extra/opencv-extra.cabal b/opencv-extra/opencv-extra.cabal index d85eecd0..683d155d 100644 --- a/opencv-extra/opencv-extra.cabal +++ b/opencv-extra/opencv-extra.cabal @@ -1,5 +1,5 @@ name: opencv-extra -version: 0.0.0.0 +version: 0.0.0.1 homepage: https://github.com/LumiGuide/haskell-opencv bug-reports: https://github.com/LumiGuide/haskell-opencv/issues license: BSD3 @@ -10,11 +10,29 @@ build-type: Custom cabal-version: >=1.23 category: AI, Graphics synopsis: Haskell binding to OpenCV-3.x extra modules +description: <> + . + This is a Haskell library providing a binding to the OpenCV-3.x contrib modules. + It binds directly with the C++ API using the @inline-c@ Haskell library. + . + The library is far from complete but the framework is there to easily + bind missing functionality. + . + Make sure to checkout the + . + +extra-source-files: + CHANGELOG.md extra-doc-files: doc/generated/*.png doc/generated/examples/*.png +source-repository head + type: git + location: git://github.com/LumiGuide/haskell-opencv.git + subdir: opencv-extra + flag internal-documentation description: Enables documentation generation for internal modules. default: False diff --git a/opencv-extra/opencv-extra.nix b/opencv-extra/opencv-extra.nix index 0ec2d714..7f3702b6 100644 --- a/opencv-extra/opencv-extra.nix +++ b/opencv-extra/opencv-extra.nix @@ -73,8 +73,8 @@ mkDerivation { libraryPkgconfigDepends = [ opencv3 ]; configureFlags = - [ "--with-gcc=g++" - "--with-ld=g++" + [ "--with-gcc=${stdenv.cc}/bin/c++" + "--with-ld=${stdenv.cc}/bin/c++" ]; hardeningDisable = [ "bindnow" ]; diff --git a/opencv/CHANGELOG.md b/opencv/CHANGELOG.md new file mode 100644 index 00000000..0de5468a --- /dev/null +++ b/opencv/CHANGELOG.md @@ -0,0 +1,21 @@ +## [0.0.1.0] - 2017-06-20 + +### Added + +- OpenCV.Calib3d: findHomography. +- OpenCV.Core.ArrayOps: hconcat, vconcat. +- include/hsc_macros.hpp: #alignof macro. + +### Changed + +- Fix build on OS X. +- Add source repo to cabal file. +- Reference opencv-extra and the examples from the Cabal package description. + + +## 0.0.0.0 - 2017-06-11 + +- Initial version + + +[0.0.1.0]: https://github.com/LumiGuide/haskell-opencv/compare/opencv-0.0.0.0...opencv-0.0.1.0 diff --git a/opencv/Setup.hs b/opencv/Setup.hs index 031daa10..6a35c073 100644 --- a/opencv/Setup.hs +++ b/opencv/Setup.hs @@ -3,6 +3,6 @@ import System.Environment ( getArgs ) main = do args <- getArgs - let args' | "configure" `elem` args = args ++ ["--with-gcc","g++", "--with-ld","g++"] + let args' | "configure" `elem` args = args ++ ["--with-gcc","c++", "--with-ld","c++"] | otherwise = args defaultMainArgs args' diff --git a/opencv/include/hsc_macros.hpp b/opencv/include/hsc_macros.hpp index 1d6e5982..22d71c3f 100644 --- a/opencv/include/hsc_macros.hpp +++ b/opencv/include/hsc_macros.hpp @@ -10,6 +10,8 @@ This files defines some hsc2hs macros. For documentation on how to construct cus #define bc_sizeof_varid(name) {printf("c'sizeof_");bc_word(name);}; \ +#define bc_alignof_varid(name) {printf("c'alignof_");bc_word(name);}; \ + /* The #sizeof macro outputs a Haskell constant with the size of the C/C++ type in bytes. @@ -27,4 +29,23 @@ Results in the following Haskell code: bc_sizeof_varid(# name);printf(" = %lu\n", sizeof(name)); \ }; \ +/* +The #alignof macro outputs a Haskell constant with the alignment off the C/C++ type in bytes. + +For example the following: + + #alignof Point2i + +Results in the following Haskell code: + + c'alignof_Point2i :: Int + c'alignof_Point2i = 4 + +The specific alignment that is calculated is platform dependent. +*/ +#define hsc_alignof(name) \ + { bc_alignof_varid(# name);printf(" :: Int\n"); \ + bc_alignof_varid(# name);printf(" = %lu\n", alignof(name)); \ + }; \ + #endif /* __HSC_MACROS_H__ */ diff --git a/opencv/opencv.cabal b/opencv/opencv.cabal index 708d67e3..4a272a95 100644 --- a/opencv/opencv.cabal +++ b/opencv/opencv.cabal @@ -1,5 +1,5 @@ name: opencv -version: 0.0.0.0 +version: 0.0.1.0 homepage: https://github.com/LumiGuide/haskell-opencv bug-reports: https://github.com/LumiGuide/haskell-opencv/issues license: BSD3 @@ -10,13 +10,22 @@ build-type: Custom cabal-version: >=1.23 category: AI, Graphics synopsis: Haskell binding to OpenCV-3.x -description: This is a Haskell library providing a binding to OpenCV-3.x. +description: <> + . + This is a Haskell library providing a binding to OpenCV-3.x. It binds directly with the C++ API using the @inline-c@ Haskell library. . The library is far from complete but the framework is there to easily bind missing functionality. + . + Note that the OpenCV contrib modules are provided by + . + . + Make sure to checkout the + . extra-source-files: + CHANGELOG.md data/*.png data/*.jpg @@ -26,6 +35,11 @@ extra-doc-files: doc/generated/examples/*.gif doc/color_conversions.png +source-repository head + type: git + location: git://github.com/LumiGuide/haskell-opencv.git + subdir: opencv + flag internal-documentation description: Enables documentation generation for internal modules. default: False diff --git a/opencv/opencv.nix b/opencv/opencv.nix index a28674cc..80ed9954 100644 --- a/opencv/opencv.nix +++ b/opencv/opencv.nix @@ -103,8 +103,8 @@ mkDerivation ({ libraryPkgconfigDepends = [ opencv3 ]; configureFlags = - [ "--with-gcc=g++" - "--with-ld=g++" + [ "--with-gcc=${stdenv.cc}/bin/c++" + "--with-ld=${stdenv.cc}/bin/c++" ]; hardeningDisable = [ "bindnow" ]; diff --git a/opencv/src/OpenCV/Calib3d.hs b/opencv/src/OpenCV/Calib3d.hs index bd62c493..d4619429 100644 --- a/opencv/src/OpenCV/Calib3d.hs +++ b/opencv/src/OpenCV/Calib3d.hs @@ -3,9 +3,12 @@ module OpenCV.Calib3d ( FundamentalMatMethod(..) + , FindHomographyMethod(..) + , FindHomographyParams(..) , WhichImage(..) -- , calibrateCamera , findFundamentalMat + , findHomography , computeCorrespondEpilines ) where @@ -14,6 +17,7 @@ import "base" Data.Word import "base" Foreign.C.Types import qualified "inline-c" Language.C.Inline as C import qualified "inline-c-cpp" Language.C.Inline.Cpp as C +import "data-default" Data.Default import "this" OpenCV.Internal.C.Inline ( openCvCtx ) import "this" OpenCV.Internal.C.Types import "this" OpenCV.Internal.Calib3d.Constants @@ -57,6 +61,24 @@ marshalWhichImage = \case Image1 -> 1 Image2 -> 2 +data FindHomographyMethod + = FindHomographyMethod_0 + -- ^ A regular method using all the points. + | FindHomographyMethod_RANSAC + -- ^ RANSAC-based robust method. + | FindHomographyMethod_LMEDS + -- ^ Least-Median robust method. + | FindHomographyMethod_RHO + -- ^ PROSAC-based robust method. + deriving (Show) + +marshalFindHomographyMethod :: FindHomographyMethod -> Int32 +marshalFindHomographyMethod = \case + FindHomographyMethod_0 -> 0 + FindHomographyMethod_RANSAC -> c'RANSAC + FindHomographyMethod_LMEDS -> c'LMEDS + FindHomographyMethod_RHO -> c'RHO + -------------------------------------------------------------------------------- -- {- | @@ -110,7 +132,7 @@ findFundamentalMat findFundamentalMat pts1 pts2 method = do (fm, pointMask) <- c'findFundamentalMat -- If the c++ function can't find a fundamental matrix it will - -- retrun an empty matrix. We check for this case by trying to + -- return an empty matrix. We check for this case by trying to -- coerce the result to the desired type. catchE (Just . (, unsafeCoerceMat pointMask) <$> coerceMat fm) (\case CoerceMatError _msgs -> pure Nothing @@ -118,7 +140,7 @@ findFundamentalMat pts1 pts2 method = do ) where c'findFundamentalMat = unsafeWrapException $ do - fm <- newEmptyMat + fm <- newEmptyMat pointMask <- newEmptyMat handleCvException (pure (fm, pointMask)) $ withPtr fm $ \fmPtr -> @@ -143,6 +165,70 @@ findFundamentalMat pts1 pts2 method = do c'numPts2 = fromIntegral $ V.length pts2 (c'method, c'p1, c'p2) = marshalFundamentalMatMethod method +data FindHomographyParams + = FindHomographyParams + { fhpMethod :: !FindHomographyMethod + , fhpRansacReprojThreshold :: !Double + , fhpMaxIters :: !Int + , fhpConfidence :: !Double + } deriving (Show) + +instance Default FindHomographyParams where + def = FindHomographyParams + { fhpMethod = FindHomographyMethod_0 + , fhpRansacReprojThreshold = 3 + , fhpMaxIters = 2000 + , fhpConfidence = 0.995 + } + +findHomography + :: (IsPoint2 point2 CDouble) + => V.Vector (point2 CDouble) -- ^ Points from the first image. + -> V.Vector (point2 CDouble) -- ^ Points from the second image. + -> FindHomographyParams + -> CvExcept ( Maybe ( Mat ('S '[ 'S 3, 'S 3 ]) ('S 1) ('S Double) + , Mat ('S '[ 'D, 'D ]) ('S 1) ('S Word8 ) + ) + ) +findHomography srcPoints dstPoints fhp = do + (fm, pointMask) <- c'findHomography + -- If the c++ function can't find a fundamental matrix it will + -- return an empty matrix. We check for this case by trying to + -- coerce the result to the desired type. + catchE (Just . (, unsafeCoerceMat pointMask) <$> coerceMat fm) + (\case CoerceMatError _msgs -> pure Nothing + otherError -> throwE otherError + ) + where + c'findHomography = unsafeWrapException $ do + fm <- newEmptyMat + pointMask <- newEmptyMat + handleCvException (pure (fm, pointMask)) $ + withPtr fm $ \fmPtr -> + withPtr pointMask $ \pointMaskPtr -> + withArrayPtr (V.map toPoint srcPoints) $ \srcPtr -> + withArrayPtr (V.map toPoint dstPoints) $ \dstPtr -> + [cvExcept| + cv::_InputArray srcPts = cv::_InputArray($(Point2d * srcPtr), $(int32_t c'numSrcPts)); + cv::_InputArray dstPts = cv::_InputArray($(Point2d * dstPtr), $(int32_t c'numDstPts)); + *$(Mat * fmPtr) = + cv::findHomography + ( srcPts + , dstPts + , $(int32_t c'method) + , $(double c'ransacReprojThreshold) + , *$(Mat * pointMaskPtr) + , $(int32_t c'maxIters) + , $(double c'confidence) + ); + |] + c'numSrcPts = fromIntegral $ V.length srcPoints + c'numDstPts = fromIntegral $ V.length dstPoints + c'method = marshalFindHomographyMethod $ fhpMethod fhp + c'ransacReprojThreshold = realToFrac $ fhpRansacReprojThreshold fhp + c'maxIters = fromIntegral $ fhpMaxIters fhp + c'confidence = realToFrac $ fhpConfidence fhp + {- | For points in an image of a stereo pair, computes the corresponding epilines in the other image diff --git a/opencv/src/OpenCV/Core/ArrayOps.hs b/opencv/src/OpenCV/Core/ArrayOps.hs index 6a669df6..c528d281 100644 --- a/opencv/src/OpenCV/Core/ArrayOps.hs +++ b/opencv/src/OpenCV/Core/ArrayOps.hs @@ -44,6 +44,8 @@ module OpenCV.Core.ArrayOps , meanStdDev , matFlip, FlipDirection(..) , matTranspose + , hconcat + , vconcat ) where import "base" Data.Proxy ( Proxy(..) ) @@ -922,3 +924,77 @@ matTranspose src = unsafePerformIO $ do cv::transpose(*$(Mat * srcPtr), *$(Mat * dstPtr)); }|] pure $ unsafeCoerceMat dst + +{- | Applies horizontal concatenation to given matrices. + +Example: + +@ +hconcatImg :: Mat ('S '[ 'D, 'D ]) ('S 3) ('S Word8) +hconcatImg = exceptError $ + hconcat $ V.fromList + [ halfSize birds_768x512 + , halfSize flower_768x512 + , halfSize sailboat_768x512 + ] + where + halfSize = exceptError . resize (ResizeRel 0.5) InterArea +@ + +<> +-} +hconcat + :: V.Vector (Mat ('S '[rows, 'D]) channels depth) + -> CvExcept (Mat ('S '[rows, 'D]) channels depth) +hconcat mats = unsafeWrapException $ do + dst <- unsafeCoerceMat <$> newEmptyMat + handleCvException (pure dst) $ + withArrayPtr mats $ \matsPtr -> + withPtr dst $ \dstPtr -> + [cvExcept| + cv::hconcat + ( $(Mat * matsPtr) + , $(size_t c'numMats) + , *$(Mat * dstPtr) + ); + |] + where + c'numMats :: C.CSize + c'numMats = fromIntegral $ V.length mats + +{- | Applies vertical concatenation to given matrices. + +Example: + +@ +vconcatImg :: Mat ('S '[ 'D, 'D ]) ('S 3) ('S Word8) +vconcatImg = exceptError $ + vconcat $ V.fromList + [ halfSize birds_768x512 + , halfSize flower_768x512 + , halfSize sailboat_768x512 + ] + where + halfSize = exceptError . resize (ResizeRel 0.5) InterArea +@ + +<> +-} +vconcat + :: V.Vector (Mat ('S '[ 'D, cols ]) channels depth) + -> CvExcept (Mat ('S '[ 'D, cols ]) channels depth) +vconcat mats = unsafeWrapException $ do + dst <- unsafeCoerceMat <$> newEmptyMat + handleCvException (pure dst) $ + withArrayPtr mats $ \matsPtr -> + withPtr dst $ \dstPtr -> + [cvExcept| + cv::vconcat + ( $(Mat * matsPtr) + , $(size_t c'numMats) + , *$(Mat * dstPtr) + ); + |] + where + c'numMats :: C.CSize + c'numMats = fromIntegral $ V.length mats diff --git a/opencv/src/OpenCV/Internal/Calib3d/Constants.hsc b/opencv/src/OpenCV/Internal/Calib3d/Constants.hsc index 2da3dc39..e9756106 100644 --- a/opencv/src/OpenCV/Internal/Calib3d/Constants.hsc +++ b/opencv/src/OpenCV/Internal/Calib3d/Constants.hsc @@ -16,3 +16,8 @@ module OpenCV.Internal.Calib3d.Constants where #num CV_FM_8POINT #num CV_FM_RANSAC #num CV_FM_LMEDS + +#num LMEDS +#num RANSAC +#num RHO +