diff --git a/README.md b/README.md index cec9831..c3ac448 100644 --- a/README.md +++ b/README.md @@ -42,6 +42,8 @@ If you want to use CV2's API, please refer to the code under `gocv/` directory. Please also note that the basic data structures in OpenCV (e.g., `cv::Mat`, `cv::Point3f`) are wrapped partially for now. For more detail on how to use these types, please refer to [GoCV's README](gocv/README.md). +*Requirement*: we will build the wrappers based on [mat64](https://godoc.org/github.com/gonum/matrix/mat64), given it is much easier to manipulate the underlaying data. In most case, it is not necessary to access the original CV data, e.g., `cv::Mat` can be converted from/to `*mat64.Dense`. + ## Example ### OpenCV2's initCameraMatrix2D @@ -49,22 +51,25 @@ Please also note that the basic data structures in OpenCV (e.g., `cv::Mat`, `cv: ```go package main -import "github.com/lazywei/go-opencv/gocv" +import . "github.com/lazywei/go-opencv/gocv" +import "github.com/gonum/matrix/mat64" func main() { - objPts := gocv.NewGcvPoint3fVector(int64(4)) - objPts.Set(0, gocv.NewGcvPoint3f(0, 25, 0)) - objPts.Set(1, gocv.NewGcvPoint3f(0, -25, 0)) - objPts.Set(2, gocv.NewGcvPoint3f(-47, 25, 0)) - objPts.Set(3, gocv.NewGcvPoint3f(-47, -25, 0)) - imgPts := gocv.NewGcvPoint2fVector(int64(4)) - imgPts.Set(0, gocv.NewGcvPoint2f(1136.4140625, 1041.89208984)) - imgPts.Set(1, gocv.NewGcvPoint2f(1845.33190918, 671.39581299)) - imgPts.Set(2, gocv.NewGcvPoint2f(302.73373413, 634.79998779)) - imgPts.Set(3, gocv.NewGcvPoint2f(1051.46154785, 352.76107788)) + objPts := mat64.NewDense(4, 3, []float64{ + 0, 25, 0, + 0, -25, 0, + -47, 25, 0, + -47, -25, 0}) - gocv.GcvInitCameraMatrix2D(objPts, imgPts) + imgPts := mat64.NewDense(4, 2, []float64{ + 1136.4140625, 1041.89208984, + 1845.33190918, 671.39581299, + 302.73373413, 634.79998779, + 1051.46154785, 352.76107788}) + + camMat := GcvInitCameraMatrix2D(objPts, imgPts) + fmt.Println(camMat) } ``` diff --git a/gocv/README.md b/gocv/README.md index 898e4d5..a8cac6b 100644 --- a/gocv/README.md +++ b/gocv/README.md @@ -5,23 +5,23 @@ Wrap the core types in OpenCV. ## Supporting Types and Examples -| OpenCV C++ | Go OpenCV | Constructor | -|---------------|---------------|-------------------------------| -| `cv::Point2i` | `GcvPoint2i` | `NewGcvPoint2i(x, y int)` | -| `cv::Point2f` | `GcvPoint2f_` | `NewGcvPoint2f(x, y float32)` | -| `cv::Point2d` | `GcvPoint2d_` | `NewGcvPoint2d(x, y float64)` | -| `cv::Point3i` | `GcvPoint3i` | `NewGcvPoint3i(x, y, z int)` | -| `cv::Point3f` | `GcvPoint3f_` | `NewGcvPoint3f(x, y, z float32)` | -| `cv::Point3d` | `GcvPoint3d_` | `NewGcvPoint3d(x, y, z float64)` | -| `cv::Size2i` | `GcvSize2i` | `NewGcvSize2i(x, y int)` | -| `cv::Size2f` | `GcvSize2f_` | `NewGcvSize2f(x, y float32)` | -| `cv::Size2d` | `GcvSize2d_` | `NewGcvSize2d(x, y float64)` | +| OpenCV C++ | Go OpenCV | Constructor | +|---------------|-----------------|-------------------------------| +| `cv::Point2i` | `GcvPoint2i` | `NewGcvPoint2i(x, y int)` | +| `cv::Point2f` | `GcvPoint2f32_` | `NewGcvPoint2f32(x, y float64)` | +| `cv::Point2d` | `GcvPoint2f64_` | `NewGcvPoint2f64(x, y float64)` | +| `cv::Point3i` | `GcvPoint3i` | `NewGcvPoint3i(x, y, z int)` | +| `cv::Point3f` | `GcvPoint3f32_` | `NewGcvPoint3f32(x, y, z float64)` | +| `cv::Point3d` | `GcvPoint3f64_` | `NewGcvPoint3f64(x, y, z float64)` | +| `cv::Size2i` | `GcvSize2i` | `NewGcvSize2i(x, y int)` | +| `cv::Size2f` | `GcvSize2f32_` | `NewGcvSize2f64(x, y float64)` | +| `cv::Size2d` | `GcvSize2f64_` | `NewGcvSize2f64(x, y float64)` | ---------- ### Note for Renamed Types Some of the types are renamed to `*_`. The reason is that we'd like to wrap a better interface for them. -For example, the original `NewPoint2f` takes strictly two `float32`, and we are not able to pass `float64` or `int`, which doesn't make too much sense. +For example, the original `NewGcvPoint2f32` takes strictly two `float32`, and we are not able to pass `float64` or `int`, which doesn't make too much sense. After wrapping an extra level, we are now able to pass `int`, `float32`, and `float64` to these methods. Also note that **renaming doesn't affect any usage**, except you are manipulating the types yourself. diff --git a/gocv/gocv.cpp b/gocv/gocv.cpp deleted file mode 100644 index 3458147..0000000 --- a/gocv/gocv.cpp +++ /dev/null @@ -1,44 +0,0 @@ -#include -#include -#include -#include - -#include "gocv.hpp" - -cv::Mat GcvInitCameraMatrix2D(VecPoint3f objPts, VecPoint2f imgPts) { - cv::Mat cameraMatrix; - - std::vector objPtsArr; - std::vector imgPtsArr; - - objPtsArr.push_back(objPts); - imgPtsArr.push_back(imgPts); - - cameraMatrix = cv::initCameraMatrix2D(objPtsArr, imgPtsArr, cv::Size(1920, 1080), 1); - std::cout << cameraMatrix.type() << std::endl; - return cameraMatrix; -} - -double GcvCalibrateCamera(VecPoint3f objPts, VecPoint2f imgPts, - cv::Size imgSize, cv::Mat cameraMatrix) { - std::vector objPtsArr; - std::vector imgPtsArr; - std::vector rvecs, tvecs; - cv::Mat distCoeffs; - - double rtn; - - objPtsArr.push_back(objPts); - imgPtsArr.push_back(imgPts); - - std::cout << "init Camera" << cameraMatrix << std::endl; - - rtn = cv::calibrateCamera(objPtsArr, imgPtsArr, imgSize, - cameraMatrix, distCoeffs, rvecs, tvecs); - - std::cout << "final Camera" << cameraMatrix << std::endl; - std::cout << "final rvecs" << rvecs[0] << std::endl; - std::cout << "final tvecs" << tvecs[0] << std::endl; - - return rtn; -} diff --git a/gocv/gocv.go b/gocv/gocv.go index 4f2e5f9..772b236 100644 --- a/gocv/gocv.go +++ b/gocv/gocv.go @@ -3,27 +3,3 @@ package gocv // #cgo CXXFLAGS: -std=c++11 // #cgo darwin pkg-config: opencv import "C" - -func NewGcvPoint3f(x, y, z float32) GcvPoint3f_ { - return NewGcvPoint3f_(float32(x), float32(y), float32(z)) -} - -func NewGcvPoint3d(x, y, z float64) GcvPoint3d_ { - return NewGcvPoint3d_(float64(x), float64(y), float64(z)) -} - -func NewGcvPoint2f(x, y float32) GcvPoint2f_ { - return NewGcvPoint2f_(float32(x), float32(y)) -} - -func NewGcvPoint2d(x, y float64) GcvPoint2d_ { - return NewGcvPoint2d_(float64(x), float64(y)) -} - -func NewGcvSize2f(x, y float32) GcvSize2f_ { - return NewGcvSize2f_(float32(x), float32(y)) -} - -func NewGcvSize2d(x, y float64) GcvSize2d_ { - return NewGcvSize2d_(float64(x), float64(y)) -} diff --git a/gocv/gocv.hpp b/gocv/gocv.hpp deleted file mode 100644 index 3710c3c..0000000 --- a/gocv/gocv.hpp +++ /dev/null @@ -1,11 +0,0 @@ -#include -#include -#include - -typedef std::vector VecPoint3f; -typedef std::vector VecPoint2f; - -cv::Mat GcvInitCameraMatrix2D(VecPoint3f objPts, VecPoint2f imgPts); - -double GcvCalibrateCamera(VecPoint3f objPts, VecPoint2f imgPts, - cv::Size2i imgSize, cv::Mat cameraMatrix); diff --git a/gocv/gocv.swigcxx b/gocv/gocv.swigcxx index be86413..4b5151f 100644 --- a/gocv/gocv.swigcxx +++ b/gocv/gocv.swigcxx @@ -1,278 +1,4 @@ %module gocv -%include "std_vector.i" -%{ -#include "opencv2/core/types_c.h" -#include "opencv2/core/version.hpp" -#include "opencv2/core/core.hpp" -#include "gocv.hpp" -%} - -%include "gocv.hpp" - -/* Classes defined in core.hpp */ -namespace cv { - - template class Size_; - template class Point_; - template class Rect_; - template class Vec; - - //////////////////////////////// Point_ //////////////////////////////// - - /*! - template 2D point class. - - The class defines a point in 2D space. Data type of the point coordinates is specified - as a template parameter. There are a few shorter aliases available for user convenience. - See cv::Point, cv::Point2i, cv::Point2f and cv::Point2d. - */ - template class Point_ - { - public: - typedef _Tp value_type; - - // various constructors - Point_(); - Point_(_Tp _x, _Tp _y); - Point_(const Point_& pt); - Point_(const CvPoint& pt); - Point_(const CvPoint2D32f& pt); - Point_(const Size_<_Tp>& sz); - Point_(const Vec<_Tp, 2>& v); - - Point_& operator = (const Point_& pt); - //! conversion to another data type - template operator Point_<_Tp2>() const; - - //! conversion to the old-style C structures - operator CvPoint() const; - operator CvPoint2D32f() const; - operator Vec<_Tp, 2>() const; - - //! dot product - _Tp dot(const Point_& pt) const; - //! dot product computed in double-precision arithmetics - double ddot(const Point_& pt) const; - //! cross-product - double cross(const Point_& pt) const; - //! checks whether the point is inside the specified rectangle - bool inside(const Rect_<_Tp>& r) const; - - _Tp x, y; //< the point coordinates - }; - - /*! - template 3D point class. - - The class defines a point in 3D space. Data type of the point coordinates is specified - as a template parameter. - - \see cv::Point3i, cv::Point3f and cv::Point3d - */ - template class Point3_ - { - public: - typedef _Tp value_type; - - // various constructors - Point3_(); - Point3_(_Tp _x, _Tp _y, _Tp _z); - Point3_(const Point3_& pt); - explicit Point3_(const Point_<_Tp>& pt); - Point3_(const CvPoint3D32f& pt); - Point3_(const Vec<_Tp, 3>& v); - - Point3_& operator = (const Point3_& pt); - //! conversion to another data type - template operator Point3_<_Tp2>() const; - //! conversion to the old-style CvPoint... - operator CvPoint3D32f() const; - //! conversion to cv::Vec<> - operator Vec<_Tp, 3>() const; - - //! dot product - _Tp dot(const Point3_& pt) const; - //! dot product computed in double-precision arithmetics - double ddot(const Point3_& pt) const; - //! cross product of the 2 3D points - Point3_ cross(const Point3_& pt) const; - - _Tp x, y, z; //< the point coordinates - }; - - //////////////////////////////// Size_ //////////////////////////////// - - /*! - The 2D size class - - The class represents the size of a 2D rectangle, image size, matrix size etc. - Normally, cv::Size ~ cv::Size_ is used. - */ - template class Size_ - { - public: - typedef _Tp value_type; - - //! various constructors - Size_(); - Size_(_Tp _width, _Tp _height); - Size_(const Size_& sz); - Size_(const CvSize& sz); - Size_(const CvSize2D32f& sz); - Size_(const Point_<_Tp>& pt); - - Size_& operator = (const Size_& sz); - //! the area (width*height) - _Tp area() const; - - //! conversion of another data type. - template operator Size_<_Tp2>() const; - - //! conversion to the old-style OpenCV types - operator CvSize() const; - operator CvSize2D32f() const; - - _Tp width, height; // the width and the height - }; - - //////////////////////////////// Rect_ //////////////////////////////// - - /*! - The 2D up-right rectangle class - - The class represents a 2D rectangle with coordinates of the specified data type. - Normally, cv::Rect ~ cv::Rect_ is used. - */ - template class Rect_ - { - public: - typedef _Tp value_type; - - //! various constructors - Rect_(); - Rect_(_Tp _x, _Tp _y, _Tp _width, _Tp _height); - Rect_(const Rect_& r); - Rect_(const CvRect& r); - Rect_(const Point_<_Tp>& org, const Size_<_Tp>& sz); - Rect_(const Point_<_Tp>& pt1, const Point_<_Tp>& pt2); - - Rect_& operator = ( const Rect_& r ); - //! the top-left corner - Point_<_Tp> tl() const; - //! the bottom-right corner - Point_<_Tp> br() const; - - //! size (width, height) of the rectangle - Size_<_Tp> size() const; - //! area (width*height) of the rectangle - _Tp area() const; - - //! conversion to another data type - template operator Rect_<_Tp2>() const; - //! conversion to the old-style CvRect - operator CvRect() const; - - //! checks whether the rectangle contains the point - bool contains(const Point_<_Tp>& pt) const; - - _Tp x, y, width, height; //< the top-left corner, as well as width and height of the rectangle - }; - - - %template(GcvSize2i) Size_; - %template(GcvSize2d_) Size_; - %template(GcvSize2f_) Size_; - - %template(GcvRect) Rect_; - - %template(GcvPoint2i) Point_; - %template(GcvPoint2f_) Point_; - %template(GcvPoint2d_) Point_; - - %template(GcvPoint3i) Point3_; - %template(GcvPoint3f_) Point3_; - %template(GcvPoint3d_) Point3_; - - - /* ----------------- Mat ----------------- */ - class Mat - { - public: - //! default constructor - Mat(); - //! constructs 2D matrix of the specified size and type - // (_type is CV_8UC1, CV_64FC3, CV_32SC(12) etc.) - Mat(int rows, int cols, int type); - Mat(cv::Size size, int type); - //! constucts 2D matrix and fills it with the specified value _s. - Mat(int rows, int cols, int type, const cv::Scalar& s); - Mat(cv::Size size, int type, const cv::Scalar& s); - - //! copy constructor - Mat(const Mat& m); - - //! builds matrix from std::vector with or without copying the data - template explicit Mat(const vector<_Tp>& vec, bool copyData=false); - //! builds matrix from cv::Vec; the data is copied by default - template explicit Mat(const Vec<_Tp, n>& vec, bool copyData=true); - //! builds matrix from cv::Matx; the data is copied by default - template explicit Mat(const Matx<_Tp, m, n>& mtx, bool copyData=true); - //! builds matrix from a 2D point - template explicit Mat(const Point_<_Tp>& pt, bool copyData=true); - //! builds matrix from a 3D point - template explicit Mat(const Point3_<_Tp>& pt, bool copyData=true); - - //! destructor - calls release() - ~Mat(); - - //! returns a new matrix header for the specified row - Mat row(int y) const; - //! returns a new matrix header for the specified column - Mat col(int x) const; - //! ... for the specified row span - Mat rowRange(int startrow, int endrow) const; - //! ... for the specified column span - Mat colRange(int startcol, int endcol) const; - //! ... for the specified diagonal - // (d=0 - the main diagonal, - // >0 - a diagonal from the lower half, - // <0 - a diagonal from the upper half) - Mat diag(int d=0) const; - //! constructs a square diagonal matrix which main diagonal is vector "d" - static Mat diag(const Mat& d); - - //! returns deep copy of the matrix, i.e. the data is copied - Mat clone() const; - - void assignTo( Mat& m, int type=-1 ) const; - - //! creates alternative matrix header for the same data, with different - // number of channels and/or different number of rows. see cvReshape. - Mat reshape(int cn, int rows=0) const; - Mat reshape(int cn, int newndims, const int* newsz) const; - - //! adds element to the end of 1d matrix (or possibly multiple elements when _Tp=Mat) - template void push_back(const _Tp& elem); - template void push_back(const Mat_<_Tp>& elem); - void push_back(const Mat& m); - //! removes several hyper-planes from bottom of the matrix - void pop_back(size_t nelems=1); - - //! special versions for 2D arrays (especially convenient for referencing image pixels) - template _Tp& at(cv::Point pt); - template const _Tp& at(cv::Point pt) const; - %template(gcvAtd) at; - %template(gcvAtf) at; - }; - -} - -/* Additional STL types */ -namespace std { - %template(GcvPoint3fVector) vector; - %template(GcvPoint2fVector) vector; - - %template(GcvIntVector) vector; - %template(GcvFloatVector) vector; -}; +%include "gocv_core.i" +%include "gocv_calib3d.i" diff --git a/gocv/gocv_calib3d.cpp b/gocv/gocv_calib3d.cpp new file mode 100644 index 0000000..88ed529 --- /dev/null +++ b/gocv/gocv_calib3d.cpp @@ -0,0 +1,56 @@ +#include +#include +#include +#include + +#include "gocv_calib3d.hpp" + +cv::Mat GcvInitCameraMatrix2D_(VecPoint3f objPts, VecPoint2f imgPts) { + cv::Mat cameraMatrix; + + std::vector objPtsArr; + std::vector imgPtsArr; + + objPtsArr.push_back(objPts); + imgPtsArr.push_back(imgPts); + + cameraMatrix = cv::initCameraMatrix2D(objPtsArr, imgPtsArr, cv::Size(1920, 1080), 1); + return cameraMatrix; +} + +double GcvCalibrateCamera_(VecPoint3f objPts, VecPoint2f imgPts, + cv::Size imgSize, cv::Mat& cameraMatrix, + cv::Mat& rvec, cv::Mat& tvec) { + std::vector objPtsArr; + std::vector imgPtsArr; + std::vector rvecs, tvecs; + cv::Mat distCoeffs; + double rtn; + + objPtsArr.push_back(objPts); + imgPtsArr.push_back(imgPts); + + std::cout << "objPts " << std::endl << objPtsArr[0] << std::endl; + std::cout << "imgPts " << std::endl << imgPtsArr[0] << std::endl; + std::cout << "imgSize " << std::endl << imgSize << std::endl; + std::cout << "Before CamMat " << std::endl << cameraMatrix << std::endl; + + rtn = cv::calibrateCamera(objPtsArr, imgPtsArr, imgSize, + cameraMatrix, distCoeffs, + rvecs, tvecs, 14575); + + rvec = rvecs[0]; + tvec = tvecs[0]; + + std::cout << "After CamMat " << std::endl << cameraMatrix << std::endl; + std::cout << "distCoeffs " << std::endl << distCoeffs << std::endl; + std::cout << "rvec " << std::endl << rvec << std::endl; + std::cout << "tvec " << std::endl << tvec << std::endl; + std::cout << "rms " << std::endl << rtn << std::endl; + + return rtn; +} + +void GcvRodrigues_(cv::Mat src, cv::Mat& dst) { + cv::Rodrigues(src, dst); +} diff --git a/gocv/gocv_calib3d.go b/gocv/gocv_calib3d.go new file mode 100644 index 0000000..4e52da8 --- /dev/null +++ b/gocv/gocv_calib3d.go @@ -0,0 +1,113 @@ +package gocv + +// #cgo CXXFLAGS: -std=c++11 +// #cgo darwin pkg-config: opencv +import "C" +import "github.com/gonum/matrix/mat64" + +// GcvInitCameraMatrix2D takes one N-by-3 matrix and one +// N-by-2 Matrix as input. +// Each row in the input matrix represents a point in real +// world (objPts) or in image (imgPts). +// Return: the camera matrix. +func GcvInitCameraMatrix2D(objPts, imgPts *mat64.Dense) (camMat *mat64.Dense) { + nObjPts, objCol := objPts.Dims() + nImgPts, imgCol := imgPts.Dims() + + if objCol != 3 || imgCol != 2 || nObjPts != nImgPts { + panic("Invalid dimensions for objPts and imgPts") + } + + objPtsVec := NewGcvPoint3f32Vector(int64(nObjPts)) + imgPtsVec := NewGcvPoint2f32Vector(int64(nObjPts)) + + for i := 0; i < nObjPts; i++ { + objPtsVec.Set(i, NewGcvPoint3f32(objPts.Row(nil, i)...)) + } + + for i := 0; i < nObjPts; i++ { + imgPtsVec.Set(i, NewGcvPoint2f32(imgPts.Row(nil, i)...)) + } + + camMat = GcvMatToMat64(GcvInitCameraMatrix2D_(objPtsVec, imgPtsVec)) + return camMat +} + +func GcvCalibrateCamera(objPts, imgPts, camMat *mat64.Dense) (calCamMat, rvec, tvec *mat64.Dense) { + nObjPts, objCol := objPts.Dims() + nImgPts, imgCol := imgPts.Dims() + + if objCol != 3 || imgCol != 2 || nObjPts != nImgPts { + panic("Invalid dimensions for objPts and imgPts") + } + + objPtsVec := NewGcvPoint3f32Vector(int64(nObjPts)) + imgPtsVec := NewGcvPoint2f32Vector(int64(nObjPts)) + + for i := 0; i < nObjPts; i++ { + objPtsVec.Set(i, NewGcvPoint3f32(objPts.Row(nil, i)...)) + } + + for i := 0; i < nObjPts; i++ { + imgPtsVec.Set(i, NewGcvPoint2f32(imgPts.Row(nil, i)...)) + } + + _camMat := Mat64ToGcvMat(camMat) + _rvec := NewGcvMat() + _tvec := NewGcvMat() + _imgSize := NewGcvSize2i(1920, 1080) + + GcvCalibrateCamera_( + objPtsVec, imgPtsVec, + _imgSize, _camMat, _rvec, _tvec) + + calCamMat = GcvMatToMat64(_camMat) + rvec = GcvMatToMat64(_rvec) + tvec = GcvMatToMat64(_tvec) + + return calCamMat, rvec, tvec +} + +// Same as cv::Rodrigues +func GcvRodrigues(src *mat64.Dense) (dst *mat64.Dense) { + gcvSrc := Mat64ToGcvMat(src) + gcvDst := NewGcvMat() + GcvRodrigues_(gcvSrc, gcvDst) + dst = GcvMatToMat64(gcvDst) + + return dst +} + +// func mat64ToGcvPoint3f32Vector(mat *mat64.Dense) NewGcvPoint3f32Vector { + +// } + +// func TestGcvCalibrateCamera(t *testing.T) { +// objPts := NewGcvPoint3fVector(int64(4)) +// objPts.Set(0, NewGcvPoint3f(0, 25, 0)) +// objPts.Set(1, NewGcvPoint3f(0, -25, 0)) +// objPts.Set(2, NewGcvPoint3f(-47, 25, 0)) +// objPts.Set(3, NewGcvPoint3f(-47, -25, 0)) + +// imgPts := NewGcvPoint2fVector(int64(4)) +// imgPts.Set(0, NewGcvPoint2f(1136.4140625, 1041.89208984)) +// imgPts.Set(1, NewGcvPoint2f(1845.33190918, 671.39581299)) +// imgPts.Set(2, NewGcvPoint2f(302.73373413, 634.79998779)) +// imgPts.Set(3, NewGcvPoint2f(1051.46154785, 352.76107788)) + +// imgSize := NewGcvSize2i(1920, 1080) + +// camMat := GcvInitCameraMatrix2D(objPts, imgPts) +// spew.Dump(camMat.GcvAtd(NewGcvSize2i(0, 0))) +// spew.Dump(camMat.GcvAtd(NewGcvSize2i(0, 1))) +// spew.Dump(camMat.GcvAtd(NewGcvSize2i(1, 1))) +// spew.Dump(camMat.GcvAtd(NewGcvSize2i(1, 2))) +// spew.Dump(camMat.GcvAtd(NewGcvSize2i(2, 2))) + +// rvec := NewMat() +// tvec := NewMat() + +// GcvCalibrateCamera(objPts, imgPts, imgSize, camMat, rvec, tvec) + +// MatToMat64(camMat) +// } diff --git a/gocv/gocv_calib3d.hpp b/gocv/gocv_calib3d.hpp new file mode 100644 index 0000000..cb9c342 --- /dev/null +++ b/gocv/gocv_calib3d.hpp @@ -0,0 +1,14 @@ +#include +#include +#include + +typedef std::vector VecPoint3f; +typedef std::vector VecPoint2f; + +cv::Mat GcvInitCameraMatrix2D_(VecPoint3f objPts, VecPoint2f imgPts); + +double GcvCalibrateCamera_(VecPoint3f objPts, VecPoint2f imgPts, + cv::Size2i imgSize, cv::Mat& cameraMatrix, + cv::Mat& rvec, cv::Mat& tvec); + +void GcvRodrigues_(cv::Mat src, cv::Mat& dst); diff --git a/gocv/gocv_calib3d.i b/gocv/gocv_calib3d.i new file mode 100644 index 0000000..9ca1bc3 --- /dev/null +++ b/gocv/gocv_calib3d.i @@ -0,0 +1,5 @@ +%{ +#include "gocv_calib3d.hpp" +%} + +%include "gocv_calib3d.hpp" diff --git a/gocv/gocv_calib3d_test.go b/gocv/gocv_calib3d_test.go new file mode 100644 index 0000000..cc527df --- /dev/null +++ b/gocv/gocv_calib3d_test.go @@ -0,0 +1,77 @@ +package gocv + +import ( + "testing" + + "github.com/gonum/matrix/mat64" + "github.com/stretchr/testify/assert" +) + +func TestGcvInitCameraMatrix2D(t *testing.T) { + objPts := mat64.NewDense(4, 3, []float64{ + 0, 25, 0, + 0, -25, 0, + -47, 25, 0, + -47, -25, 0, + }) + + imgPts := mat64.NewDense(4, 2, []float64{ + 1136.4140625, 1041.89208984, + 1845.33190918, 671.39581299, + 302.73373413, 634.79998779, + 1051.46154785, 352.76107788, + }) + + camMat := GcvInitCameraMatrix2D(objPts, imgPts) + assert.Equal(t, camMat.Row(nil, 0), []float64{4828.129063751587, 0, 959.5}) + assert.Equal(t, camMat.Row(nil, 1), []float64{0, 4828.129063751587, 539.5}) + assert.Equal(t, camMat.Row(nil, 2), []float64{0, 0, 1}) +} + +func TestGcvCalibrateCamera(t *testing.T) { + objPts := mat64.NewDense(4, 3, []float64{ + 0, 25, 0, + 0, -25, 0, + -47, 25, 0, + -47, -25, 0, + }) + + imgPts := mat64.NewDense(4, 2, []float64{ + 1136.4140625, 1041.89208984, + 1845.33190918, 671.39581299, + 302.73373413, 634.79998779, + 1051.46154785, 352.76107788, + }) + + camMat := GcvInitCameraMatrix2D(objPts, imgPts) + + camMat, rvec, tvec := GcvCalibrateCamera(objPts, imgPts, camMat) + + assert.Equal(t, camMat.Row(nil, 0), []float64{5.47022369e+03, 0.00000000e+00, 9.59500000e+02}) + assert.Equal(t, camMat.Row(nil, 1), []float64{0.00000000e+00, 5.47022369e+03, 5.39500000e+02}) + assert.Equal(t, camMat.Row(nil, 2), []float64{0.00000000e+00, 0.00000000e+00, 1.00000000e+00}) + + assert.Equal(t, rvec.Col(nil, 0), []float64{-0.99458984, 0.54674764, -2.69721055}) + assert.Equal(t, tvec.Col(nil, 0), []float64{-23.25417757, -12.6155423, -227.64212085}) +} + +func TestGcvRodrigues(t *testing.T) { + rvec := mat64.NewDense(3, 1, []float64{ + -0.99458984, + 0.54674764, + -2.69721055, + }) + rmat := GcvRodrigues(rvec) + + assert.InDelta(t, rmat.At(0, 0), -0.74853587, 1e-6) + assert.InDelta(t, rmat.At(0, 1), 0.07139127, 1e-6) + assert.InDelta(t, rmat.At(0, 2), 0.65923997, 1e-6) + + assert.InDelta(t, rmat.At(1, 0), -0.32247419, 1e-6) + assert.InDelta(t, rmat.At(1, 1), -0.90789575, 1e-6) + assert.InDelta(t, rmat.At(1, 2), -0.26783521, 1e-6) + + assert.InDelta(t, rmat.At(2, 0), 0.57940008, 1e-6) + assert.InDelta(t, rmat.At(2, 1), -0.41307214, 1e-6) + assert.InDelta(t, rmat.At(2, 2), 0.70261437, 1e-6) +} diff --git a/gocv/gocv_core.cpp b/gocv/gocv_core.cpp new file mode 100644 index 0000000..d448255 --- /dev/null +++ b/gocv/gocv_core.cpp @@ -0,0 +1,21 @@ +#include +#include +#include +#include + +#include "gocv_core.hpp" + +cv::Mat Mat64ToGcvMat_(int row, int col, std::vector data) { + assert(row * col == data.size()); + + cv::Mat mat = cv::Mat(row, col, CV_64F); + + for (int i = 0; i < row; ++i) { + for (int j = 0; j < col; ++j) { + mat.at(i, j) = data[i*col + j]; + } + + } + + return mat; +} diff --git a/gocv/gocv_core.go b/gocv/gocv_core.go new file mode 100644 index 0000000..49b2072 --- /dev/null +++ b/gocv/gocv_core.go @@ -0,0 +1,84 @@ +package gocv + +// #cgo CXXFLAGS: -std=c++11 +// #cgo darwin pkg-config: opencv +import "C" +import "github.com/gonum/matrix/mat64" + +func NewGcvPoint3f32(pts ...float64) GcvPoint3f32_ { + // This make sure we have default values + safePts := getSafePts(pts, 3) + return NewGcvPoint3f32_(float32(safePts[0]), float32(safePts[1]), float32(safePts[2])) +} + +func NewGcvPoint3f64(pts ...float64) GcvPoint3f64_ { + safePts := getSafePts(pts, 3) + return NewGcvPoint3f64_(safePts[0], safePts[1], safePts[2]) +} + +func NewGcvPoint2f32(pts ...float64) GcvPoint2f32_ { + safePts := getSafePts(pts, 2) + return NewGcvPoint2f32_(float32(safePts[0]), float32(safePts[1])) +} + +func NewGcvPoint2f64(pts ...float64) GcvPoint2f64_ { + safePts := getSafePts(pts, 2) + return NewGcvPoint2f64_(safePts[0], safePts[1]) +} + +func NewGcvSize2f32(pts ...float64) GcvSize2f32_ { + safePts := getSafePts(pts, 2) + return NewGcvSize2f32_(float32(safePts[0]), float32(safePts[1])) +} + +func NewGcvSize2f64(pts ...float64) GcvSize2f64_ { + safePts := getSafePts(pts, 2) + return NewGcvSize2f64_(safePts[0], safePts[1]) +} + +// Convert Mat, which defined by SWIG, to *mat64.Dense. +// The reason is the latter is much easier to handle +// in Go. +// GcvMat is assumed to be 2-dimensional matrix. +func GcvMatToMat64(mat GcvMat) *mat64.Dense { + col := mat.GetCols() + row := mat.GetRows() + + data := []float64{} + + for i := 0; i < row; i++ { + for j := 0; j < col; j++ { + if fltPtr, ok := mat.GcvAtf64(i, j).(*float64); ok { + data = append(data, *fltPtr) + } else { + panic("Non *float64 passed to MatToMat64") + } + + } + } + + return mat64.NewDense(row, col, data) +} + +// Convert *mat64.Dense to Mat +func Mat64ToGcvMat(mat *mat64.Dense) GcvMat { + row, col := mat.Dims() + + rawData := NewGcvFloat64Vector(int64(row * col)) + + for i := 0; i < row; i++ { + for j := 0; j < col; j++ { + rawData.Set(i*col+j, mat.At(i, j)) + } + } + + return Mat64ToGcvMat_(row, col, rawData) +} + +func getSafePts(pts []float64, size int) []float64 { + // This make sure we have default values + safePts := make([]float64, size, size) + copy(safePts, pts) + + return safePts +} diff --git a/gocv/gocv_core.hpp b/gocv/gocv_core.hpp new file mode 100644 index 0000000..90074d5 --- /dev/null +++ b/gocv/gocv_core.hpp @@ -0,0 +1,5 @@ +#include +#include +#include + +cv::Mat Mat64ToGcvMat_(int row, int col, std::vector data); diff --git a/gocv/gocv_core.i b/gocv/gocv_core.i new file mode 100644 index 0000000..9f9ed69 --- /dev/null +++ b/gocv/gocv_core.i @@ -0,0 +1,304 @@ +%{ +#include "opencv2/core/core.hpp" +#include "gocv_core.hpp" +%} + +%include "std_vector.i" +%include "gocv_core.hpp" + +/* Classes defined in core.hpp */ +namespace cv { + + template class Size_; + template class Point_; + template class Rect_; + template class Vec; + + //////////////////////////////// Point_ //////////////////////////////// + + /*! + template 2D point class. + + The class defines a point in 2D space. Data type of the point coordinates is specified + as a template parameter. There are a few shorter aliases available for user convenience. + See cv::Point, cv::Point2i, cv::Point2f and cv::Point2d. + */ + template class Point_ + { + public: + typedef _Tp value_type; + + // various constructors + Point_(); + Point_(_Tp _x, _Tp _y); + Point_(const Point_& pt); + Point_(const CvPoint& pt); + Point_(const CvPoint2D32f& pt); + Point_(const Size_<_Tp>& sz); + Point_(const Vec<_Tp, 2>& v); + + Point_& operator = (const Point_& pt); + //! conversion to another data type + template operator Point_<_Tp2>() const; + + //! conversion to the old-style C structures + operator CvPoint() const; + operator CvPoint2D32f() const; + operator Vec<_Tp, 2>() const; + + //! dot product + _Tp dot(const Point_& pt) const; + //! dot product computed in double-precision arithmetics + double ddot(const Point_& pt) const; + //! cross-product + double cross(const Point_& pt) const; + //! checks whether the point is inside the specified rectangle + bool inside(const Rect_<_Tp>& r) const; + + _Tp x, y; //< the point coordinates + }; + + /*! + template 3D point class. + + The class defines a point in 3D space. Data type of the point coordinates is specified + as a template parameter. + + \see cv::Point3i, cv::Point3f and cv::Point3d + */ + template class Point3_ + { + public: + typedef _Tp value_type; + + // various constructors + Point3_(); + Point3_(_Tp _x, _Tp _y, _Tp _z); + Point3_(const Point3_& pt); + explicit Point3_(const Point_<_Tp>& pt); + Point3_(const CvPoint3D32f& pt); + Point3_(const Vec<_Tp, 3>& v); + + Point3_& operator = (const Point3_& pt); + //! conversion to another data type + template operator Point3_<_Tp2>() const; + //! conversion to the old-style CvPoint... + operator CvPoint3D32f() const; + //! conversion to cv::Vec<> + operator Vec<_Tp, 3>() const; + + //! dot product + _Tp dot(const Point3_& pt) const; + //! dot product computed in double-precision arithmetics + double ddot(const Point3_& pt) const; + //! cross product of the 2 3D points + Point3_ cross(const Point3_& pt) const; + + _Tp x, y, z; //< the point coordinates + }; + + //////////////////////////////// Size_ //////////////////////////////// + + /*! + The 2D size class + + The class represents the size of a 2D rectangle, image size, matrix size etc. + Normally, cv::Size ~ cv::Size_ is used. + */ + template class Size_ + { + public: + typedef _Tp value_type; + + //! various constructors + Size_(); + Size_(_Tp _width, _Tp _height); + Size_(const Size_& sz); + Size_(const CvSize& sz); + Size_(const CvSize2D32f& sz); + Size_(const Point_<_Tp>& pt); + + Size_& operator = (const Size_& sz); + //! the area (width*height) + _Tp area() const; + + //! conversion of another data type. + template operator Size_<_Tp2>() const; + + //! conversion to the old-style OpenCV types + operator CvSize() const; + operator CvSize2D32f() const; + + _Tp width, height; // the width and the height + }; + + //////////////////////////////// Rect_ //////////////////////////////// + + /*! + The 2D up-right rectangle class + + The class represents a 2D rectangle with coordinates of the specified data type. + Normally, cv::Rect ~ cv::Rect_ is used. + */ + template class Rect_ + { + public: + typedef _Tp value_type; + + //! various constructors + Rect_(); + Rect_(_Tp _x, _Tp _y, _Tp _width, _Tp _height); + Rect_(const Rect_& r); + Rect_(const CvRect& r); + Rect_(const Point_<_Tp>& org, const Size_<_Tp>& sz); + Rect_(const Point_<_Tp>& pt1, const Point_<_Tp>& pt2); + + Rect_& operator = ( const Rect_& r ); + //! the top-left corner + Point_<_Tp> tl() const; + //! the bottom-right corner + Point_<_Tp> br() const; + + //! size (width, height) of the rectangle + Size_<_Tp> size() const; + //! area (width*height) of the rectangle + _Tp area() const; + + //! conversion to another data type + template operator Rect_<_Tp2>() const; + //! conversion to the old-style CvRect + operator CvRect() const; + + //! checks whether the rectangle contains the point + bool contains(const Point_<_Tp>& pt) const; + + _Tp x, y, width, height; //< the top-left corner, as well as width and height of the rectangle + }; + + + %template(GcvSize2i) Size_; + %template(GcvSize2f32_) Size_; + %template(GcvSize2f64_) Size_; + + %template(GcvRect) Rect_; + + %template(GcvPoint2i) Point_; + %template(GcvPoint2f32_) Point_; + %template(GcvPoint2f64_) Point_; + + %template(GcvPoint3i) Point3_; + %template(GcvPoint3f32_) Point3_; + %template(GcvPoint3f64_) Point3_; + + + /* ----------------- Mat ----------------- */ + %rename(GcvMat) Mat; + + class Mat + { + public: + //! default constructor + Mat(); + //! constructs 2D matrix of the specified size and type + // (_type is CV_8UC1, CV_64FC3, CV_32SC(12) etc.) + Mat(int rows, int cols, int type); + Mat(cv::Size size, int type); + //! constucts 2D matrix and fills it with the specified value _s. + Mat(int rows, int cols, int type, const cv::Scalar& s); + Mat(cv::Size size, int type, const cv::Scalar& s); + + //! copy constructor + Mat(const Mat& m); + + //! builds matrix from std::vector with or without copying the data + template explicit Mat(const vector<_Tp>& vec, bool copyData=false); + //! builds matrix from cv::Vec; the data is copied by default + template explicit Mat(const Vec<_Tp, n>& vec, bool copyData=true); + //! builds matrix from cv::Matx; the data is copied by default + template explicit Mat(const Matx<_Tp, m, n>& mtx, bool copyData=true); + //! builds matrix from a 2D point + template explicit Mat(const Point_<_Tp>& pt, bool copyData=true); + //! builds matrix from a 3D point + template explicit Mat(const Point3_<_Tp>& pt, bool copyData=true); + + //! destructor - calls release() + ~Mat(); + + //! returns a new matrix header for the specified row + Mat row(int y) const; + //! returns a new matrix header for the specified column + Mat col(int x) const; + //! ... for the specified row span + Mat rowRange(int startrow, int endrow) const; + //! ... for the specified column span + Mat colRange(int startcol, int endcol) const; + //! ... for the specified diagonal + // (d=0 - the main diagonal, + // >0 - a diagonal from the lower half, + // <0 - a diagonal from the upper half) + Mat diag(int d=0) const; + //! constructs a square diagonal matrix which main diagonal is vector "d" + static Mat diag(const Mat& d); + + //! returns deep copy of the matrix, i.e. the data is copied + Mat clone() const; + + void assignTo( Mat& m, int type=-1 ) const; + + //! creates alternative matrix header for the same data, with different + // number of channels and/or different number of rows. see cvReshape. + Mat reshape(int cn, int rows=0) const; + Mat reshape(int cn, int newndims, const int* newsz) const; + + //! adds element to the end of 1d matrix (or possibly multiple elements when _Tp=Mat) + template void push_back(const _Tp& elem); + template void push_back(const Mat_<_Tp>& elem); + void push_back(const Mat& m); + //! removes several hyper-planes from bottom of the matrix + void pop_back(size_t nelems=1); + + //! special versions for 2D arrays (especially convenient for referencing image pixels) + + template _Tp& at(int i0=0); + template const _Tp& at(int i0=0) const; + + template _Tp& at(int i0, int i1); + template const _Tp& at(int i0, int i1) const; + + template _Tp& at(int i0, int i1, int i2); + template const _Tp& at(int i0, int i1, int i2) const; + + template _Tp& at(const int* idx); + template const _Tp& at(const int* idx) const; + + template _Tp& at(const Vec& idx); + template const _Tp& at(const Vec& idx) const; + template _Tp& at(cv::Point pt); + template const _Tp& at(cv::Point pt) const; + + %template(gcvAtf32) at; + %template(gcvAtf64) at; + + /*! includes several bit-fields: + - the magic signature + - continuity flag + - depth + - number of channels + */ + int flags; + //! the matrix dimensionality, >= 2 + int dims; + //! the number of rows and columns or (-1, -1) when the matrix has more than 2 dimensions + int rows, cols; + }; +} + +/* Additional STL types */ +namespace std { + %template(GcvPoint3f32Vector) vector; + %template(GcvPoint2f32Vector) vector; + + %template(GcvIntVector) vector; + %template(GcvFloat32Vector) vector; + %template(GcvFloat64Vector) vector; +}; diff --git a/gocv/gocv_core_test.go b/gocv/gocv_core_test.go new file mode 100644 index 0000000..2b0be85 --- /dev/null +++ b/gocv/gocv_core_test.go @@ -0,0 +1,38 @@ +package gocv + +import ( + "testing" + + "github.com/davecgh/go-spew/spew" + "github.com/gonum/matrix/mat64" +) + +func TestNewGcvPoint3f32(t *testing.T) { + pt := NewGcvPoint3f32(3, 1) + spew.Dump(pt) +} + +func TestNewGcvPoint2f32(t *testing.T) { + pt := NewGcvPoint2f32(3, 1) + spew.Dump(pt) +} + +func TestNewGcvSize2f64(t *testing.T) { + size := NewGcvSize2f64(3, 1) + spew.Dump(size) +} + +func TestMat(t *testing.T) { + mat := NewGcvMat() + mat2 := NewGcvMat(mat) + spew.Dump(mat2) +} + +func TestMat64ToGcvMat(t *testing.T) { + mat := mat64.NewDense(3, 2, []float64{ + 0, 1, + 1.23, 4, + -12.3, -4, + }) + spew.Dump(Mat64ToGcvMat(mat)) +} diff --git a/gocv/gocv_test.go b/gocv/gocv_test.go deleted file mode 100644 index f1a52e8..0000000 --- a/gocv/gocv_test.go +++ /dev/null @@ -1,69 +0,0 @@ -package gocv - -import ( - "testing" - - "github.com/davecgh/go-spew/spew" -) - -func TestNewGcvPoint3f(t *testing.T) { - pt := NewGcvPoint3f(3, 1, 2) - spew.Dump(pt) -} - -func TestNewGcvPoint2f(t *testing.T) { - pt := NewGcvPoint2f(3, 1) - spew.Dump(pt) -} - -func TestNewGcvSize2d(t *testing.T) { - size := NewGcvSize2d(3, 1) - spew.Dump(size) -} - -func TestMat(t *testing.T) { - mat := NewMat() - mat2 := NewMat(mat) - spew.Dump(mat2) -} - -func TestGcvInitCameraMatrix2D(t *testing.T) { - objPts := NewGcvPoint3fVector(int64(4)) - objPts.Set(0, NewGcvPoint3f(0, 25, 0)) - objPts.Set(1, NewGcvPoint3f(0, -25, 0)) - objPts.Set(2, NewGcvPoint3f(-47, 25, 0)) - objPts.Set(3, NewGcvPoint3f(-47, -25, 0)) - - imgPts := NewGcvPoint2fVector(int64(4)) - imgPts.Set(0, NewGcvPoint2f(1136.4140625, 1041.89208984)) - imgPts.Set(1, NewGcvPoint2f(1845.33190918, 671.39581299)) - imgPts.Set(2, NewGcvPoint2f(302.73373413, 634.79998779)) - imgPts.Set(3, NewGcvPoint2f(1051.46154785, 352.76107788)) - - camMat := GcvInitCameraMatrix2D(objPts, imgPts) - spew.Dump(camMat.GcvAtd(NewGcvSize2i(0, 0))) - spew.Dump(camMat.GcvAtd(NewGcvSize2i(0, 1))) - spew.Dump(camMat.GcvAtd(NewGcvSize2i(1, 1))) - spew.Dump(camMat.GcvAtd(NewGcvSize2i(1, 2))) - spew.Dump(camMat.GcvAtd(NewGcvSize2i(2, 2))) -} - -func TestGcvCalibrateCamera(t *testing.T) { - objPts := NewGcvPoint3fVector(int64(4)) - objPts.Set(0, NewGcvPoint3f(0, 25, 0)) - objPts.Set(1, NewGcvPoint3f(0, -25, 0)) - objPts.Set(2, NewGcvPoint3f(-47, 25, 0)) - objPts.Set(3, NewGcvPoint3f(-47, -25, 0)) - - imgPts := NewGcvPoint2fVector(int64(4)) - imgPts.Set(0, NewGcvPoint2f(1136.4140625, 1041.89208984)) - imgPts.Set(1, NewGcvPoint2f(1845.33190918, 671.39581299)) - imgPts.Set(2, NewGcvPoint2f(302.73373413, 634.79998779)) - imgPts.Set(3, NewGcvPoint2f(1051.46154785, 352.76107788)) - - imgSize := NewGcvSize2i(1920, 1080) - - camMat := GcvInitCameraMatrix2D(objPts, imgPts) - - GcvCalibrateCamera(objPts, imgPts, imgSize, camMat) -}