程序出处:http://blog.csdn.net/cwjcwj520/article/details/7433103,感谢博主!
#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\\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>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'<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);//绘制目标外接矩形
}
}