如何检测图像之间的转换

时间:2014-07-15 21:10:33

标签: python image image-processing scipy

我正在分析多个图像,并且需要能够判断它们是否与参考图像相比发生了偏移。目的是告知相机在拍摄图像之间是否完全移动。理想情况下,我希望能够校正移位以便仍然进行分析,但至少我需要能够确定图像是否被移位并且如果它超出某个阈值则丢弃它。

以下是我想要检测的图像变化的一些示例:

reference image shifted image 1 shifted image 2

我将使用第一个图像作为参考,然后将所有以下图像与其进行比较,以确定它们是否被移位。图像是灰度的(它们仅使用热图以彩色显示)并存储在2-D numpy阵列中。我有什么想法可以做到这一点?我更喜欢使用我已安装的软件包(scipy,numpy,PIL,matplotlib)。

3 个答案:

答案 0 :(得分:7)

Lukas Graf提示,您正在寻找互相关。它运作良好,如果:

  1. 图像的比例不会发生很大变化。
  2. 图像中没有旋转变化。
  3. 图像中没有明显的光照变化。
  4. 对于普通翻译,互相关非常好。

    最简单的互相关工具是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将其转换回2D坐标。

    接下来,我们通过将第一个图像与第二个图像相关联来尝试相同的步骤:

    enter image description here

    相关图像看起来相似,但最佳相关性已移至(149,200),即图像中向上52像素。这是两幅图像之间的偏移。


    这似乎适用于这些简单的图像。但是,也可能存在错误的相关峰值,本答案开头所述的任何问题都可能会破坏结果。

    在任何情况下,您都应该考虑使用窗口函数。只要使用某些东西,功能的选择就不那么重要了。此外,如果您在旋转或刻度变化较小时遇到问题,请尝试将周围图像再次关联几个小区域。这将在图像的不同位置为您提供不同的位移。

答案 1 :(得分:1)

解决这个问题的另一种方法是计算两个图像中的筛选点,使用RANSAC去掉异常值,然后使用最小二乘估计来求解翻译。

答案 2 :(得分:0)

正如Bharat所说,另一个人正在使用筛选功能和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)