【OpenCV教程】第二章 读取、显示、保存以及修改图片

#【OpenCV教程】第二章 读取、显示、保存、修改图片

从昨日至今,从开始对OpenCV的懵懂到现在渐入佳境,带来部门的书籍也从之前的《人工智能-一种现代的方法变成OpenCV系列书籍,还花了不少银子从京东购入大量关于OpenCV的系列书籍!希望自己能取得更大进步,以此勉励自己,在看不到未来的道路上,依旧努力奔跑,不能停步。

OpenCV用C++编写,它的主要接口是C++,但是依然保留了大量C的接口。该库还有大量Python、Java、Matlab的接口,现如今也提供对C#、Ruby、Go的技术接口。

本文主要思路

本文主要讲述如何利用OpenCV读取、显示、保存以及修改图片

  • 利用OpenCV读取显示图片
  • 利用OpecCV保存图片
  • 利用OpenCV修改图片

OpenCV程序的基本流程图

以下是我这两天使用OpenCV总结的一个基本流程图

Created with Raphaël 2.2.0 读取图片 对图片进行相应操作 销毁相应窗口

基本标准的C++代码框架如下:

#include <iostream>

#include <opencv2/opencv.hpp>
#include <opencv2/core/core.hpp>
#include <opencv2/highgui.hpp>

using namespace cv;
using namespace std;

int main(int argc, char** argv)
{
	/******************************
	 * OpenCV读取Image的两种方式
	 * 1. 通过imread读取
	 * 2. 通过cvLoadImage读取
	 * 下面例举了两种方式的具体方法,选取其中一个即可
	 * 建议使用imread读取
	******************************/
	Mat image = imread(argv[1], 1);
	Mat image = cvLoadImage(argv[1]);

	// TODO: 对图像的具体操作

	/******************************
	 * OpenCV中等待中断的方式有两种
	 * 1. cvWaitKey(0)
	 * 2. waitKey(0)
	 * 二者的区别是,有cv的是C语言的操作方式,当使用waitKey(0)时需要在代码前使用命名空间,using namespace cv
	******************************/
	cvWaitKey(0);
	waitKey(0);

	// 释放内存,破坏展示窗口
	cvReleaseImage(image);
	cvDestroyAllWindows();
	
	return 0;
}

利用OpenCV读取显示图片

创建工程文件夹,小智学长使用的Mac操作系统,和第一章演示的一样,使用的是CMake,当然可以使用使用Mac下的XCode,或者Window下面的VS,根据自己实际使用的情况,输入以下代码,进行编译,执行即可。

> cd ~/Code/OpenCV  # 这是小智学长Mac上存放OpenCV代码的地方
> mkdir ReadAndShow #创建工程文件夹ReadAndShow
> cd ReadAndShow #创建ReadAndShow文件夹
> touch ReadAndShow.cpp
> vim ReadAndShow.cpp
#输入以下C++代码
#include <iostream>

#include <opencv2/opencv.hpp>
#include <opencv2/core/core.hpp>
#include <opencv2/highgui.hpp>

using namespace cv;
using namespace std;

int main(int argc, char** argv)
{
	// 为方便操作,我们从命令行读取图片名称
	if(argc != 2) {
		cout << "Usage: ./ProjectName ImageName" << endl;
		return -1;
	}

	Mat image = imread(argv[1], CV_LOAD_IMAGE_COLOR);

	// 判断图片是否为空图片
	if(!image.data) {
		cout << "Could not open or find the image" << endl;
		return -1;
	}

	// 创新一个窗口用于展示图片
	namedWindow("Display image window", CV_WINDOW_AUTOSIZE);
	imshow("Display image window", image);

	waitKey(0);

	return 0;
}

在ReadAndShow文件夹下创建CMakeLists.txt文件

> cd ReadAndShow
> touch CMakeLists.txt
> vim CMakeLists.txt
# 输入下面文件
project( ReadAndShow )
find_package( OpenCV REQUIRED )
add_executable( ReadAndShow ReadAndShow )
target_link_libraries( ReadAndShow ${OpenCV_LIBS} )

执行如下命令

> cmake .
> make
> ./ReadAndShow WechatIMG78.jpeg

可以得到如下结果:

