1. 程式人生 > >OpenCV單kinect多幀靜止場景的深度影象去噪

OpenCV單kinect多幀靜止場景的深度影象去噪

老闆kinect去噪的任務下達已經有半個多月了,前期除了看了幾天文獻之外就打醬油了,好像每天都很忙,可是就是不知道在忙什麼。這幾天為了交差,就胡亂湊了幾段程式碼,得到一個結果,也知道不行,先應付一下,再圖打算。

程式思想很簡單,先對靜止的場景連續取樣若干幀,然後對所有點在時間域取中值,對取完中值之後的無效點在空間域取最近鄰,勉強將黑窟窿填上了。由於程式碼較長,現在奉上關鍵的幾個片段:

  1. #include<cv.h>
  2. #include<highgui.h>
  3. #include<iostream>
  4. usingnamespace std;  
  5. #ifndef _DENOISE
  6. #define _DENOISE
  7. constint nFrames = 9;   // number of consecutive frames
  8. constint width = 640;   // frame width
  9. constint height = 480;  // frame height
  10. class kinectDenoising  
  11. {  
  12. private:                         
  13.     IplImage* denoisedImage;  
  14.     IplImage* frameSet[nFrames];  
  15.     unsigned int numOfFrames;  
  16.     CvRect imageROI;  
  17. public:  
  18.     kinectDenoising();  
  19.     ~kinectDenoising();  
  20.     void addFrame(IplImage* img);   
  21.     void setImageROI(bool isUpdate = true);  
  22.     void medianFiltering();   
  23.     void nearestFiltering();  
  24.     void updateFrameSet(IplImage* img);  
  25.     void showDenoiedImage(constchar* window);  
  26.     void showCurrentImage(
    constchar* window);  
  27. };  
  28. void insertSort(unsigned short* data,int& len,unsigned short newData);  
  29. #endif

這是定義的標頭檔案,裝模作樣的寫了一個類,在建構函式裡面,除了對denoisedImage分配記憶體之外其他都置0,解構函式需要釋放denoisedImage和frameSet陣列的記憶體。numOfFrames本來設計為frameSet中的影象的幀數,結果由於偷懶就用了一個定長的陣列。

  1. void kinectDenoising::setImageROI(bool isUpdate)  
  2. {  
  3.     if(!isUpdate)   
  4.     {  
  5.         imageROI = cvRect(22,44,591,434);  
  6.     }  
  7.     else
  8.     {  
  9.         IplImage* image8u = cvCreateImage(cvSize(width,height),IPL_DEPTH_8U,1);  
  10.         IplImage* bitImage = cvCreateImage(cvSize(width,height),IPL_DEPTH_8U,1);  
  11.         // cvThreshold can only handle images of 8UC1 or 32FC1
  12.         cvConvertScale(frameSet[0],image8u,255.0/4096.0);  
  13.         cvThreshold(image8u,bitImage,0,1,CV_THRESH_BINARY);  
  14.         // the two mats rowReduced and colReduced have to be CV_32SC1 type
  15.         // for function cvReduce() seems not to suitable for 16U type and 
  16.         // 8U type doesn't have enough room for the result.
  17.         CvMat* rowReduced = cvCreateMat(1,bitImage->width,CV_32FC1);  
  18.         // bitImage->width represents number of cols, while bitImage->height stands for rows
  19.         CvMat* colReduced = cvCreateMat(bitImage->height,1,CV_32FC1);  
  20.         cvReduce(bitImage,rowReduced,0,CV_REDUCE_SUM);  
  21.         cvReduce(bitImage,colReduced,1,CV_REDUCE_SUM);  
  22.         // compute imageROI.x
  23.         for(int i=0;i<rowReduced->cols;i++)  
  24.         {  
  25.             float temp = CV_MAT_ELEM(*rowReduced,float,0,i);  
  26.             if(temp>bitImage->height/3)  
  27.             {  
  28.                 imageROI.x = i;  
  29.                 break;  
  30.             }  
  31.         }  
  32.         // computer imageROI.width
  33.         for(int i=rowReduced->cols;i>0;i--)  
  34.         {  
  35.             float temp = CV_MAT_ELEM(*rowReduced,float,0,i-1);  
  36.             if(temp>bitImage->height/3)  
  37.             {  
  38.                 imageROI.width = i-imageROI.x;  
  39.                 break;  
  40.             }  
  41.         }  
  42.         // compute imageROI.y
  43.         for(int i=0;i<colReduced->rows;i++)  
  44.         {  
  45.             float temp = CV_MAT_ELEM(*colReduced,float,i,0);  
  46.             if(temp>bitImage->height/3)  
  47.             {  
  48.                 imageROI.y = i;  
  49.                 break;  
  50.             }  
  51.         }  
  52.         // compute imageROI.height
  53.         for(int i=colReduced->rows;i>0;i--)  
  54.         {  
  55.             float temp = CV_MAT_ELEM(*colReduced,float,i-1,0);  
  56.             if(temp>bitImage->height/3)  
  57.             {  
  58.                 imageROI.height = i-imageROI.y;  
  59.                 break;  
  60.             }  
  61.         }  
  62.         // set memory free
  63.         cvReleaseImage(&bitImage);  
  64.         cvReleaseImage(&image8u);  
  65.         cvReleaseMat(&rowReduced);  
  66.         cvReleaseMat(&colReduced);  
  67.     }  
  68. }  

