算法設計思路html
(1)讀取16位深度圖像到待處理圖像幀組;算法
(2)ROI區域計算數組
因爲kinect 彩色攝像頭和紅外深度攝像頭是存在視角誤差的,通過視角對齊後,獲得的深度圖像是有黑邊的。此處經過取幀組第一幀圖像計算感興趣區域ROI(注:kinect的攝像頭視角是固定的,ROI區域也是固定的,因此只須要計算一次就夠了,後續處理只須要使用計算好的就能夠了)。ROI計算好,咱們即可以在ROI區域作相應的圖像處理操做了。spa
(3)多幀中值濾波設計
若是當前幀(i, j)處灰度不爲0,不進行處理;若是爲0,對圖像幀組的各幀圖像(i, j)位置的不爲0的像素取出放入的data數組中,插入排序,取中值,代替(i, j)位置像素值。通過中值濾波後,不少黑洞已經被填充好了。指針
(4)空間1_近鄰濾波rest
對待處理的圖像幀,此時依然存在黑洞(因爲數據集採集時,每一幀相差不是很遠,有些地方在幀組的全部幀中都是黑洞。)。此時,採用的是空間近鄰濾波方法,在(i, j)位置周圍尋找像素值不爲0的點來填充黑洞。【能夠採起更好的方法】code
主要代碼實現htm
1. ROI區域計算blog
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]