/
simple_panography.py
99 lines (77 loc) · 3.14 KB
/
simple_panography.py
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
from __future__ import print_function, division
import numpy
import cv2
from skimage.transform import SimilarityTransform
from skimage.measure import ransac
from scipy.optimize import leastsq
from lmfit import minimize, Parameters
img1 = cv2.imread('./data/yosemite1.jpg')
img2 = cv2.imread('./data/yosemite2.jpg')
gray1 = cv2.cvtColor(img1, cv2.COLOR_BGR2GRAY)
gray2 = cv2.cvtColor(img2, cv2.COLOR_BGR2GRAY)
sift = cv2.xfeatures2d.SIFT_create()
kp1, des1 = sift.detectAndCompute(gray1, None)
kp2, des2 = sift.detectAndCompute(gray2, None)
FLANN_INDEX_KDTREE = 0
index_params = dict(algorithm=FLANN_INDEX_KDTREE, trees=5)
search_params = dict(checks=50)
flann = cv2.FlannBasedMatcher(index_params, search_params)
matches = flann.knnMatch(des2, des1, k=2)
good = []
for m, n in matches:
if m.distance < 0.7*n.distance:
good.append(m)
src_pts = numpy.float32([kp2[m.queryIdx].pt for m in good]).reshape(-1, 2)
dst_pts = numpy.float32([kp1[m.trainIdx].pt for m in good]).reshape(-1, 2)
model_robust, inliers = ransac((src_pts, dst_pts), SimilarityTransform, min_samples=3,
residual_threshold=2, max_trials=100)
M1 = model_robust.params[:2, :]
# Try to use least square method to find M instead
src_pts = src_pts[inliers]
dst_pts = dst_pts[inliers]
def residuals(params):
global src_pts, dst_pts
a, b, tx, ty = params['a'], params['b'], params['tx'], params['ty']
M = numpy.float32([[a, b, tx], [-b, a, ty]])
one_column = numpy.ones((src_pts.shape[0], 1), dtype=numpy.float32)
_src_pts = numpy.hstack((src_pts, one_column))
res = _src_pts.dot(M.T) - dst_pts
res = res ** 2
res = numpy.sqrt(res[:, 0] + res[:, 1])
return res
params = Parameters()
params.add('a', 0.916515139)
params.add('b', 0.4)
params.add('tx', -1000.0)
params.add('ty', 3000.0)
out = minimize(residuals, params).params
a, b, tx, ty = out['a'], out['b'], out['tx'], out['ty']
M2 = numpy.float32([[a, b, tx], [-b, a, ty]])
def getPano(M, img1, img2):
rows, cols = img2.shape[:2]
# This is the tricky part. For transform, the point is identified as pair
# of (col, row) instead of (row, col)
box = numpy.array([[0, 0], [cols - 1, 0], [cols - 1, rows - 1],
[0, rows - 1]], dtype=numpy.float32).reshape(-1, 1, 2)
transformed_box = cv2.transform(box, M)
min_col = min(transformed_box[:, :, 0])[0]
min_row = min(transformed_box[:, :, 1])[0]
if min_col < 0:
transformed_box[:, :, 0] -= min_col
M[0, 2] -= min_col
if min_row < 0:
transformed_box[:, :, 1] -= min_row
M[1, 2] -= min_row
max_col = max(transformed_box[:, :, 0])[0]
max_row = max(transformed_box[:, :, 1])[0]
I = numpy.array([[1, 0, 0], [0, 1, 0]], dtype=numpy.float)
transformed_img1 = cv2.warpAffine(img1, I, (max_col, max_row))
transformed_img2 = cv2.warpAffine(img2, M, (max_col, max_row))
numpy.copyto(
transformed_img1, transformed_img2, where=transformed_img1 == 0)
return transformed_img1
M1_pano = getPano(M1, img1, img2)
M2_pano = getPano(M2, img1, img2)
cv2.imshow('With Skimage & OpenCV', M1_pano)
cv2.imshow('With M found by leastsq', M2_pano)
cv2.waitKey()