blob: c3934bd2c9ec40de194f6c30c01b31dce9173848 [file] [log] [blame]
# Copyright (c) 2012 The Chromium OS Authors. All rights reserved.
# Use of this source code is governed by a BSD-style license that can be
# found in the LICENSE file.
# Import guard for OpenCV.
try:
import cv
import cv2
except ImportError:
pass
import numpy as np
from camera_utils import Pad
from camera_utils import Unpad
_GRID_REGISTRATION_MAX_ITER_NUM = 5
_GRID_REGISTRATION_MIN_MATCH_RATIO = 0.20
def _ComputePairL2Sq(xy1, xy2):
'''Compute a pair-wise L2 square distance matrix.'''
d0 = np.subtract.outer(xy1[:, 0], xy2[:, 0])
d1 = np.subtract.outer(xy1[:, 1], xy2[:, 1])
return d0 ** 2 + d1 ** 2
def _MatchPoints(src, dst, match_tol):
'''Match two points sets based on the Euclidean distance.
Args:
src: Point set 1.
dst: Point set 2.
match_tol: Maximum acceptable distance between two points.
Returns:
1: The indexs that each point in src matches to in dst. None if a point
can't find any match.
'''
n_match = src.shape[0]
# We will work in the squared distance space.
match_tol **= 2
# Compute cost matrix.
cost_mat = _ComputePairL2Sq(src, dst)
# Assign points to nearest neighbors and return.
m = np.empty(n_match, dtype=np.uint32)
m.fill(n_match)
taken = np.zeros(n_match, dtype=np.uint8)
for i, row in enumerate(cost_mat):
current = match_tol
for j, value in enumerate(row):
if not taken[j] and value <= current:
m[i] = j
current = value
if m[i] == n_match:
return None
taken[j] = True
return m
def Register(tar_four_corners, tar_corners, ref_four_corners, ref_corners,
match_tol):
'''Register two rectangular grid point sets.
The function try to match two point sets with a prespective transformation.
The four corners of both point grids must be supplied. The algorithm will
iteratively re-match two point sets and estimate the corresponding
homography matrix. It returns failure if it can't succeed in a few
iterations or the iteration diverged.
Args:
tar_four_corners: Four corners of the target point grid.
tar_corners: The target point grid.
ref_four_corners: Four corners of the reference point grid.
ref_corners: The reference point grid.
match_tol: Maximum acceptable distance between two points.
Returns:
1: Succeed or not.
2: The estimated homography matrix.
3: The indexs that each point in the target image matches to in
reference one. None if a point can't find any match.
'''
# Stupid dimension extension to fit the opencv interface.
padded_tar_corners = Pad(tar_corners)
min_match_num = int(round(mapped.shape[0] *
_GRID_REGISTRATION_MIN_MATCH_RATIO))
min_match_num = max(4, min_match_num)
# Compute an initial homography.
homography, _ = cv2.findHomography(tar_four_corners, ref_four_corners)
# Iteratively register the point grid.
for i in range(0, _GRID_REGISTRATION_MAX_ITER_NUM):
# Map and match points.
mapped = cv2.perspectiveTransform(padded_tar_corners, homography)
mapped = Unpad(mapped)
matching = _MatchPoints(mapped, ref_corners, match_tol)
# Check if all points can find a close enough match.
if not matching:
return False, None, None
# Compute a new homography.
homography, mask = cv2.findHomography(mapped,
ref_corners[matching],
method=cv.CV_LMEDS)
# Check if all points fit the found homography or return failure
# in case the iteration diverged (too few point fitted).
if not homography or mask.sum() < min_match_num:
return False, None, None
if mask.all():
return True, homography, matching
return False, None, None