本文通过 http://c.biancheng.net/view/1790.html 学习,总结并修改了一部分bug
Mat 类是存储和操作 OpenCV 中图像的主要数据结构。这个类是在 core 模块中定义的。OpenCV 已经实现了对于这些数据结构自动分配和释放内存的机制。但是,当数据结构共享相同的缓冲存储器时,程序员仍然应该特别注意。
例如,赋值运算符并没有从一个对象(Mat A)到另一个对象(Mat B)复制内存内容;而只是对其引用(相应内容的存储地址)的复制。之后,一个对象(A 或 B)的改变对两个对象都有影响。为了复制一个 Mat 对象的内存内容,应该使用成员函数Mat::clone()。
注意,OpenCV 中的许多函数在处理密集的单通道或多通道数组时,常使用 Mat 类。但是在某些场合,使用一个不同的数据类型可能很方便。例如,std::vector<>、Matx<>、Vec<> 或 Scalar。为此,OpenCV 提供了代理类 InputArray 和 OutputArray,允许前面的任意类型作为函数的参数使用。
Mat 类用于密集的 n 维单通道或多通道数组。实际上它可以存储实数或复数值向量和矩阵、彩色图像或灰度图像、直方图、点云等。
有许多种不同的方式可用来创建一个 Mat 对象,常用的是构造函数,其数组的大小和类型被指定为:
Mat(nrows,ncols,type,fillValue)
/*使用实例*/
Mat img_A(4,4,CV_8U,Scalar(255));
/*
具有 8 位无符号整数的 4x4 单个通道数组
白色图像;
(最多 255 个值,对灰度图像有效,例如,255=白色)
*/
注意,使用 OpenCV 函数正确地保存一幅 RGB 图像是很重要的,该图像必须按照 BGR 通道顺序在内存中存储。按照同样的方式,当从一个文件中读取一幅 RGB 图像时,应按照 BGR 通道顺序将其存储在内存中。而且,需要补充第四个通道(alpha)来处理具有三个通道(RGB)的图像,加上了一个透明度。对于 RGB 图像,整数值越大,意味着像素更亮或 alpha 通道更透明。
所有的 OpenCV 类和函数都在 cv 命名空间(namespace)中,因此,在源代码中还有如下两个选项:
1. 在包含头文件后还应添加使用命名空间 cv 的声明(using namespace cv);
2. 对用到的所有 OpenCV 类、函数和数据结构上附加 cv:: 前缀。如果由 OpenCV 提供的外部名字与常用的标准模块库(standard template library,STL)或其他库冲突,那么就推荐使用这个选项。
OpenCV 支持最常见的图像格式。但是,某些图像格式需要(免费提供的)第三方类库。由 OpenCV 支持的主要格式有:
需要辅助库的格式有:
对于 OpenCV 3.0 版本,除了上面列出的格式外,它还包含一个由地理数据抽象库(Geographic Data Abstraction Library,GDAL)所支持格式(NITF、DTED、SRTM等)的驱动器,通过 CMake 的选项 WITH_GDAL 来设置。注意,在 Windows OS 上,对 GDAL 的支持还没有经过广泛测试。
在 Windows 和 OS X 中,默认对这些格式(libjpeg、libjasper、libpng和libtiff)使用 OpenCV 附带的编解码器。之后,在这些操作系统中,可以读取 JPEG、PNG 和 TIFF 格式。Linux(和其他类 UNIX 开源操作系统)会寻找安装在系统中的编解码器。在 OpenCV 之前,可以安装编解码器或从 OpenCV 包中通过在 CMake 中设置正确的选项(例如,BUILD_JASPER、BUILD_JPEG、BUILD_PNG 和 BUILD_TIFF)来构建其他库。
代码实例:
1.准备好一张图片
2.开始编写test.cpp
#include
#include
using namespace std;
using namespace cv;
int main(int argc, char *argv[])
{
Mat in_image, out_image;
//对输入命令的正确性进行判断
if(argc != 3)
{
cout << "Usage: \n";
}
//读取原始图像
in_image = imread(argv[1], IMREAD_UNCHANGED);
if (in_image.empty())
{
//检查是否读取图像
cout << "Error! Input image cannot be read...\n";
return -1;
}
//创建两个具有图像名称的窗口
namedWindow(argv[1], WINDOW_FREERATIO);
namedWindow(argv[2], WINDOW_FREERATIO);
//在之前创建的窗口中显示图片
imshow(argv[1], in_image);
cvtColor(in_image, out_image, COLOR_BGR2GRAY);
imshow(argv[2], out_image);
cout << "Press any key to exit...\n";
waitKey(); // Wait for key press
//写入图像
imwrite(argv[2], in_image);
return 0;
}
3.编写CMakeLists.txt
project(test)
find_package(OpenCV REQUIRED)
add_executable(test test)
target_link_libraries(test ${OpenCV_LIBS})
cmake_minimum_required(VERSION 3.1)
4.执行命令
cmake .
make
./test zhou.jpg out.jpg
5.运行结果
(女神图片来源于网上,如有侵权,联系删除)
可以看到,我们成功将彩色图片 zhou.jpg 保存为黑白图片 out.jpg
代码解析
1.使用 #include 指令包含 opencv.hpp 头文件,实际上,它包含所有的 OpenCV 头文件。通过包含该单个文件,不再需要包含其他文件。
2.imread()函数:(OpenCV3.0版本)在 imgcodecs 模块中
原型为:Mat imread(const String& filename,int flags = IMREAD_COLOR)
flag 指定读取图像的颜色,并在 imgcodecs.hpp 头文件中由如下枚举类型定义和解释:
enum { IMREAD_UNCHANGED = -1, // 8 位,彩色或非彩色
IMREAD_GRAYSCALE = 0, // 8 位,灰度
IMREAD_COLOR = 1, //未改变深度、颜色
IMREAD_ANYDEPTH = 2, //任意深度,未改变颜色
IMREAD_ANYCOLOR = 4, //未改变深度,任意颜色
IMREAD_LOAD_GDAL = 8 // 使用 gdaL 驱动程序
};
读取输入的图像文件之后,应检查操作是否成功。可使用成员函数 in_image.empty() 来实现这个检查。
3.void namedWindow(const String& winname,int flags = WINDOW_AUTOSIZE)
如果读取图像文件时没有发生错误,会创建两个窗口分别显示输入图像和输出图像。使用此函数进行窗口的创建:
OpenCV 窗口是通过程序中一个意义明确的名字来识别的。通过下面 highgui.hpp 头文件中的枚举给出该标志的定义及其说明:
enum { WINDOW_NORMAL = 0x00000000, //用户可以调整窗口的大小(无约束) //还可以将一个全屏窗口切换为常规窗口 WINDOW_AUTOSIZE = 0x00000001, //用户不能调整窗口的大小, //窗口的大小由显示的图像所限制 WINDOW_OPENGL = 0x00001000, // OpenGL 支持的窗口 WINDOW_FULLSCREEN = 1, WINDOW_FREERATIO=0x00000100, //图像可任意使用(调整图像时可没有比例限制) WINDOW_KEE PRATIO = 0x00000000 //调整图像时保持图像的缩放比例 };
4.一个窗口的创建不会在屏幕上显示任何内容。在一个窗口中显示一幅图像的函数(属于 highgui 模块)是:
void imshow(const String& winname,InputArray mat)
如果使用 WINDOW_AUTOSIZE 标志创建该窗口(winname),那么所显示的是原始大小的图像(mat)。
在 showImage 示例中,第二个窗口显示输入图像的一个灰度副本。为了将一幅彩色图像转换为灰度图像,使用 imgproc 模块的函数 cvtColor。实际上使用这个函数来改变图像的颜色空间。
在一个程序中创建的任何窗口都可以从默认设置下调整大小和进行移动。当不再需要任何窗口时,应该销毁窗口,以便释放其资源。像示例中那样,在一个程序结束时,会隐式地完成资源的释放。
5.在内部循环中处理事件
如果在一个窗口上显示一幅图像之后不再做任何事情,出乎意料地,将不再显示图像。在一个窗口显示一幅图像之后,我们应该开始一个循环,以获取和处理与用户和窗口交互有关的事件。通过如下函数可执行这样一个任务(从 highgui 模块中):
int waitKey(int delay=0)
这个函数在数毫秒(delay>0)内等待一个按键操作,并返回键的编码,如果延迟结束时没有按键则返回 -1。如果 delay 是 0 或负数,那么函数一直等待直到一个键被按下。
6.写入图像文件
bool imwrite(const String& filename,InputArray img,const vector
这个函数将一幅图像(img)保存到一个文件(filename),作为第三个可选参数,一个“属性-值”对的向量指定编解码器的参数(为使用默认值将其设置为空)。编解码器由文件的扩展名决定。