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]

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章