代码之家  ›  专栏  ›  技术社区  ›  Mike 'Pomax' Kamermans

如何查找两个图像之间的所有共享区域

  •  2
  • Mike 'Pomax' Kamermans  · 技术社区  · 7 年前

    我一直在试图找到一种自动查找两个图像之间所有共享区域的方法,显式地 基于像素匹配或差分,我基本上在经过一段时间的搜索后什么都没有。

    假设我有以下两张图片,在本例中是网站截图。第一个是“基线”:

    enter image description here

    第二个非常相似,但是有一些修改过的CSS,所以整个块都被移动了。没有文本内容更改,没有框尺寸更改,只是重新定位了一些元素:

    enter image description here

    在这种情况下(同时也在字面上的每一种情况下,其中一个是另一个的导数的两个图像将被比较),它们的像素差实际上对于观察改变的内容是无用的:

    enter image description here

    事实上,即使我们使用一些简单的diff夸张,结果是 相当无用,因为我们仍然在查看像素差异,而不是基于 改变 ,因此我们不会(以任何方式)查看对视觉信息的实际修改:

    enter image description here

    n 我们可以找到 book1.letters[n] != book2.letters[n] ...

    所以,我要寻找的是一种计算相似区域的方法,显示两个图像的哪些部分编码相同的信息,但不一定在相同的边界框中。

    使用颜色编码的类似区域:

    enter image description here

    以及信件:

    enter image description here

    我找不到一个工具来实现这一点,甚至找不到允许使用opencv或类似技术实现这一点的教程。也许我在寻找错误的术语,也许没有人真正为这个编写过图像比较工具(这似乎难以置信?),所以冒着失去话题的风险:我 searched and researched

    3 回复  |  直到 7 年前
        1
  •  3
  •   yapws87    7 年前

    这是对初始区域聚类的一个建议。

    enter image description here

    然后,我们运行形态闭合操作,将所有附近的对象聚集在一起。

    enter image description here

    将结果设为阈值以获得强信号

    enter image description here

    运行connected component analysis以获取所有边界框。

    enter image description here

    然后选中所有方框交叉点并将它们联合起来。在我的例子中,我只是在实体模式下重画所有的边界框,然后重新分析组件以获得区域

    enter image description here

    一旦我们有了这个,我们就可以在第二幅图像上运行相同的过程,并使用简单的互相关匹配方法或任何其他奇特的匹配方法对提取的每个区域进行交叉匹配。在这种情况下,区域之间的简单宽度和高度匹配也可以。

    这是我做的代码。我希望有帮助。

    import cv2
    import numpy as np
    
    
    # Function to fill all the bounding box
    def fill_rects(image, stats):
    
        for i,stat in enumerate(stats):
            if i > 0:
                p1 = (stat[0],stat[1])
                p2 = (stat[0] + stat[2],stat[1] + stat[3])
                cv2.rectangle(image,p1,p2,255,-1)
    
    
    # Load image file
    img1 = cv2.imread('img1.jpg',0)
    img2 = cv2.imread('img2.jpg',0)
    
    # Subtract the 2 image to get the difference region
    img3 = cv2.subtract(img1,img2)
    
    # Make it smaller to speed up everything and easier to cluster
    small_img = cv2.resize(img3,(0,0),fx = 0.25, fy = 0.25)
    
    
    # Morphological close process to cluster nearby objects
    fat_img = cv2.dilate(small_img, None,iterations = 3)
    fat_img = cv2.erode(fat_img, None,iterations = 3)
    
    fat_img = cv2.dilate(fat_img, None,iterations = 3)
    fat_img = cv2.erode(fat_img, None,iterations = 3)
    
    # Threshold strong signals
    _, bin_img = cv2.threshold(fat_img,20,255,cv2.THRESH_BINARY)
    
    # Analyse connected components
    num_labels, labels, stats, centroids = cv2.connectedComponentsWithStats(bin_img)
    
    # Cluster all the intersected bounding box together
    rsmall, csmall = np.shape(small_img)
    new_img1 = np.zeros((rsmall, csmall), dtype=np.uint8)
    
    fill_rects(new_img1,stats)
    
    
    # Analyse New connected components to get final regions
    num_labels_new, labels_new, stats_new, centroids_new = cv2.connectedComponentsWithStats(new_img1)
    
    
    labels_disp = np.uint8(200*labels/np.max(labels)) + 50
    labels_disp2 = np.uint8(200*labels_new/np.max(labels_new)) + 50
    
    
    
    cv2.imshow('diff',img3)
    cv2.imshow('small_img',small_img)
    cv2.imshow('fat_img',fat_img)
    cv2.imshow('bin_img',bin_img)
    cv2.imshow("labels",labels_disp)
    cv2.imshow("labels_disp2",labels_disp2)
    cv2.waitKey(0)
    
        2
  •  0
  •   Yves Daoust    7 年前

    建议:

    现在有了边界框,要尝试的位置数量大大减少。

    还可以看看diff算法,它可以与顺序文本相关。 https://en.wikipedia.org/wiki/Diff

    enter image description here

        3
  •  0
  •   Alex Witsil    7 年前

    不幸的是,我不能产生确切的期望结果,但通过一个相当生硬的算法,我得到了一些接近。一般算法是:

    1. 请参见图1中的第一个和第三个窗格。向两幅图像中添加相同的噪声可确保通过相位相关(如下)比较无特征区域(例如白色背景)。

    2. 用图1中的框分段填充一个由零组成的矩阵

    这个矩阵的一个例子在图1的中间窗格中给出。此图像必须与图像1和图像2的尺寸相同。

    1. 在步骤2的矩阵和噪声图像2之间执行相位相关。

    有几个旋钮,你可以在这里转,可以改善最终结果。见 Performing a phase correlation with fft in R

    enter image description here

    1. 提取与最高相关值相关的x和y值“移位”

    这些值表示步骤2中的矩阵应如何在x和y方向上移动,以便 比赛 噪声图像2。

    这是通过在图像1中的行和列上循环来完成的。可以循环遍历每个索引或跳过几个索引。

    1. 创建一个x和y位移矩阵并绘制以观察图像1中的区域与图像2的比较。

    enter image description here

    注意,结果并不完全是你想要的,而是它的结束。基本上,红色区域表示图像1中的相应区域不必移动。黄色区域(在本例中)需要稍微向下移动,橙色区域需要稍微向下移动,白色区域需要向上移动。

    同样,向图像1和2添加相同的噪声也是一个重要步骤。算法依赖于分离小的框区域(在示例代码中,我使用了50x50像素框)。当循环遍历图像1的行和列并隔离相应的框区域时,几个区域将包含没有特征的区域。这在相位相关中造成了问题,因为在具有相似无特征背景的所有区域中,盒装无特征区域将具有多个高相关值。有效地,添加噪声会在两幅图像中添加特征,以减少不明确的相位相关性。

    此算法产生的结果与所需的结果不同的原因是,装箱区域不是以聪明的方式选择的——它们是在循环图像1的行和列时选择的。因此,根据您选择的框大小,某些框区域将具有与图像2相比转换不同的功能。 也许在yapws87提出的区域聚类算法之后,该算法能更好地工作

    ## read in the images 
    img1 <- readJPEG('./img1.jpg')
    img2 <- readJPEG('./img2.jpg')
    
    ## grayscale the images
    img1 <- (img1[,,1]+img1[,,2]+img1[,,3])/3
    img2 <- (img2[,,1]+img2[,,2]+img2[,,3])/3
    
    
    ## rotate the images for more intuitive R plotting
    img1 <- t(apply(img1,2,rev))
    img2 <- t(apply(img2,2,rev))
    
    ## create some uniform noise 
    noise <- matrix(runif(n=nrow(img1)*ncol(img1)),nrow=nrow(img1),ncol=ncol(img1))*0.1
    
    ## add the SAME noise to both images
    img1 <- noise+img1
    img2 <- noise+img2
    
    ## remove the mean from both images (this may not be necessary) 
    img1 <- img1/mean(img1)
    img2 <- img2/mean(img2)
    
    ## Take the conjugate of the fft of the second image
    IMG2c <- Conj(fft(img2))
    
    ## define how to loop through the first image
    row.step=50
    col.step=50
    
    ## create a zero image (made with all 0s)
    zero.img <- matrix(0,ncol=ncol(img1),nrow=nrow(img1))
    
    ## initialize some vectors to hold the x and y
    ## shifts that correspond to the highest phase correlation value
    shift.x.vec=NULL
    shift.y.vec=NULL
    
    ## keep track of how many iterations you go through
    i.iters=1
    
    ## loop over the columns
    i=1
    while((i+col.step-1)<nrow(img1)) {
    
        ## keep track of how many iterations you go through
        j.iters=1
    
        ## loop over the rows
        j=1
        while((j+col.step-1)<ncol(img1)) {
    
            ## define a current 'box' as the zero image
            cbox1 <- zero.img
    
            ## then populate a small box with values from image 1
            cbox1[i:(i+row.step-1),j:(j+col.step-1)] <- img1[i:(i+row.step-1),j:(j+col.step-1)]
    
            ## PERFORM THE PHASE CORRELATION
    
            ## go into the frequency domain
            CBOX1 <- fft(cbox1)
    
            ## find a normalized value
            norm <- abs(CBOX1 * IMG2c)
    
            ## perform the phase correlation and go back to the space domain
            corr <- Re(fft((CBOX1 * IMG2c)/norm,inv=TRUE)/length(CBOX1))
    
            ## this rearranges the quadrants of the matrix see
            ## matlabs function fftshift
            corr <- fftshift(corr)
    
            ## find the x and y index values associated with the
            ## highest correlation value.
            shift <- which(corr==max(corr),arr.ind=TRUE)
            shift.x <- shift[1]
            shift.y <- shift[2]
    
            ## populate the x and y shift vectors
            shift.x.vec <- c(shift.x.vec,shift.x)
            shift.y.vec <- c(shift.y.vec,shift.y)
    
            ## THIS IS ADDITIONAL PLOTTING AND CAN BE IGNORED
            if(i.iters==6 & j.iters==6) {
                dev.new()
                ##jpeg('./example.jpeg',width=900,height=700)
                split.screen(c(1,3))
                screen(1)
                image(1:nrow(img1),1:ncol(img1),img1,col=gray.colors(200),axes=FALSE,ylab="",xlab="",useRaster=TRUE,main='Noisy Image 1')
                rect(j,i,(j+col.step-1),(i+row.step-1))
    
                screen(2)
                image(cbox1,col=gray.colors(200),axes=FALSE,useRaster=TRUE,main='Current Box')
    
                screen(3)
                image(img2,col=gray.colors(200),axes=FALSE,useRaster=TRUE,main='Noisy Image 2')
    
                ##dev.off()
            }
    
    
    
    
            j.iters=j.iters+1
            j=j+row.step
        }
    
        i.iters=i.iters+1
        i=i+col.step
    
    }
    
    ## make a matrix of shifts values
    ## in this example, only the y shifts are interesting though
    shift.x.mat <- matrix(shift.x.vec,ncol=j.iters-1,nrow=i.iters-1,byrow=TRUE)
    shift.y.mat <- matrix(shift.y.vec,ncol=j.iters-1,nrow=i.iters-1,byrow=TRUE)
    
    
    ##jpeg('./final.jpeg',width=800,height=800)
    image(shift.y.mat,axes=FALSE,useRaster=TRUE)
    ##dev.off()