激光雷达与rgb相机外参标定

1 简介

最近在做livox与rgb相机的外参标定,网上看了很多开源方法大对数是基于ros做的,由于对ros不太熟悉,所以先基于python写一个初始版本,以下是全部代码,后面有有时间再整理,相机的内参是使用matlab工具箱标定的,大致思路是将标定板的点云数据通过左右、上下的坐标替换,深度值转换为灰度值,进而得到灰度图,对灰度图做传统图像处理,找到圆心,然后再逆转回在激光雷达坐标系上的坐标。

2 代码

import pandas as pd
import pcl
import open3d as o3d
import numpy as np
import cv2 as cvimport warningsimport xmlfrom sympy import false
warnings.filterwarnings("ignore")rgb_mtx = np.array([[164.9671, 0., 334.1256,0., 167.1601, 219.2284,0., 0., 1.]]).reshape((3, 3))rgb_dist = np.array([-0.0844, 0.0065, -2.2149e-04, -1.1157e-04, 2.8005e-04]).reshape((1, 5))# rgb_mtx = np.array([[120, 0., 640,
#                      0., 120, 360,
#                      0., 0., 1.]]).reshape((3, 3))# rgb_dist = np.zeros((5, 1), dtype=np.float64) def find_lidar_blobs(input_img, show=False):input_img = 255 - input_imgparams = cv.SimpleBlobDetector_Params()params.minThreshold = 5# params.maxThreshold = 5params.blobColor = 0# Filter by Area.params.filterByArea = Trueparams.minArea = 400params.maxArea = 21000# Filter by Circularityparams.filterByCircularity = Trueparams.minCircularity = 0.1# Filter by Convexityparams.filterByConvexity = Trueparams.minConvexity = 0.87# Filter by Inertiaparams.filterByInertia = Trueparams.minInertiaRatio = 0.1detector = cv.SimpleBlobDetector_create(params)# keypoints是一个列表,其中的每个元素都是一个cv2.KeyPoint# KeyPoint包含两个属性 圆的直径以及圆心的位置keypoints = detector.detect(input_img)# keypoints = [kp for kp in keypoints if 72 <= kp.size <= 88]img_with_keypoints = cv.drawKeypoints(input_img, keypoints, np.array([]), (0,255,0), cv.DRAW_MATCHES_FLAGS_DRAW_RICH_KEYPOINTS)# size_list = [kp.size for kp in keypoints]if show:cv.namedWindow("Keypoints", 0)cv.imshow('Keypoints', img_with_keypoints)cv.waitKey(0)cv.destroyAllWindows()return keypointsdef filter_raw_pcl(data_path, color_change):ori_pcl_data = pd.read_csv(data_path, low_memory=False)x_condition = (ori_pcl_data['X'] > 0.6) & (ori_pcl_data['X'] <2)  #纵向y_condition = (ori_pcl_data['Y'] > -0.6) & (ori_pcl_data['Y'] < 0.5) #横向z_condition = (ori_pcl_data['Z'] > 0.3) & (ori_pcl_data['Z'] < 3) #高度ref_condition =  (ori_pcl_data['Reflectivity'] > 0) & (ori_pcl_data['Reflectivity'] < 15)# ref_condition =  (ori_pcl_data['Reflectivity'] > 10)filtered_data = ori_pcl_data[x_condition & y_condition & z_condition & ref_condition]# filtered_data = ori_pcl_data[x_condition & y_condition & z_condition]if color_change:min = filtered_data['Reflectivity'].min()max = filtered_data['Reflectivity'].max()filtered_data['reflectance_normalized'] = (filtered_data['Reflectivity'] - min) / (max - min)return filtered_datadef show_pcl(data):point_cloud = o3d.geometry.PointCloud()# 根据数据类型显示if isinstance(data, pd.DataFrame):point_cloud.points = o3d.utility.Vector3dVector(data[['X', 'Y', 'Z']].values)else:point_cloud.points = o3d.utility.Vector3dVector(data)o3d.visualization.draw_geometries([point_cloud])def sac_plane(valid_data_df):valid_data = valid_data_df[['X', 'Y', 'Z']].values.astype('float32')cloud = pcl.PointCloud(valid_data)seg = cloud.make_segmenter()seg.set_optimize_coefficients(True)seg.set_model_type(pcl.SACMODEL_PLANE)seg.set_method_type(pcl.SAC_RANSAC)seg.set_distance_threshold(0.01)inliners, coefficients = seg.segment()guess = np.expand_dims(np.array(coefficients), axis=1)res = np.dot(valid_data, guess[:3, :]) + guess[3, 0]plane_cloud = cloud.extract(inliners, negative=False)plane_cloud_arr = plane_cloud.to_array()return plane_cloud_arr, coefficientsdef projecto2D(filter_data):projected_points = filter_data*1000x_min, x_max = projected_points[:, 0].min(), projected_points[:, 0].max()  z_min, z_max = projected_points[:, 2].min(), projected_points[:, 2].max()  y_min, y_max = projected_points[:, 1].min(), projected_points[:, 1].max()x_range = x_max -x_min# 创建RGB图像  image_width = int(y_max - y_min)  image_height = int(z_max - z_min)  # TODO 当前是用int8保存深度值 后面使用float16格式rgb_image = np.zeros((image_height, image_width, 1), dtype=np.uint8)# 映射颜色到RGB图像  for point in projected_points:  y, z = int(y_max -point[1]), int(z_max - point[2])if 0 <= y < image_width and 0 <= z < image_height:  rgb_image[z, y] = int(255*(point[0]- x_min)/x_range)cv.imwrite("demo.jpg", rgb_image)return[x_min, x_range, y_max, z_max], rgb_imagedef cal_rgbd(img_path, xyz, show=False):if isinstance(img_path, str):ori_img = cv.imread(img_path, cv.IMREAD_UNCHANGED)else:ori_img = img_pathimg_show = cv.cvtColor(ori_img, cv.COLOR_GRAY2BGR)# 图片二值化ret, smoothed_img = cv.threshold(ori_img, 15, 255, cv.THRESH_BINARY)kernel = np.ones((5,5),np.uint8)smoothed_img = cv.dilate(smoothed_img,kernel,iterations = 1)if show:cv.namedWindow('threshold', 0)cv.imshow("threshold", smoothed_img)key = 0while True:key = cv.waitKey()if key == 27:break# 高斯滤波smoothed_img = cv.GaussianBlur(smoothed_img, (5, 5), 0)keypoints = find_lidar_blobs(smoothed_img, show= show)gridcell_list  = []for keypoint in keypoints:x, z = int(keypoint.pt[0]), int(keypoint.pt[1])valid_point_num = 0value_sum  = 0for i in range(10):for j in range(10):# 加入距离筛选# TODO 现在深度值有问题 深度值从左到右递减if(ori_img[z+j, x+i]) > 30:valid_point_num += 1value_sum += ori_img[z+j, x+i]if valid_point_num == 0:continuemean_value = value_sum / valid_point_num# 还原为之前的深度值 mean_value_trans = mean_value*xyz[1]/255 + xyz[0]# print("mean_value_trans", mean_value_trans)if isinstance(mean_value_trans, np.ndarray):mean_value_trans = mean_value_trans.item()# return[x_min, x_range, y_max, z_max], rgb_imagex_trans= xyz[2] - xz_trans = xyz[3] -zif z_trans > 400: #在纵向上加入高度筛选gridcell_list.append([mean_value_trans,x_trans,z_trans])cv.circle(img_show,(x, z), 1, (0, 0, 255))if show:cv.namedWindow("img_show", 0)cv.imshow('img_show', img_show)key = 0while True:key = cv.waitKey()if key == 27:breakreturn np.array(gridcell_list, dtype=np.float64)def add_world_point(circle_dist=8,boardh=12, boardw=4):world_points = []for i in range(boardh):for j in range(boardw):        if i%2 == 0:x = circle_dist * (2 * j)else:x = circle_dist * (2 * j + 1)y = circle_dist * iworld_points.append([x, y, 0])return np.array(world_points, dtype=np.float32)def find_rgb_blobs(input_img, show=False):params = cv.SimpleBlobDetector_Params()params.minThreshold = 5# params.maxThreshold = 5# params.blobColor = 0# Filter by Area.params.filterByArea = Trueparams.minArea = 20params.maxArea = 500# Filter by Circularityparams.filterByCircularity = Trueparams.minCircularity = 0.1# Filter by Convexityparams.filterByConvexity = Trueparams.minConvexity = 0.87# Filter by Inertiaparams.filterByInertia = Trueparams.minInertiaRatio = 0.1detector = cv.SimpleBlobDetector_create(params)keypoints = detector.detect(input_img)img_with_keypoints = cv.drawKeypoints(input_img, keypoints, np.array([]), (0,255,0), cv.DRAW_MATCHES_FLAGS_DRAW_RICH_KEYPOINTS)# size_list = [kp.size for kp in keypoints]if show:cv.namedWindow("Keypoints", 0)cv.imshow('Keypoints', img_with_keypoints)cv.waitKey(0)cv.destroyAllWindows()return keypointsdef cal_rgb(rgb_img_path, show=False):rgb_cell_list = []rgb_img = cv.imread(rgb_img_path)gray = cv.cvtColor(rgb_img,cv.COLOR_BGR2GRAY)rgb_img[0:100, :] = 0rgb_img[:, 0:250] = 0rgb_img[250:480, :] = 0rgb_img[:, 400:] = 0keypoints = find_rgb_blobs(rgb_img, show=show)for keypoint in keypoints:x, y = int(keypoint.pt[0]), int(keypoint.pt[1])rgb_cell_list.append([x, y])rgb_cell_list  = np.array(rgb_cell_list)ori = np.copy(rgb_cell_list)# 当前的rgb已经严格按照从上到下,从左到右排列ori[:, 1] = np.round(ori[:, 1] / 8) * 8indices = np.lexsort((ori[:, 0], ori[:, 1]))rgb_cell_list = rgb_cell_list[indices]# word_points = add_world_point()# if show:#     i = 0#     for point in rgb_cell_list:#         cv.putText(rgb_img, "{}".format(i), (point[0], point[1]), cv.FONT_HERSHEY_TRIPLEX,thickness = 1,fontScale= 0.5,color=(0,255,0))#         i = i + 1#     cv.namedWindow("img_show_whole", 0)#     cv.imshow('img_show_whole', rgb_img)#     key = 0#     while True:#         key = cv.waitKey()#         if key == 27:#             break# rgb_cell_list = np.array(rgb_cell_list, dtype=np.float32).reshape(1, -1, 2)# word_points = np.array(word_points).reshape(1, -1, 3)# ret, mtx, dist, rvecs, tvecs = cv.calibrateCamera(word_points, rgb_cell_list, gray.shape[::-1], None, None)# if ret:#     print(mtx, dist)rgb_cell_list = rgb_cell_list.tolist()[:12]return np.array(rgb_cell_list, dtype=np.float64)def getRT(world_points,gridcell_list, mtx, dist):_, rvec, tvec = cv.solvePnP(world_points, gridcell_list, mtx, dist,cv.SOLVEPNP_ITERATIVE)rotation_m, _ = cv.Rodrigues(rvec)  # 罗德里格斯变换RT = np.transpose(rotation_m)shouldBeIdentity = np.dot(RT, rotation_m)I = np.identity(3, dtype=rotation_m.dtype)n = np.linalg.norm(I - shouldBeIdentity)assert (n < 1e-6)return rotation_m, tvecdef main():show = Truedata_path = '~/cxx_project/lidar_rgb_calib/data/0221/2024-02-21_18-06-03.Csv'filter_data = filter_raw_pcl(data_path, False)# show_pcl(filter_data)calib_board, conf = sac_plane(filter_data)xyz, depth_img = projecto2D(calib_board)depth_cell_list = cal_rgbd(depth_img, xyz, show=show)depth_ori = np.copy(depth_cell_list)# # 当前的rgb已经严格按照从上到下,从左到右排列depth_ori[:, 2] = np.round(depth_ori[:, 2] / 60) * 60indices = np.lexsort((-depth_ori[:, 1], -depth_ori[:, 2]))depth_cell_list = depth_cell_list[indices]/1000# depth_cell_list = depth_cell_list[:, [0, 2, 1]]print("depth_cell_list:", depth_cell_list)rgb_img_path = '~/cxx_project/lidar_rgb_calib/data/0221/BIAODINGBAN/undistort_img/52.jpg'img = cv.imread(rgb_img_path)rgb_cell_list = cal_rgb(rgb_img_path, show=show)print("rgb_cell_list:", rgb_cell_list)R, T= getRT(depth_cell_list, rgb_cell_list, rgb_mtx, rgb_dist)print("R:{}, T{}".format(R, T))image_points, _ = cv.projectPoints(calib_board, R, T, rgb_mtx, rgb_dist)for point in image_points:x, y = point[0]cv.circle(img, (int(x), int(y)), radius=1, color=(0, 255, 0), thickness=-1)# if show:cv.namedWindow("res", 0)cv.imshow('res', img)cv.waitKey(0)# cv.destroyAllWindows()if __name__ == "__main__":main()

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.mzph.cn/news/703785.shtml

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈email:809451989@qq.com,一经查实,立即删除!

