深度图转点云(Xtion/Kinect/OB等)

  今天分享一个将深度图转点云的方法。
  所谓的深度图一般指从深度相机中读取到的带有深度信息的一帧数据,一般为16bit的png/pgm格式。深度图不适合直观的去察看,点云的效果会更强,所以,一般我们都是将深度图转成点云再察看。

其实,像Xtion/Kinect等深度相机可以直接输出点云,当然,深度图的获取也不仅是以上几种设备,例如双目稠密重建算法也可以。我这里用到的是华硕的Xtion采集的数据。

基本原理

  • 基本原理很简单,其实就是简单的相似三角形。先看下面示意图:
    这里写图片描述
    如上图所示,将深度图上的m点转换成世界坐标的M点,也就是深度图转点云的子过程。我们假设深度图中心为O’(图中没有标注,失误),则可利用图中三角形OmO’和OMA相似,所以可得 OOOA=mOMA O O ′ O A = m O ′ M A ,将这个相似关系反应到内存上,就是:
    u=fxXZ+cx u = f x X Z + c x
    v=fxYZ+cy v = f x Y Z + c y

    其中m点坐标是(u,v),这个在图中也没有标注!~~
    其实,这也是内参矩阵的推导过程。 不过,要将像素坐标转换为齐次坐标。
    uv1=1Zfx000fy0cxcy1XYZ [ u v 1 ] = 1 Z [ f x 0 c x 0 f y c y 0 0 1 ] [ X Y Z ]

    习惯性的将 1Z 1 Z 写到左边,则上式变为
    Zuv1=fx000fy0cxcy1XYZ Z [ u v 1 ] = [ f x 0 c x 0 f y c y 0 0 1 ] [ X Y Z ]

    我们将矩阵
    fx000fy0cxcy1 [ f x 0 c x 0 f y c y 0 0 1 ]

    称做内参数矩阵(Camera Intrinsics)K。令P(X,Y,Z),则上式可写为:
    Puv=KTPw P u v = K T P w

    好,下面看代码。

转换关键代码

void depth2PointCloud(Mat imDepth, std::string filePath)
{
    //-- 结构光相机IR传感器内参,我这里用的是Xtion
    float fx = 525.0;
    float fy = 525.0;
    float cx = 319.5;
    float cy = 239.5;

    mypoint.clear();
    for(int i=0; i<imDepth.rows; i++) {
        for(int j=0; j<imDepth.cols; j++) {
            if( imDepth.at<unsigned short>(i,j)!=0 ) {
                unsigned short Zw = imDepth.at<unsigned short>(i, j);
                float Xw, Yw, Zw;
                Xw = Yw = Zw = 0;

                Xw = (j-cx) *  Zw / fx;
                Yw = (i-cy) * Zw / fy;
                Zw= (float)(Zw/1000.0);

                point myp;
                myp.x=Xw;
                myp.y=Yw;
                myp.z=Zw;
                mypoint.push_back(myp);
           }
        }
    }

    std::ofstream os(filePath.c_str());
    int num_p=mypoint.size();
    os<<"ply"<<"\n"<<"format ascii 1.0"<<"\n";
    os<<"element vertex "<<num_p<<"\n";
    os<<"property float x"<<"\n";
    os<<"property float y"<<"\n";
    os<<"property float z"<<"\n";
    //add************2017/6/28
    os<<"property uchar red"<<"\n";
    os<<"property uchar green"<<"\n";
    os<<"property uchar blue"<<"\n";
    //add************2017/6/28
    os<<"end_header"<<"\n";

    qDebug() << mypoint.size();

    for(int i=0;i<mypoint.size();i++)  {
//-- 这里可以增加RGB信息到ply文件中
//        os<<mypoint[i].x<<" "<<mypoint[i].y<<" "<<mypoint[i].z<<" "<< mypoint[i].r<<" "<<\
//            mypoint[i].g<<" "<<mypoint[i].b<<endl;
        os<<mypoint[i].x<<"     "<<mypoint[i].y<<"      "<<mypoint[i].z<< "\n";
   }
   os.close();
}

