handling Ptr<> arguments and return values in Python wrappers

partially wrapped features2d framework
added feature_homography.py sample
This commit is contained in:
Alexander Mordvintsev 2011-09-07 09:38:22 +00:00
parent 2ef4e2eeb7
commit d50cc51070
4 changed files with 161 additions and 34 deletions

View File

@ -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<KeyPoint>& keypoints, const Mat& mask=Mat() ) const;
CV_WRAP void detect( const Mat& image, CV_OUT vector<KeyPoint>& 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<FeatureDetector> create( const string& detectorType );
CV_WRAP static Ptr<FeatureDetector> create( const string& detectorType );
protected:
virtual void detectImpl( const Mat& image, vector<KeyPoint>& keypoints, const Mat& mask=Mat() ) const = 0;
@ -1422,10 +1422,10 @@ protected:
static void removeInvalidPoints( const Mat& mask, vector<KeyPoint>& 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<FeatureDetector>& detector, int maxLevel=2 );
CV_WRAP PyramidAdaptedFeatureDetector( const Ptr<FeatureDetector>& detector, int maxLevel=2 );
// TODO implement read/write
virtual bool empty() const;
@ -1835,7 +1835,7 @@ CV_EXPORTS Mat windowedMatchingMask( const vector<KeyPoint>& 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<KeyPoint>& keypoints, Mat& descriptors ) const;
CV_WRAP void compute( const Mat& image, CV_IN_OUT vector<KeyPoint>& 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<DescriptorExtractor> create( const string& descriptorExtractorType );
CV_WRAP static Ptr<DescriptorExtractor> create( const string& descriptorExtractorType );
protected:
virtual void computeImpl( const Mat& image, vector<KeyPoint>& 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<float>::max()) {}
DMatch( int _queryIdx, int _trainIdx, float _distance ) :
CV_WRAP DMatch() : queryIdx(-1), trainIdx(-1), imgIdx(-1), distance(std::numeric_limits<float>::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<DMatch>& matches, const Mat& mask=Mat() ) const;
CV_WRAP void match( const Mat& queryDescriptors, const Mat& trainDescriptors,
CV_OUT vector<DMatch>& 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<vector<DMatch> >& matches, int k,
CV_WRAP void knnMatch( const Mat& queryDescriptors, const Mat& trainDescriptors,
CV_OUT vector<vector<DMatch> >& 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<DescriptorMatcher> clone( bool emptyTrainData=false ) const = 0;
static Ptr<DescriptorMatcher> create( const string& descriptorMatcherType );
CV_WRAP static Ptr<DescriptorMatcher> create( const string& descriptorMatcherType );
protected:
/*
* Class to work with descriptors from several images as with one merged matrix.

View File

@ -64,9 +64,15 @@ typedef vector<Vec4i> vector_Vec4i;
typedef vector<Rect> vector_Rect;
typedef vector<KeyPoint> vector_KeyPoint;
typedef vector<Mat> vector_Mat;
typedef vector<DMatch> vector_DMatch;
typedef vector<vector<Point> > vector_vector_Point;
typedef vector<vector<Point2f> > vector_vector_Point2f;
typedef vector<vector<Point3f> > vector_vector_Point3f;
typedef vector<vector<DMatch> > vector_vector_DMatch;
typedef Ptr<FeatureDetector> Ptr_FeatureDetector;
typedef Ptr<DescriptorExtractor> Ptr_DescriptorExtractor;
typedef Ptr<DescriptorMatcher> 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 = "<unknown>")
{
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<typename _Tp> static inline PyObject* pyopencv_from(const vector<_Tp>&
}
static PyObject* pyopencv_from(const KeyPoint&);
static PyObject* pyopencv_from(const DMatch&);
template<typename _Tp> static inline bool pyopencv_to_generic_vec(PyObject* obj, vector<_Tp>& value, const char* name="<unknown>")
{
@ -689,6 +704,19 @@ template<> struct pyopencvVecConverter<KeyPoint>
}
};
template<> struct pyopencvVecConverter<DMatch>
{
static bool to(PyObject* obj, vector<DMatch>& value, const char* name="<unknown>")
{
return pyopencv_to_generic_vec(obj, value, name);
}
static PyObject* from(const vector<DMatch>& value)
{
return pyopencv_from_generic_vec(value);
}
};
static inline bool pyopencv_to(PyObject *obj, CvTermCriteria& dst, const char *name="<unknown>")
{

View File

@ -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="<unknown>")
{
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:

View File

@ -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