目录
1.1 PIL:Python图像处理类库
PIL(Python Imaging Library Python,图像处理类库)提供了通用的图像处理功能
1.1.1 转换图像格式
如图所示在运行转化格式代码前,图片格式为bmp:
from PIL import Image
import os
filelist = ['1.bmp']
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格式的图片:
下面将返回目录中所有 JPG 图像的文件名列表,如图1.jpg被返回显示:
import os
def get_imlist(path):
""" 返回目录中所有 JPG 图像的文件名列表 """
return [os.path.join(path, f) for f in os.listdir(path) if f.endswith('.jpg')]
directory_path = 'C:\\Users\\86156\\PycharmProjects\\pythonProject\\qushuiyin\\python'
jpg_files = get_imlist(directory_path)
print(jpg_files)
1.1.2 创建缩略图
使用thumbnail() 方法创建缩略图:
from PIL import Image
# 打开原始图像
pil_im = Image.open('1.jpg')
# 创建最长边为 128 像素的缩略图
pil_im.thumbnail((128, 128))
# 保存或显示缩略图
pil_im.save('thumbnail_example.jpg')
生成缩略图如图所示:
1.1.3 复制和粘贴图像区域
- 打开名为1
.jpg
的图像。 - 裁剪出一个从
(100, 100)
到(400, 400)
的区域。 - 将裁剪出的区域旋转 180 度。
- 将旋转后的区域放回原图像的相同位置。
- 将修改后的图像保存为
modified_image.jpg
。
from PIL import Image
# 打开图像文件
pil_im = Image.open('1.jpg')
# 定义裁剪区域
box = (100, 100, 400, 400)
# 裁剪图像区域
region = pil_im.crop(box)
# 旋转裁剪出的区域 180 度
region = region.transpose(Image.ROTATE_180)
# 将旋转后的区域放回原图像
pil_im.paste(region, box)
# 保存修改后的图像
pil_im.save('modified_image.jpg')
得到变换后的图像为:
1.1.4 调整尺寸和旋转
如下是使用 resize()
和 rotate()
方法来调整图像的尺寸和旋转图像:
from PIL import Image
# 打开图像文件
pil_im = Image.open('1.jpg')
# 调整图像的尺寸
# 将图像大小调整为 128x128 像素
resized_image = pil_im.resize((128, 128))
# 保存调整大小后的图像
resized_image.save('resized_image.jpg')
# 旋转图像
# 将图像逆时针旋转 45 度
rotated_image = pil_im.rotate(45)
# 保存旋转后的图像
rotated_image.save('rotated_image.jpg')
'resized_image.jpg'为调整大小后的图像:
rotated_image.jpg为旋转后的图像:
1.2 Matplotlib
Matplotlib是个很好的类库,具有比 PIL 更强大的绘图功能。
1.2.1 绘制图像、点和线
from PIL import Image
import numpy as np
import matplotlib.pyplot as plt
# 读取图像到数组中
im = np.array(Image.open('1.jpg'))
# 创建一个新的图形窗口
plt.figure()
# 绘制图像
plt.imshow(im)
# 定义一些点的坐标
x = [100, 100, 400, 400]
y = [200, 500, 200, 500]
# 使用红色星状标记绘制点
plt.plot(x, y, 'r*', markersize=10, label='Points')
# 绘制连接前两个点的线
plt.plot(x[:2], y[:2], 'b-', label='Line between first two points')
# 添加标题
plt.title('Plotting: "empire.jpg"')
# 添加图例
plt.legend()
# 显示绘制的图像和点
plt.show()
绘制后的图像为:
1.2.2 图像轮廓和直方图
绘制图像的轮廓(或者其他 二维函数的等轮廓线。因为绘制轮廓需要对每个坐标 [x, y] 的像素值施加同一个阈值,所以首先需要将图像灰度化:
from PIL import Image
from pylab import *
# 读取图像并转换为灰度图像
im = array(Image.open('1.jpg').convert('L'))
# 创建图像窗口并绘制轮廓图像
figure()
gray()
contour(im, origin='image')
axis('equal')
axis('off')
# 显示图像
show()
figure()
hist(im.flatten(),128)
show()
轮廓化后的图片:
直方图:
1.2.3 交互式标注
有时用户需要和某些应用交互,例如在一幅图像中标记一些点,或者标注一些训练 数据。PyLab 库中的 ginput() 函数就可以实现交互式标注。
from PIL import Image
from matplotlib import pyplot as plt
# 读取图像
im = Image.open('empire.jpg')
# 创建图像窗口并显示图像
plt.imshow(im)
plt.title('Please click 3 points')
# 交互式标注
points = plt.ginput(3)
# 显示点击的坐标
print('You clicked:', points)
# 显示图像窗口
plt.show()
生成图像为:
1.3 NumPy
NumPy是非常有名的 Python 科学计算工具包,其中 包含了大量有用的思想,比如数组对象(用来表示向量、矩阵、图像等)以及线性 代数函数。
1.3.1 图像数组表示
当载入图像时,我们通过调用 array() 方法将图像转换成 NumPy 的数组对象。
from PIL import Image
import numpy as np
# 读取图像并转换为NumPy数组
im = np.array(Image.open('empire.jpg'))
print('Original image shape:', im.shape, 'Data type:', im.dtype)
# 将图像转换为灰度并设置数据类型为float32
im_gray = np.array(Image.open('empire.jpg').convert('L'), dtype='float32')
print('Grayscale image shape:', im_gray.shape, 'Data type:', im_gray.dtype)
结果:
1.3.2 灰度变换
将图像读入 NumPy 数组对象后,我们可以对它们执行任意数学操作。一个简单的例子就是图像的灰度变换。
from PIL import Image
import numpy as np
import matplotlib.pyplot as plt
# 读取图像并转换为灰度图像的NumPy数组
im = np.array(Image.open('empire.jpg').convert('L'))
# 反相处理
im2 = 255 - im
# 将图像像素值变换到 100 到 200 区间
im3 = (100.0/255) * im + 100
# 对图像像素值进行平方变换
im4 = 255.0 * (im / 255.0) ** 2
# 显示原始图像及其变换结果
plt.figure(figsize=(12, 8))
plt.subplot(2, 2, 1)
plt.imshow(im, cmap='gray')
plt.title('Original Image')
plt.axis('off')
plt.subplot(2, 2, 2)
plt.imshow(im2, cmap='gray')
plt.title('Inverted Image')
plt.axis('off')
plt.subplot(2, 2, 3)
plt.imshow(im3, cmap='gray')
plt.title('Scaled Image (100 to 200)')
plt.axis('off')
plt.subplot(2, 2, 4)
plt.imshow(im4, cmap='gray')
plt.title('Squared Image')
plt.axis('off')
plt.tight_layout()
plt.show()
结果:
1.3.3 图像缩放
写一个简单的用于图像缩放的函数。把下面的函数添加到 imtool.py 文件里:
1.3.4 直方图均衡化
图像灰度变换中一个非常有用的例子就是直方图均衡化。直方图均衡化的变换函数是图像中像素值的累积分布函数。
首先定义累积分布函数cdf:
import numpy as np
def histeq(im, nbr_bins=256):
"""
对一幅灰度图像进行直方图均衡化
:param im: 输入的灰度图像(二维NumPy数组)
:param nbr_bins: 直方图中的小区间数目,默认为256
:return: 直方图均衡化后的图像和累积分布函数
"""
# 计算图像的直方图
imhist, bins = np.histogram(im.flatten(), nbr_bins, density=True)
cdf = imhist.cumsum() # 累积分布函数
cdf = 255 * cdf / cdf[-1] # 归一化到0-255范围
# 使用累积分布函数的线性插值,计算新的像素值
im2 = np.interp(im.flatten(), bins[:-1], cdf)
return im2.reshape(im.shape), cdf
随后引用函数对图像进行直方图均衡化:
from PIL import Image
import numpy as np
import imtools # 确保该模块在Python路径中
# 加载图像并转换为灰度图像的NumPy数组
im = np.array(Image.open('AquaTermi_lowcontrast.jpg').convert('L'))
# 使用直方图均衡化函数
im2, cdf = imtools.histeq(im)
# 可以使用matplotlib显示结果
import matplotlib.pyplot as plt
plt.figure(figsize=(12, 6))
plt.subplot(1, 2, 1)
plt.imshow(im, cmap='gray')
plt.title('Original Image')
plt.axis('off')
plt.subplot(1, 2, 2)
plt.imshow(im2, cmap='gray')
plt.title('Equalized Image')
plt.axis('off')
plt.show()
结果:
1.3.5 图像平均
图像平均操作是减少图像噪声的一种简单方式,通常用于艺术特效。下面的函数可以用于计算平均图像,将其添加到 imtool.py 文件里:
def compute_average(imlist):
"""
计算图像列表的平均图像
:param imlist: 包含图像文件路径的列表
:return: 平均图像的 NumPy 数组,类型为 uint8
"""
# 确保列表非空
if not imlist:
raise ValueError("Image list is empty")
# 打开第一幅图像,将其存储在浮点型数组中
averageim = np.array(Image.open(imlist[0]), dtype=np.float32)
# 累加所有图像
for imname in imlist[1:]:
try:
im = np.array(Image.open(imname), dtype=np.float32)
averageim += im
except Exception as e:
print(f"{imname}...skipped. Error: {e}")
# 计算平均值
averageim /= len(imlist)
# 返回 uint8 类型的平均图像
return np.array(averageim, dtype=np.uint8)
再引用该函数:
# main.py
from imtools import compute_average
import matplotlib.pyplot as plt
# 图像文件路径的列表(根据实际情况修改)
image_files = ['1.jpg', ]
# 计算平均图像
average_image = compute_average(image_files)
# 使用matplotlib显示结果
plt.figure(figsize=(8, 8))
plt.imshow(average_image, cmap='gray')
plt.title('Average Image')
plt.axis('off')
plt.show()
得到图像平均后的结果:
1.3.6 图像的主成分分析(PCA)
import numpy as np
import matplotlib.pyplot as plt
# PCA 函数的实现(从之前的代码中)
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 = np.dot(X, X.T) # 协方差矩阵
e, EV = np.linalg.eigh(M) # 特征值和特征向量
tmp = np.dot(X.T, EV).T # 这就是紧致技巧
V = tmp[::-1] # 由于最后的特征向量是我们所需要的,所以需要将其逆转
S = np.sqrt(e)[::-1] # 由于特征值是按照递增顺序排列的,所以需要将其逆转
for i in range(V.shape[1]):
V[:, i] /= S
else:
# PCA- 使用 SVD 方法
U, S, Vt = np.linalg.svd(X, full_matrices=False)
V = Vt.T # SVD 的 V 是转置的,所以我们需要转置回来
S = np.sqrt(S) # 对奇异值取平方根来得到标准差
# 返回投影矩阵、方差和均值
return V, S, mean_X
# 示例数据
np.random.seed(0)
num_samples = 100
num_features = 5
X = np.random.rand(num_samples, num_features)
# 执行 PCA
V, S, mean_X = pca(X)
# 输出结果
print("投影矩阵 (主成分):")
print(V)
print("\n方差(主成分的标准差):")
print(S)
print("\n均值:")
print(mean_X)
# 可视化结果(对于 2D 数据)
if num_features == 2:
plt.figure(figsize=(8, 6))
plt.scatter(X[:, 0], X[:, 1], alpha=0.5)
plt.title('Original Data')
plt.xlabel('Feature 1')
plt.ylabel('Feature 2')
plt.grid(True)
plt.show()
# 投影后的数据
X_pca = np.dot(X, V)
plt.figure(figsize=(8, 6))
plt.scatter(X_pca[:, 0], X_pca[:, 1], alpha=0.5)
plt.title('PCA Projected Data')
plt.xlabel('Principal Component 1')
plt.ylabel('Principal Component 2')
plt.grid(True)
plt.show()
结果:
1.3.7 使用pickle模块
如果想要保存一些结果或者数据以方便后续使用,Python 中的 pickle 模块非常有用。
import pickle
# 定义一个示例字典
data = {'name': 'Alice', 'age': 30, 'city': 'New York'}
# 将对象序列化到文件
with open('data.pkl', 'wb') as file:
pickle.dump(data, file)
# 从文件反序列化对象
with open('data.pkl', 'rb') as file:
loaded_data = pickle.load(file)
print(loaded_data)
这个代码片段将一个字典对象 data
序列化到一个文件 data.pkl
中,然后再从文件中读取并反序列化成原始对象。运行时会输出 { 'name': 'Alice', 'age': 30, 'city': 'New York' }
。
结果:
1.4 SciPy
SciPy是建立在 NumPy 基础上,用于数值运算的开源工具包。 SciPy 提供很多高效的操作,可以实现数值积分、优化、统计、信号处理,以及对我们来说最重要的图像处理功能。
1.4.1 图像模糊
SciPy 有用来做滤波操作的 scipy.ndimage.filters 模块。该模块使用快速一维分离 的方式来计算卷积。
from PIL import Image
from numpy import array, zeros, uint8
from scipy.ndimage import gaussian_filter
# 加载图像并转换为 NumPy 数组
im = array(Image.open('1.jpg'))
# 创建一个与输入图像相同大小的零数组,用于存储处理后的图像
im2 = zeros(im.shape)
# 对每个颜色通道应用高斯模糊
for i in range(3):
im2[:,:,i] = gaussian_filter(im[:,:,i], sigma=5)
# 将处理后的图像转换为 uint8 类型(像素值范围从 0 到 255)
im2 = uint8(im2)
# 将处理后的图像保存到文件
Image.fromarray(im2).save('1_blurred.jpg')
结果:
1.4.2 图像导数
使用 Sobel 滤波器来计算 x 和 y 的方向导数,以及梯度大小。sobel() 函数的第二个参数表示选择 x 或者 y 方向导数,第三个参数保存输出的变量。
from PIL import Image
from numpy import array, zeros, sqrt, uint8
from scipy.ndimage import sobel
# 加载图像并转换为灰度图
im = array(Image.open('empire.jpg').convert('L'))
# 计算 x 和 y 方向的 Sobel 导数
imx = zeros(im.shape)
sobel(im, axis=1, output=imx)
imy = zeros(im.shape)
sobel(im, axis=0, output=imy)
# 计算梯度大小
magnitude = sqrt(imx**2 + imy**2)
# 将梯度大小转换为 uint8 类型(像素值范围从 0 到 255)
magnitude = uint8(255 * (magnitude / magnitude.max()))
# 保存梯度大小图像
Image.fromarray(magnitude).save('gradient_magnitude.jpg')
结果:
1.4.3 形态学:对象计数
from scipy.ndimage import measurements, morphology
from PIL import Image
import numpy as np
# 载入图像并转换为灰度图像
im = np.array(Image.open('1.jpg').convert('L'))
# 进行二值化操作:假设阈值为128,小于该值的像素为1,大于该值的像素为0
im_binary = (im < 128).astype(int)
# 使用 label() 函数标记不同的对象
labels, nbr_objects = measurements.label(im_binary)
# 输出原始图像中的对象个数
print("Number of objects (before opening):", nbr_objects)
# 形态学开操作:用结构元素对图像进行开运算
# 结构元素大小为9x5的矩形
structure = np.ones((9, 5), dtype=np.int32)
im_open = morphology.binary_opening(im_binary, structure=structure, iterations=2)
# 使用 label() 函数重新标记经过开运算处理后的图像中的对象
labels_open, nbr_objects_open = measurements.label(im_open)
# 输出经过开运算处理后的对象个数
print("Number of objects (after opening):", nbr_objects_open)
结果:
1.5 高级示例:图像去噪
图像去噪是在去除图像噪声的同时,尽可能地保留图像细节和结构的处理技术。我们这里使用 ROF(Rudin-Osher-Fatemi)去噪模型。
from PIL import Image
import numpy as np
import matplotlib.pyplot as plt
import rof
# Load and convert the image to grayscale
im = np.array(Image.open('empire.jpg').convert('L'))
# Apply ROF denoising
U, T = rof.denoise(im, im)
# Plot the denoised image
plt.figure()
plt.imshow(U, cmap='gray')
plt.axis('equal')
plt.axis('off')
plt.title('ROF Denoised Image')
plt.show()
结果: