《OpenCV2 计算机视觉编程手册》视频处理一

本文主要结合《OpenCV2 计算机视觉编程手册》第10章的内容,学习OpenCV 处理视频图像的一般方法,包括读入,处理,写出。

1.头文件

#ifndef HEAD_H_
#define HEAD_H_

#include <iostream>
#include <iomanip>// 控制输出格式
#include <sstream>// 文件流控制
#include <string>
#include <vector>

#include <opencv2/core/core.hpp>
#include <opencv2/imgproc/imgproc.hpp>
#include <opencv2/highgui/highgui.hpp>

#endif // HEAD_H_


2. VideoProcessor头文件


#ifndef VPROCESSOR_H_
#define VPROCESSOR_H_

#include "head.h"

void canny(cv::Mat& img, cv::Mat& out)
{
	cv::cvtColor(img, out, CV_BGR2GRAY);                      // 灰度转换
	cv::Canny(out, out, 100, 200);                            // Canny边缘检测
	cv::threshold(out, out, 128, 255, cv::THRESH_BINARY_INV); // 二值图像反转, 小于128设置为255, 否则为0;即边缘为黑色
}

// 抽象类FrameProcessor中纯虚函数process必须在子类(其继承类)中重新定义。
// http://blog.csdn.net/hackbuteer1/article/details/7558868 
// 帧处理器接口
class FrameProcessor 
{
  public:
	// 处理方法,定义为纯虚函数, 让其子类实现具体的接口
	virtual void process(cv:: Mat &input, cv:: Mat &output)= 0;
};


class VideoProcessor 
{
  private:
	  cv::VideoCapture capture;           // OpenCV视频采集对象(object)
	  void (*process)(cv::Mat&, cv::Mat&);// 每帧处理的回调函数, 函数指针调用
	  FrameProcessor *frameProcessor;     // 基类(含纯虚函数的抽象类)FrameProcessor接口指针, 指向子类的实现
	  bool callIt;                        // 启动回调函数与否的bool判断, true:调用, false:不调用
	  std::string windowNameInput;        // 输入显示窗口名字
	  std::string windowNameOutput;       // 输出显示窗口名字
	  int delay;                          // 帧间处理延迟
	  long fnumber;                       // 已处理帧总数
	  long frameToStop;                   // 在该帧停止	  
	  bool stop;                          // 停止处理标志位!
  
	  std::vector<std::string> images;               // 输入的图像集或者图像向量(vector容器)
	  std::vector<std::string>::const_iterator itImg;// 图像集的迭代器

	  cv::VideoWriter writer;             // OpenCV视频写出对象
	  std::string outputFile;             // 输出视频文件名字
	  int currentIndex;                   // 输出图像集的当前索引
	  int digits;                         // 输出图像文件名字的数字 
	  std::string extension;              // 输出图像集的扩展名

	  // Getting the next frame which could be: video file; camera; vector of images
	  bool readNextFrame(cv::Mat& frame) 
	  {
		  if (images.size()==0)
			  return capture.read(frame);
		  else 
		  {
			  if (itImg != images.end()) 
			  {
				  frame= cv::imread(*itImg);
				  itImg++;
				  return frame.data != 0;
			  }
			  else
			  {
				  return false;
			  }
		  }
	  }

	  // Writing the output frame which could be: video file or images
	  void writeNextFrame(cv::Mat& frame) 
	  {
		  if (extension.length()) 
		  { // 输出图像文件
			  std::stringstream ss;
		      ss << outputFile << std::setfill('0') << std::setw(digits) << currentIndex++ << extension;
			  cv::imwrite(ss.str(),frame);
		  } 
		  else 
		  { // 输出视频文件
			  writer.write(frame);
		  }
	  }

  public:
	  // 构造函数
	  VideoProcessor() : callIt(false), delay(-1), 
		  fnumber(0), stop(false), digits(0), frameToStop(-1), 
	      process(0), frameProcessor(0) {}

	  // 设置视频文件的名字
	  bool setInput(std::string filename) 
	  {
		fnumber= 0;
		// In case a resource was already 
		// associated with the VideoCapture instance
		capture.release();            // 释放之前打开过的资源
		images.clear();               // 释放之前打开过的资源
		return capture.open(filename);// 打开视频文件
	  }