为其增加一点人性化的枝叶,即读取当前文件夹所有的深度图,自动转换。

void on_pb_depth2Pointcloud_clicked()
{
    QString Path = QFileDialog::getExistingDirectory(this, tr("选择文件夹"), QDir::currentPath().append("/img/"));

    /**************get file List****************/
    QDir *dir=new QDir(Path);
    QStringList filter;
    filter << "*.pgm" << "png";
    dir->setNameFilters(filter);
    QList<QFileInfo> *fileInfo=new QList<QFileInfo>(dir->entryInfoList(filter));

    if( fileInfo->isEmpty() ) {
        QMessageBox::warning(NULL, "Warning!!!", "Is there have no Image?");
        return;
    }

    //删除文件路径中的文件名
    QString rootPath = fileInfo->at(0).filePath();
    rootPath.replace(QRegExp(fileInfo->at(0).fileName()), "");

    int num = 0;
    foreach (QFileInfo tmp, *fileInfo) {
        //IMREAD_ANYDEPTH读取深度图必须添加此参数,否则不是按照16位读取
        Mat imDepth = imread(tmp.filePath().toStdString(), IMREAD_ANYDEPTH);
        qDebug() << tmp.filePath();

        QString tmp_rootPath = rootPath;
        tmp_rootPath += QString::number(num);
        tmp_rootPath += ".txt";
        depth2PointCloud(imDepth, tmp_rootPath.toStdString());
        num++;
    }
}
  • 9
    点赞
  • 89
    收藏
    觉得还不错? 一键收藏
  • 15
    评论
可以使用OpenNI库中的深度像获取函数,通过遍历深度像中的像素点,找到红色球体的像素点,然后计算这些像素点的重心坐标即可得到红色球体的重心坐标。具体实现方法可以参考以下代码: ```python import cv2 import numpy as np from primesense import openni2 # 初始化OpenNI openni2.initialize() # 打开设备 dev = openni2.Device.open_any() # 创建深度流 depth_stream = dev.create_depth_stream() # 启动深度流 depth_stream.start() # 获取深度像 depth_frame = depth_stream.read_frame() depth_data = depth_frame.get_buffer_as_uint16() # 将深度换为numpy数组 depth_array = np.ndarray((depth_frame.height, depth_frame.width), dtype=np.uint16, buffer=depth_data) # 读取RGB像 rgb_image = cv2.imread('rgb_image.jpg') # 将RGB换为HSV像 hsv_image = cv2.cvtColor(rgb_image, cv2.COLOR_BGR2HSV) # 定义红色范围 lower_red = np.array([0, 100, 100]) upper_red = np.array([10, 255, 255]) mask1 = cv2.inRange(hsv_image, lower_red, upper_red) lower_red = np.array([160, 100, 100]) upper_red = np.array([179, 255, 255]) mask2 = cv2.inRange(hsv_image, lower_red, upper_red) # 合并两个掩膜 mask = mask1 + mask2 # 使用形态学操作去除噪点 kernel = np.ones((5, 5), np.uint8) mask = cv2.morphologyEx(mask, cv2.MORPH_OPEN, kernel) # 找到红色球体的轮廓 contours, hierarchy = cv2.findContours(mask, cv2.RETR_EXTERNAL, cv2.CHAIN_APPROX_SIMPLE) # 计算红色球体的重心坐标 if len(contours) > 0: cnt = contours[0] M = cv2.moments(cnt) cx = int(M['m10'] / M['m00']) cy = int(M['m01'] / M['m00']) print('红色球体的重心坐标为:(%d, %d)' % (cx, cy)) else: print('未找到红色球体!') # 停止深度流 depth_stream.stop() # 关闭设备 dev.close() # 反初始化OpenNI openni2.unload() ``` 注意:以上代码中的rgb_image.jpg是RGB像的文件名,需要根据实际情况修改。

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论 15
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值