• 【图像配准】Canny边缘检测+模板配准红外可见光双路数据


    研究目的

    最近在做无人机遥感红外和可见光双路数据配准,由于红外相机视野范围较小,因此配准的目的主要是在可见光的视野范围内,裁剪出红外图像对应的部分,同时,保持可见光的高分辨率不变。

    本文思路

    本文尝试使用Canny边缘检测提取红外和可见光的边缘特征,然后使用模板匹配的方式去进行配准。由于红外图像和可见光图像的分辨率并不相同,因此需要对可见光不断进行下采样,以接近红外图像的分辨率。

    总体看来,使用传统方法做跨模态配准效果有限,主要是由于红外图像特征较少,不过在光照充足和建筑特征明显的情况下,有一定效果,后续会采用基于深度学习的配准方法,相关图片由于项目原因不对外公布,这里对代码进行归档。

    实验代码

    import numpy as np
    import argparse
    import cv2
    import os
    
    if __name__ == '__main__':
        ap = argparse.ArgumentParser()
        ap.add_argument("-i", "--image", required=False, default=r"lr/Infrared.jpg", help="红外图像路径")
        ap.add_argument("-v", "--visualize", required=False, default=r"rgb/Zoom.jpg", help="可见光图像路径")
        ap.add_argument("-o", "--output", required=False, default=r"output", help="输出文件夹路径")
        args = vars(ap.parse_args())
    
        # 读取红外图像/灰度化/边缘检测
        template = cv2.imread(args["image"])
        template = cv2.cvtColor(template, cv2.COLOR_BGR2GRAY)
        template = cv2.Canny(template, 50, 200)
        (tH, tW) = template.shape[:2]
    
        # 读取可见光图像
        image = cv2.imread(args["visualize"])
        # image = cv2.resize(image, (tW, tH))
        gray = cv2.cvtColor(image, cv2.COLOR_BGR2GRAY)
        found = None
    
        for scale in np.linspace(0.2, 1.0, 20)[::-1]:
            # 多尺度缩小可见光图像
            resized = cv2.resize(gray, (int(gray.shape[1] * scale), int(gray.shape[0] * scale)))
            r = gray.shape[1] / float(resized.shape[1])
    
            # 若缩小的尺度小于红外图像尺寸,跳出循环
            if resized.shape[0] < tH or resized.shape[1] < tW:
                break
    
            # 对缩小之后的图像进行边缘检测
            edged = cv2.Canny(resized, 50, 200)
            '''
            cv2.matchTemplate  模板匹配
            :param 检测图像 模板 模板匹配方法
            :returns 相似度结果矩阵:(宽: image.shape[1]-template.shape[1]+1; 高:image.shape[0]-template.shape[0]+1)
            '''
            result = cv2.matchTemplate(edged, template, cv2.TM_CCOEFF)
            # print("edged_shape:{}".format(edged.shape))  # (3888, 5184)
            # print("template_shape:{}".format(template.shape))  # (512, 640)
            # print("result_shape:{}".format(result.shape))  # (3377, 4545)
    
            # 查找模板中最大相似度值和位置
            _, maxVal, _, maxLoc = cv2.minMaxLoc(result)
    
            # 可选:查看匹配图范围
            # clone = np.dstack([edged, edged, edged])
            # clone = edged
            # cv2.rectangle(clone, (maxLoc[0], maxLoc[1]), (maxLoc[0] + tW, maxLoc[1] + tH), (0, 0, 255), 2)
            # cv2.imwrite(os.path.join(args["output"], "Visualize", "visualize.jpg"), clone)
    
            # 若在裁剪区域找到相似度更高的匹配点,更新found
            if found is None or maxVal > found[0]:
                found = (maxVal, maxLoc, r)
    
        # 得到匹配度最高的矩阵框坐标
        _, maxLoc, r = found
        (startX, startY) = (int(maxLoc[0] * r), int(maxLoc[1] * r))
        (endX, endY) = (int((maxLoc[0] + tW) * r), int((maxLoc[1] + tH) * r))
    
        # cv2.rectangle(image, (startX, startY), (endX, endY), (0, 0, 255), 2)
        crop_img = image[startY:endY, startX:endX]
        # cv2.imshow("Image", image)
        # cv2.imshow("Crop Image", crop_img)
        # cv2.waitKey(0)
    
    
        thermal_image = cv2.imread(args["image"], cv2.IMREAD_COLOR)
        # cropping out the matched part of the thermal image
        crop_img = cv2.resize(crop_img, (thermal_image.shape[1], thermal_image.shape[0]))
    
        # 创建输出文件夹存储裁剪后的可见光影像
        if not os.path.exists(os.path.join(args["output"], "process")):
            os.mkdir(os.path.join(args["output"], "process"))
        # 保存图片
        cv2.imwrite(os.path.join(args["output"], "process", os.path.basename(args["visualize"])), crop_img)
    
        # 创建对比图像
        final = np.concatenate((crop_img, thermal_image), axis=1)
        if not os.path.exists(os.path.join(args["output"], "results")):
            os.mkdir(os.path.join(args["output"], "results"))
        cv2.imwrite(os.path.join(args["output"], "results", os.path.basename(args["visualize"])), final)
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21
    • 22
    • 23
    • 24
    • 25
    • 26
    • 27
    • 28
    • 29
    • 30
    • 31
    • 32
    • 33
    • 34
    • 35
    • 36
    • 37
    • 38
    • 39
    • 40
    • 41
    • 42
    • 43
    • 44
    • 45
    • 46
    • 47
    • 48
    • 49
    • 50
    • 51
    • 52
    • 53
    • 54
    • 55
    • 56
    • 57
    • 58
    • 59
    • 60
    • 61
    • 62
    • 63
    • 64
    • 65
    • 66
    • 67
    • 68
    • 69
    • 70
    • 71
    • 72
    • 73
    • 74
    • 75
    • 76
    • 77
    • 78
    • 79
    • 80
    • 81
    • 82
    • 83
    • 84
    • 85
  • 相关阅读:
    【数据结构】栈和队列
    SpringBoot 03: 常用web组件 - - - 拦截器 + Servlet + 过滤器
    智能机场系统:打造出行体验的未来
    Spring Cloud Config Server 和Client 基本配置
    【mq】从零开始实现 mq-10-消费者拉取消息回执 pull message ack
    SDRAM的数据存储实现并对其数据进行读写操作
    zigzag算法
    Java反应式编程(3)
    SpringBoot:如何优雅地进行响应数据封装、异常处理?
    程序员转架构之路,竟被阿里用作内部晋升参考
  • 原文地址:https://blog.csdn.net/qq1198768105/article/details/134022797