分享

OpenCV:图像的加载显示及简单变换

 cyy_1212 2014-05-22

摘要(Abstract 通过笔记一的学习,我们已经能够下载、安装OpenCV并新建VS2010项目进行相关的配置,笔记一也已完成第一个程序HelloCV的演示。本文首先通过详细介绍OpenCV中如何从硬盘加载/读取一幅图像,并在窗口中进行显示来对笔记一中的演示程序做详解。其次,本文实现了简单的图像变换,将一幅RGB颜色的图片lena.jpg转化成灰度图像,以达到修改的目的,另外,在此变换中,我们还对如何将图片保存到硬盘上进行讲解。实验结果表明,通过笔记二的学习,不但能够增强对OpenCV的学习兴趣,还能有初体验OpenCV的成就感,吃嘛嘛香,为后续的学习打下坚实的基础。


1、加载并显示图像(Load and Display an Image)

1.1 学习目标

在本节中,我们预期达到以下学习目标:1) 加载一幅图像(采用imread方法);2)创建一个指定的OpenCV窗口(采用namedWindow方法);3)在OpenCV窗口中显示图像(采用imshow方法)。

1.2 源代码

#include “StdAfx.h”

#include <string>

#include <iostream>

#include <opencv2\core\core.hpp>

#include <opencv2\highgui\highgui.hpp>

 

using namespace cv;

using namespace std;

 

int main()

{

         string imageName = “lena.jpg”;

         //读入图像

         Mat img = imread(imageName, CV_LOAD_IMAGE_COLOR);

 

         //如果读入图像失败

         if (img.empty())

         {

                   cout<<”Could not open or find the image!”<<endl;

                   return -1;

         }

 

         //创建窗口

         namedWindow(“lena”, CV_WINDOW_AUTOSIZE);

 

         //显示图像

         imshow(“lena”, img);

 

         //等待按键,按键盘任意键返回

         waitKey();

 

         return 0;

}

1.3 源码详解

1.3.1 头文件

OpenCV有许多不同的模块,每个模块关心图像处理中不同的领域及方法(参见:OpenCV学习笔记(基于OpenCV 2.4)一:哈喽CV),在使用之前我们首先需要对相应的头文件进行包含,一般情况下我们都会用到的两个模块:

1)core section. 这里定义了OpenCV的一些基本的块(Blocks);

2)highgui module. 该模块包含了一些图像的输入输出操作(UI)。

另外,为了能够在控制台做输入输出,我们会包含iostream,而string是用于字符串的处理。接下来,为了防止OpenCV的数据结构或命名与其它库函数比如STL有冲突,我们引入命名空间cv,在有冲突的情况下可以用前缀cv::来指定具体使用哪个库(关于命名空间,我们会在下一讲做详细介绍)。

1.3.2 主函数


①Mat img = imread(imageName, CV_LOAD_IMAGE_COLOR);


imread的函数原型是:Mat imread( const string& filename, int flags=1 );

Mat是OpenCV里的一个数据结构,在这里我们定义一个Mat类型的变量img,用于保存读入的图像,在本文开始有写到,我们用imread函数来读取图像,第一个字段标识图像的文件名(包括扩展名),第二个字段用于指定读入图像的颜色和深度,它的取值可以有以下几种:

1) CV_LOAD_IMAGE_UNCHANGED (<0),以原始图像读取(包括alpha通道),

2) CV_LOAD_IMAGE_GRAYSCALE ( 0),以灰度图像读取

3) CV_LOAD_IMAGE_COLOR (>0),以RGB格式读取

这三点是在OpenCV的官方教程(opencv_tutorials.pdf)里摘录并翻译过来的,但是网上还有关于CV_LOAD_IMAGE_ANYDEPTH和CV_LOAD_IMAGE_ANYCOLOR的传说,而且查看OpenCV的源码可以发现,这些取值放在一个枚举(enum)类型中(opencv\build\include\opencv2\highgui\highgui_c.h),定义如下:

enum

{

/* 8bit, color or not */

