opencv 背景差分法 改进OTSU阈值去噪

/*
 *1)头文件cvaux.h的库文件需要链接到linker. cvaux210d.lib, cvaux210.lib分别是debug和release版本. 
 *   否则有些函数会出现error:: LINK 2101 
 *2)cvAdaptiveThreshold, cvThreshold的src和dst图像必须是同类型的单通道图像
 *3)重要: 函数退出之后,函数中的动态变量会随着栈的退出全部清空.
 *   要保存上次操作的结果,则在函数内声明为静态变量.或者在要调用的函数里先声明
 *4)cvAdaptiveThreshold()的处理结果不好,此函数试图找出所有的物体轮廓.用在背景差分中,会找到不想要的物体
 *5)当没有前景物体时,OTSU算法会把路面显示出来.因为阈值是自动调整的.解决办法,做一个阈值筛选
 *6)VedioControl() 未实现.
 *    将cvWaitKey()封装到VedioControl()中, 如果不触发按键,VedioControl()不会退出.造成无法自动播放
 *
 *Date: 2012/4/6
 *Author: Rocky Chen
 */
#include
#include "stdafx.h"
#include
#include
#include
#include
#include "cvaux.h" 
#include "cxmisc.h"


using namespace std;


void BackgroundDiff(IplImage* SrcImg, IplImage* FroundImg, IplImage* BackgroundImg, int nFrmNum, int threshold_method);
void cvOtsu(IplImage *src, int *thresholdValue);
void PrintVedioInfo(CvCapture* pCapture, IplImage* img);
void VedioControl();  //未实现


 //视频控制全局变量,
// 's' 画面stop
// 'q' 退出播放
// 'p' 打印OTSU算法中找到的阈值
char ctrl = NULL; 


int main( int argc, char** argv )
{
  //声明IplImage指针
  IplImage* pFrame = NULL; 
  IplImage* pFroundImg = NULL;
  IplImage* pBackgroundImg = NULL;


  IplImage* pFroundImg_c = NULL;
  IplImage* pBackgroundImg_c = NULL;


    //大门背景建模良好  best
//  CvCapture* pCapture = cvCreateFileCapture("D:\\C++ Projects\\OpenCV_project\\test_video\\gate_11ms_00-30s.avi"); 
//  CvCapture* pCapture = cvCreateFileCapture("D:\\C++ Projects\\OpenCV_project\\img_video\\video.short.mjpg.avi");
  CvCapture* pCapture = cvCreateFileCapture("D:\\C++ Projects\\OpenCV_project\\img_video\\video.long.mjpg.avi");
  int nFrmNum = 0;


  //创建窗口
  cvNamedWindow("video", 1);
  cvNamedWindow("background",1);
  cvNamedWindow("OTSU foreground",1);
  cvNamedWindow("改进的OTSU foreground",1);


  //使窗口有序排列
  cvMoveWindow("video", 30, 0);
  cvMoveWindow("background", 360, 0);
  cvMoveWindow("OTSU foreground", 690, 0);
  cvMoveWindow("改进的OTSU foreground", 690, 320);
  //逐帧读取视频
  while(pFrame = cvQueryFrame( pCapture ))
    {

 nFrmNum++;
 //视频控制
 if( (ctrl = cvWaitKey(1000/180)) =='s' )  cvWaitKey();
 else if(ctrl == 'p') cout << "Current Frame = " << nFrmNum << endl;
 else if( ctrl =='q' ) break;


 if(nFrmNum ==1)
 {  
   pBackgroundImg = cvCreateImage(cvGetSize(pFrame),  8,1);
 pFroundImg = cvCreateImage(cvGetSize(pFrame),  8,1);
 pBackgroundImg_c = cvCreateImage(cvGetSize(pFrame),  8,1); //对比算法的图像
 pFroundImg_c = cvCreateImage(cvGetSize(pFrame),  8,1);
 }


 BackgroundDiff(pFrame,pFroundImg,pBackgroundImg, nFrmNum, CV_THRESH_OTSU);  //普通OTSU
 BackgroundDiff(pFrame,pFroundImg_c,pBackgroundImg_c, nFrmNum, CV_THRESH_BINARY); //阈值筛选后的OTSU
 //打印视频信息,画面控制
 PrintVedioInfo(pCapture, pFroundImg);
 //显示图像
 cvShowImage("video", pFrame);
 cvShowImage("background", pBackgroundImg);
 cvShowImage("OTSU foreground", pFroundImg);
 cvShowImage("改进的OTSU foreground", pFroundImg_c);
   }  //while
  
  //销毁窗口
  cvDestroyAllWindows();
  //释放图像和矩阵
  cvReleaseImage(&pFroundImg);
  cvReleaseImage(&pBackgroundImg);
  cvReleaseCapture(&pCapture);


  return 0;
}




