使用循环队列的三帧帧差法
帧差法是背景减图法中的一种,只不过是帧差法不需要建模,因为它的背景模型就是上一帧的图,所以速度非常快。对于帧差法的”双影”现象,有人提出来了三帧差法。其原理如下所示:
1. 由I(t) - I(t-1)得到前景图 F1
2. 由I(t+1) - I(t)得到前景图 F2
3. F1 ∩ F2得到前景图 F3
为了减少图像深拷贝带来的时间开销,我使用了一个储存三帧图像的循环队列,只需调整队首、队尾指针,就可以定位相对的前、中、后三帧,避免直接使用数组造成的图像数据深拷贝。
二、代码:
cpp1:使用数组存储三帧图像
#include "stdio.h" #include <cstdio> #include <windows.h> #include "highgui.h" #include "cv.h" using namespace std; int main() { int ncount=0; IplImage *image1=NULL; IplImage *image2=NULL; IplImage *image3=NULL; IplImage *Imask =NULL; IplImage *Imask1=NULL; IplImage *Imask2=NULL; IplImage *Imask3=NULL; IplImage *mframe=NULL; DWORD start=GetTickCount(); CvCapture *capture = cvCreateFileCapture("E:\\Test\\video\\fire1.mp4"); int fps = cvGetCaptureProperty(capture,CV_CAP_PROP_FPS); printf("\n普通三帧帧差法\n"); printf("\n视频帧率: %d 帧/秒\n",fps); int frameNum = (int) cvGetCaptureProperty(capture, CV_CAP_PROP_FRAME_COUNT); printf("\n总帧数%d\n",frameNum); while(mframe=cvQueryFrame(capture)) { ncount += 1; //初始化 if(!mframe){ //视频结束 break; } if(ncount==1) { image1=cvCreateImage(cvGetSize(mframe),IPL_DEPTH_8U,1); image2=cvCreateImage(cvGetSize(mframe),IPL_DEPTH_8U,1); image3=cvCreateImage(cvGetSize(mframe),IPL_DEPTH_8U,1); Imask =cvCreateImage(cvGetSize(mframe),IPL_DEPTH_8U,1); Imask1=cvCreateImage(cvGetSize(mframe),IPL_DEPTH_8U,1); Imask2=cvCreateImage(cvGetSize(mframe),IPL_DEPTH_8U,1); Imask3=cvCreateImage(cvGetSize(mframe),IPL_DEPTH_8U,1); cvCvtColor(mframe,image1,CV_BGR2GRAY); printf("视频帧宽度%d\n",mframe->width); printf("视频帧高度%d\n",mframe->height); } if(ncount==2) cvCvtColor(mframe,image2,CV_BGR2GRAY); if(ncount>=3) { if(ncount==3) cvCvtColor(mframe,image3,CV_BGR2GRAY); else { cvCopy(image2,image1); cvCopy(image3,image2); cvCvtColor(mframe,image3,CV_BGR2GRAY); } cvAbsDiff(image2,image1,Imask1); cvAbsDiff(image3,image2,Imask2); cvThreshold(Imask1,Imask1,20, 255, CV_THRESH_BINARY); cvThreshold(Imask2,Imask2,20, 255, CV_THRESH_BINARY); cvAnd(Imask1,Imask2,Imask); cvShowImage("diff Frame",Imask); cvWaitKey(33); } } DWORD finish=GetTickCount(); cout<<finish-start<<"ms"<<endl; return 0; }
cpp2:使用循环队列存储三帧图像
#include "stdio.h" #include <cstdio> #include <windows.h> #include "highgui.h" #include "cv.h" #define MAX_QUEUE 4 using namespace std; int main() { IplImage *diffQueue[4] = {NULL};//用于三帧帧差法的循环队列,增加一个空位用来队列判满 int front ,rear; //队头,队尾 int ncount = 0; //记录帧数 IplImage *frontImg = NULL; //前一帧 IplImage *midImg = NULL; //中间帧 IplImage *rearImg = NULL; //后一帧 IplImage *Imask = NULL; IplImage *Imask1=NULL; IplImage *Imask2=NULL; IplImage *Imask3=NULL; IplImage *mframe=NULL; DWORD start=GetTickCount(); CvCapture *capture = cvCreateFileCapture("E:\\Test\\video\\fire1.mp4"); int fps = cvGetCaptureProperty(capture,CV_CAP_PROP_FPS); printf("\n使用循环队列的三帧帧差法\n"); printf("\n视频帧率: %d 帧/秒\n",fps); int frameNum = (int) cvGetCaptureProperty(capture, CV_CAP_PROP_FRAME_COUNT); printf("\n总帧数%d\n",frameNum); while(mframe=cvQueryFrame(capture)) { ncount ++; //初始化 if(!mframe){ //视频结束 break; } if(ncount==1) { Imask =cvCreateImage(cvGetSize(mframe),IPL_DEPTH_8U,1); Imask1=cvCreateImage(cvGetSize(mframe),IPL_DEPTH_8U,1); Imask2=cvCreateImage(cvGetSize(mframe),IPL_DEPTH_8U,1); rear = front = 0; //初始化队首、队尾 for(int i = 0;i < MAX_QUEUE;i++){ //初始化循环队列 diffQueue[i] = cvCreateImage(cvGetSize(mframe),mframe->depth,1);//循环队列只存需要帧差的灰度图 } rear = (rear + 1)%MAX_QUEUE; //第一帧疑似火焰图像入队 cvCvtColor(mframe,diffQueue[rear],CV_BGR2GRAY); //将图形帧的灰度图保存在循环队列中 printf("\n视频帧宽度%d\n",mframe->width); printf("视频帧高度%d\n\n",mframe->height); } //队列不满则火焰疑似帧继续入队 else if((rear+1)%MAX_QUEUE != front){ //for Debug,March.12,2015 //printf("\n入队\n"); rear = (rear + 1)%MAX_QUEUE; cvCvtColor(mframe,diffQueue[rear],CV_BGR2GRAY); //疑似火焰图像入队 } //队满,即队列中符合要求的视频帧数为3,开始对中间帧的每一个ROI进行帧差法 else{ frontImg = diffQueue[(front+1)%MAX_QUEUE]; midImg = diffQueue[(front+2)%MAX_QUEUE]; rearImg = diffQueue[(front+3)%MAX_QUEUE]; cvAbsDiff(midImg,frontImg,Imask1); cvAbsDiff(rearImg,midImg,Imask2); cvThreshold(Imask1,Imask1,20, 255, CV_THRESH_BINARY); cvThreshold(Imask2,Imask2,20, 255, CV_THRESH_BINARY); cvAnd(Imask1,Imask2,Imask); cvShowImage("diff Frame",Imask); cvWaitKey(33); front = (front+1)%MAX_QUEUE; //前一帧出队 } } DWORD finish=GetTickCount(); cout<<finish-start<<"ms"<<endl; return 0; }
三、实验对比
普通三帧帧差法处理速度: 48.8ms/帧
使用循环队列帧差法处理速度:25.5ms/帧
相对于使用cvCopy的普通三帧帧差法,使用循环队列储存图像的用时只是前者的52.2%。
时间: 2024-11-05 21:47:17