From 8610ff1b4d0e9618fcade06a20395c9f8c1937ef Mon Sep 17 00:00:00 2001 From: Alexander Mordvintsev Date: Mon, 16 Jul 2012 11:31:43 +0000 Subject: [PATCH] find_obj.py allows to select detector and matcher and explore matching pairs --- samples/python2/find_obj.py | 145 ++++++++++++++++++++++++++---------- 1 file changed, 107 insertions(+), 38 deletions(-) diff --git a/samples/python2/find_obj.py b/samples/python2/find_obj.py index ae054e8444..34e6da1df9 100644 --- a/samples/python2/find_obj.py +++ b/samples/python2/find_obj.py @@ -2,18 +2,62 @@ Feature-based image matching sample. USAGE - find_obj.py [ ] + find_obj.py [--feature=[-flann]] [ ] + + --feature - Feature to use. Can be sift, surf of orb. Append '-flann' to feature name + to use Flann-based matcher instead bruteforce. + + Press left mouse button on a feature point to see its mathcing point. + ''' import numpy as np import cv2 +from common import anorm, getsize FLANN_INDEX_KDTREE = 1 # bug: flann enums are missing - -flann_params = dict(algorithm = FLANN_INDEX_KDTREE, trees = 5) +FLANN_INDEX_LSH = 6 -def draw_match(img1, img2, p1, p2, status = None, H = None): +def init_feature(name): + detector, matcher = None, None + chunks = name.split('-') + if chunks[0] == 'sift': + detector = cv2.SIFT() + norm = cv2.NORM_L2 + elif chunks[0] == 'surf': + detector = cv2.SURF(1000) + norm = cv2.NORM_L2 + elif chunks[0] == 'orb': + detector = cv2.ORB(500) + norm = cv2.NORM_HAMMING + if 'flann' in chunks: + if norm == cv2.NORM_L2: + flann_params = dict(algorithm = FLANN_INDEX_KDTREE, trees = 5) + else: + flann_params= dict(algorithm = FLANN_INDEX_LSH, + table_number = 6, # 12 + key_size = 12, # 20 + multi_probe_level = 1) #2 + matcher = cv2.FlannBasedMatcher(flann_params, {}) # bug : need to pass empty dict (#1329) + else: + matcher = cv2.BFMatcher(norm) + return detector, matcher + + +def filter_matches(kp1, kp2, matches, ratio = 0.75): + mkp1, mkp2 = [], [] + for m in matches: + if len(m) == 2 and m[0].distance < m[1].distance * ratio: + m = m[0] + mkp1.append( kp1[m.queryIdx] ) + mkp2.append( kp2[m.trainIdx] ) + p1 = np.float32([kp.pt for kp in mkp1]) + p2 = np.float32([kp.pt for kp in mkp2]) + kp_pairs = zip(mkp1, mkp2) + return p1, p2, kp_pairs + +def explore_match(win, img1, img2, kp_pairs, status = None, H = None): h1, w1 = img1.shape[:2] h2, w2 = img2.shape[:2] vis = np.zeros((max(h1, h2), w1+w2), np.uint8) @@ -25,71 +69,96 @@ def draw_match(img1, img2, p1, p2, status = None, H = None): corners = np.float32([[0, 0], [w1, 0], [w1, h1], [0, h1]]) corners = np.int32( cv2.perspectiveTransform(corners.reshape(1, -1, 2), H).reshape(-1, 2) + (w1, 0) ) cv2.polylines(vis, [corners], True, (255, 255, 255)) - + if status is None: status = np.ones(len(p1), np.bool_) + p1 = np.int32([kpp[0].pt for kpp in kp_pairs]) + p2 = np.int32([kpp[1].pt for kpp in kp_pairs]) + (w1, 0) + green = (0, 255, 0) red = (0, 0, 255) - for (x1, y1), (x2, y2), inlier in zip(np.int32(p1), np.int32(p2), status): - col = [red, green][inlier] + white = (255, 255, 255) + kp_color = (51, 103, 236) + for (x1, y1), (x2, y2), inlier in zip(p1, p2, status): if inlier: - cv2.line(vis, (x1, y1), (x2+w1, y2), col) + col = green cv2.circle(vis, (x1, y1), 2, col, -1) - cv2.circle(vis, (x2+w1, y2), 2, col, -1) + cv2.circle(vis, (x2, y2), 2, col, -1) else: + col = red r = 2 thickness = 3 cv2.line(vis, (x1-r, y1-r), (x1+r, y1+r), col, thickness) cv2.line(vis, (x1-r, y1+r), (x1+r, y1-r), col, thickness) - cv2.line(vis, (x2+w1-r, y2-r), (x2+w1+r, y2+r), col, thickness) - cv2.line(vis, (x2+w1-r, y2+r), (x2+w1+r, y2-r), col, thickness) - return vis + cv2.line(vis, (x2-r, y2-r), (x2+r, y2+r), col, thickness) + cv2.line(vis, (x2-r, y2+r), (x2+r, y2-r), col, thickness) + vis0 = vis.copy() + for (x1, y1), (x2, y2), inlier in zip(p1, p2, status): + if inlier: + cv2.line(vis, (x1, y1), (x2, y2), green) + + cv2.imshow(win, vis) + def onmouse(event, x, y, flags, param): + cur_vis = vis + if flags & cv2.EVENT_FLAG_LBUTTON: + cur_vis = vis0.copy() + r = 8 + m = (anorm(p1 - (x, y)) < r) | (anorm(p2 - (x, y)) < r) + idxs = np.where(m)[0] + kp1s, kp2s = [], [] + for i in idxs: + (x1, y1), (x2, y2) = p1[i], p2[i] + col = (red, green)[status[i]] + cv2.line(cur_vis, (x1, y1), (x2, y2), col) + kp1, kp2 = kp_pairs[i] + kp1s.append(kp1) + kp2s.append(kp2) + cur_vis = cv2.drawKeypoints(cur_vis, kp1s, flags=4, color=kp_color) + cur_vis[:,w1:] = cv2.drawKeypoints(cur_vis[:,w1:], kp2s, flags=4, color=kp_color) + + cv2.imshow(win, cur_vis) + cv2.setMouseCallback(win, onmouse) if __name__ == '__main__': print __doc__ - import sys - try: fn1, fn2 = sys.argv[1:3] + import sys, getopt + opts, args = getopt.getopt(sys.argv[1:], '', ['feature=']) + opts = dict(opts) + feature_name = opts.get('--feature', 'sift') + try: fn1, fn2 = args except: fn1 = '../c/box.png' fn2 = '../c/box_in_scene.png' - + img1 = cv2.imread(fn1, 0) img2 = cv2.imread(fn2, 0) + detector, matcher = init_feature(feature_name) + if detector != None: + print 'using', feature_name + else: + print 'unknown feature:', feature_name + sys.exit(1) + - detector = cv2.SIFT() kp1, desc1 = detector.detectAndCompute(img1, None) kp2, desc2 = detector.detectAndCompute(img2, None) print 'img1 - %d features, img2 - %d features' % (len(kp1), len(kp2)) - bf_matcher = cv2.BFMatcher(cv2.NORM_L2) - flann_matcher = cv2.FlannBasedMatcher(flann_params, {}) # bug : need to pass empty dict (#1329) - - def match_and_draw(matcher, r_threshold = 0.75): - raw_matches = matcher.knnMatch(desc1, trainDescriptors = desc2, k = 2) - p1, p2 = [], [] - for m in raw_matches: - if len(m) == 2 and m[0].distance < m[1].distance * r_threshold: - m = m[0] - p1.append( kp1[m.queryIdx].pt ) - p2.append( kp2[m.trainIdx].pt ) - p1, p2 = np.float32((p1, p2)) + def match_and_draw(win): + print 'matching...' + raw_matches = matcher.knnMatch(desc1, trainDescriptors = desc2, k = 2) #2 + p1, p2, kp_pairs = filter_matches(kp1, kp2, raw_matches) if len(p1) >= 4: - H, status = cv2.findHomography(p1, p2, cv2.RANSAC, 2.0) + H, status = cv2.findHomography(p1, p2, cv2.RANSAC, 5.0) print '%d / %d inliers/matched' % (np.sum(status), len(status)) else: H, status = None, None print '%d matches found, not enough for homography estimation' % len(p1) - vis = draw_match(img1, img2, p1, p2, status, H) - return vis + vis = explore_match(win, img1, img2, kp_pairs, status, H) - print 'bruteforce match:', - vis_brute = match_and_draw( bf_matcher ) - print 'flann match:', - vis_flann = match_and_draw( flann_matcher ) - cv2.imshow('find_obj', vis_brute) - cv2.imshow('find_obj flann', vis_flann) - 0xFF & cv2.waitKey() + match_and_draw('find_obj') + cv2.waitKey() cv2.destroyAllWindows()