当前位置:首页>python>图片找不同,Python做外挂——“找茬”游戏原来如此简单

图片找不同,Python做外挂——“找茬”游戏原来如此简单

  • 2026-10-11 08:33:31
图片找不同,Python做外挂——“找茬”游戏原来如此简单
考考你的眼力,看看你能从下面的图片中找出几处不同:
很多时候看到这类图片,总想多看几眼,找出其中的差别,可是有时眼睛都瞅模糊了,也没有找到几处。
现在好了,让代码替我们去找。
先来看效果,全自动标注,一眼定位差异:
代码跑完,直接用黄色方框标出左右两边的不同之处。
是不是有点开挂的感觉,有多少个差异,分别在哪里,全部一目了然。
下面就是实现的完整代码,即使文件名带汉字也能完美运行:
import cv2from pathlib import Pathimport numpy as npdef mark_differences(image_path, output_path, min_area=100):    # 1. 读取图片    # img = cv2.imread(image_path)   # 路径中有汉字会出错    img = cv2.imdecode(np.fromfile(image_path, dtype=np.uint8), cv2.IMREAD_COLOR)    if img is None:        print("错误:无法读取图片,请检查路径。")        return    h, w = img.shape[:2]    # 2. 将图片垂直切分为左右两半(假设左右宽度相等)    mid = w // 2    left_img = img[:, :mid]    right_img = img[:, mid:]    # 3.将图片缩放到相同尺寸    right_img = cv2.resize(right_img,(mid,h))    # 4. 转换为灰度图    gray_left = cv2.cvtColor(left_img, cv2.COLOR_BGR2GRAY)    gray_right = cv2.cvtColor(right_img, cv2.COLOR_BGR2GRAY)    # 5. 计算绝对差    diff = cv2.absdiff(gray_left, gray_right)    # 6. 二值化:差异大于阈值设为白色    _, thresh = cv2.threshold(diff, 30, 255, cv2.THRESH_BINARY)    # 7. 形态学去噪    kernel = cv2.getStructuringElement(cv2.MORPH_RECT, (5, 5))    morph = cv2.morphologyEx(thresh, cv2.MORPH_CLOSE, kernel, iterations=2)    morph = cv2.morphologyEx(morph, cv2.MORPH_OPEN, kernel, iterations=1)    # 8. 查找轮廓    contours, _ = cv2.findContours(morph, cv2.RETR_EXTERNAL, cv2.CHAIN_APPROX_SIMPLE)    # 9. 在原图的副本上绘制矩形框    result = img.copy()    marked_count = 0    for cnt in contours:        area = cv2.contourArea(cnt)        if area < min_area:            continue        x, y, w_box, h_box = cv2.boundingRect(cnt)        # 标注左侧区域        cv2.rectangle(result, (x-20, y-20), (x + w_box+20, y + h_box+20), (0, 255, 255), 4)        # 标注右侧区域,矩形框向右平移mid个像素        cv2.rectangle(result, (x + mid-20, y-20), (x + mid + w_box+20, y + h_box+20), (0, 255, 255), 4)        marked_count += 1    print(f"共发现 {len(contours)} 个差异区域,标注了 {marked_count} 个(面积 ≥ {min_area})")    # 10. 保存结果    # cv2.imwrite(output_path, result)   #中文路径会报错    ext = Path(output_path).suffix   # 后缀名    _, img_encode = cv2.imencode(ext, result)  #把图像编码成内存中的 buffer,如 jpg、png    img_encode.tofile(out_path)     # 写入文件    print(f"标注结果已保存到: {output_path}")if __name__ == "__main__":    img_path = r"D:\找不同\111.jpeg"   # 要查找图片路径    out_path = r"D:\找不同\111.jpg"    # 保存查找后的路径    mark_differences(img_path, out_path, min_area=100)
代码解析(超详细)
1、读取图片
img = cv2.imdecode(np.fromfile(image_path, dtype=np.uint8), cv2.IMREAD_COLOR)
cv2.imread不支持中文路径,如果路径中包含中文会出错,在这里用cv2.imdecode配合fromfile可以彻底解决中文报错的问题。
2、将图片分为左右两部分
    mid = w // 2    left_img = img[:, :mid]    right_img = img[:, mid:]
w // 2 自动计算出图片的宽度,然后从中间切开,left_img = img[:, :mid]是左图,right_img = img[:, mid:]是右图。
3、统一图片的尺寸
right_img = cv2.resize(right_img,(mid,h))
使用cv2.resize调整右图尺寸,和左图一样大小。左右图像尺寸不一致,就会差异识别失败。
4、转为灰度图
    gray_left = cv2.cvtColor(left_img, cv2.COLOR_BGR2GRAY)    gray_right = cv2.cvtColor(right_img, cv2.COLOR_BGR2GRAY)

