学习OpenCV3——第二章:OpenCV初探

一.头文件

在安装OpenCV库以及设置好编程环境之后,下一个任务就是用代码来做一些有趣的事情。为了完成这个工作,我们需要先讨论一下头文件。介绍如下:

#include "opencv2/core/core.hpp"

新式C++风格的结构以及数学运算

#include "opencv2/flann/miniflann.hpp"

最邻近搜索匹配函数

#include "opencv2/imgproc.hpp"

新式C++风格图像处理函数

#include "opencv2/video/photo.hpp"

操作和恢复照片相关算法

#include "opencv2/video/video.hpp"

视觉追踪以及背景分割

#include "opencv2/features2d/features2d.hpp"

用于追踪的二维特征

#include "opencv2/objdetect/objdetect.hpp"

级联人脸分类器、latent SVM分类器、HoG特征和平面片检测器

#include "opencv2/calib3d/calib3d.hpp"

校准以及双目视觉相关

#include "opencv2/ml/ml.hpp"

机器学习、聚类、以及模式识别相关

#include "opencv2/highgui/highgui.hpp"

新式C++风格的显示、滑动条、鼠标操作以及输入输出相关

#include "opencv2/contrib/contrib.hpp"

用户贡献的代码、皮肤检测、模糊Mean—Shift追踪,spin image算法以及自相似特征等

二.资源

网上有几个不错的OpenCV资源:

  • 对整个OpenCV库进行概述:https://www.slideshare.net/zblair/opencv-introduction
  • OpenCV3概述:http://slideplayer.com/slide/10302485/
  • 关于加速的讨论:https://www.slideshare.net/embeddedvision/making-opencv-code-run-fast-a-presentation-from-intel
  • 关于模块的讨论:https://docs.opencv.org/3.1.0

三.第一个程序:显示图片

  OpenCV提供了一些实用工具来读取从视频流到摄像机画面的各种各样的图像格式,这些工具都是HighGUI的一部分,我们将使用其中的一些工具来创建一个简单的程序,这个程序将打开一张图像并且将其显示在屏幕上。

//示例2-1:一个简单的加载并显示图像的OpenCV程序
#include "opencv2/highgui/highgui.hpp"

using namespace cv;

int main(int argv, char** argv)
{
 Mat img = imread(argv[1],-1);//载入图像,自动申请内存,返回一个Mat结构!
 if(img.empty()) return -1;//检查载入图像是否成功
 nameWindow("Example1", WINDOW_AUTOSIZE);//会将一个名称赋予窗口,第二参数用来设置属性
 imshow("Example1", img);//显示图片
 waitKey(0);//这个函数告诉系统暂停并且等待键盘事件,如果其传入了一个大于零的参数,它将会等待等同于该参数的毫秒时间,然后继续直行程序,如果参数被设置为零或者一个负数,程序将会无线等待知道有按键被按下

 destroWindow("Example1");//我们可以让窗口自动销毁,后面例程不再使用此操作
}

四.第二个程序:带跳转的视频

         本质上,代码中添加一个全局变量来表示滑动条的位置并且添加一个回调函数来更改这个变量来重新定位视频读取的位置,在创建滑动条和回调函数之后,我们就开始运行程序。

         在程序的开始处,我们定义了一个g_slider_position来存储滑动条的位置。由于回调函数需要访问帧读取结构g_cap,所以我们最好将这个结构也提升为全局变量。为了保证代码的易读性,我们会在每一个全局变量前加上g_前缀以表明这是一个全局变量。同理,全局变量g_run将在新的跳转出发之后置零。当他为正的时候,指示在停止之前程序要播放多少张图片;当他为负的时候,表明系统处于连续视频播放模式。

        为了防止冲突,在用户单击了滑动条转到一个视频的新位置之后,我们将会通过设置g_run变量为1使视频进入单步模式。即使这样,也存在一个小问题:当视频前进的时候,我们希望滑动条也能够随着视频的当前进度前进。我们通过在程序中调用滑动条的回调函数实现这一功能。但是,我们并不希望在这个时候进入单步模式。为了避免这种情况发生,我们引入最后一个全局变量g_dontset来避免在调整进度条位置的时候触发单步模式。

        现在,定义一个用户调整滑动条的时候执行的回调程序。这个程序将会传入一个32位的整型数值以表示当前的位置。这将会是进度条存在的新的位置。在回调函数的内部,我们在新的位置通过调用g_cap_set()来真正使进度条移到我们希望的位置。if()语句设置程序是否进入单步模式。只有在用户触发滑动条时间的时候,这个设置才会生效;在系统自动调用该回调函数的时候,单步模式不会生效。

        调用g_cap_set()使我们以后经常使用的一个操作,他通常配合g_mcap.get( )使用。这些程序允许我们配置(或是询问)VideoCapture结构中的许多变量。在本例中,我们输入参数CAP_PROP_POS_FRAMES,这个参数指示我们想要帧集合的读取位置。

//示例2-4:加入了滑动条的基本浏览窗口
#include "opencv2/highgui/highgui.hpp"
#include "opencv2/imgproc/imgproc.hpp"
#include "iostream"
#include "fstream"

using namespace std;
using namespace cv;

int g_slider_position = 0;
int g_run = 1, g_dontset = 0;//start out in single step mode
VideoCapture g_cap;

void onTrackbarSlide(int pos, void *)
{
	g_cap.set(CAP_PROP_POS_FRAMES, pos);
	if (!g_dontset)
		g_run = 1;
	g_dontset = 0;
}

