PIL(Python Imaging Libarary Python,图像处理类库)提供了通用的图像处理功能,以及大量有用的基本图像处理操作,比如图像的缩放,裁剪,旋转,颜色变换等。
· 利用PIL(Python图像处理类库),从PIL中导入图像
from PIL import Image
· PIL的 open() 函数用于创建PIL图像对象。
pil_im=Image.open('C:\\experiment\\test1.jpg')
· save() 方法用于保存图像到具有指定文件名的文件。
tuxiang=pil_im.save('C:\\experiment\\test1.png','png')
· convert() 方法可以实现图像颜色的转换
pil_im=Image.open('C:\\experiment\\test1.jpg').convert('L')
· thumbnail() 方法接受一个元组参数,然后将图像转换成符合元组参数指定大小的缩略图
pil_im.thumbnail((128,128))
· crop() 方法可以从一幅图像中裁剪指定区域:
box=(100,100,400,400)
region=pil_im.crop(box)
· paste() 方法可以将该区域放回去,还能使用tranpose 旋转代码中获取的区域
region=region.transpose(Image.ROTATE_180)
pil_im.paste(region,box)
· **resize()**方法可以调整一幅图像的尺寸,使用元组参数,指定新图像的大小。调用rotate()方法,可以使用逆时针方式表示旋转角度来旋转图像。
out=pil_im.resize(128,128)
基于python的PIL库学习,综合实例
from PIL import Image
>>> im=Image.open('C:\\experiment\\test7.jpg')
>>> im=Image.open('C:\\experiment\\test7.jpg').convert('L')
>>> box=(100,100,200,200)
>>> region=im.crop(box)
>>> region=region.transpose(Image.ROTATE_180)
>>> im.paste(region,box)
>>> im.show()
结果如下:
原始图像
转换颜色
将box区域进行裁剪,再旋转180 后放入图像中
实验总结:
在截取图像时,box的取值很重要,取值错误,会报错:SystemError: tile cannot extend outside image box四元组的坐标依次是(left, top, left+width, top+height)。其他函数实验过程较为简单,直接输入函数,参数即可。
Matplotlib具有比PIL更强大的绘图功能,优势在于可以绘制出较好的饼状图,条形图,散点图,点和直线曲线。
from PIL import Image
>>> from pylab import *
>>> im = array(Image.open('C:\\experiment\\test1.jpg'))
>>> imshow(im)
>>> x=[100,100,200,200]
>>> y=[200,300,200,300]
>>> plot(x,y,'r*')
[]
>>> plot(x[:2],y[:2])
[]
>>> show()
实验分析:
实验内容:代码首先绘制出原始图像,然后在给定点坐标上绘制红色星状标记点,然后连线。
关于show(),show()命令首先打开图形用户界面,然后新建一个图像窗口,该GUI界面会循环阻断脚本,然后暂停,在每个脚本里,只能调用一次show(),通常结尾处使用。
绘制轮廓需要对每个坐标[x,y]的像素值施加同一个阈值,所以首先将其灰度化。
from PIL import Image
>>> from pylab import *
>>> im=array(Image.open('C:\\experiment\\test8.jpg').convert('L'))
>>> figure()
图像轮廓
图像直方图
使用hist () 函数来绘制直方图。图像的直方图来表征图像像素值的分布情况。用一定数目的小区间来指定表征像素值的范围,每个小区间会得到落入该小区间表示范围的像素数目。
有时用户需要和某些应用交互,例如在一幅图像中标记一些点,或者标注一些训练数据。pylab库中,ginput()函数可以实现交互式标注。
from PIL import Image
from pylab import *
im=im=array(Image.open('C:\\experiment\\test8.jpg'))
imshow(im)
print'please click 3 point'
x=ginput(3)
print'you click:',x
show()
上面的脚本首先绘制一幅图像,然后等待用户在绘图窗口空间点击三次。程序将这些点的坐标自动保存在X列表。
NumPy中的数组对象是多维的,可以用来表示向量,矩阵和图像。一个数组对象很像一个列表,但是数组中所有的元素必须有相同的数据类型。
from PIL import Image
>>> from pylab import *
>>> im=array(Image.open('C:\\experiment\\test8.jpg'))
>im=array(Image.open('C:\\experiment\\test8.jpg').convert('L'),'f')
得到的结果
(271L, 400L) uint8
(271L, 400L) float32
实验分析:每行的第一个元组表示图像数组的大小(行、列、颜色通道),紧接着的字符串表 示数组元素的数据类型。因为图像通常被编码成无符号八位整数(uint8),所以在第一种情况下,载入图像并将其转换到数组中,数组的数据类型为“uint8”。在第二种情况下,对图像进行灰度化处理,并且在创建数组时使用额外的参数“f”;该参数将数据类型转换为浮点型。
将图像读入NumPy数组对象后,可以对他们执行任意数学操作,一个简单的例子就是灰度变换。考虑任意函数,输出区间映射到自身,即输出区间与输入区间范围相同。
from PIL import Image
from numpy import *
im = array(Image.open("").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())
直方图均衡化是指将一幅图像的灰度直方图变平,使变换后的图像中每个灰度值的分布概率都相同。在对图像做进一步处理之前,直方图均衡化通常是对图像灰度值进行归一化的一个非常好 的方法,并且可以增强图像的对比度。
from PIL import Image
>>> from numpy import *
>>> from pylab import *
>>> def histeq(im,nbr_bins=256):
imhist,bins=histogram(im.flatten(),nbr_bins,normed=True)
cdf=imhist.cumsum()
cdf=255*cdf / cdf[-1]
im2=interp(im.flatten(),bins[:-1],cdf)
return im2.reshape(im.shape),cdf
>>> im1 = Image.open('C:\\experiment\\jianjiji.jpg')
>>> im_gray = im1.convert('L')
>>> im = array(im1,'f')
>>> arr = im.flatten()
>>> figure()
>>> hist(arr,256)
>im2,cdf = histeq(im)
> figure()
>>> hist(im2.flatten(),256)
>show()
图像平均操作是减少图像噪声的以一种简单方式,通常用于艺术特效。假设图像具有相同的大小,可以将图像简单相加,然后除以图像的数目,来计算平均图像。
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() 函数需要将所有的图像堆积到一个数组中;也就是说,如果有很多图像,该处理方式需要占用很多内存。
PCA(Principal Component Analysis,主成分分析)是一个非常有用的降维技巧。它可以在使用尽可能少维数的前提下,尽量多地保持训练数据地信息。PCA产生地投影矩阵可以被视为将原始坐标变换到现有坐标系,坐标系中的各个坐标按照重要性递减排列。
为了对图像数据进行PCA变换,图像需要转换成一维向量表示。可以使用NumPy类库中的flatten()方法进行变换。
将变平的图像堆积起来,得到一个矩阵,矩阵的一行表示一幅图像。在计算主方向之前,所有的行图像按照平均图像进行了中心化。
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
pickle模块可以接受几乎所有的python对象,并且将其转换成字符串表示,这个过程叫做封装。从字符串表示中重构该对象,叫做拆封。这些字符串表示可以方便地存储和传输。多个对象可以保存在同一个文件中,pickle模块中由很多不同的协议可以生成.pkl文件。在其他Python会话中载入数据,需要使用函数load()即可。
#保存均值和主成分数据
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()
savetxt('test.txt',x,'%i') #保存一个数组x到文件里
x=loadtxt('test.txt') #读取数组x
# 打开文件并保存
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)
from PIL import Image
from numpy import *
from scipy.ndimage import filters
im=array(Image.open('').convert('L'))
im2=filters.gaussian_filter(im,5) #"5"参数位标准差
随着标准差的增加,图像被模糊的程度越大,细节丢失越多。
如果模糊一幅彩色图像,只需要简单地对每个颜色通道进行高斯模糊。
im=array(Image.open())
im2=zeros(im.shape)
for i in rang(3):
im2[:,:,i]=filters.gaussian_filter(im[:,:i],5)
强度的变化可以用灰度图像I(对于彩色图像,通常对每个颜色通道分别计算导数)的x 和 y方向导数Ix和Iy进行描述。
图像的梯度向量为:
梯度有两个重要的属性,一是梯度的大小:
它描述了图像强度变化的强弱,一是梯度的角度:
描述了图像中在每个点(像素)上强度变化最大的方向。NumPy 中的 arctan2() 函数 返回弧度表示的有符号角度,角度的变化区间为 -π…π。
我们可以用离散近似的方式来计算图像的导数。图像导数大多数可以通过卷积简单地实现:
对于 Dx 和 Dy,可以选择 Sobel 滤波器:
这些导数滤波器可以使用scipy.ndimage.filters 模块地标准军及操作来简单实现。
from PIL import Image
from numpy import *
from scipy.ndimage import filters
im = array(Image.open('empire.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)
形态学(或数学形态学)是度量和分析基本形状的图像处理方法的基本框架与集合。 形态学通常用于处理二值图像,但是也能够用于灰度图像。二值图像是指图像的每 个像素只能取两个值,通常是 0 和 1。二值图像通常是,在计算物体的数目,或者度量其大小时,对一幅图像进行阈值化后的结果。
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
# 形态学开操作更好地分离各个对象
im_open = morphology.binary_opening(im,ones((9,5)),iterations=2)
labels_open, nbr_objects_open = measurements.label(im_open)
print "Number of objects:", nbr_objects_open
图像去噪是在去除图像噪声的同时,尽可能地保留图像细节和结构的处理技术。这里使用ROF (Rudin-Osher-Fatemi)去噪模型。ROF 模型具有很好的性质:使处理后的图像更平滑,同时保持图像边缘和结构信息。
ROF模型:
一幅灰度图像I的全变差(TV)定义为梯度范数之和。在连续表示的情况下,全变差表示为:
在离散表示的情况下,全变差表示为:
其中,上面的式子是在所有图像坐标x=[x,y]上取和。在Chambolle提出的ROF模型里,目标函数为寻找降噪后的图像U,使下式最小:
其中范数|| I-U ||是去噪后图像U和原始图像I差异的度量。本质上该模型使去噪后的图像像素值“平坦”变化,但在图像区域的边缘上,允许去噪后的图像像素值“跳跃”变化。
from numpy import *
def denoise(im,U_init,tolerance=0.1,tau=0.125,tv_weight=100):
""" 使用A. Chambolle(2005)在公式(11)中的计算步骤实现Rudin-Osher-Fatemi(ROF)去噪模型
输入:含有噪声的输入图像(灰度图像)、U 的初始值、TV 正则项权值、步长、停业条件
输出:去噪和去除纹理后的图像、纹理残留"""
m,n = im.shape # 噪声图像的大小
# 初始化
U = U_init
Px = im # 对偶域的x 分量
Py = im # 对偶域的y 分量
error = 1
while (error > tolerance):
Uold = U
# 原始变量的梯度
GradUx = roll(U,-1,axis=1)-U # 变量U 梯度的x 分量
GradUy = roll(U,-1,axis=0)-U # 变量U 梯度的y 分量
# 更新对偶变量
PxNew = Px + (tau/tv_weight)*GradUx
PyNew = Py + (tau/tv_weight)*GradUy
NormNew = maximum(1,sqrt(PxNew**2+PyNew**2))
Px = PxNew/NormNew # 更新x 分量(对偶)
Py = PyNew/NormNew # 更新y 分量(对偶)
# 更新原始变量
RxPx = roll(Px,1,axis=1) # 对x 分量进行向右x 轴平移
RyPy = roll(Py,1,axis=0) # 对y 分量进行向右y 轴平移
DivP = (Px-RxPx)+(Py-RyPy) # 对偶域的散度
U = im + tv_weight*DivP # 更新原始变量
# 更新误差
error = linalg.norm(U-Uold)/sqrt(n*m);
return U,im-U # 去噪后的图像和纹理残余
from numpy import *
from numpy import random
from scipy.ndimage import filters
import rof
# 使用噪声创建合成图像
im = zeros((500,500))
im[100:400,100:400] = 128
im[200:300,200:300] = 255
im = im + 30*random.standard_normal((500,500))
U,T = rof.denoise(im,im)
G = filters.gaussian_filter(im,10)
# 保存生成结果
from scipy.misc import imsave
imsave('synth_rof.pdf',U)
imsave('synth_gaussian.pdf',G)
show()