奥比中光深度相机实战:三维物体点云重建、轮廓提取与人脸鉴伪
文章目录
- 准备说明
- 从RGBD图像到(静态)点云
- 3D物体轮廓提取(静态)
- 人脸鉴伪(实时)
- 点云计算+深度分割/轮廓提取动态化
- 代码获取
来源说明:基础代码(功能较少,点云重建和轮廓提取无动态实时功能)和本文部分图片来自奥比中光教学用课件。
准备说明

在实战开始前,需要一台RGBD深度相机,可以使Intel RealSense、Microsoft Kinect或者奥比中光(Orbbec)3D相机。本实战使用Orbbec深度相机Gemini系列,需要安装相关版本驱动,软件开发平台为PyCharm IDE,如果需要调用更高级的接口功能,需要使用VS Community 2019及之前的版本(2022需要修改CMake文件)编译C++源码,基于Orbbec SDK(Software Development Kit)。

Python解释器建议选择3.10及以上,安装opencv-python和open3d库。
说明:opencv-python安装完成后名称为cv2,本实战后续均默认
import cv2 as cv
import open3d as o3d
安装好驱动后,连接深度相机,不出意外就可以使用opencv-python读取并显示RGB图和深度图数据流了。

从RGBD图像到(静态)点云
(1)深度相机拍摄的数据流的获取方式和笔记本自带摄像头方式无异,IDE创建相机时最好指定相机参数为cv.CAP_OBSENSOR, 如cap=cv.VideoCapture(0, cv.CAP_OBSENSOR);使用cap.read或者先grab后retrieve的方式读取数据流,二维数组深度图和三维数组RGB图分别读取,对应参数cv.CAP_OBSENSOR_DEPTH_MAP和cv.CAP_OBSENSOR_BGR_IMAGE;
(2)参考博客园这一篇文章:Open3D 读取、保存、显示点云 - 成理随风 - 博客园, 点云数据文件后缀一般是pcd、pts、ply(包含Mesh网格数据),数据形式可以使N个点,也就是N个xyz/xyzi/xyzrgb/xyzirgb的形式,但深度相机直接返回的是Nxz的形式,因此至少需要计算出每个点对应的x/y值;

