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

【OpenCV】双相机结构光成像与图像交叉融合实现【python篇】

双相机结构光成像与图像交叉融合实现

在这里插入图片描述
下面我将详细介绍如何使用Python实现双相机结构光成像系统及其图像交叉融合技术。这个系统通常用于三维重建、工业检测等领域。

系统架构概述

双相机结构光系统通常包含以下组件:

  1. 两个同步的工业相机
  2. 结构光投影仪(DLP或LCD)
  3. 计算机处理系统
  4. 标定装置

实现步骤

1. 硬件设置与相机同步

import cv2
import numpy as np
import timeclass DualCameraSystem:def __init__(self, cam1_index=0, cam2_index=1):"""初始化双相机系统"""self.cam1 = cv2.VideoCapture(cam1_index)self.cam2 = cv2.VideoCapture(cam2_index)# 设置相机参数self._setup_cameras()def _setup_cameras(self):"""配置相机参数"""# 设置分辨率self.cam1.set(cv2.CAP_PROP_FRAME_WIDTH, 1280)self.cam1.set(cv2.CAP_PROP_FRAME_HEIGHT, 720)self.cam2.set(cv2.CAP_PROP_FRAME_WIDTH, 1280)self.cam2.set(cv2.CAP_PROP_FRAME_HEIGHT, 720)# 设置帧率self.cam1.set(cv2.CAP_PROP_FPS, 30)self.cam2.set(cv2.CAP_PROP_FPS, 30)# 设置曝光时间(根据实际相机支持)self.cam1.set(cv2.CAP_PROP_EXPOSURE, -4)  # 示例值self.cam2.set(cv2.CAP_PROP_EXPOSURE, -4)def capture_synchronized(self):"""同步捕获图像"""# 实际项目中可能需要硬件触发实现精确同步ret1, frame1 = self.cam1.read()ret2, frame2 = self.cam2.read()if ret1 and ret2:return frame1, frame2else:raise RuntimeError("相机捕获失败")def release(self):"""释放相机资源"""self.cam1.release()self.cam2.release()

2. 结构光图案生成

class StructuredLightPatterns:@staticmethoddef generate_gray_code_patterns(width, height, bits=8):"""生成格雷码图案序列"""patterns = []# 水平方向格雷码for i in range(bits):pattern = np.zeros((height, width), dtype=np.uint8)period = 2 ** (bits - i)value = 0for x in range(width):if x % (period // 2) == 0:value = 255 - valuepattern[:, x] = valuepatterns.append(pattern)# 垂直方向格雷码(如果需要)for i in range(bits):pattern = np.zeros((height, width), dtype=np.uint8)period = 2 ** (bits - i)value = 0for y in range(height):if y % (period // 2) == 0:value = 255 - valuepattern[y, :] = valuepatterns.append(pattern)return patterns@staticmethoddef generate_sinusoidal_patterns(width, height, frequencies=[10, 20, 40], phases=4):"""生成正弦相位偏移图案"""patterns = []x = np.arange(width)y = np.arange(height)xx, yy = np.meshgrid(x, y)for freq in frequencies:for phase in range(phases):phi = 2 * np.pi * phase / phasespattern = 127.5 + 127.5 * np.sin(2 * np.pi * freq * xx / width + phi)patterns.append(pattern.astype(np.uint8))return patterns

3. 相机标定与极线校正

class StereoCalibration:def __init__(self):self.camera_matrix1 = Noneself.dist_coeffs1 = Noneself.camera_matrix2 = Noneself.dist_coeffs2 = Noneself.R = None  # 旋转矩阵self.T = None  # 平移向量self.E = None  # 本质矩阵self.F = None  # 基础矩阵def calibrate(self, image_points1, image_points2, board_size, square_size):"""双目标定"""# 生成物体点 (0,0,0), (1,0,0), (2,0,0) ..., (board_size[0]-1, board_size[1]-1,0)objp = np.zeros((board_size[0] * board_size[1], 3), np.float32)objp[:, :2] = np.mgrid[0:board_size[0], 0:board_size[1]].T.reshape(-1, 2)objp *= square_sizeobj_points = [objp] * len(image_points1)# 单目标定ret1, mtx1, dist1, rvecs1, tvecs1 = cv2.calibrateCamera(obj_points, image_points1, image_points1[0].shape[::-1], None, None)ret2, mtx2, dist2, rvecs2, tvecs2 = cv2.calibrateCamera(obj_points, image_points2, image_points2[0].shape[::-1], None, None)# 双目标定flags = cv2.CALIB_FIX_INTRINSICret, M1, d1, M2, d2, R, T, E, F = cv2.stereoCalibrate(obj_points, image_points1, image_points2,mtx1, dist1, mtx2, dist2, image_points1[0].shape[::-1],flags=flags)self.camera_matrix1 = M1self.dist_coeffs1 = d1self.camera_matrix2 = M2self.dist_coeffs2 = d2self.R = Rself.T = Tself.E = Eself.F = Freturn retdef rectify(self, image_size):"""计算校正映射"""R1, R2, P1, P2, Q, roi1, roi2 = cv2.stereoRectify(self.camera_matrix1, self.dist_coeffs1,self.camera_matrix2, self.dist_coeffs2,image_size, self.R, self.T)# 计算校正映射map1x, map1y = cv2.initUndistortRectifyMap(self.camera_matrix1, self.dist_coeffs1, R1, P1,image_size, cv2.CV_32FC1)map2x, map2y = cv2.initUndistortRectifyMap(self.camera_matrix2, self.dist_coeffs2, R2, P2,image_size, cv2.CV_32FC1)return (map1x, map1y), (map2x, map2y), Q

