Python计算机视觉——基础图像操作和处理

基础图像操作和处理


一、python图像处理类库

PIL(Python Imaging Library)图像处理类库提供了通用的图像处理功能、基本图像操作:缩放、裁剪、旋转、颜色转换
通过 save() 方法,PIL 可以将图像保存成多种格式的文件
使用 crop() 方法可以从一幅图像中裁剪指定区域

from PIL import Image 
img= Image.open('empire.jpg') #读取图像,返回PIL图像对象
#只是转换不修改img的值
img.convert('L') # convert('L')转换图像颜色,L为灰度图片
img.convert('1') #转二值图像
img.convert('P') #8位色彩图
img.convert('RGBA') #带有透明度的彩色图像
img= Image.open('test.jpg').convert('L') #修改为灰度图片,丢失颜色


#转换图像格式
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
def get_imlist(path):
"""返回目录中所有jpg图像的文件名列表"""
	return [os.path.join(path,f) for f in os.listdir(path) if f.endswith('.jpg')]

# 创建缩略图
img.thumbnail((128,128)) #指定图片的缩略图大小

#复制粘贴图像区域
box = (100,100,200,300) # 四元组的坐标依次是(左,上,右,下)
region = img.crop(box) #裁剪
region = region.transpose(Image.ROTATE_180) #指定图像区域旋转180°
img.paste(region,box) #粘贴

# 调整尺寸和旋转
out = img.resize((128,128)) # 调整图片大小
out = img.rotate(45) #逆时针旋转

二、Matplotlib

1、绘制图像、点和线

from PIL import Image
from pylab import *
# 读取图像到数组中
im = array(Image.open('empire.jpg'))
# 绘制图像
imshow(im)
# 一些点
x = [100,100,400,400]
y = [200,500,200,500]
# 使用红色星状标记绘制点
plot(x,y,'r*')
# 绘制连接前两个点的线
plot(x[:2],y[:2])
plot(x,y) # 默认为蓝色实线
plot(x,y,'go-') # 带有圆圈标记的绿线
plot(x,y,'ks:') # 带有正方形标记的黑色点线
# 添加标题,显示绘制的图像
title('Plotting: "empire.jpg"')
axis('off') # 坐标轴不显示
show()

(1)用PyLab库绘图的基本颜色格式命令

字符颜色
‘b’蓝色
‘g’绿色
‘r’红色
‘c’青色
‘m’品红
‘y’黄色
‘k’黑色
‘w’白色

(2)用PyLab库绘图的基本线型格式命令

字符线形
‘-’实线
‘–’虚线
‘:’点线

(3)用PyLab库绘图的基本绘制标记格式命令

字符标记
‘.’
‘o’圆圈
‘s’正方形
‘*’星形
‘+’加号
‘x’叉号

2、图像轮廓和直方图

from PIL import Image
from pylab import *
# 读取图像到数组中
im = array(Image.open('empire.jpg').convert('L'))
# 新建一个图像
figure()
# 不使用颜色信息
gray()
# 在原点的左上角显示轮廓图像
contour(im, origin='image')
axis('equal')
axis('off')

在这里插入图片描述

figure()
# hist只接受一维数组,bins=128,直方图箱子或分组的数量
# flatten()按照行优先准则转为一维数组
hist(im.flatten(),128)
show()

在这里插入图片描述

3、交互式标注

import matplotlib
#指定使用交互式后端
matplotlib.use('TkAgg')
from PIL import Image
from pylab import *
im = array(Image.open('empire.jpg'))
imshow(im)
print('Please click 3 points')
# 点击图像3次
x = ginput(3)
# 打印三次点击的坐标
print('you clicked:',x)
show()

三、Numpy

Numpy包含数组对象(矩阵、向量、图像)以及线性代数等,可以实现矩阵乘积、转置、解方程系统、向量乘积和归一化,为图像变形、对变化进行建模、图像分类、图像聚类提供了基础
NumPy 中的数组对象是多维的,但是Numpy数组中的元素必须具有相同的数据类型

1、图像数组表示

im = array(Image.open('test.jpg'))
print(im.shape, im.dtype)
#f将数据类型转为float
im = array(Image.open('test.jpg').convert('L'),'f')
print(im.shape, im.dtype)
"""
(1512, 1080, 3) uint8
(1512, 1080) float32
"""

""" 数组中的元素可以使用下标访问。
位于坐标 i、j,以及颜色通道 k 的像素值可以通过value = im[i,j,k]访问
"""
# 带有颜色信息时结果为array([ 69, 109, 121], dtype=uint8)
# 灰度图像为一个数字,也就是灰度值
value = im[100,200]
# 带有颜色信息可用,1为通道编号,结果为109
value = im[100,2001]

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]) # 倒数第二行

2、灰度变换

将图像读入 NumPy 数组对象后,可以对它们执行任意数学操作
NumPy总是将数组数据类型转换成能够表示数据的“最低”数据类型。对浮点数做乘积或
除法操作会使整数类型的数组变成浮点类型。