void VedioControl()
{  


}


/*
 *输出文字到图像
 */
void PrintVedioInfo(CvCapture* pCapture, IplImage* img)
{
assert( pCapture != NULL);
double frames = cvGetCaptureProperty(pCapture, CV_CAP_PROP_POS_FRAMES);  //视频当前帧数 
 double fps = cvGetCaptureProperty(pCapture,CV_CAP_PROP_FPS); //获得视频每秒帧数
 char str[255];
 sprintf(str,"%4.2f FPS %4.2f frames",fps,frames);  // 将浮点数转化为字符串
 CvPoint location = cvPoint(20,20); // 建立字符串打印的位置
 CvScalar color = cvScalar(255,255,255);
 CvFont font;  //建立字体变量
 cvInitFont(&font, CV_FONT_HERSHEY_PLAIN, 1.0,1.0);  //字体设置
 cvPutText(img, str, location, &font,color);  //打印文本到图像
}


/********
*背景差分函数,求前景目标
*重要: 函数退出之后,函数中的动态变量会随着栈的退出全部清空.
*要保存上次操作的结果,则在函数内声明为静态变量.或者在要调用的函数里先声明
*
********/
void BackgroundDiff(IplImage* SrcImg, IplImage* FroundImg, IplImage* BackgroundImg, int nFrmNum, int threshold_method = CV_THRESH_OTSU)
{


static IplImage* SrcImg_gray = NULL;//源图像的灰度图像
static IplImage* SrcImg_grayf =NULL;  //单通道浮点图像用于背景建模
static IplImage* FroundImgf = NULL;
static IplImage* BackgroundImgf = NULL;
static   IplImage* FroundImg_temp = NULL;
    if(nFrmNum == 1)
{
 
 SrcImg_gray = cvCreateImage(cvGetSize(SrcImg),  8,1);
 FroundImg_temp = cvCreateImage(cvGetSize(SrcImg),  8,1);
 BackgroundImgf = cvCreateImage(cvGetSize(SrcImg),  32,1);  //浮点图像
 FroundImgf = cvCreateImage(cvGetSize(SrcImg),  32,1);
 SrcImg_grayf = cvCreateImage(cvGetSize(SrcImg),  32,1);


 //RGB图像先转化成8位单通道图像,再转化为浮点.
 cvCvtColor(SrcImg, BackgroundImg, CV_BGR2GRAY); 
 cvCvtColor(SrcImg, FroundImg, CV_BGR2GRAY); 
 cvConvert(BackgroundImg,BackgroundImgf);
   cvConvert(FroundImg,FroundImgf);
}
else
{
 cvCvtColor(SrcImg, SrcImg_gray, CV_BGR2GRAY);  //SrcImg_gray在上次函数退出的时候被程序栈回收
   cvConvert(SrcImg_gray,SrcImg_grayf);
 //当前帧跟背景图相减
    cvAbsDiff(SrcImg_grayf, BackgroundImgf, FroundImgf);
cvConvert(FroundImgf,FroundImg_temp);  //浮点转化为整点
 //二值化前景图
 int threshold_otsu =0;
cvOtsu(FroundImg_temp, &threshold_otsu);


 if(threshold_method == CV_THRESH_OTSU)
 {
  cvThreshold(FroundImg_temp, FroundImg, 0, 255.0, CV_THRESH_OTSU); //对比自适应阈值化
 // cvAdaptiveThreshold(FroundImg_temp, FroundImg, 255.0, 0, 0, 51);  //src和dst必须同时是8bit或浮点图像
 }
 else
 {
    cvThreshold(FroundImg_temp, FroundImg, threshold_otsu, 255.0, CV_THRESH_BINARY); 
 }
     cvSegmentFGMask( FroundImg ); //对前景做连通域分割
 //更新背景
 cvRunningAvg(SrcImg_grayf, BackgroundImgf, 0.003, 0);  //必须是浮点图像,因为会有小数出现
 cvConvert(BackgroundImgf,BackgroundImg);
}
}


