#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\n", 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;
}