	  // 设置相机ID
	  bool setInput(int id) 
	  {
		fnumber= 0;
		// In case a resource was already 
		// associated with the VideoCapture instance
		capture.release();
		images.clear();
		// 打开视频文件
		return capture.open(id);
	  }

	  // 设置输入的图像集
	  bool setInput(const std::vector<std::string>& imgs) 
	  {
		fnumber= 0;
		// In case a resource was already 
		// associated with the VideoCapture instance
		capture.release();//释放之前打开过的资源

		// 输入的是图像集
		images= imgs;
		itImg= images.begin();
		return true;
	  }

	  // 设置输出视频文件, 默认参数和输入的一样
	  bool setOutput(const std::string &filename, int codec=0, double framerate=0.0, bool isColor=true) 
	  {
		  outputFile= filename;
		  extension.clear();
		  
		  if (framerate==0.0) 
			  framerate= getFrameRate(); // 与输入相同

		  char c[4];                     // 使用和输入相同的编码格式
		  if (codec==0) 
		  { 
			  codec= getCodec(c);
		  }

		  // 打开输出视频
		  return writer.open(outputFile, // 文件名
			  codec,                     // 使用的解码格式 
			  framerate,                 // 帧率
			  getFrameSize(),            // 帧大小
			  isColor);                  // 是否为彩色视频
	  }

	  // 设置输出是图像集, 后缀必须是".jpg", ".bmp" ...
	  bool setOutput(const std::string &filename, // 文件名前缀
		  const std::string &ext,                 // 图像文件后缀 
		  int numberOfDigits=3,                   // 数字位数
		  int startIndex=0)                       // 开始索引000
	  {     
		  if (numberOfDigits<0)                   // 数字位数必须是正数
			  return false;

		  outputFile = filename;                  // 文件名
		  extension  = ext;                       // 公共后缀名

		  digits       = numberOfDigits;          // 文件名中的数字位数 
		  currentIndex = startIndex;              // 开始索引

		  return true;
	  }

	  // 设置每一帧的回调函数
	  void setFrameProcessor(void (*frameProcessingCallback)(cv::Mat&, cv::Mat&)) 
	  {
		  // invalidate frame processor class instance 使FrameProcessor实例无效化
		  frameProcessor = 0;
		  process = frameProcessingCallback;
		  callProcess();
	  }

	  // 设置FrameProcessor接口实例
	  void setFrameProcessor(FrameProcessor* frameProcessorPtr) 
	  {
		  // invalidate callback function 使回调函数无效化
		  process = 0;
		  frameProcessor= frameProcessorPtr;
		  callProcess();
	  }

	  // 在frame帧停止
	  void stopAtFrameNo(long frame) 
	  {
		  frameToStop= frame;
	  }

	  // 处理回调函数
	  void callProcess() 
	  {
		  callIt= true;
	  }

	  // 不调用回调函数
	  void dontCallProcess() 
	  {
		  callIt= false;
	  }

	  // 显示输入的图像帧
	  void displayInput(std::string wn) 
	  {
		  windowNameInput= wn;
		  cv::namedWindow(windowNameInput);
	  }

	  // 显示处理的图像帧
	  void displayOutput(std::string wn) 
	  {
		  windowNameOutput= wn;
		  cv::namedWindow(windowNameOutput);
	  }

	  // 不显示处理的图像帧
	  void dontDisplay() 
	  {
		  cv::destroyWindow(windowNameInput);
		  cv::destroyWindow(windowNameOutput);
		  windowNameInput.clear();
		  windowNameOutput.clear();
	  }

	  // 设置帧间延迟时间
	  // 0 means wait at each frame
	  // negative means no delay
	  void setDelay(int d) 
	  {
		  delay= d;
	  }

	  // 处理帧的总数
	  long getNumberOfProcessedFrames() 
	  {
		  return fnumber;
	  }

	  // 返回视频帧的大小
	  cv::Size getFrameSize() 
	  {
		if (images.size()==0) 
		{
			// get size of from the capture device
			int w= static_cast<int>(capture.get(CV_CAP_PROP_FRAME_WIDTH));
			int h= static_cast<int>(capture.get(CV_CAP_PROP_FRAME_HEIGHT));

			return cv::Size(w,h);
		} 
		else 
		{ // if input is vector of images
			cv::Mat tmp= cv::imread(images[0]);
			if (!tmp.data) return cv::Size(0,0);
			else return tmp.size();
		}
	  }