int main(int argc, char** argv)
{
	namedWindow("Example2_4", WINDOW_AUTOSIZE);
	g_cap.open(string(argv[1]));
	int frames = (int)g_cap.get(CAP_PROP_FRAME_COUNT);
	int tmpw = (int)g_cap.get(CAP_PROP_FRAME_WIDTH);
	int tmph = (int)g_cap.get(CAP_PROP_FRAME_HEIGHT);
	cout << "Video has" << frames << "frames of dimensions(" << tmpw << "," << tmph << ")." << endl;
	createTrackbar("Position", "Example2_4", &g_slider_position, frames, onTrackbarSlide);

	Mat frame;
	for (;;)
	{
		if (g_run != 0) {
			g_cap >> frame;
			if (frame.empty())break;
			int current_pos = (int)g_cap.get(CAP_PROP_POS_FRAMES);
			g_dontset = 1;

			setTrackbarPos("Position", "Example2_4", current_pos);
			imshow("Example2_4", frame);

			g_run -= 1;
		}
		char c = (char)waitKey(10);
		if (c == 's')//single step
		{
			g_run = 1;
			cout << "Single step run = " << g_run << endl;
		}
		if (c == 'r')//run mode
		{
			g_run = -1;
			cout << "Run mode, run = " << g_run << endl;
		}
		if (c == 27)
			break;
	}
	return(0);
}

五.从摄像头读取

        如果一个文件名被提供,OpenCV将打开指定的文件,如果没有给予任何文件名,程序将尝试打开一个摄像头,我们加入一段检查代码来确定程序是否真的开启了一些东西,如果没有,则报错。视频播放部分没有写出来,请大家参照上面例程给出。

//示例2-10:同一个对象可以读取视频文件,也可以连接摄像头
#include 
#include "opencv2\opencv.hpp"

using namespace cv;

int main(int argc, char** argv)
{
	namedWindow("Example2_10", WINDOW_AUTOSIZE);

	VideoCapture cap;

	if (argc == 1)
	{
		cap.open(0);
	}
	else {
		cap.open(argv[1]);
	}
	if (!cap.isOpened())
	{
		cerr << "Couldn't open capture." << endl;
		return -1;
	}
	//the rest of program proceeds as in #simple_opencv_program_for_playing_a_vide
	//程序的其余部分在#simple_opencv_program_for_playing_a_vide中进行
}

六.写入AVI文件

这个程序里面有很多我们非常熟悉的元素。首先是打开视频并且读取一些在VideoWriter建立时用到的属性(每秒播放的帧数以及图像宽高)。在此之后,我们将会从VideoReader中逐帧读取视频,并将每一帧转换为对数极坐标系形式,然后将转换的对数极坐标图像写入新的视频文件,循环操作指导源文件读完或者用户按下ESC键。

VideoReader的调用有几个需要理解的参数,第一个参数是新建视频文件的文件名,第二个参数是视频编码的方式,指明视频将以何种方式进行压缩。现在又非常多的编码器可以选择,但是选择的任何编码器都必须确保可以在你的机器上使用(编码器是与OpenCV分开安装的)。在我们的例子中,我们选择了通用的MJPG编码器,我们通过使用OpenCV所提供的宏CV_FOURCC()指定他,这个宏将四个字符作为其标识。比如本例中用于运动jpeg图像编码的字符为“MJPG”,所以我们指定CV_FOURCC('M' 'J' 'P' 'G')。接下来的参数是帧率以及图像的大小。在我们的例子中,这两个值和原始视频一致。

//示例2-11:一个完整的读取彩色视频并转换为对数极坐标视频的程序
#include 
#include 

using namespace std;
using namespace cv;

int main(int argc, char* argv[])
{
	namedWindow("Example2_11", WINDOW_AUTOSIZE);
	namedWindow("Log_Polar", WINDOW_AUTOSIZE);

	//(Note:could capture from a camera by giving a camera id as an int)
	//(注意:可以通过将相机id作为int从相机捕获)
	VideoCapture capture(argv[1]);

	double fps = capture.get(CAP_PROP_FPS);
	Size size((int)capture.get(CAP_PROP_FRAME_WIDTH), (int)capture.get(CAP_PROP_FRAME_HEIGHT));

	VideoWriter writer;
	writer.open(argv[2], CV_FOURCC('M', 'J', 'P', 'G'), fps, size);

	Mat logpolar_frame, bgr_frame;
	for (;;)
	{
		capture >> bgr_frame;
		if (bgr_frame.empty())break;//end if done

		imshow("Example2_11", bgr_frame);

		logPolar(bgr_frame,             //Input color frame
			logpolar_frame,             //Output log_polar frame
			Point2f(                    //Centerpoint for log_polar transformation
				bgr_frame.cols / 2,     //x
				bgr_frame.rows / 2),    //y
			40,                         //Magnitude(scale parameter)
			WARP_FILL_OUTLIERS          //Fill outliers with 'zero'
		);

		imshow("Log_Polar", logpolar_frame);
		writer << logpolar_frame;

		char c = waitKey(10);
		if (c = 27)break;               //allow the user to break out
	}
	capture.release();
}

七.小结

在接下来的几章中,我们将更深入地探究基础知识,更详细的了解与接口相关的函数和图像数据类型。我们将研究原始图像处理操作符,然后再 进行一些更高级的操作。此后,我们将准备探索API为各种任务提供的多种专门服务,如相机的校准、跟踪、识别。准备好了吗,赶紧上车吧!

你可能感兴趣的:(学习OpenCV3)