#include "cv.h" #include <cxcore.h> #include <highgui.h> #include <cvaux.h>//必须引此头文件 #include "opencv2/imgproc/imgproc.hpp" typedef uchar byte; #define QUEUE_MAX_SIZE 100 #define RANGE 5 #define UPDATE_RATE (0.01) #define RATIO_QUEUE_LEN 1000 typedef unsigned int uint32; #define MIN(x,y) (x>y?y:x) int g_QueueSize=10; int threshold=20; int g_CarWidth=10;//车的宽和高 int g_CarHeight=10; int g_CarArea=20;//检测的范围 int g_Erosion_kernel_size=3; int g_Dilation_kernel_size=3; struct PIXEL_RECORD { byte PX_data; uint32 PX_count; }; struct QUEUE { struct RECORD { double ratio; uint64 count; }record [RATIO_QUEUE_LEN]; int curPos; double MostRatio; }; struct RECORD { PIXEL_RECORD queue[QUEUE_MAX_SIZE]; uint32 CurPos;//the postion of last element in queue; }; void QUEUE_init(QUEUE* queue) { for (int i=0;i<RATIO_QUEUE_LEN;i++) { queue->record[i].ratio=0.0; queue->record[i].count=0; } queue->curPos=0; queue->MostRatio=0.0; } double UpatePixelRatio(QUEUE& queue,double ratio) { int i=0; int MinPos=0; int MaxPos=0; for (;i<RATIO_QUEUE_LEN;i++) { if(queue.record[i].ratio == ratio) { queue.record[i].count++; break; } } if (i==RATIO_QUEUE_LEN) { if(queue.curPos<RATIO_QUEUE_LEN) { queue.record[queue.curPos++].ratio=ratio; } if(queue.curPos== RATIO_QUEUE_LEN) { uint32 MinCount=0xFFFFFFFF; for(int j=0;j<queue.curPos;j++) { if(queue.record[j].count<MinCount) { MinPos=j; MinCount=queue.record[j].count; } } queue.record[MinPos].ratio=ratio; } } uint32 MaxCount=0; for (int j=0;j<RATIO_QUEUE_LEN;j++) { if (queue.record[j].count>MaxCount) { MaxCount=queue.record[j].count; MaxPos=j; } } return queue.record[MaxPos].ratio; } uint32 GetMaxValueData(const PIXEL_RECORD* arr,int len) { uint32 tmp=0; uint32 MaxPos=0; for (int i=0;i<len-1;i++) { if(arr[i].PX_count>tmp) { tmp=arr[i].PX_count; MaxPos=i; } } return arr[MaxPos].PX_data; } uint32 GetMinValuePos(const PIXEL_RECORD* arr,int len) { byte tmp=255; uint32 MinPos=g_QueueSize; for (int i=0;i<len-1;i++) { if(arr[i].PX_count<tmp) { tmp=arr[i].PX_count; MinPos=i; } } return MinPos; } byte UpdateQueue(RECORD* record,byte Vpixel,uint32 nFrm) { PIXEL_RECORD tmp; int i=0; double UpdateRate; for (;i<g_QueueSize;i++) { if ((record->queue[i].PX_data)/RANGE == Vpixel/RANGE ) { record->queue[i].PX_count++; record->queue[i].PX_data=(record->queue[i].PX_data)*(1-UPDATE_RATE)+Vpixel*UPDATE_RATE; } } if (i==g_QueueSize) { if (record->CurPos < g_QueueSize) { record->queue[record->CurPos++].PX_data=Vpixel; } else { uint32 Pos=GetMinValuePos(record->queue,record->CurPos); record->queue[Pos].PX_data=Vpixel; } } return GetMaxValueData(record->queue,record->CurPos); } void RecordFrame(IplImage* pFrame,IplImage* pBKImg ,const int nFrm,RECORD* pRecordMap,QUEUE& PX_queue) { IplImage* pFrmGrayImg=NULL; pFrmGrayImg=cvCreateImage(cvSize(pFrame->width, pFrame->height), IPL_DEPTH_8U,1); if(pFrame->colorModel != CV_8U && pFrame->nChannels != 1) { cvCvtColor(pFrame,pFrmGrayImg,CV_BGR2GRAY); } else pFrmGrayImg=pFrame; uint32 ROW=pFrmGrayImg->width; uint32 COL=pFrmGrayImg->height; for (uint32 i=0;i<ROW*COL;i++) { pBKImg->imageData[i]=UpdateQueue(pRecordMap+i,pFrmGrayImg->imageData[i], nFrm); } } void TouchTag(int* buf,int ROW,int COL,int& ctag,int x,int y) { int tag=ctag; bool mark=0; for (int i=-1;i<2;i++) { for (int j=-1;j<2;j++) { if (buf[(x+i)*COL+(y+j)]>0) { tag= MIN(buf[(x+i)*COL+(y+j)],tag); mark=1; } } } if(mark==0) { ctag++; tag=ctag; } buf[x*COL+y]=tag; } size_t DetectRect(CvMat* Img,int* buf) { uint32 ROW=Img->rows; uint32 COL=Img->cols; int cTag=0; for (int i=1;i<ROW -1;i++)//! 图像中的横轴为X轴,即为列坐标 { for (int j=1;j<COL-1;j++) { if(*(byte*)(CV_MAT_ELEM_PTR(*Img,i,j)) == 255) { TouchTag(buf,ROW,COL,cTag,i,j); } } } return cTag; } struct TAG_RECORD { uint32 MinX; uint32 MaxX; uint32 MinY; uint32 MaxY; uint32 count; }; void GetRect(std::vector<TAG_RECORD>& TagArry,size_t cTag,int* buf,uint32 ROW,uint32 COL) { for (size_t i=0;i<TagArry.size();i++) { TagArry[i].MaxX=0; TagArry[i].MaxY=0; TagArry[i].MinX=COL; TagArry[i].MinY=ROW; TagArry[i].count=0; } int index=0; for (int i=0;i<ROW;i++) { for (int j=0;j<COL;j++) { index=buf[i*COL+j]; if (index>0 && index<TagArry.size()) { TagArry[index].count++; if (i<TagArry[index].MinY) { TagArry[index].MinY=i; } if(i>TagArry[index].MaxY) { TagArry[index].MaxY=i; } if (j<TagArry[index].MinX) { TagArry[index].MinX=j; } if (j>TagArry[index].MaxX) { TagArry[index].MaxX=j; } } } } } void onTrackbarSlide(int) { } int main() { //声明IplImage指针 IplImage* pFrame = NULL; IplImage* pFrImg = NULL; IplImage* pBkImg = NULL; CvMat* pDiff =NULL; CvMat* pFrameMat = NULL; CvMat* pFrMat = NULL; CvMat* pBkMat = NULL; RECORD* pRecordMap=NULL; int* TagMap=NULL; CvCapture* pCapture = NULL; CvRect FrRect; int nFrmNum = 0; int ncount=0; QUEUE PX_queue; size_t RecordSize; int ROW; int COL; //! 文字输出 CvFont font; char str[20]="flow:"; double hscale = 0.6; double vscale = 0.6; int linewidth = 1; double flow; char ncar[20]="cars:"; char nframe[20]="frames:"; char sframeRate[30]="[FPS]:"; //创建窗口 cvNamedWindow("video", 1); cvNamedWindow("background",1); cvNamedWindow("foreground",1); //使窗口有序排列 cvMoveWindow("video", 30, 0); cvMoveWindow("background", 360, 0); cvMoveWindow("foreground", 690, 0); char* filename="d:\test.mp4"; //打开视频文件 if( !(pCapture = cvCaptureFromFile(filename))) { fprintf(stderr, "Can not open video file %s ", filename); return -2; } QUEUE_init(&PX_queue); double framesRate=cvGetCaptureProperty(pCapture,CV_CAP_PROP_FPS); int frames=cvGetCaptureProperty(pCapture,CV_CAP_PROP_FRAME_COUNT); cvCreateTrackbar("QueueSize","background", &g_QueueSize, QUEUE_MAX_SIZE,onTrackbarSlide); cvCreateTrackbar("CarWiedth","video", &g_CarWidth, 150,onTrackbarSlide); cvCreateTrackbar("CarHeight","video", &g_CarHeight, 150,onTrackbarSlide); cvCreateTrackbar("CarArea","video", &g_CarArea, 200,onTrackbarSlide); cvCreateTrackbar("Threshold","foreground", &threshold, 255,onTrackbarSlide); cvCreateTrackbar("dilate","foreground", &g_Dilation_kernel_size, 20,onTrackbarSlide); cvCreateTrackbar("erode","foreground", &g_Erosion_kernel_size, 20,onTrackbarSlide); //逐帧读取视频 while(pFrame = cvQueryFrame( pCapture )) { nFrmNum++; //如果是第一帧,需要申请内存,并初始化 if(nFrmNum == 1) { pBkImg = cvCreateImage(cvSize(pFrame->width, pFrame->height), IPL_DEPTH_8U,1); pFrImg = cvCreateImage(cvSize(pFrame->width, pFrame->height), IPL_DEPTH_8U,1); pDiff =cvCreateMat(pFrame->height, pFrame->width, CV_8U); pBkMat = cvCreateMat(pFrame->height, pFrame->width, CV_32FC1); pFrMat = cvCreateMat(pFrame->height, pFrame->width, CV_32FC1); pFrameMat = cvCreateMat(pFrame->height, pFrame->width, CV_32FC1); ROW=pDiff->rows; COL=pDiff->cols; //转化成单通道图像再处理 cvCvtColor(pFrame, pFrImg, CV_BGR2GRAY); RecordSize=pFrame->width * pFrame->height; pRecordMap=new RECORD[RecordSize]; TagMap=new int[RecordSize]; for (int i=0;i<RecordSize;i++) //init { for (int j=0;j<g_QueueSize;j++) { pRecordMap[i].queue[j].PX_count=0; pRecordMap[i].queue[j].PX_data=0; pRecordMap[i].CurPos=0; } } cvConvert(pFrImg, pFrameMat); cvConvert(pBkImg, pBkMat); } else { cvCvtColor(pFrame, pFrImg, CV_BGR2GRAY); cvSmooth(pFrImg, pFrImg, CV_GAUSSIAN, 3, 0, 0); RecordFrame(pFrImg,pBkImg,nFrmNum,pRecordMap,PX_queue); //高斯滤波先,以平滑图像 cvSmooth(pFrameMat, pFrameMat, CV_GAUSSIAN, 3, 0, 0); //当前帧跟背景图相减 cvAbsDiff(pFrImg, pBkImg, pDiff); //二值化前景图 cvThreshold(pDiff, pDiff, threshold, 255, CV_THRESH_BINARY); //进行形态学滤波,去掉噪音 IplConvKernel* element_erode=cvCreateStructuringElementEx(g_Erosion_kernel_size + 1, g_Erosion_kernel_size+1, g_Erosion_kernel_size/2, g_Erosion_kernel_size/2, 0); IplConvKernel* element_dilate = cvCreateStructuringElementEx(g_Dilation_kernel_size + 1, g_Dilation_kernel_size+1, g_Dilation_kernel_size/2, g_Dilation_kernel_size/2, 0); //! 腐蚀操作 cvErode( pDiff, pDiff, element_erode ); /// 膨胀操作 cvDilate( pDiff, pDiff, element_dilate ); // cvErode(pDiff, pDiff, 0, 1); // cvDilate(pDiff, pDiff, 0, 1); // for (int i=0;i<RecordSize;i++) { TagMap[i]=0; } size_t cTag=DetectRect(pDiff,TagMap); TAG_RECORD tmp; tmp.MinX=pDiff->cols; tmp.MinY=pDiff->rows; tmp.MaxX=0; tmp.MaxY=0; std::vector<TAG_RECORD> TagRecord(cTag+1,tmp); GetRect(TagRecord,cTag,TagMap,pDiff->rows,pDiff->cols); ncount=0; for (size_t i=1;i<TagRecord.size();i++) { if (TagRecord[i].count<g_CarArea) { continue; } int MinX=TagRecord[i].MinX; int MinY=TagRecord[i].MinY; int MaxX=TagRecord[i].MaxX; int MaxY=TagRecord[i].MaxY; if (MaxX-MinX<g_CarWidth || MaxY-MinY<g_CarHeight) { continue; } ncount++; cvRectangle( pFrame, cvPoint(MinX,MinY), cvPoint(MaxX,MaxY), CV_RGB(255,0,0), 1, 8, 0 ); } flow=framesRate*ncount/(double)nFrmNum; sprintf(nframe+7,"%d",nFrmNum); sprintf(ncar+5,"%d",ncount); sprintf(str+5,"%f",flow); sprintf(sframeRate+6,"%f",framesRate); cvInitFont(&font,CV_FONT_HERSHEY_SIMPLEX | CV_FONT_ITALIC,hscale,vscale,0,linewidth); cvPutText(pFrame,sframeRate,cvPoint(0,15),&font,cvScalar(0,0,0)); cvPutText(pFrame,ncar,cvPoint(0,35),&font,cvScalar(0,0,0)); cvPutText(pFrame,nframe,cvPoint(0,55),&font,cvScalar(0,0,0)); cvPutText(pFrame,str,cvPoint(0,75),&font,cvScalar(0,0,0)); //显示图像 cvShowImage("video", pFrame); cvShowImage("background", pBkImg); cvShowImage("foreground", pDiff); //如果有按键事件,则跳出循环 //此等待也为cvShowImage函数提供时间完成显示 //等待时间可以根据CPU速度调整 if( cvWaitKey(2) >= 0 ) break; } } cvWaitKey(); //销毁窗口 cvDestroyWindow("video"); cvDestroyWindow("background"); cvDestroyWindow("foreground"); //释放图像和矩阵 cvReleaseImage(&pFrImg); cvReleaseImage(&pBkImg); cvReleaseMat(&pFrameMat); cvReleaseMat(&pFrMat); cvReleaseMat(&pBkMat); cvReleaseCapture(&pCapture); delete[] pRecordMap; delete[] TagMap; return 0; }