对比标记两张图片的差异

python环境:python3.8
依赖模块

scikit-image==0.21.0
opencv-python==4.9.0.80
imutils==0.5.4
import os
import cv2
import imutils

from skimage.metrics import structural_similarity

def cmp_picture(base_pic_path, cmp_pic_path, diff_pic_storage_dir_path=None):
    """
    对比图片
    :param base_pic_path: 基准图片路径
    :param cmp_pic_path: 对比图片路径
    :param diff_pic_storage_dir_path: 指定存储矩形标记的差异图片目录路路径,不指定存放当前目录下
    :return: 基于对比图片生成的对差异的地方用矩形标记的图片的绝对路径
    """
    # 加载两张图片
    base_pic_ndarray = cv2.imread(base_pic_path)
    cmp_pic_ndarray = cv2.imread(cmp_pic_path)
    # 对比图片与基准图片大小对齐
    height, width, channels = base_pic_ndarray.shape
    cmp_pic_ndarray = cv2.resize(cmp_pic_ndarray, (width, height))
    # 灰度处理
    base_pic_gray_ndarray = cv2.cvtColor(base_pic_ndarray, cv2.COLOR_BGR2GRAY)
    cmp_pic_gray_ndarray = cv2.cvtColor(cmp_pic_ndarray, cv2.COLOR_BGR2GRAY)
    # 计算两个灰度图像之间的结构相似度指数:score和差异矩阵
    # 不过ssim多用于压缩图片后的失真度比较
    score, diff = structural_similarity(base_pic_gray_ndarray, cmp_pic_gray_ndarray, full=True)
    diff = (diff * 255).astype("uint8")
    # 找到不同点的轮廓以致于我们可以在被标识为“不同”的区域周围放置矩形:
    thresh = cv2.threshold(diff, 0, 255, cv2.THRESH_BINARY_INV | cv2.THRESH_OTSU)[1]
    # cv2.findContours()函数返回两个值,一个是轮廓本身,还有一个是每条轮廓对应的属性。
    # 其首先返回一个list,list中每个元素都是图像中的一个轮廓
    cnts = cv2.findContours(thresh.copy(), cv2.RETR_EXTERNAL, cv2.CHAIN_APPROX_SIMPLE)
    # 注意cv版本,下面这一行会出现下列问题:
    # OpenCV 3 改为cv2.findContours(...)返回值为image, contours, hierarchy,
    # OpenCV 2 cv2.findContours(...)和OpenCV 4 的cv2.findContours(...)返回值为contours, hierarchy。
    # 把contour轮廓储存在cnts这个list列表里
    cnts = cnts[1] if imutils.is_cv2() else cnts[0]
    # 找到一系列区域,在区域周围放置矩形:
    # cv2.rectangle(imageA,(x,y),(x+w,y+h),(0,0,255),2)  参数解释
    # 第一个参数:img是原图
    # 第二个参数:(x,y)是矩阵的左上点坐
    # 第三个参数:(x+w,y+h)是矩阵的右下点坐标
    # 第四个参数:(0,0,255)是画线对应的rgb颜色
    # 第五个参数:1是所画的线的宽度
    for c in cnts:
        x, y, w, h = cv2.boundingRect(c)
        cv2.rectangle(cmp_pic_ndarray, (x, y), (x + w, y + h), (0, 0, 255), 1)
    # 保存差异图片
    cmp_pic_name = "diff_" + cmp_pic_path.split(os.sep)[-1]
    if diff_pic_storage_dir_path:
        os.makedirs(diff_pic_storage_dir_path, exist_ok=True)
        diff_pic_path = os.path.join(diff_pic_storage_dir_path, cmp_pic_name)
    else:
        diff_pic_path = os.path.join(".", cmp_pic_name)
    cv2.imwrite(diff_pic_path, cmp_pic_ndarray)
    return os.path.abspath(diff_pic_path)

if __name__ == '__main__':
    print(cmp_picture(r"C:\Users\xxx\Pictures\Saved Pictures\1.jpg",
                r"C:\Users\xxx\Pictures\Saved Pictures\2.jpg"))

对比图片并返回差异图片的数据流,方便网络传输并在前端显示

import os
import cv2
import base64
import imutils

from skimage.metrics import structural_similarity

