研究目的
最近在做无人机遥感红外和可见光双路数据配准,由于红外相机视野范围较小,因此配准的目的主要是在可见光的视野范围内,裁剪出红外图像对应的部分,同时,保持可见光的高分辨率不变。
本文思路
本文尝试使用Canny边缘检测提取红外和可见光的边缘特征,然后使用模板匹配的方式去进行配准。由于红外图像和可见光图像的分辨率并不相同,因此需要对可见光不断进行下采样,以接近红外图像的分辨率。
总体看来,使用传统方法做跨模态配准效果有限,主要是由于红外图像特征较少,不过在光照充足和建筑特征明显的情况下,有一定效果,后续会采用基于深度学习的配准方法,相关图片由于项目原因不对外公布,这里对代码进行归档。
实验代码
代码语言:javascript复制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)