OpenCV提供了一些实用工具来读取从视频流到摄像机画面的各种各样的图像格式,这些工具都是HighGUI的一部分。下面几个小例子可以让初学者提前体验一下OpenCV的功能。
1. 打开一张图像并且将其显示在屏幕上
#include "stdafx.h"
#include <opencv2/opencv.hpp>
int main( int argc, char** argv )
{
// 载入图像
cv::Mat img = cv::imread("C:\\Users\\Bello\\Desktop\\test.jpg", -1);
// 检查图像是否真的被载入,如果没有返回-1,结束程序
if ( img.empty() ) return -1;
// 由High GUI模块提供,会将一个名称赋给窗口,未来High GUI的和这个窗口的交互函数将由这个名称来指定要与哪个窗口交互。
// 第二个参数说明了Windows的特性,这个可以设置为0(默认),也可设为cv::WINDOW_AUTOSIZE,窗口会在图像载入的时候
// 自动缩放以适应图像的真实大小,也可能由用户自行调整。
cv::namedWindow("Example1", cv::WINDOW_AUTOSIZE );
// 将图像数据显示为图像,将建一个窗口(若不存在,会自动调用cv::namedWindow()新建窗口)。
cv::imshow( "Example1", img );
// 系统暂停并且等待键盘事件。
// 如果传入了一个大于0的参数,它将会等待等同于该参数的毫秒数时间,然后继续执行程序。
// 如果参数被设置为0或一个负数,程序将会无限等待知道有键被按下。
cv::waitKey( 1000 );
// 关闭窗口并释放掉相关联的内存空间
cv::destroyWindow("Example1");
return 0;
}
2. 播放视频文件
#include "stdafx.h"
#include <opencv2/highgui/highgui.hpp>
#include <opencv2/imgproc/imgproc.hpp>
int main()
{
cv::namedWindow( "Example3", cv::WINDOW_AUTOSIZE );
// 视频读取结构
cv::VideoCapture cap;
// 视频读取结构通过传入字符串打开文件,这个字符串指示了想要打开的视频文件的路径。
// 视频打开后,视频读取结构将会包含所有的关于这个视频文件可以读取的属性,包括状态信息。
cap.open( "D:\\mgtv\\[高清]《妈妈是超人》第二季先导片:安吉“魔音”乱入吓哭小鱼儿 鬼马精灵包饺子实力坑妈.mp4" );
// 声明一个可以保存视频帧的结构。
cv::Mat frame;
for ( ; ; )
{
// 按照帧从视频流中读取视频文件
cap >> frame;
// 检查是否读取成功
if ( frame.empty() )
{
break;
}
// 显示图片
cv::imshow("Example3", frame);
// 等待33毫秒,在此等待时间内按任何键推出循环,若没有进行下一层循环。
// 注:设置为33ms是因为这能让视频以30FPS的速度播放,并能允许用户在播放的时候打断。
if(cv::waitKey(33) >= 0 )
{
break;
}
}
cv::destroyWindow("Example3");
return 0;
}
3. 加入了滑动条的基本浏览窗口
// example2-4.cpp: 定义控制台应用程序的入口点。
//
#include "stdafx.h"
#include <opencv2/highgui/highgui.hpp>
#include <opencv2/imgproc/imgproc.hpp>
#include <iostream>
#include <fstream>
using namespace std;
// 全局变量,存储滑动条位置
int g_slider_position = 0;
// g_run为1进入单步模式;g_dontset用来避免调整进度条位置的时候触发单例模式
int g_run = 1, g_dontset = 0;
// 数据读取结构
cv::VideoCapture g_cap;
// 用户调整滑动条时将触发的回调函数,来重新定位视频读取的位置
void onTrackbarSlide(int pos, void *) {
// 根据滑动条位置重定向视频读取位置
g_cap.set(cv::CAP_PROP_POS_FRAMES, pos);
if (!g_dontset)
g_run = 1;
g_dontset = 0;
}
int main()
{
cv::namedWindow("Example4", cv::WINDOW_AUTOSIZE );
g_cap.open("D:\\mgtv\\[高清]《妈妈是超人》第二季先导片:安吉“魔音”乱入吓哭小鱼儿 鬼马精灵包饺子实力坑妈.mp4");
// 确定总帧数
int frames = (int)g_cap.get(cv::CAP_PROP_FRAME_COUNT);
// 确定视频的宽度
int tmpw = (int)g_cap.get(cv::CAP_PROP_FRAME_WIDTH);
// 确定视频的高度
int tmph = (int)g_cap.get(cv::CAP_PROP_FRAME_HEIGHT);
// 输出视频的信息
cout << "Video has " << frames << " frames of dimensions(" << tmpw << ", " << tmph << ")." << endl;
// 创建滑动条,第一个参数是滑动条标签,第二个参数指定窗口,第三个参数是滑动条的初始位置,第四个参数是视频总帧数,第五个参数是回调函数,不要时可设置为0
cv::createTrackbar("Position", "Example4", &g_slider_position, frames, onTrackbarSlide);
cv::Mat frame;
for (;;) {
if (g_run != 0) {
// 按帧从视频流中读取视频文件
g_cap >> frame;
if (frame.empty()){
break;
}
// 获取当前帧集合读取的位置
int current_pos = (int)g_cap.get(cv::CAP_PROP_POS_FRAMES);
g_dontset = 1;
// 更新进度条位置,将触发回调函数
cv::setTrackbarPos("Position", "Example4", current_pos);
// 显示图片
cv::imshow("Example4", frame);
// 若为单步模式,那么此运算后g_run为0,则之后的循环无法进入该语句段;
// 若为连续模式,那么此运算后g_run依旧不为0,所以可以连续播放视频。
g_run -= 1;
}
// 从键盘输入获取值
char c = (char)cv::waitKey(10);
// 如果按键是s,则进入单步模式
if (c == 's') {
g_run = 1;
cout << "Single step, run = " << g_run << endl;
}
// 如果按键是r,则恢复连续模式
else if(c == 'r'){
g_run = -1;
cout << "Run mode, run = " << g_run << endl;
}
// 如果按键是esc,则退出播放
else if(c == 27){
break;
}
}
return 0;
}
对于上述添加滑动条并实现单步模式和连续模式切换的代码,逻辑上有一点绕,具体分析如下:
① 初始状态,g_run=1,g_dontset=0,则采用单步模式。当第一次循环结束,g_run=0,g_dontset=0,则在不输入r、s或esc的情况下,不会进入视频读取的代码段,因此图片显示会停留在当前状态。
② 当按下“s”键后,g_run=1,g_dontset=0,和初始状态相同,则运行过程如①所示。
③ 当按下“r”键后,g_run=-1,g_dontset=0,采用连续模式。在每次循环后g_run减少1,则g_run永远是负值不为0,因此在没有其他操作情况下,会继续执行,实现连续播放。
④ 当手动调整进度条时,没有进入for循环中的if,所以g_dontset=0,此时则满足回调函数中的if条件,则g_run=1,g_dontset=0,和初始状态相同,采用单步模式继续进行。
⑤ 注意,在系统自动调用回调函数前g_dontset都被赋值为1,因此不会使得g_run=1,单步模式不会生效;只有当用户手动触发滑动条事件时,g_dontset=0,使得回调函数将g_run赋值为1,单步模式生效。