OpenCV可移植图形工具HighGUI实现图像和视频操作

OpenCV把用于操作系统、文件系统以及摄像机等硬件设备交换的函数纳入了HighGUI(High-level Graphical User Interface)模块中。有了HighGUI模块,我们可以方便地打开窗口、显示图像、读出或写入图像相关的文件、鼠标事件和键盘事件。下面将对三部分分别进行介绍。

HighGUI三部分的作用
部分 作用
硬件部分 最主要是对摄像机的操作
文件系统部分 主要工作是完成图片的载入和保存
窗口系统GUI 创建窗口并将图片放入窗口显示,同时添加响应鼠标和键盘事件功能

图像文件的处理

图像的载入与保存

使用cv::imread()读取图片

cv::Mat cv::imread(const string& filename, int flags=cv::IMREAD_COLOR)

作用:载入图像,若失败不会抛出异常,返回的是空的cv::Mat(可以用cv::Mat::empty()==true来判断)。

flags的取值
标志 含义 默认值
cv::IMREAD_COLOR 读取三通道图像,即使输入是灰度图像,也会有三通道,只是每个通道拥有相同的数据
cv::IMREAD_GRAYSCALE 读取单通道图像
cv::IMREAD_ANYCOLOR 通道数由文件实际通道数(不超过3)
cv::IMREAD_ANYDEPTH 允许加载超过8bit深度
cv::IMREAD_UNCHANGED 相当于cv::IMREAD_ANYCOLOR和cv::IMREAD_ANYDEPTH组合使用,可保留alpha通道。

使用cv::imwrite()保存图像

bool cv::imwrite(const string& filename, InputArray image, const vector<int>& params=vector<int>())

作用:保存图像,成功返回true,否则返回false。

常用扩展名
扩展名 格式 大小 通道
.jpg或.jpeg baseline JPEG 8位 单通道或三通道
.jp2 JPEG2000 8位或16位 单通道或三通道
.tif或.tiff TIFF 8位或16位 单通道、三通道或四通道
.png PNG 8位或16位 单通道、三通道或四通道
.bmp BMP 8位 单通道、三通道或四通道
.ppm或.pgm NetPBM 8位 单通道(PGM)和三通道(PPM)
params的使用
标志 含义 取值范围 默认值
cv::IMWRITE_JPG_QUALITY JPEG的质量 0~100 95
cv::IMWRITE_PNG_COMPRESSION PNG压缩值 0~9 3
cv::IMWRITE_PXM_BINARY 对PPM、PGM或PBM文件是否使用二值格式 0或1 1

关于codecs的一些注释

用于图像处理的库通常被叫做codecs(co-mpression and dec-compress libraries-s)。OpenCV对一些图片格式自带编码解码库,对于这些有一下三中选择:

① 不使用这些编码解码库;

② 使用OpenCV所提供的编码解码库;

③ 使用相应的拓展库(如libjpeg、libpng等)。

在Windows系统中默认选②选项,而在OS X或Linux系统上,默认选③选项。

图片的编码与解码

使用cv::imencode()压缩图像

void cv::imencode(const string& ext, InputArray img, vector<uchar>& buf, const vector<int>& params=vector<int>())

作用:对图像进行编码。

参数列表
参数 含义
ext 文件扩展名,用于函数与对应的编码压缩方式相关联。指明格式并帮助系统索引对应编码库
img 要被压缩的图像数据
buf 用来存储编码压缩后的图像数据的字符数组
params 指明要使用编码器的输入参数

使用cv::imdecode()解码文件

cv::Mat cv::imdecode(InputArray buf, int flags=cv::IMREAD_COLOR)

作用:将字节流数据解码成数组形式的图像。

视频处理

使用cv::VideoCapture对象读取视频流

cv::VideoCapture::VideoCapture(const string& filename)

cv::VideoCapture::VideoCapture(int device)

cv::VideoCapture::VideoCapture()

作用:打开视频文件或摄像机。打开成功则cv::VideoCapture::isOpen()会返回true,否则返回false。

构造设备参数时,传入的标识符是相机的域索引和相机索引之和。相机索引是指系统中摄像机设备的索引,从0开始计数。相机域表明我们用何种相机,如下表所示。