這是計算深度影象的濾波範圍。由於深度影象和彩色影象的視點不一致,導致了將深度影象對映到彩色影象上時有效畫素會縮小,典型的現象就是在深度影象的四周會出現黑色的區域。這個函式就是用來將四周的黑色框框去掉。用OpenCV的投影的方法。由於cvReduce()函式要進行累積和的計算,為了不使資料溢位,目標陣列應該用32位的浮點型(此函式只支援8位unsigned char型和32位float型)。

  1. void kinectDenoising::medianFiltering()  
  2. {  
  3.     // set result image zero
  4.     cvSetZero(denoisedImage);  
  5.     unsigned short data[nFrames];  
  6.     int total;  
  7.     for(int i=imageROI.y;i<imageROI.y+imageROI.height;i++)  
  8.     {  
  9.         unsigned short* denoisedImageData = (unsigned short*)(denoisedImage->imageData+denoisedImage->widthStep*i);  
  10.         for(int j=imageROI.x;j<imageROI.x+imageROI.width;j++)  
  11.         {  
  12.             total = 0;  
  13.             for(int k=0;k<nFrames;k++)  
  14.             {  
  15.                 insertSort(data,total,CV_IMAGE_ELEM(frameSet[k],unsigned short,i,j));  
  16.             }  
  17.             if(total != 0)  
  18.             {  
  19.                 denoisedImageData[j] = data[total/2];  
  20.             }  
  21.         }  
  22.     }  
  23. }  

中值濾波,統計有效點並排序,然後取中值。insertSort()函式用來將值按從小到大的順序進行插入,鑑於篇幅的關係,就不貼出來了。

  1. void kinectDenoising::nearestFiltering()  
  2. {  
  3.     CvPoint topLeft,downRight;  
  4.     IplImage* tempImage = cvCloneImage(denoisedImage);  
  5.     for(int i=imageROI.y;i<imageROI.y+imageROI.height;i++)  
  6.     {  
  7.         unsigned short* data = (unsigned short*)(denoisedImage->imageData+denoisedImage->widthStep*i);  
  8.         for(int j=imageROI.x;j<imageROI.x+imageROI.width;j++)  
  9.         {  
  10.             for(int k=1;data[j]==0;k++)  
  11.             {  
  12.                 topLeft = cvPoint(j-k,i-k);    // j為行數 i為列數
  13.                 downRight = cvPoint(j+k,i+k);  
  14.                 for(int m=topLeft.x;(m<=downRight.x) && (data[j]==0);m++)  
  15.                 {  
  16.                     if(m<0) continue;  
  17.                     if(m>=width) break;  
  18.                     if(topLeft.y>=0)  
  19.                     {  
  20.                         unsigned short temp = CV_IMAGE_ELEM(tempImage,unsigned short,topLeft.y,m);  
  21.                         if(temp > 0)  
  22.                         {  
  23.                             data[j] = temp;  
  24.                             break;  
  25.                         }  
  26.                     }  
  27.                     if(downRight.y < height)  
  28.                     {  
  29.                         unsigned short temp = CV_IMAGE_ELEM(tempImage,unsigned short,downRight.y,m);  
  30.                         if(temp > 0)  
  31.                         {  
  32.                             data[j] = temp;  
  33.                             break;  
  34.                         }  
  35.                     }                     
  36.                 }  
  37.                 for(int m=topLeft.y;(m<downRight.y) && (data[j]==0);m++)  
  38.                 {  
  39.                     if(m<0) continue;  
  40.                     if(m>=height) break;  
  41.                     if(topLeft.x>0)  
  42.                     {  
  43.                         unsigned short temp = CV_IMAGE_ELEM(tempImage,unsigned short,m,topLeft.x);  
  44.                         if(temp > 0)  
  45.                         {  
  46.                             data[j] = temp;  
  47.                             break;  
  48.                         }  
  49.                     }  
  50.                     if(downRight.x<width)  
  51.                     {  
  52.                         unsigned short temp = CV_IMAGE_ELEM(tempImage,unsigned short,m,downRight.x);  
  53.                         if(temp > 0)  
  54.                         {  
  55.                             data[j] = temp;  
  56.                             break;  
  57.                         }  
  58.                     }  
  59.                 }  
  60.             }  
  61.         }  
  62.     }  
  63.     cvReleaseImage(&tempImage);  
  64. }  

最後是中值濾波,從最內層開始,一層層往外擴,直到找到有效值為止。

執行結果:

源影象:


結果影象:

附註:本來這個程式是在8點陣圖像上進行的。先取得16位的unsigned short型深度影象,然後通過cvConvertScale()函式將其轉化為8位的unsigned char型,結果在進行去噪的時候怎麼都不對,將unsigned char型的資料放到matlab中一看,發現在unsigned short型資料中為0值的畫素莫名其妙的在unsigned char型裡有了一個很小的值(比如說1, 2, 3, 4, 5什麼的,就是不為0)。很奇怪,不知道OpenCV中是怎麼搞的。看來還是源資料靠譜,於是將其改為16位的unsigned short型,結果形勢一片大好。