def cmp_and_get_diff_picture_streams(base_pic_path, cmp_pic_path):
    """
    对比图片并返回差异图片的数据流,方便网络传输
    需要在HTML的`<img>`标签中直接显示由数据流生成的JPEG图片时,应当使用"data URI scheme",
    src属性前缀应加上"data:image/jpeg;base64," 
    具体示例如下: <img src="data:image/jpeg;base64,{{encodedImageData}}"> 其中
     "{{encodedImageData}}" 是一个占位符,表示已编码为Base64格式的JPEG图片数据流
    :param base_pic_path: 基准图片路径
    :param cmp_pic_path: 对比图片路径
    :return: (标记差异矩阵后的基础图片数据流, 标记差异矩阵后的对比图片数据流)
    """
    # 加载两张图片
    base_pic_ndarray = cv2.imread(base_pic_path)
    cmp_pic_ndarray = cv2.imread(cmp_pic_path)
    # 对比图片与基准图片大小对齐
    height, width, channels = base_pic_ndarray.shape
    cmp_pic_ndarray = cv2.resize(cmp_pic_ndarray, (width, height))
    # 灰度处理
    base_pic_gray_ndarray = cv2.cvtColor(base_pic_ndarray, cv2.COLOR_BGR2GRAY)
    cmp_pic_gray_ndarray = cv2.cvtColor(cmp_pic_ndarray, cv2.COLOR_BGR2GRAY)
    # 计算两个灰度图像之间的结构相似度指数:score和差异矩阵
    # 不过ssim多用于压缩图片后的失真度比较
    score, diff = structural_similarity(base_pic_gray_ndarray, cmp_pic_gray_ndarray, full=True)
    diff = (diff * 255).astype("uint8")
    # 找到不同点的轮廓以致于我们可以在被标识为“不同”的区域周围放置矩形:
    thresh = cv2.threshold(diff, 0, 255, cv2.THRESH_BINARY_INV | cv2.THRESH_OTSU)[1]
    # cv2.findContours()函数返回两个值,一个是轮廓本身,还有一个是每条轮廓对应的属性。
    # 其首先返回一个list,list中每个元素都是图像中的一个轮廓
    cnts = cv2.findContours(thresh.copy(), cv2.RETR_EXTERNAL, cv2.CHAIN_APPROX_SIMPLE)
    # 注意cv版本,下面这一行会出现下列问题:
    # OpenCV 3 改为cv2.findContours(...)返回值为image, contours, hierarchy,
    # OpenCV 2 cv2.findContours(...)和OpenCV 4 的cv2.findContours(...)返回值为contours, hierarchy。
    # 把contour轮廓储存在cnts这个list列表里
    cnts = cnts[1] if imutils.is_cv2() else cnts[0]
    # 找到一系列区域,在区域周围放置矩形:
    # cv2.rectangle(imageA,(x,y),(x+w,y+h),(0,0,255),2)  参数解释
    # 第一个参数:img是原图
    # 第二个参数:(x,y)是矩阵的左上点坐
    # 第三个参数:(x+w,y+h)是矩阵的右下点坐标
    # 第四个参数:(0,0,255)是画线对应的rgb颜色
    # 第五个参数:2是所画的线的宽度
    for c in cnts:
        x, y, w, h = cv2.boundingRect(c)
        cv2.rectangle(base_pic_ndarray, (x, y), (x + w, y + h), (0, 0, 255), 2)  # 给基准图片标记差异矩阵
        cv2.rectangle(cmp_pic_ndarray, (x, y), (x + w, y + h), (0, 0, 255), 2)   # 给对比图片标记差异矩阵
    # 对图像进行编码,例如转换为JPEG格式
    # 参数1是目标格式(扩展名),参数2是要编码的图像矩阵
    # 这里的cv2.IMWRITE_JPEG_QUALITY参数用于设置JPEG编码的质量等级,范围通常是从0到100。尽管设置为100能够获得最好的质量,
    # 但JPEG始终是一种有损压缩格式,这意味着即使在最高质量设置下仍会有一定程度的信息丢失。如果你需要真正的无损存储,则应选择PNG等支持无损压缩的格式。
    _, base_pic_buffer = cv2.imencode('.jpg', base_pic_ndarray, [int(cv2.IMWRITE_JPEG_QUALITY), 100])  # 可以设置压缩质量等参数
    _, cmp_pic_buffer = cv2.imencode('.jpg', cmp_pic_ndarray, [int(cv2.IMWRITE_JPEG_QUALITY), 100])  # 可以设置压缩质量等参数
    # 将此数据流转换为base64字符串以便在网络传输中使用
    # decode成可打印的字符串形式 现在你可以发送或保存 base64_str 或 data_stream
    return base64.b64encode(base_pic_buffer.tobytes()).decode('utf-8'), \
        base64.b64encode(cmp_pic_buffer.tobytes()).decode('utf-8')        
要使用C#和OpenCV来对两张图像的差异并标它们的位置,你可以使用以下步骤操作: 1. 引入必要的命名间: ```csharp using OpenCvSharp; using OpenCvSharp.Extensions; ``` 2. 加载两个输入图像: ```csharp Mat image1 = Cv2.ImRead("image1.jpg", ImreadModes.Color); Mat image2 = Cv2.ImRead("image2.jpg", ImreadModes.Color); ``` 3. 将图像转换为灰度图像: ```csharp Mat gray1 = new Mat(); Mat gray2 = new Mat(); Cv2.CvtColor(image1, gray1, ColorConversionCodes.BGR2GRAY); Cv2.CvtColor(image2, gray2, ColorConversionCodes.BGR2GRAY); ``` 4. 计算两个灰度图像的差异: ```csharp Mat diff = new Mat(); Cv2.Absdiff(gray1, gray2, diff); ``` 5. 对差异图像进行二值化处理: ```csharp Mat threshold = new Mat(); Cv2.Threshold(diff, threshold, 30, 255, ThresholdTypes.Binary); ``` 6. 查找差异区域的轮廓: ```csharp Point[][] contours; HierarchyIndex[] hierarchy; Cv2.FindContours(threshold, out contours, out hierarchy, RetrievalModes.External, ContourApproximationModes.ApproxSimple); ``` 7. 标记差异区域的位置: ```csharp foreach (var contour in contours) { var rect = Cv2.BoundingRect(contour); Cv2.Rectangle(image1, rect, Scalar.Red, 2); } ``` 8. 显示带有标记的图像: ```csharp Cv2.ImShow("Marked Image", image1); Cv2.WaitKey(0); ``` 9. 释放图像资源: ```csharp image1.Dispose(); image2.Dispose(); gray1.Dispose(); gray2.Dispose(); diff.Dispose(); threshold.Dispose(); ``` 上述代码将会对比两个输入图像并在第一个图像上标记差异的位置。你可以根据自己的需求调整标记的颜色、线条粗细等参数。 希望这个示例对你有帮助!如果还有其他问题,请随时提问。
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值