OpenCV單kinect多幀靜止場景的深度圖像去噪
老板kinect去噪的任務(wù)下達(dá)已經(jīng)有半個多月了,前期除了看了幾天文獻(xiàn)之外就打醬油了,好像每天都很忙,可是就是不知道在忙什么。這幾天為了交差,就胡亂湊了幾段代碼,得到一個結(jié)果,也知道不行,先應(yīng)付一下,再圖打算。程序思想很簡單,先對靜止的場景連續(xù)采樣若干幀,然后對所有點在時間域取中值,對取完中值之后的無效點在空間域取最近鄰,勉強(qiáng)將黑窟窿填上了。由于代碼較長,現(xiàn)在奉上關(guān)鍵的幾個片段:
#include《cv.h》
#include《highgui.h》
#include《iostream》
using namespace std;
#ifndef _DENOISE
#define _DENOISE
const int nFrames = 9; // number of consecutive frames
const int width = 640; // frame width
const int height = 480; // frame height
class kinectDenoising
{
private:
IplImage* denoisedImage;
IplImage* frameSet[nFrames];
unsigned int numOfFrames;
CvRect imageROI;
public:
kinectDenoising();
~kinectDenoising();
void addFrame(IplImage* img);
void setImageROI(bool isUpdate = true);
void medianFiltering();
void nearestFiltering();
void updateFrameSet(IplImage* img);
void showDenoiedImage(const char* window);
void showCurrentImage(const char* window);
};
void insertSort(unsigned short* data,int& len,unsigned short newData);
#endif
這是定義的頭文件,裝模作樣的寫了一個類,在構(gòu)造函數(shù)里面,除了對denoisedImage分配內(nèi)存之外其他都置0,析構(gòu)函數(shù)需要釋放denoisedImage和frameSet數(shù)組的內(nèi)存。numOfFrames本來設(shè)計為frameSet中的圖像的幀數(shù),結(jié)果由于偷懶就用了一個定長的數(shù)組。
void kinectDenoising::setImageROI(bool isUpdate)
{
if(!isUpdate)
{
imageROI = cvRect(22,44,591,434);
}
else
{
IplImage* image8u = cvCreateImage(cvSize(width,height),IPL_DEPTH_8U,1);
IplImage* bitImage = cvCreateImage(cvSize(width,height),IPL_DEPTH_8U,1);
// cvThreshold can only handle images of 8UC1 or 32FC1
cvConvertScale(frameSet[0],image8u,255.0/4096.0);
cvThreshold(image8u,bitImage,0,1,CV_THRESH_BINARY);
// the two mats rowReduced and colReduced have to be CV_32SC1 type
// for function cvReduce() seems not to suitable for 16U type and
// 8U type doesn‘t have enough room for the result.
CvMat* rowReduced = cvCreateMat(1,bitImage-》width,CV_32FC1);
// bitImage-》width represents number of cols, while bitImage-》height stands for
rows
CvMat* colReduced = cvCreateMat(bitImage-》height,1,CV_32FC1);
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++)
{
float temp = CV_MAT_ELEM(*rowReduced,float,0,i);
if(temp》bitImage-》height/3)
{
imageROI.x = i;
break;
}
}
// computer 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;
}
}
// set memory free
cvReleaseImage(&bitImage);
cvReleaseImage(&image8u);
cvReleaseMat(&rowReduced);
cvReleaseMat(&colReduced);
}
}
這是計算深度圖像的濾波范圍。由于深度圖像和彩色圖像的視點不一致,導(dǎo)致了將深度圖像映射到彩色圖像上時有效像素會縮小,典型的現(xiàn)象就是在深度圖像的四周會出現(xiàn)黑色的區(qū)域。這個函數(shù)就是用來將四周的黑色框框去掉。用OpenCV的投影的方法。由于cvReduce()函數(shù)要進(jìn)行累積和的計算,為了不使數(shù)據(jù)溢出,目標(biāo)數(shù)組應(yīng)該用32位的浮點型(此函數(shù)只支持8位unsigned char型和32位float型)。
void kinectDenoising::medianFiltering()
{
// set result image zero
cvSetZero(denoisedImage);
unsigned short data[nFrames];
int total;
for(int i=imageROI.y;i《imageROI.y+imageROI.height;i++)
{
unsigned short* denoisedImageData = (unsigned short*)(denoisedImage-
》imageData+denoisedImage-》widthStep*i);
for(int j=imageROI.x;j《imageROI.x+imageROI.width;j++)
{
total = 0;
for(int k=0;k《nFrames;k++)
{
insertSort(data,total,CV_IMAGE_ELEM(frameSet[k],unsigned
short,i,j));
}
if(total != 0)
{
denoisedImageData[j] = data[total/2];
}
}
}
}
中值濾波,統(tǒng)計有效點并排序,然后取中值。insertSort()函數(shù)用來將值按從小到大的順序進(jìn)行插入,鑒于篇幅的關(guān)系,就不貼出來了。
void kinectDenoising::nearestFiltering()
{
CvPoint topLeft,downRight;
IplImage* tempImage = cvCloneImage(denoisedImage);
for(int i=imageROI.y;i《imageROI.y+imageROI.height;i++)
{
unsigned short* data = (unsigned short*)(denoisedImage-》imageData
+denoisedImage-》widthStep*i);
for(int j=imageROI.x;j《imageROI.x+imageROI.width;j++)
{
for(int k=1;data[j]==0;k++)
{
topLeft = cvPoint(j-k,i-k); // j為行數(shù) i為列數(shù)
downRight = cvPoint(j+k,i+k);
for(int m=topLeft.x;(m《=downRight.x) && (data[j]==0);m++)
{
if(m《0) continue;
if(m》=width) break;
if(topLeft.y》=0)
{
unsigned short temp = CV_IMAGE_ELEM
?。╰empImage,unsigned short,topLeft.y,m);
if(temp 》 0)
{
data[j] = temp;
break;
}
}
if(downRight.y 《 height)
{
unsigned short temp = CV_IMAGE_ELEM
?。╰empImage,unsigned short,downRight.y,m);
if(temp 》 0)
{
data[j] = temp;
break;
}
}
}
for(int m=topLeft.y;(m《downRight.y) && (data[j]==0);m++)
{
if(m《0) continue;
if(m》=height) break;
if(topLeft.x》0)
{
unsigned short temp = CV_IMAGE_ELEM
?。╰empImage,unsigned short,m,topLeft.x);
if(temp 》 0)
{
data[j] = temp;
break;
}
}
if(downRight.x《width)
{
unsigned short temp = CV_IMAGE_ELEM
(tempImage,unsigned short,m,downRight.x);
if(temp 》 0)
{
data[j] = temp;
break;
}
}
}
}
}
}
cvReleaseImage(&tempImage);
}
最后是中值濾波,從最內(nèi)層開始,一層層往外擴(kuò),直到找到有效值為止。
運行結(jié)果:
源圖像:
結(jié)果圖像:
附注:本來這個程序是在8位圖像上進(jìn)行的。先取得16位的unsigned short型深度圖像,然后通過cvConvertScale()函數(shù)將其轉(zhuǎn)化為8位的unsigned char型,結(jié)果在進(jìn)行去噪的時候怎么都不對,將unsigned char型的數(shù)據(jù)放到matlab中一看,發(fā)現(xiàn)在unsigned short型數(shù)據(jù)中為0值的像素莫名其妙的在unsigned char型里有了一個很小的值(比如說1, 2, 3, 4, 5什么的,就是不為0)。很奇怪,不知道OpenCV中是怎么搞的??磥磉€是源數(shù)據(jù)靠譜,于是將其改為16位的unsigned short型,結(jié)果形勢一片大好。
========
評論
查看更多