相关文章

音视频剪辑|Windows|抽帧和合帧

什么是抽帧&#xff1f; FFmpeg 抽帧&#xff08;Extracting frames&#xff09;的作用是从视频文件中按需提取单张或多张静止图像&#xff08;帧&#xff09;&#xff0c;并将它们保存为图片文件&#xff08;如 JPEG、PNG 等格式&#xff09;。这一功能在以下场合十分有用&am…

Python服务器监测测试策略与工具:确保应用的高可用性!

在构建高可用性的应用程序时&#xff0c;服务器监测测试是至关重要的一环。Python作为一种强大的编程语言&#xff0c;提供了丰富的工具和库来帮助我们进行服务器监测测试。本文将介绍一些关键的策略和工具&#xff0c;帮助你确保应用的高可用性。 1. 监测策略的制定&#xff…

Vue3 (unplugin-auto-import自动导入的使用)

安装 参考链接 npm i -D unplugin-auto-importvite.config.ts里面配置 import AutoImport from unplugin-auto-import/viteAutoImport({imports:[ vue,vue-router]})重新运行项目会生成一个auto-imports.d.ts的文件 /* eslint-disable */ /* prettier-ignore */ // ts-nochec…

YOLOv9尝鲜测试五分钟极简配置

pip安装python包&#xff1a; pip install yolov9pip在https://github.com/WongKinYiu/yolov9/tree/main中下载好权重文件yolov9-c.pt。 运行下面代码&#xff1a; import yolov9model yolov9.load("yolov9-c.pt", device"cpu") # load pretrained or c…