彩色图片干扰因素多,cv2.cvtColor(left_img, cv2.COLOR_BGR2GRAY)可以将彩色图片转为黑白灰度图,只保留亮度差异。

5、计算像素差异

diff = cv2.absdiff(gray_left, gray_right)

cv2.absdiff是计算两个数组绝对值的函数,用来检测两幅图像之间的像素差异。cv2.absdiff会逐像素对比两张图片,不一样的地方会高高度显示,找出不一样的点。

6、二值化

_, thresh = cv2.threshold(diff, 30, 255, cv2.THRESH_BINARY)

cv2.threshold是图像二值化工具。diff是一张灰度图,设定30的阈值,cv2.THRESH_BINARY的作用是大于阈值的变白,小于等于阈值的变黑。thresh是要输出的二值化图像,最终会得到一张黑白分明的差异图。

7、去噪点

    kernel = cv2.getStructuringElement(cv2.MORPH_RECT, (5, 5))    morph = cv2.morphologyEx(thresh, cv2.MORPH_CLOSE, kernel, iterations=2)    morph = cv2.morphologyEx(morph, cv2.MORPH_OPEN, kernel, iterations=1)

cv2.getStructuringElement是做一个小模板,MORPH_RECT是矩形,(5,5)是这个矩形的大小。

用cv2.morphologyEx作“闭运算”和"开运算“,就是用这把刷子来干活。cv2.MORPH_CLOSE是做闭运算,也就是先膨胀再腐蚀;cv2.MORPH_OPEN是做开运算,就是先腐蚀再膨胀;iterations=2表示迭代次数。这一步的作用是把零散的差异点聚集到成块。

8、找出轮廓

contours, _ = cv2.findContours(morph, cv2.RETR_EXTERNAL, cv2.CHAIN_APPROX_SIMPLE)

cv2.findContours的作用是把这些白色区域的边界描出来,存成一个一个的轮廓对象。cv2.RETR_EXTERNAL表示只要最外层的轮廓,不要里面的内容。cv2.CHAIN_APPROX_SIMPLE只存直线的两个端点,把中间的点扔掉。

contour是一个列表,列表中每个元素就是个轮廓。图像中有几个白色块,列表里就有几个轮廓。

9、标注颜色框

    result = img.copy()    marked_count = 0    for cnt in contours:        area = cv2.contourArea(cnt)        if area < min_area:            continue        x, y, w_box, h_box = cv2.boundingRect(cnt)        # 标注左侧区域        cv2.rectangle(result, (x-20, y-20), (x + w_box+20, y + h_box+20), (0, 255, 255), 4)        # 标注右侧区域,矩形框向右平移mid个像素        cv2.rectangle(result, (x + mid-20, y-20), (x + mid + w_box+20, y + h_box+20), (0, 255, 255), 4)        marked_count += 1

 cv2.contourArea(cnt):计算当前轮廓cnt的面积,单位为像素个数。

if area < min_area:min_area是函数参数的设置,默认值为100,小于100个像素的差异区域不进行标注。

x, y, w_box, h_box = cv2.boundingRect(cnt):其中x为矩形左上角的X坐标,y为矩形左上角的Y坐标,w_box为矩形的宽度,h_box为矩形的高度。

cv2.rectangle:在图上画一个矩形框。(x-20, y-20)为矩形框的左上角坐标,减20表示往外扩20个像素,(x + w_box+20, y + h_box+20)为矩形右下角的坐标,(0,255,255)表示矩形框的颜色,4表示矩形框的线条粗细。

10、保存图片

    # cv2.imwrite(output_path, result)    ext = Path(out_path).suffix   # 后缀名    _, img_encode = cv2.imencode(ext, result)  #把图像编码成内存中的 buffer,如 jpg、png    img_encode.tofile(out_path)     # 写入文件

cv2.imwrite不支持中文路径,cv2.imencode和tofile配合可以完美解决这个问题。

if __name__ == "__main__":    img_path = r"D:\找不同\111.jpeg"   # 要查找图片路径    out_path = r"D:\找不同\111.jpg"    # 保存查找后的路径    mark_differences(img_path, out_path, min_area=100)

这是代码的主程序,只需修改路径,然后运行即可。

费眼、费力的事,用这几十行代码就能轻松解决。你觉得有用的话,还请点赞+在看+转发!

最新文章

随机文章