目录
一、PIL:Python图像处理类库
PIL在python中提供了通用的图像处理功能,以及大量有用的基本图像操作,在平时的python图像操作中使用十分广泛。
例如,可以通过PIL中的函数从大多数的图像文件格式的文件里读取数据,并写入最常见的图像格式文件中,这里就要使用到模块Image了。示例如下:
from PIL import Image
import matplotlib.pyplot as plt
re_image = Image.open('D:\\picture\\tupian.jpg')
plt.imshow(re_image), plt.title('re_image')
plt.show()
re_image 得到的是一个PIL图像对象,并通过matplotlib库进行图像输出,得到如下图像。
对图像的颜色进行转换可以使用convert()方法实现。
convert()方法对颜色转换有多种模式,这里介绍一下L模式,RGB模式和1模式
# 利用Image模块读取一幅图像
from PIL import Image
import matplotlib.pyplot as plt
re_image = Image.open('D:\\picture\\test5.jpg')
# 利用convert()方法进行图像颜色的转换
# L模式:转化为为灰色图像,每个像素用8个bit表示,0表示黑,255表示白,0~255代表不同的灰度。需要注意的是,在PIL中,RGB是通过以下公式转化为L的:
# L = R * 299/1000 + G * 587/1000 + B * 114/1000
co_image1 = Image.open('D:\\picture\\test5.jpg').convert("L")
# RGB模式:RGB色彩模式是工业界的一种颜色标准,是通过对红(R)、绿(G)、蓝(B)三个颜色通道的变化以及它们相互之间的叠加来得到各式各样的颜色的,每个通道的值在0~255之间。
co_image2 = Image.open('D:\\picture\\test5.jpg').convert("RGB")
# 1模式,转化为为二值图像,非黑即白,每个像素用8个bit表示,0表示黑,255表示白。
co_image3 = Image.open('D:\\picture\\test5.jpg').convert("1")
plt.subplot(221)
plt.imshow(re_image), plt.title('or_image')
plt.subplot(222)
plt.imshow(co_image1), plt.title('image_L')
plt.subplot(223)
plt.imshow(co_image2), plt.title('image_RGB')
plt.subplot(224)
plt.imshow(co_image3), plt.title('image_1')
plt.show()
这里可以看出,在使用convert()方法后,图片的颜色发生了明显的变化。
1.1 转化图像格式
可以通过save()方法将图像保存成多种格式的文件。
这里的get_imlist()方法返回的是目录中所有的jgp图像的文件名列表,
# 使用save()方法转换图像格式
from PIL import Image
import os
from imtools import get_imlist
filelist = get_imlist("D:\\picture1\\")
for infile in filelist: # 遍历整个文件夹查找出路径中符合条件的图像文件
outfile = os.path.splitext(infile)[0] + '.png' # 对原图像文件的格式进行修改
if infile !=outfile: # 判断是否修改成功,成功则进行save()方法
try:
Image.open(infile).save(outfile)
except IOError:
print("cannot convert",infile)
亦或者可以直接使用save()方法,进行图片格式的转换
from PIL import Image
or_image = Image.open("D:\\picture1\\num.jpg")
re_image = or_image.save("D:\\picture1\\num.jpeg")
1.2 创建缩略图
为创建图片的缩略图,可以使用thumbnail()方法,令其接受一个元组参数以指定缩略图的大小,并将原图转化为符合元组参数指定大小的缩略图。
from PIL import Image
from numpy import *
from pylab import *
op_image = Image.open('D:\\picture\\tupian.jpg')
subplot(121)
imshow(op_image), title("original") # 输出原图
op_image.thumbnail((128, 128)) #图像缩略处理,指定大小为128*128
subplot(122)
imshow(op_image), title("result")
show()
从图中可以明显看出缩略后的图像其模糊度发生了明显的改变。
1.3 复制和粘贴图像区域
使用crop()方法可以从一幅图像中裁剪指定的区域:
from PIL import Image
from numpy import *
from pylab import *
op_image = Image.open('D:\\picture\\tupian.jpg')
box = (50, 50, 400, 400) # 元组给出裁剪区域
re_img = op_image.crop(box) # 利用crop方法进行图片裁剪
subplot(121)
imshow(op_image), title("original")
subplot(122)
imshow(re_img), title("result")
show()
可以从图像中看出裁剪区域为(50,50,400,400)的输出图像。四元组的坐标为(左,上,右,下),当然也可旋转该区域并将其放回去。具体使用paste()方法。
from PIL import Image
from numpy import *
from pylab import *
op_image = Image.open('D:\\picture\\tupian.jpg')
box = (100, 100, 400, 400) # 元组给出裁剪区域
re_img = op_image.crop(box) # 利用crop方法进行图片裁剪
subplot(131)
imshow(op_image), title("original")
subplot(132)
imshow(re_img), title("result")
reverse = re_img.transpose(Image.ROTATE_180) # 裁剪区域旋转180度
op_image.paste(reverse,box) # 将旋转后的图片放回去
subplot(133)
imshow(op_image), title("re_result")
show()
若在最后阶段中的op_image.paste(reverse,box)中,未将box考虑进去,就会到至裁剪图像出现在图片的左上角,且单纯的覆盖在原图之上。
1.4 调整尺寸和旋转
调整一幅图像的大小需要使用resize()方法,其参数是一个元组,用以指定其大小。
from PIL import Image
from numpy import *
from pylab import *
size = (128,128)
or_image = Image.open("D:\\picture\\tupian.jpg")
# resize()用以调整图片的大小,参数为元组
out_image1 = or_image.resize(size)
# rotate()用以对图像进行旋转,参数为(0-360)
out_image2 = or_image.rotate(180)
subplot(131)
imshow(or_image), title('original')
subplot(132)
imshow(out_image1), title('resize')
subplot(133)
imshow(out_image2), title('rotate')
show()
根据输出图像,可以直观的看出调整后的图像的结构。
二、Matplotlib
Matplotlib是一个比PIL类库更加强大的类库,具有很强的绘图功能,其中的pylab接口包含了很多方便用户创建图像的函数。
2.1 绘制图像、点和线
在图像中绘制点,并实现点连成线:
# 通过点和线绘制图像
from PIL import Image
from pylab import *
# 将图像读取到数组中
img = array(Image.open('D:\\picture\\tupian.jpg'))
imshow(img)
x = [100, 100, 400, 400]
y = [200, 500, 200, 500]
# 根据给出的坐标绘制红色点
plot(x, y, "r*")
# 绘制连接4个点的线
plot(x[:4], y[:4])
title('plotting')
show()
在Pylab中约定左上角为坐标原点,若不想让坐标轴显示,可以使用axis("off")。当然,类库也提供了其它可供选择的图形:
plot(x, y) # 默认蓝色实线
plot(x, y, "go-") # 带有圆圈标记的绿线
plot(x, y, "ks:") # 带有正方形标记的黑色虚线
plot(x, y, "r*") # 红色星状标记
Pylab库绘图的基本颜色格式命令:
Pylab库绘图的基本线性格式命令:
Pylab库绘图的基本绘制标记格式命令:
2.2 图像轮廓和直方图
在绘制图像轮廓和直方图前需要对图像进行灰度化,因为需要对每个坐标的像素值设定一个阈值。
from PIL import Image
from pylab import *
# 读取图像至数组中,并将其转为灰度图像
im = array(Image.open('D:\\picture\\tupian.jpg').convert('L'))
re_im = Image.open('D:\\picture\\tupian.jpg')
subplot(131)
imshow(re_im), title('original')
subplot(132)
gray() # 不使用颜色信息
contour(im, origin='image') # 在原点的左上角显示轮廓图像
axis('off')
axis('equal')
subplot(133) # 直方图绘制
hist(im.flatten(), 128)
show()
hist()函数的第二个参数指定小区间的数目。由于hist只接受一维数组作为输入所以在绘制直方图前需要进行压平处理,fatten()方法可以将任意数组按照优先准则转换成一维数组。
2.3 交互式标注
用户需要和某些应用进行交互时,比如在一幅图像中标记一个点,或标注一些训练数据,可以使用ginput()函数实现交互式标注。
from PIL import Image
from pylab import *
im = array(Image.open('D:\\picture\\tupian.jpg'))
imshow(im)
while(True):
print("please click 3 points")
x = ginput(3)
print("you click:", x)
show()
在本实验中对图片进行点击执行框中会出现其方位具体信息,就如上图所示一般。
三、Numpy
Numpy是python中的科学计算工具包,包含了诸如数组对象、线性代数函数等重要数学公式,为图像变形、对变化进行建模、图像分类、图像聚类等提供了可靠的基础。
3.1 图像数组表示
上面的例子中,当要将图像转变了数组对象时都是通过调用array()方法实现的,在Numpy中数组对象是多维的,可以表示向量、矩阵以及图像,一个数组类似一个列表,但数组中的元素必须具有相同的数据类型,一般情况下,数组内的数据类型会按照元素的数据类型自动确定。
# 查看数组的数据类型
from PIL import Image
from pylab import *
from numpy import *
img0 = array(Image.open('D:\\picture\\tupian.jpg'))
print(img0.shape, img0.dtype)
img = array(Image.open('D:\\picture\\tupian.jpg').convert('RGB'))
# RGB
print(img.shape, img.dtype)
img1 = array(Image.open('D:\\picture\\tupian.jpg').convert('1'))
# 二值图像非0即1,故数据类型为bool型
print(img1.shape, img1.dtype)
img2 = array(Image.open('D:\\picture\\tupian.jpg').convert('L'))
# 灰度图,灰度值在0-255之间,且为设定其数据类型,array自动判定为uint8型
print(img2.shape, img2.dtype)
img3 = array(Image.open('D:\\picture\\tupian.jpg').convert('L'), 'f')
# 灰度图,‘f’设定其数据类型为float类型,故显示为float32
print(img3.shape, img3.dtype
array()方法的输出中元组代表着图像数组的大小(行、列、颜色通道),在上面的例子中,分别将图像转变成了二值图像和灰度图像以及RGB图像,图片相同其行列也相同,颜色通道则根据颜色转变成没有颜色信息和3通道。因为图像通常会被编码成无符号的八位整数uint8,因此在没有特殊设定的情况下数组的数据类型就为uint8。
同时,也可以通过下标对数组中的元素进行访问:
value = im[i,j,k] # 坐标位i,j;k代表图像的颜色通道。
多个数组元素可以通过使用数组切片的方式进行访问。切片方式返回的是以指定间隔下标访问该数组的元素值。
3.2 灰度变换
灰度变换的一些例子:
# 灰度变换
from PIL import Image
from pylab import *
from numpy import *
or_img = array(Image.open('D:\\picture\\tupian.jpg'))
# 灰度变换
gr_img = array(Image.open('D:\\picture\\tupian.jpg').convert('L'))
# 对灰度图进行反相处理
gr_img1 = 255 - gr_img
# 将图像的像素转变到100-200区间
gr_img2 = (100.0/255) * gr_img + 100
# 将灰度图像的像素求平方
gr_img3 = 255.0 * (gr_img/255.0)**2
subplot(231)
imshow(or_img), title('origin')
subplot(232)
imshow(gr_img), title('gray')
subplot(233)
imshow(gr_img1), title('gray_re')
subplot(234)
imshow(gr_img2), title('gray(100-200)')
subplot(235)
imshow(gr_img3), title('gray**2')
show()
3.3 直方图均衡化
直方图均衡化是指将一幅图像的灰度直方图变平,使得变换后的图像中每个灰度值的分布概率都相同。在进行进一步操作前需要对图像进行直方图均衡化,这是对图像灰度值进行归一化的一个非常好的方法,且可以增强图像的对比度。
这种情况下,直方图均衡化的变换函数是图像中像素值的累积分布函数。
from PIL import Image
from pylab import *
from numpy import *
from imtools import histeq
img = array(Image.open('D:\\picture\\tupian.jpg').convert('L'))
img2, cdf = histeq(img)
subplot(231)
hist(img.flatten(), 128)
subplot(234)
imshow(img), title('gray')
subplot(233)
hist(img2.flatten(), 128)
subplot(232)
plot(cdf, 'b-')
subplot(236)
imshow(img2), title('gray_ch')
show()
上图就是直方图均衡化例子的结果。可以看出在均衡化后的图片对比度愈发显著。
3.4 图像平均
图像平均是减少噪声干扰的一种简单方式,假设所有图像都具有相同大小,就可以通过将图像简单的相加,并除以总图像的数目,以此来计算平均图像。函数代码如下:
def com_avg(imlist):
"""
计算图像列表的平均图像
:param imlist:
:return:
"""
# 打开第一个图像,并将其存储在浮点型数组中
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()函数计算平均图像,这需要将所有的图像堆积到一个数组中。
3.5 图像成分分析(PCA)
PCA是一个非常有用的降维技巧,可以在使用尽可能少的维数下,尽量多的保持训练数据的信息。进行PCA变换前,图像需要转换成一维向量表示,可以使用flatten()方法。对于一幅100*100像素的小灰度图像,有10000维,可以看出10000维空间中的一个点。由于图像具有很高的维数,在许多计算机视觉应用中,需要使用到降维操作。将变平的图像堆积起来,就可以得到一个矩阵,矩阵的一行就代表一幅图像。
PCA操作代码如下:
def pca(x):
"""
:param x: 矩阵x,存储训练数据,每一行为一个训练数据
:return: 投影矩阵、方差和均值
"""
# 获取维数
num_data, dim = x.shape
# 数据中心化
mean_x = x.mean(axis=0)
x = x-mean_x
if dim>num_data:
# 协方差矩阵
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] /= 5
else:
U, S, V = linalg.svd(x)
V = V[:num_data]
return V, S, mean_x
该函数首先通过减去每一维的均值将数据中心化,然后计算协方差矩阵对应最大特征值的特征向量,此时就可以使用简明的技巧了。当数据小于向量维数,不用SVD分解,而是计算维数更小的协方差矩阵的特征向量。
四、 Scipy
Scipy是建立在Numpy基础之上的,是用于数值运算的开源工具包。可以实现数值积分、优化、统计、信号处理以及图像处理功能。
4.1 图像模糊
图像的高斯模糊,是图像卷积的经典例子,本质上就是将灰度图与一个高斯核进行卷积操作:
这里的就代表这卷积操作,
是标准差为
的二维高斯核,可将其定义为:
在Scipy中有用来滤波的scipy.ndimage.fliters模块,可以用快速一维分离的方式来计算卷积,如下所示:
# 高斯模糊处理
from PIL import Image
from numpy import *
from pylab import *
from scipy.ndimage import filters
# 灰度图像的模糊处理
img = array(Image.open('D:\\picture\\tupian.jpg').convert('L'))
img_re0 = filters.gaussian_filter(img, 2)
img_re1 = filters.gaussian_filter(img, 5)
img_re2 = filters.gaussian_filter(img, 10)
# 彩色图像的模糊处理
img_or = array(Image.open('D:\\picture\\tupian.jpg'))
img_or2 = zeros(img_or.shape)
for i in range(3):
img_or2[:, :, i] = filters.gaussian_filter(img_or[:, :, i], 5)
img_or2 = uint8(img_or2)
subplot(231)
imshow(img_or), title('original')
subplot(233)
imshow(img_or2), title('rgb——5')
subplot(234)
imshow(img_re1), title('gray——2')
subplot(235)
imshow(img_re2), title('gray——5')
subplot(236)
imshow(img_re2), title('gray——10')
show()
上图分别显示了灰度图像和彩色图像在高斯模糊下的情形,可以看出当越大时,图像的模糊度也高。
4.2 图像导数
图像的强度变换是非常重要的信息,强度的变化可以通过灰度图像I的x,y方向的方向导数进行描述。
一幅图像的梯度向量为,我们知道梯度的两个重要属性一个是其大小,另一个是其角度。
大小描述了图像强度变化的强弱,可以写为:
角度描述了图像中在每个像素上强度变化最大的方向。可以写为:
在数字图像处理中一般使用近似的方式达成导数,这里就使用了离散近似的方法。因为图像导数大多数可以通过卷积来实现:
对于,通常就选择Prewitt滤波器进行处理:
亦或者可以选用Sobel滤波器:
这些导数滤波器都可以通过scipy.ndimage.fliters模块的标准卷积操作加以实现。
from PIL import Image
from numpy import *
from pylab import *
from scipy.ndimage import filters
img = array(Image.open('D:\\picture\\tupian.jpg').convert('L'))
# Sobel滤波器
img_x = zeros(img.shape)
imgX = filters.sobel(img, 1, img_x)
img_y = zeros(img.shape)
imgY = filters.sobel(img, 0, img_y)
# 梯度
magnitude = sqrt(img_x**2+img_y**2)
subplot(221)
imshow(img, cmap='gray'), title('original')
subplot(222)
imshow(imgX, cmap='gray'), title('x')
subplot(223)
imshow(imgY, cmap='gray'), title('y')
subplot(224)
imshow(magnitude, cmap='gray'), title('magnitude')
show()
当然上面计算图像的方法还是有些缺陷,滤波器的尺度要随着图像的分辨率的变化而变化,为了在图像噪声方面更加稳健,可以使用高斯导数滤波器:
表示
在x和y方向上的导数。
# 高斯导数滤波器
from PIL import Image
from pylab import *
from numpy import *
from scipy.ndimage import filters
img_or = array(Image.open('D:\\picture\\tupian.jpg').convert('L'))
gray()
def gaussian(img, sigma):
img_x = zeros(img.shape)
imgx = filters.gaussian_filter(img, (sigma, sigma),(0, 1), img_x)
img_y = zeros(img.shape)
imgy = filters.gaussian_filter(img, (sigma, sigma),(1, 0), img_y)
magnitude = sqrt(img_x ** 2 + img_y ** 2)
return imgx, imgy, magnitude
imgx1, imgy1, magnitude1 = gaussian(img_or, 2)
imgx2, imgy2, magnitude2 = gaussian(img_or, 5)
imgx3, imgy3, magnitude3 = gaussian(img_or, 10)
subplot(341)
imshow(img_or), title('a'), axis('off')
subplot(342)
imshow(imgx1), title('b'), axis('off')
subplot(343)
imshow(imgx2), title('c'), axis('off')
subplot(344)
imshow(imgx3), title('d'), axis('off')
subplot(345)
imshow(img_or), title(''), axis('off')
subplot(346)
imshow(imgy1), title(''), axis('off')
subplot(347)
imshow(imgy2), title(''), axis('off')
subplot(348)
imshow(imgy3), title(''), axis('off')
subplot(349)
imshow(img_or), title(''), axis('off')
subplot(3, 4, 10)
imshow(magnitude1), title(''), axis('off')
subplot(3, 4, 11)
imshow(magnitude2), title(''), axis('off')
subplot(3, 4, 12)
imshow(magnitude3), title(''), axis('off')
show()
a列为原图,b列为在为2下的x,y和梯度图像,c为
=5时,d为
=10时。
4.3 形态学:对象计数
形态学(或数学形态学)是度量和分析基本形状的图像处理方法的基本框架与集合。形态学通常用于处理二值图像,但是也能够用于灰度图像。二值图像是指图像的每个像素只能取两个值,通常是0和1。二值图像通常是,在计算物体的数目,或者度量其大小时,对一幅图像进行阈值化后的结果。
scipy.ndimage中的morphology模块可以实现形态学操作,measurements模块可以实现二值图像的计数和度量功能。
from PIL import Image
from pylab import *
from numpy import *
from scipy.ndimage import measurements, morphology
img = array(Image.open('D:\\picture1\\num.jpg').convert('L'))
gray()
subplot(221)
imshow(img), title('original')
img = 1*(img < 128)
labels, nbr_objects = measurements.label(img)
print("number of objects:", nbr_objects)
subplot(222)
imshow(labels), title('the picture after label')
img_open = morphology.binary_opening(img, ones((9, 5)), iterations=2)
subplot(223)
imshow(img_open), title('open')
labels_open, nbr_objects_open = measurements.label(img_open)
print("number of objects:",nbr_objects_open)
subplot(224)
imshow(labels_open), title('result')
show()
pycharm显示框中的结果为:number of objects: 615;number of objects: 51
4.4 有用的Scipy模块
1、读写.mat文件,当数据以Matlab的.mat文件格式存储时,可以用scipy.io模块进行读取
data - scipy.io.loadmat('test.mat')
data对象中包含有一个字典,字典的键对应于保存在原始.mat文件中的变量名,当需要对.mat文件进行存储时,使用savemat()函数。
data = {}
data['x'] = x
scipy.io.savemat('test.mat',data)
2、imsave()函数 因为我们需要对图像进行操作,并且需要使用数组对象来做运算,所以将数组直接保存为图像文件非常有用。可以从scipy.misc模块中载入:
from scipy.misc import imsave
imsave('test.jpg',im)
五、图像去噪
ROF模型去噪:
from PIL import Image
from pylab import *
from numpy import *
from scipy.ndimage import filters
from rof import denoise
img = array(Image.open('D:\\picture\\tupian.jpg').convert('L'))
gray()
U, T = denoise(img, img)
G = filters.gaussian_filter(img, 10)
subplot(131)
imshow(img), title('original')
axis('off')
subplot(132)
imshow(U), title('rof')
axis('off')
subplot(133)
imshow(G), title('gaussian')
axis('off')
show()
其中denoise方法为:
def denoise(im, U_init, tolerance=0.1, tau=0.125, tv_weight=100):
"""
rof模型去噪
:param im:
:param U_init:
:param tolerance:
:param tau:
:param tv_weight:
:return:
"""
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
Py = PyNew / NormNew
# 更新原始变量
RxPx = roll(Px, 1, axis=1)
RyPy = roll(Py, 1, axis=0)
Divp = (Px - RxPx) + (Py - RyPy)
U = im + tv_weight * Divp
# 更新误差
error = linalg.norm(U-Uold)/sqrt(n*m)
return U, im-U
可以看出,经过rof模型去杂牌后的图像很好的保留了图像的边缘信息。
六、总结
本次学习中主要就python实现基础的图像处理操作进行了研究,在进行图像灰度处理时,可以看到,前一部分的输出灰度图像均是颜色较为鲜艳的,并不是传统意义上的灰度图像,后面的实验中就加以改进了,颜色变成灰色的图像里,处理代码时,输出图像时,用到了cmap方法,这是因为imshow()在输出图像数据时旨在科学的表示数据,而不只是图像数据,因此默认选用了对比度较高的调色板,当我们要输出灰色时,就可以采用cmap强制使用灰度显示数据,亦或者,在图像进行灰度转化后使用gray()方法,使得图像的映射颜色为灰色。