/********
 *OTSU大津法
 * thresholdValue 为使类间方差最大的阈值
 * 当找到的阈值小于一个修正阈值,返回此修正阈值.防止没有前景物体时,将背景找出来
 ********/
void cvOtsu(IplImage *src, int *thresholdValue)
{  
    int deltaT = 0; //光照调节参数
uchar grayflag =1;
IplImage* gray = NULL;
if(src->nChannels != 1) //检查源图像是否为灰度图像
{
gray = cvCreateImage(cvGetSize(src), 8, 1);
cvCvtColor(src, gray, CV_BGR2GRAY);
grayflag = 0;
}
else gray = src;
uchar* ImgData=(uchar*)(gray->imageData);   
int thresholdValue_temp = 1;
    int ihist[256];   //图像直方图,256个点  
   
    int i, imgsize; //循环变量,图像尺寸
    int n, n1, n2;  //n 非零像素个数, n1 前景像素个数, n2 背景像素个数
    double m1, m2, sum, csum, fmax, sb;//m1前景灰度均值,m2背景灰度均值
    //对直方图置零   
    memset(ihist, 0, sizeof(ihist));   
    //生成直方图  
    imgsize = (gray->widthStep)*(gray->height);//图像数据总数 
    for (i=0; i     {   
    ihist[((int)(*ImgData))&255]++;//灰度统计 '&255'防止指针溢出  
    ImgData++;//像素遍历
    }   
    // set up everything   
    sum=csum=0.0;   
    n=0;   
    for (i=0; i<255; i++)   
    {   
    sum+=(double)i*(double)ihist[i];  // x*f(x)质量矩   
    n+= ihist[i];   //f(x)质量 像素总数
    }


deltaT = (int)(sum/imgsize); //像素平均灰度
deltaT = deltaT>>1; //与之矫正,delatT = v*n; v=0.5
   
    if (!n)   
    {//图像全黑,输出警告
    fprintf (stderr, "NOT NORMAL thresholdValue=160\n");   
    }   
    // OTSU算法
    fmax=-1.0;   
    n1=0;   
    for (i=0; i<255; i++)   
    {   
        n1+= ihist[i];   
        if (n1==0) {continue;}
        n2=n-n1;   
        if (n2==0) {break;}   
        csum += (double)i *ihist[i];   
        m1=csum/n1;   
        m2=(sum-csum)/n2;   
        sb=(double)n1*(double)n2*(m1-m2)*(m1-m2); //计算类间方差,  公式已简化  
        if (sb>fmax)   
        {   
            fmax=sb;   
            thresholdValue_temp=i;  //找到使类间方差最大的灰度值i   
        }  
    }   
   
if(thresholdValue_temp < 20) *thresholdValue = 20;  //阈值筛选
else *thresholdValue = thresholdValue_temp;
if( ctrl == 'p')  //ctrl  = cvWaitKey(100),且是全局变量
{
   cout << "OTSU thresholdValue = " << thresholdValue_temp<<
", Returned thresholdValue = " << *thresholdValue<<'\n'< }
if(!grayflag) cvReleaseImage(&gray);
}  


/***********
*轮廓提取
************/
void Labeling(IplImage *src, IplImage *dst) 
{
    CvMemStorage* storage = 0;
    storage = cvCreateMemStorage(0); //开辟默认大小的空间
    CvSeq* contour=0;
    cvCopy(src,dst,0);
    cvFindContours( dst, storage, &contour, sizeof(CvContour),
              CV_RETR_EXTERNAL, CV_CHAIN_APPROX_SIMPLE ); //外边缘
    int num=0;
    for( ;contour!=0; contour=contour->h_next)
    {
          CvRect rect;
      rect = cvBoundingRect(contour,0);//得到目标外接矩形
          num++;
        if((rect.height + rect.width) >= 16)
        cvRectangle(src,cvPoint(rect.x,rect.y),cvPoint(rect.x+rect.width,rect.y+rect.height),
                  CV_RGB(255, 255,255),1,8);//绘制目标外接矩形
// cvRectangle(dst,cvPoint(rect.x,rect.y),cvPoint(rect.x+rect.width,rect.y+rect.height),
 //                 CV_RGB(255, 255,255),1,8);//绘制目标外接矩形
    }
}

你可能感兴趣的:(OpenCV)