相机域常量
相机捕捉常数 数值 相机捕捉常数 数值 相机捕捉常数 数值
cv::CAP_ANY 0 cv::CAP_DSHOW 700 cv::CAP_OPENNI2 1600
cv::CAP_MIL 100 cv::CAP_PVAPI 800 cv::CAP_OPENNI2_ASUS 1610
cv::CAP_VFW 200 cv::CAP_OPENNI 900 cv::CAP_GPHOTO2 1700
cv::CAP_V4L 200 cv::CAP_OPENNI_ASUS 910 cv::CAP_GSTREAMER 1800
cv::CAP_V4L2 200 cv::CAP_ANDROID 1000 cv::CAP_FFMPEG 1900
cv::CAP_FIREWIRE 300 cv::CAP_XIAPI 1100 cv::CAP_IMAGES 2000
cv::CAP_FIREWARE 300 cv::CAP_AVFOUNDATION 1200 cv::CAP_ARAVIS 2100
cv::CAP_IEEE11394 300 cv::CAP_GIGANETIX 1300 cv::CAP_OPENCV_MJPEG 2200
cv::CAP_DC1394 300 cv::CAP_MSMF 1400 cv::CAP_INTEL_MFX 2300
cv::CAP_QT 500 cv::CAP_WINRT 1410 cv::CAP_XINE 2400
cv::CAP_UNICAP 600 cv::CAP_INTELPERC 1500    

当传入的标识符为-1时,则OpenCV会弹出一个选择窗口,可手动选择希望使用的相机。

最后一种形式只是直接创建一个VideoCapture对象,无法直接使用,需要调用cv::VideoCapture::open()函数来指定文件名或设备号,和前两种形式一样。

使用cv::VideoCapture::read()从视频流中读取图像

bool cv::VideoCapture::read(OutputArray img)

作用:从文件中读取下一帧数据,并将结果存入img中,并会自动更新。读取成功返回true,读取失败返回false。

使用cv::VideoCapture::operator>>()从视频流读取图像数据

cv::VideoCapture& cv::VideoCapture::operator>>(Mat& img)

作用:重载输入流操作符,从文件中读取下一帧数据,并将结果存入img中。

使用cv::VideoCapture::grab()和cv::VideoCapture::retrieve()从视频流中读取图像数据

bool cv::VideoCapture::grab()

作用:将当前可使用的图像数据拷贝至用户看不到的一个内存缓存区。抓取的图像帧是未处理的,该函数只是设计用于将原始数据尽快获取到电脑。

bool cv::VideoCapture::retrieve(OutputArray image, int channel)

作用:对内部缓存数据进行必要的图像解码、内存分配与拷贝,最后以Mat形式的序列将图像帧返回。

相机属性cv::VideoCapture::get()和cv::VideoCapture::set()

double cv::VideoCapture::get(int propid)

bool cv::VideoCapture::set(int propid, double value)

作用:获取或设置元数据(meta data)。

视频捕捉属性常量
ID 是否只在摄像头模式下可以使用 含义
cv::CAP_PROP_POS_MSEC   视频文件中当前位置(毫秒)或视频捕获时间戳
cv::CAP_PROP_POS_FRAMES   从0开始下一帧的索引
cv::CAP_PROP_POS_AVI_RATIO   视频的相对位置
cv::CAP_PROP_FRAME_WIDTH   视频帧的像素宽度
cv::CAP_PROP_FRAME_HEIGHT   视频帧的像素高度
cv::CAP_PROP_FPS   录制视频的帧速率
cv::CAP_PROP_FOURCC   四个字符代码指示编解码
cv::CAP_PROP_FRAME_COUNT   视频文件的总帧数
cv::CAP_PROP_FORMAT   返回Mat对象的格式(如CV_8UC3)
cv::CAP_PROP_MODE   表示捕捉模式,值是特定于正在使用的视频后端(如DC1394)
cv::CAP_PROP_BRIGHTNESS 相机的亮度设置
cv::CAP_PROP_CONCAT 相机的对比度设置
cv::CAP_PROP_SATURATION 相机的饱和度设置
cv::CAP_PROP_HUE 相机的色调设置
cv::CAP_PROP_GAIN 相机的增益设置
cv::CAP_PROP_EXPOSURE 相机的曝光设置
cv::CAP_PROP_CONVERT_RGB 如果非0,获取的图像将被转换为具有三个通道
cv::CAP_PROP_WHITE_BALANCE 相机的白平衡设置
cv::CAP_PROP_RECTIFICATION 立体相机整流标志(仅适用于DC1934-2.x)

使用cv::VideoWriter对象写入视频

cv::VideoWriter::VideoWriter(const string& filename, int fourcc, double fps, Size frame_size, bool is_color=true)

cv::VideoWriter::VideoWriter()

作用:创建一个VideoWriter对象,is_color为true则传入彩色图,反之可以传入灰度图。同样第二种形式不能直接使用,需要调用cv::VideoWriter::open()函数进行配置。

