OpenCV
提供两种用户界面选项:
- 基于原生用户界面的基本界面,适用于
Mac OS X
的cocoa
或carbon
,以及适用于Linux
或Windows
用户界面的GTK
,这些界面在编译OpenCV
时被默认选择。 - 基于
Qt
库的略微更高级的界面,这是跨平台的界面。必须在编译OpenCV
之前,在CMake
中手动启用Qt
选项。
1. 图像显示
在 OpenCV
中,图像显示过程非常简单,只需用 imread
函数载入到新版本的图像存储数据结构 Mat
类中,然后用 imshow
函数显示即可。
#include <iostream>
#include "opencv2/opencv.hpp"
using namespace cv;
// 使用 cv 命名空间,否则后面需要使用 cv::Mat, cv::imread 等带cv前缀
int main()
{
Mat image;
image = imread("./image/dog.jpg"); // 载入图像
if (image.empty()) // 或者 if(!image.data) 判断图片是否加载成功
{
std::cout << "image not exist";
return -1;
}
namedWindow("demo 图片");
imshow("demo 图片", image); // 显示图像
imwrite("save.jpg", image);
waitKey(0); // 等待任意按键按下,退出图片显示
return 0;
}
显示结果
代码分析:
1.1 Mat 类
Mat
类是用于保存图像以及其他矩阵数据的数据结构, 默认情况下其尺寸为 0。我们也可以指定其初始尺寸, 比如定义一个 Mat
类对象, 就要写 cv::Mat pic( 320, 640, cv::Scalar(100))
。
1.2 imread 载入图像函数
首先来看 imread
函数, 其用于读取文件中的图片到 OpenCV
中。可以在 OpenCV
官方文档中查到它的原型, 如下。
Mat imread( const String& filename, int flags = IMREAD_COLOR )
- 第一个参数,
const string&
类型的filename
, 填我们需要载入的图片路径名。 - 第二个参数,
int
类型的flags
, 为载入标识,它指定一个加载图像的颜色类型。默认值为 1 这个参数可以在OpenCV
中标识图像格式的枚举体中取值,详情如下:
enum ImreadModes {
IMREAD_UNCHANGED = -1, //!< If set, return the loaded image as is (with alpha channel, otherwise it gets cropped).
IMREAD_GRAYSCALE = 0, //!< If set, always convert image to the single channel grayscale image (codec internal conversion).
IMREAD_COLOR = 1, //!< If set, always convert image to the 3 channel BGR color image.
IMREAD_ANYDEPTH = 2, //!< If set, return 16-bit/32-bit image when the input has the corresponding depth, otherwise convert it to 8-bit.
IMREAD_ANYCOLOR = 4, //!< If set, the image is read in any possible color format.
IMREAD_LOAD_GDAL = 8, //!< If set, use the gdal driver for loading the image.
IMREAD_REDUCED_GRAYSCALE_2 = 16, //!< If set, always convert image to the single channel grayscale image and the image size reduced 1/2.
IMREAD_REDUCED_COLOR_2 = 17, //!< If set, always convert image to the 3 channel BGR color image and the image size reduced 1/2.
IMREAD_REDUCED_GRAYSCALE_4 = 32, //!< If set, always convert image to the single channel grayscale image and the image size reduced 1/4.
IMREAD_REDUCED_COLOR_4 = 33, //!< If set, always convert image to the 3 channel BGR color image and the image size reduced 1/4.
IMREAD_REDUCED_GRAYSCALE_8 = 64, //!< If set, always convert image to the single channel grayscale image and the image size reduced 1/8.
IMREAD_REDUCED_COLOR_8 = 65, //!< If set, always convert image to the 3 channel BGR color image and the image size reduced 1/8.
IMREAD_IGNORE_ORIENTATION = 128 //!< If set, do not rotate the image according to EXIF's orientation flag.
};
所以有时候这个参数在调用时可以忽略,就表示载入三通道的彩色图像。
因为 flags
是 int
型的变量,若我们不在这个枚举体中取固定的值, 可以这样进行:
flags>0
返回一个3 通道的彩色图像;flags=0
返回灰度图像;flags<0
返回包含 Alpha 通道的加载图像;
注意:若以彩色模式载入图像,解码后的图像会以 BGR
的通道顺序进行存储,即蓝、绿、红的顺序,而不是通常的 RGB
的顺序。
1.3 imshow 显示图像函数
imshow()
函数用于在指定的窗口中显示一幅图像, 函数原型如下。
void imshow(const String& winname, InputArray mat);
- 第一个参数:
const string&
类型的winname
,填需要显示的窗口标识名称。 - 第二个参数:
InputArray
类型的mat
, 填需要显示的图像。
imshow
函数用于在指定的窗口中显示图像。如果窗口是用 CV_WINDOW_AUTOSIZE
(默认值)标志创建的,那么显示图像原始大小。否则,将图像进行缩放以适合窗口。
而 imshow
函数缩放图像,取决于图像的深度,具体如下。
- 如果载入的图像是 8 位无符号类型(8-bit unsigned),就显示图像本来的样子。
- 如果图像是 16 位无符号类型(16-bit unsigned)或 32 位整型( 32-bit integer),便用像素值除以 256。也就是说,值的范围是[0,255 x 256]映射到[0,255]。
- 如果图像是 32 位浮点型(32-bit floating-point), 像素值便要乘以 255。也就是说, 该值的范围是[0,1]映射到[0,255]。
1.4 namedWindow 创建窗口函数
namedWindow
函数用于创建一个窗口。若是简单地进行图片显示,可以略去namedWindow
函数的调用,即先调用 imread
读入图片,然后用 imshow
直接指定出窗口名进行显示即可。
但需要在显示窗口之前就用到窗口名时, 比如我们后面会马上讲到滑动条的使用, 要指定滑动条依附到某个窗口上, 就需要 namedWindow
函数先创建出窗口, 显式地规定窗口名称了。
namedWindow
的函数原型如下:
void namedWindow(const String& winname, int flags = WINDOW_AUTOSIZE);
- 第一个参数,
const string&
型的name
, 填写被用作窗口的标识符的窗口名称; - 第二个参数,
int
类型的flags
, 窗口的标识, 可以填如下几种值;
enum WindowFlags {
WINDOW_NORMAL = 0x00000000, //!< the user can resize the window (no constraint) / also use to switch a fullscreen window to a normal size.
WINDOW_AUTOSIZE = 0x00000001, //!< the user cannot resize the window, the size is constrainted by the image displayed.
WINDOW_OPENGL = 0x00001000, //!< window with opengl support.
WINDOW_FULLSCREEN = 1, //!< change the window to fullscreen.
WINDOW_FREERATIO = 0x00000100, //!< the image expends as much as it can (no ratio constraint).
WINDOW_KEEPRATIO = 0x00000000, //!< the ratio of the image is respected.
WINDOW_GUI_EXPANDED=0x00000000, //!< status bar and tool bar
WINDOW_GUI_NORMAL = 0x00000010, //!< old fashious way
};
namedWindow
函数的作用是通过指定的名字, 创建一个可以作为图像和进度条的容器窗口。如果具有相同名称的窗口已经存在, 则函数不做任何事情。
我们可以调用 destroyWindow()
或者 destroyAllWindows()
函数来关闭窗口, 并取消之前分配的与窗口相关的所有内存空间。
但是事实上, 对于代码量不大的简单程序来说, 我们完全没有必要手动调用上述的 destroyWindow()
或者 destroyAllWindows()
函数, 因为在退出时, 所有的资源和应用程序的窗口会被操作系统自动关闭。
1.5 imwrite 保存图像函数
在 OpenCV
中, 输出图像到文件一般采用 imwrite
函数, 它的声明如下。
bool imwrite( const String& filename, InputArray img,
const std::vector<int>& params = std::vector<int>());
- 第一个参数,
const string&
类型的filename
,填需要写入的文件名。注意要带上后缀,如“save.jpg ”。 - 第二个参数,
InputArray
类型的img
, 一般填一个Mat
类型的图像数据。 - 第三个参数,
const vector<int>&
类型的params
, 表示为特定格式保存的参数编码。它有默认值vector<int>()
, 所以一般情况下不需要填写。而如果要填写的话,有下面这些需要了解的地方:
- 对于
JPEG
格式的图片, 这个参数表示从 0 到100 的图片质量 (CV _IMWRITE_JPEG_ QUALITY
), 默认值是 95。 - 对于
PNG
格式的图片, 这个参数表示压缩级别(CV_IMWRITE_PNG_COMPRESSION
) 从 0 到 9。较高的值意味着更小的尺寸和更长的压缩时间,默认值是 3。 - 对于
PPM
、PGM
、或PBM
格式的图片, 这个参数表示一个二进制格式标志(CV _IMWRITE_PXM_ BINARY
),取值为 0 或1 , 默认值是 1 。
注意: imwrite
函数用于将图像保存到指定的文件。图像格式是基于文件扩展名的,可保存的扩展名和 imread
中可以读取的图像扩展名一致。
2. 图像腐蚀
再来看如何用 OpenCV
实现最基本的形态学运算之腐蚀, 即用图像中的暗色部分“腐蚀”掉图像中的高亮部分。
#include <iostream>
#include "opencv2/highgui.hpp" // OpenCV highgui 模块头文件
#include "opencv2/imgproc.hpp" // OpenCV 图像处理头文件
using namespace cv;
int main()
{
Mat src = imread("./image/dog.jpg"); // 载入图像
if (src.empty()) // 或者 if(!image.data) 判断图片是否加载成功
{
std::cout << "image not exist";
return -1;
}
imshow("原始图片", src); // 显示图像
// 进行腐蚀操作
Mat element = getStructuringElement(MORPH_RECT, Size(15, 15));
Mat dst;
erode(src, dst, element);
imshow("腐蚀图片", dst);
// waitKey(30)等待30毫秒,以此检查用户是否使用任何键停止应用程序的执行。
waitKey(0); // 等待任意按键按下,退出图片显示,
return 0;
}
程序首先依然是载入和显示一幅图像,然后定义一个 Mat
类型的变量来获得 getStructuringElement
函数的返回值,而 getStructuringElement
函数的返回值为指定形状和尺寸的结构元素(内核矩阵)。参数准备完毕,接着便可以调用 erode
函数进行图像腐蚀操作,最后调用 imshow
函数进行显示,用 waitKey
函数等待按键按下,以便能让窗口一直显示。
显示结果:
3. 图像模糊
接着让我们看看用 OpenCV
对图像进行均值滤波操作,模糊一幅图像的代码如何书写。主要使用进行均值滤波操作的 blur
函数,代码如下:
#include <iostream>
#include "opencv2/highgui.hpp"
#include "opencv2/imgproc.hpp"
using namespace cv;
int main()
{
Mat src = imread("./image/dog.jpg"); // 载入图像
if (src.empty()) // 或者 if(!image.data) 判断图片是否加载成功
{
std::cout << "image not exist";
return -1;
}
imshow("原始图片", src); // 显示图像
// 进行均值滤波模糊操作
Mat dst;
blur(src, dst, Size(15, 15));
imshow("模糊图片", dst);
waitKey(0); // 等待任意按键按下,退出图片显示
return 0;
}
显示结果
4. 边缘检测
接着我们来看看如何用 OpenCV
进行 canny
边缘检测。载入图像,并将其转成灰度图,再用 blur
函数进行图像模糊以降噪,然后用 canny
函数进行边缘检测,最后进行显示。
#include <iostream>
#include "opencv2/highgui.hpp"
#include "opencv2/imgproc.hpp"
using namespace cv;
int main()
{
Mat src = imread("./image/dog.jpg"); // 载入图像
if (src.empty()) // 或者 if(!image.data) 判断图片是否加载成功
{
std::cout << "image not exist";
return -1;
}
imshow("原始图片", src); // 显示图像
Mat dst, edge, gray;
// 创建与原图片同类型和大小的矩阵( dst )
dst.create(src.size(), src.type());
// 将原图像转换为灰度图像 OpenCV2 版本
// cvtColor(src, gray, CV_BGR2GRAY)
// OpenCV3 或者 OpenCV4 版本
cvtColor(src, gray, COLOR_BGR2GRAY);
// 先使用5 x 5 内核来降噪
blur(gray, edge, Size(5, 5));
// 运行Canny 算子
Canny(edge, edge, 1, 3, 3);
imshow("Canny 边缘检测", edge);
waitKey(0); // 等待任意按键按下,退出图片显示
return 0;
}
显示结果:
5. 其它示例
#include <iostream>
#include <string>
#include <sstream>
using namespace std;
// OpenCV includes
#include "opencv2/core.hpp"
#include "opencv2/highgui.hpp"
using namespace cv;
const int CV_GUI_NORMAL= 0x10;
int main( int argc, const char** argv )
{
// Read images
Mat lena= imread("../lena.jpg");
Mat photo= imread("../photo.jpg");
// Create windows
namedWindow("Lena", WINDOW_NORMAL);
// Checking if Lena image has been loaded
if (!lena.data) {
cout << "Lena image missing!" << endl;
return -1;
}
namedWindow("Photo", WINDOW_AUTOSIZE);
if (!photo.data) {
cout << "Lena image missing!" << endl;
return -1;
}
// Move window
// moveWindow函数将窗口移动到桌面的任何区域
moveWindow("Lena", 10, 10);
moveWindow("Photo", 520, 10);
// show images
imshow("Lena", lena);
imshow("Photo", photo);
// Resize window, only non autosize
// resizeWindow函数将Lena窗口的大小调整为512像素,该函数有三个参
// 数:window name、width和height。
resizeWindow("Lena", 512, 512);
// wait for any key press
waitKey(0);
// Destroy the windows
destroyWindow("Lena");
destroyWindow("Photo");
// Create 10 windows
for(int i =0; i< 10; i++)
{
ostringstream ss;
ss << "Photo " << i;
namedWindow(ss.str());
moveWindow(ss.str(), 20*i, 20*i);
imshow(ss.str(), photo);
}
waitKey(0);
// Destroy all windows
destroyAllWindows();
return 0;
}
以上都是原生 OpenCV
的一些基本图像处理过滤器,但也有新的开源替代品,它们能够添加更多功能,比如 cvui
(https://dovyski.github.io/cvui/) 或 OpenCVGUI
(https://damiles.github.io/OpenCVGUI/)。