	  // 返回下一帧的帧数
	  long getFrameNumber() 
	  {
		if (images.size()==0) 
		{
			// get info of from the capture device
	 	    long f= static_cast<long>(capture.get(CV_CAP_PROP_POS_FRAMES));
		    return f; 
		} 
		else 
		{ // if input is vector of images
			return static_cast<long>(itImg-images.begin());
		}
	  }

	  // return the position in ms
	  double getPositionMS() 
	  {
		  // undefined for vector of images
		  if (images.size()!=0) return 0.0;

	 	  double t= capture.get(CV_CAP_PROP_POS_MSEC);
		  return t; 
	  }

	  // 返回帧率
	  double getFrameRate() 
	  {
		  // undefined for vector of images
		  if (images.size()!=0) return 0;

	 	  double r= capture.get(CV_CAP_PROP_FPS);
		  return r; 
	  }

	  // 返回视频中图像的总数
	  long getTotalFrameCount()
	  {
		  // for vector of images
		  if (images.size()!=0) return images.size();

	 	  long t= capture.get(CV_CAP_PROP_FRAME_COUNT);
		  return t; 
	  }

	  // 获取输入视频的编解码器
	  int getCodec(char codec[4]) 
	  {
		  // 未制定的图像集
		  if (images.size()!=0) return -1;

		  union 
		  {// 4-char编码的数据结果
			  int value;
			  char code[4]; 
		  } returned;
		  // 获取编码
		  returned.value= static_cast<int>(capture.get(CV_CAP_PROP_FOURCC));
		  // 获得4字符
		  codec[0]= returned.code[0];
		  codec[1]= returned.code[1];
		  codec[2]= returned.code[2];
		  codec[3]= returned.code[3];
		  // 返回对应的整数
		  return returned.value;
	  }
	  
	  // 设置帧位置
	  bool setFrameNumber(long pos) 
	  {
		  // for vector of images
		  if (images.size()!=0) 
		  {
			  // move to position in vector
			  itImg= images.begin() + pos;
			  // is it a valid position?
			  if (pos < images.size())
				  return true;
			  else
				  return false;

		  } 
		  else 
		  { // if input is a capture device
			return capture.set(CV_CAP_PROP_POS_FRAMES, pos);
		  }
	  }

	  // go to this position
	  bool setPositionMS(double pos) 
	  {
		  // not defined in vector of images
		  if (images.size()!=0) 
			  return false;
		  else 
		      return capture.set(CV_CAP_PROP_POS_MSEC, pos);
	  }

	  // go to this position expressed in fraction of total film length
	  bool setRelativePosition(double pos) 
	  {
		  // for vector of images
		  if (images.size()!=0) 
		  {
			  // move to position in vector
			  long posI= static_cast<long>(pos*images.size()+0.5);
			  itImg= images.begin() + posI;
			  // is it a valid position?
			  if (posI < images.size())
				  return true;
			  else
				  return false;
		  } 
		  else 
		  { // if input is a capture device
			  return capture.set(CV_CAP_PROP_POS_AVI_RATIO, pos);
		  }
	  }

	  // 停止运行
	  void stopIt() 
	  {
		  stop= true;
	  }
	  // 是否已停止运行?
	  bool isStopped() 
	  {
		  return stop;
	  }


	  // 判断是否是视频捕获设备或图像集
	  bool isOpened() 
	  {
		  return capture.isOpened() || !images.empty();
	  }
	  
	  // 获取并处理图像
	  void run() 
	  {
		  cv::Mat frame;  // 当前帧
		  cv::Mat output; // 输出帧

		  // if no capture device has been set
		  if (!isOpened())
			  return;

		  stop= false;
		  while (!isStopped()) 
		  {
			  // 读取下一帧
			  if (!readNextFrame(frame))
				  break;

			  // 显示输出帧
			  if (windowNameInput.length()!=0) 
				  cv::imshow(windowNameInput,frame);

		      // 调用帧处理回调函数或FrameProcessor实例
			  if (callIt) 
			  {  
				// 处理当前帧
				if (process)             // 如果是回调函数
				    process(frame, output);
				else if (frameProcessor) //如果是FrameProcessor实例
					frameProcessor->process(frame,output);
				// 增加帧数
			    fnumber++;

			  }
			  else
			  {
				output= frame;
			  }

			  // 写出输出图像序列
			  if (outputFile.length()!=0)
				  writeNextFrame(output);

			  // 显示输出帧
			  if (windowNameOutput.length()!=0) 
				  cv::imshow(windowNameOutput,output);
			
			  // 引入帧间延迟
			  if (delay>=0 && cv::waitKey(delay)>=0)
				stopIt();

			  // 检查是否需要停止运行
			  if (frameToStop>=0 && getFrameNumber()==frameToStop)
				  stopIt();
		  }
	  }
};

