维数为1 x N的数组称为行向量。行向量中元素的存取是使用一维索引进行的。因此,v(1)是向量v(1)的第一个元素,v(2)是第二个元素,以此类推。MATLAB中向量的元素使用方括号括起,并由空格或逗号隔开。例如
>> v = [1 3 5 7 9]
v =
1 3 5 7 9
>> v(2)
ans =
3
使用转置运算符(.’)可将行向量转换为列向量:
>> w = v.'
w = 1
3
5
7
9
要存取元素的数据块,我们可使用MATLAB的冒号。例如,要存取v的前三个元素,可使用语句
>> v(1:3)
ans =
1 3 5
使用如下语句存取第三个到最后一个元素:
>> v(3:end)
ans =
5 7 9
其中,end表示向量中的最后一个元素。
>> v(:)
ans =
1
3
5
7
9
产生一个列向量,而语句
>> v(1:end)
ans =
1 3 5 7 9
产生一个行向量。
索引并不限于连续的元素。例如,
>> v(1:2:end)
ans =
1 5 9
符号1:2:end表示索引从1开始计数,步长为2,直到最后一个元素时停止。步长也可以为负:
>> v(end:-2:1)
ans =
9 5 1
这时,索引从最后一个元素开始计数,步长为-2,直到第一个元素时停止。
函数linspace的语法为
x = linspace(a , b, n)
该语句产生一个含有n个元素的行向量x,这n个元素之间线性地隔开并且包含a与b。
一个向量也可用做另一个向量的索引。例如,我们可以使用如下命令挑出向量v的第一个、第四个和第五个元素:
>> v([1 4 5])
ans =
1 7 9
在MATLAB中,从矩阵中选取元素和从向量中选取元素是一样的,
但我们现在需要两个索引:一个用于确定行位置,另一个用于确定相应的列位置。例如,要提取第2行第三列的元素,可以使用语句
>> A(2 , 3)
ans =
6
矩阵索引中使用冒号操作符在矩阵中选择一个二维元素块。例如,
>> C3 = A(: , 3)
C3 =
3
6
9
其中,冒号本身的作用相当与语句A(1 : 3 , 3),即简单地将矩阵的第三列挑出来。类似地,我们也可以提取第二行
>> R2 = A(2 , :)
R2 =
4 5 6
下列语句提取出矩阵的前两行:
>> T2 = A(1 :2,1:3)
T2 =
1 2 3
4 5 6
要产生一个与A相等但其最后一列全部置为0的矩阵B,我们可使用如下语句:
>> B = A;
>> B(: , 3) = 0
B =
1 2 0
4 5 0
7 8 0
使用向量作为矩阵的索引为元素的选择提供了一种强大的方法,例如,
>> E = A([1 , 3], [2 , 3])
E =
2 3
8 9
符号A([a b],[c d])从A中挑选出坐标为(行a,列c),(行a,列d),(行b,列c)和(行b,列d)的元素。
>> D = logical([1 0 0; 0 0 1; 0 0 0])
D =
1 0 0
0 0 1
0 0 0
则
>> A(D)
ans =
1
6
使用索引矩阵一种特别有用的寻址方法是形式A(D),其中D是逻辑数组。
在为矩阵做索引时,单引号会选择该数组的全部元素(以逐列的方式),并将这些元素排列成一个列向量的形式。例如矩阵T2:
<< v = T2(:)
v =
1
4
2
5
3
6
冒号的使用对我们很有帮助,例如求矩阵的所有元素的和
>> s = sum(A(:))
s =
45
相当于执行命
>> sum(sum(A));
因为使用单个冒号会将该矩阵转换为一个向量。使用冒号运算符实际上是矩阵或多维数组的一种线性索引形式。事实上,MATLAB将每个数组都作为列向量来储存,而不管它实际的维数是多少。这个列由数组列首尾相连而成。
operation(A , dim)
operation表示MATLAB中的一种可用操作,A是一个数组,dim是一个标量。例如,假设A是一个大小为MxN的数组。命令
>> k = size(A , 1);
沿A的第一个维数(在MATLAB中定义为垂直方向)给出A的大小。换言之,该命令给出A的行数。类似地,数组的第二个维数为水平方向,所以语句size(A , 2)给出A的列数。单一维数是任意维数dim,且size(A , dim) = 1。
函数ndims的语法为:
**d = ndims(A)**
它将给出数组A的维数。函数ndims返回值不会小于2,因为即使是标量,我们也认为它有两个维数,这时的标量是大小为1x1的数组
M文件由文本编辑器创建,并以filename.m形式的文件名存储,如average.m和filter.m。M文件函数的组成部分为
function [outputs] = name(inputs)
MATLAB运算符可以分为以下三种主要类别
算数运算符
MATLAB不复制信息
图像是等价于矩阵的二维数组
关系运算符
对于向量和矩形数组,两个操作数必须有相同的维数,或者其中的操作数时标量。此时,MATLAB将另一个操作数的每一个元素相比较,产生一个与操作数大小相同的逻辑数组,在满足指定关系的位置取1,其余位置取0。若两个操作数都是标量,则当指定关系满足时结果为1,否则为0。
逻辑运算符
在所有的逻辑测试中,MATLAB将逻辑1或非零数值量作为true来处理,将逻辑0和数值0作为false来处理。例如,当两个操作数都为逻辑1或非零数值时,两个操作数AND运算的结果为1;当两个操作数中的任何一个时逻辑0或数值0时,或两个操作数都为逻辑0或数值0时,AND运算的结果为0。
逻辑函数
数的表示
MATLAB为数字使用十进制计数法。科学计数法使用字母e来表示10的幂次。虚数使用i或j作为后缀。
条件句if的语法为
if expression
statements
end
评估expression后,若结果为true,则MATLAB在执行由if和end行之间的statements表示的一个或多个命令。若expression的结果为false,则MATLAB将跳过if与end行之间的所有语句,而继续执行end行后面的行。每一个if都必须与一个相应的end配对。
向量化循环
向量化意味着简单地将for循环和while循环转换为等价的向量或矩阵运算,运算除了明显地变得简短之外,向量化不仅可加速计算,还可增强代码的可读性。
假如要生成一个以为函数,该函数的形式为f(x)=Asin(x/2PI),其中,x=0,1,2…,M-1。实现计算:
for x = 1:M
f(x) = A * sin((x-1)/(2*pi));
end
但向量化后,代码效率会更高;换言之,通过充分利用MATLAB索引,上述代码可简化为
x = 0:M-1
f = A*sin(x/(2*pi));
MATLAB使用函数meshgrid来实现二维函数的评估,该函数的语法为:
[C, R] = meshgrid(c, f)
该函数将由行向量c和r指定的域变换为数组C和R,这两个数组能用来评估有着两个变量的函数和三维表面图(注意,在meshgrid的输入和输出中,列总是首先列出)。
预分配数组
加快代码执行时间的另一种方法是在程序中预分配数组的大小。在处理数值或逻辑数组时,预分配只是简单地创建有着适当维数的数组,数组的元素均为零。例如,若我们正在处理两幅大小均为1024 x 1024像素的图像f和g,则预分配由如下语句构成:
>> f = zeros(1024); g = zeros(1024);
处理大数组时,预分配也可帮助我们减少存储器碎片。动态存储器的分配和去分配会使得存储器出现碎片化。实际的结果是在计算过程中可能会有足够空间的可用物理存储器,但可能没有足够的连续空间来容纳一个较大的变量。预分配通过在计算开始时就允许MATLAB为大数据构造保留足够的存储空间,来阻止无连续空间的情形出现。
函数disp用来在屏幕上显示信息。其语法为
disp( argument )
若argument是一个数组,则disp显示数组的内容。若argument是一个文本串,则disp显示串的字符。(只显示argument的内容,而不显示如ans=这样的字)
函数input用于将数据输入到M函数。基本语法为
t = input(‘message’)
输出message的内容并接收一个字符串,字符串的内容可用逗号或空格隔开。
t = input(‘message’, ‘s’)
函数str2num将串的元素(做字符处理)转换为double类数字。语法为
n = str2num(t)
若输入中既有字符又有数字,则可以利用MATLAB中的串处理函数之一。如strread函数,其语法为:
[a,b,c, …] = strread(cstr,‘format’,‘param’,‘value’)
该函数指定用指定的format和param/value的组合,从字符串cstr中读取数据。用%f和%q,分别表示浮点数和字符串。对于param项,我们使用delimiter,以表明format中识别的项将由value中指定的字符分隔(一般为逗号或空格)。例如:
>> t = '12.6, x2y, z';
>>[a,b,c] = strread(t,'%f%q%q','delimiter',',')
a =
12.6000
b =
'x2y'
c =
'z'
>> d = char(b)
d =
x2y
在处理混合变量(如字符与数字)时,可以充分利用单元数组。MATLAB中的单元数组是一个多维数组,其元素是其他数组元素的副本。例如:
c = {'gauss',[1 0; 0 1], 3}
>> c{1}
ans =
gauss
>> c{2}
ans =
1 0
0 1
>> c{3}
ans =
3
空间域技术直接对图像的像素进行操作,空间域处理的表达式为:
g(x,y)=T[f(x,y)]
其中f(x,y)为输入图像,g(x,y)为输出(处理后的)图像,T是对图像f进行处理的操作符,定义在点(x,y)的邻域内。T还可以对一组图像进行处理,例如为降低噪声而让K幅图像相加。在计算(x,y)处的g值时,只使用该邻域的像素。
变换T的最简单形式是图3.1中的邻域大小为1 x 1(单个像素)时。此时,(x,y)处的g值仅由f在该点处的亮度决定,T也变为一个亮度或灰度级变换函数。处理单色(灰度)图像时,这两个术语可以相互换用的。
由于亮度变换函数仅取决于亮度的值,而与(x,y)无关,所以亮度变换函数通常可写做如下所示的简单形式:
s = T®
其中,r表示图像f中相应点(x,y)的亮度,s表示图像g中相应点(x,y)的亮度。
函数imadjust是对灰度图像进行亮度变换的基本IPT工具。语法为:
g = imadjust(f,[low_in high_in],[low_out high_out],gamma)
此函数将图像f中的亮度值映像到g中的新值,即将low_in至high_in之间的值映射到low_out至high_out之间的值。low_in以下与high_in以上的值则被剪切掉了;换言之,low_in以下的值映射为low_out,high_in以上的值映射为high_out。
输入图像应为uint8类,iunt16类或double类图像,输出图像与输入图像有着相同的类。除图像f外,函数imadjust的所有输入值均指定在0和1之间,而不论图像f的类。若f是uint8类图像,则函数imadjust将乘以255来确定应用中的实际值,uint16乘以65535。为[low_in high_in]或[low_out high_out]使用空矩阵([])会得到默认值[0 1]。若high_out小于low_out,则输出亮度会反转。
参数gamma指定了曲线形状,该曲线用来映射f的亮度值,以便生成图像g。若gamma小于1,则映射被加权至更高(更亮)的输出值。若gamma大于1,则映射被加权至更低(更暗)的输出值。若省略了函数的参量,则gamma默认为1(线性映射)。
明暗反转图像:
>> g1 = imadjust(f, [0 1],[1 0]);
g1 = imcomplement(f);
对数与对比度拉伸变换是进行动态范围处理的基本工具。对数变换通过如下表达式实现:
g = c*log(1 + double(f))
c是个常数,变换的形状类似于gamma曲线。但gamma曲线的形状是可变的,而对数函数形状则是固定的。对数变换的一项主要应用是压缩动态范围。
处理可变数量的输入和/或输出
为检测输入到M函数的参量数目,我们可以使用函数nargin
n = nargin
该函数将返回输入到M函数的参量的实际数目。函数nargout用于M函数的输出。其语句为:
n = nargout
例如,假设执行M函数:
>> T = testhv(4, 5);
该函数体中使用nargin返回2,使用nargout返回1。
函数nargchk可用于一个M函数体中,以检测传递的参量数目是否正确。语法为:
msg = nargchk(low, high, number)
具有可变数目的输入变量和输出变量的函数,varargin和varargout。(必须用小写形式)例如:
function[m, n] = testhv3(varargin)
将输入的变量数读到函数testhv3中,而
function [varargout] = testhv4(m , n, p)
则通过函数testhv4返回输出的变量数。若函数testhv3有一个固定的输入变量x,后跟输入变量的可变数目,则调用
function [m,n] = testhv3(x,varargin)
亮度变换的另一个M函数
在编写亮度变换M函数时我们将用到函数changeclass,其语法为
g = changeclass(newclass, f)
此函数将图像f转换成由参数newclass指定的类别,并输出图像g。newclass的有效值是’uint8’,‘uint16’和’double’。
亮度标度的M函数
处理图像时,像素值域由负到正的现象很普通,尽管中间的计算过程没有问题,但当我们想利用8比特或16比特格式保存或查看一幅图像的时候,就会出现问题。
#3.3 直方图处理与函数绘图
以从图像灰度直方图中提取的信息为基础的灰度变换函数,在诸如增强、压缩、分割、描述等方面的图像处理中起着重要作用。 一幅数字图像在[0,G]范围内共有L个灰度级,其直方图定义为下列离散函数:
h ( r k ) = n k h\left(r_{k}\right)=n_{k} h(rk)=nk
式中,rk是区间[0,G]内的第k级灰度,nk为图像中出现rk这种灰度级的像素数。对于uint8类图像,G的值为255;对于uint16类图像,G的值为65535;对于浮点图像,G的值为1.0.注意,对于uint8类和uint16类图像,G=L-1。
用h(rk)h(rk)的所有元素除以图像中的总像素数n,就可以简单地得到归一化直方图:
p ( r k ) = h ( r k ) n = n k n \begin{aligned} p\left(r_{k}\right) &=\frac{h\left(r_{k}\right)}{n} \\ &=\frac{n_{k}}{n} \end{aligned} p(rk)=nh(rk)=nnk
式中,对于整数函数,k=0,1,2,~~~~L-1.从基础概率论的角度,可以认为 \\(p(r_k)\\)是灰度级rk出现的概率的估计。 在处理图像直方图的工具箱中,核心函数是imhist,其基本语法如下:h=imhist(f,b)
其中,f为输入图像,h为其直方图。b用来形容直方图的“容器”的数目(若b未包含在此参量中,默认其值为256)。
使用表达式,可以得到归一化直方图:
p=imhist(f,b)/numel(f)
函数numel(f)给出数组f中的元素数
计算并绘制图像直方图
imhist(f);
[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-AUbrZMv7-1593095740116)(https://i.imgur.com/9qUJe9U.png)]
直方图还可以用条形图来绘制,为此,可使用函数
bar(horz,z,width)
其中z是一个包含被绘制的点的行向量;horz是一个与z同维数的向量,它包含了水平刻度的增量;width是一个介于0和1之间的数。horz的值给出了水平增量,z的值是相应的垂直值。若horz被省略,水平轴会从0至length(z)等分为若干单位。当width的值为1时,竖条比较明显;当width值为0时,竖条是垂直线。width的默认值为0.8,绘制条形图时,通常会通过将水平轴等分为几段来降低其分辨率。
如下命令将产生把水平轴分为10级一组的条形图:
h=imhist(f,25);
horz=linspace(0,255,25);
bar(horz,h)
axis([0 255 0 60000])
set(gca,’xtick’,0:50:255)
set(gca,’ytick’,0:20000:60000)
[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-Kb2ZApsm-1593095740130)(https://i.imgur.com/CtNFlVJ.png)]
杆状图与条形图相似,语法为:
stem(horz,z,’LineSpec’,’fill’)
其中,z是一个包含了将被绘制的点的行向量,horz是一个与z同维数的向量,它包含了水平刻度的增量,若horz被省略,水平轴会从0至length(z)等分为若干单位。
下图所示杆状图由下列语句得到
h=imhist(f,25);
horz=linspace(0,255,25);
stem(horz,h,’fill’)
axis([0 255 0 60000])
set(gca,’xtick’,[0:50:255])
set(gca,’ytick’,[0:20000:60000])
[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-UrIaD1Dj-1593095740134)(https://i.imgur.com/CPFiRgf.png)]
函数plot,将一组点用直线连接起来,其语法为:
plot(horz,z,’LineSpec’)
如同函数stem那样,plot中的属性也指定为一个三值组。plot的默认值是不带标记点的蓝色实线。
所示图形由以下语句得到:
hc=imhist(f);
plot(hc)%Use the default values.
axis([0 255 0 15000])
set(gca,’xtick’,[0:50:255])
set(gca,’ytick’,[0:2000:15000])
[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-rdG8pIfi-1593095740139)(https://i.imgur.com/HOCsDvV.png)]
直方图均衡由工具箱中的函数histeq实现,其语法为:
g=histeq(f,nlev)
其中,f为输入图像,nlev是为输出图像设定的灰度级数。若nlev与L(输入图像中可能的灰度级总数)相等,则histeq直接执行变换函数。若nlev小于L,则histeq试图分配灰度级,以便得到近似平坦的直方图。
图3.8a是花粉的电子显微图像,已经放大了700倍,就所需的图像增强而言,这幅图最突出的特点就是较暗,且其动态范围较低。这些特点在图2.8b所示的直方图中很明显。其中图像较暗的性质导致直方图偏向于灰度级的暗端,从直方图相对于整个灰度范围非常狭窄的事实看出,其 较低的动态范围是很明显的。令f表示输入图像,下列各步骤产生图3.8a到图3.8d所示结果。
imshow(f);%Fig.3.8(a).
[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-o9zd0S6R-1593095740143)(https://i.imgur.com/RUvDJqt.png)]
figure,imhist(f)%Fig.3.8(b).
[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-ZSqWpc8t-1593095740148)(https://i.imgur.com/CLrBEDO.png)]
ylim(‘auto’)
g=histeq(f,256);
figure,imshow(g)%Fig.3.8©.
[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-5MCcY86b-1593095740151)(https://i.imgur.com/tsKFEGN.png)]
figure,imhist(g)%Fig.3.8(d).
[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-mnys696h-1593095740152)(https://i.imgur.com/iCXvhvm.png)]
ylim(‘auto’)
如前所述,在直方图均衡中使用的变换函数是归一化直方图的累加求和。可以利用函数cumsum实现变换功能,如下:
hnorm=imhist(f)./numel(f);
**cdf=cumsum(hnorm); **
由cdf绘制的图形如图3.9所示,可由如下命令得到:
hnorm=imhist(f)./numel(f);
cdf=cumsum(hnorm);
x=linspace(0,1,256);
plot(x,cdf)
axis([0 1 0 1]);
set(gca,’xtick’,0:.2:1)
set(gca,’ytick’,0:.2:1)
xlabel(‘Input intensity values’,’fontsize’,9)
ylabel(‘Output intensity values’,’fontsize’,9)
[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-e6yCDZH0-1593095740154)(https://i.imgur.com/B1wbepK.png)]
图3.9 用于将图3.7a所示输入图像映射到图3.7c所示输出图像的变换函数
从图3.8所示的直方图可看出,图3.9中的变换函数把输入灰度级低端中较窄的灰度级映射到输出图像的整个灰度范围。比较图3.8中的输入图像和输出图像,可见图像对比度的改进是很明显的。
##2.3.3 直方图匹配(规定化)
能够规定处理后图像的直方图形状在某些应用中是非常有用的。生成具有特定直方图的图像的方法,称为直方图匹配或直方图规定化。
实现直方图匹配的工具箱函数histeq的语法如下:
g=histeq(f,hspec)
其中,f为输入图像,hspec为规定的直方图(一个规定值的行向量),g为输出图像,输出图像的直方图近似于指定的直方图hspec。histeq的特性是当length(hspec)比图像f中的灰度级数小很多时,图像g的直方图通常会较好地匹配hspec.
直方图匹配
图3.10a显示了火星天体福布司的图像f,图3.10b显示使用了imhist(f)得到的直方图。这幅图片受大片较暗区域控制,造成直方图中大部分像素都集中在灰度级的暗端。图3.10c使用了直方图均衡,使得较暗区域中的细节更加明显,命令:
f1=histeq(f,256);
结果表明,使用直方图均衡方法,在这种情况下,图像产生了“褪色”现象。图3.10d为均衡后图像的直方图。我们看到,灰度级已经移到了较高端一侧,因而给出了一幅低对比度且有褪色现象的图像。灰度级的移动是由于在原始直方图中灰度级在0及其附近区域过于集中。由直方图得到的累积变换函数非常陡,因此把在低端过于集中的像素点映射到了灰度级的高端。
邻域处理有以下几个步骤:(1)选取中心点(x,y);(2)仅对预先定义的关于点(x,y)的邻域内的像素执行操作;(3)令运算结果为该点处的响应;(4)对图像中的每一点重复该处理。
中心点移动的过程会产生新的邻域,每个邻域对应输入图像上的一个像素。用来标识该处理的两个主要术语是邻域处理和空间滤波,其中后者更为通用。若对邻域中像素执行的计算为线性的,则称该操作为线性空间滤波(也用术语空间卷积);否则称为非线性空间滤波。
本章,直接对图像中的像素执行滤波运算,我们使用术语线性空间滤波来区分这种类型的处理与频域滤波。
本章感兴趣的线性操作包括邻域中的每个像素乘以相应的系数,将结果求和,从而得到点(x,y)处的响应。若领域的大小为m*n,则需要mn个系数。这些系数被排列为一个矩阵,称为滤波器、模板、滤波模板、核、掩模或窗口,也用卷积滤波、卷积模板或卷积核等术语。
imfilter的最常见语法是:
g=imfilter(f,w,’replicate’)
当在工具箱中实现标准的线性空间滤波时,使用这一语法。
使用一个旋转后的滤波器执行相关操作与使用该原始滤波器执行卷积操作是相同的。如果该滤波器关于其中心对称,则两种操作产生相同的结果。