目录
一、PIL-Python图像库
PIL(图像处理类库)提供了通用的图像处理功能,以及大量有用的基本图像操作,比如图像缩放、裁剪、旋转、颜色转换等。利用 PIL 中的函数,我们可以从大多数图像格式的文件中读取数据,然后写入最常见的图像格式文件中。PIL 中最重要的模块为 Image
。要读取一幅图像,可以使用如下:
from PIL import Image
pil_im = Image.open('C:/Users/asus/Pictures/window/11.jpg') # 原始图像
上述代码的返回值 pil_im
是一个 PIL 图像对象。
图像的颜色转换可以使用 convert()
方法来实现。要读取一幅图像,并将其转换成灰度图像,只需要加上 convert('L')
,如下所示:
pil_im = Image.open('empire.jpg').convert('L')
1.1 读取图像并转换灰度图
实现代码:
# -*- coding: utf-8 -*-
from PIL import Image
from pylab import * # pylab库画图功能
# 添加中文字体支持
from matplotlib.font_manager import FontProperties
font = FontProperties(fname=r"c:\windows\fonts\SimSun.ttc", size=14)
figure()
pil_im = Image.open('C:/Users/asus/Pictures/window/11.jpg') # 原始图像
gray()
subplot(121) # 一行两列 ,第一个图 (显示)
title(u'原图', fontproperties=font)
axis('off')
imshow(pil_im)
# 读取图像并转换为灰度图像
pil_im = Image.open('C:/Users/asus/Pictures/window/11.jpg').convert('L')
subplot(122)
title(u'灰度图', fontproperties=font)
axis('off')
imshow(pil_im)
show() # 屏幕显示
结果截图:
1.2 对图片进行格式转换
利用save()
方法,PIL可以将图片保存问很多不同的图像格式。下面的例子从文件名列表(filelist
)中读取所有的图像文件,并转换成 JPEG 格式:
from PIL import Image
import os
for infile in filelist:
outfile = os.path.splitext(infile)[0] + ".jpg"
if infile != outfile:
try:
Image.open(infile).save(outfile)
except IOError:
print "cannot convert", infile
PIL 的 open()
函数用于创建 PIL 图像对象,save()
方法用于保存图像到具有指定文件名的文件。除了后缀变为“.jpg”,上述代码的新文件名和原文件名相同。
1.3 创建缩略图
使用 PIL 可以很方便地创建图像的缩略图。thumbnail()
方法接受一个元组参数(该参数指定生成缩略图的大小),然后将图像转换成符合元组参数指定大小的缩略图。例如,创建最长边为 128 像素的缩略图,可以使用下列命令:
pil_im.thumbnail((128,128))
1.4 拷贝并粘贴区域
使用 crop()
方法可以从一幅图像中裁剪指定区域,如下:
box = (100,100,400,400)
region = pil_im.crop(box)
该区域使用四元组来指定。四元组的坐标依次是(左,上,右,下)。PIL 中指定坐标系的左上角坐标为(0,0)。我们可以旋转上面代码中获取的区域,然后使用 paste()
方法将该区域放回去,具体实现如下:
region = region.transpose(Image.ROTATE_180)
pil_im.paste(region,box)
1.5 调整尺寸和旋转
要调整一幅图像的尺寸,我们可以调用 resize()
方法。该方法的参数是一个元组,用来指定新图像的大小:
out = pil_im.resize((128,128))
要旋转一幅图像,可以使用逆时针方式表示旋转角度,然后调用 rotate()
方法:
out = pil_im.rotate(45)
下面代码显示上面提到的所有的图像处理操作,即原图显示、RGB图像转为灰度图像、拷贝粘贴区域、生成缩略图、调整图像尺寸、图像旋转变换的实例代码:
# -*- coding: utf-8 -*-
from PIL import Image
from pylab import *
# 添加中文字体支持
from matplotlib.font_manager import FontProperties
font = FontProperties(fname=r"c:\windows\fonts\SimSun.ttc", size=14)
figure()
# 显示原图
pil_im = Image.open('C:/Users/asus/Pictures/window/11.jpg')
print(pil_im.mode, pil_im.size, pil_im.format)
subplot(231)
title(u'原图', fontproperties=font)
axis('off')
imshow(pil_im)
# 显示灰度图
pil_im = Image.open('C:/Users/asus/Pictures/window/11.jpg').convert('L')
gray()
subplot(232)
title(u'灰度图', fontproperties=font)
axis('off')
imshow(pil_im)
# 拷贝粘贴区域
pil_im = Image.open('C:/Users/asus/Pictures/window/11.jpg')
box = (100,100,400,400)
region = pil_im.crop(box)
region = region.transpose(Image.ROTATE_180)
pil_im.paste(region,box)
subplot(233)
title(u'拷贝粘贴区域', fontproperties=font)
axis('off')
imshow(pil_im)
# 缩略图
pil_im = Image.open('C:/Users/asus/Pictures/window/11.jpg')
size = 128, 128
pil_im.thumbnail(size)
print(pil_im.size)
subplot(234)
title(u'缩略图', fontproperties=font)
axis('off')
imshow(pil_im)
pil_im.save('C:/Users/asus/Pictures/window/thumbnail.jpg') # 保存缩略图
# 调整图像尺寸
pil_im = Image.open('C:/Users/asus/Pictures/window/11.jpg')
pil_im = pil_im.resize(size)
print(pil_im.size)
subplot(235)
title(u'调整尺寸后的图像', fontproperties=font)
axis('off')
imshow(pil_im)
# 旋转图像45°
pil_im = Image.open('C:/Users/asus/Pictures/window/11.jpg')
pil_im = pil_im.rotate(45)
subplot(236)
title(u'旋转45°后的图像', fontproperties=font)
axis('off')
imshow(pil_im)
show()
结果截图:
二、Matplotlib库
当在处理数学及绘图或在图像上描点、画直线、曲线时,Matplotlib是一个很好的绘图库,它比PIL库提供了更有力的特性。下面例子展示部分函数:
2.1 画图、描点和线
虽然Matplotlib
可以绘制出较好的条形图、饼状图、散点图等,但是对于大多数计算机视觉应用来说,仅仅需要用到几个绘图命令。最重要的是,我们想用点和线来表示一些事物,比如兴趣点、对应点以及检测出的物体。下面是用几个点和一条线绘制图像的例子:
实现代码:
# -*- coding: utf-8 -*-
from PIL import Image
from pylab import *
# 添加中文字体支持
from matplotlib.font_manager import FontProperties
font = FontProperties(fname=r"c:\windows\fonts\SimSun.ttc", size=14)
# 读取图像到数组中
im = array(Image.open('C:/Users/asus/Pictures/window/11.jpg'))
figure()
# 画有坐标轴的
subplot(121)
imshow(im) # 绘制图像
# 绘制一些点
x = [100, 100, 400, 400]
y = [200, 500, 200, 500]
plot(x, y, 'r*') # red红色的标记点
plot(x[:2], y[:2]) # 绘制连接前两个点的线
title(u'绘图: "11.jpg"', fontproperties=font)
# 不显示坐标轴
subplot(122)
imshow(im)
x = [100, 100, 400, 400]
y = [200, 500, 200, 500]
plot(x, y, 'r*')
plot(x[:2], y[:2]) # plot(x,y) 默认为蓝色实线
axis('off') # 不显示坐标轴
title(u'绘图: "11.jpg"', fontproperties=font)
show()
结果截图:
2.2 图像轮廓和直方图
在画图像轮廓前需要转换为灰度图像,因为轮廓需要获取每个坐标[x,y]位置的像素值。图像的直方图可以使用hist()
函数绘制,代码如下:
figure()
hist(im.flatten(),128)
show()
hist()
函数的第二个参数指定小区间的数目。需要注意的是,因为 hist()
只接受一维数组作为输入,所以我们在绘制图像直方图之前,必须先对图像进行压平处理。flatten()
方法将任意数组按照行优先准则转换成一维数组。
下面是画图像轮廓和直方图的代码:
# -*- coding: utf-8 -*-
from PIL import Image
from pylab import *
# 添加中文字体支持
from matplotlib.font_manager import FontProperties
font = FontProperties(fname=r"c:\windows\fonts\SimSun.ttc", size=14)
im = array(Image.open('C:/Users/asus/Pictures/window/11.jpg').convert('L')) # 打开图像,并转成灰度图像
figure() # 新建一个图像
subplot(121)
gray() # 不使用颜色信息
contour(im, origin='image') # 在原点的左上角显示轮廓图像
axis('equal')
axis('off') # 不显示坐标轴
title(u'图像轮廓', fontproperties=font)
subplot(122)
hist(im.flatten(), 128)
title(u'图像直方图', fontproperties=font)
plt.xlim([0, 260])
plt.ylim([0, 11000])
show()
结果截图:
2.3 交互式标注
有时,用户需要和应用进行交互,比如在图像中用点做标识,或者在一些训练数据中进行注释。PyLab提供了一个很简洁好用的函数ginput(),它可以完成该任务,下面是一个演示交互注释的简短示例:
from PIL import Image
from pylab import *
im = array(Image.open('C:/Users/asus/Pictures/window/11.jpg'))
imshow(im)
print('Please click 3 points')
imshow(im)
x = ginput(3)
print('You clicked:', x)
show()
上面代码先读取11.jpg图像,显示读取的图像,然后用ginput()交互注释,这里设置的交互注释数据点设置为3个,用户在注释后,会将注释点的坐标打印出来。
标注结果:
三、NumPy库
NumPy是Python一个流行的用于科学计算包,其中包含了大量有用的思想,比如数组对象(用来表示向量、矩阵、图像等)以及线性代数函数。
3.1 图像数组表示
在前面载入图像的示例中,我们将图像用array()函数转为NumPy数组对象,但是并没有提到它表示的含义。数组就像列表一样,只不过它规定了数组中的所有元素必须是相同的类型。下面的例子用于说明图像数组表示:
# -*- coding: utf-8 -*-
from PIL import Image
from pylab import *
im = array(Image.open('C:/Users/asus/Pictures/window/11.jpg'))
print(im.shape, im.dtype)
im = array(Image.open('C:/Users/asus/Pictures/window/11.jpg').convert('L'), 'f')
print(im.shape, im.dtype)
运行代码显示的结果:
每行的第一个元组表示图像数组的大小(行、列、颜色通道),紧接着的字符串表示数组元素的数据类型。因为图像通常被编码成无符号八位整数(uint8),所以在第一种情况下,载入图像并将其转换到数组中,数组的数据类型为“uint8”。在第二种情况下,对图像进行灰度化处理,并且在创建数组时使用额外的参数“f”;该参数将数据类型转换为浮点型。注意,由于灰度图像没有颜色信息,所以在形状元组中,它只有两个数值。
数组中的元素可以使用下标访问。位于坐标 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]) # 倒数第二行
注意,示例仅仅使用一个下标访问数组。如果仅使用一个下标,则该下标为行下标。在最后几个例子中,负数切片表示从最后一个元素逆向计数。我们将会频繁地使用切片技术访问像素值,这也是一个很重要的思想。
3.2 灰度变换
在读入图像到NumPy数组后,就可以对它进行任何我们想要的操作了。对图像进行灰度变换便是一个简单的例子。这里给出一些进行灰度变换的例子:
# -*- coding: utf-8 -*-
from PIL import Image
from numpy import *
from pylab import *
im = array(Image.open('C:/Users/asus/Pictures/window/11.jpg').convert('L'))
print(int(im.min()), int(im.max()))
im2 = 255 - im # 对图像进行反相处理
print(int(im2.min()), int(im2.max()))
im3 = (100.0/255) * im + 100 # 将图像像素值变换到100...200 区间
print(int(im3.min()), int(im3.max()))
im4 = 255.0 * (im/255.0)**2 # 对图像像素值求平方后得到的图像
print(int(im4.min()), int(im4.max()))
figure()
gray()
subplot(1, 3, 1)
imshow(im2)
axis('off')
title(r'$f(x)=255-x$')
subplot(1, 3, 2)
imshow(im3)
axis('off')
title(r'$f(x)=\frac{100}{255}x+100$')
subplot(1, 3, 3)
imshow(im4)
axis('off')
title(r'$f(x)=255(\frac{x}{255})^2$')
show()
结果:
上面左边灰度变换函数采用的是 f(x)=255-x,中间采用的是 f(x)=(100/255)x+100,右边采用的是变换函数是 f(x)=255(x/255)^2。
正如上面代码所示,你可以用通过下面命令检查每幅图像的最小值和最大值:
print(int(im.min()), int(im.max()))
如果你对每幅图像用到了打印最小像素值和最大像素值,你会得到下面的输出结果:
1 255
0 254
100 200
0 255
array()
变换的相反操作可以使用 PIL 的 fromarray()
函数完成:
pil_im = Image.fromarray(im)
如果你通过一些操作将“uint8”数据类型转换为其他数据类型,比如之前例子中的 im3 或者 im4,那么在创建 PIL 图像之前,需要将数据类型转换回来:
pil_im = Image.fromarray(uint8(im))
如果你并不十分确定输入数据的类型,安全起见,应该先转换回来。注意,NumPy
总是将数组数据类型转换成能够表示数据的“最低”数据类型。对浮点数做乘积或除法操作会使整数类型的数组变成浮点类型。
3.3 调整图像尺寸
NumPy数组将成为我们对图像及数据进行处理的最主要工具,但是调整矩阵大小并没有一种简单的方法。我们可以用PIL图像对象转换写一个简单的图像尺寸调整函数:
def imresize(im,sz):
""" Resize an image array using PIL. """
pil_im = Image.fromarray(uint8(im))
return array(pil_im.resize(sz))
3.4 直方图均衡化
图像灰度变换中一个非常有用的例子就是直方图均衡化。直方图均衡化是指将一幅图像的灰度直方图变平,使变换后的图像中每个灰度值的分布概率都相同。在对图像做进一步处理之前,直方图均衡化通常是对图像灰度值进行归一化的一个非常好的方法,并且可以增强图像的对比度。
下面是对图像直方图进行均衡化处理的代码:
# -*- coding: utf-8 -*-
from PIL import Image
from pylab import *
from PCV.tools import imtools
# 添加中文字体支持
from matplotlib.font_manager import FontProperties
font = FontProperties(fname=r"c:\windows\fonts\SimSun.ttc", size=14)
im = array(Image.open('C:/Users/asus/Pictures/window/11.jpg').convert('L')) # 打开图像,并转成灰度图像
im2, cdf = imtools.histeq(im)
figure()
subplot(2, 2, 1)
axis('off')
gray()
title(u'原始图像', fontproperties=font)
imshow(im)
subplot(2, 2, 2)
axis('off')
title(u'直方图均衡化后的图像', fontproperties=font)
imshow(im2)
subplot(2, 2, 3)
axis('off')
title(u'原始直方图', fontproperties=font)
hist(im.flatten(), 128, normed=True)
subplot(2, 2, 4)
axis('off')
title(u'均衡化后的直方图', fontproperties=font)
hist(im2.flatten(), 128, normed=True)
show()
结果截图:
3.5 图像平均
对图像取平均是一种图像降噪的简单方法,经常用于产生艺术效果。假设所有的图像具有相同的尺寸,我们可以对图像相同位置的像素相加取平均,下面是一个演示对图像取平均的例子:
# -*- coding: utf-8 -*-
from PCV.tools.imtools import get_imlist
from PIL import Image
from pylab import *
from PCV.tools import imtools
# 添加中文字体支持
from matplotlib.font_manager import FontProperties
font = FontProperties(fname=r"c:\windows\fonts\SimSun.ttc", size=14)
filelist = get_imlist('C:/Users/asus/Pictures/window//avg/') # 获取convert_images_format_test文件夹下的图片文件名(包括后缀名)
avg = imtools.compute_average(filelist)
for impath in filelist:
im1 = array(Image.open(impath))
subplot(2, 2, filelist.index(impath)+1)
imshow(im1)
imNum = str(filelist.index(impath)+1)
title(u'待平均图像'+imNum, fontproperties=font)
axis('off')
subplot(2, 2, 4)
imshow(avg)
title(u'平均后的图像', fontproperties=font)
axis('off')
show()
结果截图:
3.6 Pickle模块
如果你想将结果保存下来,或者将数据保存下来以便后面使用,那么pickle模块是非常有用的。Pickle模块能够获取几乎所有的Python对象,并将它转换成字符串表示,该过程称为封装;从字符串表示重构对象的过程为拆封。下面用一个例子对其进行说明。正如你在上面注释部分看到的一样,假设我们想将前一节字体图像的平均值和主成分保存起来,可以通过下面操作:
f = open('../data/fontimages/font_pca_modes.pkl', 'wb')
pickle.dump(immean,f)
pickle.dump(V,f)
f.close()
上面在使用封装操作前,需要导入pickle模块。如果要载入保存的.pkl数据,可以通过load()方法,如下:
# load mean and principal components
f = open('../data/fontimages/font_pca_modes.pkl', 'rb')
immean = pickle.load(f)
V = pickle.load(f)
f.close()
四、SciPy模块
SciPy是一个开源的数学工具包,它是建立在NumPy的基础上的。它提供了很多有效的常规操作,包括数值综合、最优化、统计、信号处理以及图像处理。正如接下来所展示的,SciPy库包含了很多有用的模块。
4.1 图像模糊
一个经典的并且十分有用的图像卷积例子是对图像进行高斯模糊。高斯模糊可以用于定义图像尺度、计算兴趣点以及很多其他的应用场合。如下例子:
代码:
# -*- coding: utf-8 -*-
from PIL import Image
from pylab import *
from scipy.ndimage import filters
# 添加中文字体支持
from matplotlib.font_manager import FontProperties
font = FontProperties(fname=r"c:\windows\fonts\SimSun.ttc", size=14)
im = array(Image.open('C:/Users/asus/Pictures/window/11.jpg').convert('L'))
figure()
gray()
axis('off')
subplot(1, 4, 1)
axis('off')
title(u'原图', fontproperties=font)
imshow(im)
for bi, blur in enumerate([2, 5, 10]):
im2 = zeros(im.shape)
im2 = filters.gaussian_filter(im, blur)
im2 = np.uint8(im2)
imNum = str(blur)
subplot(1, 4, 2 + bi)
axis('off')
title(u'标准差为' + imNum, fontproperties=font)
imshow(im2)
# 如果是彩色图像,则分别对三个通道进行模糊
# for bi, blur in enumerate([2, 5, 10]):
# im2 = zeros(im.shape)
# for i in range(3):
# im2[:, :, i] = filters.gaussian_filter(im[:, :, i], blur)
# im2 = np.uint8(im2)
# subplot(1, 4, 2 + bi)
# axis('off')
# imshow(im2)
show()
结果截图:
4.2 图像差分
图像强度的改变是一个重要的信息,被广泛用以很多应用中。下面是图像差分的例子。
代码:
# -*- coding: utf-8 -*-
from PIL import Image
from pylab import *
from scipy.ndimage import filters
import numpy
# 添加中文字体支持
from matplotlib.font_manager import FontProperties
font = FontProperties(fname=r"c:\windows\fonts\SimSun.ttc", size=14)
im = array(Image.open('C:/Users/asus/Pictures/window/11.jpg').convert('L'))
gray()
subplot(1, 4, 1)
axis('off')
title(u'(a)原图', fontproperties=font)
imshow(im)
# Sobel derivative filters
imx = zeros(im.shape)
filters.sobel(im, 1, imx)
subplot(1, 4, 2)
axis('off')
title(u'(b)x方向差分', fontproperties=font)
imshow(imx)
imy = zeros(im.shape)
filters.sobel(im, 0, imy)
subplot(1, 4, 3)
axis('off')
title(u'(c)y方向差分', fontproperties=font)
imshow(imy)
mag = 255-numpy.sqrt(imx**2 + imy**2)
subplot(1, 4, 4)
title(u'(d)梯度幅度', fontproperties=font)
axis('off')
imshow(mag)
show()
结果截图:
4.3 图像降噪
图像降噪是一个在尽可能保持图像细节和结构信息时去除噪声的过程。我们采用Rudin-Osher-Fatemi de-noising(ROF)模型。图像去噪可以应用于很多场合,它涵盖了从你的度假照片使之更好看到卫星照片质量提高。
下面显示图像降噪的代码例子:
# -*- coding: utf-8 -*-
from PCV.tools import rof
from PIL import Image
from pylab import *
from scipy.ndimage import filters
""" This is the de-noising example using ROF in Section 1.5. """
# 添加中文字体支持
from matplotlib.font_manager import FontProperties
font = FontProperties(fname=r"c:\windows\fonts\SimSun.ttc", size=14)
im = array(Image.open('C:/Users/asus/Pictures/window/11.jpg').convert('L'))
U,T = rof.denoise(im,im)
G = filters.gaussian_filter(im, 10)
# save the result
# imsave('synth_original.pdf',im)
# imsave('synth_rof.pdf',U)
# imsave('synth_gaussian.pdf',G)
# plot
figure()
gray()
subplot(1, 3, 1)
imshow(im)
# axis('equal')
axis('off')
title(u'原噪声图像', fontproperties=font)
subplot(1, 3, 2)
imshow(G)
# axis('equal')
axis('off')
title(u'高斯模糊后的图像', fontproperties=font)
subplot(1, 3, 3)
imshow(U)
# axis('equal')
axis('off')
title(u'ROF降噪后的图像', fontproperties=font)
show()
结果截图:
4.4 有用的SciPy模块
SciPy有一些用于输入和输出数据有用的模块,其中两个是io和misc。
读写.mat文件
如果你有一些数据存储在Matlab .mat文件中,可以用scipy.io模块读取:
data = scipy.io.loadmat('test.mat')
如果要保存到.mat文件中的话,同样也很容易,仅仅只需要创建一个字典,字典中即可保存你想保存的所有变量,然后用savemat()方法即可:
#创建字典
data = {}
#将变量x保存在字典中
data['x'] = x
scipy.io.savemat('test.mat',data)
保存数组为图像
在scipy.misc模块中,包含了imsave()函数,要保存数组为一幅图像,可通过下面方式完成:
from scipy.misc import imsave
imsave('test.jpg',im)
scipy.misc模块中还包含了著名的"Lena"测试图像:
lena = scipy.misc.lena()
上面得到的lena图像是一幅512*512大小的灰度图像。