OpenCV学习心得——基础篇——图像与视频的读取与写入
FOR THE SIGMA
FOR THE GTINDER
FOR THE ROBOMASTER
简介:
这一系列的学习心得第一轮将参考《学习OpenCV3》一书
操作系统版本:Ubuntu16.04(在这里博主在Linux下进行运行的)
http://www.ubuntu.org.cn/download/desktop 桌面版ubuntu16.04 下载
电子版书籍下载地址
暂无资源
参考:
内容:
opencv将用于操作系统、文件系统以及摄影机等硬件设备交互的函数纳入到HighGUI模块中。可以用来打开窗口(调试)、显示图像、读取或者写入图像相关的文件、处理简单的鼠标点击与移动,键盘事件等。
Highgui模块主要有3个模块:硬件相关的部分、文件系统部分、图形用户界面部分。
硬件部分最主要的是对摄影机的操作。
文件系统部分的主要工作是完成图片的载入、保存与视频的读取与处理。
窗口部分的主要工作为新建一个窗口,将图片或视频放入窗口中显示。
图片处理
opencv为为我们提供了许多的特殊函数用于图片加载与保存,它们显式地或者隐式地解决了u图像数据紧密相关的编码与解码问题。与后面的以XML/YAML为基础的函数有些差别。前者主要应用与图像的文件处理,与普通的数组数据不一样,图像数据的处理对现有压缩与解压算法有很强的依赖性。而一些图像压缩与解压缩的算法会通过牺牲一些图像中的部分信息为代价,使得一些参数数据丢失。
图像的载入与保存
cv::imread()
cv::Mat cv::imread(
const string& filename,
int flags = cv::IMREAD_COLOR
);
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通道 | 否 |
使用该函数可以读取一张图片,同时函数并不是靠拓展名来的,而是分析文件中前几个字节来确定图像的编码格式。而flags值的选取参考以上几个。
alpha通道则是一个8位的灰度通道,该通道用256级灰度来记录图像中的透明度信息,定义透明、不透明和半透明区域,其中白表示不透明,黑表示透明,灰表示半透明。
参考:
cv::imwrite()
bool cv::imwrite(
const string& filename,
cv::InputArray image,
const vector<int>& params = vector<int>()
第一个参数给定文件名,决定以何种格式保存图像。
扩展名 | 格式 | 大小 | 通道 |
.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) |
第二个参数是待储存的输入图像。
标志 | 含义 | 取值范围 | 默认 |
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 |
第三个参数被用作特殊类型文件的写入操作时所需的数据。
实例:huitu.cpp
#include <iostream>
#include <opencv2/core/core.hpp>
#include <opencv2/highgui/highgui.hpp>
using namespace std;
using namespace cv;
Mat src;
Mat image;
string str = "./";
/*创建alpha表,整体偏红色,左上角到右下角呈现从完全透明到完全不透明变化趋势*/
void createAlphaMat(Mat &mat)
{
for (int i = 0; i < mat.rows; ++i) {
for (int j = 0; j < mat.cols; ++j) {
Vec4b& rgba = mat.at<Vec4b>(i, j);
rgba[0] = UCHAR_MAX; //r分量一直最大,所以整体偏红
rgba[1] = saturate_cast<uchar>((float (mat.cols - j)) / ((float)mat.cols) * UCHAR_MAX);
rgba[2] = saturate_cast<uchar>((float (mat.rows - i)) / ((float)mat.rows) * UCHAR_MAX);
rgba[3] = saturate_cast<uchar>(0.5 * (rgba[1] + rgba[2]));
}
}
}
int main()
{
/*采用默认参数进行图片的保存*/
src = imread("pic.jpg");
imwrite(str+"原图.jpg", src); //c版本中的保存图片为cvSaveImage()函数,c++版本中直接与matlab的相似,imwrite()函数。
imshow("src", src);
Rect rect(src.cols/4, src.rows/4, src.cols/2, src.rows/2);
image = src(rect);
imwrite(str+"截取原图中的一部分区域小图.jpg", image);
imshow("image", image);
/*采用自己设置的参数来保存图片*/
Mat mat(480, 640, CV_8UC4);
createAlphaMat(mat);
vector<int> compression_params;
compression_params.push_back(CV_IMWRITE_PNG_COMPRESSION);
compression_params.push_back(9); //png格式下,默认的参数为3.
try {
imwrite("alpha.png", mat, compression_params);
}
catch (runtime_error& ex) {
fprintf(stderr, "Exception converting image to PNG format: %s\n", ex.what());
return 1;
}
fprintf(stdout, "Saved PNG file with alpha data.\n");
waitKey(0);
return 0;
}
编译流程参考:
效果图:
其中src为程序所提取的图片即pic.jpg,保存为两个图片(大小不一样),附带alpha通道使用。
opencv针对一些图片格式(JPEG、PNG、TIFF等)自带用于编码解码库
三种选择:
1、不使用这些编解码库。
2、使用Opencv提供了的编解码库(与其他模块一切编译)(windows系统)
3、使用对应的拓展库(libjpeg,libpng等)(OS X/Linux系统)
注:若CMake无法发现编解码库,就会使用选项2.
cv::imencode()
void cv::imencode(
const string& ext,
cv::InputArray img,
vector<uchar>& buf,
const vector<int>& params = vector<int>()
);
用于压缩图像,第一个参数ext以string表示的文件扩展名,用于函数与对应的编码压缩方法相关联。img为被编码压缩的图像数据。参数buf表示用来储存编码压缩后的图像数据的字符数组。参数params用于指明要使用编解码器的输入参数。参数params可以选用的值是cv::imwrite()的输入参数。
cv::imdecode
cv::Mat cv::imdecode(
cv::InputArray buf,
int flags = cv::IMREAD_COLOR
);
用于将字节流数据解码成数组形式的图像。第一个参数为字节流数据buf(std::vector类型),第二参数flags的取值与cv::imwrite()函数使用的flags有相同的选项。
视频的读取与写入
cv::VideoCapture
cv::VideoCapture::VideoCapture(
const string& filename,
);
cv::VideoCapture::VideoCapture(
int device
);
cv::VideoCapture::VideoCapture();
在第一个构造函数中,需要各处对应的视频文件的文件名即可,opencv会打开对应的视频文件,如果打开成功则可以从中获取图像帧,成员函数cv::VideoCapture::isOpened()会返回ture。若由于文件无法打开(要吗是绝对地址不正确,要吗就是文件不存在)或是视频被编码时所用的编解码器未知,将会返回false。
在多个相机一起工作时,我们给出一个标示符来表明我们希望使用哪个摄像机以及我们希望操作系统以何种方式与该摄像机通信。如果当下系统只有一个摄像机时参数为0,当有多个摄像机同时处在当前系统之中时,参数随之递增即可。标识符的另一部分为相机的域。
相机捕捉参数 | 对应数值 |
cv::CAP_ANY | 0 |
cv::CAP_MIL | 100 |
cv::CAP_VFW | 200 |
cv::CAP_V4L | 200 |
cv::CAP_V4L2 | 200 |
cv::CAP_FIREWIRE | 300 |
cv::CAP_IEEE1394 | 300 |
cv::CAP_DC1394 | 300 |
cv::CAP_CMU1394 | 300 |
cv::CAP_QT | 500 |
cv::DSHOW | 700 |
cv::CAP_PVAPI | 800 |
cv::CAP_OPENNI | 900 |
cv::CAP_ANDROID | 1000 |
这里举个例子:
cv::VideoCapture capture( cv::CAP_FIREWIRE);
该例子中将试图打开第一个对应的FireWire相机。在大多数情况下,如果只有一个相机,则值不是必须的,使用cv::CAP_ANY即可。
创建VideoCapture对象的最后一种方式便是直接创建一个VideoCapture对象而不提供关于即将打开的硬件设备的任何信息,例如下面这个。
cv::VideoCapture cap;
cap.open( "my_video.avi" );
==cv::VideoCapture::read() ==
bool cv::VideoCapture::read(
cv::OutputArray image
);
当我们有一个cv::VideoCapture对象时便可从中读取图像,该函数会从cv::VideoCapture表示的文件中读取下一个帧数据,并将数据插入提供了的变量中。这行为将自动使VideoCapture对象更新,以便随后调用cv::VideoCapture:read()能够返回下一帧的图像。若读取未成功,将返回false,同时为函数提供的序列对象也会为空。
cv::VideoCapture::operator>>()
cv::VideoCapture& cv::VideoCapture::operator>>(
cv::Mat& image
);
使用重载函数从VideoCapture对象中读取下一帧数据。由于是个流操作符,所以无论读取是否成功,它都会返回一个对最初的cv::Capture对象的引用,作用与read完全相同,所以需要检查返回的图像序列是否为空。
cv::VideoCapture::grab()与cv::Video::retrieve()
只读取一张图像并完成解码的操作,可以看成一个捕获操作(内存拷贝)和恢复操作(实际完成对抓取数据的解码)。
bool cv::VideoCapture::grab(void)
bool cv::VideoCapture::retrieve(
cv::OutputArray image,
int channel = 0
);
cv::VideoCapture::grab()函数被设计将原始图像数据尽快获取到电脑(一般是从相机),将当前可以使用的图像数据拷贝至用户看不到的一个内部缓存区,抓取成功时返回true。一旦成功抓取便可以调用cv::VideoCapture::retrieve()函数处理必要的图像解码,内存分配与拷贝,最终以cv::Mat形式的序列将图像帧返回给你。在传入不同的通道参数时,可以通过多次调用cv::VideoCapture::retrieve()函数。
cv::VideoCapture::get()和cv::VideoCapture::set()
视频文件通常不仅仅包含一帧一帧的图像,还包含许多重要的元数据,这对于正常处理文件有着重要的意义。当打开一个视频文件时,相关信息就会被拷贝到cv::VideoCapture对象内部数据区。
double cv::VideoCapture::get(
int propid
);
bool cv::VideoCapture::set(
int propid //类型
double value //数值
);
视频捕获属性 | 必须要摄像头模式才能用 | 含义 |
cv::CAP_PROP_POS_MSEC | NO | 视频文件中的当前位置ms或视频捕获时间戳 |
cv::CAP_PROP_POS_FRAMES | NO | 从零开始下一帧的索引 |
cv::CAP_PROP_POS_AVI_RATIO | NO | 视频中的相对位置(范围0.0~0.1) |
cv::CAP_PROP_FRAME_WIDTH | NO | 视频帧的像素宽度 |
cv::CAP_PROP_FRAME_HEIGHT | NO | 视频帧的像素高度 |
cv::CAP_PROP_FPS | NO | 视频的帧速度 |
cv::CAP_PROP_FOURCC | NO | 四个字符代码指示编解码 |
cv::CAP_PROP_FRAME_COUNT | NO | 视频文件中的帧总数 |
cv::CAP_PROP_FORMAT | NO | 返回的Mat对象的格式(CV_8UC3) |
cv::CAP_PROP_MODE | NO | 表示捕捉模式,值是特定于正在使用的视频后端 |
cv::CAP_PROP_BRIGHTNESS | YES | 相机的亮度设置(支持时) |
cv::CAP_PROP_CONTRAST | YES | 相机的对比度设置(支持时) |
cv::CAP_PROP_SATURATION | YES | 相机饱和度设置(支持时) |
cv::CAP_PROP_HUE | YES | 相机色调设置(支持时) |
cv::CAP_PROP_GAIN | YES | 相机的增益设置(支持时) |
cv::CAP_PROP_EXPOSURE | YES | 相机曝光设置(支持时) |
cv::CAP_PROP_CONVERT_RGB | YES | 非零则图像转换为具有三个通道 |
cv::CAP_PROP_WHITE_BALANCE | YES | 相机的白平衡设置(支持时) |
cv::CAP_PROP_RECTIFICATION | YES | 立体相机整理标志(仅适用于DC1394-2.x) |
上方表中并非后能够被VideoCapture的后端部分识别以及处理。例如在由Firewire相机启动,QuickTime或是Kinect驱动所用的捕获机制差别非常大,并不对上表所有参数支持
cv::VideoWriter
我们对视频的另一个操作就是将视频写入磁盘。Opencv使得这事变得很简单。
在写入视频前,必须先建立一个cv::VideoWriter对象。此时VideoWriter类仅有两个构造函数,一个是默认构造函数,仅仅创建一个未初始化的VideoWriter对象用于之后的打开操作,另一种是拥有所有需要的参数,并初始化VideoWriter对象。
cv::ViedoWriter;:VideoWriter(
const string& filename,
int fourcc, //编解码格式
double fps, //帧率
cv::Size frame_size, //每一帧的大小
bool is_color = true //true彩色,false灰度
);
例如:
cv::VideoWriter out;
out.open(
"my_video.mpg",
CV_FOURCC('D','I','V','X'), //MPEG-4
30.0, //FPS
cv::Size( 640 , 480 ),
true
);
cv::VideoWriter:write()
cv::VideoWriter::write(
const Mat& image
);
一旦确认创建对象之后就可以开始写入了,图像必须和你所创建的VideoWriter具有相同的图像尺寸。采用相对应的通道数。
cv::VideoWriter::operator<<()
进行输出流操作符的重载,一旦VideoWriter成功打开,便可以向视频写入图像帧了。
my_video_writer << my_frame;
实例部分
#include<opencv2/opencv.hpp>
#include<iostream>
using namespace cv;
using namespace std;
int main()
{
VideoCapture cap;
cap.open("/home/whl970831/sdad/150/Windmills-master/wind.mp4");
//cap.open(0);外接摄像头调用,否则会报错,同时0是指相机序号(即默认电脑的摄像头)改为1就是外接摄像头
if(!cap.isOpened())
return 0;
cap.set(CV_CAP_PROP_FRAME_WIDTH, 720); //帧宽度
cap.set(CV_CAP_PROP_FRAME_HEIGHT,640); //帧高度
cap.set(CV_CAP_PROP_FPS,30); //帧率
//下方需要外接一个摄像头否则不支持
//cap.set(CAP_PROP_BRIGHTNESS,(-1)); //亮度
//cap.set(CV_CAP_PROP_CONTRAST,40);//对比度 40
//cap.set(CV_CAP_PROP_SATURATION, 50);//饱和度 50
//cap.set(CV_CAP_PROP_HUE, 50);//色调 50
//cap.set(CV_CAP_PROP_EXPOSURE, 50);//曝光 50
---------------------
Mat frame;
while(1)
{
cap>>frame;//等价于cap.read(frame);
if(frame.empty())
break;
imshow("video", frame);
if(waitKey(20)>0)
break;
}
cap.release();
}
进行编译。
退出可以按任意键或者直接Ctrl+c