4. 相位解包裹与深度计算

class PhaseProcessing:@staticmethoddef compute_phase(images, phases=4):"""从相位偏移图像计算相位"""if len(images) != phases:raise ValueError(f"需要{phases}幅图像,但得到了{len(images)}")numerator = 0.0denominator = 0.0for k, img in enumerate(images):img_float = img.astype(np.float32)numerator += img_float * np.sin(2 * np.pi * k / phases)denominator += img_float * np.cos(2 * np.pi * k / phases)phase = np.arctan2(numerator, denominator)return phase@staticmethoddef unwrap_phase(gray_code_patterns, phase):"""使用格雷码解包裹相位"""# 将格雷码转换为二进制码code = np.zeros_like(phase, dtype=np.int32)for i, pattern in enumerate(gray_code_patterns):code += (pattern > 127) * (2 ** i)# 解包裹相位unwrapped_phase = phase + 2 * np.pi * codereturn unwrapped_phase@staticmethoddef compute_depth(phase1, phase2, Q, baseline, wavelength):"""从双相机相位计算深度"""# 计算相位差phase_diff = phase1 - phase2# 避免相位跳变phase_diff = np.where(phase_diff < -np.pi, phase_diff + 2*np.pi, phase_diff)phase_diff = np.where(phase_diff > np.pi, phase_diff - 2*np.pi, phase_diff)# 计算深度depth = (wavelength * baseline) / (2 * np.pi) * phase_diffreturn depth

5. 图像交叉融合算法

class ImageFusion:@staticmethoddef pyramid_fusion(img1, img2, levels=5):"""基于金字塔的图像融合"""# 生成高斯金字塔gauss_pyr1 = [img1.astype(np.float32)]gauss_pyr2 = [img2.astype(np.float32)]for i in range(levels):img1 = cv2.pyrDown(gauss_pyr1[-1])img2 = cv2.pyrDown(gauss_pyr2[-1])gauss_pyr1.append(img1)gauss_pyr2.append(img2)# 生成拉普拉斯金字塔laplacian_pyr1 = [gauss_pyr1[levels-1]]laplacian_pyr2 = [gauss_pyr2[levels-1]]for i in range(levels-1, 0, -1):expanded1 = cv2.pyrUp(gauss_pyr1[i])expanded2 = cv2.pyrUp(gauss_pyr2[i])l1 = gauss_pyr1[i-1] - expanded1l2 = gauss_pyr2[i-1] - expanded2laplacian_pyr1.append(l1)laplacian_pyr2.append(l2)# 融合拉普拉斯金字塔fused_pyr = []for l1, l2 in zip(laplacian_pyr1, laplacian_pyr2):fused = (l1 + l2) / 2fused_pyr.append(fused)# 重建融合图像fused_img = fused_pyr[0]for i in range(1, levels):fused_img = cv2.pyrUp(fused_img)fused_img = fused_img + fused_pyr[i]return np.clip(fused_img, 0, 255).astype(np.uint8)@staticmethoddef wavelet_fusion(img1, img2, wavelet='db1', level=3):"""基于小波的图像融合"""import pywt# 将图像转换为浮点型img1 = img1.astype(np.float32)img2 = img2.astype(np.float32)# 多通道处理if len(img1.shape) == 3:fused_channels = []for c in range(img1.shape[2]):coeffs1 = pywt.wavedec2(img1[:,:,c], wavelet, level=level)coeffs2 = pywt.wavedec2(img2[:,:,c], wavelet, level=level)# 融合系数 - 这里使用简单的平均策略fused_coeffs = []for (c1, c2) in zip(coeffs1, coeffs2):if isinstance(c1, tuple):  # 细节系数fused = tuple((a+b)/2 for a, b in zip(c1, c2))else:  # 近似系数fused = (c1 + c2) / 2fused_coeffs.append(fused)# 重建图像fused_channel = pywt.waverec2(fused_coeffs, wavelet)fused_channels.append(fused_channel)# 合并通道fused_img = np.stack(fused_channels, axis=-1)else:# 单通道图像coeffs1 = pywt.wavedec2(img1, wavelet, level=level)coeffs2 = pywt.wavedec2(img2, wavelet, level=level)# 融合系数fused_coeffs = []for (c1, c2) in zip(coeffs1, coeffs2):if isinstance(c1, tuple):fused = tuple((a+b)/2 for a, b in zip(c1, c2))else:fused = (c1 + c2) / 2fused_coeffs.append(fused)fused_img = pywt.waverec2(fused_coeffs, wavelet)# 归一化并转换为uint8fused_img = np.clip(fused_img, 0, 255)return fused_img.astype(np.uint8)

