Python计算机视觉编程1.3  NumPy_Python计算机视觉编程1.3  NumPy试读-查字典图书网
查字典图书网
当前位置: 查字典 > 图书网 > 编程 > Python计算机视觉编程 > 1.3  NumPy

Python计算机视觉编程——1.3  NumPy

NumPy(http://www.scipy.org/NumPy/)是非常有名的Python 科学计算工具包,其中包含了大量有用的思想,比如数组对象(用来表示向量、矩阵、图像等)以及线性代数函数。NumPy 中的数组对象几乎贯穿用于本书的所有例子中1 数组对象可以帮助你实现数组中重要的操作,比如矩阵乘积、转置、解方程系统、向量乘积和归一化, 这为图像变形、对变化进行建模、图像分类、图像聚类等提供了基础。 注1: PyLab 实际上包含NumPy 的一些内容,如数组类型。这也是我们能够在1.2 节使用数组类型的原因。 NumPy 可以从http://www.scipy.org/Download 免费下载,在线说明文档(http://docs. scipy.org/doc/numpy/)包含了你可能遇到的大多数问题的答案。关于NumPy 的更多内容,请参考开源书籍[24]。 1.3.1  图像数组表示 在先前的例子中,当载入图像时,我们通过调用array() 方法将图像转换成NumPy 的数组对象,但当时并没有进行详细介绍。NumPy 中的数组对象是多维的,可以用来表示向量、矩阵和图像。一个数组对象很像一个列表(或者是列表的列表),但是数组中所有的元素必须具有相同的数据类型。除非创建数组对象时指定数据类型,否则数据类型会按照数据的类型自动确定。 对于图像数据,下面的例子阐述了这一点: im = array(Image.open('empire.jpg')) print im.shape, im.dtype im = array(Image.open('empire.jpg').convert('L'),'f') print im.shape, im.dtype 控制台输出结果如下所示: (800, 569, 3) uint8 (800, 569) float32 每行的第一个元组表示图像数组的大小(行、列、颜色通道),紧接着的字符串表示数组元素的数据类型。因为图像通常被编码成无符号八位整数(uint8),所以在第一种情况下,载入图像并将其转换到数组中,数组的数据类型为“uint8”。在第二种情况下,对图像进行灰度化处理,并且在创建数组时使用额外的参数“f”;该参数将数据类型转换为浮点型。关于更多数据类型选项,可以参考图书[24]。注意, 由于灰度图像没有颜色信息,所以在形状元组中,它只有两个数值。 数组中的元素可以使用下标访问。位于坐标i、j,以及颜色通道k 的像素值可以像下面这样访问: value = im[i,j,k] 多个数组元素可以使用数组切片方式访问。切片方式返回的是以指定间隔下标访问该数组的元素值。下面是有关灰度图像的一些例子: im[i,:] = im[j,:] # 将第j 行的数值赋值给第i 行 im[:,i] = 100 # 将第i 列的所有数值设为100 im[:100,:50].sum() # 计算前100 行、前50 列所有数值的和 im[50:100,50:100] # 50~100 行,50~100 列(不包括第100 行和第100 列) im[i].mean() # 第i 行所有数值的平均值 im[:,-1] # 最后一列 im[-2,:] (or im[-2]) # 倒数第二行 注意,示例仅仅使用一个下标访问数组。如果仅使用一个下标,则该下标为行下标。注意,在最后几个例子中,负数切片表示从最后一个元素逆向计数。我们将会频繁地使用切片技术访问像素值,这也是一个很重要的思想。 我们有很多操作和方法来处理数组对象。本书将在使用到的地方逐一介绍。你可以查阅在线文档或者开源图书[24] 获取更多信息。 1.3.2  灰度变换 将图像读入NumPy 数组对象后,我们可以对它们执行任意数学操作。一个简单的例子就是图像的灰度变换。考虑任意函数f,它将0...255 区间(或者0...1 区间)映射到自身(意思是说,输出区间的范围和输入区间的范围相同)。下面是关于灰度变换的一些例子: from PIL import Image from numpy import * im = array(Image.open('empire.jpg').convert('L')) im2 = 255 - im # 对图像进行反相处理 im3 = (100.0/255) * im + 100 # 将图像像素值变换到100...200 区间 im4 = 255.0 * (im/255.0)**2 # 对图像像素值求平方后得到的图像 第一个例子将灰度图像进行反相处理;第二个例子将图像的像素值变换到100...200 区间;第三个例子对图像使用二次函数变换,使较暗的像素值变得更小。图1-4 为所使用的变换函数图像。图1-5 是输出的图像结果。你可以使用下面的命令查看图像中的最小和最大像素值: print int(im.min()), int(im.max()) 图1-4:灰度变换示例。三个例子中所使用函数的图像,其中虚线表示恒等变换 图1-5:灰度变换。对图像应用图1-4 中的函数:f (x)=255-x 对图像进行反相处理(左);f (x)=(100/255) x+100 对图像进行变换(中);f (x)=255(x/255)2 对图像做二次变换(右) 如果试着对上面例子查看最小值和最大值,可以得到下面的输出结果: 2 255 0 253 100 200 0 255 array() 变换的相反操作可以使用PIL 的fromarray() 函数完成: pil_im = Image.fromarray(im) 如果你通过一些操作将“uint8”数据类型转换为其他数据类型,比如之前例子中的im3 或者im4,那么在创建PIL 图像之前,需要将数据类型转换回来: pil_im = Image.fromarray(uint8(im)) 如果你并不十分确定输入数据的类型,安全起见,应该先转换回来。注意,NumPy 总是将数组数据类型转换成能够表示数据的“最低”数据类型。对浮点数做乘积或除法操作会使整数类型的数组变成浮点类型。 1.3.3  图像缩放 NumPy 的数组对象是我们处理图像和数据的主要工具。想要对图像进行缩放处理没有现成简单的方法。我们可以使用之前PIL 对图像对象转换的操作,写一个简单的用于图像缩放的函数。把下面的函数添加到imtool.py 文件里: def imresize(im,sz): """ 使用PIL 对象重新定义图像数组的大小""" pil_im = Image.fromarray(uint8(im)) return array(pil_im.resize(sz)) 我们将会在接下来的内容中使用这个函数。 1.3.4  直方图均衡化 图像灰度变换中一个非常有用的例子就是直方图均衡化。直方图均衡化是指将一幅图像的灰度直方图变平,使变换后的图像中每个灰度值的分布概率都相同。在对图像做进一步处理之前,直方图均衡化通常是对图像灰度值进行归一化的一个非常好的方法,并且可以增强图像的对比度。 在这种情况下,直方图均衡化的变换函数是图像中像素值的累积分布函数(cumulative distribution function,简写为cdf,将像素值的范围映射到目标范围的归一化操作)。 下面的函数是直方图均衡化的具体实现。将这个函数添加到imtool.py 里: def histeq(im,nbr_bins=256): """ 对一幅灰度图像进行直方图均衡化""" # 计算图像的直方图 imhist,bins = histogram(im.flatten(),nbr_bins,normed=True) cdf = imhist.cumsum() # cumulative distribution function cdf = 255 * cdf / cdf[-1] # 归一化 # 使用累积分布函数的线性插值,计算新的像素值 im2 = interp(im.flatten(),bins[:-1],cdf) return im2.reshape(im.shape), cdf 该函数有两个输入参数,一个是灰度图像,一个是直方图中使用小区间的数目。函数返回直方图均衡化后的图像,以及用来做像素值映射的累积分布函数。注意,函数中使用到累积分布函数的最后一个元素(下标为-1),目的是将其归一化到0...1 范围。你可以像下面这样使用该函数: from PIL import Image from numpy import * im = array(Image.open('AquaTermi_lowcontrast.jpg').convert('L')) im2,cdf = imtools.histeq(im) 图1-6 和图1-7 为上面直方图均衡化例子的结果。上面一行显示的分别是直方图均衡化之前和之后的灰度直方图,以及累积概率分布函数映射图像。可以看到,直方图均衡化后图像的对比度增强了,原先图像灰色区域的细节变得清晰。 图1-6:直方图均衡化示例。左侧为原始图像和直方图,中间图为灰度变换函数,右侧为直方图均衡化后的图像和相应直方图 图1-7:直方图均衡化示例。左侧为原始图像和直方图,中间图为灰度变换函数,右侧为直方图均衡化后的图像和相应直方图 1.3.5  图像平均 图像平均操作是减少图像噪声的一种简单方式,通常用于艺术特效。我们可以简单地从图像列表中计算出一幅平均图像。假设所有的图像具有相同的大小,我们可以将这些图像简单地相加,然后除以图像的数目,来计算平均图像。下面的函数可以用于计算平均图像,将其添加到imtool.py 文件里: def compute_average(imlist): """ 计算图像列表的平均图像""" # 打开第一幅图像,将其存储在浮点型数组中 averageim = array(Image.open(imlist[0]), 'f') for imname in imlist[1:]: try: averageim += array(Image.open(imname)) except: print imname + '...skipped' averageim /= len(imlist) # 返回uint8 类型的平均图像 return array(averageim, 'uint8') 该函数包括一些基本的异常处理技巧,可以自动跳过不能打开的图像。我们还可以使用mean() 函数计算平均图像。mean() 函数需要将所有的图像堆积到一个数组中; 也就是说,如果有很多图像,该处理方式需要占用很多内存。我们将会在下一节中使用该函数。 1.3.6  图像的主成分分析(PCA) PCA(Principal Component Analysis,主成分分析)是一个非常有用的降维技巧。它可以在使用尽可能少维数的前提下,尽量多地保持训练数据的信息,在此意义上是一个最佳技巧。即使是一幅100×100 像素的小灰度图像,也有10 000 维,可以看成10 000 维空间中的一个点。一兆像素的图像具有百万维。由于图像具有很高的维数,在许多计算机视觉应用中,我们经常使用降维操作。PCA 产生的投影矩阵可以被视为将原始坐标变换到现有的坐标系,坐标系中的各个坐标按照重要性递减排列。 为了对图像数据进行PCA 变换,图像需要转换成一维向量表示。我们可以使用NumPy 类库中的flatten() 方法进行变换。 将变平的图像堆积起来,我们可以得到一个矩阵,矩阵的一行表示一幅图像。在计算主方向之前,所有的行图像按照平均图像进行了中心化。我们通常使用SVD(Singular Value Decomposition,奇异值分解)方法来计算主成分;但当矩阵的维数很大时, SVD 的计算非常慢,所以此时通常不使用SVD 分解。下面就是PCA 操作的代码: from PIL import Image from numpy import * def pca(X): """ 主成分分析: 输入:矩阵X ,其中该矩阵中存储训练数据,每一行为一条训练数据 返回:投影矩阵(按照维度的重要性排序)、方差和均值""" # 获取维数 num_data,dim = X.shape # 数据中心化 mean_X = X.mean(axis=0) X = X - mean_X if dim>num_data: # PCA- 使用紧致技巧 M = dot(X,X.T) # 协方差矩阵 e,EV = linalg.eigh(M) # 特征值和特征向量 tmp = dot(X.T,EV).T # 这就是紧致技巧 V = tmp[::-1] # 由于最后的特征向量是我们所需要的,所以需要将其逆转 S = sqrt(e)[::-1] # 由于特征值是按照递增顺序排列的,所以需要将其逆转 for i in range(V.shape[1]): V[:,i] /= S else: # PCA- 使用SVD 方法 U,S,V = linalg.svd(X) V = V[:num_data] # 仅仅返回前nun_data 维的数据才合理 # 返回投影矩阵、方差和均值 return V,S,mean_X 该函数首先通过减去每一维的均值将数据中心化,然后计算协方差矩阵对应最大特征值的特征向量,此时可以使用简明的技巧或者SVD 分解。这里我们使用了range() 函数,该函数的输入参数为一个整数n,函数返回整数0...(n-1) 的一个列表。你也可以使用arange() 函数来返回一个数组,或者使用xrange() 函数返回一个产生器(可能会提升速度)。我们在本书中贯穿使用range() 函数。 如果数据个数小于向量的维数,我们不用SVD 分解,而是计算维数更小的协方差矩阵XXT 的特征向量。通过仅计算对应前k(k 是降维后的维数)最大特征值的特征向量,可以使上面的PCA 操作更快。由于篇幅所限,有兴趣的读者可以自行探索。矩阵V 的每行向量都是正交的,并且包含了训练数据方差依次减少的坐标方向。 我们接下来对字体图像进行PCA 变换。fontimages.zip 文件包含采用不同字体的字符a 的缩略图。所有的2359 种字体可以免费下载1。假定这些图像的名称保存在列表imlist 中,跟之前的代码一起保存传在pca.py 文件中,我们可以使用下面的脚本计算图像的主成分: from PIL import Image from numpy import * from pylab import * import pca im = array(Image.open(imlist[0])) # 打开一幅图像,获取其大小 m,n = im.shape[0:2] # 获取图像的大小 imnbr = len(imlist) # 获取图像的数目 # 创建矩阵,保存所有压平后的图像数据 immatrix = array([array(Image.open(im)).flatten() for im in imlist],'f') # 执行PCA 操作 V,S,immean = pca.pca(immatrix) # 显示一些图像(均值图像和前7 个模式) figure() gray() subplot(2,4,1) imshow(immean.reshape(m,n)) for i in range(7): subplot(2,4,i+2) imshow(V[i].reshape(m,n)) show() 注意,图像需要从一维表示重新转换成二维图像;可以使用reshape() 函数。如图1-8 所示,运行该例子会在一个绘图窗口中显示8 个图像。这里我们使用了PyLab 库的subplot() 函数在一个窗口中放置多个图像。 注1: 免费字体图像库由Martin Solli 收集并上传(http://webstaff.itn.liu.se/~marso/)。 图1-8:平均图像(左上)和前7 个模式(具有最大方差的方向模式) 1.3.7  使用pickle模块 如果想要保存一些结果或者数据以方便后续使用,Python 中的pickle 模块非常有用。pickle 模块可以接受几乎所有的Python 对象,并且将其转换成字符串表示,该过程叫做封装(pickling)。从字符串表示中重构该对象,称为拆封(unpickling)。这些字符串表示可以方便地存储和传输。 我们来看一个例子。假设想要保存上一节字体图像的平均图像和主成分,可以这样来完成: # 保存均值和主成分数据 f = open('font_pca_modes.pkl', 'wb') pickle.dump(immean,f) pickle.dump(V,f) f.close() 在上述例子中,许多对象可以保存到同一个文件中。pickle 模块中有很多不同的协议可以生成.pkl 文件;如果不确定的话,最好以二进制文件的形式读取和写入。在其他Python 会话中载入数据,只需要如下使用load() 方法: # 载入均值和主成分数据 f = open('font_pca_modes.pkl', 'rb') immean = pickle.load(f) V = pickle.load(f) f.close() 注意,载入对象的顺序必须和先前保存的一样。Python 中有个用C语言写的优化版本,叫做cpickle 模块,该模块和标准pickle 模块完全兼容。关于pickle 模块的更多内容,参见pickle 模块文档页http://docs.python.org/library/pickle.html。 在本书接下来的章节中,我们将使用with 语句处理文件的读写操作。这是Python 2.5 引入的思想,可以自动打开和关闭文件(即使在文件打开时发生错误)。下面的例子使用with() 来实现保存和载入操作: # 打开文件并保存 with open('font_pca_modes.pkl', 'wb') as f: pickle.dump(immean,f) pickle.dump(V,f) 和 # 打开文件并载入 with open('font_pca_modes.pkl', 'rb') as f: immean = pickle.load(f) V = pickle.load(f) 上面的例子乍看起来可能很奇怪,但with() 确实是个很有用的思想。如果你不喜欢它,可以使用之前的open 和close 函数。 作为pickle 的一种替代方式,NumPy 具有读写文本文件的简单函数。如果数据中不包含复杂的数据结构,比如在一幅图像上点击的点列表,NumPy 的读写函数会很有用。保存一个数组x 到文件中,可以使用: savetxt('test.txt',x,'%i') 最后一个参数表示应该使用整数格式。类似地,读取可以使用: x = loadtxt('test.txt') 你可以从在线文档http://docs.scipy.org/doc/numpy/reference/generated/numpy.loadtxt. html 了解更多内容。 最后,NumPy 有专门用于保存和载入数组的函数。你可以在上面的在线文档里查看关于save() 和load() 的更多内容。

展开全文

推荐文章

猜你喜欢

附近的人在看

推荐阅读

拓展阅读

《Python计算机视觉编程》其他试读目录

• 1.1  PIL:Python图像处理类库
• 1.2  Matplotlib
• 1.3  NumPy [当前]
• 1.4  SciPy
• 1.5  高级示例:图像去噪