原文地址:-Image Difference with Opencv and Python-

本文是原作者之前提到的SSIM方法的一种延申,本文主要利用Opencv和Python依据SSIM来实现两幅图片不同之处的可视化。运行环境python3/opencv3

利用原作者的图片,通过调整阈值,本文可以得到非常好的结果,但是使用网络上的找茬图片,存在很多的噪声,效果不是很好,原因多在于图片经过了裁剪、旋转、移位、压缩等操作,后续继续研究另外一位作者cangyan的思路和方法。

原文效果图

59970f5fb316?utm_campaign=maleskine&utm_content=note&utm_medium=seo_notes&utm_source=recommendation

image_difference_output_02

1.计算不同

我们肉眼可以轻松识别下面两幅图的不同之处:右图右下角缺少一个logo。

59970f5fb316?utm_campaign=maleskine&utm_content=note&utm_medium=seo_notes&utm_source=recommendation

image_difference_input

我们可以立刻找到两幅图片的不同,也许要花一点点时间,但是当两幅图片的差别特别细微的时候,我们肉眼几乎是分辨不出的。

那么,识别图片的不同为什么这么重要呢?

比较常见的一个问题就是钓鱼网站,攻击者利用几乎一模一样的图片制作一个高仿的银行网站,迷惑那些毫无戒心的网友。

对比网站logo以及及时熟知网站的用户界面在很大程度上可以防止钓鱼攻击。相比于两幅图的比较,钓鱼网站检测系统将更为复杂,但我们仍然可以使用已有的知识来判别图像是否做了手脚。

读入图片

原文代码:

# import the necessary packages

from skimage.measure import compare_ssim

import argparse

import imutils

import cv2

# construct the argument parse and parse the arguments

ap = argparse.ArgumentParser()

ap.add_argument("-f", "--first", required=True,

help="first input image")

ap.add_argument("-s", "--second", required=True,

help="second")

args = vars(ap.parse_args())

# load the two input images

imageA = cv2.imread(args["first"])

imageB = cv2.imread(args["second"])

# convert the images to grayscale

grayA = cv2.cvtColor(imageA, cv2.COLOR_BGR2GRAY)

grayB = cv2.cvtColor(imageB, cv2.COLOR_BGR2GRAY)

查找轮廓函数cv2.findcontours需要图片是二值图,所以先将图片由BGR转为gray;

59970f5fb316?utm_campaign=maleskine&utm_content=note&utm_medium=seo_notes&utm_source=recommendation

image_difference_grayscale

接下来,计算两幅图之间的SSIM,由于diff的值范围[0, 1],为了可以用Opencv进一步操作,将其转换为[0, 255];

参考代码:

# compute the Structural Similarity Index (SSIM) between the two

# images, ensuring that the difference image is returned

(score, diff) = compare_ssim(grayA, grayB, full=True)

diff = (diff * 255).astype("uint8")

print("SSIM: {}".format(score))

然后,调整阈值,获得二值图,再利用Opencv找到两幅图内容差异diff的轮廓,并用矩形标出来;

参考代码:

# threshold the difference image, followed by finding contours to

# obtain the regions of the two input images that differ

thresh = cv2.threshold(diff, 0, 255, cv2.THRESH_BINARY_INV | cv2.THRESH_OTSU)[1]

cnts = cv2.findContours(thresh.copy(), cv2.RETR_EXTERNAL, cv2.CHAIN_APPROX_SIMPLE)

cnts = imutils.grab_contours(cnts)

原文作者考虑的甚是周全,考虑到用户的Opencv版本可能不同,使用了imutils.grab_contours()函数,具体可以参考imutils.grab_contours源码

# if the length the contours tuple returned by cv2.findContours

# is '2' then we are using either OpenCV v2.4, v4-beta, or

# v4-official

if len(cnts) == 2:

cnts = cnts[0]

# if the length of the contours tuple is '3' then we are using

# either OpenCV v3, v4-pre, or v4-alpha

elif len(cnts) == 3:

cnts = cnts[1]

# otherwise OpenCV has changed their cv2.findContours return

# signature yet again and I have no idea WTH is going on

else:

raise Exception(("Contours tuple must have length 2 or 3, "

"otherwise OpenCV changed their cv2.findContours return "

"signature yet again. Refer to OpenCV's documentation "

"in that case"))

# return the actual contours array

return cnts

显示得到的二值图,效果非常理想;

59970f5fb316?utm_campaign=maleskine&utm_content=note&utm_medium=seo_notes&utm_source=recommendation

image_difference_thresh

使用红色矩形圈出“不同之处”的轮廓;

59970f5fb316?utm_campaign=maleskine&utm_content=note&utm_medium=seo_notes&utm_source=recommendation

image_difference_output_01

参考代码:

# loop over the contours

for c in cnts:

# compute the bounding box of the contour and then draw the

# bounding box on both input images to represent where the two

# images differ

(x, y, w, h) = cv2.boundingRect(c)

cv2.rectangle(imageA, (x, y), (x + w, y + h), (0, 0, 255), 2)

cv2.rectangle(imageB, (x, y), (x + w, y + h), (0, 0, 255), 2)

# show the output images

cv2.imshow("Original", imageA)

cv2.imshow("Modified", imageB)

cv2.imshow("Diff", diff)

cv2.imshow("Thresh", thresh)

cv2.waitKey(0)

2.图片不同可视化

使用下面的命令,可以很好的发现两幅图片的不同:

$ python image_diff.py --first images/original_02.png --second images/modified_02.png

59970f5fb316?utm_campaign=maleskine&utm_content=note&utm_medium=seo_notes&utm_source=recommendation

image_difference_output_02 (1)

59970f5fb316?utm_campaign=maleskine&utm_content=note&utm_medium=seo_notes&utm_source=recommendation

image_difference_output_03

3.个人测试

详细代码参考:myGithub

使用网络图片2,得到了400多个轮廓,画出轮廓面积最大的10个结果,可以看到得到了较好的判定结果,除了发现不同之外,还多出了3处;

59970f5fb316?utm_campaign=maleskine&utm_content=note&utm_medium=seo_notes&utm_source=recommendation

individual result2

使用网络图片1,效果相比差点,虽然也发现了稍有的几处不同,但误判的区域更占多数,主要原因更多是图片的质量问题,右边稍有压缩痕迹。

59970f5fb316?utm_campaign=maleskine&utm_content=note&utm_medium=seo_notes&utm_source=recommendation

individual result1

总结

利用Opencv/Python/Skimage计算的SSIM,我们实现了两幅图之间的差异可视化效果,要得到完全正确的结果,前提要保证变动的部分是严格在原始图上进行的操作,而且内容要重合好,这样可以得到像原作者文中的效果。

Logo

永洪科技,致力于打造全球领先的数据技术厂商,具备从数据应用方案咨询、BI、AIGC智能分析、数字孪生、数据资产、数据治理、数据实施的端到端大数据价值服务能力。

更多推荐