![执行结果]
(https://img-blog.csdn.net/20180816142856349?watermark/2/text/aHR0cHM6Ly9ibG9nLmNzZG4ubmV0L3UwMTIyNjY2MzQ=/font/5a6L5L2T/fontsize/400/fill/I0JBQkFCMA==/dissolve/70#pic_center=100x100)

重点函数解析
关于Mat

在2001年,OpenCV刚刚出来的时候,OpenCV是基于C语言开发的,为了在内存中存储照片,采用名为IplImage的C语言结果题,其中《学习OpenCV中文版》(清华大学出版社)中采用的就是IplImage的方式,所有大家以后买参考资料的时候可以暂时跳过这本书。采用C语言开发,在享受其便捷的同时,必须接受其不足,其中最大的问题就是内存管理,需要程序自己手动管理内存,在小程序和自己的demo中这点不是问题,但是在大型项目中,我们不能不重视这个问题。

在OpenCV2.0中,基于C++进行开发,引入类的概念,同时带来了另外一项便利,那就是自动内存管理。有点类似Python的内存管理,主要通过引用计数机制来实现,简而言之,就是有人引用Mat时,引用计数机制就加一,反之,就减一,当引用计数机制为零时,就会释放内存。

Mat主要由两部分组成:一个是矩阵头(包括矩阵的尺寸、存储方法和存储地址等信息),另外一个是指向存储所有像素值存储矩阵的指针。不妨站在设计者的角度思考?Mat类为什么要这样存储?众所周知,图像的操作中,开销最大的是矩阵的复制,而非矩阵头,局枕头的存储空间是固定而且相对矩阵而言是非常小的,因此为了避免不必要的开销,在运算的过程中,我们一般传递矩阵头。因此在复制操作中,我们往往也是复制矩阵头,当我们真正想要复制矩阵时,OpenCV为我们提供了两个函数clone()copyTo()

Mat的具体操作如下:

// 此处只创建矩阵头(有些书籍上叫做信息头,二者通用)
Mat imageFirst, imageSecond; 
// 读取照片,并未矩阵开辟具体的存储空间,将存储地址写入矩阵头中
imageFirst = imread(argv[1], CV_LOAD_IMAGE_COLOR); 

// 使用拷贝构造函数,只拷贝矩阵头和矩阵指针
Mat imageThird(imageFirst);   
imageSecond = imageFrist;

// 真正拷贝矩阵的方式
Mat imageClone = imageFirst.clone();
Mat imageCopy;
imageFirst.copyTo(imageCopy);

// 我一直在寻找一种方式,希望写代码有写诗的感觉,相比有些博客动不动用A、B命名,我更像有一个更美妙的变量名。

TODO: 草稿,需要修改

imread() 函数

可以子啊OpenCV官方文档中查看到imread函数的原型:
Mat imread(const string& filename, int flags=1)

  • 第一个参数,const string&类型的filename,填我们需要载入的图片路径名。
  • 第二个参数,int类型的flags,为载入标识,它制定一个加载图像的颜色类型。可以看到它自带默认值1,所有有时候这个参数在调用时可以忽略。如果忽略,标识载入三通道的彩色图像。如果输入有冲突鄂标志,采用较小的数字值。另外如果以彩色模式载入图像,解码后的图像将以BGR的通道顺序进行存储,即蓝、绿、红的顺序,而不是通常的RGB的顺序。
imshow函数

imshow()函数用于在指定的窗口中显示一副图像,函数的原型如下:
void imshow(const string& winname, InputArray mat)

  • 第一个参数:const string&类型的winname,填需要显示的窗口标识名称
  • 第二个参数: InputArray类型的mat,填需要显示的图像

imshow函数用于在指定的窗口中显示图像,如果窗口是用CV_WINDOW_AUTOSIZE 标志创建的,那么显示图像的原始大小。否则,将图像将图像进行缩放以适合窗口。而imshow函数缩放图像,取决于图像的深度,具体如下:

  • 如果载入的图像是8位无符号类型,就显示图像本来的样子
  • 如果图像是16位无符号类型或32位整型,便用像素值除以256。也就是说,值得范围是[0, 255*256]映射到[0, 255].
  • 如果图像是32位浮点型,像素值便要乘以255.也就是说,该值得范围是[0,1]映射到[0,255]。
namedWindow()函数

namedWindow函数用于创建一个窗口。若是简单地进行图片显示,可以略去namedWindow函数的调用,即先调用imread读入图片,然后用imshow函数指定出窗口名进行显示即可。
namedWindow函数的原型如下:
void namedWindow(const string& winname, int flags=WINDOW_AUTOSIZE)

  • 第一个参数,const string&型的name,填写被用作窗口标识符的窗口名称
  • 第二个参数,int类型的flags,窗口的标识,可以填写如下几种值
  1. WINDOW_NORMAL,设置这个值,用户可以改变窗口的大小(没有限制)。OpenCV2中还可以写成CV_WINDOW_NORMAL
  2. WINDOW_AUTOSIZE,设置这个值,窗口的大小会自动调整以适应现实的图像,并且用户不能手动改变窗口大小,OpenCV2中它还可以写成CV_WINDOW_AUTOSIZE。
  3. WINDOW_OPENGL,设置这个值,窗口创建的时候会支持OpenGL。OpenCV2中它还可以写成CV_WINDOW_OPENGL。

在通常情况下,namedWindow函数的默认值是WINDOW_AUTOSIZE,同时可以调用destroyWindow()或者destroyAllWindows()函数来关闭窗口,并取消之前分配的与窗口相关的所有内存空间。

输出图像到文件:imwrite()函数

在OpenCV中,输出图像到文件一般采用imwrite函数,它的声明如下:
bool imwrite(const string& filename, InputArray img, const vector& params=vector())

  • 第一个参数,const string&类型的filename,填需要写入的文件名,注意要带上后缀。
  • 第二个参数,InputArray类型的img,一般填一个Mat类型的图像数据
  • 第三个参数,const vector&类型的params,表示为特定格式保存的参数编码,它的默认值为vector()
滑动条的创建和使用

滑动条(Trackbar)是OpenCV动态调节参数特别好用的一种工具,它依附于窗口而存在。
由于OpenCV中并没有实现按钮的功能,所有很多时候,我们还可以用仅含0-1的滑动条来实现按钮的按下,弹起效果。下面是createTrackbar的函数原型:
int createTrackbar(const string& trackbarname, const string& winname, int* value, int count, TrackbarCallback onChange=0, void* userdata=0)

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值