mirror of
https://github.com/wassname/scikit-image.git
synced 2026-06-28 03:51:05 +08:00
55f5103dd8
Modified travis_script.sh to account for the new structure of the gallery Added README.txt files in directories of gallery examples Fixed references to gallery images in user guide pages Fixed broken links
140 lines
5.0 KiB
Python
140 lines
5.0 KiB
Python
"""
|
|
============================
|
|
Robust matching using RANSAC
|
|
============================
|
|
|
|
In this simplified example we first generate two synthetic images as if they
|
|
were taken from different view points.
|
|
|
|
In the next step we find interest points in both images and find
|
|
correspondences based on a weighted sum of squared differences of a small
|
|
neighborhood around them. Note, that this measure is only robust towards
|
|
linear radiometric and not geometric distortions and is thus only usable with
|
|
slight view point changes.
|
|
|
|
After finding the correspondences we end up having a set of source and
|
|
destination coordinates which can be used to estimate the geometric
|
|
transformation between both images. However, many of the correspondences are
|
|
faulty and simply estimating the parameter set with all coordinates is not
|
|
sufficient. Therefore, the RANSAC algorithm is used on top of the normal model
|
|
to robustly estimate the parameter set by detecting outliers.
|
|
|
|
"""
|
|
from __future__ import print_function
|
|
|
|
import numpy as np
|
|
from matplotlib import pyplot as plt
|
|
|
|
from skimage import data
|
|
from skimage.util import img_as_float
|
|
from skimage.feature import (corner_harris, corner_subpix, corner_peaks,
|
|
plot_matches)
|
|
from skimage.transform import warp, AffineTransform
|
|
from skimage.exposure import rescale_intensity
|
|
from skimage.color import rgb2gray
|
|
from skimage.measure import ransac
|
|
|
|
|
|
# generate synthetic checkerboard image and add gradient for the later matching
|
|
checkerboard = img_as_float(data.checkerboard())
|
|
img_orig = np.zeros(list(checkerboard.shape) + [3])
|
|
img_orig[..., 0] = checkerboard
|
|
gradient_r, gradient_c = (np.mgrid[0:img_orig.shape[0],
|
|
0:img_orig.shape[1]]
|
|
/ float(img_orig.shape[0]))
|
|
img_orig[..., 1] = gradient_r
|
|
img_orig[..., 2] = gradient_c
|
|
img_orig = rescale_intensity(img_orig)
|
|
img_orig_gray = rgb2gray(img_orig)
|
|
|
|
# warp synthetic image
|
|
tform = AffineTransform(scale=(0.9, 0.9), rotation=0.2, translation=(20, -10))
|
|
img_warped = warp(img_orig, tform.inverse, output_shape=(200, 200))
|
|
img_warped_gray = rgb2gray(img_warped)
|
|
|
|
# extract corners using Harris' corner measure
|
|
coords_orig = corner_peaks(corner_harris(img_orig_gray), threshold_rel=0.001,
|
|
min_distance=5)
|
|
coords_warped = corner_peaks(corner_harris(img_warped_gray),
|
|
threshold_rel=0.001, min_distance=5)
|
|
|
|
# determine sub-pixel corner position
|
|
coords_orig_subpix = corner_subpix(img_orig_gray, coords_orig, window_size=9)
|
|
coords_warped_subpix = corner_subpix(img_warped_gray, coords_warped,
|
|
window_size=9)
|
|
|
|
|
|
def gaussian_weights(window_ext, sigma=1):
|
|
y, x = np.mgrid[-window_ext:window_ext+1, -window_ext:window_ext+1]
|
|
g = np.zeros(y.shape, dtype=np.double)
|
|
g[:] = np.exp(-0.5 * (x**2 / sigma**2 + y**2 / sigma**2))
|
|
g /= 2 * np.pi * sigma * sigma
|
|
return g
|
|
|
|
|
|
def match_corner(coord, window_ext=5):
|
|
r, c = np.round(coord).astype(np.intp)
|
|
window_orig = img_orig[r-window_ext:r+window_ext+1,
|
|
c-window_ext:c+window_ext+1, :]
|
|
|
|
# weight pixels depending on distance to center pixel
|
|
weights = gaussian_weights(window_ext, 3)
|
|
weights = np.dstack((weights, weights, weights))
|
|
|
|
# compute sum of squared differences to all corners in warped image
|
|
SSDs = []
|
|
for cr, cc in coords_warped:
|
|
window_warped = img_warped[cr-window_ext:cr+window_ext+1,
|
|
cc-window_ext:cc+window_ext+1, :]
|
|
SSD = np.sum(weights * (window_orig - window_warped)**2)
|
|
SSDs.append(SSD)
|
|
|
|
# use corner with minimum SSD as correspondence
|
|
min_idx = np.argmin(SSDs)
|
|
return coords_warped_subpix[min_idx]
|
|
|
|
|
|
# find correspondences using simple weighted sum of squared differences
|
|
src = []
|
|
dst = []
|
|
for coord in coords_orig_subpix:
|
|
src.append(coord)
|
|
dst.append(match_corner(coord))
|
|
src = np.array(src)
|
|
dst = np.array(dst)
|
|
|
|
|
|
# estimate affine transform model using all coordinates
|
|
model = AffineTransform()
|
|
model.estimate(src, dst)
|
|
|
|
# robustly estimate affine transform model with RANSAC
|
|
model_robust, inliers = ransac((src, dst), AffineTransform, min_samples=3,
|
|
residual_threshold=2, max_trials=100)
|
|
outliers = inliers == False
|
|
|
|
|
|
# compare "true" and estimated transform parameters
|
|
print(tform.scale, tform.translation, tform.rotation)
|
|
print(model.scale, model.translation, model.rotation)
|
|
print(model_robust.scale, model_robust.translation, model_robust.rotation)
|
|
|
|
# visualize correspondence
|
|
fig, ax = plt.subplots(nrows=2, ncols=1)
|
|
|
|
plt.gray()
|
|
|
|
inlier_idxs = np.nonzero(inliers)[0]
|
|
plot_matches(ax[0], img_orig_gray, img_warped_gray, src, dst,
|
|
np.column_stack((inlier_idxs, inlier_idxs)), matches_color='b')
|
|
ax[0].axis('off')
|
|
ax[0].set_title('Correct correspondences')
|
|
|
|
outlier_idxs = np.nonzero(outliers)[0]
|
|
plot_matches(ax[1], img_orig_gray, img_warped_gray, src, dst,
|
|
np.column_stack((outlier_idxs, outlier_idxs)), matches_color='r')
|
|
ax[1].axis('off')
|
|
ax[1].set_title('Faulty correspondences')
|
|
|
|
plt.show()
|