    CV_LOAD_IMAGE_UNCHANGED  =-1,

/* 8bit, gray */

    CV_LOAD_IMAGE_GRAYSCALE  =0,

/* ?, color */

    CV_LOAD_IMAGE_COLOR      =1,

/* any depth, ? */

    CV_LOAD_IMAGE_ANYDEPTH   =2,

/* ?, any color */

    CV_LOAD_IMAGE_ANYCOLOR   =4

};

关于该枚举类型的详细信息,官方教程的写法跟官方发布的正式版代码描述的不相同,可能是我理解不够深入,或者两者是等价的,这点以后再找时间研究,但这并不影响我们对这一章节的学习。

NoteOpenCV提供了多种格式图像的支持,包括Windows bitmap(bmp),portable image formats (pbm, pgm,ppm) 以及 Sun raster (sr, ras)。关于其它的格式,有JPEG (jpeg, jpg, jpe),JPEG 2000,TIFF 文件 (tiff, tif),portable network graphics (png),还有openEXR格式,如果OpenCV是自己打包的话,读取这些格式需要有插件支持,如果是官方提供的库,则不需再添加插件。

 

在检查图像是否读取成功之后,我们需要显示读入的图像,因此,我们采用namedWindow函数来创建一个OpenCV窗口,该函数的定义如下:

CV_EXPORTS_W void namedWindow(const string& winname, int flags = WINDOW_AUTOSIZE);

为此,我们需要指定该窗口的名称(窗口标识符, window identifier)以及如何处理窗口大小。①窗口标识符需要唯一指定,如果已经存在一个该名字的窗口,则此函数不进行任何处理;②flags参数目前只支持CV_WINDOW_AUTOSIZE,在highgui_c.h中,OpenCV定义了CV_WINDOW_AUTOSIZE= 0×00000001,如果设置该参数,则表示显示的时候窗口自适应于需要显示的图像,而且不能修改窗口大小,如果不设置(省略此参数),可以通过代码进行修改;③如果将OpenCV用于Qt后端开发,该参数还支持其它值,具体可查看OpenCV开发文档,这里不再赘述。


②imshow(“lena”, img);


imshow用于在我们创建的窗口中显示需要显示的图像,其函数原型为:

void imshow(const string& winname, InputArray mat);

第一个参数winname是指窗口的名称,也就是窗口标识符,第二个参数mat就是我们要显示的图像了。正如我们在namedWindow函数中所描述的那样,如果namedWindow指定了参数CV_WINDOW_AUTOSIZE,则图像会按原始大小显示,否则图像会根据窗口大小进行缩放。


③waitKey();


这条语句表示等待用户键盘操作,waitKey函数的函数原型如下:

int waitKey(int delay = 0);

我们可以看到,该函数可包含一个整形参数,不设置参数的情况下,默认为0,也就是无限制等待。该整数表示需要等待用户操作的毫秒数。

2 加载、修改并保存图像(Load, Modify, and Save an Image)

2.1 学习目标

在这一章,我们将学习:1)加载一副图像(采用imread函数,同第一章);2)将一副图像从RGB格式转换成灰度图(grayscale,采用cvtColor函数);3)保存转换后的图像到磁盘上(采用imwrite函数)。

2.2 源代码

<pre lang=”cpp”>

#include “StdAfx.h”

#include <cv.h>

#include <highgui.h>

#include <string>

 

using namespace cv;

using namespace std;

 

int main()

{

         char* imageName = “lena.jpg”;

         Mat image = imread(imageName, 1);

 

         if (!image.data)

         {

                   cout<<”Could not open or find the image!”<<endl;

                   return -1;

         }

 

         Mat gray_image;

         String grayImageName = “lena_gray”;

 

         cvtColor(image,gray_image,CV_RGB2GRAY);//将RGB图像转换成灰度图像

         imwrite(“../../lena_gray.jpg”,gray_image);//保存图像

 

         namedWindow(imageName, CV_WINDOW_AUTOSIZE);//创建用于显示元图像窗口

         namedWindow(grayImageName,CV_WINDOW_AUTOSIZE);//创建用于显示转换后图像窗口

 

         imshow(imageName,image);

         imshow(“grayImageName”, gray_image);

 

         waitKey(0);

         return 0;

}

