1 PIL
PIL提供通用的图像处理功能以及大量有用的基本图像操作,比如图像缩放,裁剪,旋转,颜色转换等。
PIL中最重要的模块为Image。
下面为基本图像操作函数。用L代表图像
颜色转换为灰度图像:convert(‘L’)
读取图像:open(‘L’)
转换图像格式:save()
创建缩略图(接受一个元组参数,将图像转换为符合元组参数指定大小的缩略图。):thumbnail()
复制和粘贴图像区域(从一幅图像中裁剪指定区域):crop(‘L’)和paste()
调整尺寸(参数为一个元组,指定新图像的大小):resize()
旋转(使用逆时针方式表示旋转角度,参数为角度):rotate()
2 Matplotlib
这个库用于处理数学运算,绘制图表,在图像上绘制点,直线和曲线。
- 绘制图像,点和线
对于大多数计算机视觉应用,仅仅需要用几个绘图命令,用点和线来表示一些事物,比如兴趣点,对应以及检测出的物体。
from PIL import Image
from pylab import *
# 读取图像到数组中
im = array(Image.open('jimei.jpg'))
# 绘制图像
imshow(im)
# 一些点
x = [100,100,400,400]
y = [200,500,200,500]
# 使用红色星状标记绘制点
plot(x,y,'r*')
# 绘制连接前两个点的线
plot(x[:2],y[:2])
# 添加标题,显示绘制的图像
title('Plotting: "empire.jpg"')
axis('off')#用于使坐标轴不显示
show()
每个脚本中,只能调用一次show()命令。
- 图像轮廓和直方图
绘制轮廓需要对每个坐标的像素值施加一个阈值,首先需要将图像灰度化。
from PIL import Image
from pylab import *
# 读取图像到数组中
im = array(Image.open('jimei.jpg').convert('L'))
# 新建一个图像
figure()
# 不使用颜色信息
gray()
# 在原点的左上角显示轮廓图像
contour(im, origin='image')
axis('equal')
axis('off')
figure()
hist(im.flatten(),128)
show()
灰度图像的直方图使用hist()函数绘制。
- 交互式标注
用于使用户和某些应用交互,可以使用Pylab库中的ginput()函数实现交互式标记。
from PIL import Image
from pylab import *
im = array(Image.open('jimei.jpg'))
imshow(im)
print ('请选择三个点')
x = ginput(3)
print ('选择的点是:',x)
show()
3 Numpy
该工具包包含了数组对象以及线性代数函数。数组对象可以实现矩阵乘积,转置,解方程系统,向量乘积和归一化。为图像变形,对变化进行建模,图像分类,图像聚类等提供了基础。
- 图像数组表示
Numpy数组对象是多维的,可以用来表示向量,矩阵和图像。
from PIL import Image
from pylab import *
import numpy as np
im = array(Image.open('jimei.jpg'))
print (im.shape, im.dtype)
im = array(Image.open('jimei.jpg').convert('L'),'f')
print (im.shape, im.dtype)
该代码第一行输出原图像数组的大小(行,列,颜色通道)和数据类型。
第二行输出转化为灰度图像的大小和数据类型。
多个数组元素可以使用数组切片方式(以指定间隔下标访问该数组的元素值)访问。
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]) # 倒数第二行
- 灰度变换
from PIL import Image
from numpy import *
from matplotlib import pyplot as plt
im = array(Image.open('jimei.jpg').convert('L'))
im2 = 255 - im # 对图像进行反相处理
im3 = (100.0/255) * im + 100 # 将图像像素值变换到 100...200 区间
im4 = 255.0 * (im/255.0)**2 # 对图像像素值求平方后得到的图像
plt.subplot(131), plt.imshow(im2, plt.cm.gray)
plt.subplot(132), plt.imshow(im3, plt.cm.gray)
plt.subplot(133), plt.imshow(im4, plt.cm.gray)
plt.show()
下图为灰度图像反相处理,区间变换(将图像的像素值变换为100-200区间),求平方
-
图像缩放
使用resize()函数 -
直方图均衡化
直方图均衡化指将一幅图像的灰度直方图变平,使变换后的图像中每个灰度值的分布概率都相同,可以增强图像的对比度。 -
图像平均
减少图像噪声的一种方式
def compute_average(imlist):
""" 计算图像列表的平均图像"""
# 打开第一幅图像,将其存储在浮点型数组中
averageim = np.array(Image.open(imlist[0]), 'f')
for imname in imlist[1:]:
try:
averageim += np.array(Image.open(imname))
except:
print(imname + '...skipped')
averageim /= len(imlist)
# 返回uint8
# 类型的平均图像
return np.array(averageim, 'uint8')
- 图像的主成分分析(PCA)
PCA是一个有用的降维技巧。在使用尽可能少维数的前提下,尽量多保持训练数据的信息。可以使用Numpy类库中的flatten()方法进行变换。
通常使用SVD方法来计算主成分,但矩阵的维数很大时,SVD计算非常慢。
函数如下:
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
- pickle模块
pickle魔魁啊可以用于保存一些结果或者数据来方便后续使用,该模块可以接受几乎所有的Python对象,并且将其转换为字符串表示(称为封装)。从字符串表示中重构该对象(称为拆封)
# 保存均值和主成分数据
f = open('font_pca_modes.pkl', 'wb')
pickle.dump(immean,f)
pickle.dump(V,f)
f.close()
# 载入均值和主成分数据
f = open('font_pca_modes.pkl', 'rb')
immean = pickle.load(f)
V = pickle.load(f)
f.close()
4 Scipy
Scipy是建立再Numpy基础上,用于数值运算的工具包,可实现数值积分,优化,统计,信号处理以及图像处理。
4.1 图像模糊
图像的高斯模糊是经典的图像卷积例子,图像模糊是将图像和一个高斯核进行卷积操作。通常是其他图像处理操作的一部分,比如图像插值操作,兴趣点计算以及其他应用。
from PIL import Image
from numpy import *
from scipy.ndimage import filters
from matplotlib import pyplot as plt
im = array(Image.open('jimei.jpg').convert('L'))
im2 = filters.gaussian_filter(im,5)
plt.subplot(121), plt.imshow(im, plt.cm.gray), plt.title('origin'),
plt.subplot(122), plt.imshow(im2, plt.cm.gray), plt.title('blur'),
plt.show()
4.2 图像导数
强度的变化可以用灰度图像 I \boldsymbol{I} I (对于彩色图像, 通常对每个颜色通道分别计算导数) 的 x 和 y 方向导数 I x \boldsymbol{I}_{x} Ix 和 I y \boldsymbol{I}_{y} Iy 进行描述。
图像的梯度向量为 ∇ I = [ I x , I y ] T \nabla \boldsymbol{I}=\left[\boldsymbol{I}_{x}, \boldsymbol{I}_{y}\right]^{T} ∇I=[Ix,Iy]T 。梯度有两个重要的属性, 一是梯度的大小:
∣ ∇ I ∣ = I x 2 + I y 2 |\nabla \boldsymbol{I}|=\sqrt{\boldsymbol{I}_{x}^{2}+\boldsymbol{I}_{y}^{2}} ∣∇I∣=Ix2+Iy2
它描述了图像强度变化的强弱, 一是梯度的角度:
α = arctan 2 ( I y , I x ) \alpha=\arctan 2\left(\boldsymbol{I}_{y}, \boldsymbol{I}_{x}\right) α=arctan2(Iy,Ix)
描述了图像中在每个点(像素)上强度变化最大的方向。NumPy 中的 arctan 2 ( ) \arctan 2( ) arctan2()函数返回弧度表示的有符号角度, 角度的变化区间为 − π … π -\pi \ldots \pi −π…π 。
from PIL import Image
from numpy import *
from scipy.ndimage import filters
from matplotlib import pyplot as plt
im = array(Image.open('jimei.jpg').convert('L'))
# Sobel导数滤波器
imx = zeros(im.shape)
filters.sobel(im,1,imx)
imy = zeros(im.shape)
filters.sobel(im,0,imy)
magnitude = sqrt(imx**2+imy**2)
plt.rcParams['font.sans-serif'] = ['SimHei']
plt.subplot(131), plt.imshow(imx, plt.cm.gray), plt.title('x导数图像')
plt.subplot(132), plt.imshow(imy, plt.cm.gray), plt.title('y导数图像')
plt.subplot(133), plt.imshow(magnitude, plt.cm.gray), plt.title('梯度图像')
plt.show()
4.3 形态学:对象计数
形态学(或数学形态学)是度量和分析基本形状的图像处理方法的基本框架与集合形态学通常用于处理二值图像,但是也能够用于灰度图像。二值图像是指图像的每个像素只能取两个值,通常是0和1。二值图像通常是,在计算物体的数目,或者度量其大小时,对一幅图像进行阈值化后的结果。
scipy.ndimage中的morphology模块可以实现形态学操作。可以使用scipy.ndimage 中的 measurements模块来实现二值图像的计数和度量功能。
下面代码用于计算图像中对象的个数
from scipy.ndimage import measurements,morphology
# 载入图像,然后使用阈值化操作,以保证处理的图像为二值图像
im = array(Image.open('houses.png').convert('L'))
im = 1*(im<128)
labels, nbr_objects = measurements.label(im)
print "Number of objects:", nbr_objects
4.4 其他Scipy模块
- 读写.mat文件
可以使用scipy.io模块对.mat文件格式进行读取。 - 以图像形式保存数组
imsave()函数可以从scipy.misc模块中载入,可以将数组保存为图像文件。
5 图像去噪
图像去噪是再去除图像噪声的同时,尽可能保留图像细节和结构的处理计算,下面使用ROF去噪模型。
from matplotlib import pyplot as plt
from PIL import Image
from numpy import *
from numpy import random
from scipy.ndimage import filters
def denoise(img, U_init, tolerance=0.1, tau=0.125, K=100):
m, n = img.shape
U = U_init
px = img
py = img
error = 1
while (error > tolerance):
Uold = U
Gradx = roll(U, -1, axis=1) - U
Grady = roll(U, -1, axis=0) - U
pxNew = px + (tau / K) * Gradx
pyNew = py + (tau / K) * Grady
NormNew = maximum(1, sqrt(pxNew ** 2 + pyNew ** 2))
px = pxNew / NormNew
py = pyNew / NormNew
Rxpx = roll(px, 1, axis=1)
Rypy = roll(py, 1, axis=0)
DivP = (px - Rxpx) + (py - Rypy)
U = img + K * DivP
error = linalg.norm(U - Uold) / sqrt(m * n)
return U, img - U
img = Image.open('jimei.jpg').convert('L')
img = array(img)
U, T = denoise(img, img)
G = filters.gaussian_filter(img, 5)
plt.rcParams['font.sans-serif'] = ['SimHei']
plt.subplot(131), plt.imshow(img, plt.cm.gray), plt.title('原始图像'), plt.axis('off')
plt.subplot(132), plt.imshow(G, plt.cm.gray), plt.title('高斯模糊图像'), plt.axis('off')
plt.subplot(133), plt.imshow(U, plt.cm.gray), plt.title('ROF去噪后图像'), plt.axis('off')
plt.show()