From d50cc5107058678153882b7aa3c7bf07aa0ae4e8 Mon Sep 17 00:00:00 2001 From: Alexander Mordvintsev Date: Wed, 7 Sep 2011 09:38:22 +0000 Subject: [PATCH] handling Ptr<> arguments and return values in Python wrappers partially wrapped features2d framework added feature_homography.py sample --- .../include/opencv2/features2d/features2d.hpp | 56 +++++++------- modules/python/src2/cv2.cpp | 28 +++++++ modules/python/src2/gen2.py | 38 ++++++++-- samples/python2/feature_homography.py | 73 +++++++++++++++++++ 4 files changed, 161 insertions(+), 34 deletions(-) create mode 100644 samples/python2/feature_homography.py diff --git a/modules/features2d/include/opencv2/features2d/features2d.hpp b/modules/features2d/include/opencv2/features2d/features2d.hpp index b7281f8b61..eacf0d976a 100644 --- a/modules/features2d/include/opencv2/features2d/features2d.hpp +++ b/modules/features2d/include/opencv2/features2d/features2d.hpp @@ -1378,7 +1378,7 @@ protected: /* * Abstract base class for 2D image feature detectors. */ -class CV_EXPORTS FeatureDetector +class CV_EXPORTS_W FeatureDetector { public: virtual ~FeatureDetector(); @@ -1390,7 +1390,7 @@ public: * mask Mask specifying where to look for keypoints (optional). Must be a char * matrix with non-zero values in the region of interest. */ - void detect( const Mat& image, vector& keypoints, const Mat& mask=Mat() ) const; + CV_WRAP void detect( const Mat& image, CV_OUT vector& keypoints, const Mat& mask=Mat() ) const; /* * Detect keypoints in an image set. @@ -1406,10 +1406,10 @@ public: virtual void write( FileStorage& ) const; // Return true if detector object is empty - virtual bool empty() const; + CV_WRAP virtual bool empty() const; // Create feature detector by detector name. - static Ptr create( const string& detectorType ); + CV_WRAP static Ptr create( const string& detectorType ); protected: virtual void detectImpl( const Mat& image, vector& keypoints, const Mat& mask=Mat() ) const = 0; @@ -1422,10 +1422,10 @@ protected: static void removeInvalidPoints( const Mat& mask, vector& keypoints ); }; -class CV_EXPORTS FastFeatureDetector : public FeatureDetector +class CV_EXPORTS_W FastFeatureDetector : public FeatureDetector { public: - FastFeatureDetector( int threshold=10, bool nonmaxSuppression=true ); + CV_WRAP FastFeatureDetector( int threshold=10, bool nonmaxSuppression=true ); virtual void read( const FileNode& fn ); virtual void write( FileStorage& fs ) const; @@ -1673,11 +1673,11 @@ protected: * Adapts a detector to detect points over multiple levels of a Gaussian * pyramid. Useful for detectors that are not inherently scaled. */ -class CV_EXPORTS PyramidAdaptedFeatureDetector : public FeatureDetector +class CV_EXPORTS_W PyramidAdaptedFeatureDetector : public FeatureDetector { public: // maxLevel - The 0-based index of the last pyramid layer - PyramidAdaptedFeatureDetector( const Ptr& detector, int maxLevel=2 ); + CV_WRAP PyramidAdaptedFeatureDetector( const Ptr& detector, int maxLevel=2 ); // TODO implement read/write virtual bool empty() const; @@ -1835,7 +1835,7 @@ CV_EXPORTS Mat windowedMatchingMask( const vector& keypoints1, const v * distances between descriptors. Therefore we represent a collection of * descriptors as a cv::Mat, where each row is one keypoint descriptor. */ -class CV_EXPORTS DescriptorExtractor +class CV_EXPORTS_W DescriptorExtractor { public: virtual ~DescriptorExtractor(); @@ -1846,7 +1846,7 @@ public: * keypoints The input keypoints. Keypoints for which a descriptor cannot be computed are removed. * descriptors Copmputed descriptors. Row i is the descriptor for keypoint i. */ - void compute( const Mat& image, vector& keypoints, Mat& descriptors ) const; + CV_WRAP void compute( const Mat& image, CV_IN_OUT vector& keypoints, CV_OUT Mat& descriptors ) const; /* * Compute the descriptors for a keypoints collection detected in image collection. @@ -1860,12 +1860,12 @@ public: virtual void read( const FileNode& ); virtual void write( FileStorage& ) const; - virtual int descriptorSize() const = 0; - virtual int descriptorType() const = 0; + CV_WRAP virtual int descriptorSize() const = 0; + CV_WRAP virtual int descriptorType() const = 0; - virtual bool empty() const; + CV_WRAP virtual bool empty() const; - static Ptr create( const string& descriptorExtractorType ); + CV_WRAP static Ptr create( const string& descriptorExtractorType ); protected: virtual void computeImpl( const Mat& image, vector& keypoints, Mat& descriptors ) const = 0; @@ -2201,19 +2201,19 @@ struct CV_EXPORTS Hamming /* * Struct for matching: query descriptor index, train descriptor index, train image index and distance between descriptors. */ -struct CV_EXPORTS DMatch +struct CV_EXPORTS_W_SIMPLE DMatch { - DMatch() : queryIdx(-1), trainIdx(-1), imgIdx(-1), distance(std::numeric_limits::max()) {} - DMatch( int _queryIdx, int _trainIdx, float _distance ) : + CV_WRAP DMatch() : queryIdx(-1), trainIdx(-1), imgIdx(-1), distance(std::numeric_limits::max()) {} + CV_WRAP DMatch( int _queryIdx, int _trainIdx, float _distance ) : queryIdx(_queryIdx), trainIdx(_trainIdx), imgIdx(-1), distance(_distance) {} - DMatch( int _queryIdx, int _trainIdx, int _imgIdx, float _distance ) : + CV_WRAP DMatch( int _queryIdx, int _trainIdx, int _imgIdx, float _distance ) : queryIdx(_queryIdx), trainIdx(_trainIdx), imgIdx(_imgIdx), distance(_distance) {} - int queryIdx; // query descriptor index - int trainIdx; // train descriptor index - int imgIdx; // train image index + CV_PROP_RW int queryIdx; // query descriptor index + CV_PROP_RW int trainIdx; // train descriptor index + CV_PROP_RW int imgIdx; // train image index - float distance; + CV_PROP_RW float distance; // less is better bool operator<( const DMatch &m ) const @@ -2228,7 +2228,7 @@ struct CV_EXPORTS DMatch /* * Abstract base class for matching two sets of descriptors. */ -class CV_EXPORTS DescriptorMatcher +class CV_EXPORTS_W DescriptorMatcher { public: virtual ~DescriptorMatcher(); @@ -2273,14 +2273,14 @@ public: * Method train() is run in this methods. */ // Find one best match for each query descriptor (if mask is empty). - void match( const Mat& queryDescriptors, const Mat& trainDescriptors, - vector& matches, const Mat& mask=Mat() ) const; + CV_WRAP void match( const Mat& queryDescriptors, const Mat& trainDescriptors, + CV_OUT vector& matches, const Mat& mask=Mat() ) const; // Find k best matches for each query descriptor (in increasing order of distances). // compactResult is used when mask is not empty. If compactResult is false matches // vector will have the same size as queryDescriptors rows. If compactResult is true // matches vector will not contain matches for fully masked out query descriptors. - void knnMatch( const Mat& queryDescriptors, const Mat& trainDescriptors, - vector >& matches, int k, + CV_WRAP void knnMatch( const Mat& queryDescriptors, const Mat& trainDescriptors, + CV_OUT vector >& matches, int k, const Mat& mask=Mat(), bool compactResult=false ) const; // Find best matches for each query descriptor which have distance less than // maxDistance (in increasing order of distances). @@ -2308,7 +2308,7 @@ public: // but with empty train data. virtual Ptr clone( bool emptyTrainData=false ) const = 0; - static Ptr create( const string& descriptorMatcherType ); + CV_WRAP static Ptr create( const string& descriptorMatcherType ); protected: /* * Class to work with descriptors from several images as with one merged matrix. diff --git a/modules/python/src2/cv2.cpp b/modules/python/src2/cv2.cpp index f99f6524a2..0dc07c29d8 100644 --- a/modules/python/src2/cv2.cpp +++ b/modules/python/src2/cv2.cpp @@ -64,9 +64,15 @@ typedef vector vector_Vec4i; typedef vector vector_Rect; typedef vector vector_KeyPoint; typedef vector vector_Mat; +typedef vector vector_DMatch; typedef vector > vector_vector_Point; typedef vector > vector_vector_Point2f; typedef vector > vector_vector_Point3f; +typedef vector > vector_vector_DMatch; + +typedef Ptr Ptr_FeatureDetector; +typedef Ptr Ptr_DescriptorExtractor; +typedef Ptr Ptr_DescriptorMatcher; static PyObject* failmsgp(const char *fmt, ...) { @@ -312,6 +318,14 @@ static PyObject* pyopencv_from(size_t value) return PyLong_FromUnsignedLong((unsigned long)value); } +static bool pyopencv_to(PyObject* obj, size_t& value, const char* name = "") +{ + if(!obj || obj == Py_None) + return true; + value = (int)PyLong_AsUnsignedLong(obj); + return value != -1 || !PyErr_Occurred(); +} + static PyObject* pyopencv_from(int value) { return PyInt_FromLong(value); @@ -605,6 +619,7 @@ template static inline PyObject* pyopencv_from(const vector<_Tp>& } static PyObject* pyopencv_from(const KeyPoint&); +static PyObject* pyopencv_from(const DMatch&); template static inline bool pyopencv_to_generic_vec(PyObject* obj, vector<_Tp>& value, const char* name="") { @@ -689,6 +704,19 @@ template<> struct pyopencvVecConverter } }; +template<> struct pyopencvVecConverter +{ + static bool to(PyObject* obj, vector& value, const char* name="") + { + return pyopencv_to_generic_vec(obj, value, name); + } + + static PyObject* from(const vector& value) + { + return pyopencv_from_generic_vec(value); + } +}; + static inline bool pyopencv_to(PyObject *obj, CvTermCriteria& dst, const char *name="") { diff --git a/modules/python/src2/gen2.py b/modules/python/src2/gen2.py index 274d7cef0a..4b9ecadd70 100644 --- a/modules/python/src2/gen2.py +++ b/modules/python/src2/gen2.py @@ -7,7 +7,11 @@ gen_template_check_self = Template(""" if(!PyObject_TypeCheck(self, &pyopencv """) gen_template_call_constructor = Template("""self = PyObject_NEW(pyopencv_${name}_t, &pyopencv_${name}_Type); - if(self) ERRWRAP2(self->v = $op$cname""") + new (&(self->v)) Ptr<$cname>(); // init Ptr with placement new + if(self) ERRWRAP2(self->v = new $cname""") + +gen_template_simple_call_constructor = Template("""self = PyObject_NEW(pyopencv_${name}_t, &pyopencv_${name}_Type); + if(self) ERRWRAP2(self->v = $cname""") gen_template_parse_args = Template("""const char* keywords[] = { $kw_list, NULL }; if( PyArg_ParseTupleAndKeywords(args, kw, "$fmtspec", (char**)keywords, $parse_arglist)$code_cvt )""") @@ -66,7 +70,7 @@ gen_template_type_decl = Template(""" struct pyopencv_${name}_t { PyObject_HEAD - ${cname}* v; + Ptr<${cname}> v; }; static PyTypeObject pyopencv_${name}_Type = @@ -79,9 +83,31 @@ static PyTypeObject pyopencv_${name}_Type = static void pyopencv_${name}_dealloc(PyObject* self) { - delete ((pyopencv_${name}_t*)self)->v; + ((pyopencv_${name}_t*)self)->v = NULL; PyObject_Del(self); } + +static PyObject* pyopencv_from(const Ptr<${cname}>& r) +{ + pyopencv_${name}_t *m = PyObject_NEW(pyopencv_${name}_t, &pyopencv_${name}_Type); + new (&(m->v)) Ptr<$cname>(); // init Ptr with placement new + m->v = r; + return (PyObject*)m; +} + +static bool pyopencv_to(PyObject* src, Ptr<${cname}>& dst, const char* name="") +{ + if( src == NULL || src == Py_None ) + return true; + if(!PyObject_TypeCheck(src, &pyopencv_${name}_Type)) + { + failmsg("Expected ${cname} for argument '%s'", name); + return false; + } + dst = ((pyopencv_${name}_t*)src)->v; + return true; +} + """) gen_template_map_type_cvt = Template(""" @@ -490,10 +516,10 @@ class FuncInfo(object): if self.isconstructor: code_decl += " pyopencv_%s_t* self = 0;\n" % selfinfo.name - op = "new " + templ = gen_template_call_constructor if selfinfo.issimple: - op = "" - code_fcall = gen_template_call_constructor.substitute(name=selfinfo.name, cname=selfinfo.cname, op=op) + templ = gen_template_simple_call_constructor + code_fcall = templ.substitute(name=selfinfo.name, cname=selfinfo.cname) else: code_fcall = "ERRWRAP2( " if v.rettype: diff --git a/samples/python2/feature_homography.py b/samples/python2/feature_homography.py new file mode 100644 index 0000000000..00e72d4c02 --- /dev/null +++ b/samples/python2/feature_homography.py @@ -0,0 +1,73 @@ +''' +Feature homography +================== + +Example of using features2d framework for interactive video homography matching. + +Keys +---- +SPACE - set reference frame +ESC - exit +''' + +import numpy as np +import cv2 +import video +from common import draw_str + +if __name__ == '__main__': + + print __doc__ + + detector = cv2.FeatureDetector_create('ORB') + extractor = cv2.DescriptorExtractor_create('ORB') + matcher = cv2.DescriptorMatcher_create('BruteForce-Hamming') # 'BruteForce-Hamming' # FlannBased + + ref_desc = None + ref_kp = None + + green, red = (0, 255, 0), (0, 0, 255) + + cap = video.create_capture(0) + while True: + ret, img = cap.read() + vis = img.copy() + kp = detector.detect(img) + + for p in kp: + x, y = np.int32(p.pt) + r = int(0.5*p.size) + cv2.circle(vis, (x, y), r, (0, 255, 0)) + draw_str(vis, (20, 20), 'feature_n: %d' % len(kp)) + + desc = extractor.compute(img, kp) + if ref_desc is not None: + raw_matches = matcher.knnMatch(desc, ref_desc, 2) + eps = 1e-5 + matches = [(m1.trainIdx, m1.queryIdx) for m1, m2 in raw_matches if (m1.distance+eps) / (m2.distance+eps) < 0.7] + match_n = len(matches) + + inliner_n = 0 + if match_n > 10: + p0 = np.float32( [ref_kp[i].pt for i, j in matches] ) + p1 = np.float32( [kp[j].pt for i, j in matches] ) + + H, status = cv2.findHomography(p0, p1, cv2.RANSAC, 10.0) + inlier_n = sum(status) + if inlier_n > 10: + for (x1, y1), (x2, y2), inlier in zip(np.int32(p0), np.int32(p1), status): + cv2.line(vis, (x1, y1), (x2, y2), (red, green)[inlier]) + + h, w = img.shape[:2] + overlay = cv2.warpPerspective(ref_img, H, (w, h)) + vis = cv2.addWeighted(vis, 0.5, overlay, 0.5, 0.0) + draw_str(vis, (20, 40), 'matched: %d ( %d outliers )' % (match_n, match_n-inlier_n)) + + cv2.imshow('img', vis) + ch = cv2.waitKey(1) + if ch == ord(' '): + ref_desc = desc + ref_kp = kp + ref_img = img.copy() + if ch == 27: + break