#endif // VPROCESSOR_H_

3. 主函数


#include "head.h"
#include "videoprocessor.h"


int main()
{
	//----Zero Test----
	cv::VideoCapture capture("../bike.avi"); // 打开视频/摄像头0
	if (!capture.isOpened())
		return 1;
	double rate= capture.get(CV_CAP_PROP_FPS);// 获取帧率
	bool stop(false);
	cv::Mat frame;                            // 当前帧
	cv::namedWindow("Extracted Frame");
	int delay= 1000/rate;                     // 延迟的毫秒
	//int delay = 1000;
	// 处理视频所有帧
	while (!stop) 
	{
		// read next frame if any
		if (!capture.read(frame))
			break;
		cv::imshow("Extracted Frame",frame);
		if (cv::waitKey(delay)>=0)            // 延迟等待直到cv::waitKey(delay)<0
				stop= true;
	}
	capture.release();                       // 因为capture自动调用析构函数,所以capture.release不是必须的!
	cv::waitKey();
		
	//----First Test----
	VideoProcessor processor;                           // 创建VideoProcessor类实例 processor
	processor.setInput("../bike.avi");                  // 打开视频文件bike.avi
	processor.displayInput("Input Video");              // 声明输入视频显示窗口
	processor.displayOutput("Output Video");            // 声明输出视频显示窗口
	processor.setDelay(1000./processor.getFrameRate()); // 设置播放视频为原始输入视频帧率
	processor.setFrameProcessor(canny);                 // 设置帧处理器的回调函数--canny
	processor.run();                                    // 开始处理视频文件
	cv::waitKey();                                      // 等待按键响应
	
	//----Second test----
	processor.setInput("../bike.avi");                            // 重新设置打开视频
	cv::Size size= processor.getFrameSize();                      // 获取视频文件的基本信息
	std::cout << size.width << " " << size.height << std::endl;   // 视频图像的宽度(列)和高度(行)
	std::cout << processor.getFrameRate() << std::endl;           // 视频的帧率
	std::cout << processor.getTotalFrameCount() << std::endl;     // 视频总的帧数
	std::cout << processor.getFrameNumber() << std::endl;         // 视频帧的编号
	std::cout << processor.getPositionMS() << std::endl;          // 视频帧的位置(ms)
	processor.dontCallProcess();                                   // 不处理打开视频文件
    // 输出.jpg视频图像到output文件夹, 图像名字为bikeOut000.jpg~bikeOut118.jpg
	processor.setOutput("../output/bikeOut",".jpg");               
	processor.run(); 
	cv::waitKey();
	// 输出bike.avi视频到output文件夹,编解码器为:XVID, 基于MPEG-4视频标准的开源解码库
	char codec[4];                                                 // 编解码器标识
	processor.setOutput("../output/bike.avi",processor.getCodec(codec),processor.getFrameRate());
	std::cout << "Codec: " << codec[0] << codec[1] << codec[2] << codec[3] << std::endl;
	processor.run();
	cv::waitKey();


	//----Three test----
	processor.setInput("../bike.avi");
	processor.displayInput("Input Video");              // 声明输入视频显示窗口
	processor.displayOutput("Output Video");            // 声明输出视频显示窗口
    processor.setFrameNumber(80);                       // 设置帧的位置
    processor.stopAtFrameNo(120);                       // 停止的帧位置
	processor.setDelay(1000./processor.getFrameRate());
	processor.run();
	cv::waitKey();

	return 0;
}

Canny边缘检测



视频写出结果(包含文件和视频)



制定开始帧和结束帧位置








评论 4
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

Digital2Slave

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值