6. 完整系统集成

class StructuredLight3DScanner:def __init__(self, cam1_index=0, cam2_index=1):self.camera_system = DualCameraSystem(cam1_index, cam2_index)self.calibration = StereoCalibration()self.is_calibrated = Falseself.rectify_maps = Noneself.Q = Nonedef calibrate_system(self, checkerboard_size, square_size, num_images=15):"""使用棋盘格标定系统"""obj_points = []  # 3D点img_points1 = []  # 相机1的2D点img_points2 = []  # 相机2的2D点pattern_found = 0while pattern_found < num_images:frame1, frame2 = self.camera_system.capture_synchronized()gray1 = cv2.cvtColor(frame1, cv2.COLOR_BGR2GRAY)gray2 = cv2.cvtColor(frame2, cv2.COLOR_BGR2GRAY)ret1, corners1 = cv2.findChessboardCorners(gray1, checkerboard_size, None)ret2, corners2 = cv2.findChessboardCorners(gray2, checkerboard_size, None)if ret1 and ret2:# 提高角点检测精度criteria = (cv2.TERM_CRITERIA_EPS + cv2.TERM_CRITERIA_MAX_ITER, 30, 0.001)cv2.cornerSubPix(gray1, corners1, (11,11), (-1,-1), criteria)cv2.cornerSubPix(gray2, corners2, (11,11), (-1,-1), criteria)img_points1.append(corners1)img_points2.append(corners2)pattern_found += 1# 可视化cv2.drawChessboardCorners(frame1, checkerboard_size, corners1, ret1)cv2.drawChessboardCorners(frame2, checkerboard_size, corners2, ret2)cv2.imshow('Camera 1', frame1)cv2.imshow('Camera 2', frame2)cv2.waitKey(500)cv2.destroyAllWindows()# 执行标定image_size = gray1.shape[::-1]self.calibration.calibrate(img_points1, img_points2, checkerboard_size, square_size)# 计算校正映射self.rectify_maps, _, self.Q = self.calibration.rectify(image_size)self.is_calibrated = Truedef capture_patterns(self, patterns):"""捕获结构光图案序列"""all_images1 = []all_images2 = []for pattern in patterns:# 在实际系统中,这里应该控制投影仪显示patternprint(f"投影图案: {pattern.shape}")# 等待投影稳定time.sleep(0.5)# 捕获图像frame1, frame2 = self.camera_system.capture_synchronized()gray1 = cv2.cvtColor(frame1, cv2.COLOR_BGR2GRAY)gray2 = cv2.cvtColor(frame2, cv2.COLOR_BGR2GRAY)all_images1.append(gray1)all_images2.append(gray2)return all_images1, all_images2def reconstruct_3d(self, images1, images2, wavelength=40):"""三维重建"""if not self.is_calibrated:raise RuntimeError("系统未标定")# 校正图像rectified1 = []rectified2 = []map1x, map1y = self.rectify_maps[0]map2x, map2y = self.rectify_maps[1]for img1, img2 in zip(images1, images2):r1 = cv2.remap(img1, map1x, map1y, cv2.INTER_LINEAR)r2 = cv2.remap(img2, map2x, map2y, cv2.INTER_LINEAR)rectified1.append(r1)rectified2.append(r2)# 计算相位phase_processor = PhaseProcessing()# 假设前4幅是相位偏移图案phase1 = phase_processor.compute_phase(rectified1[:4])phase2 = phase_processor.compute_phase(rectified2[:4])# 假设后8幅是格雷码图案(水平+垂直)gray_code_patterns1 = rectified1[4:12]gray_code_patterns2 = rectified2[4:12]# 解包裹相位unwrapped_phase1 = phase_processor.unwrap_phase(gray_code_patterns1[:4], phase1)unwrapped_phase2 = phase_processor.unwrap_phase(gray_code_patterns2[:4], phase2)# 计算深度baseline = np.linalg.norm(self.calibration.T)  # 基线距离depth = phase_processor.compute_depth(unwrapped_phase1, unwrapped_phase2, self.Q, baseline, wavelength)return depthdef fuse_images(self, images1, images2, method='pyramid'):"""融合双相机图像"""fused_images = []for img1, img2 in zip(images1, images2):if method == 'pyramid':fused = ImageFusion.pyramid_fusion(img1, img2)elif method == 'wavelet':fused = ImageFusion.wavelet_fusion(img1, img2)else:raise ValueError("不支持的融合方法")fused_images.append(fused)return fused_imagesdef close(self):"""关闭系统"""self.camera_system.release()

