OpenCV(Open Source Computer Vision Library)是计算机视觉领域最广泛使用的开源库之一。作为一个跨平台的计算机视觉库,它包含了数百种图像处理和计算机视觉算法,广泛应用于工业检测、医学影像、安防监控、自动驾驶等领域。
OpenCV 的历史可以追溯到 1999 年,最初由 Intel 开发。在 Python 接口中,模块名从 cv 变为 cv2 有几个关键原因:
架构革新:OpenCV 2.x 版本对核心架构进行了全面重构,引入了更现代的 C++ API,同时保留了 C 接口的兼容性。这个重大改变通过模块名 cv2 体现出来。
命名空间管理:cv2 模块提供了更清晰的命名空间组织,避免了与旧版 cv 模块的命名冲突。
向后兼容:尽管 OpenCV 已经发展到 4.x 版本,但保持 cv2 的模块名确保了大量现有代码的兼容性。在实际使用中,我们仍然通过 import cv2 来导入最新版本的 OpenCV。
提示:在 Python 中,虽然模块名是 cv2,但实际导入的是完整的 OpenCV 库,包含所有功能模块。
OpenCV 的功能可以划分为几个主要领域:
OpenCV 提供了简单易用的图像读写接口:
python复制import cv2
# 读取图像 (支持 JPEG, PNG, TIFF 等多种格式)
img = cv2.imread('image.jpg')
# 显示图像
cv2.imshow('Image Window', img)
cv2.waitKey(0) # 等待按键
cv2.destroyAllWindows() # 关闭窗口
# 保存图像
cv2.imwrite('output.jpg', img)
注意:OpenCV 默认使用 BGR 色彩顺序而非 RGB,这在与其他库交互时需要特别注意。
色彩空间转换是图像处理的基础操作:
python复制# BGR 转灰度
gray = cv2.cvtColor(img, cv2.COLOR_BGR2GRAY)
# BGR 转 HSV (常用于颜色识别)
hsv = cv2.cvtColor(img, cv2.COLOR_BGR2HSV)
# BGR 转 RGB (用于 matplotlib 显示)
rgb = cv2.cvtColor(img, cv2.COLOR_BGR2RGB)
常见的色彩空间包括:
OpenCV 提供了丰富的几何变换功能:
python复制# 缩放
resized = cv2.resize(img, (new_width, new_height))
# 旋转
(h, w) = img.shape[:2]
center = (w // 2, h // 2)
M = cv2.getRotationMatrix2D(center, 45, 1.0) # 45度旋转
rotated = cv2.warpAffine(img, M, (w, h))
# 仿射变换
pts1 = np.float32([[50,50],[200,50],[50,200]])
pts2 = np.float32([[10,100],[200,50],[100,250]])
M = cv2.getAffineTransform(pts1, pts2)
affine = cv2.warpAffine(img, M, (w, h))
# 透视变换
pts1 = np.float32([[56,65],[368,52],[28,387],[389,390]])
pts2 = np.float32([[0,0],[300,0],[0,300],[300,300]])
M = cv2.getPerspectiveTransform(pts1, pts2)
perspective = cv2.warpPerspective(img, M, (300,300))
图像平滑是去除噪声的常用技术:
python复制# 高斯模糊
blur = cv2.GaussianBlur(img, (5,5), 0)
# 中值模糊 (对椒盐噪声特别有效)
median = cv2.medianBlur(img, 5)
# 双边滤波 (保留边缘)
bilateral = cv2.bilateralFilter(img, 9, 75, 75)
各种模糊技术的比较:
形态学操作是基于形状的图像处理方法:
python复制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)
# 梯度 (膨胀与腐蚀的差,得到物体轮廓)
gradient = cv2.morphologyEx(img, cv2.MORPH_GRADIENT, kernel)
形态学操作常用于:
Canny 边缘检测是最常用的边缘提取算法:
python复制edges = cv2.Canny(img, 100, 200) # 阈值1, 阈值2
Canny 边缘检测的步骤:
Harris 角点检测是经典的特征点检测方法:
python复制gray = cv2.cvtColor(img, cv2.COLOR_BGR2GRAY)
gray = np.float32(gray)
dst = cv2.cornerHarris(gray, 2, 3, 0.04)
dst = cv2.dilate(dst, None)
img[dst > 0.01 * dst.max()] = [0,0,255] # 标记角点
OpenCV 提供了多种特征检测算法:
python复制# SIFT 特征
sift = cv2.SIFT_create()
kp = sift.detect(gray, None)
img_sift = cv2.drawKeypoints(img, kp, None)
# ORB 特征 (更快,专利免费)
orb = cv2.ORB_create()
kp, des = orb.detectAndCompute(gray, None)
img_orb = cv2.drawKeypoints(img, kp, None)
特征点匹配示例:
python复制# 初始化 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)
# 绘制前10个匹配
img_matches = cv2.drawMatches(img1, kp1, img2, kp2, matches[:10], None, flags=2)
OpenCV 提供了简单的视频处理接口:
python复制# 打开摄像头
cap = cv2.VideoCapture(0)
# 打开视频文件
cap = cv2.VideoCapture('video.mp4')
while cap.isOpened():
ret, frame = cap.read()
if not ret:
break
# 在此处处理每一帧
gray = cv2.cvtColor(frame, cv2.COLOR_BGR2GRAY)
cv2.imshow('frame', gray)
if cv2.waitKey(1) & 0xFF == ord('q'):
break
cap.release()
cv2.destroyAllWindows()
背景减除是视频分析中的常用技术:
python复制# 创建背景减除器
fgbg = cv2.createBackgroundSubtractorMOG2()
while True:
ret, frame = cap.read()
if not ret:
break
# 应用背景减除
fgmask = fgbg.apply(frame)
cv2.imshow('frame', fgmask)
if cv2.waitKey(30) & 0xFF == 27:
break
光流可以估计图像中物体的运动:
python复制# Lucas-Kanade 光流
feature_params = dict(maxCorners=100, qualityLevel=0.3, minDistance=7, blockSize=7)
lk_params = dict(winSize=(15,15), maxLevel=2, criteria=(cv2.TERM_CRITERIA_EPS | cv2.TERM_CRITERIA_COUNT, 10, 0.03))
# 读取第一帧
ret, old_frame = cap.read()
old_gray = cv2.cvtColor(old_frame, cv2.COLOR_BGR2GRAY)
p0 = cv2.goodFeaturesToTrack(old_gray, mask=None, **feature_params)
while True:
ret, frame = cap.read()
if not ret:
break
frame_gray = cv2.cvtColor(frame, cv2.COLOR_BGR2GRAY)
# 计算光流
p1, st, err = cv2.calcOpticalFlowPyrLK(old_gray, frame_gray, p0, None, **lk_params)
# 选择好的点
good_new = p1[st==1]
good_old = p0[st==1]
# 绘制轨迹
for i,(new,old) in enumerate(zip(good_new, good_old)):
a,b = new.ravel()
c,d = old.ravel()
frame = cv2.line(frame, (a,b), (c,d), (0,255,0), 2)
frame = cv2.circle(frame, (a,b), 5, (0,0,255), -1)
cv2.imshow('frame', frame)
if cv2.waitKey(30) & 0xFF == 27:
break
# 更新前一帧和特征点
old_gray = frame_gray.copy()
p0 = good_new.reshape(-1,1,2)
Haar 级联是 OpenCV 中经典的目标检测方法:
python复制# 加载预训练的人脸检测模型
face_cascade = cv2.CascadeClassifier(cv2.data.haarcascades + 'haarcascade_frontalface_default.xml')
# 检测人脸
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)
方向梯度直方图(HOG)结合支持向量机(SVM)是另一种经典检测方法:
python复制# 初始化 HOG 描述符/检测器
hog = cv2.HOGDescriptor()
hog.setSVMDetector(cv2.HOGDescriptor_getDefaultPeopleDetector())
# 检测行人
boxes, weights = hog.detectMultiScale(img, winStride=(4,4), padding=(8,8), scale=1.05)
# 绘制检测框
for (x,y,w,h) in boxes:
cv2.rectangle(img, (x,y), (x+w,y+h), (0,255,0), 2)
OpenCV 可以加载多种框架训练的模型:
python复制# 加载 Caffe 模型
net = cv2.dnn.readNetFromCaffe('deploy.prototxt', 'model.caffemodel')
# 加载 TensorFlow 模型
net = cv2.dnn.readNetFromTensorflow('frozen_inference_graph.pb', 'graph.pbtxt')
# 加载 ONNX 模型
net = cv2.dnn.readNetFromONNX('model.onnx')
python复制# 加载 COCO 类别标签
classes = []
with open('coco.names', 'r') as f:
classes = [line.strip() for line in f.readlines()]
# 加载 YOLOv3 模型
net = cv2.dnn.readNet('yolov3.weights', 'yolov3.cfg')
# 从图像创建 blob
blob = cv2.dnn.blobFromImage(img, 1/255, (416,416), (0,0,0), True, crop=False)
net.setInput(blob)
# 获取输出层
layer_names = net.getLayerNames()
output_layers = [layer_names[i[0] - 1] for i in net.getUnconnectedOutLayers()]
# 前向传播
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:
# 检测到的对象
center_x = int(detection[0] * width)
center_y = int(detection[1] * height)
w = int(detection[2] * width)
h = int(detection[3] * height)
# 矩形坐标
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
colors = np.random.uniform(0, 255, size=(len(classes), 3))
for i in range(len(boxes)):
if i in indexes:
x, y, w, h = boxes[i]
label = str(classes[class_ids[i]])
color = colors[class_ids[i]]
cv2.rectangle(img, (x, y), (x + w, y + h), color, 2)
cv2.putText(img, label, (x, y + 30), font, 3, color, 3)
GrabCut 是一种交互式图像分割算法,它结合了图割和迭代能量最小化的思想。与传统的分割方法相比,GrabCut 具有以下优势:
算法核心步骤:
python复制import numpy as np
import cv2
# 读取图像
img = cv2.imread('input.jpg')
mask = np.zeros(img.shape[:2], np.uint8)
# 初始化背景和前景模型
bgd_model = np.zeros((1, 65), np.float64)
fgd_model = np.zeros((1, 65), np.float64)
# 定义矩形区域 (x,y,w,h)
rect = (50, 50, 450, 290)
# 应用 GrabCut
cv2.grabCut(img, mask, rect, bgd_model, fgd_model, 5, cv2.GC_INIT_WITH_RECT)
# 修改掩码
mask2 = np.where((mask == 2) | (mask == 0), 0, 1).astype('uint8')
# 提取前景
result = img * mask2[:, :, np.newaxis]
# 显示结果
cv2.imshow('Result', result)
cv2.waitKey(0)
cv2.destroyAllWindows()
避免不必要的拷贝:
python复制# 不好 - 创建了副本
gray = cv2.cvtColor(img.copy(), cv2.COLOR_BGR2GRAY)
# 好 - 直接操作原图
gray = cv2.cvtColor(img, cv2.COLOR_BGR2GRAY)
使用 UMats:
python复制# 启用 OpenCL 加速
img_umat = cv2.UMat(img)
gray_umat = cv2.cvtColor(img_umat, cv2.COLOR_BGR2GRAY)
gray = gray_umat.get()
并行处理:
python复制# 设置线程数
cv2.setNumThreads(4)
图像显示问题:
python复制# 确保在适当的时候调用 waitKey
cv2.imshow('image', img)
cv2.waitKey(0) # 等待按键
cv2.destroyAllWindows()
视频捕获问题:
python复制# 检查摄像头是否打开
if not cap.isOpened():
print("无法打开摄像头")
exit()
内存管理:
python复制# 及时释放资源
cap.release()
cv2.destroyAllWindows()
python复制def order_points(pts):
# 初始化坐标点
rect = np.zeros((4, 2), dtype="float32")
# 左上角点将具有最小的和,而右下角点将具有最大的和
s = pts.sum(axis=1)
rect[0] = pts[np.argmin(s)]
rect[2] = pts[np.argmax(s)]
# 计算点之间的差,右上角点将具有最小的差,而左下角点将具有最大的差
diff = np.diff(pts, axis=1)
rect[1] = pts[np.argmin(diff)]
rect[3] = pts[np.argmax(diff)]
return rect
def four_point_transform(image, pts):
# 获取有序点并计算新图像的宽度和高度
rect = order_points(pts)
(tl, tr, br, bl) = rect
# 计算新图像的宽度
widthA = np.sqrt(((br[0] - bl[0]) ** 2) + ((br[1] - bl[1]) ** 2))
widthB = np.sqrt(((tr[0] - tl[0]) ** 2) + ((tr[1] - tl[1]) ** 2))
maxWidth = max(int(widthA), int(widthB))
# 计算新图像的高度
heightA = np.sqrt(((tr[0] - br[0]) ** 2) + ((tr[1] - br[1]) ** 2))
heightB = np.sqrt(((tl[0] - bl[0]) ** 2) + ((tl[1] - bl[1]) ** 2))
maxHeight = max(int(heightA), int(heightB))
# 构建目标点集
dst = np.array([
[0, 0],
[maxWidth - 1, 0],
[maxWidth - 1, maxHeight - 1],
[0, maxHeight - 1]], dtype="float32")
# 计算透视变换矩阵并应用
M = cv2.getPerspectiveTransform(rect, dst)
warped = cv2.warpPerspective(image, M, (maxWidth, maxHeight))
return warped
# 读取图像
image = cv2.imread('document.jpg')
# 预处理
gray = cv2.cvtColor(image, cv2.COLOR_BGR2GRAY)
gray = cv2.GaussianBlur(gray, (5, 5), 0)
edged = cv2.Canny(gray, 75, 200)
# 查找轮廓
cnts = cv2.findContours(edged.copy(), cv2.RETR_LIST, cv2.CHAIN_APPROX_SIMPLE)
cnts = cnts[0] if len(cnts) == 2 else cnts[1]
cnts = sorted(cnts, key=cv2.contourArea, reverse=True)[:5]
# 遍历轮廓
for c in cnts:
# 近似轮廓
peri = cv2.arcLength(c, True)
approx = cv2.approxPolyDP(c, 0.02 * peri, True)
# 如果近似轮廓有四个点,则假定找到了文档
if len(approx) == 4:
screenCnt = approx
break
# 应用四点变换
warped = four_point_transform(image, screenCnt.reshape(4, 2))
# 转换为灰度图并锐化
warped = cv2.cvtColor(warped, cv2.COLOR_BGR2GRAY)
warped = cv2.adaptiveThreshold(warped, 255, cv2.ADAPTIVE_THRESH_GAUSSIAN_C,
cv2.THRESH_BINARY, 21, 10)
# 显示结果
cv2.imshow("Original", image)
cv2.imshow("Scanned", warped)
cv2.waitKey(0)
python复制# 初始化人脸检测器
face_cascade = cv2.CascadeClassifier(cv2.data.haarcascades + 'haarcascade_frontalface_default.xml')
# 打开摄像头
cap = cv2.VideoCapture(0)
while True:
ret, frame = cap.read()
if not ret:
break
# 检测人脸
gray = cv2.cvtColor(frame, cv2.COLOR_BGR2GRAY)
faces = face_cascade.detectMultiScale(gray, 1.3, 5)
for (x,y,w,h) in faces:
# 提取人脸区域
face_roi = frame[y:y+h, x:x+w]
# 应用双边滤波 (美颜效果)
face_roi = cv2.bilateralFilter(face_roi, 15, 75, 75)
# 锐化眼睛区域
eyes_roi = face_roi[int(h*0.2):int(h*0.5), int(w*0.1):int(w*0.9)]
kernel = np.array([[-1,-1,-1], [-1,9,-1], [-1,-1,-1]])
eyes_roi = cv2.filter2D(eyes_roi, -1, kernel)
# 放回处理后的区域
face_roi[int(h*0.2):int(h*0.5), int(w*0.1):int(w*0.9)] = eyes_roi
frame[y:y+h, x:x+w] = face_roi
# 显示结果
cv2.imshow('Beautified', frame)
if cv2.waitKey(1) & 0xFF == ord('q'):
break
cap.release()
cv2.destroyAllWindows()
OpenCV 可以与主流深度学习框架协同工作:
python复制# 使用 OpenCV 运行 TensorFlow 模型
net = cv2.dnn.readNetFromTensorflow('frozen_inference_graph.pb', 'graph.pbtxt')
# 使用 OpenCV 运行 PyTorch 模型 (通过 ONNX)
net = cv2.dnn.readNetFromONNX('model.onnx')
# 使用 OpenCV 运行 Darknet (YOLO) 模型
net = cv2.dnn.readNetFromDarknet('yolov3.cfg', 'yolov3.weights')
对于支持 CUDA 的系统,可以启用 GPU 加速:
python复制# 检查 CUDA 支持
print(cv2.cuda.getCudaEnabledDeviceCount())
# 设置后端和目标
net.setPreferableBackend(cv2.dnn.DNN_BACKEND_CUDA)
net.setPreferableTarget(cv2.dnn.DNN_TARGET_CUDA)
OpenCV 特别适合在树莓派等嵌入式设备上运行:
python复制# 在树莓派上优化 OpenCV
cv2.setUseOptimized(True)
cv2.setNumThreads(4) # 使用多核
# 使用较小的图像尺寸
frame = cv2.resize(frame, (320, 240))
# 使用轻量级模型
net = cv2.dnn.readNet('mobilenet.caffemodel', 'mobilenet.prototxt')
在实际使用 OpenCV 的过程中,我发现文档和示例代码是最快的学习途径。对于复杂功能,建议先从官方示例开始,理解基本原理后再进行定制开发。OpenCV 的功能非常丰富,但并非所有功能都适合所有场景,选择合适的方法往往比使用最先进的技术更重要。