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

Image processing 如何查找两个图像之间的所有共享区域,image-processing,Image Processing,我一直在努力寻找一些东西,可以自动找到两幅图像之间的所有共享区域,这显然不是基于像素匹配或差异,经过一段时间的搜索,我基本上什么都没有找到 假设我有以下两张图片,在本例中是网站截图。第一个是“基线”: 第二个非常相似,但是有一些修改过的CSS,所以整个块都被移动了。没有文字内容更改,没有框尺寸更改,只有一些元素重新定位: 在这种情况下(但也确实是每隔一种情况下,其中一个图像是另一个图像的派生图像进行比较),它们的像素差异实际上对于观察变化是无用的: 事实上,即使我们应用一些简单的diff夸

我一直在努力寻找一些东西,可以自动找到两幅图像之间的所有共享区域,这显然不是基于像素匹配或差异,经过一段时间的搜索,我基本上什么都没有找到

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

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

在这种情况下(但也确实是每隔一种情况下,其中一个图像是另一个图像的派生图像进行比较),它们的像素差异实际上对于观察变化是无用的:

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

因此,这就像比较两本书,然后根据
n
的值的多少来决定书是不同的,我们可以找到哪个
book1.letters[n]!=书2.字母[n]

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

例如,在上述两幅图像中,几乎所有的数据都是相同的,只是一些部分被重新定位。唯一真正的区别是有神秘的空白

具有类似区域的颜色编码:

以及信件:


我找不到一个工具来实现这一点,甚至找不到允许使用opencv或类似技术实现这一点的教程。也许我在寻找错误的术语,也许从来没有人为此编写过图像比较工具(这似乎难以置信),所以冒着脱离主题的风险:我尽我所能,在这里。如果我需要这个工具,可以作为QA/测试的普通(开源)工具链的一部分运行,那么我有什么选择?(所以:对于同样昂贵的商业软件来说,不是一些昂贵的插件)。

建议:

如果你能够分割出蓝色的句子,这个问题就可以大大缓解,这可以通过形态学膨胀和二值化来实现。如果扩展足够强,所有字符都可以接触(尽管不同的文本行保持分开),则连接的组件标签可以提取孔线

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

还可以看看diff算法,它可能与顺序文本相关


以下是对初始区域聚类的建议

首先,我们减去这两幅图像,找出不同的区域。然后我们将其调整为更小的规模,以实现更快的速度和更容易的集群

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

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

运行“连接的零部件分析”以获取所有边界框

然后检查所有长方体交点并将其联合。在我的例子中,我只是在实体模式下重新绘制所有边界框,然后重新分析组件以获得区域

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

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

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)

不幸的是,我不能产生准确的预期结果,但通过一个相当简单的算法,我得到了一些接近。一般算法为:

  • 向每个图像添加相同的随机噪声。

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

  • 使用图1中的方框部分填充零矩阵

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

  • 在步骤2中的矩阵和带噪图像2之间执行相位相关。
  • 您可以在此处转动几个旋钮,以改善最终结果。看

  • 提取与最高相关值相关的x和y值“移位”
  • 这些值指示步骤2中的矩阵应如何在x和y方向上移动,以使其与带噪图像2最佳匹配

  • 在步骤3中调整方框部分的位置,以使噪声图像1中的所有区域都循环。然后重复步骤3和4。
  • 这是通过在图1中的行和列上循环来完成的。您可以循环遍历每个索引或跳过几个索引

  • 创建x和y位移矩阵并绘图,以观察图像1中的区域与图像2中的区域进行比较。
  • 请注意,结果并不是您想要的结果,而是非常接近的结果。基本上,红色区域表示图像1中的相应区域不必移动。黄色区域(在本例中)需要稍微向下移动,橙色区域更需要向下移动,白色区域需要向上移动

    同样,向图像1和图像2添加相同的噪声是一个重要步骤。该算法依赖于隔离小方框区域(在示例代码中,我使用了50x50像素方框)。当您在图像1的行和列中循环并隔离相应的装箱区域时,几个区域将包含没有featu的区域
    ## 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()