使用示例

if __name__ == "__main__":# 初始化扫描仪scanner = StructuredLight3DScanner()try:# 标定系统print("开始标定...")scanner.calibrate_system((7,9), 0.025)  # 7x9棋盘格,每个方格25mmprint("标定完成")# 生成结构光图案pattern_gen = StructuredLightPatterns()patterns = []patterns.extend(pattern_gen.generate_sinusoidal_patterns(1280, 720))patterns.extend(pattern_gen.generate_gray_code_patterns(1280, 720, bits=4))# 捕获图案print("捕获结构光图案...")images1, images2 = scanner.capture_patterns(patterns)# 图像融合print("图像融合...")fused_images = scanner.fuse_images(images1, images2, method='pyramid')# 三维重建print("三维重建...")depth_map = scanner.reconstruct_3d(images1, images2)# 可视化结果cv2.imshow("融合图像", fused_images[0])cv2.imshow("深度图", cv2.normalize(depth_map, None, 0, 255, cv2.NORM_MINMAX).astype(np.uint8))cv2.waitKey(0)finally:scanner.close()

关键技术说明

  1. 结构光编码

    • 使用格雷码和正弦相位偏移图案进行编码
    • 格雷码提供粗定位,正弦图案提供精确定位
  2. 相位解包裹

    • 结合格雷码和相位偏移技术解决相位模糊问题
    • 实现高精度的相位测量
  3. 立体匹配

    • 利用双相机系统获取不同视角的相位信息
    • 通过相位差计算三维坐标
  4. 图像融合

    • 金字塔融合:保留不同频率的信息
    • 小波融合:在频域实现图像融合
  5. 系统标定

    • 精确的相机内参和外参标定
    • 极线校正简化匹配过程

实际应用中的注意事项

  1. 硬件同步

    • 使用硬件触发器确保相机和投影仪的精确同步
    • 考虑曝光时间和投影时序
  2. 环境光干扰

    • 在暗室环境中工作或使用窄带滤光片
    • 考虑环境光补偿算法
  3. 标定精度

    • 使用高精度标定板
    • 多次标定取平均值提高精度
  4. 实时性优化

    • 使用GPU加速计算密集型操作
    • 优化算法实现实时处理

这个实现提供了双相机结构光系统的完整框架,包括图像采集、系统标定、结构光解码、三维重建和图像融合等功能。根据具体应用需求,可以进一步优化和扩展各个模块。

相关文章:

  • Cursor 工具项目构建指南:MySql 数据库结构设计的 Cursor 规范
  • PCB设计教程【大师篇】——STM32开发板原理图设计(单片机最小系统)
  • 华为云Flexus+DeepSeek征文 | 基于ModelArts Studio、DeepSeek大模型和Dify搭建网站智能客服助手
  • 基于算法竞赛的c++编程(29)类的概念和简单应用
  • day52 ResNet18 CBAM
  • 【小记】2024-2025生物计算类热点问题
  • 10- AI大模型-LangChainV0.3应用(一) - 简介,模型调用,prompt模板,输出解析器
  • #Word“嵌入式”插图显示不全的解决教程
  • python打卡第50天
  • PG库创建自增ID
  • 操作系统的一些名词
  • UDP(Echoserver)
  • VUE element table 列合并
  • V837s-sdk buildroot文件系统设置串口登录密码
  • 【ModelArts】ModelArts一站式AI开发平台详解(一)
  • 豆包全新视频生成模型、视觉深度思考模型发布
  • 曼昆《经济学原理》第九版 第十五章垄断
  • 线程与进程(java)
  • 汽车生产虚拟实训中的技能提升与生产优化​
  • MongoDB(八) - MongoDB GridFS介绍及使用Python操作GridFS
  • 网站上的聊天框怎么做的/东莞企业网站排名
  • 网站建设相关技术/班级优化大师网页版登录
  • 一个刚做好的网站怎么做seo/小吴seo博客
  • 网站推广策划的策略/全网品牌推广
  • asp.net 4.0网站建设基础教程/一手项目对接app平台
  • 现在公众号做电影网站的发展/重庆网站快速排名优化