苹果手机动画演示动画PR样机模板视频素材 iPhone Mockup 7.0

适用于Premiere苹果手机动画演示PR样机模板视频素材 iPhone Mockup 7.0 产品信息&#xff1a; 5款支持alpha频道的动画智能手机。 轻松关闭屏幕上的背景、阴影和反射。 适用于Premiere Pro 2023 每个样机持续时间为13秒。 4K分辨率。 轻松更改颜色。 适用于图像或视频。 包括视…

m估计及其c++简单实现

文章目录 什么是m估计怎么求解m估计呢&#xff1f;Huber函数时的线性m估计 什么是m估计 自20世纪60年代稳健统计建立以来&#xff0c;在国内外众多学者的研究之下&#xff0c;诞生了一系列稳健统计重要理论和成果。其中最主要且广泛使用的稳健统计有以下三类&#xff1a; L-e…

Jmeter学习系列之八:控制器Controllers 的入门介绍

一、Controllers 简介 Jmeter有两种类型的控制器&#xff1a;Samplers&#xff08;取样器&#xff09;和Logical Controllers&#xff08;逻辑控制器&#xff09;&#xff1b;它们驱动着测试的进行取样器&#xff1a;让jmeter发送请求到服务器以及接收服务器的响应数据逻辑控制…

