《OpenCV》Part4 OpenCV3.1.0 提取视频中的每一帧
看到一篇好的博文,忍不住扒下来了。提取视频中的每一帧,并保存成图片,支持摄像头和视频(.avi)中的图像抓取。
一、从摄像头获取初始化:
CvCapture* capture = cvCaptureFromCAM(0); // capture from video device #0
从视频文件filename.avi获取初始化:
CvCapture* capture = cvCaptureFromAVI("infile.avi");
抓取帧:
IplImage* img = 0;
if(!cvGrabFrame(capture)){ // 抓取一帧,失败退出
printf("Could not grab a frame\n");
exit(0);
}
img=cvRetrieveFrame(capture); // 恢复获取的帧图像
要从多个摄像头同时获取图像, 首先从每个摄像头抓取一帧. 在抓取动作都结束后再恢复帧图像.释放抓取源(和释放单幅图像时类似):
cvReleaseCapture(&capture);
注意由设备抓取的图像是由capture函数自动分配和释放的. 不要试图自己释放它.获取设备特性:
cvQueryFrame(capture); // this call is necessary to get correct
// capture properties
int frameH = (int) cvGetCaptureProperty(capture, CV_CAP_PROP_FRAME_HEIGHT);
int frameW = (int) cvGetCaptureProperty(capture, CV_CAP_PROP_FRAME_WIDTH);
int fps = (int) cvGetCaptureProperty(capture, CV_CAP_PROP_FPS);
int numFrames = (int) cvGetCaptureProperty(capture, CV_CAP_PROP_FRAME_COUNT);
所有帧数似乎只与视频文件有关. 用摄像头时不对,奇怪!!!.获取帧信息:
float posMsec = cvGetCaptureProperty(capture, CV_CAP_PROP_POS_MSEC);
int posFrames = (int) cvGetCaptureProperty(capture, CV_CAP_PROP_POS_FRAMES);
float posRatio = cvGetCaptureProperty(capture, CV_CAP_PROP_POS_AVI_RATIO);
获取所抓取帧在视频序列中的位置, 从首帧开始按[毫秒]算. 或者从首帧开始从0标号, 获取所抓取帧的标号. 或者取相对位置,首帧为0,末帧为1, 只对视频文件有效。
设定所抓取的第一帧标号:
// 从视频文件相对位置0.9处开始抓取
cvSetCaptureProperty(capture, CV_CAP_PROP_POS_AVI_RATIO, (double)0.9);
只对从视频文件抓取有效. 不过似乎也不成功!!!
二、初始化视频存储器:
CvVideoWriter *writer = 0;
int isColor = 1;
int fps = 25; // or 30
int frameW = 640; // 744 for firewire cameras
int frameH = 480; // 480 for firewire cameras
writer=cvCreateVideoWriter("out.avi",CV_FOURCC('P','I','M','1'),
fps,cvSize(frameW,frameH),isColor);
其他有效编码:
CV_FOURCC('P','I','M','1') = MPEG-1 codec
CV_FOURCC('M','J','P','G') = motion-jpeg codec (does not work well)
CV_FOURCC('M', 'P', '4', '2') = MPEG-4.2 codec
CV_FOURCC('D', 'I', 'V', '3') = MPEG-4.3 codec
CV_FOURCC('D', 'I', 'V', 'X') = MPEG-4 codec
CV_FOURCC('U', '2', '6', '3') = H263 codec
CV_FOURCC('I', '2', '6', '3') = H263I codec
CV_FOURCC('F', 'L', 'V', '1') = FLV1 codec
若把视频编码设为-1则将打开一个编码选择窗口(windows系统下).存储视频文件:
IplImage* img = 0;
int nFrames = 50;
for(i=0;i<nFrames;i++){
cvGrabFrame(capture); // 抓取帧
img = cvRetrieveFrame(capture); // 恢复图像
cvWriteFrame(writer,img); // 将帧添加入视频文件
}
若想在抓取中查看抓取图像, 可在循环中加入下列代码:
cvShowImage("mainWin", img);
key = cvWaitKey(20); // wait 20 ms
若没有20[毫秒]延迟,将无法正确显示视频序列.释放视频存储器:
cvReleaseVideoWriter(&writer);
三、示例1:从视频中抓取每一帧,显示并保存
#include <cstring>
#include <opencv2/opencv.hpp>
#include <opencv2/highgui.hpp>
using namespace std;
int main(){
CvCapture* capture;
capture = cvCreateFileCapture("bike.avi");
assert(capture!=NULL);
IplImage *frame;
cvNamedWindow("camera",1);
int n = 1, m = 120;
char* cstr = new char[120];
while (m--)
{
frame = cvQueryFrame(capture);
if (!frame) break;
sprintf(cstr, "%s%d%s", "D:\\OpenCVWorkSpace\\video_capture\\video_capture",n++,".jpg");
cvShowImage("camera",frame);
cvSaveImage(cstr, frame);
if (cvWaitKey(330)>=0) break;
}
cvReleaseCapture(&capture);
cvReleaseImage(&frame);
cvDestroyWindow("camera");
return 0;
}
四、示例2:从摄像头中提取图片后并将图片重新连续播放出来。
// 该程序实现视频和图片的相互转换.
// Image_to_video()函数将一组图片合成AVI视频文件.
// Video_to_image()函数将AVI视频文件读入,将每一帧存储为jpg文件.
//
////////////////////////////////////////////////////////////////////////
#include <stdlib.h>
#include <stdio.h>
#include <math.h>
#include <cv.h>
#include <highgui.h>
#define NUM_FRAME 300 //只处理前300帧,根据视频帧数可修改
void Video_to_image(char* filename)
{
printf("------------- video to image ... ----------------n");
//初始化一个视频文件捕捉器
CvCapture* capture = cvCaptureFromAVI(filename);
//获取视频信息
cvQueryFrame(capture);
int frameH = (int)cvGetCaptureProperty(capture, CV_CAP_PROP_FRAME_HEIGHT);
int frameW = (int)cvGetCaptureProperty(capture, CV_CAP_PROP_FRAME_WIDTH);
int fps = (int)cvGetCaptureProperty(capture, CV_CAP_PROP_FPS);
int numFrames = (int)cvGetCaptureProperty(capture, CV_CAP_PROP_FRAME_COUNT);
printf("tvideo height : %dntvideo width : %dntfps : %dntframe numbers : %dn", frameH, frameW, fps, numFrames);
//定义和初始化变量
int i = 0;
IplImage* img = 0;
char image_name[13];
cvNamedWindow("mainWin", CV_WINDOW_AUTOSIZE);
//读取和显示
while (1)
{
img = cvQueryFrame(capture); //获取一帧图片
cvShowImage("mainWin", img); //将其显示
char key = cvWaitKey(20);
sprintf(image_name, "%s%d%s", "image", ++i, ".jpg");//保存的图片名
cvSaveImage(image_name, img); //保存一帧图片
if (i == NUM_FRAME) break;
}
cvReleaseCapture(&capture);
cvDestroyWindow("mainWin");
}
void Image_to_video()
{
int i = 0;
IplImage* img = 0;
char image_name[13];
printf("------------- image to video ... ----------------n");
//初始化视频编写器,参数根据实际视频文件修改
CvVideoWriter *writer = 0;
int isColor = 1;
int fps = 30; // or 25
int frameW = 400; // 744 for firewire cameras
int frameH = 240; // 480 for firewire cameras
writer = cvCreateVideoWriter("out.avi", CV_FOURCC('X', 'V', 'I', 'D'), fps, cvSize(frameW, frameH), isColor);
printf("tvideo height : %dntvideo width : %dntfps : %dn", frameH, frameW, fps);
//创建窗口
cvNamedWindow("mainWin", CV_WINDOW_AUTOSIZE);
while (i < NUM_FRAME)
{
sprintf(image_name, "%s%d%s", "image", ++i, ".jpg");
img = cvLoadImage(image_name);
if (!img)
{
printf("Could not load image file...n");
exit(0);
}
cvShowImage("mainWin", img);
char key = cvWaitKey(20);
cvWriteFrame(writer, img);
}
cvReleaseVideoWriter(&writer);
cvDestroyWindow("mainWin");
}
int main(int argc, char *argv[])
{
char filename[13] = "bike.avi";
Video_to_image(filename); //视频转图片
Image_to_video(); //图片转视频
return 0;
}
五、示例3:从摄像头中提取图片,显示并保存。
由于本人不帅,所以这个程序就不贴效果图了,具体的效果自己贴到程序中测试,可以自我欣赏一下。
注意:
//////////////调用摄像头/////////////////
videoCapture cam;
cam.open(0);
/////////////设置摄像头窗口大小///////////////////
cam.set(CV_CAP_PROP_FRAM_WIDTH,600);
cam.set(CV_CAP_PROP_FRAM_HEIGHT,400);
/////////////获取摄像头视频中的每一帧///////////////////
cv::Mat cameraFrame;
camera>>cameraFrame;
//////////////存储从视频中获取的每一帧——存储路径与存储名称/////////////////
char* cstr = new char[120];
sprintf(cstr, "%s%d%s", "D:\\OpenCVWorkSpace\\video_capture\\video_capture",n++,".jpg");
///////////////openCV3.1.0中图片存储函数/////////////
imwrite(cstr,cameraFrame);//这个函数很重要,与cvSaveImage()这个函数不同,存储的图片类型是cv::Mat类型的,所以要注意
#include <cstring>
#include <opencv2/opencv.hpp>
#include <opencv2/highgui.hpp>
#include <opencv2/video.hpp>
#include "opencv2/imgproc/imgproc.hpp"
#include "opencv2/ml/ml.hpp"
using namespace std;
using namespace cv;
#include "opencv2/imgproc/imgproc.hpp"
#include "opencv2/ml/ml.hpp"
using namespace std;
using namespace cv;
int main(int argc, char *argv[])
{
int cameraNumber = 0;
if (argc > 1)
cameraNumber = atoi(argv[1]);
VideoCapture camera;
camera.open(cameraNumber);
camera.set(CV_CAP_PROP_FRAME_WIDTH, 600);//设置界面大小
camera.set(CV_CAP_PROP_FRAME_HEIGHT, 400);
if (!camera.isOpened())
{
cerr << "ERROPR" << endl;
exit(1);
}
int n = 1;
while (true)
{
Mat cameraFrame;
camera >> cameraFrame;
if (cameraFrame.empty())
{
cerr << "ERROR1" << endl;
exit(1);
}
char* cstr = new char[120];
sprintf(cstr, "%s%d%s", "D:\\OpenCVWorkSpace\\video_capture\\video_capture",n++,".jpg");
imshow("Video", cameraFrame);
//const CvArr* s = (CvArr*)&cameraFrame;
imwrite(cstr,cameraFrame);//这个函数很重要,与cvSaveImage()这个函数不同,存储的图片类型是cv::Mat类型的,所以要注意
char key = cv::waitKey(50);
if (key == 33)
{
break;
}
}
}
运行效果图略。
注意:在开启摄像头时,上面的程序不会出错,但是在调用视频的时候,视频结束时会有下述的错误:
OpenCV Error: Assertion failed (size.width>0 && size.height>0) in cv::imshow, file..\..\..\..\opencv\modules\highgui\src\window.cpp, line 261
这时就要注意,imread函数最好是放在if判断语句里面:
网上的说法:
我们用opencv打开视频的时候,会自动先监测摄像头有没有读到帧,如果没有,就会报错,然后再执行你的程序,加一个if判断就是跳过系统自己的判断,直接执行我们的程序。加上后确实可以解决错误。
if (!img_src1.empty())
{
imshow("Froeground", img_src1);
}
else
{
return 0;
}