书名:数字图像处理与机器视觉——Visual C++与Matlab实现(第2版)
ISBN:978-7-115-34668-1
本书由人民邮电出版社发行数字版。版权所有,侵权必究。
您购买的人民邮电出版社电子书仅供您个人使用,未经授权,不得以任何方式复制和传播本书内容。
我们愿意相信读者具有这样的良知和觉悟,与我们共同保护知识产权。
如果购买者有侵权行为,我们可能对该用户实施包括但不限于关闭该帐号等维权措施,并可能追究法律责任。
• 著 张 铮 徐 超 任淑霞 韩海玲
责任编辑 张 涛
• 人民邮电出版社出版发行 北京市丰台区成寿寺路11号
邮编 100164 电子邮件 315@ptpress.com.cn
• 读者服务热线:(010)81055410
反盗版热线:(010)81055315
本书将理论知识、科学研究和工程实践有机结合起来,内容涉及数字图像处理和识别技术的方方面面,包括图像的点运算、几何变换、空域和频域滤波、小波变换、图像复原、彩色图像处理、形态学处理、图像分割、图像压缩以及图像特征提取等;同时对机器视觉进行了前导性的探究,重点介绍了3种目前在工程技术领域非常流行的分类技术——人工神经网络(ANN)、支持向量机(SVM)和AdaBoost,并在配套给出的识别案例中直击光学字符识别(OCR)、人脸识别和性别分类等热点问题。
全书结构紧凑,内容深入浅出,讲解图文并茂,适合于计算机、通信和自动化等相关专业的本科生、研究生,以及工作在图像处理和识别领域一线的广大工程技术人员阅读参考。
图像处理与机器视觉是当今计算机科学中的一个热门研究方向,应用广泛,发展前景乐观。近年来,伴随着人工智能、模式识别学科以及人机智能接口技术的飞速发展,机器视觉的研究正在不断升温——从日常生活中与人类息息相关的光学字符识别(OCR)和汽车自动驾驶,到医学应用中的病灶检测与分析,再到未来人机智能交互领域中的人脸识别、情感计算等,图像处理作为机器视觉研究中必不可少的图像预处理环节是读者需要掌握的首要技术。
和图像相关的东西往往容易引起计算机初学者的兴趣,笔者在读本科的时候就觉得能让计算机理解所“看”到的东西是一件非常神秘和令人兴奋的事情;但同时它的理论性较强,门槛较高,在各个高校中,这门课程大多也是作为计算机专业研究生的选修课程。要理解该领域的知识,读者需要具有一定的数学基础,除此之外还涉及信号处理、统计分析、模式识别和机器学习等专业领域知识,因此令很多人望而却步。
其实“难以理解”的关键在于缺乏必要的先序知识,造成了读者在相关知识上难以跨越的鸿沟。在撰写本书过程中,对于可能造成读者理解困难的地方,均尽可能地给出了必要的基本知识,深入浅出,尽量定性地去描述,对于那些并不一目了然的结论均给出了思路和解释,必要的还提供了证明,对于某些非常专业已经超过本书讨论范围的相关知识在最后给出了参考文献,供有兴趣的读者进一步学习和研究。
本书的宗旨是在向读者介绍知识的同时,培养读者的思维方法,使读者知其然还要知其所以然,并在解决实际问题中能有自己的想法。
本书在第一版的基础上,根据技术的发展和应用的需求进行改版,主要内容介绍如下。
第0~2章介绍了数字图像处理的基础知识和编程基础,使读者第一步能够建立起对于数字图像本质的正确认识,了解和掌握必要的术语和预备知识,并且熟悉本书自始至终需要使用的两大工具MATLAB和Visual C++,给出了功能强大的C++数字图像处理基类CImg及其派生类CImgProcess的框架。
第3~4章分别介绍了图像的灰度变换和几何变换。通过灰度变换可以有效改善图像的外观,并在一定程度上实现图像的灰度归一化;几何变换则主要应用在图像的几何归一化和图像校准当中。总体而言,这些内容大多作为图像的前期预处理工作的一部分,是图像处理中相对固定和程式化的内容。
第5~6章分别从空间域和频率域两个角度去考量图像增强的各个主要方面。图像增强作为数字图像处理中相对简单却最具艺术性的领域之一,可理解为根据特定的需要突出一幅图像中的某些信息,同时,削弱或去除某些不需要的信息的处理方法。其主要目的是使处理后的图像对某种特定的应用来说,比原始图像更适用。
第7章小波变换继第6章之后继续在频率域中研究图像。傅里叶变换一直是频率域图像处理的基石,它能用正余弦函数之和表示任何分析函数,而小波变换则基于一些有限宽度的基小波,这些小波不仅在频率上是变化的,而且具有有限的持续时间。比如对于一个乐谱,小波变换不仅能提供要演奏的音符,而且说明了何时演奏等细节信息,但是傅里叶变换只提供了音符,局部信息在变换中丢失。
第8章图像复原与图像增强相似,其目的也是改善图像质量。但是图像复原是试图利用退化过程的先验知识使已被退化的图像恢复本来面目,而图像增强是用某种试探的方式改善图像质量,以适应人眼的视觉与心理。引起图像退化的因素包括由光学系统、运动等造成的图像模糊,以及源自电路和光学因素的噪声等。图像复原是基于图像退化的数学模型,复原的方法也建立在比较严格的数学推导上。
第9章是本书中相对独立的一章,以介绍色彩模型之间的相互转换以及彩色图像处理方面的基本概念和基本方法为主。随着基于互联网的图像处理应用在不断增长,彩色图像处理已经成为一个重要领域。
第10章图像压缩旨在减少表示数字图像时需要的数据量。主要介绍了图像压缩的基本原理、DCT变换、预测编码、霍夫曼编解码,以及算术编码和游程编码等知识,并给出了有关JPEG和JPEG2000压缩标准的内容。
第11~13章(形态学图像处理、图像分割、特征提取)是从单纯图像处理向图像识别(机器视觉)过渡,这一阶段的特点是输入是图像,输出则是在识别意义上读者感兴趣的图像元素。形态学处理是提取图像元素的有力技术,它在表现和描述形状方面非常有用;分割过程则将一幅图像划分为组成部分或目标对象;研究特征提取则是要将前面提取出来的图像元素或目标对象表示为适合计算机后续处理的数值形式,最终形成能够直接供分类器使用的特征。
第14章在前面知识的基础之上,引出了机器视觉的前导性内容,给出了解决识别问题的一般思路。
最后3章(人工神经网络、支持向量机和AdaBoost)介绍了3种十分强大的分类技术,并在手写数字字符识别、人脸识别和性别分类这样的经典案例中进行讲解。
本书所有Visual C++实例的源代码和大部分Matlab实例的源代码均可在随书附赠的光盘中找到。为了尽可能满足所有读者的需要,同时提供了C++代码的Visual C++ 6.0版本和Visual Studio 2010/2012版本。虽然本书中的所有例子都已经在Windows XP、Windows 2003和Windows7下测试通过,但由于许多算法比较复杂且笔者水平局限,也有存在缺陷的可能,即便正确也很可能存在更加优化的算法或更加合理的程序结构,如发现任何上述问题,欢迎读者通过以下方式联系本书作者,以便做出改进。
邮箱:book95_editor@qq.com
腾讯微博:@Book95_Aaron
首先要感谢我的授业恩师——南开大学的白刚教授和天津大学的赵政教授,是他们引导我进入了图像处理与机器视觉的研究领域。同时,他们在我写作过程中的指点和教诲确保了本书的权威性和严谨性。
感谢我的好友王烨阳提供并调试了许多实例代码;感谢徐超、陈明、张阳、李广鹏、马惠来、马宏、卞长迪、郑琦、王命达、杜强、陈香凝、苑春苗、宁升、李鸿鹏、李明剑等参与了本书部分章节的编写和修改;感谢罗小科先生为本书制作了很多插图;感谢我的兄长张钊为本书提供了部分照片;还要感谢朱毅、丁宗尧、刘群忠、贾万宏等为本书的编写提出了很多的宝贵意见和建议。
最后感谢我的妻子马宏、儿子张垚淼及我的父母家人,没有你们的鼓励和支持就不会有我的这部作品。
编 著
图像是指能在人的视觉系统中产生视觉印象的客观对象,包括自然景物、拍摄到的图片、用数学方法描述的图形等。图像的要素有几何要素(刻画对象的轮廓、形状等)和非几何要素(刻画对象的颜色、材质等)。
本章中,主要讲解数字图像和数字图像处理的实质内容和一般步骤,以及一些后面会经常使用到的基本概念。
自然界中的图像都是模拟量,在计算机普遍应用之前,电视、电影、照相机等图像记录与传输设备都是使用模拟信号对图像进行处理。但是,计算机只能处理数字量,而不能直接处理模拟图像。所以要在使用计算机处理图像之前进行图像数字化。
简单地说,数字图像就是能够在计算机上显示和处理的图像,可根据其特性分为两大类——位图和矢量图。位图通常使用数字阵列来表示,常见格式有BMP、JPG、GIF等;矢量图由矢量数据库表示,接触最多的就是PNG图形。
提示
本书只涉及数字图像中位图图像的处理与识别,如无特别说明,后文提到的“图像”和“数字图像”都仅仅是指位图图像。一般而言,使用数字摄像机或数字照相机得到的图像都是位图图像。
将一幅图像视为一个二维函数f(x, y),其中x和y是空间坐标,而在x – y平面中的任意一对空间坐标(x, y)上的幅值f称为该点图像的灰度、亮度或强度。此时,如果f、x、y均为非负有限离散,则称该图像为数字图像(位图)。
一个大小为M×N数字图像是由M行N列的有限元素组成的,每个元素都有特定的位置和幅值,代表了其所在行列位置上的图像物理信息,如灰度和色彩等。这些元素称为图像元素或像素。
不论是CRT显示器还是LCD显示器,都是由许多点构成的,显示图像时这些点对应着图像的像素,称显示器为位映像设备。所谓位映像,就是一个二维的像素矩阵,而位图也就是采用位映像方法显示和存储的图像。当一幅数字图像被放大后就可以明显地看出图像是由很多方格形状的像素构成的,如图0.1所示。
图0.1 位图图像示例
根据每个像素所代表信息的不同,可将图像分为二值图像、灰度图像、RGB图像以及索引图像等。
每个像素只有黑、白两种颜色的图像称为二值图像。在二值图像中,像素只有0和1两种取值,一般用0来表示黑色,用1表示白色。
在二值图像中进一步加入许多介于黑色与白色之间的颜色深度,就构成了灰度图像。这类图像通常显示为从最暗黑色到最亮的白色的灰度,每种灰度(颜色深度)称为一个灰度级,通常用L表示。在灰度图像中,像素可以取0~L-1之间的整数值,根据保存灰度数值所使用的数据类型不同,可能有256种取值或者说2k种取值,当k=1时即退化为二值图像。
众所周知,自然界中几乎所有颜色都可以由红(Red, R)、绿(Green, G)、蓝(Blue, B)3种颜色组合而成,通常称它们为RGB三原色。计算机显示彩色图像时采用最多的就是RGB模型,对于每个像素,通过控制R、G、B三原色的合成比例决定该像素的最终显示颜色。
对于三原色RGB中的每一种颜色,可以像灰度图那样使用L个等级来表示含有这种颜色成分的多少。例如对于含有256个等级的红色,0表示不含红色成分,255表示含有100%的红色成分。同样,绿色和蓝色也可以划分为256个等级。这样每种原色可以用8位二进制数据表示,于是3原色总共需要24位二进制数,这样能够表示出的颜色种类数目为256×256××256=224,大约有1600万种,已经远远超过普通人所能分辨出的颜色数目。
RGB颜色代码可以使用十六进制数减少书写长度,按照两位一组的方式依次书写R、G、B三种颜色的级别。例如:0xFF0000代表纯红色,0x00FF00代表纯绿色,而0x00FFFF是青色(这是绿色和蓝色的加和)。当RGB三种颜色的浓度一致时,所表示的颜色就退化为灰度,比如0 x808080就是50%的灰色,0x000000为黑色,而0xFFFFFF为白色。常见颜色的RGB组合值如表0.1所示。
表0.1 常见颜色的RGB组合值
颜 色 |
R |
G |
B |
---|---|---|---|
红(0xFF0000) |
255 |
0 |
0 |
蓝(0x00FF00) |
0 |
255 |
0 |
绿(0x0000FF) |
0 |
0 |
255 |
黄(0xFFFF00) |
255 |
255 |
0 |
紫(0xFF00FF) |
255 |
0 |
255 |
青(0x00FFFF) |
0 |
255 |
255 |
白(0xFFFFFF) |
255 |
255 |
255 |
黑(0x000000) |
0 |
0 |
0 |
灰(0x808080) |
128 |
128 |
128 |
未经压缩的原始BMP文件就是使用RGB标准给出的3个数值来存储图像数据的,称为RGB图像。在RGB图像中每个像素都是用24位二进制数表示,故也称为24位真彩色图像。
如果对每个像素都直接使用24位二进制数表示,图像文件的体积将变得十分庞大。来看一个例子,对一个长、宽各为200像素,颜色数为16的彩色图像,每个像素都用RGB三个分量表示。这样每个像素由3个字节表示,整个图像就是200×200×3=120kB。这种完全未经压缩的表示方式,浪费了大量的存储空间,下面简单介绍另一种更节省空间的存储方式:索引图像。
同样还是对200×200像素的16色图像,由于这张图片中最多只有16种颜色,那么可以用一张颜色表(16×3的二维数组)保存这16种颜色对应的RGB值,在表示图像的矩阵中使用那16种颜色在颜色表中的索引(偏移量)作为数据写入相应的行列位置。例如,颜色表中第3个元素为0xAA1111,那么在图像中所有颜色为0xAA1111的像素均可以由3-1=2表示(颜色表索引下标从0开始)。这样一来,每一个像素所需要使用的二进制数就仅仅为4位(0.5字节),从而整个图像只需要200×200×0.5=20kB就可以存储,而不会影响显示质量。
上文所指的颜色表就是常说的调色板(Palette),另一种说法叫作颜色查找表(Look Up Table,LUT)。Windows位图中应用到了调色板技术。其实不仅是Windows位图,许多其他的图像文件格式比如PCX、TIF、GIF都应用了这种技术。
在实际应用中,调色板中通常只有少于256种的颜色。在使用许多图像编辑工具生成或者编辑GIF文件的时候,常常会提示用户选择文件包含的颜色数目。当选择较低的颜色数目时,将会有效地降低图像文件的体积,但也会一定程度上降低图像的质量。
使用调色板技术可以减小图像文件体积的条件是图像的像素数目相对较多,而颜色种类相对较少。如果一个图像中用到了全部的24位真彩色,对其使用颜色查找表技术是完全没有意义的,单纯从颜色角度对其进行压缩是不可能的。
实际上,0.1.1小节中对于数字图像f(x, y)的定义仅适用于最为一般的情况,即静态的灰度图像。更严格地说,数字图像可以是2个变量(对于静止图像,Static Image)或3个变量(对于动态画面,Video Sequence)的离散函数。在静态图像的情况下是f (x, y),而如果是动态画面,则还需要时间参数t,即f (x, y, t)。函数值可能是一个数值(对于灰度图像),也可能是一个向量(对于彩色图像)。
提示
静态的灰度图像是本书研究的主要对象,对于函数值为向量的情况会在第9章彩色图像处理中阐述。
图像处理是一个涉及诸多研究领域的交叉学科,下面就从不同的角度来审视数字图像。
(1)从线性代数和矩阵论的角度,数字图像就是一个由图像信息组成的二维矩阵,矩阵的每个元素代表对应位置上的图像亮度和/或色彩信息。当然,这个二维矩阵在数据表示和存储上可能不是二维的,这是因为每个单位位置的图像信息可能需要不只一个数值来表示,这样可能需要一个三维矩阵来对其进行表示(参见1.2节关于Matlab中RGB图像表示的介绍)。
(2)由于随机变化和噪声的原因,图像在本质上是统计性的。因而有时将图像函数作为随机过程的实现来观察其存在的优越性。这时有关图像信息量和冗余的问题可以用概率分布和相关函数来描述和考虑。例如,如果知道概率分布,可以用熵(Entropy)H①来度量图像的信息量,这是信息论中一个重要的思想。
(3)从线性系统的角度考虑,图像及其处理也可以表示为用狄拉克冲激公式表达的点展开函数的叠加,在使用这种方式对图像进行表示时,可以采用成熟的线性系统理论研究。在大多数时候,都考虑使用线性系统近似的方式对图像进行近似处理以简化算法。虽然实际的图像并不是线性的,但是图像坐标和图像函数的取值都是有限的和非连续的。
为了表述像素之间的相对和绝对位置,通常还需要对像素的位置进行坐标约定。本书中所使用的坐标约定如图0.2所示。但在MATLAB中坐标的约定会有变化,具体请参见1.1.5小节。
图0.2 数字图像的坐标约定
在这之后,一幅物理图像就被转化成了数字矩阵,从而成为计算机能够处理的对象了。数字图像f的矩阵表示如下所示。
(0-1)
有时也可以使用传统矩阵表示法来表示数字图像和像素,如下式所示。
(0-2)
其中行列(M行N列)必须为正整数,而离散灰度级数目L一般为2的k次幂,k为整数(因为使用二进制整数值表示灰度值),图像的动态范围为[0, L-1],那么图像存储所需的比特数为b = M×N×k。注意到在矩阵f(y, x)中,一般习惯于先行下标,后列下标的表示方法,因此这里先是纵坐标y(对应行),然后才是横坐标x(对应列)。
而有些图像矩阵中,很多像素的值都是相同的。例如在一个纯黑背景上使用不同灰度勾勒的图像,大多数像素的值都会是0。这种矩阵称为稀疏矩阵(Sparse Matrix),可以通过简单描述非零元素的值和位置来代替,大量地写入0元素。这时存储图像需要的比特数可能会大大减少。
图像的空间分辨率是指图像中每单位长度所包含的像素或点的数目,常以像素/英寸(pixels per inch, ppi)为单位来表示。如72ppi表示图像中每英寸包含72个像素或点。分辨率越高,图像将越清晰,图像文件所需的磁盘空间也越大,编辑和处理所需的时间也越长。
像素越小,单位长度所包含的像素数据就越多,分辨率也就越高,但同样物理大小范围内所对应图像的尺寸也会越大,存储图像所需要的字节数也越多。因而,在图像的放大缩小算法中,放大就是对图像的过采样,缩小是对图像的欠采样,这些会在4.5节图像缩放中进一步介绍。
一般在没有必要对涉及像素的物理分辨率进行实际度量时,通常会称一幅大小为M×N的数字图像的空间分辨率为M×N像素。
图0.3给出了同一幅图像在不同的空间分辨率下呈现出的不同效果。当高分辨率下的图像以低分辨率表示时,在同等的显示或者打印输出条件下,图像的尺寸变小,细节变得不明显;而当将低分辨率下的图像放大时,则会导致图像的细节仍然模糊,只是尺寸变大。这是因为缩小的图像已经丢失了大量的信息,在放大图像时只能通过复制行列的插值的方法来确定新增像素的取值。
图0.3 图像的空间分辨率——一幅分辨率为1024×1024的图像逐次减少至32×32的分辨率
在数字图像处理中,灰度级分辨率又叫色阶,是指图像中可分辨的灰度级数目,即前文提到的灰度级数目L,它与存储灰度级别所使用的数据类型有关。由于灰度级度量的是投射到传感器上光辐射值的强度,所以灰度级分辨率也叫辐射计量分辨率。
随着图像的灰度级分辨率逐渐降低,图像中包含的颜色数目变少,从而在颜色的角度造成图像信息受损,同样使图像细节表达受到了一定的影响,如图0.4所示。
图0.4 图像的灰度级分辨率——分别具有256、32、16、8、4和2个灰度级的一幅图像
图像处理、图像分析和图像识别是认知科学与计算机科学中的一个令人兴奋的活跃分支。从1970年这个领域经历了人们对其兴趣的爆炸性增长以来,到20世纪末逐渐步入成熟。其中遥感、技术诊断、智能车自主导航、医学平面和立体成像以及自动监视领域是发展最快的一些方向。这种进展最集中地体现在市场上多种应用这类技术的产品的纷纷涌现。事实上,从数字图像处理到数字图像分析,再发展到最前沿的图像识别技术,其核心都是对数字图像中所含有的信息的提取及与其相关的各种辅助过程。
数字图像处理(Digital Image Processing)就是指使用电子计算机对量化的数字图像进行处理,具体地说就是通过对图像进行各种加工来改善图像的外观,是对图像的修改和增强。
图像处理的输入是从传感器或其他来源获取的原始的数字图像,输出是经过处理后的输出图像。处理的目的可能是使输出图像具有更好的效果,以便于人的观察;也可能是为图像分析和识别做准备,此时的图像处理是作为一种预处理步骤,输出图像将进一步供其他图像进行分析、识别算法。
数字图像分析(Digital Image Analyzing)是指对图像中感兴趣的目标进行检测和测量,以获得客观的信息。数字图像分析通常是指将一幅图像转化为另一种非图像的抽象形式,例如图像中某物体与测量者的距离、目标对象的计数或其尺寸等。这一概念的外延包括边缘检测和图像分割、特征提取以及几何测量与计数等。
图像分析的输入是经过处理的数字图像,其输出通常不再是数字图像,而是一系列与目标相关的图像特征(目标的描述),如目标的长度、颜色、曲率和个数等。
数字图像识别(Digital Image Recognition)主要是研究图像中各目标的性质和相互关系,识别出目标对象的类别,从而理解图像的含义。这往往囊括了使用数字图像处理技术的很多应用项目,例如光学字符识别(OCR)、产品质量检验、人脸识别、自动驾驶、医学图像和地貌图像的自动判读理解等。
图像识别是图像分析的延伸,它根据从图像分析中得到的相关描述(特征)对目标进行归类,输出使用者感兴趣的目标类别标号信息(符号)。
总而言之,从图像处理到图像分析再到图像识别这个过程,是一个将所含信息抽象化,尝试降低信息熵,提炼有效数据的过程,如图0.5所示。
图0.5 数字图像处理、分析和识别的关系
从信息论的角度上说,图像应当是物体所含信息的一个概括,而数字图像处理侧重于将这些概括的信息进行变换,例如升高或降低熵值,数字图像分析则是将这些信息抽取出来以供其他过程调用。当然,在不太严格时,数字图像处理也可以兼指图像处理和分析。
读者或许也听过另一个概念,计算机图形学(Computer Graphics)。此概念与数字图像分析大致相反,它是一个对由概念或数学表述的物体图像进行处理和显示的过程。
机器视觉(Machine Vision),又称计算机视觉(Computer Vision)。它是将数字图像处理和数字图像分析、图像识别结合起来,试图开发出一种能与人脑的部分机能比拟,能够理解自然景物和环境的系统,在机器人领域中为机器人提供类似人类视觉的功能。计算机视觉是数字成像领域的尖端方向,具有最综合的内容和最广泛的涵盖面。
提示
后文中,如无特别说明,文章通常使用广义的图像处理概念,即用数字图像处理这个词涵盖上文所提到的图像处理和数字图像分析;而对于图像识别和机器视觉的概念常常不加区分,尽管严格地说识别只对应于高级视觉的范畴。
如今,数字图像处理与机器视觉的应用越来越广泛,已经渗透到国家安全、航空航天、工业控制、医疗保健等各个领域乃至人们的日常生活和娱乐当中,在国民经济中发挥着举足轻重的作用。
一些典型的应用如表0.2所示。
表0.2 图像处理与识别的典型应用
相关领域 |
典型应用 |
---|---|
安全监控 |
指纹验证、基于人脸识别的门禁系统 |
工业控制 |
产品无损检测、商品自动分类 |
医疗保健 |
X光照片增强、CT、核磁共振、病灶自动检测 |
生活娱乐 |
基于表情识别的笑脸自动检测、汽车自动驾驶、手写字符识别 |
下面结合两个典型的应用来说明。
图0.6中的两幅图片,图0.6(a)是一幅直接拍摄未经处理的X光照片,对比度较低,图像细节难以辨识;图0.6(b)中呈现了图0.6(a)经过简单的增强处理后的效果,图像较为清晰,可以有效地指导诊断和治疗。从中读者应该可以看出图像处理技术在辅助医学成像上的重要作用。
图0.6 图像处理前后的效果对比
著名的自动驾驶系统ALVINN是人工神经网络(关于人工神经网络的介绍详见第15章)的一个典型的应用。该系统使用一个经过训练的神经网络以正常速度在高速公路上驾驶汽车。如图0.7(b)所示,ALVINN具有一个典型的3层结构,网络的输入层共有30×32个单元,对应于一个30×32的像素点阵,是由一个安装在车辆上的前向摄像机获取的图像经过重采样得到的。输出层共有30个单元,输出情况指出了车辆行进的方向。
图0.7 学习汽车自动驾驶的ALVINN系统
在训练阶段,ALVINN以人类驾驶时摄像机所捕获的前方交通状况作为输入,以人类通过操作方向盘给出的前进方向作为目标输出,整个训练过程大约5分钟;在测试阶段,ALVINN用学习到的网络在高速公路上以70英里的时速成功地驾驶了90英里。
注
(a)为车内的摄像头和前方的实际情况;(b)为ALVINN的网络结构,摄像头捕获图像的30×32的重采样图像被作为网络的输入,对应于960个输入层单元,这些输入又连接至4个隐藏单元,再连接到30个输出单元,输出为一个30维向量,相当于把整个方向盘的控制范围分成30份,每个输出单元对应一个特定的驾驶方向,决策结果为输出值最大的单元对应的行驶方向。
数字图像是由一组具有一定的空间位置关系的像素组成的,因而具有一些度量和拓扑性质。理解像素间的关系是学习图像处理的必要准备,这主要包括相邻像素,邻接性、连通性,区域、边界的概念,以及今后要用到的一些常见距离度量方法。另外0.3.3小节还将简单介绍几种基本的图像操作。
为理解这些概念,需要首先了解相邻像素的概念。依据标准的不同,可以关注像素P的4邻域和8邻域,如图0.8所示。
图0.8 P的各种邻域
定义V是用于决定邻接性的灰度值集合,它是一种相似性的度量,用于确定所需判断邻接性的像素之间的相似程度。比如在二值图像中,如果认为只有灰度值为1的像素是相似的,则即V={1},当然相似性的规定具有主观标准,因此也可以认为V={0,1},此时邻接性完全由位置决定;而对于灰度图像,这个集合中则很可能包含更多的元素。此外,定义对角邻域ND(P)为8-邻域中不属于4-邻域的部分(见图0.8(c)),那么有如下的规定。
(1)4邻接(4-Neighbor):如果 Q∈N4(P),则称具有V中数值的两个像素P和Q是4邻接的。
(2)8邻接(8-Neighbor):如果 Q∈N8(P),则称具有V中数值的两个像素P和Q是8邻接的。
举例来说,图0.9(a)、图0.9(b)分别是像素和Q、Q1、Q2的4邻接和8邻接示意图。而对于两个图像子集S1和S2,如果S1中的某些像素和S2中的某些像素相邻,则称这两个子集是邻接的。
图0.9 邻接示意图
为了定义像素的连通性,首先需要定义像素P到像素Q的通路(Path)。这也是建立在邻接性的基础上的。
像素P到像素Q的通路(Path)指的是一个特定的像素序列(x0, y0), (x1, y1), …, (xn, yn),其中(x0, y0) = (xp, yp),(xn, yn) = (xq, yq)。并且像素(xi, yi) 和(xi-1, yi-1) 在满足1≤i≤n时是邻接的。在上面的定义中,n是通路的长度,若(x0, y0) = (xn, yn),则这条通路是闭合通路。相对应于邻接的概念,在这里有4通路和8通路。这个定义和图论中的通路定义是基本相同的,只是由于邻接概念的加入而变得更加复杂。
像素的连通性(Contiguous):令S代表一幅图像中的像素子集,如果在S中全部像素之间存在一个通路,则可以称2个像素P和Q在S中是连通的。此外,对于S中的任何像素P,S中连通到该像素的像素集叫作S的连通分量。如果S中仅有一个连通分量,则集合S叫做连通集。
区域的定义是建立在连通集的基础上的。令R是图像中的一个像素子集,如果R同时是连通集,则称R为一个区域(Region)。
边界(Boundary)的概念是相对于区域而言的。一个区域的边界(或边缘、轮廓)是区域中所有有一个或多个不在区域R中的邻接像素的像素所组成的集合。显然,如果区域R是整幅图像,那么边界就由图像的首行、首列、末行和末列定义。因而,通常情况下,区域指一幅图像的子集,并包括区域的边缘。而区域的边缘(Edge)由具有某些导数值的像素组成,是一个像素及其直接邻域的局部性质,是一个有大小和方向属性的矢量。
边界和边缘是不同的。边界是和区域有关的全局概念,而边缘表示图像函数的局部性质。
基于上一小节提到的相关知识,来理解距离度量的概念。假设对于像素P(xp, yp)、Q(xq, yq)、R(xr, yr)而言,有函数D满足如下3个条件,则函数D可被称为距离函数或度量。
① D(P, Q) ≥0,当且仅当P = Q时,有D(P, Q) = 0
② D(P, Q) = D(Q, P)
③ D(P, Q) ≤D(P, R) + D(R, Q)
常见的几种距离函数如下所示。 ① 欧氏距离
(0-3)
即距离等于r的像素形成的以P为圆心的圆。
② D4距离(街区距离)
(0-4)
即距离等于r的像素形成的以P为中心的菱形。
③ D8距离(棋盘距离)
(0-5)
即距离等于r的像素形成的以P为中心的方形。
距离度量参数可以用于对图像特征进行比较和分类或者进行某些像素级操作。最常用的距离度量是欧氏距离,然而在形态学中,也可能使用街区距离和棋盘距离。
在后续章节中,将涉及各种各样的图像操作,这里就几种最为典型和常用的图像操作着重说明。按照处理图像的数量分类,可以分为对单幅图像操作(如滤波)和对多幅图像操作(如求和、求差和逻辑运算等);按照参与操作的像素范围的不同,可以分为点运算和邻域运算;而根据操作的数学性质,又可以分为线性操作和非线性操作。
点运算指的是对图像中的每一个像素逐个进行同样的灰度变换运算。设r和s分别是输入图像f(x, y)和输出图像g(x, y)在任一点(x, y)的灰度值,则点运算可以使用下式定义。
s = T(r)
(0-6)
而如果将点运算扩展,对图像中每一个小范围(邻域)内的像素进行灰度变换运算,即称为邻域运算或邻域滤波。这可以使用下式定义。
g(x, y) = T[f(x, y)]
(0-7)
文章将分别在第3章和第5章介绍点运算和邻域运算。
令H是一种算子,其输入输出都是图像。若对于任意两幅(或两组)图像F1和F2及任意两个标量a和b都有如下关系成立,
H(aF1+bF1)=aH(F1)+bH(F2)
(0-8)
则称H为线性算子。也即对两幅图像的线性组合应用该算子与分别应用该算子后的图像在进行同样的线性组合所得到的结果相同,也即算子H满足线性性质。同样的,不符合上述定义的算子即为非线性算子,对应的是非线性图像操作。举例来说,滤波中的平均平滑、高斯平滑、梯度锐化等都是线性运算,而中值滤波(详见第5章空间域图像增强)则是非线性的。
线性操作由于其稳定性的特点而在图像处理中占有非常重要的地位。 尽管非线性算子常常也能够提供较好的性能,但它的不可预测性使其在一些如军事图像处理和医学图像处理等严格的应用领域中难以获得广泛的应用。
① 熵(Entropy):熵是信息论中用于度量信息量的一个概念。一个系统越是有序,信息熵就越低;反之,一个系统越是混乱,信息熵就越高。所以,信息熵也可以说是系统有序化程度的一个度量。
MATLAB是Mathworks公司开发的一款工程数学计算软件。不同于C++、Java、FORTRAN等高级编程语言,它们是对机器行为进行描述,而MATLAB是对数学操作进行更直接的描述。MATLAB图像处理工具箱(Image Processing Toolbox,IPT)封装了一系列针对不同图像处理需求的标准算法,它们都是通过直接或间接地调用MATLAB中的矩阵运算和数值运算函数来完成图像处理任务的。
本节将介绍一些MATLAB R2011a中与图像处理密切相关的数据结构及基本操作,如基本文件操作、变量使用、程序流程控制、打开和关闭图像以及图像格式转换和存储方式等。这些都是后续将要学习的图像处理算法的基础。
图1.1所示是运行于32-bit Windows操作系统上的MATLAB R2011a截图。软件主界面由3个子窗口组成,左上为当前工作目录的文件列表,右上方为当前工作区的变量,右下为当前和最近会话的命令历史记录,而中间的主窗口则是命令输入和结果输出区,>>为提示符。
图1.1 MATLAB界面
可以在>>提示符后面输入简单的算式(例如5*3-2)或带有函数的算式(例如sin(pi/2)*sqrt(3)/2)并回车,会提示ans=0.8660,这就是MATLAB最基本的计算功能。
这样的输入形式实际上是MATLAB命令,而如果在每行命令的结尾输入半角分号,命令窗口不会立即显示命令执行的结果,而会将结果保存在工作区中。例如下面的命令。
>> res = sin(pi/2)*sqrt(3)/2; % 将计算结果保存至变量res当中
此时,变量res已经存在于工作区中,但是命令窗口不会回显它的值。
另外,也可以在文件菜单下执行“New”→“M-Files”命令来创建一个新的MATLAB文件,在里面输入命令(以半角分号结尾),从而得到一个MATLAB程序。在MATLAB程序中,使用“%”表示注释,其用法和C/C++中的“//”注释符类似。
MATLAB允许在同一行中输入多条语句,之间用分号隔开。同时,MATLAB还允许将同一条语句分割在多行中书写以方便较长语句的阅读,方法是在行末使用3个半角圆点。例如下面的语句。
>> z = 2 .* x +exp( x .^ 2 + y .^ 2 - sqrt(1 - log(x) - log (y) ) )...
- y .* sqrt(t) - x .* sqrt(t);
默认情况下,MATLAB可以自动搜索到当前目录(Current Directory)和MATLAB的路径变量path中所含有目录下面的文件。对处在这些位置可由MATLAB执行的文件,直接在命令窗口中输入文件名即可运行。如果需要直接运行其他目录下的文件,就要使用addpath和genpath等命令向路径列表中添加路径。
向path变量中加入指定的目录路径,其原型如下。
addpath('dir','dir2','dir3' ...'-flag')
该函数可以接受任意数目的参数。
参数说明:
表1.1 addpath函数中flag参数的取值
合法取值 |
含 义 |
---|---|
0或者begin |
这些路径将被添加到搜索列表的最前面,这些目录中含有的文件将先于原列表中的同名文件被找到从而执行。这往往用于需要修改系统某一命令行为的场合 |
1或者end |
这些路径将被添加到搜索列表的最后面,原列表中的同名文件将先于这些目录中含有的文件被找到从而执行。这样可以避免用户M文件覆盖系统M文件的功能 |
省略 |
与0或begin相同 |
可以在使用addpath函数前后查看path变量的内容,以确定添加成功。
生成包含指定目录下所有子目录的路径变量,其原型如下。
p = genpath('directory');
参数说明:
返回值:
>> addpath(genpath('F:\doctor research\Matlab Work\FaceRec')) %注意这里要使用绝对路径
也可以在运行M文件时使用完整的文件路径,从而避免同名文件的冲突问题,或是从资源管理器中将M文件拖动到MATLAB的命令窗口中直接运行。
如果需要编辑某个M文件,可以使用open命令和edit命令,它们的调用形式如下。
open filename
edit filename
参数filename为需要打开的文件名。edit命令只能编辑M文件,而open命令可以使用Windows默认操作打开一系列其他类型的文件。
在MATLAB中,有以下4种方法获取软件的在线帮助。
help命令可以用于查看MATLAB系统或M文件中内置的在线帮助信息。命令格式如下。
help command-name
command-name为需要查看在线帮助的命令或函数的名称。例如,想要查看doc命令的使用方法,可在命令提示符下直接输入“help doc”,如图1.2所示。
图1.2 help命令界面
doc命令可以用于查看命令或函数的HTML帮助,这种帮助信息可以在帮助浏览器窗口中打开。其调用格式如下。
doc function-name
doc命令可提供比help命令更多的信息,还可能包含图片或视频等的多媒体例子,对图像处理工具箱中的函数更是如此。
图1.3所示为在命令行中输入doc imhist命令后出现的帮助示例界面。
图1.3 doc命令结果
当忘记命令或函数的完整拼写时,可以使用lookfor命令查找当前目录和自动搜索列表下所有名字中含有所查内容的函数或命令。其调用格式如下。
lookfor keyword
keyword为指定要查找的关键字。此命令可以给出一个包含指定字符串的函数列表,其中的函数名称为超链接,点击即可查看该函数的在线帮助,如图1.4所示。
图1.4 lookfor命令界面
在MATLAB R2011a的主界面中按键盘的“F1”键,弹出如图1.5所示的对话框。
图1.5 F1命令界面
单击左下角的“Open Help Browser”链接打开如图1.6所示的帮助浏览器窗口。在左上角的编辑框中输入感兴趣的关键字,单击回车进行查询,右侧会出现相应的帮助信息。
在后面的章节中,如果忘记了曾经提到的命令的含义,建议首先通过在线帮助寻求相关信息,以此增强自学能力。
图1.6 Help浏览器界面
变量可以保存中间结果和输出数值等信息,MATLAB中变量的命名规则和C/C++等常见的编程语言很类似,同时也对大小写是敏感的。另外,MATLAB中的变量不需要先行定义,但在使用前一定要赋值。
可以通过赋值语句来给变量赋值。赋值操作使用等号“=”,例如a=5是给a(注意不是A)这个变量赋值5,如果未定义变量a,会自动定义。在MATLAB中,变量定义时不需要显式地指明类型,Matlab会根据等号右边的值自动确定变量的类型。默认的对数字的存储类型为double型或double型数组,而字符的存储类型为char型,字符串的存储类型为char型数组。
对字符串赋值时,需要用半角单引号“'”括起来(注意不是双引号,也不是任何的全角字符),例如msg='Hello world'。
MATLAB有某些内部变量名和保留字,如表1.2所示。变量命名时不要与它们重名。
表1.2 MATLAB内部变量列表
特殊变量 |
说 明 |
---|---|
ans |
默认的结果输出变量 |
pi |
圆周率 |
Inf或inf |
无穷大值,如1/0 |
i和j |
单位虚数值 |
eps |
浮点运算的相对精度 |
realmax |
最大的正浮点数 |
realmin |
最小的正浮点数 |
NaN或nan |
不定量,如0/0 |
nargin |
函数输入参数个数 |
nargout |
函数输出参数个数 |
lasterr |
最近的错误信息 |
lastwarning |
最近的警告信息 |
computer |
计算机类型 |
version |
MATLAB版本 |
使用who和whos命令可以查看所有当前工作区中变量的情况。使用clear或clear all命令可以清除工作区中所有的变量定义,也可以在clear后面加上变量名,清除特定的变量定义。另外,clc命令可以用来清屏,所以这两个命令常常用在M文件的开头用来构造一个干净的工作区。
>> a = 1; %定义一个数值型变量a
>> str = 'hello'; %定义一个字符串变量(字符数组)
>> v = [3 2 1] %定义一个数值型向量
v =
3 2 1
>> whos
Name Size Bytes Class Attributes
a 1x1 8 double
str 1x5 10 char
v 1x3 24 double
>> clear all
>> whos
>>
MATLAB中的数据类型列表如表1.3所示。
表1.3 MATLAB数据类型
数据类型 |
说 明 |
---|---|
double |
MATLAB中最常见也是默认的数据类型,双精度方式存储的浮点数。有效范围是-10308到10308。这同时也是MATLAB所能直接给出的最大数值范围。此种类型占用的内存空间为8字节 |
uint8 |
8位无符号整数,范围是0到255。此种类型占用的内存空间为1字节 |
uint16 |
16位无符号整数,范围是0到65 535。此种类型占用的内存空间为2字节 |
uint32 |
32位无符号整数,范围是0到4 294 967 295。此种类型占用的内存空间为4字节 |
uint64 |
64位无符号整数,范围是0到18 446 744 073 709 551 615。此种类型占用的内存空间为8字节 |
int8 |
8位有符号整数,范围是-128到127。此种类型占用的内存空间为1字节 |
int16 |
16位有符号整数,范围是-32 768到32 767。此种类型占用的内存空间为2字节 |
int32 |
32位有符号整数,范围是-2 147 483 648到2 147 483 647。此种类型占用的内存空间为4字节 |
int64 |
64位有符号整数,范围是-9 223 372 036 854 775 808到9 223 372 036 854 775 807。此种类型占用的内存空间为8字节 |
single |
单精度浮点数,范围是-1038到1038。此种类型占用的内存空间为4字节 |
char |
字符型变量,占用空间为2字节 |
logical |
布尔型变量,占用空间为1字节。此种类型的转换函数也可以使用boolean,与logical等效 |
默认情况下,MATLAB将变量存储为双精度浮点数(double),而MATLAB中的很多函数也只接受这种类型的数据。然而,图像处理操作中经常使用到uint8等类型的数据,这就需要执行数据类型的强制转换操作。这种操作很简单,调用格式统一如下。
Destination_Var = type_name(Source_Var)
其中,type_name即数据的存储类型,Destination_Var和Source_Var分别为目标变量和原始变量。例如下面的命令将double原始变量a转换为uint8变量b。
>> a = 1;
>> b = uint8(a);
save命令可以将当前工作区的变量以二进制的方式保存到扩展名为MAT的文件中;load命令可以读出这样的文件。它们的调用格式如下。
save filename arg1 arg2 arg3, …
load filename arg1 arg2 arg3, …
下面的命令将price、age和number三个变量保存到文件MyData.mat中。
>> save('MyData.mat', 'price', 'age', 'number')
提示
也可以不指定变量名,从而将当前工作区中所有的变量一起储存到mat文件或将文件中保存的所有变量一起读入工作区,这个批量保存和读取功能在运行非常耗时的程序时显得十分有用——由于MATLAB执行效率并不高(和Visual C++相比),所以对于一个计算量很大的程序而言,运行几个小时并不稀奇。这时,可以根据需要在希望中断程序时保存程序的所有上下文变量,以备之后随时从中断点开始执行。
在MATLAB中定义矩阵很简单。可以使用半角分号分隔行与行,使用半角逗号(或者空格)分隔列与列来直接定义矩阵,比如下面的命令就定义了一个3行3列的二维矩阵A。
A=[1, 2, 3; 4, 5, 6; 7, 8, 9]
A =
1 2 3
4 5 6
7 8 9
还有另一种方式可以生成行向量,[begin:inc:end]会生成从begin开始到end结束,增量为incre的一系列数字组成的向量。如v=[2:1:10]表示生成从2到10的间隔为1的向量(一维矩阵),即:
v= [2:1:10]
v=
2 3 4 5 6 7 8 9 10
如果间隔为1,也可以忽略中间的参数,直接输入I=[2:10]即可。
除直接定义外,可以通过函数生成特定的矩阵,比如eye(n)生成N阶单位阵,zeros(n)生成N阶每个元素均为0的方阵,magic(n)生成N阶幻方阵等。常见的用于生成矩阵的函数列表如表1.4所示。
表1.4 生成矩阵的函数
函数名称 |
用 途 |
---|---|
eye |
产生单位矩阵 |
zeros |
产生全部元素为0的矩阵 |
ones |
产生全部元素为1的矩阵 |
true |
产生全部元素为真的逻辑矩阵 |
false |
产生全部元素为假的逻辑矩阵 |
rand |
产生均匀分布随机矩阵 |
randn |
产生正态分布随机矩阵 |
randperm |
产生随机排列 |
Linspace |
产生线性等分的矩阵 |
Logspace |
产生对数等分向量 |
Company |
产生伴随矩阵 |
Hadamarb |
产生Hadamarb矩阵 |
Magic |
产生幻方矩阵 |
Hilb |
产生Hilbert矩阵 |
Invhilb |
产生逆Hilbert矩阵 |
size函数可以获得指定数组某一维的大小,可以用来查看图像的高度和宽度以及动态图像的帧数等。其调用方法如下。
size(A,dim)
例如,对于一个3行5列的矩阵B,有size(B, 1)=3,size(B, 2) = 5,size(B) = [3 5]。
函数ndims可以查看数组的维数。调用方式如下。
ndims(A)
其中A为需要查看维数的数组。
访问矩阵的一个元素的方式是在矩阵名字的后面注明行列序号,例如访问A的第3行第2列元素就是A(3,2)。提取矩阵的一整行元素,如要提出A的第2行使用A(2,:),如果是第2列则是A(:,2);而A(:)表示将矩阵按列存储得到一个长列向量。示例如下。
>> A=[1, 2, 3; 4, 5, 6; 7, 8, 9] ; %定义矩阵A
>> A(1, :) %提取第1行
ans =
1 2 3
>> A(:, 3) %提取第3列
ans =
3
6
9
>> A(:)'
ans =
1 4 7 2 5 8 3 6 9
注意
MATLAB中的矩阵下标是从1开始的。对图像矩阵也是一样,所以一个m×n的矩阵实际的下标范围为[1:m]和[1:n]。
对于矩阵A,提取矩阵元素或子块的方法如表1.5所示。
表1.5 提取矩阵元素或子块的方法
命令片断 |
用 途 |
---|---|
A(m,n) |
提取m行n列位置的一个元素 |
A(:,n) |
提出第n列 |
A(m,:) |
提出第m行 |
A(m1:m2, n1:n2) |
提出m1到m2行,n1到n2列的一个子块 |
A(m:end, n) |
提出m行到最后一行,第n列的一个子块 |
A(:) |
将矩阵按列存储得到一个长列向量 |
可以像对数字操作一样对矩阵进行操作,常见算术运算符的使用方法如表1.6所示。
表1.6 常见的算术运算符
运 算 |
符号 |
对应函数 |
说 明 |
---|---|---|---|
加 |
+ |
plus(A,B) |
|
减 |
- |
minus(A,B) |
|
乘 |
* |
mtimes(A,B) |
即通常意义上的矩阵乘法 |
点乘 |
.* |
times(A,B) |
矩阵的对应元素相乘。参与运算的两个矩阵必须拥有同样的大小 |
乘方 |
.^ |
mpower(A,B) |
对矩阵的每一个元素进行指定幂次的乘方 |
矩阵乘方 |
^ |
power(A,B) |
|
矩阵左除 |
\ |
mldevide(A,B) |
左除A\B相当于inv(A) * B |
矩阵右除 |
/ |
mrdevide(A,B) |
右除A/B相当于B * inv(A) |
左除 |
.\ |
ldevide(A,B) |
矩阵中对应位置的元素的左除 |
右除 |
./ |
rdevide(A,B) |
矩阵中对应位置的元素的右除 |
矩阵与向量转置 |
.' |
transpose(A,B) |
这里的转置不对复数进行共轭操作 |
复数矩阵转置(共轭) |
' |
ctranspose(A,B) |
应用于复数数值时的含义是取共轭,应用于实数矩阵时的含义与普通转置相同,应用于复数矩阵时首先对所有元素取共轭再求矩阵转置 |
矩阵运算的求值顺序和一般的数学求值顺序相同:表达式是从左向右执行的,幂运算的优先级最高,乘除次之,最后是加减。如果有括号,那么括号的优先级最高。
对于图像矩阵,还有一系列MATLAB函数可以进行专门针对图像的像素级操作。如图像叠 加——imadd,图像相减——imsubtract等。
在处理函数返回值和示波器部件输出时,常常会遇到不同维度的返回值同时被一个函数返回的情况。同时,通常也希望能使函数的输入参数尽可能少。MATLAB提供了允许这样做的方式。
细胞数组是MATLAB特有的一种数据结构,它的各个元素可以是不同的数据类型。细胞数组可采用下标访问。
例如,一个细胞数组可以采用如下方式定义。
Cell = {'Harry', 15, [1 0; 15 2]};
也可以通过{}加上索引来直接定义细胞数组的某个元素,如下所示。
%定义细胞数组的另一种方式
>>Cell {1}= 'Harry';
>>Cell{2}= 15;
>>Cell{3}= [1 0; 15 2];
注意使用花括号{}而不是方括号[]来定义细胞数组。对细胞数组的访问方式也很简单,同样使用花括号{}来给定索引值。
%访问细胞数组
>> Cell{1}
ans =
Harry
>> Cell{2}
ans =
15
>> Cell{3}
ans =
1 0
15 2
而使用圆括号形式的索引可以得到变量的描述,如下所示。
>> Cell(3)
ans =
[2x2 double]
注意
细胞数组中存储的是建立该对象时所使用的其他对象(矩阵或字符串、数字等)的复制而不是引用或指针,即使其他对象的值被改变,细胞数组中的值也不变。
结构体是另一种形式的聚合类型,它与C/C++中的结构体或类很相似,拥有多个不同类型的字段,通过圆点运算符“.”引用内部字段,字段必须具有独特的名字以便区分。访问结构体的方式与定义的方式相同。上面的例子如果用结构体表示,则如下所示。
% 定义结构体
Struct.Name = 'Harry';
Struct.Age = 15;
Struct.SalaryMatrix = [1 0; 15 2];
>>
>> Struct %显示结构体的内容
Struct =
Name: 'Harry'
Age: 15
SalaryMatrix: [2x2 double]
>>
% 访问结构体的内部字段
>>name = Struct.Name;
而访问结构体内容时,使用相同的语法即可,例如Struct.Name的值仍然是“Harry”。
这两种复合类型在保存用户输入和使用Simulink仿真输出时尤为常用。
关系运算符的运算结果是布尔量(0或1),具体说明如表1.7所示。
表1.7 关系运算符的使用
运 算 |
符 号 |
运 算 |
符 号 |
---|---|---|---|
大于 |
> |
小于 |
< |
大于等于 |
>= |
小于等于 |
<= |
等于 |
== |
不等于 |
~= |
MATLAB同样支持逻辑运算,常见的逻辑运算符如表1.8所示。
表1.8 逻辑运算符的使用
运 算 |
符 号 |
---|---|
与 |
& |
非 |
~ |
或 |
| |
异或 |
Xor |
MATLAB最为强大的功能是依靠函数实现的,这些函数可能是MATLAB内置的,也可能是由M文件提供的。常见的有sin、cos、tan、log、log2这样的数值函数和trace这样的矩阵函数,还有逻辑函数等。逻辑函数和矩阵函数在图像处理中应用较多,表1.9所示为其中较常用的一部分函数。关于这些函数更详细的用法描述,可以通过help或open命令获得。
表1.9 常用的逻辑函数和矩阵函数
函 数 |
用 途 |
---|---|
all |
是否所有元素非零 |
any |
是否至少有一元素非零 |
isempty |
是否空矩阵 |
isequal |
是否两矩阵相同 |
isinf |
判断有无inf元素 |
isnan |
判断有无nan元素 |
isreal |
判断是否实矩阵 |
find |
返回一个由非零元素下标组成的矩阵 |
det |
计算方阵对应的行列式值 |
diag |
抽取对角线元素 |
eig |
求特征值和特征向量 |
fliplr |
左右翻转 |
flipud |
上下翻转 |
inv |
求逆矩阵 |
lu |
三角分解 |
norm |
求范数 |
orth |
正交化 |
poly |
求特征多项式 |
qr |
正交三角分解 |
rank |
求矩阵的秩 |
svd |
奇异值分解 |
trace |
求矩阵的迹 |
Tril |
抽取上三角阵 |
Tnu |
抽取下三角阵 |
调用MATLAB函数的方法为:函数名后使用一对圆括号括住提供给函数的参数,如sin(t)。如果函数有返回值,但调用者没有指定接收返回值的变量,系统会使用默认的ans变量存储返回值。如果函数返回多个值,则ans中只保留第一个返回值,因此对于这种情况应显式地使用向量来接收返回值,如下所示。
[V D] = eigs(A) ; %计算矩阵A的特征值和特征向量,返回值中V为特征向量,D为特征值
使用函数还应当注意以下几点。
(1)函数只能出现在等式的右边。
(2)每个函数依原型不同,对自变量的个数和类型有一定的要求,如sin和sind函数。
(3)函数允许按照规则嵌套,比如sin(acos(0.5))。
MATLAB提供了程序流程分支控制的语句,它们的用法和C/C++中几乎完全一致,如表1.10所示。
表1.10 程序流程控制方法
语 句 |
规范写法 |
备 注 |
---|---|---|
if…elseif…else |
if expression1 |
如果elseif使用的层次较多,可以考虑转而使用switch |
for |
for index=start:increment:end |
increment指定步进值,省略则默认为1。可以嵌套使用 |
while |
while expression |
同样可以嵌套使用 |
break |
- |
终止while或for循环的执行 |
continue |
- |
直接跳到下一个循环 |
switch |
switch expression |
没有默认的fall-through,因而不需要使用配套的break语句 |
return |
- |
返回调用函数 |
在后面,将多次使用这些语句,因而,在此给出几个简单的例子。读者可以在金羽图书论坛(http://bbs.book95.com)的“金羽图书与答疑”板块与本书同名的主题帖子附件中的chapter1/code目录下找到示例1.1~示例1.3所对应的M文件,也可以通过菜单“File”→“New”→“M-Files”新建一个M文件,在出现的窗口中粘贴这些代码,然后运行。在非注释行处按下F12可以设置断点,按下F5键可以运行程序。
【例1.1】if语句和for循环及其嵌套。
%ex1_1.m
arg=input('Input argument:'); % 提示输入arg变量
total = 0; detail = 0;
% if语句开始
if(arg==1)
% 外层for语句开始
for i=1:1:5
total = total + 1;
% 内层for语句开始
for j=1:0.1:2
detail = detail + total;
% 内层for语句结束
end
% 外层for语句结束
end
% if语句的另一分支
elseif (arg==2)
total = 0;
detail = total;
% if语句的其他所有分支
else
error('Invalid arguments!');
%if语句结束
end
detail % 显示detail变量
请注意本例中分号的使用。
【例1.2】与例1.1类似的功能,使用switch分支和while循环。
%ex1_2.m
arg=input('Input argument:');
total = 0; detail = 0;
% switch语句开始
switch arg
% 分支1
case 1
i=1;
% 外层while语句开始
while (i<=5)
total = total + 1;
i = i + 1;
j = 1;
% 内层while语句开始
while (j<=2);
detail = detail + total;
j = j + 0.1;
% 内层while语句结束
end
% 外层while语句结束
end
% 分支2
case 2
total = 0;
detail = total;
% 分支其他
case others
error('Invalid arguments');
% switch语句结束
end
detail
总结这两个例子,可以发现,在分支较多时使用switch是合算的,而for和while用于循环控制,这一点与C/C++是完全相同的。但是,相对于C/C++,MATLAB有一个突出的优点,就是可以自动生成元素之间具有特定间隔的矩阵,从而避免使用某些循环,这里仅仅使用二维的情况举例。
【例1.3】产生一幅亮度按对角线方向的余弦规律变化的灰度图,比较一维方法和二维方法所需的时间。
A = rand(3000, 3000);
f = zeros(3000, 3000);
u0 = 100; v0 = 100;
tic; % 开始计时
% 一维方法
% 外层for循环开始
for r=1:3000
u0x=u0*(r-1);
% 内层for循环开始
for c=1:3000
v0y=v0*(c-1);
f(r,c) = A(r,c) * cos(u0x+v0y);
% 内层for循环结束
end
% 外层for循环结束
end
t1=toc % 停止计时并记录时间到t1
tic; % 重新开始计时
% 二维方法
r = 0:3000-1;
c = 0:3000-1;
[C, R] = meshgrid(c, r);
% meshgrid是生成网格坐标的函数,实际就是生成需要的二维像素点的坐标拟合表示
% 建议读者在这里中断,观察一下C和R矩阵的内容。
g = A .* cos(u0 .* R + v0 .* C);
%系统将自动执行“循环”操作,实质是对R和C中每个数据按照指定公式操作
t2 = toc % 停止计时并将计时值保存到t2
在运行之后,结果发现,t2远小于t1。因此,在用MATLAB对数字图像按像素进行操作时,需要尽可能地避免使用笨拙的多层嵌套循环。
例1.3中用到的meshgrid()函数用于根据给定的横纵坐标点生成坐标网格,以便计算二元函数的取值,在绘制三维曲面时常常会用到它。其调用方式如下。
[X,Y] = meshgrid(x, y)
参数说明:
返回值:
下面以绘制二维高斯函数曲面为例说明meshgrid的用法。
中心在原点的二维高斯函数表达式为:
下面的程序分别为u和v赋值[-10:0.1:10],令σ=3,使用meshgrid函数生成网格,并计算函数值,(注意这里使用的是.^和./,而不是^和/,因为计算的对象是矩阵中的元素),然后再使用mesh函数将其显示到绘图窗口中。
u = [-10:0.1:10];
v = [-10:0.1:10];
[U,V] = meshgrid(u,v);
H = exp(-(U.^2 + V.^2)./2/3^2);
mesh(u, v, H);
% mesh函数是绘制三维曲面的函数,第一个和第二个参数分别为x轴和y轴的坐标点序列,第三个参数为在由坐标点序列确定的每一个方格点上的函数值
生成的图像如图1.7所示。
图1.7 高斯函数曲面
优化小技巧:提前分配矩阵内存
这个技巧与动态内存的使用有关。在C/C++里,使用大块动态内存往往意味着堆操作,而当分配的动态内存零散无序时,会产生大量内存碎片,进而导致内存分配和回收效率降低。所以,可以事先分配一块足够大的空间(当然,不是过大)以尽量减少内存碎片的产生。事实上,MATLAB中分配动态内存远没有C/C++那样麻烦,只需要类似如下一条语句即可。
memo = zeros(1024, 128);
这条语句本来是用于构造一个元素全部为零的矩阵,但同时很自然地也就分配了一块足够大的空间。
M文件和C/C++中c/cpp文件类似,就是存储MATLAB代码并可以执行的文件。MATLAB的源代码文件可以直接执行而不需编译(也可以通过编译来使代码运行得更快)。很多情况下,M文件用于封装一个功能函数从而提供某些特定功能。一般来说,M文件以文本格式存储,执行顺序从第1行开始向下遇到终止语句结束,用户可以在M文件中定义函数和过程。
M文件可以使用任何文本编辑器编写,但最常使用的还是MATLAB自带的M文件编辑器。如果是编写函数,最好将它放在MATLAB的搜索路径列表中的某个目录下,并与系统自带的M文件分开,以便管理。
对于位于当前工作目录中的M文件,可以直接在命令行输入其文件名来运行它。作为函数时,M文件也可以接受参数。
稍后将提供一个MATLAB自带M文件的例子,并给予简单分析。
MATLAB函数通常在M文件中定义,一个文件可以定义多个函数。一个MATLAB函数通常包含以下组成部分。
function [outputs] = name(inputs)
MATLAB允许返回多个参数(outputs),如果只返回一个参数,可以省略方括号。需要注意的是,输入参数是使用圆括号括起来的。例如,如果要定义一个用于平滑图像的函数imsmooth,可以将定义行书写如下。
function [imgOut, retCode] = imsmooth(imgIn, args)
某些函数可能没有输出参数,那么就需要在省略方括号及其中内容的同时,省略等号。于是无返回值的函数就需要定义如下。
function imsmooth(imgIn, args)
MATLAB允许区分函数名的前63个字母,多出的字母将被忽略。函数的命名规则与C/C++类似,必须以字母开头,可以包含字母、数字和下划线,但不能包含空格。
函数可以在其他的M函数中被调用,也可以在命令行直接调用。调用函数的方法很简单,只需要写出函数定义中除了function之外的部分即可。例如:
[a,b] = imsmooth(I,arg);
“H1”行是M文件中的第一个注释行(即以百分号开始的行),它必须紧跟着函数定义行,中间不能有空行,这一行的百分号前也不能有空白字符或缩进。这一行的内容将在使用help命令时显示在第一行,而lookfor命令查找H1行中的指定关键词,并在结果的右侧列显示H1行。一个典型的H1行的例子如下。
% IMSMOOTH Perform smooth operation on specified image with certain arguments.
这样,smooth函数在lookfor命令查找的时候就会显示如下。
IMSMOOTH SMOOTH Perform smooth operation on specified image with certain arguments.
帮助文本的位置和约定同H1行类似,只能紧跟H1行,中间不能有任何空行或者缩进。同样,帮助文本的本质就是注释行,因而需要以%开头。
MATLAB通过判断是否紧跟函数定义来判断一个注释行究竟是H1行、帮助文本,还是普通注释。因此可以在加入一个或多个空行后加入普通注释,使用Help命令将不会显示普通注释的内容。
这些部分的编写方式和普通的MATLAB程序类似,如果有返回值,应在函数体中为输出变量赋值。
可以在命令行输入edit imfinfo查看完整源文件。
function info = imfinfo(filename, format) %函数定义 %IMFINFO Information about graphics file. %H1行 % INFO = IMFINFO(FILENAME,FMT) returns a structure whose %帮助文本 % fields contain information about an image in a graphics
% file. FILENAME is a string that specifies the name of the
% graphics file, and FMT is a string that specifies the format
% of the file. The file must be in the current directory or in
% a directory on the MATLAB path. If IMFINFO cannot find a
% file named FILENAME, it looks for a file named FILENAME.FMT.
%
% The possible values for FMT are contained in the file format
% registry, which is accessed via the IMFORMATS command.
%
% If FILENAME is a TIFF, HDF, ICO, GIF, or CUR file containing more
% than one image, INFO is a structure array with one element for
% each image in the file. For example, INFO(3) would contain
% information about the third image in the file.
%
% INFO = IMFINFO(FILENAME) attempts to infer the format of the
% file from its content.
%
% INFO = IMFINFO(URL,...) reads the image from an Internet URL.
% The URL must include the protocol type (e.g., "http://").
%
% The set of fields in INFO depends on the individual file and
% its format. However, the first nine fields are always the
% same. These common fields are:
%
% Filename A string containing the name of the file
%
% FileModDate A string containing the modification date of
% the file
%
% FileSize An integer indicating the size of the file in
% bytes
%
% Format A string containing the file format, as
% specified by FMT; for formats with more than one
% possible extension (e.g., JPEG and TIFF files),
% the first variant in the registry is returned
%
% FormatVersion A string or number specifying the file format
% version
%
% Width An integer indicating the width of the image
% in pixels
%
% Height An integer indicating the height of the image
% in pixels
%
% BitDepth An integer indicating the number of bits per
% pixel
%
% ColorType A string indicating the type of image; this could
% include, but is not limited to, 'truecolor' for a
% truecolor (RGB) image, 'grayscale', for a grayscale
% intensity image, or 'indexed' for an indexed image.
%
% If FILENAME contains Exif tags (JPEG and TIFF only), then the INFO
% struct may also contain 'DigitalCamera' or 'GPSInfo' (global
% positioning system information) fields.
%
% The value of the GIF format's 'DelayTime' field is given in hundredths
% of seconds.
%
% Example:
%
% info = imfinfo('ngc6543a.jpg');
%
% See also IMREAD, IMWRITE, IMFORMATS.
% Copyright 1984-2008 The MathWorks, Inc.
% $Revision: 1.1.6.14 $ $Date: 2009/11/09 16:27:13 $
error(nargchk(1, 2, nargin, 'struct')); %函数体和注释
…(函数体)
% Delete temporary file from Internet download.
if (isUrl)
deleteDownload(filename);
end
在0.1.3小节介绍数字图像的分类时,曾接触到一些主要的图像类型。本节就来看一看这些主要的图像类型在MATLAB中是如何存储和表示的,主要包括亮度图像、RGB图像、索引图像、二值图像和多帧图像。
亮度图像即灰度图像。MATLAB使用二维矩阵存储亮度图像,矩阵中的每个元素直接表示一个像素的亮度(灰度)信息。例如,一个200×300像素的图像被存储为一个200行300列的矩阵,可以使用1.1.5小节介绍的选取矩阵元素(或子块)的方式来选择图像中的一个像素或一个区域。
如果矩阵元素的类型是双精度的,则元素的取值范围是从0到1;如果是8位无符号整数,则取值范围从0到255。数据0表示黑色,而1(或255)表示最大亮度(通常为白色)。
图1.8所示是一个使用双精度矩阵存储亮度图像的例子。
图1.8 MATLAB中亮度图像的表示方法
RGB图像使用3个一组的数据表达每个像素的颜色,即其中的红色、绿色和蓝色分量。在MATLAB中,RGB图像被存储在一个m×n×3的三维数组中。对于图像中的每个像素,存储的3个颜色分量合成像素的最终颜色。例如,RGB图像I中位置在11行40列的像素的RGB值为I(11,40,1:3)或I(11,40,:),该像素的红色分量为I(11,40,1),蓝色分量为I(11,40,3)。而I(:,:,1)则表示整个的红色分量图像。
RGB图像同样可以由双精度数组或8位无符号整数数组存储。图1.9所示是一个使用双精度数组存储RGB图像的例子。
图1.9 MATLAB中RGB图像的表示方式
索引图像往往包含两个数组,一个图像数据矩阵(Image Matrix)和一个颜色索引表(Colormap)。对应于图像中的每一个像素,图像数据数组都包含一个指向颜色索引表的索引值。
颜色索引表是一个m×3的双精度型矩阵,每一行指定一种颜色的3个RGB分量,即color = [R G B]。其中R、G、B是实数类型的双精度数,取值0~1。0表示全黑,1表示最大亮度。图1.10给出一个索引图像的实例,注意图像中的每个像素都用整数表示,其含义为颜色索引表中对应颜色的索引。
图像数据矩阵和颜色索引表的关系取决于图像数据矩阵中存储的数据类型是双精度类型还是8位无符号整数。
如果图像数据使用双精度类型存储,像素数据1表示颜色索引表中的第一行,像素数据2表示颜色索引表中的第二行,依此类推。而如果图像数据使用8位无符号整数存储,则存在一个额外的偏移量-1,像素数据0表示颜色索引表中的第一行,而1表示索引表中的第二行,以此类推。
8位方式存储的图像可以支持256种颜色(或256级灰度)。图1.10中,数据矩阵使用的是双精度类型,所以没有偏移量,数据5表示颜色表中的第5种颜色。
图1.10 MATLAB中索引图像的表示方法
在二值图像中,像素的颜色只有两种可能取值:黑或白。MATLAB将二值图像存储为一个二维矩阵,每个元素的取值只有0和1两种情况,0表示黑色,而1表示白色。
二值图像可以被看作是一种特殊的只存在黑和白两种颜色的亮度图像,当然,也可以将二值图像看作是颜色索引表中只存在两种颜色(黑和白)的索引图像。
MATLAB中使用uint8型的逻辑数组存储二值图像,通过一个逻辑标志表示数据有效范围是0到1,而如果逻辑标志未被置位,则有效范围为0到255。
二值图像的表示方法如图1.11所示。
图1.11 MATLAB中二值图像的表示方法
对于某些应用,可能要处理多幅按时间或视角方式连续排列的图像,称之为多帧图像(所谓“帧”就是影像动画中最小单位的单幅影像画面)。例如核磁共振成像数据或视频片断。Matlab提供了在同一个矩阵中存储多帧图像的方法,实际上就是在图像矩阵中增加一个维度来代表时间或视角信息。例如,一个拥有5张连续的400×300像素的RGB图像的多帧连续片断的存储方式是一个400×300×3×5的矩阵,一组同样大小的灰度图像则可以使用一个400×300×1×5的矩阵来存储。
如果多帧图像使用索引图像的方式存储,只有图像数据矩阵被按多帧形式存储,而颜色索引表只能公用。因此,在多帧索引图像中,所有的索引图像公用一个颜色索引表,进而只能使用相同的颜色组合。
cat函数可以在指定维度上连接数组,其调用方式如下。
CAT(DIM, A, B);
或
CAT(DIM, A1, A2, …);
此函数在第DIM维度将第2至第n个参数提供的数组连接起来。于是,若要构造一个由5幅RGB图像构成的多帧图像组,使用的命令如下。
ANIM=CAT(4, A1, A2, A3, A4, A5);
图像处理工具箱中的某些函数只能处理图像矩阵中的前2维或前3维信息。当然,也可以使用它们处理拥有4个维度或5个维度的RGB图像或者连续图像序列,但这需要单独处理每帧符合要求的亮度/二值/索引/RGB图像。例如,显示ANIM中的第3帧图像需要使用如下方式。
imshow(ANIM(:,:,:,3));
函数imshow的作用是显示一帧图像,详见1.5节。
注意
如果向一个函数传递了超过其所能够处理的维度的图像矩阵,那么结果可能是不确定的。某些函数的行为可能是处理图像的第一帧或第一个颜色维度,但某些函数可能带来不确定的行为和处理结果。
默认情况下,MATLAB将绝大多数数据存储为双精度类型(64位浮点数)以保证运算的精确性。而对于图像而言,这种数据类型在图像尺寸较大时可能并不理想。例如,一张1000像素见方的图像拥有100万个像素,如果每个像素用64位二进制数表示,总共需要大约8MB的内存空间。
为了减小图像信息的空间开销,可以将图像信息存为8位无符号整型数(uint8)或16位无符号整型数(uint16)的数组,这样只需要双精度浮点数1/8或1/4的空间。在上述3种存储类型中以双精度和uint8使用最多,uint16的情况与uint8大致类似。
有时必须将图像存储格式加以转换才能使用某些图像处理函数。例如,当使用某些MATLAB内置的滤镜时,需要将索引图像转换为RGB图像或者灰度图像,MATLAB才会将图像滤镜应用于图像数据本身,而不是索引图像中的颜色索引值表(这将产生无意义的结果)。
MATLAB提供了一系列存储格式转换函数,如表1.11所示。它们的名字都很便于记忆,例如ind2gray可以将索引图像转化为灰度图像。
表1.11 图像格式转换函数
函 数 |
描 述 |
---|---|
dither |
使用抖动的方式创建较小颜色信息量的图像。例如从灰度图转换成黑白图,或者从RGB图转换成索引图。多数时候返回uint8类型的图像,如果输出图像是包含大于256色颜色表的索引图,则使用uint16类型 |
gray2ind |
从灰度图转换成索引图。多数时候返回uint8类型的图像,如果输出图像是包含大于256色颜色表的索引图,则使用uint16类型。例如: |
grayslice |
使用阈值法从灰度图创建索引图。多数时候返回uint8类型的图像,例如:如果输出图像是包含大于256色颜色表的索引图,则使用uint16类型。 |
im2bw |
使用阈值法从灰度图、索引图或RGB图创建二值图。返回逻辑型矩阵存储的图像。 |
ind2gray |
从索引图创建灰度图。返回的图像与原图像存储类型相同。例如: |
ind2rgb |
从索引图创建RGB图,返回double类型存储的图像。例如: |
mat2gray |
使用归一化方法将一个矩阵中的数据扩展成对应的灰度图,返回图像使用double类型存储。 |
rgb2gray |
从RGB图创建灰度图,返回图像与原图像存储类型相同。例如: |
rgb2ind |
从RGB图创建索引图。多数时候返回uint8类型的图像,如果输出图像是包含大于256色颜色表的索引图,则使用uint16类型。例如: |
也可以使用一些矩阵操作函数实现某些格式的转换。例如,下面的语句可以将一幅灰度图像转换为RGB图像。
RGBIMAGE = CAT(3, GRAY, GRAY, GRAY);
MATLAB图像处理工具箱中支持的默认图像数据类型是uint8,使用imread函数读取图像文件一般都为uint8类型。然而,很多数学函数如sin等并不支持double以外的类型,例如当试图对uint8类型直接使用sin函数进行操作时,MATLAB会提示如下的错误信息。
I = imread(‘coins.png’); %读入一幅unit8图像
sin(I);
??? Undefined function or method 'sin' for input arguments of type 'uint8'
针对这种情况,除了使用1.1.4小节介绍的强制类型转换方法外,还可利用图像处理工具箱中的内置图像数据类型转换函数。内置转换函数的优势在于它们可以帮助处理数据偏移量和归一化变换,从而简化了使用者的编程工作。
一些常用的图像类型转换函数如表1.12所示。
表1.12 图像数据类型转换函数
im2uint8 |
将图像转换为uint8类型 |
im2uint16 |
将图像转换为uint16类型 |
im2double |
将图像转换为double类型 |
可以在使用MATLAB数学函数前将图像转换为double类型,而在准备将图像写入文件时再将其转换为uint8类型,如下所示。
I_d = im2double(I_uint8); %将uint8图像转换为double类型,灰度范围也相应从[0,255]归一化至[0,1]
Iout_d = sin(I_d); %进行数学计算
Iout_uint8= im2uint8(Iout_d); %转换回uint8(灰度范围也重新扩展到[0,255]), 准备写入文件
MATLAB可以处理以下的图像文件类型:BMP、HDF、JPEG、PCX、TIFF、XWD、ICO、GIF、CUR。可以使用imread和imwrite函数对图像文件进行读写操作,使用imfinfo函数来获得数字图像的相关信息。
imread()函数可以将指定位置的图像文件读入工作区。对于除索引图像以外的情况,其原型如下。
A = imread(FILENAME, FMT);
参数说明:
返回值:
对于索引图像,情况有所不同,此时imread的调用形式如下。
[X, MAP] = imread(FILENAME, FMT);
此时的返回值中,X为图像数据矩阵,MAP则是颜色索引表。图像中的颜色索引数据会被归一化到0到1的范围内。因为,对于索引图像,不论图像文件本身使用何种数据类型,imread函数都会使用双精度类型存储图像数据。
imread函数还可以处理RGBA等格式存储的图像,可以通过在命令窗口中输入help imread来查看MATLAB中有关imread的在线帮助信息。
imwrite()将指定的图像数据写入文件中,通过指定不同的保存文件扩展名,可以起到图像格式转换的作用(参见例1.4)。其调用格式如下。
imwrite(A, FILENAME, FMT);
存储索引图像时,还需要一并存储颜色索引表,则此时imwrite函数的使用方法应如下。
imwrite(A, MAP, FILENAME, FMT);
MAP是合法的MATLAB颜色索引表。
imwrite函数还可以控制图像文件的很多属性,例如TIFF文件格式所选择的彩色空间、GIF格式中的透明色以及图像文件的作者、版权信息、解析度和创建软件等。
【例1.4】读入一幅tif图像文件,并在写入磁盘时将tif图像转换为bmp图像。
>>I=imread('pout.tif'); %读入图像
>>whosI %查看图像变量信息
Name Size Bytes Class
I 291x240 69840 uint8 array
Grand total is 69840 elements using 69840 bytes
%通过whos命令可以看到读入的高为291、宽为240的灰度图像I就是一个291*240的二维矩阵
>>imwrite(I, 'pout.bmp'); %将图像写入文件pout.bmp,同时起到了转换文件类型的作用
例1.4中的功能已经被封装在金羽图书论坛(http://bbs.book95.com)的“金羽图书与答疑”板块与本书同名的主题帖子附件中的“Chapter1/Code”目录下的“ex1_4.m”文件内。
imfinfo()函数可以读取图像文件中的某些属性信息,比如修改日期、大小、格式、高度、宽度、色深、颜色空间、存储方式等。使用方法如下。
imfinfo(FILENAME, FMT);
【例1.5】查看图像文件信息。
>>imfinfo('pout.tjf') %查看图像文件信息
ans =Filename: 'F:\Program Files\MATLAB\R2011a\toolbox\images\imdemos\pout.tif'
FileModDate: '04-十二月-2000 13:57:50'
FileSize: 69004
Format: 'tif'
FormatVersion: []
Width: 240
Height: 291
BitDepth: 8
ColorType: 'grayscale'
FormatSignature: [73 73 42 0]
ByteOrder: 'little-endian'
NewSubFileType: 0
BitsPerSample: 8
Compression: 'PackBits'
PhotometricInterpretation: 'BlackIsZero'
StripOffsets: [9x1 double]
SamplesPerPixel: 1
RowsPerStrip: 34
StripByteCounts: [9x1 double]
XResolution: 72
YResolution: 72
ResolutionUnit: 'None'
Colormap: []
PlanarConfiguration: 'Chunky'
TileWidth: []
TileLength: []
TileOffsets: []
TileByteCounts: []
Orientation: 1
FillOrder: 1
GrayResponseUnit: 0.0100
MaxSampleValue: 255
MinSampleValue: 0
Thresholding: 1
Offset: 68754
一般使用imshow函数来显示图像,该函数可以创建一个图像对象,并可以自动设置图像的诸多属性,从而简化编程操作。这里介绍imshow函数的几种常见调用方式。
imshow函数用于显示工作区或图像文件中的图像,在显示的同时可控制部分效果,常用的调用形式如下。
imshow(I, [low high], param1, value1, param2, value2, …)
imshow(I, MAP)
imshow(filename)
【例1.6】图像文件的读取、显示、回写。
% ex1_6.m
% 读取图像文件
>>I = imread('gantrycrane.png');
% 显示图像
>>imshow(I);
% 写回到文件
>>imwrite(I, 'gantrycrane.tif', 'TIFF');
有时需要将多幅图像一起显示以比较它们之间的异同,这在考察不同算法对同一幅图像的处理效果时尤为有用。
可以在同一窗口或者不同的窗口显示多幅图像,这两种方式的实现如例1.7所示。
【例1.7】显示多幅图像。
% ex1_7.m
I = imread(’pout.tif’); % 读取图像
% 在不同窗口显示
figure; %创建一个新的窗口
imshow(I);
figure;
imshow(I, [ ]);
% 在相同窗口显示
figure;
subplot(1, 2,1);
imshow(I);
subplot(1,2,2);
imshow(I, [ ]);
上述程序中figure函数用于新创建一个显示窗口,从而避免新图像的显示覆盖原图像;subplot(m,n,p)函数的含义是,打开一个有m行n列图像位置的窗口,并将焦点位于第p个位置上。
注意
在多幅索引图像的显示中存在着潜在的问题。由于索引图像使用的颜色表可能不同,而系统的全局颜色表在默认情况下是8位的,最多只能存储256种颜色,这样一来,如果所有图像总颜色种类超过256种,则超出的部分将不会被正确显示。所以,通常先使用ind2rgb(I)将图像转换为RGB模式。此外,也可以使用subimage(I,map),这个函数在显示图像之前会自动将其转换为RGB格式。
在显示多帧图像时,可以显示多帧中的一帧,或者将它们显示在同一个窗口内,也可以将多帧图像转化成电影播放出来。这3种方式的实现分别如例1.8所示。
【例1.8】多幅图像D,存储了一组索引图像,MAP为颜色索引表。分别以上述的3种方式显示它们。
>>load mri %载入Matlab自带的核磁共振图像
>>imshow(D(:,:,7), map); % 显示多幅中的一幅
% 同一窗口显示
>>figure, montage(D, map);
% 转化成为电影
>>figure
>>mov=immovie(D, map);
>>colormap(map); %设定颜色表
>>movie(mov); %播放电影
金羽图书论坛(http://bbs.book95.com)的“金羽图书与答疑”板块与本书同名的主题帖子附件中“chapter1/code”目录下的“ex1_8.m”文件封装了例1.8中的功能。
有时需要将图像的某一部分放大以查看局部的详细情况。只需输入zoom on命令即可实现图像的放缩,zoom off可以关闭图像放缩功能。打开图像放缩功能之后,就可以通过简单的鼠标操作观察图像细部了。
使用imshow函数显示一幅图像之后,可以通过输入impixelinfo命令在最后显示的图像窗口的左下角,随鼠标光标的移动显示鼠标指针所指位置处的像素值,如图1.12所示。
图1.12 像素查看器
还可以通过imdistline命令以交互的方式查看图像中两点之间的距离,如图1.13所示。
图1.13 使用imdistline命令查看距离