基于opencv的视频处理——基础数据结构
在一个封装的还算比较好的库中,一般都不会直接采用那些基本的数据结构像char, int 之类,一是
不具有可读性,二是不方便修改移植。通常是通过typedef 来改变。下面介绍opencv中基础数据结构。
先介绍点,CvPoint, CvPoint2D32f, CvPoint3D32f,下面给出原型。
二维坐标系下的点,类型为整型
typedef struct CvPoint
{
int x; /* X坐标, 通常以0为基点 */
int y; /* y坐标, 通常以0为基点 */
}
CvPoint;
二维坐标下的点,类型为浮点
typedef struct CvPoint2D32f
{
float x; /* X坐标, 通常以0为基点*/
float y; /* Y坐标, 通常以0为基点*/
}
CvPoint2D32f;
三维坐标下的点,类型为浮点
typedef struct CvPoint3D32f
{
float x; /* x-坐标, 通常基于0 */
float y; /* y-坐标, 通常基于0 */
float z; /* z-坐标, 通常基于0 */
}
CvPoint3D32f;
大家很明显可以看出来,这些点还是由基本的数据类型构成的。下面再顺便提一下相应类型的构造函数
CvPoint 型
inline CvPoint cvPoint( int x, int y );
CvPoint2D32f型
inline CvPoint2D32f cvPoint2D32f( double x, double y );
CvPoint3D32f型
inline CvPoint3D32f cvPoint3D32f( double x, double y, double z );
下面再介绍几个简单的
CvSize
矩形框大小,以像素为精度
typedef struct CvSize
{
int width; /* 矩形宽 */
int height; /* 矩形高 */
}
CvSize;
CvSize2D32f
以亚像素精度标量矩形框大小
typedef struct CvSize2D32f
{
float width; /* 矩形宽 */
float height; /* 矩形高 */
}
CvSize2D32f;
CvRect
矩形框的偏移和大小
typedef struct CvRect
{
int x; /* 方形的最左角的x-坐标 */
int y; /* 方形的最上或者最下角的y-坐标 */
int width; /* 宽 */
int height; /* 高 */
}
CvRect;
CvScalar
可存放在1-,2-,3-,4-TUPLE类型的捆绑数据的容器
typedef struct CvScalar
{
double val[4]
}
CvScalar;
具体用法会在后续文章中说到,
现在介绍点重要的
IplImage
IPL 图像头
typedef struct _IplImage
{
int nSize; /* IplImage大小,=sizeof(IplImage)*/
int ID; /* 版本 (=0)*/
int nChannels; /* 大多数OPENCV函数支持1,2,3 或 4 个通道 */
int alphaChannel; /* 被OpenCV忽略 */
int depth; /* 像素的位深度: IPL_DEPTH_8U, IPL_DEPTH_8S, IPL_DEPTH_16U,IPL_DEPTH_16S,
IPL_DEPTH_32S, IPL_DEPTH_32F and IPL_DEPTH_64F 可支持 */
char colorModel[4]; /* 被OpenCV忽略 */
char channelSeq[4]; /* 被OpenCV忽略 */
int dataOrder; /* 0 - 交叉存取颜色通道,对三通道RGB图像,像素存储顺序为BGR BGR BGR
... BGR;1 - 分开的颜色通道,对三通道RGB图像,像素存储顺序为RRR...R GGG...G BBB...B。
cvCreateImage只能创建交叉存取图像 */
int origin; /* 0 - 顶—左结构, 1 - 底—左结构 (Windows bitmaps 风格) */
int align; /* 图像行排列 (4 or 8). OpenCV 忽略它,使用 widthStep 代替 */
int width; /* 图像宽像素数 */
int height; /* 图像高像素数*/
struct _IplROI *roi;/* 图像感兴趣区域. 当该值非空只对该区域进行处理 */
struct _IplImage *maskROI; /* 在 OpenCV中必须置NULL */
void *imageId; /* 同上*/
struct _IplTileInfo *tileInfo; /*同上*/
int imageSize; /* 图像数据大小(在交叉存取格式下imageSize=image->height*image-
>widthStep),单位字节*/
char *imageData; /* 指向排列的图像数据 */
int widthStep; /* 排列的图像行大小,以字节为单位 */
int BorderMode[4]; /* 边际结束模式, 被OpenCV忽略 */
int BorderConst[4]; /* 同上 */
char *imageDataOrigin; /* 指针指向一个不同的图像数据结构(不是必须排列的),是为了纠正图像内存分配准备的 */
}
IplImage;
IplImage结构来自于 Intel Image Processing Library(是其本身所具有的)。OpenCV 只支持其中的一个子集:
* alphaChannel 在OpenCV中被忽略。
* colorModel 和channelSeq 被OpenCV忽略。OpenCV颜色转换的唯一函数 cvCvtColor把原图像的颜
色空间的目标图像的颜色空间作为一个参数。
* dataOrder 必须是IPL_DATA_ORDER_PIXEL (颜色通道是交叉存取),然而平面图像的被选择通道可
以被处理,就像COI(感兴趣的通道)被设置过一样。
* align 是被OpenCV忽略的,而用 widthStep 去访问后继的图像行。
* 不支持maskROI 。处理MASK的函数把他当作一个分离的参数。MASK在 OpenCV 里是 8-bit,然而在
IPL他是 1-bit。
* tileInfo 不支持。
* BorderMode和BorderConst是不支持的。每个 OpenCV 函数处理像素的邻近的像素,通常使用单一的固定代码边际模式。
除了上述限制,OpenCV处理ROI有不同的要求。要求原图像和目标图像的尺寸或 ROI的尺寸必须(根据不同的操作,
例如cvPyrDown 目标图像的宽(高)必须等于原图像的宽(高)除以2 ±1)精确匹配,而IPL
处理交叉区域,如图像的大小或ROI大小可能是完全独立的。
这是一个很重要的数据结构,处理图像时传入的就是IplImage *Img. 对于初学者来说,首先要关注
的是成员width, height, widthstep和imageDate,比如说做一个遍历的话
int i, j;
char *data = NULL;
for(i=0; i
width; i++)
{
for(j=0; jheight; j++)
{
data = Img->imageData + i*widthStep + j; //取图片相应点的数据,要知道图片的存储是以
一个矩阵的形式
statement
}
}
最后还有一个比较重要的
CvMat
多通道矩阵
typedef struct CvMat
{
int type; /* CvMat 标识 (CV_MAT_MAGIC_VAL), 元素类型和标记 */
int step; /* 以字节为单位的行数据长度*/
int* refcount; /* 数据引用计数 */
union
{
uchar* ptr;
short* s;
int* i;
float* fl;
double* db;
} data; /* data 指针 */
#ifdef __cplusplus
union
{
int rows;
int height;
};
union
{
int cols;
int width;
};
#else
int rows; /* 行数 */
int cols; /* 列数*/
#endif
} CvMat;
========
OpenCV成长之路 视频的处理
视频中包含的信息量要远远大于图片,对视频的处理分析也越来越成为计算机视觉的主流,而本质上视频
是由一帧帧的图像组成,所以视频处理最终还是要归结于图像处理,但在视频处理中,有更多的时间维的
信息可以利用。本文主要介绍OpenCV在处理视频时的一些基本函数。
一、视频帧的读取
OpenCV为视频的读入提供了一个类VideoCapture,下面我们说明一下类的几个重要的方法:
1,打开一段视频或默认的摄像头
有两种方法,一种是在定义类的时候,一种是用open()方法。
VideoCapture capture("../video.avi");
// 方法1
capture.open("../video.avi");
// 方法2
如果把文件名换为设置ID,则可打开摄像头,默认摄像头为0。
2,获取视频帧
获取视频帧可以有多种方法
// 方法一
capture.read(frame);
// 方法二
capture.grab();
capture.retrieve(frame);
// 方法三
capture>>frame;
3,获取视频的参数
一个视频有很多参数,比如:帧率、总帧数、尺寸、格式等,VideoCapture的get方法可以获取大量这些参数。
double rate=capture.get(CV_CAP_PROP_FPS);
// 获取
long nFrame=static_cast(capture.get(CV_CAP_PROP_FRAME_COUNT));
// 获取总帧数
更加相关的参数可以参考手册。
4,设置视频帧的读取位置
VideoCapture类的set方法可以允许我们取出视频中某个位置的帧,它有一些参数,可以按时间,也可以
按帧号,还可以按视频长短的比例。
// 第100帧
double position=100.0;
capture.set(CV_CAP_PROP_POS_FRAMES,position);
// 第1e6毫秒
double position=1e6;
capture.set(CV_CAP_PROP_POS_MSEC,position);
// 视频1/2位置
double position=0.5;
capture.set(CV_CAP_PROP_POS_AVI_RATIO,position);
当然,set方法仅用于取视频帧的位置,还可以设置视频的帧率、亮度。
下面是一个将canny边缘检测应用于视频的程序:
int main()
{
VideoCapture capture("../track.avi");
if(!capture.isOpened())
return 1;
double rate=capture.get(CV_CAP_PROP_FPS);
bool stop(false);
Mat frame;
namedWindow("Canny Video");
int delay=1000/rate;
while(!stop)
{
if(!capture.read(frame))
break;
Mat result;
Canny(frame,result,100,200);
threshold(result,result,128,255,THRESH_BINARY);
imshow("Canny Video",result);
if(waitKey(delay)>=0)
stop=true;
}
capture.release();
}
二、视频的写入
视频的写入与读取类似,OpenCV中是使用VideoWriter类来实现的,这个类有几个方法,都很简单。除了
构造函数外,提供了open、IsOpen、write、和重载操作符<<
值得注意的是OpenCV里对视频的编码解码等支持并不是很良好,所以不要希望用这个类去实现摄像头图像
的获取与转码,有兴趣的可以参考FFmpeg库。
VideoWriter::VideoWriter(const string& filename, int fourcc, double fps, Size frameSize,
bool isColor=true);
bool VideoWriter::open(const string& filename, int fourcc, double fps, Size frameSize, bool
isColor=true);
上面是类的构造函数与open方法,它们的参数相同,首先指定文件名,第二个参数是编码格式,OpenCV里
提供了很多种的编码格式,如CV_FOURCC(‘P’,’I’,’M’,’1’)是MPEG-1格式,CV_FOURCC(‘M’,’
G’,’P’,’G’)为motion-jpeg格式。
第三个参数为帧率,第4个参数为视频的尺寸大小。
VideoCapture capture("../track.avi");
double rate=capture.get(CV_CAP_PROP_FPS);
Size videoSize(capture.get(CV_CAP_PROP_FRAME_WIDTH),
capture.get(CV_CAP_PROP_FRAME_HEIGHT));
VideoWriter writer;
writer.open("../result.avi",CV_FOURCC('P','I','M','1'),rate, videoSize);
Mat frame;
capture>>frame;
writer<
========
OpenCV视频处理相关编程总结
高级语言自带的播放视频的API多是把视频当成一个整体的文件进行读取,但是实际上视频文件时由
图片按帧排列而成,中间加上时间戳,普通的这种视频读取方法不利用按帧进行图像处理,然后适时播放
。譬如我们要写一个程序,detect这个视频中出现的某一个人,当这个人出现的时候,在视频中把这个人
的位置画出来。普通的视频读取方法无法完成这一功能。幸运的是,作为一个强大的图像处理工具包,
OpenCV提供了一套按帧读取的开放API,本文主要介绍OpenCV在视频处理中常用的一些方法。
(1)按帧读取视频的例子程序
#include "opencv2/opencv.hpp"
#include "opencv2/core/core.hpp"
#include
using namespace std;
using namespace cv;
int main()
{
// Open the video file
cv::VideoCapture capture("F:/Output.avi");
// check if video successfully opened
if (!capture.isOpened())
return 1;
// Get the frame rate
double rate= capture.get(CV_CAP_PROP_FPS);
bool stop(false);
cv::Mat frame; // current video frame
cv::namedWindow("Extracted Frame");
// Delay between each frame in ms
// corresponds to video frame rate
long frames= static_cast(
capture.get(CV_CAP_PROP_FRAME_COUNT));
cout<<"rate="< cout<<"Frames="<
int delay= 1000/rate;
cout<<"delay="< // for all frames in video
//while (!stop) {
// // read next frame if any
// if (!capture.read(frame))
// break;
// cv::imshow("Extracted Frame",frame);
// // introduce a delay
// // or press key to stop
// if (cv::waitKey(delay)>=0)
// stop= true;
//}
// Close the video file.
// Not required since called by destructor
//read the video by for-loop
for(int i=1;i {
double position= double(i);
capture.set(CV_CAP_PROP_POS_FRAMES, position);
capture.read(frame);
imshow("Extracted Frame",frame);
waitKey(100);//define the dalay time
}
capture.release();
}
(2)在Android编程中使用OpenCV
http://underthehood.blog.51cto.com/2531780/670169
(3).hpp和.h的区别
hpp实质上就是将.cpp的实现代码混入.h头文件当中,定义与实现都包含在同一文件。
(4)初始化一个矩阵
Mat img=Mat::zeros(53,21,CV_8U);//uchar类型的矩阵
(5)OpenCV与彩色图像
int main()
{
Mat img=imread("data/test.jpg",1);
for(int i=0;i {
for(int j=0;j {
img.ptr>(i,j)->x=0;
img.ptr>(i,j)->y=0;
img.ptr>(i,j)->z=255;
}
}
imshow("img",img);
waitKey(0);
return 0;
}
========
基于opencv的视频处理——高斯背景建模
运动检测的一般方法
目前,运动物体检测的问题主要分为两类,摄像机固定和摄像机运动。对于摄像机运动的运动物体检
测问题,比较著名的解决方案是光流法,通过求解偏微分方程求 的图像序列的光流场,从而预测摄像机
的运动状态。对于摄像机固定的情形,当然也可以用光流法,但是由于光流法的复杂性,往往难以实时的
计算,所以我采用高 斯背景模型。因为,在摄像机固定的情况下,背景的变化是缓慢的,而且大都是光
照,风等等的影响,通过对背景建模,对一幅给定图像分离前景和背景,一般来 说,前景就是运动物体
,从而达到运动物体检测的目的。
在这里我要介绍的是摄像机固定的情况,分离图像的前景和背景,之前的想法很天真----帧差法。就
是取含有目标物体的当前帧去减背景,现在想想真的很天真,这就是没有文化不知道害怕啊!完全没有图
像处理的理论基础啊,完全不顾噪音等的影响。亏我认真的去调程序,可结果让我咋舌阿!
然后知道了高斯背景建模的处理方法,高斯处理的还不错,我又加了平滑处理,代码如下
#include
#include "cv.h"
#include "cxcore.h"
#include "highgui.h"
#include "cxtypes.h"
//#include "function.h"
#include "cvaux.h"
int cvSumImage(IplImage *Img)
{
int sum = 0;
int i, j;
int width = Img->width;
int height = Img->height;
int step = Img->widthStep;
unsigned char *data = (unsigned char*)Img->imageData;
for(i=0; i for(j=0; j {
if(data[i*step+j])
sum ++;
}
return sum;
}
int main(int argc, char **argv)
{
IplImage* pFrame = NULL;
IplImage* pFrImg = NULL;
IplImage* pBkImg = NULL;
IplImage* Img0 = NULL;
IplImage* DiffImg = NULL;
IplImage* SumImg = NULL;
IplImage* FirstImg = NULL;
CvCapture* pCapture = NULL;
int nFrmNum = 0;
cvNamedWindow("video", 1);
cvNamedWindow("background", 1);
cvNamedWindow("foreground", 1);
cvMoveWindow("video", 30, 0);
cvMoveWindow("background", 360, 0);
cvMoveWindow("foreground", 690, 0);
if(argc > 3)
{
fprintf(stderr, "Usage: bkgrd [video_file_name\n]");
return -1;
}
if(argc == 2)
if( !(pCapture = cvCaptureFromFile(argv[1])))
{
fprintf(stderr, "Can not open video file %s\n", argv[1]);
return -1;
}
if(argc == 3)
if( !(pCapture = cvCaptureFromCAM(-1)))
{
fprintf(stderr, "Can not open camera.\n");
return -2;
}
CvGaussBGModel* bg_model = NULL;
while( pFrame=cvQueryFrame( pCapture ))
{
nFrmNum ++;
if( nFrmNum == 1)
{
pBkImg = cvCreateImage(cvSize(pFrame->width, pFrame->height), IPL_DEPTH_8U, 3);
pFrImg = cvCreateImage(cvSize(pFrame->width, pFrame->height), IPL_DEPTH_8U, 1);
FirstImg = cvCreateImage(cvSize(pFrame->width, pFrame->height), IPL_DEPTH_8U,
1);
// cvCvtColor(pFrame, pFrImg, CV_BGR2GRAY);
// cvSaveImage("1.jpg",pFrImg);
CvGaussBGStatModelParams params;
params.win_size = 200;
params.bg_threshold = 0.7;
params.std_threshold = 2.5;
params.weight_init = 0.05;
params.variance_init = 30*30;
params.minArea = 15.f;
params.n_gauss = 5;
bg_model = (CvGaussBGModel*) cvCreateGaussianBGModel(pFrame, 0);
}
else
{
int regioncount = 0;
int totalNum = pFrImg->width * pFrImg->height;
// regioncount = icvUpdateGaussianBGModel(pFrame, bg_model);
cvUpdateBGStatModel(pFrame, (CvBGStatModel *)bg_model);
cvCopy(bg_model->foreground, pFrImg, 0);
cvCopy(bg_model->background, pBkImg, 0);
cvErode(pFrImg, pFrImg, NULL, 1);
cvDilate(pFrImg, pFrImg, 0, 1);
cvSmooth(pFrImg, pFrImg, CV_GAUSSIAN, 3, 0, 0, 0);
// cvCvtColor(pFrame, pFrImg, CV_BGR2GRAY);
// cvSaveImage("2.jpg", pFrImg);
//把图像正过来
pBkImg->origin = 1;
pFrImg->origin = 1;
cvShowImage("video", pFrame);
cvShowImage("background", pBkImg);
cvShowImage("foreground", pFrImg);
// printf("number is %d\n", cvSumImage(pFrImg));
// 取目标帧
if(nFrmNum > 10 && (double)cvSumImage(pFrImg) > 0.3*totalNum )
{
int first, next;
first = cvSumImage(FirstImg);
next = cvSumImage( pFrImg );
printf("number is %d\n", next);
if(next < first)
{
break;
}
cvCopy(pFrImg, FirstImg, 0);
}
cvCopy(pFrImg, FirstImg, 0);
if( cvWaitKey(2)>=0)
break;
}
}
printf("%d\n", pFrImg->width*pFrImg->height);
cvReleaseBGStatModel((CvBGStatModel**)&bg_model);
cvDestroyWindow("video");
cvDestroyWindow("background");
cvWaitKey(0);
cvDestroyWindow("foreground");
cvReleaseImage(&pFrImg);
cvReleaseImage(&pBkImg);
cvReleaseImage(&FirstImg);
cvReleaseCapture(&pCapture);
return 0;
}
========
基于opencv的视频处理——图像轮廓边缘检测Canny算法
在数字图像处理中有一个重要的内容就是边缘检测。先说下边缘检测的原理吧!
边缘检测实际上就是标记图像中变化梯度比较大的点,这时大家应该想到倒数了。
下面附上程序,挺简单的一个程序。具体细节以后再说
#include
#include "cv.h"
#include "cxcore.h"
#include "highgui.h"
//#include "tklopencv.h"
int main(int argc, char **argv)
{
int i,j;
char *p = NULL;
IplImage *pImg = NULL;
IplImage *pCannyImg = NULL;
IplImage *ReImg = NULL;
if(argc == 2 && (pImg = cvLoadImage(argv[1],0)) != 0)
{
int width = pImg->width;
int height = pImg->height;
pCannyImg = cvCreateImage(cvGetSize(pImg), IPL_DEPTH_8U, 1);
ReImg = cvCreateImage(cvSize(800,640), IPL_DEPTH_8U, 1);
printf("%d\t%d\n", pImg->width, pImg->widthStep);
cvCanny(pImg, pCannyImg, 50, 159, 3); //设定阈值并检测
cvErode(pCannyImg, pCannyImg, NULL, 1);
cvDilate(pCannyImg, pCannyImg, 0, 1);
cvSmooth(pCannyImg, pCannyImg, CV_GAUSSIAN, 3, 0, 0, 0);
cvNamedWindow("src", 1);
cvNamedWindow("canny", 1);
// cvNamedWindow("Reset", 1);
cvShowImage("src", pImg);
cvShowImage("canny", pCannyImg);
// cvShowImage("Reset", ReImg);
cvWaitKey(0);
cvDestroyWindow("src");
cvDestroyWindow("canny");
// cvDestroyWindow("Reset");
cvReleaseImage(&pImg);
cvReleaseImage( &pCannyImg);
cvReleaseImage(&ReImg);
return 0;
}
printf("Error!\n");
return -1;
}
========
OpenCV2学习笔记:视频流读取与处理
由于项目需要,计划实现九路视频拼接,因此必须熟悉OpenCV对视频序列的处理。视频信号处理是图像处
理的一个延伸,所谓的视频序列是由按一定顺序进行排放的图像组成,即帧(Frame)。在这里,主要记录
下如何使用Qt+OpenCV读取视频中的每一帧,之后,在这基础上将一些图像处理的算法运用到每一帧上(
如使用Canny算子检测视频中的边缘)。
一. 读取视频序列
OpenCV提供了一个简便易用的框架以提取视频文件和USB摄像头中的图像帧,如果只是单单想读取某个视
频,你只需要创建一个cv::VideoCapture实例,然后在循环中提取每一帧。新建一个Qt控制台项目,直接
在main函数添加:
#include
#include
#include
#include
#include
int main(int argc, char *argv[])
{
QCoreApplication a(argc, argv);
// 读取视频流
cv::VideoCapture capture("e:/BrokeGirls.mkv");
// 检测视频是否读取成功
if (!capture.isOpened())
{
qDebug() << "No Input Image";
return 1;
}
// 获取图像帧率
double rate= capture.get(CV_CAP_PROP_FPS);
bool stop(false);
cv::Mat frame; // 当前视频帧
cv::namedWindow("Extracted Frame");
// 每一帧之间的延迟
int delay= 1000/rate;
// 遍历每一帧
while (!stop)
{
// 尝试读取下一帧
if (!capture.read(frame))
break;
cv::imshow("Extracted Frame",frame);
// 引入延迟
if (cv::waitKey(delay)>=0)
stop= true;
}
return a.exec();
}
(注意:要正确打开视频文件,计算机中必须安装有对应的解码器,否则cv::VideoCapture无法理解视频
格式!)运行后,将出现一个窗口,播放选定的视频(需要在创建cv::VideoCapture对象时指定视频的文
件名)。
二. 处理视频帧
为了对视频的每一帧进行处理,这里创建自己的类VideoProcessor,其中封装了OpenCV的视频获取框架,
该类允许我们指定每帧调用的处理函数。
首先,我们希望指定一个回调处理函数,每一帧中都将调用它。该函数接受一个cv::Mat对象,并输出处
理后的cv::Mat对象,其函数签名如下:
void processFrame(cv::Mat& img, cv::Mat& out);
作为这样一个处理函数的例子,以下的Canny函数计算图像的边缘:
// 对视频的每帧做Canny算子边缘检测
void canny(cv::Mat& img, cv::Mat& out)
{
// 先要把每帧图像转化为灰度图
cv::cvtColor(img,out,CV_BGR2GRAY);
// 调用Canny函数
cv::Canny(out,out,100,200);
// 对像素进行翻转
cv::threshold(out,out,128,255,cv::THRESH_BINARY_INV);
}
定义好一个视频处理类,它将与一个回调函数相关联。使用该类,可以创建一个实例,指定输入的视频文
件,绑定回调函数,然后开始对每一帧进行处理,要调用这个视频处理类,只需在main函数中添加:
// 定义一个视频处理类处理视频帧
// 首先创建实例
VideoProcessor processor;
// 打开视频文件
processor.setInput("e:/BrokeGirls.mkv");
// 声明显示窗口
// 分别为输入和输出视频
processor.displayInput("Input Video");
processor.displayOutput("Output Video");
// 以原始帧率播放视频
processor.setDelay(1000./processor.getFrameRate());
// 设置处理回调函数
processor.setFrameProcessor(canny);
// 开始帧处理过程
processor.run();
cv::waitKey();
未完待续…
========
视频加载、处理、输出
一、加载播放视频
#include "stdafx.h"
using namespace std;
using namespace cv;
int main()
{
//下面两种方法都可以打开视频
VideoCapture capture("../1.avi");
/*2、VideoCapture capture;
capture.open("../1.avi");*/
if(!capture.isOpened())
return 1;
double rate = capture.get(CV_CAP_PROP_FPS);
bool stop(false);
Mat frame;
namedWindow("Extracted Frame");
//这个delay的单位是ms,若是秒,则delay为1/rate。
//rate为每秒播放的帧数
int delay = 1000/rate;
//用于设置直接播放哪一帧
double position= 0.0;
capture.set(CV_CAP_PROP_POS_FRAMES, position);
while(!stop)
{
//以下三种方法都可以读取视频
if(!capture.read(frame))
break;
/*2、capture>>frame;*/
/*3、capture.grab();
capture.retrieve(frame);*/
imshow("Extracted Frame",frame);
if(waitKey(delay) >= 0)
stop = true;//当
delay==0时,将会暂停在该帧,不再执行下去,若不是0,则会等
待键盘的消息输入,则结束
}
//关闭视频文件,但是不
是必须的,VideoCapture构造函数会默认调用它
capture.release();
}
二、对视频读入、处理、输出进行的封装
头文件代码:
#pragma once
using namespace cv;
class VideoProcessor
{
private://用于打开处理视频
//opencv 视频捕捉对象
VideoCapture capture;
//回调函数将调用每帧处理函数
void (*process)(Mat&,Mat&);
//是否调用回调函数的开关
bool callIt;
//输入视频的播放窗口名字
string windowNameInput;
//输入视频的播放窗口名字
string windowNameOutput;
//播放每帧之间的暂停间隔
int delay;
//已处理的帧数
long fnumber;
//停止在某一帧
long frameToStop;
//停止处理的开关
bool stop;
//存储获得的视频每帧
Mat image;
public:
//设置回调函数,用于处理帧
void setFrameProcessor(
void (*frameProcessingCallback)(Mat&,Mat&))
{
process = frameProcessingCallback;
}
//打开一个视频文件
bool setInput(string filename);
//创建一个窗口显示输入帧
void displayInput(string wn);
//创建一个窗口显示输出帧
void displayOutput(string wn);
//不显示帧
void dontDisplay(void);
//抓取或处理帧序列
void run(void);
//捕捉设备是否打开
bool isOpened(){ return capture.isOpened() || !images.empty();}
//是否停止处理
bool isStopped(){ return stop; }
//停止处理
void stopIt(){ stop = true; }
//从视频或摄像头或图片文件读取下一帧
bool readNextFrame(Mat& frame);
//设置播放延时
void setDelay(int d){ delay = d;}
//设置调用回调函数
void callProcess(){ callIt = true; }
//设置不调用回调函数
void dontCallProcess(){ callIt = false;}
//设置停留在某帧
void stopAtFrameNo(long frame){ frameToStop = frame;}
//返回视频总帧数
long getFrameNumber(void);
//返回输入视频的帧频
long getFrameRate(){ return capture.get(CV_CAP_PROP_FPS); }
private://用于输出视频或图片文件
//opencv用于视频输出类对象
VideoWriter writer;
//输出文件名
string outputFile;
//输出图片的当前索引值
int currentIndex;
//输出图像的数字编号的位数
int digits;
//扩展名
string extension;
//保存图片文件名
vector images;
//遍历图片vector的迭代器
vector::const_iterator itImg;
public:
//输出视频文件以原视频相同的参数
//还不能正确得到输入视频的codec格式,只能在codec==-1时,输出视频
bool setOutput(const string& filename, int codec=-1, double framerate=0.0, bool
isColor=true);
//用于输出每一帧
void writeNextFrame(Mat& frame);
//用于以图片格式输出
bool setOutput(const string &filename,const string &ext,int numberOfDigits=3, int
startIndex=0);
//获得输入视频的编码格式
int getCodec(char codec[4]);
//获得图片大小
cv::Size getFrameSize()
{
cv::Size S ;
return S = Size((int) capture.get(CV_CAP_PROP_FRAME_WIDTH), //获取输入尺寸
(int) capture.get(CV_CAP_PROP_FRAME_HEIGHT));
}
public:
VideoProcessor(void);
~VideoProcessor(void);
//设置输入图片向量
bool setInput(const vector& imgs);
};
相应cpp文件:
#include "StdAfx.h"
#include "VideoProcessor.h"
VideoProcessor::VideoProcessor(void):
callIt(true),
delay(0),
fnumber(0),
stop(false),
frameToStop(-1),
currentIndex(0),
digits(0)
{
}
VideoProcessor::~VideoProcessor(void)
{
}
bool VideoProcessor::setInput(string filename)
{
fnumber = 0;
//万一该VideoCapture对象已经关联了一个资源
capture.release();
//images.clear();
//打开视频文件
return capture.open(filename);
}
void VideoProcessor::displayInput(string wn)
{
windowNameInput = wn;
namedWindow(windowNameInput);
}
void VideoProcessor::displayOutput(string wn)
{
windowNameOutput = wn;
namedWindow(windowNameOutput);
}
void VideoProcessor::dontDisplay(void)
{
destroyWindow(windowNameInput);
destroyWindow(windowNameOutput);
windowNameInput.clear();
windowNameOutput.clear();
}
void VideoProcessor::run(void)
{
//当前帧
Mat frame;
//输出帧
Mat output;
//判断是否打开成功
if(!isOpened())
return;
stop = false;
while(!isStopped())
{
//若存在视频下一帧,则读取
if(!readNextFrame(frame))
break;
//将当前帧存于image中
image = frame;
//frame.copyTo(image);
//显示输入帧
if(windowNameInput.length() != 0)
imshow(windowNameInput,frame);
//调用处理函数
if(callIt)
{
process(frame,output);
fnumber++;
}else{
output = frame;
}
//**写输出序列
if(outputFile.length() != 0)
writeNextFrame(output);
//显示输出帧
if(windowNameOutput.length() != 0)
imshow(windowNameOutput,output);
//延时
if(delay >= 0 && waitKey(delay) >= 0)
stopIt();
if(frameToStop >= 0 && getFrameNumber() == frameToStop)
stopIt();
}
}
bool VideoProcessor::readNextFrame(Mat& frame)
{
if(images.size() == 0)
return capture.read(frame);
else{
if(itImg != images.end())
{
frame = imread(*itImg);
itImg++;
return frame.data != 0;
}else{
return false;
}
}
}
long VideoProcessor::getFrameNumber(void)
{
long fnumber = static_cast(capture.get(CV_CAP_PROP_POS_FRAMES));
return fnumber;
}
bool VideoProcessor::setOutput(const string& filename, int codec, double framerate, bool
isColor)
{
outputFile = filename;
extension.clear();
if(framerate == 0.0)
framerate = getFrameRate();
char c[4];
//使用与输出视频相同的编码方式
if(codec == 0)
{
codec = getCodec(c);
}
writer.open(outputFile,
codec=-1,
framerate,
getFrameSize(),
isColor);
if(writer.isOpened()) std::cout<<"dai kai cheng gong"< return true;
}
void VideoProcessor::writeNextFrame(Mat& frame)
{
if(extension.length())
{
std::stringstream ss;
//构成输出文件名
ss< < < imwrite(ss.str(),frame);
}else//否则以视频格式写出
{
writer.write(frame);
}
}
bool VideoProcessor::setOutput(const string &filename,//文件前缀
const string& ext,//图片文件的扩展名
int numberOfDigits,//数据位
int startIndex) //开始索引值
{
//数字必须为正
if(numberOfDigits<0)
return false;
//文件名及其扩展名
outputFile = filename;
extension = ext;
//输出图片的编号
digits = numberOfDigits;
//图片数字编号的起始值
currentIndex = startIndex;
return true;
}
int VideoProcessor::getCodec(char codec[4])
{
//
if(images.size() != 0)
return -1;
union{//4-char编码的数据结构
int value;
char code[4];
}returned;
//获得编码方式
returned.value = static_cast(
capture.get(CV_CAP_PROP_FOURCC));
//获得四个字符
codec[0] = returned.code[0];
codec[1] = returned.code[1];
codec[2] = returned.code[2];
codec[3] = returned.code[3];
/*std::cout << "Codec: " << codec[0] << codec[1]
<< codec[2] << codec[3] << std::endl;*/
return returned.value;
}
bool VideoProcessor::setInput(const vector& imgs)
{
fnumber = 0;
capture.release();
//将输入图片放入iamges中
images = imgs;
itImg = images.begin();
return true;
}
主函数对这个类的引用:
#include "stdafx.h"
#include "VideoProcessor.h"
using namespace std;
using namespace cv;
void canny(cv::Mat& img, cv::Mat& out) {
// Convert to gray
if (img.channels()==3)
cv::cvtColor(img,out,CV_BGR2GRAY);
// Compute Canny edges
cv::Canny(out,out,100,200);
// Invert the image
cv::threshold(out,out,128,255,cv::THRESH_BINARY_INV);
}
int main()
{
VideoProcessor processor;
processor.setInput("../1.avi");
processor.displayInput("Current Frame");
processor.displayOutput("Output Frame");
processor.setDelay(1000./processor.getFrameRate());
processor.setFrameProcessor(canny);
processor.setOutput("../laneout.avi");
//processor.setOutput("../laneout",".bmp");
processor.stopAtFrameNo(300);
processor.run();
}
代码注释:输出视频和输出图片一次只能设置一个。具体应用时,对处理函数的形式进行修改,使其适合
相应程序的要求。
========
使用OpenCV 读取和处理任意格式视频文件
记得几个月前,使用OpenCV 做背景分割时使用它 很多视频都不能处理。所以投靠了 directshow,这个
超级复杂的东西。虽然说已经用 directshow 写了一个几万行的商业软件,说实话对它还是想敬而远之。
今天在测一段网上的代码时,竟然都通了。我想这也可能和我在编译器里链接了directshow 的东西吧!
下面贴一段代码
int main( int argc, char** argv )
{
//声明IplImage指针
IplImage* pFrame = NULL;
IplImage* pFrImg = NULL;
IplImage* pBkImg = NULL;
CvMat* pFrameMat = NULL;
CvMat* pFrMat = NULL;
CvMat* pBkMat = NULL;
CvCapture* pCapture = NULL;
int nFrmNum = 0;
//创建窗口
cvNamedWindow("video", 1);
cvNamedWindow("background",1);
cvNamedWindow("foreground",1);
//使窗口有序排列
cvMoveWindow("video", 30, 0);
cvMoveWindow("background", 360, 0);
cvMoveWindow("foreground", 690, 0);
//if( argc > 2 )
//{
// fprintf(stderr, "Usage: bkgrd [video_file_name]\n");
// return -1;
//}
////打开摄像头
//if (argc ==1)
// if( !(pCapture = cvCaptureFromCAM(-1)))
// {
// fprintf(stderr, "Can not open camera.\n");
// return -2;
// }
//打开视频文件
//if(argc == 2)
//if( !(pCapture = cvCaptureFromFile(argv[1])))
if( !(pCapture = cvCaptureFromFile(videoname2)))
{
fprintf(stderr, "Can not open video file %s\n", argv[1]);
return -2;
}
//逐帧读取视频
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);
pBkMat = cvCreateMat(pFrame->height, pFrame->width, CV_32FC1);
pFrMat = cvCreateMat(pFrame->height, pFrame->width, CV_32FC1);
pFrameMat = cvCreateMat(pFrame->height, pFrame->width, CV_32FC1);
//转化成单通道图像再处理
cvCvtColor(pFrame, pBkImg, CV_BGR2GRAY);
cvCvtColor(pFrame, pFrImg, CV_BGR2GRAY);
cvConvert(pFrImg, pFrameMat);
cvConvert(pFrImg, pFrMat);
cvConvert(pFrImg, pBkMat);
}
else
{
cvCvtColor(pFrame, pFrImg, CV_BGR2GRAY);
cvConvert(pFrImg, pFrameMat);
//高斯滤波先,以平滑图像
//cvSmooth(pFrameMat, pFrameMat, CV_GAUSSIAN, 3, 0, 0);
//当前帧跟背景图相减
cvAbsDiff(pFrameMat, pBkMat, pFrMat);
//二值化前景图
cvThreshold(pFrMat, pFrImg, 60, 255.0, CV_THRESH_BINARY);
//进行形态学滤波,去掉噪音
//cvErode(pFrImg, pFrImg, 0, 1);
//cvDilate(pFrImg, pFrImg, 0, 1);
//更新背景
cvRunningAvg(pFrameMat, pBkMat, 0.003, 0);
//将背景转化为图像格式,用以显示
cvConvert(pBkMat, pBkImg);
//显示图像
cvShowImage("video", pFrame);
cvShowImage("background", pBkImg);
cvShowImage("foreground", pFrImg);
//如果有按键事件,则跳出循环
//此等待也为cvShowImage函数提供时间完成显示
//等待时间可以根据CPU速度调整
if( cvWaitKey(2) >= 0 )
break;
}
}
//销毁窗口
cvDestroyWindow("video");
cvDestroyWindow("background");
cvDestroyWindow("foreground");
//释放图像和矩阵
cvReleaseImage(&pFrImg);
cvReleaseImage(&pBkImg);
cvReleaseMat(&pFrameMat);
cvReleaseMat(&pFrMat);
cvReleaseMat(&pBkMat);
cvReleaseCapture(&pCapture);
return 0;
}
========
OpenCV读视频文件和运动物体检测
简要说明:本程序 尝试打开本电脑上的摄像头作为视频输入设备,或者将命令行的输入参数作为文件名
来打开的视频文件。不管是哪一种方法,最后都是不断的循环处理一帧一帧地处理,涉及到的图像处理有
背景擦除,平滑滤波,二值化等。
有微小调整。在VS2005+OpenCV下调试通过。
#include
#include
#include
#include
#include
int main( int argc, char** argv )
{
//声明IplImage指针
IplImage* pFrame = NULL;
IplImage* pFrImg = NULL;
IplImage* pBkImg = NULL;
CvMat* pFrameMat = NULL;
CvMat* pFrMat = NULL;
CvMat* pBkMat = NULL;
CvCapture* pCapture = NULL;
int nFrmNum = 0;
//创建窗口
cvNamedWindow("video", 1);
cvNamedWindow("background",1);
cvNamedWindow("foreground",1);
//使窗口有序排列
cvMoveWindow("video", 30, 0);
cvMoveWindow("background", 360, 0);
cvMoveWindow("foreground", 690, 0);
if( argc > 2 )
{
fprintf(stderr, "Usage: bkgrd [video_file_name]\n");
return -1;
}
//打开摄像头
if (argc ==1)
{
if( !(pCapture = cvCaptureFromCAM(0)))
{
fprintf(stderr, "Can not open camera.\n");
return -2;
}
}
//打开视频文件
if(argc == 2)
{
if( !(pCapture = cvCaptureFromFile(argv[1])))
{
fprintf(stderr, "Can not open video file %s\n", argv[1]);
return -2;
}
}
//逐帧读取视频
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);
pBkMat = cvCreateMat(pFrame->height, pFrame->width, CV_32FC1);
pFrMat = cvCreateMat(pFrame->height, pFrame->width, CV_32FC1);
pFrameMat = cvCreateMat(pFrame->height, pFrame->width, CV_32FC1);
//转化成单通道图像再处理
cvCvtColor(pFrame, pBkImg, CV_BGR2GRAY);
cvCvtColor(pFrame, pFrImg, CV_BGR2GRAY);
cvConvert(pFrImg, pFrameMat);
cvConvert(pFrImg, pFrMat);
cvConvert(pFrImg, pBkMat);
}
else
{
cvCvtColor(pFrame, pFrImg, CV_BGR2GRAY);
cvConvert(pFrImg, pFrameMat);
//高斯滤波先,以平滑图像
//cvSmooth(pFrameMat, pFrameMat, CV_GAUSSIAN, 3, 0, 0);
//当前帧跟背景图相减
cvAbsDiff(pFrameMat, pBkMat, pFrMat);
//二值化前景图
cvThreshold(pFrMat, pFrImg, 60, 255.0, CV_THRESH_BINARY);
//进行形态学滤波,去掉噪音
//cvErode(pFrImg, pFrImg, 0, 1);
//cvDilate(pFrImg, pFrImg, 0, 1);
//更新背景
cvRunningAvg(pFrameMat, pBkMat, 0.003, 0);
//将背景转化为图像格式,用以显示
cvConvert(pBkMat, pBkImg);
//显示图像
cvShowImage("video", pFrame);
cvShowImage("background", pBkImg);
cvShowImage("foreground", pFrImg);
//如果有按键事件,则跳出循环
//此等待也为cvShowImage函数提供时间完成显示
//等待时间可以根据CPU速度调整
if( cvWaitKey(2) >= 0 )
break;
}
}
//销毁窗口
cvDestroyWindow("video");
cvDestroyWindow("background");
cvDestroyWindow("foreground");
//释放图像和矩阵
cvReleaseImage(&pFrImg);
cvReleaseImage(&pBkImg);
cvReleaseMat(&pFrameMat);
cvReleaseMat(&pFrMat);
cvReleaseMat(&pBkMat);
cvReleaseCapture(&pCapture);
return 0;
}
========
基于轮廓寻找的视频流运动检测
#include "cv.h"
#include "highgui.h"
#include
#include
#include
#include
#include
// various tracking parameters (in seconds) //跟踪的参数(单位为秒)
const double MHI_DURATION = 0.5;//0.5s为运动跟踪的最大持续时间
const double MAX_TIME_DELTA = 0.5;
const double MIN_TIME_DELTA = 0.05;
const int N = 3;
//
const int CONTOUR_MAX_AERA = 1000;
// ring image buffer 圈出图像缓冲
IplImage **buf = 0;//指针的指针
int last = 0;
// temporary images临时图像
IplImage *mhi = 0; // MHI: motion history image
CvFilter filter = CV_GAUSSIAN_5x5;
CvConnectedComp *cur_comp, min_comp;
CvConnectedComp comp;
CvMemStorage *storage;
CvPoint pt[4];
// 参数:
// img – 输入视频帧
// dst – 检测结果
void update_mhi( IplImage* img, IplImage* dst, int diff_threshold )
{
double timestamp = clock()/100.; // get current time in seconds 时间戳
CvSize size = cvSize(img->width,img->height);
// get current frame size,得到当前帧的尺寸
int i, idx1, idx2;
IplImage* silh;
IplImage* pyr = cvCreateImage( cvSize((size.width & -2)/2, (size.height & -2)/2), 8, 1
);
CvMemStorage *stor;
CvSeq *cont;
/*先进行数据的初始化*/
if( !mhi || mhi->width != size.width || mhi->height != size.height )
{
if( buf == 0 ) //若尚没有初始化则分配内存给他
{
buf = (IplImage**)malloc(N*sizeof(buf[0]));
memset( buf, 0, N*sizeof(buf[0]));
}
for( i = 0; i < N; i++ )
{
cvReleaseImage( &buf[i] );
buf[i] = cvCreateImage( size, IPL_DEPTH_8U, 1 );
cvZero( buf[i] );// clear Buffer Frame at the beginning
}
cvReleaseImage( &mhi );
mhi = cvCreateImage( size, IPL_DEPTH_32F, 1 );
cvZero( mhi ); // clear MHI at the beginning
} // end of if(mhi)
/*将当前要处理的帧转化为灰度放到buffer的最后一帧中*/
cvCvtColor( img, buf[last], CV_BGR2GRAY ); // convert frame to grayscale
/*设定帧的序号*/
/*
last---->idx1
^
|
|
|
idx2<-----(last+1)%3
*/
idx1 = last;
idx2 = (last + 1) % N; // index of (last - (N-1))th frame
last = idx2;
// 做帧差
silh = buf[idx2];//差值的指向idx2 |idx2-idx1|-->idx2(<-silh)
cvAbsDiff( buf[idx1], buf[idx2], silh ); // get difference between frames
// 对差图像做二值化
cvThreshold( silh, silh, 30, 255, CV_THRESH_BINARY ); //threshold it,二值化
cvUpdateMotionHistory( silh, mhi, timestamp, MHI_DURATION ); // update MHI
cvConvert( mhi, dst );//将mhi转化为dst,dst=mhi
// 中值滤波,消除小的噪声
cvSmooth( dst, dst, CV_MEDIAN, 3, 0, 0, 0 );
cvPyrDown( dst, pyr, CV_GAUSSIAN_5x5 );// 向下采样,去掉噪声,图像是原图像的四分之一
cvDilate( pyr, pyr, 0, 1 ); // 做膨胀操作,消除目标的不连续空洞
cvPyrUp( pyr, dst, CV_GAUSSIAN_5x5 );// 向上采样,恢复图像,图像是原图像的四倍
//
// 下面的程序段用来找到轮廓
//
// Create dynamic structure and sequence.
stor = cvCreateMemStorage(0);
cont = cvCreateSeq(CV_SEQ_ELTYPE_POINT, sizeof(CvSeq), sizeof(CvPoint) , stor);
// 找到所有轮廓
cvFindContours( dst, stor, &cont, sizeof(CvContour),
CV_RETR_LIST, CV_CHAIN_APPROX_SIMPLE, cvPoint(0,0));
// 直接使用CONTOUR中的矩形来画轮廓
for(;cont;cont = cont->h_next)
{
CvRect r = ((CvContour*)cont)->rect;
if(r.height * r.width > CONTOUR_MAX_AERA) // 面积小的方形抛弃掉
{
cvRectangle( img, cvPoint(r.x,r.y),
cvPoint(r.x + r.width, r.y + r.height),
CV_RGB(255,0,0), 1, CV_AA,0);
}
}
// free memory
cvReleaseMemStorage(&stor);
cvReleaseImage( &pyr );
}
int main(int argc, char** argv)
{
IplImage* motion = 0;
CvCapture* capture = 0;
if( argc == 1 || (argc == 2 && strlen(argv[1]) == 1 && isdigit(argv[1][0])))
capture = cvCaptureFromCAM( argc == 2 ? argv[1][0] - '0' : 0 );//摄像头为视频来源
else if( argc == 2 )
capture = cvCaptureFromAVI( argv[1] );//AVI为视频来源
if( capture )
{
cvNamedWindow( "Motion", 1 );//建立窗口
for(;;)
{
IplImage* image;
if( !cvGrabFrame( capture ))//捕捉一桢
break;
image = cvRetrieveFrame( capture );//取出这个帧
if( image )//若取到则判断motion是否为空
{
if( !motion )
{
motion = cvCreateImage( cvSize(image->width,image->height), 8, 1 );
//创建motion帧,八位,一通道
cvZero( motion );
//零填充motion
motion->origin = image->origin;
//内存存储的顺序和取出的帧相同
}
}
update_mhi( image, motion, 60 );//更新历史图像
cvShowImage( "Motion", image );//显示处理过的图像
if( cvWaitKey(10) >= 0 )//10ms中按任意键退出
break;
}
cvReleaseCapture( &capture );//释放设备
cvDestroyWindow( "Motion" );//销毁窗口
}
return 0;
}
========
OpenCV读视频文件和运动问题检测
下面这个程序 来自于仕琪的讲稿 《使用OpenCV进行图像处理》]中的例程:
cvRunningAvg
opencv中的函数,用来更新移动平均。用法:
void cvRunningAvg(const CvArr * image,
CvArr* acc,
double alpha,
const CvArr* mask=NULL)
image:输入图像,1或3通道,8比特或32比特的float型
acc:累加器,和image一样大小
alpha:更新时,image所占的权重
mask:操作符掩码
if mask(x,y)!= 0 (1-alpha)*acc(x,y)+alpha*image(x,y) =>acc(x,y)
#if 0
#include
#include
#include
#include
#pragma comment(lib, "cv.lib")
#pragma comment(lib, "cxcore.lib")
#pragma comment(lib, "highgui.lib")
int main( int argc, char** argv )
{
//声明IplImage指针
IplImage* pFrame = NULL;
IplImage* pFrImg = NULL;
IplImage* pBkImg = NULL;
CvMat* pFrameMat = NULL;
CvMat* pFrMat = NULL;
CvMat* pBkMat = NULL;
CvCapture* pCapture = NULL;
int nFrmNum = 0;
//创建窗口
cvNamedWindow("video", 1);
cvNamedWindow("background",1);
cvNamedWindow("foreground",1);
//使窗口有序排列
cvMoveWindow("video", 30, 0);
cvMoveWindow("background", 360, 0);
cvMoveWindow("foreground", 690, 0);
if( !(pCapture = cvCaptureFromAVI(".\\test.avi")))
{
//pCapture = cvCaptureFromCAM(-1))
fprintf(stderr, "Can not open camera.\n");
return -2;
}
//逐帧读取视频
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);
pBkMat = cvCreateMat(pFrame->height, pFrame->width, CV_32FC1);
pFrMat = cvCreateMat(pFrame->height, pFrame->width, CV_32FC1);
pFrameMat = cvCreateMat(pFrame->height, pFrame->width, CV_32FC1);
//转化成单通道图像再处理
cvCvtColor(pFrame, pBkImg, CV_BGR2GRAY);
cvCvtColor(pFrame, pFrImg, CV_BGR2GRAY);
cvConvert(pFrImg, pFrameMat);
cvConvert(pFrImg, pFrMat);
cvConvert(pFrImg, pBkMat);
}
else
{
cvCvtColor(pFrame, pFrImg, CV_BGR2GRAY);
cvConvert(pFrImg, pFrameMat);
//高斯滤波先,以平滑图像
//cvSmooth(pFrameMat, pFrameMat, CV_GAUSSIAN, 3, 0, 0);
//当前帧跟背景图相减
cvAbsDiff(pFrameMat, pBkMat, pFrMat);
//二值化前景图
cvThreshold(pFrMat, pFrImg, 60, 255.0, CV_THRESH_BINARY);
//进行形态学滤波,去掉噪音
//cvErode(pFrImg, pFrImg, 0, 1);
//cvDilate(pFrImg, pFrImg, 0, 1);
//更新背景
cvRunningAvg(pFrameMat, pBkMat, 0.003, 0);
//将背景转化为图像格式,用以显示
cvConvert(pBkMat, pBkImg);
//显示图像
cvShowImage("video", pFrame);
cvShowImage("background", pBkImg);
cvShowImage("foreground", pFrImg);
//如果有按键事件,则跳出循环
//此等待也为cvShowImage函数提供时间完成显示
//等待时间可以根据CPU速度调整
if( cvWaitKey(20) >= 0 )
{
break;
}
}
}
cvWaitKey();
//销毁窗口
cvDestroyWindow("video");
cvDestroyWindow("background");
cvDestroyWindow("foreground");
//释放图像和矩阵
cvReleaseImage(&pFrImg);
cvReleaseImage(&pBkImg);
cvReleaseMat(&pFrameMat);
cvReleaseMat(&pFrMat);
cvReleaseMat(&pBkMat);
cvReleaseCapture(&pCapture);
return 0;
}
#else
#include
#include
#pragma comment(lib, "cv.lib")
#pragma comment(lib, "cxcore.lib")
#pragma comment(lib, "highgui.lib")
int main( int argc, char** argv )
{
CvCapture* capture = NULL;
IplImage *frame = NULL;
IplImage *frame_last = NULL;//存储上一帧
IplImage *m_out_image = NULL;
IplImage* frame_gray = NULL;
IplImage* frame_gray_last = NULL;
IplImage* m_out_image_copy = NULL;
int nFrmNum = 0;
//capture = cvCaptureFromCAM(-1);
capture = cvCaptureFromAVI(".\\test.avi");
//创建窗口
cvNamedWindow( "result", 1 );
cvNamedWindow("差分");
//逐帧读取视频
while(frame = cvQueryFrame( capture ))
{
nFrmNum++;
//如果是第一帧,需要申请内存,并初始化
if(nFrmNum == 1)
{
frame_last = cvCreateImage(cvSize(frame->width,frame->height),IPL_DEPTH_8U,
frame->nChannels);
cvCopy(frame,frame_last);
}
else
{
frame_gray = cvCreateImage(cvSize(frame->width,frame->height),IPL_DEPTH_8U, 1);
frame_gray_last = cvCreateImage(cvSize(frame->width,frame->height),IPL_DEPTH_8U,
1);
m_out_image = cvCreateImage(cvSize(frame->width,frame->height),IPL_DEPTH_8U, 1);
m_out_image_copy = cvCreateImage( cvSize(frame->width,frame-
>height),IPL_DEPTH_8U, 1 );
cvCvtColor(frame,frame_gray,CV_RGB2GRAY);
cvCvtColor(frame_last,frame_gray_last,CV_RGB2GRAY);
cvAbsDiff(frame_gray,frame_gray_last,m_out_image);
//cvThreshold(m_out_image,m_out_image,128, 255,CV_THRESH_BINARY);
//将处理后的图像写出到第二个图像中
if( m_out_image_copy->origin == IPL_ORIGIN_TL )
{
cvCopy( m_out_image, m_out_image_copy, 0 );
}
else
{
cvFlip( m_out_image, m_out_image_copy, 0 );
}
frame_last = cvCreateImage(cvSize(frame->width,frame->height),IPL_DEPTH_8U,
frame->nChannels);
cvCopy(frame,frame_last);
cvShowImage("差分",m_out_image_copy);
cvShowImage("result",frame);
cvReleaseImage( &m_out_image_copy );
cvReleaseImage( &frame_gray_last );
cvReleaseImage( &frame_gray );
if( cvWaitKey( 20 ) >= 0 )
{
break;
}
}
}
cvWaitKey();
cvReleaseImage( &m_out_image );
cvReleaseImage( &frame_last );
cvReleaseImage( &frame );
cvReleaseCapture( &capture );
cvDestroyWindow("result");
cvDestroyWindow("差分");
return 0;
}
#endif
========
Opencv中对视频流进行边缘检测
#include "stdafx.h"
#include "cv.h"
#include "highgui.h"
#include
#include
int main( int argc, char** argv )
{
IplImage* laplace = 0;
IplImage* colorlaplace = 0;
IplImage* planes[3] = { 0, 0, 0 }; // 多个图像面
CvCapture* capture = 0;
//CvCapture
//
//视频获取结构
//typedef struct CvCapture CvCapture;
//结构CvCapture 没有公共接口,它只能被用来作为视频获取函数的一个参数。
// 下面的语句说明在命令行执行程序时,如果指定AVI文件,那么处理从
// AVI文件读取的视频流,如果不指定输入变量,那么处理从摄像头获取
// 的视频流
//capture = cvCaptureFromAVI( "3.avi" );
if( argc == 1 || (argc == 2 && strlen(argv[1]) == 1 && isdigit(argv[1][0])))
capture = cvCaptureFromCAM( argc == 2 ? argv[1][0] - '0' : 0 );
else if( argc == 2 )
capture = cvCaptureFromAVI( argv[1] );
if( !capture )
{
fprintf(stderr,"Could not initialize capturing...\n");
return -1;
}
cvNamedWindow( "Laplacian", 0 );
// 循环捕捉,直到用户按键跳出循环体
for(;;)
{
IplImage* frame = 0;
int i;
//cvQueryFrame
//
//从摄像头或者文件中抓取并返回一帧
//IplImage* cvQueryFrame( CvCapture* capture );
//capture
//视频获取结构。
//函数cvQueryFrame从摄像头或者文件中抓取一帧,然后解压并返回这一帧。
//这个函数仅仅是函数cvGrabFrame和函数cvRetrieveFrame在一起调用的组合。
//返回的图像不可以被用户释放或者修改。 抓取后,capture被指向下一帧,
//可用cvSetCaptureProperty调整capture到合适的帧。
//
//注意: cvQueryFrame返回的指针总是指向同一块内存。建议cvQueryFrame后拷贝一份
//。而且返回的帧需要FLIP后才符合OPENCV的坐标系。 若返回值为NULL,说明到了视频的最后
一帧。
frame = cvQueryFrame( capture );
if( !frame )
break;
if( !laplace )
{
for( i = 0; i < 3; i++ )
planes[i] = cvCreateImage( cvSize(frame->width,frame->height), 8, 1 );
//CreateImage
//创建头并分配数据
//IplImage* cvCreateImage( CvSize size, int depth, int channels );
//size
//图像宽、高.
//depth
//图像元素的位深度,可以是下面的其中之一:
//IPL_DEPTH_8U - 无符号8位整型
//IPL_DEPTH_8S - 有符号8位整型
//IPL_DEPTH_16U - 无符号16位整型
//IPL_DEPTH_16S - 有符号16位整型
//IPL_DEPTH_32S - 有符号32位整型
//IPL_DEPTH_32F - 单精度浮点数
//IPL_DEPTH_64F - 双精度浮点数
//channels
//每个元素(像素)的颜色通道数量.可以是 1, 2, 3 或 4.通道是交叉存取的,例如通常
的彩色图像数据排列是:
//b0 g0 r0 b1 g1 r1 ...
//虽然通常 IPL 图象格式可以存贮非交叉存取的图像,并且一些OpenCV 也能处理他, 但是
这个函数只能创建交叉存取图像.
//函数 cvCreateImage 创建头并分配数据,这个函数是下列的缩写型式
//header = cvCreateImageHeader(size,depth,channels);
//cvCreateData(header); //只是创建空间,并不会初始化空间内的数据
laplace = cvCreateImage( cvSize(frame->width,frame->height),
IPL_DEPTH_16S, 1 );
colorlaplace = cvCreateImage( cvSize(frame->width,frame->height), 8, 3 );
}
//cvCvtPixToPlane
//
// openCV里面的一个函数
// 可以看作cvSplit是他的宏:
// #define cvCvtPixToPlane cvSplit
// void cvSplit( const CvArr* src, CvArr* dst0, CvArr* dst1,CvArr* dst2, CvArr*
dst3 );
// 作用是:分割多通道数组成几个单通道数组或者从数组中提取一个通道
// 一般用法是cvCvtPixToPlane(IplImage * src,IplImage * dst1,IplImage
*dst2,IplImage * dst3,IplImage *dst4)
// 第一个参数是源图像,后面是分离出来每个通道的目标图像,如果圆筒到时3通道的,可
以把最后一个参数设置为空。
// 例如cvCvtPixToPlane(IplImage * src,IplImage * dst1,IplImage *dst2,IplImage *
dst3,NULL)
cvCvtPixToPlane( frame, planes[0], planes[1], planes[2], 0 );
for( i = 0; i < 3; i++ )
{
// Laplace
//计算图像的 Laplacian 变换
//void cvLaplace( const CvArr* src, CvArr* dst, int aperture_size=3 );
//src
//输入图像.
//dst
//输出图像.
//aperture_size
//核大小 (与 cvSobel 中定义一样).
cvLaplace( planes[i], laplace, 3 ); // 3: aperture_size
//ConvertScaleAbs
//使用线性变换转换输入数组元素成8位无符号整型
//void cvConvertScaleAbs( const CvArr* src, CvArr* dst, double scale=1, double
shift=0 );
//#define cvCvtScaleAbs cvConvertScaleAbs
//src
//原数组
//dst
//输出数组 (深度为 8u).
//scale
//比例因子.
//shift
//原数组元素按比例缩放后添加的值。
//函数 cvConvertScaleAbs 与前一函数是相同的,但它是存贮变换结果的绝对值:
//dst(I)=abs(src(I)*scale + (shift,shift,...))
//函数只支持目标数数组的深度为 8u (8-bit 无符号) , 对于别的类型函数仿效于
cvConvertScale 和 cvAbs 函数的联合
cvConvertScaleAbs( laplace, planes[i], 1, 0 ); // planes[] = ABS(laplace)
}
//cvCvtPixToPlane是cvCvtPlaneToPix的逆函数
cvCvtPlaneToPix( planes[0], planes[1], planes[2], 0, colorlaplace );
//IplImage
// IPL 图像头
// typedef struct _IplImage
//{
// int nSize;
// int ID;
// int nChannels;
// int alphaChannel;
// int depth;
// char colorModel[4];
// char channelSeq[4];
// int dataOrder;
// int origin;
// int align;
// int width;
// int height;
// struct _IplROI *roi;
// struct _IplImage *maskROI;
// void *imageId;
// struct _IplTileInfo *tileInfo;
// int imageSize;
// char *imageData;
// int widthStep;
// int BorderMode[4];
// int BorderConst[4];
// char *imageDataOrigin;
//}
// IplImage;
//IplImage结构来自于 Intel Image Processing Library(是其本身所具有的)。OpenCV 只支
持其中的一个子集:
//alphaChannel 在OpenCV中被忽略。
//colorModel 和channelSeq 被OpenCV忽略。OpenCV颜色转换的唯一函数 cvCvtColor把原图像
的颜色空间的目标图像的颜色空间作为一个参数。
//dataOrder 必须是IPL_DATA_ORDER_PIXEL (颜色通道是交叉存取),然而平面图像的被选择通
道可以被处理,就像COI(感兴趣的通道)被设置过一样。
//align 是被OpenCV忽略的,而用 widthStep 去访问后继的图像行。
//不支持maskROI 。处理MASK的函数把他当作一个分离的参数。MASK在 OpenCV 里是 8-bit,然
而在 IPL他是 1-bit。
//tileInfo 不支持。
//BorderMode和BorderConst是不支持的。每个 OpenCV 函数处理像素的邻近的像素,通常使用
单一的固定代码边际模式。
//除了上述限制,OpenCV处理ROI有不同的要求。要求原图像和目标图像的尺寸或 ROI的尺寸必
须(根据不同的操作,
//例如cvPyrDown 目标图像的宽(高)必须等于原图像的宽(高)除以2 ±1)精确匹配,而IPL
处理交叉区域,如图像的大小或ROI大小可能是完全独立的。
colorlaplace->origin = frame->origin; //让他们结构一致
cvShowImage("Laplacian", colorlaplace );
if( cvWaitKey(10) >= 0 )
break;
}
cvReleaseCapture( &capture );
cvDestroyWindow("Laplacian");
return 0;
========