OpenCV 笔记(02)— 图像显示、保存、腐蚀、模糊、canny 边缘检测(imread、imshow、namedWindow、imwrite)

OpenCV 提供两种用户界面选项:

  • 基于原生用户界面的基本界面,适用于 Mac OS Xcocoacarbon,以及适用于 LinuxWindows 用户界面的 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;
}

显示结果
demo1

代码分析:

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.
     };

所以有时候这个参数在调用时可以忽略,就表示载入三通道的彩色图像。

因为 flagsint 型的变量,若我们不在这个枚举体中取固定的值, 可以这样进行:

  • 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);
  1. 第一个参数, const string& 型的 name , 填写被用作窗口的标识符的窗口名称;
  2. 第二个参数, 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>());
  1. 第一个参数,const string& 类型的 filename,填需要写入的文件名。注意要带上后缀,如“save.jpg ”。
  2. 第二个参数, InputArray 类型的 img , 一般填一个 Mat 类型的图像数据。
  3. 第三个参数, const vector<int>& 类型的 params , 表示为特定格式保存的参数编码。它有默认值 vector<int>() , 所以一般情况下不需要填写。而如果要填写的话,有下面这些需要了解的地方:
  • 对于 JPEG 格式的图片, 这个参数表示从 0 到100 的图片质量 ( CV _IMWRITE_JPEG_ QUALITY ), 默认值是 95。
  • 对于 PNG 格式的图片, 这个参数表示压缩级别( CV_IMWRITE_PNG_COMPRESSION ) 从 0 到 9。较高的值意味着更小的尺寸和更长的压缩时间,默认值是 3。
  • 对于 PPMPGM 、或 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 函数等待按键按下,以便能让窗口一直显示。

显示结果:
demo2

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;
}

显示结果
demo3

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;
}

显示结果:
demo4

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 的一些基本图像处理过滤器,但也有新的开源替代品,它们能够添加更多功能,比如 cvuihttps://dovyski.github.io/cvui/) 或 OpenCVGUIhttps://damiles.github.io/OpenCVGUI/)。

  • 2
    点赞
  • 6
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值