判断是否初始化成功,可通过cv::VideoWriter::isOpen()的返回值进行判断。

使用cv::VideoWriter::write()函数完成图像帧的写入

cv::VideoWriter::write(const Mat& img)

作用:完成图像帧的写入,图像必须和cv::VideoWriter具有相同的图像尺寸,如果是彩色必是三通道,反之使用单通道。

使用cv::VideoWriter::operator<<()将图像写入视频

cv::VideoWriter& cv::VideoWriter::operator<<(Mat& img)

作用:重载输出流操作符,将img存入视频文件。

数据存储

作为对标准视频压缩方式的补充,OpenCV提供了一种序列化与反序列化的机制,用于将不同数据类型的数据已YAML或XML格式写入磁盘或者从磁盘读取。这些方法可以用来加载或者保存任何OpenCV的数值变量到一个文件中。

cv::FileStorage的写入

cv::FileStorage::FileStorage(string filename, int flag)

cv::FileStorage::FileStorage()

作用:创建FileStorage对象(代表XML或YAML格式的数据文件),flag指定功能,写入时可为cv::FileStorage::WRITE和cv::FileStorage::APPEND。同样第二种形式不能直接使用,需要调用cv::FileStorage::open()函数进行配置。

写入时可以使用重载输出操作符函数cv::FileStorage::operator<<()进行写入。写完时调用cv::FileStorage::release()关闭该文件。

cv::FileStorage内部数据的存储形式主要有mapping和sequence,在最顶层是一个mapping,其中可以嵌套mapping和sequence。新建mapping或sequence分别使用{}和[]。

#include "stdafx.h"
#include <opencv2/opencv.hpp>
#include <time.h>


// 使用cv::FileStorage创建一个test.yml参数文件
int main()
{
	// 创建FileStorage对象,实现其写入功能
	cv::FileStorage fs("test.yml", cv::FileStorage::WRITE);

	// 写入内容
	fs << "frameCount" << 5;
	
	// 获取当前时间并写入
	time_t rawtime;
	time(&rawtime);
	fs << "calibrationDate" << asctime(localtime(&rawtime));

	// 生成矩阵,并写入
	cv::Mat cameraMatrix = (cv::Mat_<double>(3, 3) << 1000, 0, 320, 0, 1000, 240, 0, 0, 1);
	cv::Mat distCoeffs = (cv::Mat_<double>(5, 1) << 0.1, 0.01, -0.001, 0, 0);
	fs << "cameraMatrix" << cameraMatrix << "distCoeffs" << distCoeffs;

	// 随机生成数及数组并写入
	fs << "features" << "[";
	for (int i = 0; i < 3; i++) {
		int x = rand() % 640;
		int y = rand() % 480;
		uchar lbp = rand() % 256;
		fs << "{:" << "x" << x << "y" << y << "lbp" << "[:";
		for (int j = 0; j < 8; j++) {
			fs << ((lbp >> j) & 1);
		}
		fs << "]" << "}";
	}
	fs << "]";

	// 关闭文件
	fs.release();
    return 0;
}

 用记事本打开test.yml文件,内容如下:

cv::FileStorage读取文件

cv::FileStorage::FileStorage(string filename, int flag)

cv::FileStorage::FileStorage()

作用:创建FileStorage对象(代表XML或YAML格式的数据文件),flag指定功能,读取时可为cv::FileStorage::READ。同样第二种形式不能直接使用,需要调用cv::FileStorage::open()函数进行配置。

读取时可以使用重载输出操作符函数cv::FileStorage::operator>>()进行读取,得到的不是直接的数据,而是FileNode对象。若该值是基本数据类型,则可以通过强制类型转换直接获取;若该值是mapping,则通过重载操作符[]并给定所需关键字访问;若该值是sequence,则通过重载操作符[]并给定整型参数访问,也可使用cv::FileStorageIterator迭代器进行访问。

读完时调用cv::FileStorage::release()关闭该文件。

cv::FileNode

FileNode的基本使用在上述内容中已做过介绍,下面将主要针对cv::FileStorageIterator进行介绍。

给定一个FileNode对象,通过cv::FileNode::begin()和cv::FileNode::end()可以分别返回该mapping或者sequence的首元素的迭代器和末尾首元素的迭代器。该迭代器对象可以通过cv::FileStorageIterator::operator*()来进行解引用操作并返回迭代器对应的位置的FileNode对象,并通过自增和自减来控制迭代器。

若其对一个mapping进行遍历,返回的FileNode对象将会有名字属性,可通过cv::FileNode::name()得到。同样,通过cv::FileNode::type()可获得cv::FileNode中定义的枚举类型。

