Python OpenCV2 完整教程
OpenCV (Open Source Computer Vision Library) 是一个强大的、开源的计算机视觉和机器学习软件库,结合 Python,它变得异常简单和高效,非常适合快速原型开发、学术研究和实际应用。

本教程将涵盖以下内容:
- 环境准备:安装 Python 和 OpenCV。
- 基础操作:读取、显示和保存图像。
- 图像处理:访问像素、图像变换(灰度、大小、旋转等)。
- 图像处理进阶:图像平滑、边缘检测。
- 视频处理:读取摄像头和视频文件。
- 绘制功能:在图像上画线、圆、矩形等。
- 形态学操作:腐蚀与膨胀。
- 直方图:分析图像亮度分布。
- 项目实战:人脸检测。
环境准备
在开始之前,请确保你已经安装了 Python,推荐使用 Python 3.x 版本。
安装 OpenCV
最简单的方式是使用 pip,在终端或命令提示符中运行以下命令:
# 安装 OpenCV-Python 主包 pip install opencv-python # (可选但推荐) 安装 OpenCV 的额外模块,包含 SIFT, SURF 等高级功能 pip install opencv-python-headless pip install opencv-contrib-python
注意:opencv-python 包已经包含了 numpy,但如果你遇到 numpy 相关的错误,可以手动安装:
pip install numpy