from PIL import Image
from numpy import *
im = array(Image.open('empire.jpg').convert('L'))
im2 = 255 - im # 对图像进行反相处理
im3 = (100.0/255) * im + 100 # 将图像像素值变换到 100...200 区间
im4 = 255.0 * (im/255.0)**2 # 对图像像素值求平方后得到的图像
# 最大最小灰度值
print(int(im.min()), int(im.max()))
#转为数组后,灰度图片为绿色,需要指定指定灰度颜色映射
imshow(im,cmap='gray')
show()

#array() 变换的相反操作可以使用 PIL 的 fromarray() 函数完成
pil_im = Image.fromarray(255-im)
# 转换数据类型
pil_im = Image.fromarray(uint8(im))

3、图像缩放

def imresize(im,sz):
 """ 使用 PIL 对象重新定义图像数组的大小
 im为数组,sz为图片大小size:[x,y]或(x,y) """
 	pil_im = Image.fromarray(uint8(im))
 	return array(pil_im.resize(sz))
# fromarray将数组转为PIL图像对象
Image.fromarray(imresize(im,[128,256])).show()

4、直方图均衡化

直方图均衡化是指将一幅图像的灰度直方图变平,使变换后的图像中每个灰度值的分布概率都相同,对图像灰度值进行归一化增强图像的对比度

def histeq(im,nbr_bins=256):
    """对灰度图像进行直方图均衡化"""
    # normed被淘汰了,nbr_bins箱子数量,imhist数组,包含了每个箱中的像素数
    imhist,bins = histogram(im.flatten(),nbr_bins)
    cdf = imhist.cumsum() # 积累分布函数,将像素值范围映射到目标范围的归一化
    cdf = 255 * cdf / cdf[-1] # 归一化0~255, cdf[-1]图像像素总和
    # 使用积累分布函数的线性插值,计算新的像素值,bins[:-1]为x,cdf为y
    im2 = interp(im.flatten(),bins[:-1],cdf)
    return im2.reshape(im.shape), cdf
from PIL import Image
from numpy import *
im = array(Image.open('test.jpg').convert('L'))
im2,cdf = histeq(im)

5、图像平均

减少图像噪音的简单方式。从图像列表中计算出平均图像,假设所有图像大小相同,将像素简单相加,除以图像的数目。
还可以使用mean计算,但很多图像会占用很多内存

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')

6、图像的主成分分析(PCA)

降维,尽可能减少维度的情况下,尽量多的保留训练数据的信息。
通常使用SVD(Singular Value Decomposition,奇异值分解)计算主成分,但当矩阵维度很大时,计算非常慢。
PCA产生的投影矩阵可以将被视为原始坐标变换到现有坐标系,坐标系中的各个坐标按照重要性递减排列。
使用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- 使用紧致技巧
	# cov_matrix = np.cov(X, rowvar=False)
	#rowvar=False意味着每列是一个变量,每行是一个观测。
 	M = dot(X,X.T) # 协方差矩阵
 	e,EV = linalg.eigh(M) # 特征值和特征向量
 	tmp = dot(X.T,EV).T # 这就是紧致技巧
 	V = tmp[::-1] # 由于最后的特征向量是我们所需要的,所以需要将其逆转
 	S = sqrt(e)[::-1] # 由于特征值是按照递增顺序排列的,所以需要将其逆转
 	# 归一化特征向量,图像的个数也就是dim
 	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

以下是个简单的应用

from PIL import Image
from numpy import *
from pylab import *
import pca
im = array(Image.open(imlist[0])) # 打开一幅图像,获取其大小
m,n = im.shape[0:2] # 获取图像的大小
imnbr = len(imlist) # 获取图像的数目
# 创建矩阵,保存所有压平后的图像数据
immatrix = array([array(Image.open(im)).flatten()
 for im in imlist],'f')
# 执行 PCA 操作
V,S,immean = pca.pca(immatrix)
# 显示一些图像(均值图像和前 7 个模式)
figure()
gray()
subplot(2,4,1) 
imshow(immean.reshape(m,n))
for i in range(7):
 subplot(2,4,i+2)
 imshow(V[i].reshape(m,n))
show()

7、使用pickle模块

pickle 模块可以接受几乎所有的 Python 对象,并且将其转换成字符串表示,该
过程叫做封装(pickling);从字符串表示中重构该对象,称为拆封(unpickling)
字符串表示可以方便地存储和传输
文件不确定的话,最好以二进制文件的形式读取和写入

# 保存均值和主成分数据
# 打开文件并保存
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)
 
# 保存一个数组 x 到文件中, fmt='%i'表示使用整数,delimiter=','元素之间用逗号分隔,默认为空格
savetxt('test.txt',x,'%i')
x = loadtxt('test.txt')

四、Scipy

SciPy是建立在 NumPy 基础上,用于数值运算,提供很多高效的操作,可以实现数值积分、优化、统计、信号处理,图像处理功能

1、图像模糊

图像模糊就是将(灰度)图像I 和一个高斯核进行卷积操作:

