我正在使用Python和OpenCV开发行人跟踪算法。
我们可以使用SIFT关键点作为帧上行人轮廓的标识符,然后在两组SIFT关键点之间(即在一帧和下一帧之间)执行强力匹配,以在下一帧中找到行人。为了在帧序列上可视化,我们可以绘制一个界定行人的边界矩形。这就是它的样子:
主要问题是使用关键点来表征行人的运动。这里的想法是使用2个连续帧上的关键点的坐标来找到仿射变换(即在x和y中的平移,旋转和缩放)。理想情况下,这种仿射变换在某种程度上对应于行人的运动。为了跟踪这个行人,我们只需要在边界矩形坐标上应用相同的仿射变换。最后一部分效果不佳。如下图或上图所示,矩形在几帧内不断缩小,不可避免地消失或偏离行人。
为了指定,我们用2个极值点来表征边界矩形:
有一些内置的cv2函数可以对图像应用仿射变换,比如cv2.warpAffine(),但我只想将它应用于边界矩形坐标(即2点或1点+宽度和高度)。
为了找到两组关键点之间的仿射变换,我编写了自己的函数(如果有帮助,我可以发布代码),但是在使用cv2.getAffineTransform()时我观察到类似的结果。
你知道如何正确地将仿射变换应用于这个边界矩形吗?
编辑:这里是一些更好的上下文的解释和代码:
hog.setSVMDetector(cv2.HOGDescriptor_getDefaultPeopleDetector())
&hog.detectMultiScale()
(xA, yA, w, h)
的坐标(我们在第一次检测后停止使用SVM,因为它非常慢,我们现在关注的是一个行人)image[yA: yA+h, xA: xA+w]
并使用surf.detectAndCompute()
搜索SURF关键点cv2.BFMatcher(cv2.NORM_L2, crossCheck=True)
previousKpts = previousKpts[:5] # select 4 best matches
currentKpts = currentKpts[:5]
# build A matrix of shape [2 * Nb of keypoints, 4]
A = np.ndarray(((2 * len(previousKpts), 4)))
for idx, keypoint in enumerate(previousKpts):
# Keypoint.pt = (x-coord, y-coord)
A[2 * idx, :] = [keypoint.pt[0], -keypoint.pt[1], 1, 0]
A[2 * idx + 1, :] = [keypoint.pt[1], keypoint.pt[0], 0, 1]
# build b matrix of shape [2 * Nb of keypoints, 1]
b = np.ndarray((2 * len(previousKpts), 1))
for idx, keypoint in enumerate(currentKpts):
b[2 * idx, :] = keypoint.pt[0]
b[2 * idx + 1, :] = keypoint.pt[1]
# convert the numpy.ndarrays to matrix :
A = np.matrix(A)
b = np.matrix(b)
# solution of the form x = [x1, x2, x3, x4]' = ((A' * A)^-1) * A' * b
x = np.linalg.inv(A.T * A) * A.T * b
theta = math.atan2(x[1, 0], x[0, 0]) # outputs rotation angle in [-pi, pi]
alpha = math.sqrt(x[0, 0] ** 2 + x[1, 0] ** 2) # scaling parameter
bx = x[2, 0] # translation along x-axis
by = x[3, 0] # translation along y-axis
return theta, alpha, bx, by
# define the 4 bounding points using xA, yA
xB = xA + w
yB = yA + h
rect_pts = np.array([[[xA, yA]], [[xB, yA]], [[xA, yB]], [[xB, yB]]], dtype=np.float32)
# warp the affine transform into a full perspective transform
affine_warp = np.array([[alpha*np.cos(theta), -alpha*np.sin(theta), tx],
[alpha*np.sin(theta), alpha*np.cos(theta), ty],
[0, 0, 1]], dtype=np.float32)
# apply affine transform
rect_pts = cv2.perspectiveTransform(rect_pts, affine_warp)
xA = rect_pts[0, 0, 0]
yA = rect_pts[0, 0, 1]
xB = rect_pts[3, 0, 0]
yB = rect_pts[3, 0, 1]
return xA, yA, xB, yB
(xA, yA, xB, yB)
,所有当前关键点和描述符,并迭代下一帧:使用我们之前保存的image[yA: yB, xA: xA]
选择(xA, yA, xB, yB)
,获取SURF关键点等。正如Micka所说,cv2.perspectiveTransform()
是实现这一目标的简单方法。你只需要通过在底部添加第三行并使用值[0, 0, 1]
将你的仿射变形转换为全透视变换(单应性)。例如,让我们在点w, h = 100, 200
放置一个带(10, 20)
的框,然后使用仿射变换移动点,使框移动到(0, 0)
(即向左移10个像素,向上移20个像素):
>>> xA, yA, w, h = (10, 20, 100, 200)
>>> xB, yB = xA + w, yA + h
>>> rect_pts = np.array([[[xA, yA]], [[xB, yA]], [[xA, yB]], [[xB, yB]]], dtype=np.float32)
>>> affine_warp = np.array([[1, 0, -10], [0, 1, -20], [0, 0, 1]], dtype=np.float32)
>>> cv2.perspectiveTransform(rect_pts, affine_warp)
array([[[ 0., 0.]],
[[ 100., 0.]],
[[ 0., 200.]],
[[ 100., 200.]]], dtype=float32)
因此,按预期完美运行。您也可以通过矩阵乘法简单地转换点:
>>> rect_pts.dot(affine_warp[:, :2]) + affine_warp[:, 2]
array([[[ 0., 0.]],
[[ 100., 0.]],
[[ 0., 200.]],
[[ 100., 200.]]], dtype=float32)