程序出处:http://blog.csdn.net/cwjcwj520/article/details/7433103,感谢博主!
- #include <stdio.h>
- //#include "stdafx.h"
- #include <cv.h>
- #include <cxcore.h>
- #include <highgui.h>
- #include <iostream>
- #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\\img_video\\video.long.mjpg.avi");
- CvCapture* pCapture=cvCaptureFromCAM(0);//从摄像头读取视频。cvCaptureFromAVI("2.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);
- // pCapture = cvCaptureFromAVI("2.avi");
- //逐帧读取视频
- 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 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<imgsize;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'<<endl;
- }
- 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);//绘制目标外接矩形
- }
- }
|