基础操作:读取、显示、保存图像
这是使用 OpenCV 的第一步,我们将学习如何加载一张图片,在窗口中显示它,并保存处理后的结果。
代码示例
import cv2
import numpy as np
# --- 1. 读取图像 ---
# cv2.imread() 用于读取图像
# 第二个参数是读取模式:
# cv2.IMREAD_COLOR: 默认,加载彩色图像
# cv2.IMREAD_GRAYSCALE: 加载灰度图像
# cv2.IMREAD_UNCHANGED: 加载包含 alpha 通道的图像
image_path = 'path/to/your/image.jpg' # <--- 替换为你的图片路径
img_color = cv2.imread(image_path, cv2.IMREAD_COLOR)
img_gray = cv2.imread(image_path, cv2.IMREAD_GRAYSCALE)
# 检查图像是否成功加载
if img_color is None:
print(f"错误:无法加载图像,请检查路径: {image_path}")
else:
print(f"彩色图像尺寸: {img_color.shape}")
print(f"灰度图像尺寸: {img_gray.shape}")
# --- 2. 显示图像 ---
# cv2.imshow() 用于在窗口中显示图像
# 第一个参数是窗口的名称
cv2.imshow('Color Image', img_color)
cv2.imshow('Gray Image', img_gray)
# cv2.waitKey() 是一个关键的函数,它等待键盘输入。
# 参数 0 表示无限等待,直到有任意键被按下。
# 如果参数是正数,则等待指定的毫秒数。
cv2.waitKey(0)
# --- 3. 保存图像 ---
# cv2.imwrite() 用于将图像保存到文件
output_path_gray = 'output_gray.jpg'
cv2.imwrite(output_path_gray, img_gray)
print(f"灰度图像已保存到: {output_path_gray}")
# --- 4. 关闭所有窗口 ---
# cv2.destroyAllWindows() 用于关闭所有由 OpenCV 创建的窗口
cv2.destroyAllWindows()
图像处理基础
访问和修改像素
图像在 OpenCV 中被表示为一个 NumPy 数组,你可以像操作 NumPy 数组一样操作像素。
import cv2
img = cv2.imread('path/to/your/image.jpg')
# 获取图像的高度和宽度
height, width = img.shape[:2]
# 访问像素 BGR 值 (注意:OpenCV 使用 BGR 顺序,不是 RGB)
# 坐标格式是 (行, 列),也就是 (y, x)
pixel_BGR = img[100, 150]
print(f"像素 (100, 150) 的 BGR 值: {pixel_BGR}")
# 修改像素值
img[100, 150] = [255, 255, 255] # 将该像素设置为白色
# 修改一个区域 (ROI - Region of Interest)
roi = img[100:200, 300:400]
roi[:, :] = [0, 0, 255] # 将 ROI 区域设置为蓝色
cv2.imshow('Modified Image', img)
cv2.waitKey(0)
cv2.destroyAllWindows()
图像变换
a. 调整大小
import cv2
img = cv2.imread('path/to/your/image.jpg')
# cv2.resize() 调整图像大小
# 参数: (图像, (新宽度, 新高度), 插值方法)
# 常用插值方法:
# cv2.INTER_NEAREST: 最近邻插值
# cv2.INTER_LINEAR: 双线性插值 (默认)
# cv2.INTER_CUBIC: 三次样条插值 (慢,但效果好)
resized_img = cv2.resize(img, (300, 200), interpolation=cv2.INTER_LINEAR)
cv2.imshow('Resized Image', resized_img)
cv2.waitKey(0)
cv2.destroyAllWindows()
b. 旋转图像
import cv2
import numpy as np
img = cv2.imread('path/to/your/image.jpg')
# 获取图像的中心点
center = (img.shape[1] // 2, img.shape[0] // 2)
# 定义旋转矩阵
# cv2.getRotationMatrix2D(中心点, 角度, 缩放因子)
rotation_matrix = cv2.getRotationMatrix2D(center, 45, 1.0)
# 应用旋转
# cv2.warpAffine(图像, 旋转矩阵, (输出图像宽度, 输出图像高度))
rotated_img = cv2.warpAffine(img, rotation_matrix, (img.shape[1], img.shape[0]))
cv2.imshow('Rotated Image', rotated_img)
cv2.waitKey(0)
cv2.destroyAllWindows()
图像处理进阶
a. 图像平滑(模糊)
模糊通常用于降噪。
import cv2
img = cv2.imread('path/to/your/image.jpg', cv2.IMREAD_COLOR)
# cv2.blur() - 均值模糊
# 参数: (图像, 核大小)
blurred_mean = cv2.blur(img, (15, 15))
# cv2.GaussianBlur() - 高斯模糊 (常用)
# 参数: (图像, 核大小, X方向标准差)
blurred_gaussian = cv2.GaussianBlur(img, (15, 15), 0)
# cv2.medianBlur() - 中值模糊 (对椒盐噪声效果好)
blurred_median = cv2.medianBlur(img, 15)
cv2.imshow('Original', img)
cv2.imshow('Mean Blur', blurred_mean)
cv2.imshow('Gaussian Blur', blurred_gaussian)
cv2.imshow('Median Blur', blurred_median)
cv2.waitKey(0)
cv2.destroyAllWindows()
b. 边缘检测
Canny 边缘检测是一种非常流行的边缘检测算法。

import cv2
img = cv2.imread('path/to/your/image.jpg', cv2.IMREAD_COLOR)
# 通常先转换为灰度图
gray_img = cv2.cvtColor(img, cv2.COLOR_BGR2GRAY)
# 使用高斯模糊降噪
blurred_gray = cv2.GaussianBlur(gray_img, (5, 5), 0)
# cv2.Canny() 边缘检测
# 参数: (灰度图, 低阈值, 高阈值)
# 阈值需要根据图像调整
edges = cv2.Canny(blurred_gray, 50, 150)
cv2.imshow('Original', img)
cv2.imshow('Edges', edges)
cv2.waitKey(0)
cv2.destroyAllWindows()
视频处理
视频本质上是一系列连续的图像帧。
a. 从摄像头读取
import cv2
# 打开默认摄像头 (通常是 0)
# 如果你的摄像头不是 0,可以尝试 1, 2 等
cap = cv2.VideoCapture(0)
if not cap.isOpened():
print("无法打开摄像头")
exit()
while True:
# cap.read() 读取一帧
# ret 是一个布尔值,表示是否成功读取到帧
# frame 是读取到的图像帧
ret, frame = cap.read()
if not ret:
print("无法获取帧,退出...")
break
# 在帧上显示一些文本
cv2.putText(frame, 'Press Q to quit', (10, 30), cv2.FONT_HERSHEY_SIMPLEX, 1, (0, 255, 0), 2)
# 显示帧
cv2.imshow('Camera Feed', frame)
# 检查是否按下了 'q' 键
# cv2.waitKey(1) 的返回值是按键的 ASCII 码
if cv2.waitKey(1) & 0xFF == ord('q'):
break
# 释放摄像头资源
cap.release()
# 关闭所有窗口
cv2.destroyAllWindows()
b. 播放视频文件
播放视频文件和从摄像头读取几乎一样,只是 VideoCapture 的参数是文件路径。
import cv2
video_path = 'path/to/your/video.mp4'
cap = cv2.VideoCapture(video_path)
while cap.isOpened():
ret, frame = cap.read()
if not ret:
print("视频播放完毕或文件损坏。")
break
# 可以在这里对每一帧进行处理,例如边缘检测
# gray_frame = cv2.cvtColor(frame, cv2.COLOR_BGR2GRAY)
# edges = cv2.Canny(gray_frame, 50, 150)
# cv2.imshow('Video Edges', edges)
# 或者直接显示原始帧
cv2.imshow('Video Playback', frame)
if cv2.waitKey(25) & 0xFF == ord('q'): # 25ms 是一个合理的帧率
break
cap.release()
cv2.destroyAllWindows()
绘制功能
你可以在图像上绘制各种图形,这对于标注非常有用。
import cv2
import numpy as np
# 创建一个黑色的空白图像
img = np.zeros((512, 512, 3), np.uint8)
# 1. 画一条线
# cv2.line(图像, 起点, 终点, 颜色, 线条粗细)
cv2.line(img, (0, 0), (511, 511), (255, 0, 0), 5)
# 2. 画一个矩形
# cv2.rectangle(图像, 左上角坐标, 右下角坐标, 颜色, 线条粗度)
# 如果线条粗度为 -1,则填充矩形
cv2.rectangle(img, (384, 0), (510, 128), (0, 255, 0), 3)
# 3. 画一个圆
# cv2.circle(图像, 圆心, 半径, 颜色, 线条粗度)
cv2.circle(img, (447, 63), 63, (0, 0, 255), -1)
# 4. 画一个椭圆
# cv2.ellipse(图像, 中心点, 长轴/短轴, 旋转角度, 起始角, 结束角, 颜色, 线条粗度)
cv2.ellipse(img, (256, 256), (100, 50), 0, 0, 180, (255, 255, 0), -1)
# 5. 画一个多边形
# 定义多边形的顶点
pts = np.array([[10, 5], [20, 30], [70, 20], [50, 10]], np.int32)
pts = pts.reshape((-1, 1, 2))
# cv2.polylines(图像, 顶点数组, 是否闭合, 颜色, 线条粗度)
cv2.polylines(img, [pts], True, (0, 255, 255), 3)
# 6. 添加文字
# cv2.putText(图像, 文本, 坐标, 字体, 大小, 颜色, 粗细)
font = cv2.FONT_HERSHEY_SIMPLEX
cv2.putText(img, 'OpenCV', (10, 500), font, 4, (255, 255, 255), 2, cv2.LINE_AA)
cv2.imshow('Drawings', img)
cv2.waitKey(0)
cv2.destroyAllWindows()
形态学操作
形态学操作基于图像形状进行处理,常用于去除噪声、连接断开的物体等,它需要两个输入:原始图像和一个结构元素(Kernel)。
a. 腐蚀 与膨胀
import cv2
import numpy as np
# 创建一个带噪声的图像
img = np.zeros((50, 50), np.uint8)
cv2.rectangle(img, (10, 10), (40, 40), 255, -1)
cv2.circle(img, (25, 25), 5, 0, -1) # 添加一个黑色噪声点
# 定义一个结构元素 (一个 3x3 的矩形)
kernel = np.ones((3, 3), np.uint8)
# 腐蚀: 像素值与结构元素做与操作,会“腐蚀”掉白色部分的边界
eroded_img = cv2.erode(img, kernel, iterations=1)
# 膨胀: 像素值与结构元素做或操作,会“扩大”白色部分
dilated_img = cv2.dilate(img, kernel, iterations=1)
cv2.imshow('Original', img)
cv2.imshow('Eroded', eroded_img)
cv2.imshow('Dilated', dilated_img)
cv2.waitKey(0)
cv2.destroyAllWindows()
b. 开运算 与闭运算
- 开运算 = 先腐蚀后膨胀,用于去除小的白色噪点,并平滑物体轮廓。
- 闭运算 = 先膨胀后腐蚀,用于填充物体内部的小黑洞。
import cv2
import numpy as np
# 创建一个带噪声的图像
img = np.zeros((50, 50), np.uint8)
cv2.rectangle(img, (10, 10), (40, 40), 255, -1)
cv2.circle(img, (25, 25), 5, 0, -1) # 内部噪声
cv2.circle(img, (5, 5), 2, 255, -1) # 外部噪声
kernel = np.ones((3, 3), np.uint8)
# 开运算
opening = cv2.morphologyEx(img, cv2.MORPH_OPEN, kernel)
# 闭运算
closing = cv2.morphologyEx(img, cv2.MORPH_CLOSE, kernel)
cv2.imshow('Original', img)
cv2.imshow('Opening', opening)
cv2.imshow('Closing', closing)
cv2.waitKey(0)
cv2.destroyAllWindows()
直方图
直方图用于统计图像中像素强度(灰度值)的分布。
import cv2
import numpy as np
import matplotlib.pyplot as plt
img = cv2.imread('path/to/your/image.jpg', cv2.IMREAD_COLOR)
# 计算直方图
# cv2.calcHist([图像列表], 通道索引, 掩码, 直方图大小, 范围)
# 对于彩色图像,我们需要分别计算每个通道的直方图
hist_b = cv2.calcHist([img], [0], None, [256], [0, 256])
hist_g = cv2.calcHist([img], [1], None, [256], [0, 256])
hist_r = cv2.calcHist([img], [2], None, [256], [0, 256])
# 使用 Matplotlib 绘制直方图
plt.figure(figsize=(10, 5))'Color Histogram')
plt.xlabel('Pixel Value')
plt.ylabel('Count')
plt.plot(hist_b, color='blue', label='Blue Channel')
plt.plot(hist_g, color='green', label='Green Channel')
plt.plot(hist_r, color='red', label='Red Channel')
plt.legend()
plt.show()
项目实战:人脸检测
我们将使用 OpenCV 自带的 Haar 级联分类器来检测图像中的人脸。
准备工作
你需要下载 Haar 级联分类器的 XML 文件,最常用的是 haarcascade_frontalface_default.xml。
你可以从这里下载:OpenCV GitHub Repository
将下载的 XML 文件放在你的项目文件夹中。
代码示例
import cv2
# --- 1. 加载 Haar 级联分类器 ---
# 确保文件路径正确
face_cascade_path = 'haarcascade_frontalface_default.xml'
eye_cascade_path = 'haarcascade_eye.xml' # (可选) 检测眼睛
# 创建级联分类器对象
face_cascade = cv2.CascadeClassifier(face_cascade_path)
eye_cascade = cv2.CascadeClassifier(eye_cascade_path)
# 检查分类器是否加载成功
if face_cascade.empty():
print(f"错误:无法加载人脸级联分类器,请检查路径: {face_cascade_path}")
exit()
# --- 2. 读取图像 ---
image_path = 'path/to/your/people.jpg' # <--- 替换为包含人脸的图片路径
img = cv2.imread(image_path)
if img is None:
print(f"错误:无法加载图像,请检查路径: {image_path}")
exit()
# --- 3. 转换为灰度图 ---
# 人脸检测在灰度图上更快、更准确
gray_img = cv2.cvtColor(img, cv2.COLOR_BGR2GRAY)
# --- 4. 检测人脸 ---
# detectMultiScale() 会返回检测到的人脸的列表
# 每个人脸是一个矩形 [x, y, w, h]
# 参数:
# - scaleFactor: 图像缩放比例,用于创建图像金字塔
# - minNeighbors: 检测矩形保留多少个邻近矩形才算有效
# - minSize: 可能的最小人脸尺寸
faces = face_cascade.detectMultiScale(gray_img, scaleFactor=1.1, minNeighbors=5, minSize=(30, 30))
# --- 5. 在检测到的人脸上画矩形 ---
print(f"发现 {len(faces)} 张人脸。")
for (x, y, w, h) in faces:
# 在原图上画一个矩形框
cv2.rectangle(img, (x, y), (x + w, y + h), (255, 0, 0), 2)
# (可选) 在人脸区域内检测眼睛
roi_gray = gray_img[y:y+h, x:x+w]
roi_color = img[y:y+h, x:x+w]
eyes = eye_cascade.detectMultiScale(roi_gray)
for (ex, ey, ew, eh) in eyes:
cv2.rectangle(roi_color, (ex, ey), (ex + ew, ey + eh), (0, 255, 0), 2)
# --- 6. 显示结果 ---
cv2.imshow('Face Detection', img)
# --- 7. 等待按键并关闭窗口 ---
cv2.waitKey(0)
cv2.destroyAllWindows()
总结与进阶
这份教程涵盖了 OpenCV 的核心功能,要深入学习,你可以:
- 官方文档:OpenCV-Python Tutorials 是最权威的资源。
- 实战项目:尝试实现更复杂的项目,如物体识别、车牌识别、手势识别、图像风格迁移等。
- 学习机器学习:OpenCV 与机器学习(特别是 DNN 模块)结合,可以实现更强大的功能,如使用预训练的深度学习模型进行分类、检测等。
- 关注性能:对于实时应用,了解如何优化代码(例如使用多线程、GPU加速)非常重要。
希望这份教程能帮助你开启 OpenCV 的精彩世界!