@SpringBootApplication

目录 1. SpringBootApplication注解简介 2. 使用SpringBootApplication注解 3. 自定义SpringBootApplication注解 在Spring Boot中&#xff0c;SpringBootApplication是一个非常重要的注解&#xff0c;它用于开启自动配置&#xff0c;简化了我们的开发工作。本文将详细介绍这…

施华洛世奇 Swarovski EDI需求分析

施华洛世奇为全球首屈一指的光学器材及精确切割仿水晶制造商&#xff0c;为时尚服饰、首饰、灯饰、建筑及室内设计提供仿水晶元素。施华洛世奇有两个主要业务&#xff0c;分别负责制造及销售仿水晶元素&#xff0c;以及设计制造成品。 EDI传输协议 施华洛世奇 Swarovski 与合作…

自定义el-upload 上传文件

前言 最近在做一个文件上传的功能&#xff0c;后端接口写好了、发现前端上传文件的页面不会写……&#xff08;我很笨的&#xff09;然后我就找啊找发现element有个组件是<el-upload/>能直接上传文件。我就想直接用拿来改改改成自己想要的&#xff0c;可是就是这样我花了…

远程连接服务器及可视化方法(Win和Linux)

1.win端 1、通过SSH连接至服务器 在window下&#xff0c;打开命令行提示符&#xff08;快捷键winr后输入cmd回车&#xff09; 在命令行中输入 ssh 服务器上的用户名192.168.50.204回车并输入服务器上的用户登录密码 至此&#xff0c;已成功通过SSH连接至服务器。 2、通过…

