当前位置: 首页 > news >正文

红外相机和RGB相机标定:实现两种模态数据融合

1. 前期准备

  1. RGB相机:森云智能SG2-IMX390,1个
  2. 红外相机:艾睿光电IR-Pilot 640X-32G,1个
  3. 红外标定板:https://item.taobao.com/item.htm?_u=jp3fdd12b99&id=644506141871&spm=a1z09.2.0.0.5f822e8dKrxxYI

2.操作步骤

2.1 采集标定数据

两种模态相机均未进行内参标定,如果发现原始图片畸变较大,可以先进行内参标定。数据采集代码如下,加热红外标定板后断电,移动标定板到合适的位置,按下s键,同时保存IR图和RG图

#!/usr/bin/env python3
import cv2 , time
import numpy as npir_dev = "/dev/video6"
rgb_dev = "/dev/video0"
# define a video capture object 
ir_vid = cv2.VideoCapture(ir_dev) 
rgb_vid = cv2.VideoCapture(rgb_dev) count = 0
while(True): # Capture the video frame by frame st_time = time.time()ret, ir_frame = ir_vid.read()# print(f"{time.time() - st_time}") ret, rgb_frame = rgb_vid.read()print(f"{time.time() - st_time}") # Display the resulting frame height, width = ir_frame.shape[:2]#(512,1280)index = [2*i+1 for i in range(width//2)]vis_ir_frame = ir_frame[:,index,:]vis_rgb_frame = cv2.resize(rgb_frame, (640,512))cv2.imshow('IR frame', vis_ir_frame) cv2.imshow('RGB frame', vis_rgb_frame) key = cv2.waitKey(1) & 0xFF if key == ord('q'): breakif key == ord('s'):cv2.imwrite(f"IR_{count}.png", vis_ir_frame)cv2.imwrite(f"RGB_{count}.png", vis_rgb_frame)count += 1# After the loop release the cap object 
ir_vid.release() 
rgb_vid.release() 
# Destroy all the windows 
cv2.destroyAllWindows() 

2.2 进行标定

核心操作是调用opencv函数cv2.findHomography计算两个相机之间的单应性矩阵,代码如下

#!/usr/bin/python
# -*- coding: UTF-8 -*-
import cv2
import numpy as npdef find_chessboard(filename, pattern=(9,8), wind_name="rgb"):# read input imageimg = cv2.imread(filename)# cv2.imshow("raw", img)# img = cv2.undistort(img, camera_matrix, distortion_coefficients)# convert the input image to a grayscalegray = cv2.cvtColor(img, cv2.COLOR_BGR2GRAY)# Find the chess board cornersret, corners = cv2.findChessboardCorners(gray, pattern, None)# if chessboard corners are detectedif ret == True:# Draw and display the cornersimg = cv2.drawChessboardCorners(img, pattern, corners, ret)#Draw number,打印角点编号,便于确定对应点corners = np.ceil(corners[:,0,:])for i, pt in enumerate(corners): cv2.putText(img, str(i), (int(pt[0]),int(pt[1])), cv2.FONT_HERSHEY_COMPLEX, 0.3, (0,255,0), 1)cv2.imshow(wind_name,img)return cornersreturn Noneif __name__ == '__main__' :idx = 2 #0~71rgb_img = cv2.imread(f"RGB_{idx}.png")t_img = cv2.imread(f"IR_{idx}.png")#chessboard grid nums in rgb ,注意观察,同一块标定板在RGB相机和红外相机中的格子说可能不一样rgb_width, rgb_height = 9, 8rgb_corners = find_chessboard(f"RGB_{idx}.png", (rgb_width, rgb_height), "rgb")#chessboard grid nums in thermal thermal_width, thermal_height = 11, 8t_corners = find_chessboard(f"IR_{idx}.png", (thermal_width, thermal_height), "thermal")if rgb_corners is not None and t_corners is not None:# test the id correspondence between rgb and thermal cornersrgb_idx = 27 #可视化一个点,确认取对应点的过程是否正确row, col = rgb_idx//rgb_width, rgb_idx%rgb_widtht_idx = row*thermal_width + col + 1pt = rgb_corners[rgb_idx]cv2.putText(rgb_img, str(rgb_idx), (int(pt[0]),int(pt[1])), cv2.FONT_HERSHEY_COMPLEX, 0.3, (0,255,0), 1)pt = t_corners[t_idx]cv2.putText(t_img, str(t_idx), (int(pt[0]),int(pt[1])), cv2.FONT_HERSHEY_COMPLEX, 0.3, (0,255,0), 1)cv2.imshow(f"Point {rgb_idx} on rgb", rgb_img)cv2.imshow(f"Point {t_idx} on thermal", t_img)# Calculate Homographysrc_pts = []for rgb_idx in range(len(rgb_corners)):row, col = rgb_idx//9, rgb_idx%9t_idx = row*11+col+1src_pts.append(t_corners[t_idx])h, status = cv2.findHomography(np.array(src_pts)[:,None,:], rgb_corners[:,None,:])np.savetxt("calib.param", h)# Warp source image to destination based on homographyt_warp = cv2.warpPerspective(t_img, h, (640,512), borderValue=(255,255,255))#colorizet_warp = cv2.applyColorMap(t_warp, cv2.COLORMAP_JET)#mix rgb and thermalalpha = 0.5merge = cv2.addWeighted(rgb_img, alpha, t_warp, 1-alpha, gamma=0)cv2.imshow("warp", merge)cv2.waitKey(0)cv2.destroyAllWindows()