根据计算原理图,物距像距关系由磨镜者公式决定,实际计算认为相距d≈f凸透镜焦距,因此给定深度图每一个像素点的深度信息,可以有图像中心(cx,cy)的坐标(此坐标由相机分辨率参数决定,比如本实战的Gemini相机是HXW=480x640分辨率,但是实际相片的大小由相机内部画幅决定,两者共同决定了照片的清晰度。
需要注意实际的相机焦距在x(垂直)和y(水平)方向可能不同,计算公式需作出调整。
根据计算得到的XYZ三维坐标,就可以绘制基本的点云数据了。

(3)结合RGBD深度相机拍摄的帧对齐(时间戳匹配)RGB图像,可以把每个点的rgb颜色信息一并记录,就会获得xyzrgb数据,可能可以通过转成HSV图像,从而获得像素点的亮度值,得到存储N x (x,y,z,i,r,g,b)的点云;可以保存到本地目录以便用到历史数据的算法使用;

3D物体轮廓提取(静态)
这一部分不需要用到点云数据,仅基于深度3D相机获取的二维深度图。基本思路是深度图二值化分割-图像增强处理-梯度图计算-第二次二值化处理-原图修改呈现。
-
深度图二值化分割:做一个简单的阈值分割,可以调用cv.inRange(src,lowerb,upperb)函数,或者基于条件判断和numpy.logic_and()参数做基于深度区间阈值的二值化分割。
-
图像增强处理:基于腐蚀和膨胀操作,如果先前没有接触过这个概念,可以参考更久之前我写的一篇文章:数字图像处理—第7练-CSDN博客,经过本人实际验证,建议使用两次(5,5)”均匀“(kernel是全1)膨胀+一次(5,5)“均匀”腐蚀操作,连接给定深度区间内不连续信息点,同时筛除小噪声斑点。
说明:虽然笔者没有查询膨胀和腐蚀操作的kernel的作用,但是猜测kernel元素为0或者1代表这个位置的像素对于操作是否有效,举一个极端例子:3x3 kernel,中间元素为1其余均为0,仅代表中间元素有影响,无论是腐蚀还是膨胀得到的结果都和原图一致。0的个数越多,腐蚀和膨胀效果越不明显。
-
梯度图计算:可以基于cv.findContours(src,mode,method),但是本人不太推荐,因为笔者在使用该opencv库函数的时候尽管设置了面积阈值,但是不知道遇到了什么问题,debug结果显示查找处的轮廓是覆盖所有宽度维度的大片矩形条纹(没有贴图);替代方法比较容易想出,使用等权重的x方向和y方向一阶差分计算梯度图,最终深度图的第1行和第1列的像素点不在梯度图中。
-
第二次二值化处理:由于前面的腐蚀和膨胀并不会产生新的分布数值,所有亮度值只能是0或255,梯度图的数值在abs绝对值化后只有0,127,128,255四种可能,所以如果需要确保轮廓的连续性,二值化时阈值应该严格小于127(在cv.THRESH_BINARY模式下,等于阈值的部分会归到小于的部分),实验表明如果阈值选用128会导致轮廓连续性大幅下降。
-
原图修改呈现:将检测的三维物体轮廓标注在RGB图像中并显示,注意防止出现左1格上1格的错位,需要先把RGB图像切片处理。
def depth_split(bgr:np.ndarray,depth:np.ndarray,threshold:tuple):# assert(len(threshold)==2)binary=255*np.logical_and(depth>threshold[0],depth<threshold[1]).astype(np.uint8)binary=cv.erode(cv.dilate(binary,kernel=np.ones((5,5), dtype=np.uint8),iterations=2),kernel=np.ones((5,5), dtype=np.uint8),iterations=1)# contours,hierarchy=cv.findContours(binary,mode=cv.RETR_EXTERNAL,method=cv.CHAIN_APPROX_NONE)_,binary_grad=cv2.threshold(np.asarray(abs(binary[1:,1:]-binary[:-1,1:]/2-binary[1:,:-1]/2),np.uint8),64,255,cv2.THRESH_BINARY)cv.imshow('Depth_Mask',binary)cv.imshow('Contour_Demo',binary_grad)bgr[1:,1:][np.where(binary_grad>0)]=[0,255,0]cv.imshow('Contour',bgr)# 帧率30fpscv.waitKey(33)return binary
](https://i-blog.csdnimg.cn/direct/2ad72bc5318b49b28638c1aff2183e53.png)
注意深度图的深度单位是毫米(mm),笔者设置深度区间为300mm-700mm。
人脸鉴伪(实时)
最直接的思路是——使用cv预训练的人脸区域标注的onnx模型,获取5个关键像素点,通过深度相机的深度信息,计算5个深度数组的标准差来判断真假人脸,小于阈值则说明是照片或者海报(平面脸)。如果点位不全不计算标准差。

点云计算+深度分割/轮廓提取动态化
实际场景可能更多的需要实时的、动态的轮廓提取和点云计算功能,在静态的基础上,结合open3d库的visualization.Visualizer类,实时添加、更新geometry点云对象,加入事件轮询(检测)和计时功能并更新渲染状态,最后及时移除点云对象,即可实现实时效果,可根据需求指定不同帧率。加入按键检测,实现点云颜色显示、点云/深度分割和轮廓检测窗口开闭、退出程序的功能。
奥比中光深度相机实战-深度区间二值化演示
说明:对于点云计算和轮廓提取实时化的部分,opencv库的imshow窗口之间,以及和open3d的window可能存在互相冲突的情况,导致出现按键轮询无法响应,解决办法就是鼠标先点击RGB窗口和深度窗口,再按下按键。
代码获取
如果需要实时3D点云重建、实时3D物体轮廓提取标注、人脸鉴伪的源代码,可到我的个人空间免费下载。
源代码:奥比中光深度相机实战-三维物体点云重建、轮廓提取与人脸鉴伪