Java根据excel模版导出Excel(easyexcel、poi)——含项目测试例子拿来即用

Java根据excel模版导出Excel&#xff08;easyexcel、poi&#xff09;——含项目测试例子拿来即用 1. 前言1.1 关于Excel的一般导出2.2 关于easyexcel的根据模版导出 2. 先看效果2.1 模版2.2 效果 3. 代码实现&#xff08;核心代码&#xff09;3.1 项目代码结构3.2 静态填充例子…

Rabbitmq 超时异常解决:PRECONDITION_FAILED - Timeout value used: 1800000 ms.

Rabbitmq 超时异常解决&#xff1a;PRECONDITION_FAILED - Timeout value used: 1800000 ms. 在使用 docker 启动 rabbitmq 的时候&#xff0c;执行一个超长时间的任务&#xff0c;出现了报错。 查询了一下发现&#xff0c;这个问题在于 rabbitmq 默认客户端超时时间是30分钟,…

python 基础语法及保留字

编码 默认情况下&#xff0c;Python 3 源码文件以 UTF-8 编码&#xff0c;所有字符串都是 unicode 字符串。 当然你也可以为源码文件指定不同的编码&#xff1a; # -*- coding: cp-1252 -*-上述定义允许在源文件中使用 Windows-1252 字符集中的字符编码&#xff0c;对应适合语…

《高考》期刊杂志投稿邮箱知网教育类期刊发表

《高考》杂志是由国家新闻出版总署批准的正规教育类期刊。主要宣传高中新课程改革的专业性&#xff0c;是教育管理工作者、高中一线教师交流经验、探讨问题的重要平台&#xff0c;期刊突出政策性、针对性、指导性&#xff0c;是一本以教育科研成果展示为主&#xff0c;兼具教育…

x86使用GDT表实现系统调用--用户调用系统功能

系统调用 视频讲解可以看这一个课程 GDT表相关知识 原理 注册 允许应用调用操作系统的一些函数, 主要是由于权限, 需要在特区级下面运行一些操作 页表相关设置的时候有一个设置是PDE_U位, 这时候用户就可以访问这一段地址, 否则就是需要系统操作级来进行操作 实现系统调用…

xss-跨站脚本攻击漏洞

前备知识&#xff1a; Cookie和Session是Web开发中用于维持用户状态、跟踪用户会话的核心技术&#xff0c;它们的主要目的是在无状态的HTTP协议基础上实现有状态的用户交互。 **Cookie**&#xff1a; - Cookie是一种由服务器发送到客户端&#xff08;通常是用户的浏览器&#x…

OpenAI视频生成Sora技术简析

基本介绍 Sora是春节期间OpenAI发布的产品&#xff0c;主要是通过文字描述生成视频&#xff0c;通过大规模视频数据训练而成的生成模型&#xff0c;当前还没开放试用。官方发布的技术报告&#xff1a;https://openai.com/research/video-generation-models-as-world-simulators…

无线综合测试仪8960(E5515C)

无线综合测试仪8960&#xff08;E5515C&#xff09; 简述&#xff1a; 8960是美国安捷伦&#xff08;Agilent&#xff09;公司生产的手机综测仪&#xff0c;8960测试仪是一款E5515C主机&#xff0c;具有特定于技术的硬件选件和软件应用程序。有两个硬件选项&#xff0c;8960能…

SpringBoot 学习笔记

文章目录 一、IoC二、AOP三、bean3.1 bean 生命周期3.2 三种依赖注入方式3.3 bean 线程安全 四、SpringMVC五、常用注解5.1 Scope5.2 PostConstruct 和 PreDestroy5.3 Component 和 Bean5.4 Autowired 和 Resource 六、基于 ApplicationContextAware 实现工厂模式七、事务失效八…