123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168 |
- #!/usr/bin/env python
- '''
- Affine invariant feature-based image matching sample.
- This sample is similar to find_obj.py, but uses the affine transformation
- space sampling technique, called ASIFT [1]. While the original implementation
- is based on SIFT, you can try to use SURF or ORB detectors instead. Homography RANSAC
- is used to reject outliers. Threading is used for faster affine sampling.
- [1] http://www.ipol.im/pub/algo/my_affine_sift/
- USAGE
- asift.py [--feature=<sift|surf|orb|brisk>[-flann]] [ <image1> <image2> ]
- --feature - Feature to use. Can be sift, surf, orb or brisk. Append '-flann'
- to feature name to use Flann-based matcher instead bruteforce.
- Press left mouse button on a feature point to see its matching point.
- '''
- # Python 2/3 compatibility
- from __future__ import print_function
- import numpy as np
- import cv2 as cv
- # built-in modules
- import itertools as it
- from multiprocessing.pool import ThreadPool
- # local modules
- from common import Timer
- from find_obj import init_feature, filter_matches, explore_match
- def affine_skew(tilt, phi, img, mask=None):
- '''
- affine_skew(tilt, phi, img, mask=None) -> skew_img, skew_mask, Ai
- Ai - is an affine transform matrix from skew_img to img
- '''
- h, w = img.shape[:2]
- if mask is None:
- mask = np.zeros((h, w), np.uint8)
- mask[:] = 255
- A = np.float32([[1, 0, 0], [0, 1, 0]])
- if phi != 0.0:
- phi = np.deg2rad(phi)
- s, c = np.sin(phi), np.cos(phi)
- A = np.float32([[c,-s], [ s, c]])
- corners = [[0, 0], [w, 0], [w, h], [0, h]]
- tcorners = np.int32( np.dot(corners, A.T) )
- x, y, w, h = cv.boundingRect(tcorners.reshape(1,-1,2))
- A = np.hstack([A, [[-x], [-y]]])
- img = cv.warpAffine(img, A, (w, h), flags=cv.INTER_LINEAR, borderMode=cv.BORDER_REPLICATE)
- if tilt != 1.0:
- s = 0.8*np.sqrt(tilt*tilt-1)
- img = cv.GaussianBlur(img, (0, 0), sigmaX=s, sigmaY=0.01)
- img = cv.resize(img, (0, 0), fx=1.0/tilt, fy=1.0, interpolation=cv.INTER_NEAREST)
- A[0] /= tilt
- if phi != 0.0 or tilt != 1.0:
- h, w = img.shape[:2]
- mask = cv.warpAffine(mask, A, (w, h), flags=cv.INTER_NEAREST)
- Ai = cv.invertAffineTransform(A)
- return img, mask, Ai
- def affine_detect(detector, img, mask=None, pool=None):
- '''
- affine_detect(detector, img, mask=None, pool=None) -> keypoints, descrs
- Apply a set of affine transformations to the image, detect keypoints and
- reproject them into initial image coordinates.
- See http://www.ipol.im/pub/algo/my_affine_sift/ for the details.
- ThreadPool object may be passed to speedup the computation.
- '''
- params = [(1.0, 0.0)]
- for t in 2**(0.5*np.arange(1,6)):
- for phi in np.arange(0, 180, 72.0 / t):
- params.append((t, phi))
- def f(p):
- t, phi = p
- timg, tmask, Ai = affine_skew(t, phi, img)
- keypoints, descrs = detector.detectAndCompute(timg, tmask)
- for kp in keypoints:
- x, y = kp.pt
- kp.pt = tuple( np.dot(Ai, (x, y, 1)) )
- if descrs is None:
- descrs = []
- return keypoints, descrs
- keypoints, descrs = [], []
- if pool is None:
- ires = it.imap(f, params)
- else:
- ires = pool.imap(f, params)
- for i, (k, d) in enumerate(ires):
- print('affine sampling: %d / %d\r' % (i+1, len(params)), end='')
- keypoints.extend(k)
- descrs.extend(d)
- print()
- return keypoints, np.array(descrs)
- def main():
- import sys, getopt
- opts, args = getopt.getopt(sys.argv[1:], '', ['feature='])
- opts = dict(opts)
- feature_name = opts.get('--feature', 'brisk-flann')
- try:
- fn1, fn2 = args
- except:
- fn1 = 'aero1.jpg'
- fn2 = 'aero3.jpg'
- img1 = cv.imread(cv.samples.findFile(fn1), cv.IMREAD_GRAYSCALE)
- img2 = cv.imread(cv.samples.findFile(fn2), cv.IMREAD_GRAYSCALE)
- detector, matcher = init_feature(feature_name)
- if img1 is None:
- print('Failed to load fn1:', fn1)
- sys.exit(1)
- if img2 is None:
- print('Failed to load fn2:', fn2)
- sys.exit(1)
- if detector is None:
- print('unknown feature:', feature_name)
- sys.exit(1)
- print('using', feature_name)
- pool=ThreadPool(processes = cv.getNumberOfCPUs())
- kp1, desc1 = affine_detect(detector, img1, pool=pool)
- kp2, desc2 = affine_detect(detector, img2, pool=pool)
- print('img1 - %d features, img2 - %d features' % (len(kp1), len(kp2)))
- def match_and_draw(win):
- with Timer('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 = cv.findHomography(p1, p2, cv.RANSAC, 5.0)
- print('%d / %d inliers/matched' % (np.sum(status), len(status)))
- # do not draw outliers (there will be a lot of them)
- kp_pairs = [kpp for kpp, flag in zip(kp_pairs, status) if flag]
- else:
- H, status = None, None
- print('%d matches found, not enough for homography estimation' % len(p1))
- explore_match(win, img1, img2, kp_pairs, None, H)
- match_and_draw('affine find_obj')
- cv.waitKey()
- print('Done')
- if __name__ == '__main__':
- print(__doc__)
- main()
- cv.destroyAllWindows()
|