Python 图像处理:Pillow、Scikit-image 和 OpenCV 的应用
大家好,今天我们来深入探讨 Python 在图像处理方面的应用,重点讲解三个非常流行的库:Pillow、Scikit-image 和 OpenCV。我们将从基础概念入手,逐步深入到实际应用,并通过代码示例展示它们各自的优势和特点。
1. 图像处理基础
在开始之前,我们先简单回顾一下图像处理的一些基础概念。
- 图像表示: 数字图像通常由像素矩阵表示,每个像素代表图像中的一个点,其数值表示颜色或灰度值。
- 颜色空间: 常见的颜色空间包括 RGB(红绿蓝)、灰度、HSV(色相、饱和度、亮度)等。
- 基本操作: 包括图像读取、显示、保存,以及像素级别的操作。
2. Pillow:图像处理的瑞士军刀
Pillow 是 Python Imaging Library (PIL) 的一个分支,提供了广泛的图像处理功能,易于使用,适合处理各种图像格式。
2.1 安装 Pillow
pip install Pillow
2.2 图像的读取、显示和保存
from PIL import Image
# 读取图像
img = Image.open("image.jpg")
# 显示图像 (需要安装图像查看器)
img.show()
# 获取图像尺寸
width, height = img.size
print(f"图像尺寸:{width} x {height}")
# 获取图像格式
print(f"图像格式:{img.format}")
# 获取图像颜色模式
print(f"图像模式:{img.mode}")
# 保存图像
img.save("image_copy.png")
2.3 图像转换
Pillow 支持图像格式和颜色模式的转换。
from PIL import Image
img = Image.open("image.jpg")
# 转换为灰度图像
img_gray = img.convert("L")
img_gray.save("image_gray.jpg")
# 转换为 RGB 图像 (如果原始图像是其他格式)
img_rgb = img.convert("RGB")
img_rgb.save("image_rgb.jpg")
2.4 图像裁剪和缩放
from PIL import Image
img = Image.open("image.jpg")
# 裁剪图像 (左上角坐标 x, y, 右下角坐标 x, y)
cropped_img = img.crop((100, 100, 300, 300))
cropped_img.save("image_cropped.jpg")
# 缩放图像
resized_img = img.resize((200, 200))
resized_img.save("image_resized.jpg")
# 保持比例缩放
def resize_image(image, size):
"""
保持原始图像的纵横比进行缩放
"""
width, height = image.size
new_width, new_height = size
# 计算缩放比例
width_ratio = new_width / width
height_ratio = new_height / height
# 选择较小的缩放比例以避免裁剪
ratio = min(width_ratio, height_ratio)
# 计算新的尺寸
resized_width = int(width * ratio)
resized_height = int(height * ratio)
# 使用 LANCZOS (抗锯齿) 算法调整大小
resized_image = image.resize((resized_width, resized_height), Image.LANCZOS)
# 创建一个新图像,并在中心粘贴调整大小后的图像
new_image = Image.new("RGB", (new_width, new_height), (0, 0, 0)) # 黑色背景
new_image.paste(resized_image, ((new_width - resized_width) // 2, (new_height - resized_height) // 2))
return new_image
img = Image.open("image.jpg")
resized_img_with_padding = resize_image(img, (200, 200))
resized_img_with_padding.save("image_resized_padding.jpg")
2.5 图像旋转
from PIL import Image
img = Image.open("image.jpg")
# 旋转 45 度
rotated_img = img.rotate(45)
rotated_img.save("image_rotated.jpg")
# 旋转 45 度,并填充空白区域
rotated_img_expand = img.rotate(45, expand=True)
rotated_img_expand.save("image_rotated_expand.jpg")
2.6 图像滤镜
Pillow 提供了一些内置的图像滤镜。
from PIL import Image, ImageFilter
img = Image.open("image.jpg")
# 应用模糊滤镜
blurred_img = img.filter(ImageFilter.BLUR)
blurred_img.save("image_blurred.jpg")
# 应用锐化滤镜
sharpened_img = img.filter(ImageFilter.SHARPEN)
sharpened_img.save("image_sharpened.jpg")
# 应用边缘增强滤镜
edge_enhanced_img = img.filter(ImageFilter.EDGE_ENHANCE)
edge_enhanced_img.save("image_edge_enhanced.jpg")
2.7 像素级别的操作
from PIL import Image
img = Image.open("image.jpg")
pixels = img.load() # 返回一个像素访问对象
width, height = img.size
# 反转颜色
for x in range(width):
for y in range(height):
r, g, b = pixels[x, y]
pixels[x, y] = (255 - r, 255 - g, 255 - b)
img.save("image_inverted.jpg")
3. Scikit-image:科学图像处理库
Scikit-image 是一个基于 NumPy 的图像处理库,提供了大量的图像处理算法,包括图像分割、特征提取、图像增强等,更偏向于科学研究。
3.1 安装 Scikit-image
pip install scikit-image
3.2 图像的读取、显示和保存
from skimage import io
# 读取图像
img = io.imread("image.jpg")
# 显示图像 (需要 matplotlib)
import matplotlib.pyplot as plt
plt.imshow(img)
plt.show()
# 获取图像尺寸
height, width, channels = img.shape
print(f"图像尺寸:{width} x {height}, 通道数:{channels}")
# 保存图像
io.imsave("image_copy.png", img)
3.3 图像转换
from skimage import io, color
img = io.imread("image.jpg")
# 转换为灰度图像
img_gray = color.rgb2gray(img)
io.imsave("image_gray.jpg", img_gray)
# 转换为 HSV 图像
img_hsv = color.rgb2hsv(img)
io.imsave("image_hsv.jpg", img_hsv) # HSV图像保存通常需要做一些处理,因为它的值范围不同于RGB
# 将灰度图像转换为 RGB 图像 (重复灰度值到三个通道)
img_rgb = color.gray2rgb(img_gray)
io.imsave("image_rgb.jpg", img_rgb)
3.4 图像增强
from skimage import io, exposure
img = io.imread("image.jpg")
# 调整伽马值
gamma_corrected_img = exposure.adjust_gamma(img, gamma=0.5) # gamma < 1 增强图像亮度
io.imsave("image_gamma.jpg", gamma_corrected_img)
# 直方图均衡化
equalized_img = exposure.equalize_hist(img)
io.imsave("image_equalized.jpg", equalized_img)
# 自适应直方图均衡化 (CLAHE)
from skimage import exposure
img = io.imread("image.jpg")
clahe = exposure.equalize_adapthist(img, clip_limit=0.03)
io.imsave("image_clahe.jpg", clahe)
3.5 图像滤波
from skimage import io, filters
img = io.imread("image.jpg")
# 高斯滤波
blurred_img = filters.gaussian(img, sigma=1)
io.imsave("image_gaussian.jpg", blurred_img)
# 中值滤波
from skimage.morphology import disk
from skimage.filters import rank
selem = disk(5)
median_filtered_img = rank.median(img, selem=selem)
io.imsave("image_median.jpg", median_filtered_img)
# Sobel 边缘检测
edges = filters.sobel(img_gray) #需要灰度图
io.imsave("image_sobel.jpg", edges)
3.6 图像分割
from skimage import io, segmentation
img = io.imread("image.jpg")
# 使用 Felzenszwalb 算法进行图像分割
segments_fz = segmentation.felzenszwalb(img, scale=100, sigma=0.5, min_size=50)
io.imsave("image_segmented_fz.jpg", segmentation.mark_boundaries(img, segments_fz))
# 使用 SLIC 算法进行图像分割
segments_slic = segmentation.slic(img, n_segments=100, compactness=10, sigma=1)
io.imsave("image_segmented_slic.jpg", segmentation.mark_boundaries(img, segments_slic))
# 使用 Watershed 算法进行图像分割 (需要预处理,例如计算梯度)
from skimage.filters import sobel
from skimage.segmentation import watershed
from scipy import ndimage as ndi
elevation_map = sobel(img_gray)
markers = ndi.label(elevation_map < 0.08)[0] #阈值需要根据图像进行调整
segments_watershed = watershed(elevation_map, markers)
io.imsave("image_segmented_watershed.jpg", segmentation.mark_boundaries(img, segments_watershed))
3.7 特征提取
from skimage import io, feature
img = io.imread("image.jpg", as_gray=True)
# 提取 HOG 特征 (方向梯度直方图)
hog_features, hog_image = feature.hog(img, orientations=8, pixels_per_cell=(16, 16),
cells_per_block=(1, 1), visualize=True, channel_axis=None)
io.imsave("image_hog.jpg", hog_image)
print(f"HOG 特征维度:{hog_features.shape}")
4. OpenCV:强大的计算机视觉库
OpenCV (Open Source Computer Vision Library) 是一个广泛应用于计算机视觉领域的库,提供了丰富的图像处理和计算机视觉算法,性能优异,适合实时应用。
4.1 安装 OpenCV
pip install opencv-python
4.2 图像的读取、显示和保存
import cv2
# 读取图像
img = cv2.imread("image.jpg")
# 显示图像 (使用 OpenCV 的窗口)
cv2.imshow("Image", img)
cv2.waitKey(0) # 等待按键按下
cv2.destroyAllWindows()
# 获取图像尺寸
height, width, channels = img.shape
print(f"图像尺寸:{width} x {height}, 通道数:{channels}")
# 保存图像
cv2.imwrite("image_copy.png", img)
4.3 图像转换
import cv2
img = cv2.imread("image.jpg")
# 转换为灰度图像
img_gray = cv2.cvtColor(img, cv2.COLOR_BGR2GRAY)
cv2.imwrite("image_gray.jpg", img_gray)
# 转换为 HSV 图像
img_hsv = cv2.cvtColor(img, cv2.COLOR_BGR2HSV)
cv2.imwrite("image_hsv.jpg", img_hsv)
# BGR 转换为 RGB (OpenCV 默认使用 BGR 颜色顺序)
img_rgb = cv2.cvtColor(img, cv2.COLOR_BGR2RGB)
cv2.imwrite("image_rgb.jpg", img_rgb)
#RGB to LAB
img_lab = cv2.cvtColor(img, cv2.COLOR_BGR2LAB)
cv2.imwrite("image_lab.jpg", img_lab)
4.4 图像增强
import cv2
import numpy as np
img = cv2.imread("image.jpg")
# 调整亮度
alpha = 1.5 # 对比度控制 (1.0-3.0)
beta = 50 # 亮度控制 (0-100)
adjusted_img = cv2.convertScaleAbs(img, alpha=alpha, beta=beta)
cv2.imwrite("image_adjusted.jpg", adjusted_img)
# 直方图均衡化
img_gray = cv2.cvtColor(img, cv2.COLOR_BGR2GRAY)
equalized_img = cv2.equalizeHist(img_gray)
cv2.imwrite("image_equalized.jpg", equalized_img)
# 自适应直方图均衡化 (CLAHE)
clahe = cv2.createCLAHE(clipLimit=2.0, tileGridSize=(8, 8))
clahe_img = clahe.apply(img_gray)
cv2.imwrite("image_clahe.jpg", clahe_img)
4.5 图像滤波
import cv2
img = cv2.imread("image.jpg")
# 高斯滤波
blurred_img = cv2.GaussianBlur(img, (5, 5), 0) # (5, 5) 是内核大小,0 是标准差
cv2.imwrite("image_gaussian.jpg", blurred_img)
# 中值滤波
median_filtered_img = cv2.medianBlur(img, 5) # 5 是内核大小
cv2.imwrite("image_median.jpg", median_filtered_img)
# Sobel 边缘检测
img_gray = cv2.cvtColor(img, cv2.COLOR_BGR2GRAY)
sobelx = cv2.Sobel(img_gray, cv2.CV_64F, 1, 0, ksize=3)
sobely = cv2.Sobel(img_gray, cv2.CV_64F, 0, 1, ksize=3)
abs_sobelx = cv2.convertScaleAbs(sobelx)
abs_sobely = cv2.convertScaleAbs(sobely)
edges = cv2.addWeighted(abs_sobelx, 0.5, abs_sobely, 0.5, 0)
cv2.imwrite("image_sobel.jpg", edges)
4.6 形态学操作
import cv2
import numpy as np
img = cv2.imread("image.jpg", cv2.IMREAD_GRAYSCALE) #读取灰度图
# 定义内核
kernel = np.ones((5, 5), np.uint8)
# 腐蚀
erosion = cv2.erode(img, kernel, iterations=1)
cv2.imwrite("image_erosion.jpg", erosion)
# 膨胀
dilation = cv2.dilate(img, kernel, iterations=1)
cv2.imwrite("image_dilation.jpg", dilation)
# 开运算 (先腐蚀后膨胀)
opening = cv2.morphologyEx(img, cv2.MORPH_OPEN, kernel)
cv2.imwrite("image_opening.jpg", opening)
# 闭运算 (先膨胀后腐蚀)
closing = cv2.morphologyEx(img, cv2.MORPH_CLOSE, kernel)
cv2.imwrite("image_closing.jpg", closing)
4.7 图像分割
import cv2
import numpy as np
img = cv2.imread("image.jpg")
img_gray = cv2.cvtColor(img, cv2.COLOR_BGR2GRAY)
# Otsu's 二值化
ret, thresh = cv2.threshold(img_gray, 0, 255, cv2.THRESH_BINARY + cv2.THRESH_OTSU)
cv2.imwrite("image_threshold.jpg", thresh)
# K-means 聚类分割
# 将图像转换为一维数组
pixels = img.reshape((-1, 3))
pixels = np.float32(pixels)
# 定义停止条件
criteria = (cv2.TERM_CRITERIA_EPS + cv2.TERM_CRITERIA_MAX_ITER, 100, 0.2)
# 应用 K-means
k = 3 # 分成 3 类
_, labels, centers = cv2.kmeans(pixels, k, None, criteria, 10, cv2.KMEANS_RANDOM_CENTERS)
# 将像素重新塑形为原始图像尺寸
segmented_img = labels.reshape(img.shape[:2])
cv2.imwrite("image_kmeans.jpg", segmented_img * (255 // (k - 1))) #缩放到0-255方便显示
4.8 特征提取
import cv2
img = cv2.imread("image.jpg")
img_gray = cv2.cvtColor(img, cv2.COLOR_BGR2GRAY)
# SIFT 特征检测
sift = cv2.SIFT_create()
keypoints, descriptors = sift.detectAndCompute(img_gray, None)
# 在图像上绘制关键点
img_with_keypoints = cv2.drawKeypoints(img_gray, keypoints, img)
cv2.imwrite("image_sift.jpg", img_with_keypoints)
# ORB 特征检测
orb = cv2.ORB_create()
keypoints_orb, descriptors_orb = orb.detectAndCompute(img_gray, None)
img_with_keypoints_orb = cv2.drawKeypoints(img_gray, keypoints_orb, img)
cv2.imwrite("image_orb.jpg", img_with_keypoints_orb)
5. 三个库的对比
为了更清晰地了解这三个库的特点,我们用表格进行对比:
特性 | Pillow | Scikit-image | OpenCV |
---|---|---|---|
设计目标 | 易用性,通用图像处理 | 科学研究,图像分析 | 实时性,计算机视觉应用 |
算法丰富度 | 适中 | 高 | 高 |
性能 | 较好 | 中等 | 优异 |
依赖 | 无核心依赖 (可选 Tkinter) | NumPy, SciPy, Matplotlib | NumPy |
易用性 | 非常容易 | 容易 | 中等 |
应用场景 | 图像格式转换、缩放、裁剪、简单滤镜 | 图像分割、特征提取、图像分析、科学研究 | 目标检测、人脸识别、视频处理、实时应用 |
开发语言 | Python, 部分 C 代码 | Python, 部分 C 代码 | C++, Python |
6. 选择合适的库
如何选择合适的库取决于你的具体需求:
- Pillow: 如果你需要进行简单的图像处理,例如格式转换、缩放、裁剪等,并且希望代码易于编写和理解,那么 Pillow 是一个不错的选择。
- Scikit-image: 如果你需要进行更复杂的图像分析和科学研究,例如图像分割、特征提取、图像增强等,那么 Scikit-image 提供了丰富的算法和工具。
- OpenCV: 如果你需要进行实时图像处理或计算机视觉应用,例如目标检测、人脸识别、视频处理等,并且对性能有较高要求,那么 OpenCV 是最佳选择。
当然,在实际应用中,你也可以结合使用这三个库,例如使用 Pillow 进行图像的读取和保存,使用 Scikit-image 进行图像分析,使用 OpenCV 进行实时处理。
7. 实际案例:图像去噪
我们来看一个实际的例子:使用 OpenCV 和 Scikit-image 对图像进行去噪。
import cv2
from skimage import io, restoration
# 使用 OpenCV 进行中值滤波去噪
img_cv = cv2.imread("noisy_image.jpg")
img_cv_median = cv2.medianBlur(img_cv, 5)
cv2.imwrite("denoised_cv_median.jpg", img_cv_median)
# 使用 Scikit-image 进行 TV 去噪 (Total Variation Denoising)
img_sk = io.imread("noisy_image.jpg", as_gray=True) #TV去噪需要灰度图
img_sk_tv = restoration.denoise_tv_chambolle(img_sk, weight=0.1) #权重需要根据图像进行调整
io.imsave("denoised_sk_tv.jpg", img_sk_tv)
在这个例子中,我们使用了 OpenCV 的中值滤波和 Scikit-image 的 TV 去噪算法,分别对图像进行了去噪处理。你可以根据实际情况选择合适的算法和参数。
8. 常见问题与技巧
在实际使用中,可能会遇到一些常见问题,以下是一些建议:
- 图像格式兼容性: 不同的库可能支持不同的图像格式,需要注意格式转换。
- 颜色空间: 确保在进行图像处理之前,将图像转换为合适的颜色空间。
- 数据类型: 不同的算法可能需要不同的数据类型,例如浮点型或整型。
- 性能优化: 对于性能敏感的应用,可以使用 OpenCV 的 C++ 接口或 NumPy 的向量化操作进行优化。
- 参数调整: 图像处理算法通常有很多参数需要调整,需要根据实际情况进行尝试。
9. 探索更多的图像处理技术
图像处理是一个非常广泛的领域,还有很多其他的技术值得学习,例如:
- 深度学习: 使用卷积神经网络 (CNN) 进行图像分类、目标检测、图像分割等。
- 图像配准: 将多幅图像对齐到同一个坐标系中。
- 三维重建: 从二维图像中重建三维模型。
- 视频处理: 对视频进行分析和处理,例如目标跟踪、行为识别等。
10. 选择合适的工具,熟练掌握图像处理技术
我们今天讨论了如何使用 Pillow、Scikit-image 和 OpenCV 进行图像处理。这三个库各有特点和优势,选择合适的库取决于具体的应用场景。希望通过今天的讲解,大家能够对 Python 图像处理有更深入的了解,并在实际应用中灵活运用这些工具。