好的,我们开始吧。
计算机视觉:OpenCV和Pillow在图像处理中的高级应用
各位同学,今天我们深入探讨计算机视觉领域,聚焦于两个强大的Python库:OpenCV和Pillow。 虽然它们都用于图像处理,但它们在设计理念、功能侧重和适用场景上存在显著差异。 本次讲座将讲解它们的高级应用,并通过实际代码示例帮助大家掌握它们的核心功能。
第一部分:OpenCV(cv2)高级应用
OpenCV(Open Source Computer Vision Library)是一个跨平台的计算机视觉库,专注于实时图像处理、视频分析以及机器学习。 其核心是用C/C++编写,提供了Python、Java等语言的接口。
1.1 图像滤波进阶
除了常见的均值滤波、高斯滤波之外,OpenCV还提供了更高级的滤波技术,例如双边滤波和形态学滤波。
-
双边滤波 (Bilateral Filtering)
双边滤波是一种非线性滤波技术,它在平滑图像的同时,能够较好地保持边缘信息。 它的核心思想是考虑像素的空间距离和像素值差异,只对空间距离近且像素值相近的像素进行平均。
import cv2 import numpy as np # 读取图像 img = cv2.imread('image.jpg') # 双边滤波 blurred = cv2.bilateralFilter(img, d=9, sigmaColor=75, sigmaSpace=75) # 显示结果 cv2.imshow('Original Image', img) cv2.imshow('Bilateral Filtered Image', blurred) cv2.waitKey(0) cv2.destroyAllWindows()
其中,
d
是滤波器的直径,sigmaColor
是颜色空间的标准差,sigmaSpace
是坐标空间的标准差。sigmaColor
越大,颜色差异较大的像素也会被纳入平均范围,图像会变得更模糊。sigmaSpace
越大,越多的相邻像素会被纳入平均范围。 -
形态学滤波 (Morphological Filtering)
形态学滤波是一组基于图像形状的非线性操作,常用于图像的去噪、分割、特征提取等。 常见的形态学操作包括腐蚀、膨胀、开运算和闭运算。
- 腐蚀 (Erosion): 腐蚀操作可以消除图像中小的噪声点,并且可以分离图像中粘连的对象。
- 膨胀 (Dilation): 膨胀操作可以填充图像中小的空洞,并且可以连接图像中分离的对象。
- 开运算 (Opening): 开运算是先腐蚀后膨胀的操作,可以消除小的噪声点,同时保持图像的整体形状。
- 闭运算 (Closing): 闭运算是先膨胀后腐蚀的操作,可以填充小的空洞,同时连接相邻的对象。
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) # 膨胀 dilation = cv2.dilate(img, kernel, iterations=1) # 开运算 opening = cv2.morphologyEx(img, cv2.MORPH_OPEN, kernel) # 闭运算 closing = cv2.morphologyEx(img, cv2.MORPH_CLOSE, kernel) # 显示结果 cv2.imshow('Original Image', img) cv2.imshow('Erosion', erosion) cv2.imshow('Dilation', dilation) cv2.imshow('Opening', opening) cv2.imshow('Closing', closing) cv2.waitKey(0) cv2.destroyAllWindows()
kernel
是结构元素,它定义了形态学操作的形状和大小。iterations
是操作的迭代次数。
1.2 特征检测与描述
OpenCV提供了多种特征检测和描述算法,例如SIFT、SURF、ORB、以及更现代的KAZE、AKAZE等。
-
ORB (Oriented FAST and Rotated BRIEF)
ORB是一种快速、鲁棒的特征检测和描述算法,它结合了FAST关键点检测和BRIEF描述符。 ORB具有计算速度快、对旋转和尺度变化具有较好的鲁棒性的优点。
import cv2 # 读取图像 img1 = cv2.imread('image1.jpg', cv2.IMREAD_GRAYSCALE) img2 = cv2.imread('image2.jpg', cv2.IMREAD_GRAYSCALE) # 创建ORB对象 orb = cv2.ORB_create() # 检测关键点和计算描述符 kp1, des1 = orb.detectAndCompute(img1, None) kp2, des2 = orb.detectAndCompute(img2, None) # 创建BFMatcher对象 bf = cv2.BFMatcher(cv2.NORM_HAMMING, crossCheck=True) # 匹配描述符 matches = bf.match(des1, des2) # 根据距离排序 matches = sorted(matches, key=lambda x: x.distance) # 绘制匹配结果 img3 = cv2.drawMatches(img1, kp1, img2, kp2, matches[:10], None, flags=cv2.DRAW_MATCHES_FLAGS_NOT_DRAW_SINGLE_POINTS) # 显示结果 cv2.imshow('ORB Feature Matching', img3) cv2.waitKey(0) cv2.destroyAllWindows()
这段代码展示了如何使用ORB算法进行特征检测和描述,并使用BFMatcher进行特征匹配。
cv2.drawMatches
函数用于绘制匹配结果。 -
KAZE/AKAZE
KAZE和AKAZE是基于非线性扩散滤波的特征检测和描述算法。 它们对尺度和旋转变化具有较好的鲁棒性,并且在光照变化和图像模糊的情况下表现良好。 AKAZE是KAZE的加速版本。
import cv2 # 读取图像 img1 = cv2.imread('image1.jpg', cv2.IMREAD_GRAYSCALE) img2 = cv2.imread('image2.jpg', cv2.IMREAD_GRAYSCALE) # 创建AKAZE对象 akaze = cv2.AKAZE_create() # 检测关键点和计算描述符 kp1, des1 = akaze.detectAndCompute(img1, None) kp2, des2 = akaze.detectAndCompute(img2, None) # 创建BFMatcher对象 bf = cv2.BFMatcher(cv2.NORM_HAMMING, crossCheck=True) # AKAZE 通常使用 Hamming 距离 # 匹配描述符 matches = bf.match(des1, des2) # 根据距离排序 matches = sorted(matches, key=lambda x: x.distance) # 绘制匹配结果 img3 = cv2.drawMatches(img1, kp1, img2, kp2, matches[:10], None, flags=cv2.DRAW_MATCHES_FLAGS_NOT_DRAW_SINGLE_POINTS) # 显示结果 cv2.imshow('AKAZE Feature Matching', img3) cv2.waitKey(0) cv2.destroyAllWindows()
注意:AKAZE描述符通常使用Hamming距离进行匹配,因此BFMatcher的
normType
参数设置为cv2.NORM_HAMMING
。
1.3 目标检测
OpenCV提供了多种目标检测算法,包括Haar级联检测器、HOG(Histogram of Oriented Gradients)+ SVM、以及基于深度学习的目标检测器(例如YOLO、SSD)。
-
Haar级联检测器 (Haar Cascade Classifiers)
Haar级联检测器是一种基于 Haar 特征的实时目标检测算法。 它通过训练大量的正负样本,学习到目标的 Haar 特征,然后使用 Adaboost 算法将这些特征组合成一个强分类器。
import cv2 # 加载 Haar 级联分类器 face_cascade = cv2.CascadeClassifier('haarcascade_frontalface_default.xml') # 读取图像 img = cv2.imread('image.jpg') gray = cv2.cvtColor(img, cv2.COLOR_BGR2GRAY) # 检测人脸 faces = face_cascade.detectMultiScale(gray, scaleFactor=1.1, minNeighbors=5, minSize=(30, 30)) # 绘制矩形框 for (x, y, w, h) in faces: cv2.rectangle(img, (x, y), (x+w, y+h), (255, 0, 0), 2) # 显示结果 cv2.imshow('Face Detection', img) cv2.waitKey(0) cv2.destroyAllWindows()
这段代码使用Haar级联检测器检测图像中的人脸。
haarcascade_frontalface_default.xml
是人脸检测器的XML文件,可以从OpenCV的GitHub仓库下载。detectMultiScale
函数用于检测图像中的人脸,scaleFactor
是图像缩放因子,minNeighbors
是每个候选矩形应该保留的邻居数,minSize
是最小的人脸尺寸。 -
基于深度学习的目标检测器 (YOLO/SSD)
YOLO (You Only Look Once) 和 SSD (Single Shot MultiBox Detector) 是两种流行的基于深度学习的目标检测算法。 它们具有检测速度快、精度高的优点。 OpenCV 提供了对这些算法的支持,可以通过加载预训练的模型来进行目标检测。
import cv2 # 加载 YOLO 模型 net = cv2.dnn.readNet('yolov3.weights', 'yolov3.cfg') # 加载 COCO 类别名称 with open('coco.names', 'r') as f: classes = [line.strip() for line in f.readlines()] # 获取输出层名称 layer_names = net.getLayerNames() output_layers = [layer_names[i[0] - 1] for i in net.getUnconnectedOutLayers()] # 读取图像 img = cv2.imread('image.jpg') height, width, channels = img.shape # 图像预处理 blob = cv2.dnn.blobFromImage(img, 0.00392, (416, 416), (0, 0, 0), True, crop=False) net.setInput(blob) outs = net.forward(output_layers) # 解析输出 class_ids = [] confidences = [] boxes = [] for out in outs: for detection in out: scores = detection[5:] class_id = np.argmax(scores) confidence = scores[class_id] if confidence > 0.5: # Object detected center_x = int(detection[0] * width) center_y = int(detection[1] * height) w = int(detection[2] * width) h = int(detection[3] * height) # Rectangle coordinate x = int(center_x - w / 2) y = int(center_y - h / 2) boxes.append([x, y, w, h]) confidences.append(float(confidence)) class_ids.append(class_id) # 应用非极大值抑制 indexes = cv2.dnn.NMSBoxes(boxes, confidences, 0.5, 0.4) # 绘制矩形框 font = cv2.FONT_HERSHEY_PLAIN for i in range(len(boxes)): if i in indexes: x, y, w, h = boxes[i] label = str(classes[class_ids[i]]) color = (0,255,0) cv2.rectangle(img, (x, y), (x + w, y + h), color, 2) cv2.putText(img, label, (x, y - 5), font, 1, color, 1) # 显示结果 cv2.imshow("YOLO Object Detection", img) cv2.waitKey(0) cv2.destroyAllWindows()
这段代码使用YOLOv3模型检测图像中的目标。 需要下载
yolov3.weights
和yolov3.cfg
文件,以及coco.names
文件,这些文件可以在YOLO的官方网站或其他资源网站找到。cv2.dnn.blobFromImage
函数用于将图像转换为blob格式,net.setInput
函数用于设置网络的输入,net.forward
函数用于进行前向推理,cv2.dnn.NMSBoxes
函数用于应用非极大值抑制。
1.4 视频分析
OpenCV提供了丰富的视频分析功能,例如运动检测、目标跟踪、行为识别等。
-
运动检测 (Motion Detection)
运动检测是指检测视频中运动的物体。 一种常用的方法是使用背景建模技术,例如高斯混合模型 (Gaussian Mixture Model, GMM)。
import cv2 # 创建背景建模对象 fgbg = cv2.createBackgroundSubtractorMOG2() # 打开视频文件 cap = cv2.VideoCapture('video.mp4') while(1): # 读取帧 ret, frame = cap.read() if not ret: break # 应用背景建模 fgmask = fgbg.apply(frame) # 显示结果 cv2.imshow('Original Video', frame) cv2.imshow('Foreground Mask', fgmask) k = cv2.waitKey(30) & 0xff if k == 27: break # 释放资源 cap.release() cv2.destroyAllWindows()
这段代码使用高斯混合模型进行背景建模,从而检测视频中的运动物体。
cv2.createBackgroundSubtractorMOG2
函数用于创建背景建模对象,fgbg.apply
函数用于应用背景建模。 -
目标跟踪 (Object Tracking)
OpenCV提供了多种目标跟踪算法,例如MeanShift、CamShift、KCF、以及更现代的CSRT、GOTURN等。
import cv2 # 打开视频文件 cap = cv2.VideoCapture('video.mp4') # 读取第一帧 ret, frame = cap.read() # 选择跟踪区域 bbox = cv2.selectROI(frame, False) # 初始化跟踪器 tracker = cv2.TrackerCSRT_create() ok = tracker.init(frame, bbox) while(1): # 读取帧 ret, frame = cap.read() if not ret: break # 更新跟踪器 ok, bbox = tracker.update(frame) # 绘制矩形框 if ok: # Tracking success p1 = (int(bbox[0]), int(bbox[1])) p2 = (int(bbox[0] + bbox[2]), int(bbox[1] + bbox[3])) cv2.rectangle(frame, p1, p2, (255,0,0), 2, 1) else : # Tracking failure cv2.putText(frame, "Tracking failure detected", (100,80), cv2.FONT_HERSHEY_SIMPLEX, 0.75,(0,0,255),2) # 显示结果 cv2.imshow("Tracking", frame) # 退出 k = cv2.waitKey(1) & 0xff if k == 27 : break # 释放资源 cap.release() cv2.destroyAllWindows()
这段代码使用CSRT跟踪器跟踪视频中的目标。
cv2.selectROI
函数用于选择跟踪区域,cv2.TrackerCSRT_create
函数用于创建CSRT跟踪器,tracker.init
函数用于初始化跟踪器,tracker.update
函数用于更新跟踪器。
第二部分:Pillow高级应用
Pillow是Python Imaging Library (PIL) 的一个分支,是一个强大的图像处理库。 它提供了广泛的图像处理功能,包括图像格式转换、图像缩放、图像裁剪、图像增强、以及像素级别的操作。 Pillow更侧重于图像处理本身,而非像OpenCV那样侧重于计算机视觉算法。
2.1 图像增强进阶
Pillow 提供了多种图像增强方法,例如色彩平衡、对比度调整、锐化、以及滤镜效果。
-
色彩平衡 (Color Balance)
调整图像的色彩平衡可以改变图像的整体色调,使其更加自然或更具艺术感。
from PIL import Image from PIL import ImageEnhance # 打开图像 img = Image.open('image.jpg') # 创建色彩平衡增强器 enhancer = ImageEnhance.Color(img) # 增强色彩平衡 factor = 1.5 # 增强因子 img_enhanced = enhancer.enhance(factor) # 显示结果 img_enhanced.show()
ImageEnhance.Color
类用于创建色彩平衡增强器,enhance
方法用于增强色彩平衡。factor
参数控制增强的程度,factor > 1
表示增强色彩,factor < 1
表示减弱色彩。 -
锐化 (Sharpening)
锐化可以增强图像的细节,使其看起来更加清晰。
from PIL import Image from PIL import ImageEnhance # 打开图像 img = Image.open('image.jpg') # 创建锐化增强器 enhancer = ImageEnhance.Sharpness(img) # 增强锐化 factor = 2 # 增强因子 img_enhanced = enhancer.enhance(factor) # 显示结果 img_enhanced.show()
ImageEnhance.Sharpness
类用于创建锐化增强器,enhance
方法用于增强锐化。factor
参数控制增强的程度,factor > 1
表示增强锐化,factor < 1
表示减弱锐化。 -
滤镜效果 (Filters)
Pillow 提供了多种滤镜效果,例如模糊、边缘增强、浮雕等。
from PIL import Image from PIL import ImageFilter # 打开图像 img = Image.open('image.jpg') # 应用模糊滤镜 img_blurred = img.filter(ImageFilter.BLUR) # 应用边缘增强滤镜 img_edge_enhanced = img.filter(ImageFilter.EDGE_ENHANCE) # 应用浮雕滤镜 img_embossed = img.filter(ImageFilter.EMBOSS) # 显示结果 img_blurred.show() img_edge_enhanced.show() img_embossed.show()
ImageFilter
模块提供了多种滤镜,例如BLUR
(模糊)、EDGE_ENHANCE
(边缘增强)、EMBOSS
(浮雕)等。
2.2 像素级别操作
Pillow 允许我们访问和修改图像的每个像素,从而实现更精细的图像处理。
-
访问像素 (Accessing Pixels)
可以使用
getpixel
方法访问指定位置的像素值,使用putpixel
方法修改指定位置的像素值。from PIL import Image # 打开图像 img = Image.open('image.jpg') # 获取图像尺寸 width, height = img.size # 访问像素 pixel = img.getpixel((100, 100)) # 获取 (100, 100) 位置的像素值 print(pixel) # 修改像素 img.putpixel((100, 100), (255, 0, 0)) # 将 (100, 100) 位置的像素设置为红色 # 显示结果 img.show()
-
图像直方图 (Image Histogram)
图像直方图是图像像素值的统计分布,可以用于图像分析和图像增强。
from PIL import Image import matplotlib.pyplot as plt # 打开图像 (灰度图) img = Image.open('image.jpg').convert('L') # 获取直方图 histogram = img.histogram() # 绘制直方图 plt.hist(histogram, bins=256, range=(0, 256)) plt.show()
histogram
方法返回一个包含256个元素的列表,每个元素表示对应像素值的像素数量。 可以使用matplotlib
库绘制直方图。
2.3 图像序列处理
Pillow 支持处理图像序列,例如 GIF 动画和多帧 TIFF 图像。
-
GIF 动画 (GIF Animation)
可以读取 GIF 动画的每一帧,并进行处理。
from PIL import Image # 打开 GIF 动画 img = Image.open('animation.gif') # 遍历每一帧 for i in range(img.n_frames): img.seek(i) # 跳转到指定帧 frame = img.copy() # 复制当前帧 # 对帧进行处理 # ... # frame.save(f'frame_{i}.png') # 保存帧
img.n_frames
属性表示 GIF 动画的帧数,img.seek
方法用于跳转到指定帧,img.copy
方法用于复制当前帧。
第三部分:OpenCV 与 Pillow 的结合应用
OpenCV 和 Pillow 可以结合使用,发挥各自的优势。 例如,可以使用 OpenCV 进行图像的预处理和特征提取,然后使用 Pillow 进行图像的增强和显示。
import cv2
from PIL import Image
# 使用 OpenCV 读取图像
img_cv = cv2.imread('image.jpg')
# 将 OpenCV 图像转换为 Pillow 图像
img_pil = Image.fromarray(cv2.cvtColor(img_cv, cv2.COLOR_BGR2RGB))
# 使用 Pillow 进行图像增强
img_pil_enhanced = img_pil.filter(ImageFilter.EDGE_ENHANCE)
# 将 Pillow 图像转换为 OpenCV 图像
img_cv_enhanced = cv2.cvtColor(np.array(img_pil_enhanced), cv2.COLOR_RGB2BGR)
# 使用 OpenCV 显示图像
cv2.imshow('Enhanced Image', img_cv_enhanced)
cv2.waitKey(0)
cv2.destroyAllWindows()
这段代码展示了如何将 OpenCV 图像转换为 Pillow 图像,然后使用 Pillow 进行图像增强,最后将 Pillow 图像转换回 OpenCV 图像。
第四部分:实际案例分析
4.1 案例一:基于OpenCV的车牌识别
车牌识别是一个典型的计算机视觉应用。 我们可以使用OpenCV进行车牌定位、字符分割和字符识别。
- 车牌定位: 使用边缘检测、形态学操作等技术提取车牌区域。
- 字符分割: 使用投影法或连通域分析等方法分割车牌字符。
- 字符识别: 使用机器学习算法(例如SVM、神经网络)识别字符。
(由于篇幅限制,这里只提供思路,详细代码实现较为复杂,需要进行模型训练和参数调整。)
4.2 案例二:基于Pillow的图像水印添加
图像水印可以用于保护图像的版权。 我们可以使用Pillow将水印添加到图像的指定位置。
from PIL import Image
from PIL import ImageDraw
from PIL import ImageFont
# 打开图像
img = Image.open('image.jpg')
# 创建 Draw 对象
draw = ImageDraw.Draw(img)
# 设置水印文字
text = '© My Company'
# 设置字体
font = ImageFont.truetype('arial.ttf', size=36) # 需要下载字体文件
# 计算文字位置
text_width, text_height = draw.textsize(text, font=font)
width, height = img.size
x = width - text_width - 10
y = height - text_height - 10
# 添加水印
draw.text((x, y), text, font=font, fill=(255, 255, 255, 128)) # 白色半透明
# 保存图像
img.save('image_with_watermark.png')
# 显示图像
img.show()
这段代码将水印文字添加到图像的右下角。 可以根据需要调整水印的位置、字体、颜色和透明度。
第五部分:选择合适的工具
特性 | OpenCV | Pillow |
---|---|---|
主要用途 | 计算机视觉,实时图像/视频处理 | 图像处理,格式转换,图像增强 |
核心语言 | C/C++ | Python |
算法支持 | 丰富的计算机视觉算法(特征检测、目标检测等) | 图像处理算法(滤镜、色彩调整等) |
实时性 | 更好 | 相对较弱 |
易用性 | 相对复杂,需要了解底层原理 | 简单易用,API友好 |
适用场景 | 实时视频分析、机器人视觉、自动驾驶等 | 图像编辑、图像格式转换、Web图像处理等 |
选择哪个库取决于你的具体需求。 如果你需要进行实时的计算机视觉处理,OpenCV 是更好的选择。 如果你需要进行简单的图像编辑和格式转换,Pillow 则更加方便易用。 当然,也可以将两者结合使用,发挥各自的优势。
OpenCV专注于计算机视觉算法,Pillow侧重于图像本身的处理,两者的结合能发挥更大的作用。
本次讲座到此结束,谢谢大家。