运行结果如下,观察红外和RGB图中角点的对应关系,编号已经可视化出来了

同时,也单独画出了1个对应后的点,如下图,可检查映射关系是否找对

最后,融合结果如下图所示:

相关文章:

  • [ C++ ] STL---string类的使用指南
  • 首个业内DNA存储技术规范发布
  • SpringBoot中的HttpServletRequest
  • camunda 与 pycamunda学习
  • 51单片机-蜂鸣器
  • 【安全类书籍-2】Web渗透测试:使用Kali Linux
  • 深入理解Java中的TCP连接:三次握手和四次挥手
  • C++: 多态实现原理解析
  • 微信小程序项目实战遇到的问题
  • 详细分析Python模块中的雪花算法(附模板)
  • Django框架的全面指南:从入门到高级【第128篇—Django框架】
  • 24计算机考研调剂 | 温州大学
  • C# 连接neo4j数据库,包括非默认的neo4j默认库
  • 基于ssm+layui的图书管理系统
  • C语言分析基础排序算法——归并排序
  • (三)从jvm层面了解线程的启动和停止
  • avalon2.2的VM生成过程
  • extract-text-webpack-plugin用法
  • java 多线程基础, 我觉得还是有必要看看的
  • MySQL QA
  • select2 取值 遍历 设置默认值
  • -- 查询加强-- 使用如何where子句进行筛选,% _ like的使用
  • 开放才能进步!Angular和Wijmo一起走过的日子
  • 浅谈JavaScript的面向对象和它的封装、继承、多态
  • 入手阿里云新服务器的部署NODE
  • 删除表内多余的重复数据
  • #define、const、typedef的差别
  • #我与Java虚拟机的故事#连载04:一本让自己没面子的书
  • (11)MSP430F5529 定时器B
  • (2022版)一套教程搞定k8s安装到实战 | RBAC
  • (26)4.7 字符函数和字符串函数
  • (arch)linux 转换文件编码格式
  • (Forward) Music Player: From UI Proposal to Code
  • (Mirage系列之二)VMware Horizon Mirage的经典用户用例及真实案例分析
  • (rabbitmq的高级特性)消息可靠性
  • (附源码)ssm航空客运订票系统 毕业设计 141612
  • (附源码)ssm户外用品商城 毕业设计 112346
  • (附源码)ssm旅游企业财务管理系统 毕业设计 102100
  • (亲测)设​置​m​y​e​c​l​i​p​s​e​打​开​默​认​工​作​空​间...
  • (四)汇编语言——简单程序
  • (一) storm的集群安装与配置
  • (一)Mocha源码阅读: 项目结构及命令行启动
  • (转)IIS6 ASP 0251超过响应缓冲区限制错误的解决方法
  • .CSS-hover 的解释
  • .net core MVC 通过 Filters 过滤器拦截请求及响应内容
  • .Net Core和.Net Standard直观理解
  • .NET Standard 的管理策略
  • .net websocket 获取http登录的用户_如何解密浏览器的登录密码?获取浏览器内用户信息?...
  • .NET 中的轻量级线程安全
  • .NET学习教程二——.net基础定义+VS常用设置
  • [ vulhub漏洞复现篇 ] Hadoop-yarn-RPC 未授权访问漏洞复现
  • [ web基础篇 ] Burp Suite 爆破 Basic 认证密码
  • []指针
  • [AIGC] 开源流程引擎哪个好,如何选型?
  • [CSS]CSS 字体属性