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 barWINDOW_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 imagesMat lena= imread("../lena.jpg");Mat photo= imread("../photo.jpg");// Create windowsnamedWindow("Lena", WINDOW_NORMAL);// Checking if Lena image has been loadedif (!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 imagesimshow("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 presswaitKey(0);// Destroy the windowsdestroyWindow("Lena");destroyWindow("Photo");// Create 10 windowsfor(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 windowsdestroyAllWindows();return 0;}
以上都是原生OpenCV
的一些基本图像处理过滤器,但也有新的开源替代品,它们能够添加更多功能,比如cvui
(https://dovyski.github.io/cvui/) 或OpenCVGUI
(https://damiles.github.io/OpenCVGUI/)。