</pre>

2.3 源码详解

有了第一章的基础后,再来理解本章代码相对就很容易,在这一节,关于imread函数的使用则不再赘述,下面给cvtColor和imwrite来一个特写。


①cvtColor(image,gray_image,CV_RGB2GRAY);// 将RGB图像转换成灰度图像


cvtColor函数用于将图像从一个颜色空间转换到另一个颜色空间,其函数原型为:

void cvtColor( InputArray src, OutputArray dst, int code, int dstCn=0 );

参数src:是指需要转化的图像,可以是8位或16位等的无符号型或者是单精度浮点型(Single-Precision Floating-Point);

参数dst:与原始图像具有相同大小和深度的目标图像;

参数code:颜色空间转换代码;

参数dstCn:目标图像的通道数,如果该参数为0,则通道数可由src和code自动获得;

对于一个原图像或目标图像是RGB的转换,我们需要详细地指定通道的顺序(RGB or BGR)。我们注意到,OpenCV默认情况下的颜色格式一般是指RGB,但实际上却进行了一个反转变成BGR,因此对一个标准的24位图像来说,其第一个字节为8位的蓝色部分(Blue Component),其次是绿色,接着是红色,再然后就是第二个像素,同样以BGR的通道顺序排列。

常规的RGB通道的值的范围如下:

对于8位无符号精度图像(CV_8U Images),其范围是0~255

对于16位无符号精度图像(CV_16U Images),其范围是0~65535

对于32位单精度浮点型图像(CV_32F Images),其范围是0~1

在线性变换的情况下,我们可以不用考虑其通道的取值范围,但对于非线性变换(Non-Linear Transformation),一个RGB输入图像应该先做规格化处理(Normalized),以便得到一个合适的范围来获取正确的结果。比如对于一个RGB颜色空间到LUV颜色空间的变换,如果我们需要将一副8位图像转换到一副32位的浮点型精度图像而不进行任何缩放,也就是说,我们将一个从0~255的范围替换成0~1的范围,那么我们首先要将图像按比例缩小(Scale the Image Down):

img *= 1./255;

cvtColor(img, img, CV_BGR2Luv);

如果我们采用8位的图像进行转换,该过程中可能会有信息的丢失,尽管在一般的应用中,这种丢失并不明显(Noticeable),但我们强烈建议使用一个32位的图像或者在变换之前先转换成32位。

备注:关于参数code的跟多取值,可以参见OpenCV 2.4.0官方文档:cvtColor函数指南及使用方法


 

②imwrite(“../../lena_gray.jpg”,gray_image);// 保存图像


imwrite函数用于将图像保存到指定的文件,其函数原型为:

bool imwrite(const string& filename, InputArray image, const vector<int>& params=vector<int>())

参数filename:指代需要保存文件的名称

参数image:需要保存的图像

参数params:保存至指定格式图像格式时的参数设置

关于params参数的取值如下:

对JPEG图像,它表示图像质量(CV_IMWRITE_JPEG_QUALITY),取值从1~100,值越大质量越高,默认为95;

对PNG图像,它表示图像压缩率(CV_IMWRITE_PNG_COMPRESSION),取值从0~9,值越大压缩率越大压缩时间越长,默认值为3;

对PPM,PGM或PBM,它是一个二进制标识(CV_IMWRITE_PXM_BINARY),取值为0或1,默认为1。

有关于该函数及参数params的详细信息及应用可参见开发文档:imwrite函数指南及使用方法

    本站是提供个人知识管理的网络存储空间,所有内容均由用户发布,不代表本站观点。请注意甄别内容中的联系方式、诱导购买等信息,谨防诈骗。如发现有害或侵权内容,请点击一键举报。
    转藏 分享 献花(0

    0条评论

    发表

    请遵守用户 评论公约

    类似文章 更多