• kinect 深度图像去噪算法


    算法设计思路

    (1)读取16位深度图像到待处理图像帧组;

    (2)ROI区域计算

    由于kinect 彩色摄像头和红外深度摄像头是存在视角偏差的,经过视角对齐后,得到的深度图像是有黑边的。此处通过取帧组第一帧图像计算感兴趣区域ROI(注:kinect的摄像头视角是固定的,ROI区域也是固定的,所以只需要计算一次就够了,后续处理只需要使用计算好的就可以了)。ROI计算好,我们便可以在ROI区域做相应的图像处理操作了。

    (3)多帧中值滤波

    如果当前帧(i, j)处灰度不为0,不进行处理;如果为0,对图像帧组的各帧图像(i, j)位置的不为0的像素取出放入的data数组中,插入排序,取中值,代替(i, j)位置像素值。经过中值滤波后,很多黑洞已经被填充好了。

    (4)空间1_近邻滤波

    对待处理的图像帧,此时依然存在黑洞(由于数据集采集时,每一帧相差不是很远,有些地方在帧组的所有帧中都是黑洞。)。此时,采用的是空间近邻滤波方法,在(i, j)位置周围寻找像素值不为0的点来填充黑洞。【可以采取更好的方法】

    主要代码实现

    1. ROI区域计算

    void kinectDenoising::setImageROI(bool isUpdate)
    {
               if (!isUpdate)  // 若为false,定义ROI区域范围。
        {
            imageROI = cvRect(22, 44, 591, 434);
        }
        else // 若为true,表示计算出ROI区域范围
        {
            IplImage* image8u = cvCreateImage(cvSize(width, height), IPL_DEPTH_8U, 1); // 灰度图像格式
            IplImage* bitImage = cvCreateImage(cvSize(width, height), IPL_DEPTH_8U, 1);// 二值图像格式
            
    //         cvShowImage("0", frameSet[0]);
            
            // 查看像素--正确
            for(int i=0;i<height;i++)
            {
              for(int j=0;j<width;j++)
              {
                double ImgPixelVal = cvGetReal2D( frameSet[0], i, j );
                //输出像素值
                cout <<ImgPixelVal<<"  ";
              }
              cout << "\n"<<endl;
            }
            cout << "\n"<<endl;
            cout << "\n"<<endl;
            
    
    
            // 像素约束到[0, 255]空间中
            cvConvertScale(frameSet[0], image8u, 255.0 / 4096.0, 0); // 输入,输出,比例因子,平移因子
            // cvThreshold只对单通道数组进行固定阈值操作【8UC1(8位无符号整型单通道矩阵)、32FC1(32位浮点型单通道矩阵)】
            // 典型应用:对灰度图像进行阈值操作得到二值图像
            cvThreshold(image8u, bitImage, 0, 1, CV_THRESH_BINARY); // 二值化--> bitImage
            
    
            // 分配矩阵空间---必须为CV_32FC1,因为16U和8U不能满足cvReduce()结果的空间需求
            CvMat* rowReduced = cvCreateMat(1, bitImage->width, CV_32FC1); // 行数、列数、预定义类型
            CvMat* colReduced = cvCreateMat(bitImage->height, 1, CV_32FC1);
    
            // 将二维数组转化为向量
            // 参数:输入矩阵、输出矩阵、维数(0表示矩阵处理成1行,1表示处理成1列)、输出矩阵的所有行/列的和。
            cvReduce(bitImage, rowReduced, 0, CV_REDUCE_SUM);
            cvReduce(bitImage, colReduced, 1, CV_REDUCE_SUM);
    
            // compute imageROI.x
            for (int i = 0; i<rowReduced->cols; i++) // 1行,cols列
            {
                // CV_MAT_ELEM参数:(输入矩阵、提取元素类型、行、列)---> 从Mat矩阵中获取元素
                float temp = CV_MAT_ELEM(*rowReduced, float, 0, i);
                // 判断条件:当bitImage整列元素和大于其高度的1/3时,视为所需的点。
                if (temp > bitImage->height / 3)
                {
                    imageROI.x = i;
                    break;
                }
            }
    
            // compute imageROI.width
            for (int i = rowReduced->cols; i > 0; i--)
            {
                float temp = CV_MAT_ELEM(*rowReduced, float, 0, i - 1);
                if (temp > bitImage->height / 3)
                {
                    imageROI.width = i - imageROI.x;
                    break;
                }
            }
            
            // compute imageROI.y
            for (int i = 0; i<colReduced->rows; i++)
            {
                float temp = CV_MAT_ELEM(*colReduced, float, i, 0);
                if (temp>bitImage->height / 3)
                {
                    imageROI.y = i;
                    break;
                }
            }
    
            // compute imageROI.height
            for (int i = colReduced->rows; i > 0; i--)
            {
                float temp = CV_MAT_ELEM(*colReduced, float, i - 1, 0);
                if (temp > bitImage->height / 3)
                {
                    imageROI.height = i - imageROI.y;
                    break;
                }
            }
            
            // 释放内存
            cvReleaseImage(&bitImage);
            cvReleaseImage(&image8u);
            cvReleaseMat(&rowReduced);
            cvReleaseMat(&colReduced);
        }
    }

    2. 多帧中值滤波

     1 void kinectDenoising::medianFiltering()
     2 {
     3     // set result image zero
     4     cvSetZero(denoisedImage);
     5 
     6     unsigned short data[nFrames];
     7     int total;
     8     // x : 4      width : 591
     9     // y : 36   height: 442
    10     //
    11     for (int i = imageROI.y; i < imageROI.y + imageROI.height; i++)
    12     {
    13         // denoiseImageData[j]表示的是image图像中第i行第j列的像素值
    14         unsigned short* denoisedImageData = (unsigned short*)(denoisedImage->imageData + i * denoisedImage->widthStep);
    15         //
    16         for (int j = imageROI.x; j < imageROI.x + imageROI.width; j++)
    17         {
    18             if(CV_IMAGE_ELEM(frameSet[4], unsigned short, i, j) != 0){
    19               denoisedImageData[j] =CV_IMAGE_ELEM(frameSet[4], unsigned short, i, j);
    20             }
    21             else
    22             {
    23             
    24             total = 0;   // 表示长度
    25             for (int k = 0; k < nFrames; k++)
    26             {
    27 //                      cout << CV_IMAGE_ELEM(frameSet[k], unsigned short, i, j)<<  " " ;
    28                 // 多帧图像
    29                 // CV_IMAGE_ELEM(数据指针, 数据类型,像素行坐标、像素列坐标) --> 访问图像帧组(i,j)位置的数据
    30                 insertSort(data, total, CV_IMAGE_ELEM(frameSet[k], unsigned short, i, j));
    31             }
    32             if (total != 0)
    33             {
    34                 // 将(i,j)位置的像素值设置为时间域取中值
    35                 denoisedImageData[j] = data[total / 2];
    36 //                 cout << denoisedImageData[j] <<  " " ;
    37             }
    38             }
    39         }
    40         cout << "\n" << endl;
    41     }
    42 }
    43 
    44 // 插入排序
    45 void insertSort(unsigned short* data, int& len, unsigned short newData)
    46 {
    47     if (newData != 0)
    48     {
    49         if (len == 0)
    50         {
    51             data[len++] = newData;
    52         }
    53         else
    54         {
    55             int i = len;
    56             while ((i > 0) && (data[i - 1] > newData) )
    57             {
    58                 data[i] = data[i - 1];
    59                 i--;
    60             }
    61             data[i] = newData;
    62             len++;
    63         }
    64     }
    65 }

     3. 空间1_近邻滤波

     1 void kinectDenoising::nearestFiltering()
     2 {
     3     CvPoint topLeft, downRight;
     4     IplImage* tempImage = cvCloneImage(denoisedImage); // 复制整个IplImage结构,连同ROI等参数
     5     // 行 [y, y + height]
     6     for (int i = imageROI.y; i < imageROI.y + imageROI.height; i++)
     7     {
     8         // denoiseImageData[j]表示的是image图像中第i行第j列的像素值
     9         unsigned short* data = (unsigned short*)(denoisedImage->imageData + denoisedImage->widthStep*i);
    10         // 列 [x, x + width]
    11         for (int j = imageROI.x; j < imageROI.x + imageROI.width; j++)
    12         {
    13             // 如果(i, j)位置像素值为0,视为无效点,向周围查找有效点!!
    14             for (int k = 1; data[j] == 0; k++) // k从1逐渐增大,直到data[j] != 0 是一个有效点
    15             {
    16                 // 左上点和右下点
    17                 // (j-k,i-k) ( j ,i-k) (j+k,i-k)
    18                 // (j-k, i ) ( j , i ) (j+k, i )
    19                 // (j-k,i+k) ( j ,i+k) (j+k,i+k)
    20                 topLeft = cvPoint(j - k, i - k);    // j为列数 i为行数【注意分别】
    21                 downRight = cvPoint(j + k, i + k);
    22 
    23                 /************************************************************/
    24                 for (int m = topLeft.x; (m <= downRight.x) && (data[j] == 0); m++)
    25                 {
    26                     if (m<0) continue;
    27                     if (m >= width) break;
    28                     if (topLeft.y >= 0)
    29                     {
    30                         // 获取中心点(j,i)左上角(topLeft.y,m)位置数据
    31                         unsigned short temp = CV_IMAGE_ELEM(tempImage, unsigned short, topLeft.y, m);
    32                         if (temp > 0)  // 从该像素左上角查找,找到有效的点,代替中心点的像素
    33                         {
    34                             data[j] = temp;
    35                             break;
    36                         }
    37                     }
    38                     if (downRight.y < height)
    39                     {
    40                         // 获取中心点(j,i)右下角(downRight.y,m)位置数据
    41                         unsigned short temp = CV_IMAGE_ELEM(tempImage, unsigned short, downRight.y, m);
    42                         if (temp > 0)
    43                         {
    44                             data[j] = temp;
    45                             break;
    46                         }
    47                     }
    48                 }
    49                 // (j-k,i-k) ( j ,i-k) (j+k,i-k)
    50                 // (j-k, i ) ( j , i ) (j+k, i )
    51                 // (j-k,i+k) ( j ,i+k) (j+k,i+k)
    52                 for (int m = topLeft.y; (m<downRight.y) && (data[j] == 0); m++)
    53                 {
    54                     if (m<0) continue;
    55                     if (m >= height) break;
    56                     if (topLeft.x>0)
    57                     {
    58                         unsigned short temp = CV_IMAGE_ELEM(tempImage, unsigned short, m, topLeft.x);
    59                         if (temp > 0)
    60                         {
    61                             data[j] = temp;
    62                             break;
    63                         }
    64                     }
    65 
    66                     if (downRight.x<width)
    67                     {
    68                         unsigned short temp = CV_IMAGE_ELEM(tempImage, unsigned short, m, downRight.x);
    69                         if (temp > 0)
    70                         {
    71                             data[j] = temp;
    72                             break;
    73                         }
    74                     }
    75                 }
    76                 /************************************************************/
    77             }
    78         }
    79     }
    80     cvReleaseImage(&tempImage);
    81 }

    算法实现效果

    1. 原图

    彩色图

     

    深度图

    2. 中值处理

    3. 近邻处理

    完整代码,待更新。

    耗时统计

    medianFiltering timeUsed = 13.263
    nearestFiltering timeUsed = 12.807
    filter timeUsed = 26.07
    

    点云效果

     载入点云:pcl_viewer ./data/pointcloud.pcd

    原点云图

    > Loading ./data/pointcloud.pcd [done, 963 ms : 236836 points]

    去噪点云图

    Loading ./data/pointcloud.pcd [done, 1065 ms : 258867 points]

  • 相关阅读:
    HDOJ/HDU 1015 Safecracker(枚举、暴力)
    nodejs之入门
    git错误收集总结
    git基本操作
    git使用前配置
    花开花落花非花、缘起缘灭缘随缘
    js之定时器
    js之Date(日期对象)
    es5严格模式简谈
    try...catch
  • 原文地址:https://www.cnblogs.com/dtyy/p/7655477.html
Copyright © 2020-2023  润新知