如何检测图像之间的位移

23 投票
3 回答
21681 浏览
提问于 2025-04-18 13:28

我正在分析多张图片,需要判断这些图片相对于一张参考图像是否有偏移。这样做的目的是为了确定在拍摄这些图片时,摄像头是否有移动。我希望能够纠正这种偏移,以便继续进行分析,但至少我需要能够判断一张图片是否偏移,如果偏移超过某个阈值,就把它丢掉。

以下是我想要检测的图片偏移的几个例子:

参考图像偏移图像 1偏移图像 2

我会把第一张图作为参考,然后把后面的所有图和它进行比较,以判断它们是否有偏移。这些图片是灰度图(只是用热图的方式显示成彩色),并存储在一个二维的numpy数组中。有没有什么好的方法可以做到这一点?我希望能使用我已经安装的包(scipy、numpy、PIL、matplotlib)。

3 个回答

1

另一种解决方法是先在两张图片中计算出特征点,然后使用RANSAC算法来去掉一些不准确的点,最后再用最小二乘法来计算出图片之间的平移量。

2

正如Bharat所说,另一种方法是使用sift特征和Ransac算法:

import numpy as np
import cv2
from matplotlib import pyplot as plt

def crop_region(path, c_p):
    """
      This function crop the match region in the input image
      c_p: corner points
    """    
    # 3 or 4 channel as the original
    img = cv2.imread(path, -1)

    # mask 
    mask = np.zeros(img.shape, dtype=np.uint8) 

    # fill the the match region 
    channel_count = img.shape[2]  
    ignore_mask_color = (255,)*channel_count
    cv2.fillPoly(mask, c_p, ignore_mask_color)

    # apply the mask
    matched_region = cv2.bitwise_and(img, mask)

    return matched_region

def features_matching(path_temp,path_train):
    """
          Function for Feature Matching + Perspective Transformation
    """       
    img1 = cv2.imread(path_temp, 0)   # template
    img2 = cv2.imread(path_train, 0)   # input image

    min_match=10

    # SIFT detector
    sift = cv2.xfeatures2d.SIFT_create()

    # extract the keypoints and descriptors with SIFT

    kps1, des1 = sift.detectAndCompute(img1,None)
    kps2, des2 = sift.detectAndCompute(img2,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(des1, des2, k=2)

    # store all the good matches (g_matches) as per Lowe's ratio 
    g_match = []
    for m,n in matches:
        if m.distance < 0.7 * n.distance:
            g_match.append(m)
    if len(g_match)>min_match:
        src_pts = np.float32([ kps1[m.queryIdx].pt for m in g_match ]).reshape(-1,1,2)
        dst_pts = np.float32([ kps2[m.trainIdx].pt for m in g_match ]).reshape(-1,1,2)

        M, mask = cv2.findHomography(src_pts, dst_pts, cv2.RANSAC,5.0)
        matchesMask = mask.ravel().tolist()

        h,w = img1.shape
        pts = np.float32([ [0,0],[0,h-1],[w-1,h-1],[w-1,0] ]).reshape(-1,1,2)
        dst = cv2.perspectiveTransform(pts,M)

        img2 = cv2.polylines(img2, [np.int32(dst)], True, (0,255,255) , 3, cv2.LINE_AA)

    else:
        print "Not enough matches have been found! - %d/%d" % (len(g_match), min_match)
        matchesMask = None

    draw_params = dict(matchColor = (0,255,255), 
                       singlePointColor = (0,255,0),
                       matchesMask = matchesMask, # only inliers
                       flags = 2)
    # region corners    
    cpoints=np.int32(dst)
    a, b,c = cpoints.shape

    # reshape to standard format
    c_p=cpoints.reshape((b,a,c))  

    # crop matching region
    matching_region = crop_region(path_train, c_p)

    img3 = cv2.drawMatches(img1, kps1, img2, kps2, g_match, None, **draw_params)
    return (img3,matching_region)
33

正如Lukas Graf提到的,你在寻找的是交叉相关。它在以下情况下效果很好:

  1. 你的图像大小变化不大。
  2. 图像没有旋转变化。
  3. 图像的光照变化不明显。

对于简单的平移,交叉相关非常有效。

最简单的交叉相关工具是scipy.signal.correlate。不过,它使用的是一种简单的方法,对于边长为n的二维图像,计算复杂度是O(n^4)。在实际应用中,处理你的图像时会非常耗时。

更好的工具是scipy.signal.fftconvolve,因为卷积和相关性是密切相关的。

像这样:

import numpy as np
import scipy.signal

def cross_image(im1, im2):
   # get rid of the color channels by performing a grayscale transform
   # the type cast into 'float' is to avoid overflows
   im1_gray = np.sum(im1.astype('float'), axis=2)
   im2_gray = np.sum(im2.astype('float'), axis=2)

   # get rid of the averages, otherwise the results are not good
   im1_gray -= np.mean(im1_gray)
   im2_gray -= np.mean(im2_gray)

   # calculate the correlation image; note the flipping of onw of the images
   return scipy.signal.fftconvolve(im1_gray, im2_gray[::-1,::-1], mode='same')

看起来有点奇怪的im2_gray[::-1,::-1]索引方式实际上是将图像旋转了180°(水平和垂直都翻转)。这就是卷积和相关的区别,相关是将第二个信号进行镜像后的卷积。

现在如果我们将第一张(最上面的)图像与自己进行相关,我们会得到:

enter image description here

这给出了图像的自相似性度量。最亮的点在(201, 200)的位置,这个位置正好在(402, 400)图像的中心。

最亮的点坐标可以通过以下方式找到:

np.unravel_index(np.argmax(corr_img), corr_img.shape)

最亮像素的线性位置是通过argmax返回的,但需要用unravel_index转换回二维坐标。

接下来,我们尝试将第一张图像与第二张图像进行相关:

enter image description here

相关图像看起来相似,但最佳相关位置移动到了(149, 200),也就是在图像中向上移动了52个像素。这就是两张图像之间的偏移。


对于这些简单的图像,这似乎是有效的。然而,也可能会出现错误的相关峰值,之前提到的任何问题都可能影响结果。

无论如何,你应该考虑使用窗口函数。函数的选择并不是特别重要,只要使用了某种函数即可。此外,如果你遇到小的旋转或缩放变化的问题,可以尝试将几个小区域与周围的图像进行相关。这将给你在图像不同位置的不同位移。

撰写回答