其中 * 表示卷积操作;Gσ 是标准差为 σ 的二维高斯核,定义为 :
在这里插入图片描述
高斯模糊通常是其他图像处理操作的一部分,比如图像插值操作、兴趣点计算以及
很多其他应用。
SciPy 有用来做滤波操作的 scipy.ndimage.filters 模块(应该已经被淘汰弃用了,各种操作可能直接定义在 scipy.ndimage模块中)。该模块使用快速一维分离
的方式来计算卷积。
一维分离卷积的基本思想是将二维卷积分解为两个连续的一维卷积;计算效率更高,占用存储空间更少,算法更简单容易实现,不涉及索引和边界处理。
基于卷积的交换律和结合律,即卷积操作可以重新排序或以不同的方式组合而不改变最终结果,

# 灰度图像
from PIL import Image
from numpy import *
# filters已弃用
from scipy.ndimage import gaussian_filter
im = array(Image.open('empire.jpg').convert('L'))
# 标准差为5,标准差σ越大越模糊
im2 = gaussian_filter(im,5)

#彩色图像,对每个颜色通道进行高斯模糊
im = array(Image.open('empire.jpg'))
im2 = zeros(im.shape)
for i in range(3):
	im2[:,:,i] = gaussian_filter(im[:,:,i],5) 
# im2 = array(im2,'uint8')也可以
im2 = uint8(im2)
Image.fromarray(im2).show()

2、图像导数

图像强度的变化可以用灰度图像 I(对于彩色图像,通常对每个颜色通道分别计算导数)的x和 y方向导数 Ix和Iy进行描述。
图像的梯度向量为
梯度有两个重要的属性,梯度的大小,描述了图像强度变化的强弱:
在这里插入图片描述梯度的角度,描述了图像中在每个像素点上强度变化的方向:
在这里插入图片描述
NumPy 中的 arctan2() 函数返回弧度表示的有符号角度,角度的变化区间为 -π…π。
图像导数大多数可以通过卷积简单地实现:Ix=IDx 和 Iy=IDy
对于 Dx 和 Dy,通常选择 Prewitt 滤波器:
在这里插入图片描述
或者 Sobel 滤波器(边缘检测):
在这里插入图片描述

from PIL import Image
from numpy import *
from scipy.ndimage import sobel
im = array(Image.open('empire.jpg').convert('L'))
# Sobel 导数滤波器
# sobel() 函数的第二个参数表示选择 x 或者 y 方向导数,第三个参数保存输出的变量。
imx = zeros(im.shape)
sobel(im,1,imx)
imy = zeros(im.shape)
sobel(im,0,imy)
magnitude = sqrt(imx**2+imy**2)

在上述方法中,滤波器的尺度需要随着图像分辨率的变化而变化,而高斯导数滤波器在图像噪声方面更稳健,可以在任意尺寸上计算导数。
高斯导数滤波器,Gσx 和 Gσy 表示 Gσ 在 x 和 y 方向上的导数,Gσ 为标准差为 σ 的高斯函数:
在这里插入图片描述

sigma = 5 # 标准差
imx = zeros(im.shape)
gaussian_filter(im, (sigma,sigma), (0,1), imx)
imy = zeros(im.shape)
gaussian_filter(im, (sigma,sigma), (1,0), imy)

3、形态学:对象计数

形态学(或数学形态学)是度量和分析基本形状的图像处理方法的基本框架与集合
形态学通常用于处理二值图像,能够用于灰度图像。
二值图像是指图像的每个像素只能取两个值,通常是 0 和 1。
scipy.ndimage 中 的 label模 块 可 以 实 现 形 态 学 操 作。 你 可 以 使 用 scipy.ndimage 中的 binary_opening模块来实现二值图像的计数和度量功能。
binary_opening()函数用于对二值图像进行开运算(Opening Operation。开运算它通过对图像先腐蚀再膨胀来实现,主要用于去除图像中的小对象(如噪声点、小颗粒等),同时尽量保持图像中较大对象的形状和大小不变。
binary_closing() 函数用于对二值图像进行闭运算(Closing Operation)。闭运算它通过对图像先进行膨胀后腐蚀来实现,主要用于填充图像内部的小孔和缺口,使图像边缘更加平滑,同时保留图像的整体形状。
的操作。

from scipy.ndimage import label,binary_opening
# 载入图像,然后使用阈值化操作,以保证处理的图像为二值图像
im = array(Image.open('houses.png').convert('L'))
im = 1*(im<128)
labels, nbr_objects = label(im)
print("Number of objects:", nbr_objects)
# 形态学开操作更好地分离各个对象
im_open = binary_opening(im,ones((9,5)),iterations=2)
labels_open, nbr_objects_open = label(im_open)
print("Number of objects:", nbr_objects_open)

总结

图像处理,和数据可视化完全不同,理论和实践的难度升级,目前只能慢慢啃,如有错误,欢迎指正。
后续也许会有一些例子和练习

参考文献

《Python计算机视觉编程》朱文涛 袁勇译
matplotlib后端(backends)概述

  • 12
    点赞
  • 13
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值