python计算机视觉编程——1.基本的图像操作和处理
- 1.基本的图像操作和处理
- 1.1 PIL:Python图像处理类库
- 1.1.1 转换图像格式
- 1.1.2 创建缩略图
- 1.1.3 复制和粘贴图像区域
- 1.1.4 调整尺寸和旋转
- 1.2 Matplotlib
- 1.2.1绘制图像、点和线
- 1.2.2 图像轮廓和直方图
- 1.3 Numpy
- 1.3.1 图像数组表示
- 1.3.2灰度变换
- 1.3.3 图像缩放
- 1.3.4 直方图均衡化
- 1.3.5 图像平均
- 1.3.6 图像的主成分分析(PCA)
- 1.3.7 使用pickle模块
- 1.4 SciPy
- 1.4.1 图像模糊
- 1.4.2 图像导数
- Sobel导数滤波器
- 高斯滤波器
- 1.4.3 形态学:对象计数
- 1.5高级示例:图像去噪
1.基本的图像操作和处理
1.1 PIL:Python图像处理类库
from PIL import Image
pil_im=Image.open('sun.jpg')
imshow(pil_im)
- convert参数有三个值
- L:转换为8bit灰度图像
- RGB:3×8像素图像
- CMYK:4×8像素图像
pil_im=Image.open('sun.jpg').convert('L')
plt.imshow(pil_im)
还可以给定转换矩阵,将RGB图像转换为CIE XYZ色彩空间
rgb2xyz = (
0.412453, 0.357580, 0.180423, 0,
0.212671, 0.715160, 0.072169, 0,
0.019334, 0.119193, 0.950227, 0)
pil_im = Image.open('sun.jpg').convert("RGB", rgb2xyz)
plt.imshow(pil_im)
1.1.1 转换图像格式
from PIL import Image
import os
filelist=['sun.jpg']
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)
可以返回目录中所有jpg图像的列表
def get_imlist(path):
return [os.path.join(path,f) for f in os.listdir(path) if f.endswith('.JPG')]
get_imlist(r'D:\20计本二班照片')
1.1.2 创建缩略图
from PIL import Image
import matplotlib.pyplot as plt
import numpy as np
pil_im=Image.open('sun.jpg')
pil_im.thumbnail((128,128))
plt.imshow(pil_im)
1.1.3 复制和粘贴图像区域
from PIL import Image
import matplotlib.pyplot as plt
pil_im=Image.open('sun.jpg')
box=(200,200,974,974) # 四元组的坐标依次是(左,上,右,下),表示从左上裁切到右下
region=pil_im.crop(box)
plt.imshow(region)
region=region.transpose(Image.ROTATE_180) #将图片旋转180°
pil_im.paste(region,box)
plt.imshow(region)
1.1.4 调整尺寸和旋转
from PIL import Image
import matplotlib.pyplot as plt
pil_im=Image.open('sun.jpg')
out=pil_im.resize((256,256))
plt.subplot(121)
plt.imshow(out)
out=pil_im.rotate(45)
plt.subplot(122)
plt.imshow(out)
1.2 Matplotlib
1.2.1绘制图像、点和线
- 点的颜色(加粗为颜色简写):blue,green,red,cyan(青色),magenta(品红),yellow,black,white
- 线的类型:‘-’ 实线,'–‘虚线,’:'点线
- 点的形状:'.'点,'o’圆圈,‘s’正方形,’*‘星形,’+'加号,'x’叉号
from PIL import Image
from pylab import *
im=array(Image.open('sun.jpg'))#读取图像到数组中
imshow(im)
x=[200,100,1000,1000]
y=[800,500,200,500]
plot(x,y,'r*')
plot(x[:2],y[:2],':')
title('Plotting:"sun.jpg"')
1.2.2 图像轮廓和直方图
from PIL import Image
from pylab import *
im=array(Image.open('sun.jpg').convert('L'))
figure() # 保留图像显示,不进行覆盖
gray() # 不使用颜色信息
contour(im,origin='image') #设置origin='image'确保图像数据的原点在左上角
axis('equal')
axis('off')
图像像素值被分成128个区间,用于计算每个区间的像素值频率
figure()
hist(im.flatten(),128) #flatten()把二维图像数组展品为一维数组
show()
1.3 Numpy
1.3.1 图像数组表示
from PIL import Image
from pylab import *
im=array(Image.open('sun.jpg'))
print(im.shape,im.dtype)
im=array(Image.open('sun.jpg').convert('L'),'f') # 对图像进行灰度值处理后,并转换为浮点型数值
print(im.shape,im.dtype)
imshow(im)
im=array(Image.open('sun.jpg').convert('L'),'f')
im[100:200,:]=im[0:100,:] # 将100-200行所有元素替换成0-100行的所有元素
imshow(im)
im=array(Image.open('sun.jpg').convert('L'),'f')
im[0:100,:]=0# 将0-100行的所有数值设为0
imshow(im)
print(im[:100,:50].sum())#因为刚刚把值全部设为了0,所以求和完还是0
print('--------')
print(im[100:200,:50].sum())
print('--------')
print(im[50:150,50:100])
print('--------')
print(im[101].mean())#均值
1.3.2灰度变换
from PIL import Image
from numpy import *
im=array(Image.open('sun.jpg').convert('L'))
im2=255-im # 对图像进行反向处理
im3=(100.0/255)*im+100 # 将图像像素值变换到100-200区间,im/255为归一化,最后100.0*(im/255)化为100中所对应的数值
im4=255.0*(im/255.0)**2# 对图像像素值求平方后得到的图像,使较暗的像素值变得更小,明暗区别程度更大
subplot(221)
imshow(im)
subplot(222)
imshow(im2)
subplot(223)
imshow(im3)
subplot(224)
imshow(im4)
1.3.3 图像缩放
from PIL import Image
from numpy import *
def imresize(im,sz):
pil_im=Image.fromarray(uint8(im)) #把图像数组转换为图像
return array(pil_im.resize(sz))
im=array(Image.open('sun.jpg').convert('L'))
new_size = (400, 300)#(长、宽)
new_im=imresize(im,new_size)
imshow(new_im)
1.3.4 直方图均衡化
from PIL import Image
from numpy import *
import matplotlib.pyplot as plt
def histeq(im,nbr_bins=256):
#flatten()展平数组
# nbr_bins:指定区间数量,这里指定直方图需要划分为256个区间
# histogram函数返回两个参数
# 1.imhist:一个一维数组,表示每个区间中的数据频数。
# 2.bins:一个一维数组,表示直方图区间的边界值。
imhist,bins=histogram(im.flatten(),nbr_bins,density=True)
cdf=imhist.cumsum() # 计算累积分布函数 (CDF)
# cdf:累积分布函数值,表示像素值小于或等于某—灰度值的概率。
# cdf[-1]:累积分布函数的最大值。对于归一化的CDF,这个值通常是1;对于非归一化的CDF,这个值通常是数据的总数(像素的总数)。
cdf=255*cdf/cdf[-1]#将CDF的值映射到0到255的范围,以便用于灰度图像的均衡化。
# interp():用于执行一维线性插值。
# im.flatten():需要插值的点
# bins[:-1] 则是每个直方图区间的左边界(不包括最后一个边界)。表示每个区间的起始值,所以就是对应的横坐标
# cdf为对应的纵坐标
im2=interp(im.flatten(),bins[:-1],cdf)
return im2.reshape(im.shape),cdf,bins
im=array(Image.open('sun.jpg').convert('L'))
figure()
subplot(121)
#bins:将数据分成30个箱子(区间);alpha:设置图形的透明度(0 是完全透明,1 是完全不透明)
plt.hist(im, bins=30, alpha=0.5)
subplot(122)
imshow(im)
figure()
im2,cdf,bins=histeq(im)
# print(cdf)
plt.plot(bins[1:], cdf, marker='.', linestyle='none')
figure()
subplot(121)
plt.hist(im2, bins=30, alpha=0.5)
subplot(122)
imshow(im2)
1.3.5 图像平均
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)
return array(averageim,'uint8')
1.3.6 图像的主成分分析(PCA)
from PIL import Image
from numpy import *
from pylab import *
import os
import matplotlib.pyplot as plt
def pca(X):
# num_data:样本数量;dim:特征维数(列数)
# 2359×625:有2359张图片,因为每张图片为25×25,所以压平后的长度就是625
num_data,dim=X.shape
# mean_X:计算每个特征的均值,axis=0表示按列计算
mean_X=X.mean(axis=0) #矩阵大小:1×625
# 对数据进行中心化,即减去均值
X=X-mean_X
if dim>num_data: # 如果特征维数大于样本数量,使用协方差矩阵
M=dot(X,X.T) #计算协方差矩阵
# M=cov(X,rowvar=0) #等效计算协方差矩阵,rowvar=0表示每一列是一个变量,每一行是一个观测值(rowvar=1表示每一行是一个变量,每一列是一个观测值)
e,EV=linalg.eigh(M) #特征值分解,e是特征值,EV是特征向量
tmp=dot(X.T,EV).T #计算主成分
V=tmp[::-1] #对主成分进行排序(按特征值的降序)
S=sqrt(e)[::-1] #计算特征值的平方根,并进行排序
for i in range(V.shape[1]):#对V进行归一化,使其成为单位特征向量
V[:,i]/=S
else: # 否则使用奇异值分解(SVD)的方法
U,S,V=linalg.svd(X) #奇异值分解,U和V是左右奇异向量,S是奇异值
V=V[:num_data] #主成分,提取前num_data个主成分
return V,S,mean_X
- 当特征维数小于样本数量时,使用SVD分解
- 一个矩阵X,在本例中维数为2359×625,SVD 将其分解为:
X
=
[
U
S
V
T
]
{\rm X}=[{\rm USV^T} ]
X=[USVT]
其中:- U:一个2359×2359的正交矩阵,包含左奇异向量。
- S:一个2359×625 的对角矩阵,其中对角线上的值是奇异值,按降序排列。奇异值是矩阵X的平方根的特征值。
- V T {\rm V^T} VT:一个625×625的正交矩阵的转置,包含右奇异向量。
- 一个矩阵X,在本例中维数为2359×625,SVD 将其分解为:
X
=
[
U
S
V
T
]
{\rm X}=[{\rm USV^T} ]
X=[USVT]
# 可以返回目录中所有jpg图像的列表
def get_imlist(path):
return [os.path.join(path,f) for f in os.listdir(path) if f.endswith('.jpg')]
imlist=get_imlist(r'D:\pyFile\Python计算机视觉编程\data\fontimages\a_thumbs')
im=array(Image.open(imlist[0]))
m,n=im.shape#获取图像的大小 25×25
imnbr=len(imlist)#获取图像的数目
immatrix=array([array(Image.open(im)).flatten() for im in imlist],'f')#创建矩阵,保存所有压平后的图像数据
V,S,immean=pca(immatrix)
figure()
gray()
subplot(241)
imshow(immean.reshape(m,n))
axis('off')
for i in range(7):
subplot(2,4,i+2)
imshow(V[i].reshape(m,n))
axis('off')
show()
1.3.7 使用pickle模块
import pickle
f=open('font_pca_modes.pkl','wb')
pickle.dump(immean,f)
pickle.dump(V,f)
f.close()
等价于
with open('font_pca_modes.pkl','wb') as f:
pickle.dump(immean,f)
pickle.dump(V,f)
f=open('font_pca_modes.pkl','rb')
immean=pickle.load(f)
V=pickle.load(f)
f.close()
等价于
with open('font_pca_modes.pkl','rb') as f:
immean=pickle.load(f)
V=pickle.load(f)
1.4 SciPy
1.4.1 图像模糊
from PIL import Image
from numpy import *
from scipy.ndimage import filters
import matplotlib.pyplot as plt
im=array(Image.open('sun.jpg').convert('L'))
im2=filters.gaussian_filter(im,5)#标准差为5
plt.figure()
plt.imshow(im)
plt.figure()
plt.imshow(im2)
im=array(Image.open('sun.jpg'))
im2=zeros(im.shape)
for i in range(3):#彩色图像则是对三个通道分别进行模糊
im2[:,:,i]=filters.gaussian_filter(im[:,:,i],5)
im2=uint8(im2)
plt.imshow(im2)
1.4.2 图像导数
from PIL import Image
from numpy import *
from scipy.ndimage import filters
import matplotlib.pyplot as plt
Sobel导数滤波器
im=array(Image.open('sun.jpg').convert('L'))
plt.figure()
plt.imshow(im)
#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.figure()
plt.subplot(121)
plt.imshow(imx)
plt.subplot(122)
plt.imshow(imy)
plt.figure()
plt.imshow(magnitude)
高斯滤波器
- 高斯滤波器通过卷积操作将高斯函数应用于图像,从而实现平滑效果。在代码中,filters.gaussian_filter被用于计算图像的梯度。下面详细介绍函数的参数
- filters.gaussian_filter(im,(sigma,sigma),(0,1),imx)
- 第二个参数可以是一个标量或元组。它决定了滤波器的平滑程度。
- 如果是一个标量,表示在所有维度上使用相同的标准差;
- 如果是一个元组,可以为不同的维度指定不同的标准差。(sigma, sigma)表示在两个维度上都使用标准差 sigma。
- 第三个参数是指定对每个方向计算哪种类型的导数。在这里指:滤波器在x方向上应用,而在y方向上不应用。
- 第二个参数可以是一个标量或元组。它决定了滤波器的平滑程度。
def gaussianFilter(sigma):
plt.figure()
imx=zeros(im.shape) # 创建一个与输入图像im形状相同的零数组imx。这个数组将用于存储图像在x方向上的梯度信息。
filters.gaussian_filter(im,(sigma,sigma),(0,1),imx)
imy=zeros(im.shape)
filters.gaussian_filter(im,(sigma,sigma),(1,0),imy)
plt.title('sigma=%d Gaussian filter result' % sigma)
plt.axis('off')
plt.subplot(121)
plt.imshow(imx)
plt.subplot(122)
plt.imshow(imy)
gaussianFilter(2)
gaussianFilter(5)
gaussianFilter(10)
参数sigma为标准差(σ),是高斯滤波器的一个重要参数,它控制滤波器的平滑程度。标准差越大,高斯函数的曲线越宽,滤波器的影响范围就越大。
标准差较小: 高斯滤波器会产生较小范围的平滑效果,图像的细节保留更多。
标准差较大: 高斯滤波器会在更大范围内进行平滑,使得图像变得更加模糊,细节和边缘特征会被更强地平滑掉。
- 卷积操作: 高斯滤波器通过卷积操作应用于图像。卷积操作将高斯函数应用于图像的每一个像素,以计算新像素值。对于每个像素,卷积操作涉及将高斯函数的加权平均值与周围像素进行计算。
- 平滑效果: 高斯滤波器的作用是将图像中每个像素的值替换为其周围像素的加权平均值。权重由高斯函数决定,因此离中心像素越远的像素权重越小。这种方式平滑了图像,并减少了噪声。
1.4.3 形态学:对象计数
from scipy.ndimage import measurements,morphology
import matplotlib.pyplot as plt
im=array(Image.open('D:\pyFile\Python计算机视觉编程\data\houses.png').convert('L'))
plt.figure()
plt.imshow(im, cmap='gray')
im=1*(im<128)#通过与1相乘,转换成二进制表示
# 对二值图像进行标记。该函数返回两个值:
# labels:一个与输入图像同样大小的数组,其中每个连通区域都有一个唯一的标签。
# nbr_objects: 图像中连通区域的数量。
labels,nbr_objects=measurements.label(im)
plt.figure()
plt.imshow(im, cmap='gray') #使用灰度颜色映射显示二值图像。
plt.title(f'Number of objects: {nbr_objects}')
plt.show()
plt.subplot(121)
plt.imshow(im, cmap='gray') #使用灰度颜色映射显示二值图像。
plt.title(f'Number of objects: {nbr_objects}')
# 形态学开操作更好的分离各个对象
# iterations为迭代次数
# ones((9,5)):创建一个9行5列的结构元素,作为开操作中的腐蚀和膨胀的形状。
im_open=morphology.binary_opening(im,ones((9,5)),iterations=2)
labels_opens,nbr_objects_open=measurements.label(im_open)
print("Number of objects:",nbr_objects_open)
plt.subplot(122)
plt.title(f'Number of objects: {nbr_objects_open}')
plt.imshow(im_open, cmap='gray')
1.5高级示例:图像去噪
from numpy import *
from scipy.ndimage import filters
import matplotlib.pyplot as plt
def denoise(im,U_init,tolerance=0.1,tau=0.125,tv_weight=100):
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 #更新Px
PyNew=Py+(tau/tv_weight)*GradUy #更新Py
NormNew=maximum(1,sqrt(PxNew**2+PyNew**2))#计算PxNew和PyNew的范数,确保其最小值为1
Px=PxNew/NormNew #更新x分量
Py=PyNew/NormNew #更新y分量
RxPx=roll(Px,1,axis=1)#计算Px在x方向上的右移
RyPy=roll(Py,1,axis=0)#计算Px在y方向上的下移
DivP=(Px-RxPx)+(Py-RyPy)#计算Px和Py的梯度
U=im+tv_weight*DivP # 更新去噪后的图像U
error=linalg.norm(U-Uold)/sqrt(n*m)# 计算当前误差
return U,im-U #返回去噪后的图像U和噪声图像
im=zeros((500,500))
im[100:400,100:400]=128
im[200:300,200:300]=255
im=im+30*random.standard_normal((500,500))
plt.subplot(131)
plt.imshow(im)
U,T=denoise(im,im)
G=filters.gaussian_filter(im,10)
plt.subplot(132)
plt.imshow(G)
plt.subplot(133)
plt.imshow(U)
plt.subplots_adjust(wspace=0.4) #调整子图之间的水平间距
from PIL import Image
from pylab import *
im=array(Image.open('D:\pyFile\Python计算机视觉编程\data\empire.jpg').convert('L'))
U,T=denoise(im,im)
subplot(131)
imshow(im)
axis('off')
gray()
G=filters.gaussian_filter(im,5)
subplot(132)
imshow(G)
axis('off')
subplot(133)
imshow(U)
axis('off')
show()