cv::FileNode的成员函数
示例 说明
cv::FileNode fn 文件节点对象的默认构造函数
cv::FileNode fn1(fn0) 文件节点对象的复制构造函数,从节点fn0创建节点fn1
cv::FileNode fn1(fs,node) 文件节点对象构造函数,从C风格的CvFileStorage*指针和C风格的CvFileNode*指针节点创建FileNode对象

fn[ (string) key ]

fn[ (const char*) key ]

命名子串(映射节点)的STL字符串或C字符串存取器,将秘钥转换为适当的子节点。
fn[ (int) id ] 子编号(序列节点)的访问;将ID转换为相应的子节点
fn.type() 返回节点类型enum
fn.empty() 确定节点是否为空
fn.isNone() 确定节点是否为None
fn.isSeq() 确定节点是否为序列
fn.isMap() 确定节点是否为映射

fn.isInt()

fn.isReal()

fn.isString()

确定节点是否为整数

确定节点是否为浮点数

确定节点是否为字符串

fn.name() 如果节点是映射的子节点,返回节点名称
size_t sz= fn.size() 如果是序列或映射,返回节点中的元素数量,否则返回1。

(int) fn

(float) fn

(double) fn

(string) fn

从包含整数的节点中提取

从包含32位浮点数的节点中提取

从包含64位浮点数的节点中提取

从包含字符串的节点中提取

cv::FileNode::type()可能的返回值
示例 说明
cv::FileNode::NONE = 0 节点类型为None
cv::FileNode::INT = 1 节点包含一个整数

cv::FileNode::REAL = 2

cv::FileNode::FLOAT = 2

节点包含一个浮点数(不分精度)

cv::FileNode::STR = 3

cv::FileNode::STRING = 3

节点包含一个字符串
cv::FileNode::REF = 4 节点包含参考(即复合对象)
cv::FileNode::SEQ = 5 节点本身是其他节点的序列
cv::FileNode::MAP = 6 节点本身是其他节点的映射
cv::FileNode::FLOW = 8 节点是序列或映射的紧凑表示
cv::FileNode::USER = 16 节点是注册对象(如矩阵)
cv::FileNode::EMPTY = 32 节点没有复制给它
cv::FileNode::NAMED = 64 节点是映射的子节点(即它有一个名字)

注:一个FileNode除了拥有前8种属性之外还可以同时拥有最后四个中的一个或全部属性。

#include "stdafx.h"
#include <opencv2/opencv.hpp>


// 使用FileStorage来读取test.yml文件
int main()
{
	// 创建FileStorage对象,实现读取功能
	cv::FileStorage fs("D:\\personal-data\\opencv\\example8-2\\example8-2\\test.yml", cv::FileStorage::READ);

	// 对FileNode进行强制类型转换,得到数据
	int frameCount = (int)fs["frameCount"];

	// 使用重载>>操作符
	std::string date;
	fs["calibrationDate"] >> date;
	
	cv::Mat cameraMatrix, distCoeffs;
	fs["cameraMatrix"] >> cameraMatrix;
	fs["distCoeffs"] >> distCoeffs;

	std::cout << "frameCount" << frameCount << std::endl;
	std::cout << "calibrationDate" << date << std::endl;
	std::cout << "cameraMatrix" << cameraMatrix << std::endl;
	std::cout << "distCoeffs" << distCoeffs << std::endl;

	cv::FileNode features = fs["features"];
	cv::FileNodeIterator it = features.begin(), it_end = features.end();
	int idx = 0;
	std::vector<uchar> lbpval;

	// 使用迭代器遍历序列
	for (; it != it_end; ++it, idx++)
	{
		std::cout << "features #" << idx << ": ";
		std::cout << "x=" << (int)(*it)["x"] << ", y=" << (int)(*it)["y"] << ", lbp: (";
		(*it)["lbp"] >> lbpval;
		for (int i = 0; i < (int)lbpval.size(); i++)
		{
			std::cout << " " << (int)lbpval[i];
		}
		std::cout << ")" << std::endl;
	}

	//关闭文件
	fs.release();
    return 0;
}

运行结果:

frameCount5
calibrationDateMon Aug 20 20:20:03 2018

cameraMatrix[1000, 0, 320;
 0, 1000, 240;
 0, 0, 1]
distCoeffs[0.1;
 0.01;
 -0.001;
 0;
 0]
features #0: x=41, y=227, lbp: ( 0 1 1 1 1 1 0 1)
features #1: x=260, y=449, lbp: ( 0 0 1 1 0 1 1 0)
features #2: x=598, y=78, lbp: ( 0 1 0 0 1 0 1 0)

 

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章