第 15 章 — GPU 加速
第 15 章 — GPU 加速
15.1 GPU 加速概述
OpenCV 提供三种 GPU 加速方式:
| 方式 | 复杂度 | 加速比 | 适用场景 |
|---|
| UMat(透明 API) | 最低 | 2-10× | 通用图像处理 |
| CUDA 模块 | 中等 | 10-100× | 高性能计算 |
| DNN CUDA | 低 | 10-50× | 深度学习推理 |
前置条件
# 检查 CUDA 支持
nvidia-smi
# 检查 OpenCV CUDA 编译
python3 -c "
import cv2
count = cv2.cuda.getCudaEnabledDeviceCount()
print(f'CUDA 设备: {count}')
print(cv2.getBuildInformation())
"
15.2 UMat 透明 API
UMat 是 OpenCV 的透明 API,使用方式与 Mat 完全一致,自动利用 GPU 加速。
import cv2
import numpy as np
import time
img = cv2.imread("photo.jpg")
# === CPU 路径 ===
start = time.perf_counter()
for _ in range(100):
gray = cv2.cvtColor(img, cv2.COLOR_BGR2GRAY)
blurred = cv2.GaussianBlur(gray, (15, 15), 0)
edges = cv2.Canny(blurred, 50, 150)
cpu_time = (time.perf_counter() - start) / 100 * 1000
# === GPU 路径(UMat)===
img_gpu = cv2.UMat(img) # 转换为 UMat
start = time.perf_counter()
for _ in range(100):
gray_gpu = cv2.cvtColor(img_gpu, cv2.COLOR_BGR2GRAY)
blurred_gpu = cv2.GaussianBlur(gray_gpu, (15, 15), 0)
edges_gpu = cv2.Canny(blurred_gpu, 50, 150)
gpu_time = (time.perf_counter() - start) / 100 * 1000
print(f"CPU: {cpu_time:.2f} ms")
print(f"GPU (UMat): {gpu_time:.2f} ms")
print(f"加速比: {cpu_time / gpu_time:.1f}×")
# UMat → NumPy 转换
result = edges_gpu.get() # 从 GPU 取回结果
UMat 使用限制
| 限制 | 说明 |
|---|
不支持 [] 索引 | 不能用 umat[y, x] 访问像素 |
不支持 np.array() | 需用 .get() 获取数据 |
| 部分函数不支持 | 某些 OpenCV 函数无 GPU 实现 |
| 内存管理 | GPU 内存有限,需及时释放 |
15.3 CUDA 模块
15.3.1 GPU 设备信息
import cv2
count = cv2.cuda.getCudaEnabledDeviceCount()
if count == 0:
print("没有 CUDA 设备")
exit()
for i in range(count):
info = cv2.cuda.DeviceInfo(i)
print(f"\nGPU #{i}: {info.name()}")
print(f" 计算能力: {info.majorVersion()}.{info.minorVersion()}")
print(f" 总内存: {info.totalMemory() / 1024**3:.1f} GB")
print(f" 多处理器: {info.multiProcessorCount()}")
print(f" 是否集成: {info.isIntegrated()}")
print(f" 是否兼容: {info.isCompatible()}")
15.3.2 CUDA 图像处理
import cv2
import numpy as np
import time
img = cv2.imread("photo.jpg")
h, w = img.shape[:2]
# 上传到 GPU
gpu_img = cv2.cuda_GpuMat()
gpu_img.upload(img)
# === CUDA 高斯模糊 ===
# 创建 CUDA 高斯滤波器
gauss_filter = cv2.cuda.createGaussianFilter(
cv2.CV_8UC3, cv2.CV_8UC3, (15, 15), 5.0
)
# 应用滤波器
start = time.perf_counter()
gpu_blurred = gauss_filter.apply(gpu_img)
gpu_time = (time.perf_counter() - start) * 1000
# CPU 对比
start = time.perf_counter()
cpu_blurred = cv2.GaussianBlur(img, (15, 15), 5.0)
cpu_time = (time.perf_counter() - start) * 1000
print(f"CUDA 高斯模糊: {gpu_time:.2f} ms")
print(f"CPU 高斯模糊: {cpu_time:.2f} ms")
print(f"加速比: {cpu_time / gpu_time:.1f}×")
# 下载结果
result = gpu_blurred.download()
15.3.3 CUDA 常用操作
import cv2
# 上传图像到 GPU
gpu_img = cv2.cuda_GpuMat()
gpu_img.upload(img)
# 颜色转换
gpu_gray = cv2.cuda.cvtColor(gpu_img, cv2.COLOR_BGR2GRAY)
# 调整大小
gpu_resized = cv2.cuda.resize(gpu_img, (640, 480))
# 阈值处理
_, gpu_thresh = cv2.cuda.threshold(gpu_gray, 127, 255,
cv2.THRESH_BINARY)
# 边缘检测(Canny)
gpu_edges = cv2.cuda.Canny(gpu_gray, 50, 150)
# 滤波
# 创建滤波器
box_filter = cv2.cuda.createBoxFilter(cv2.CV_8UC1, cv2.CV_8UC1, (5, 5))
gpu_boxed = box_filter.apply(gpu_gray)
# 形态学操作
morph_filter = cv2.cuda.createMorphologyFilter(
cv2.MORPH_DILATE, cv2.CV_8UC1,
cv2.getStructuringElement(cv2.MORPH_RECT, (5, 5))
)
gpu_dilated = morph_filter.apply(gpu_thresh)
# 算术运算
gpu_result = cv2.cuda.add(gpu_img, gpu_img)
gpu_result = cv2.cuda.multiply(gpu_img, 2.0)
# 下载结果
result = gpu_result.download()
15.3.4 CUDA 图像处理函数
| 函数 | CPU 等价 | 说明 |
|---|
cuda::cvtColor | cv2.cvtColor | 颜色转换 |
cuda::resize | cv2.resize | 缩放 |
cuda::threshold | cv2.threshold | 阈值 |
cuda::Canny | cv2.Canny | 边缘检测 |
cuda::warpAffine | cv2.warpAffine | 仿射变换 |
cuda::warpPerspective | cv2.warpPerspective | 透视变换 |
cuda::meanShift | 无直接等价 | 均值漂移 |
cuda::calcHist | cv2.calcHist | 直方图 |
cuda::equalizeHist | cv2.equalizeHist | 均衡化 |
cuda::bilateralFilter | cv2.bilateralFilter | 双边滤波 |
cuda::HoughLines | cv2.HoughLines | 霍夫线 |
15.4 CUDA 流(Stream)
import cv2
# 创建 CUDA 流(异步操作)
stream = cv2.cuda_Stream()
# 异步上传
gpu_img1 = cv2.cuda_GpuMat()
gpu_img1.upload(img1, stream)
gpu_img2 = cv2.cuda_GpuMat()
gpu_img2.upload(img2, stream)
# 异步处理
result = cv2.cuda.add(gpu_img1, gpu_img2, stream=stream)
# 等待完成
stream.waitForCompletion()
# 下载
output = result.download()
15.5 DNN CUDA 推理
import cv2
import numpy as np
net = cv2.dnn.readNetFromONNX("model.onnx")
# 设置 CUDA 后端
net.setPreferableBackend(cv2.dnn.DNN_BACKEND_CUDA)
net.setPreferableTarget(cv2.dnn.DNN_TARGET_CUDA_FP16)
# 正常使用,OpenCV 自动 GPU 推理
blob = cv2.dnn.blobFromImage(image, 1/255.0, (640, 640),
swapRB=True, crop=False)
net.setInput(blob)
output = net.forward()
15.6 性能对比
典型操作 GPU vs CPU(1080p 图像)
| 操作 | CPU (ms) | CUDA (ms) | 加速比 |
|---|
| 高斯模糊 15×15 | 3.5 | 0.8 | 4.4× |
| Canny 边缘 | 5.0 | 1.5 | 3.3× |
| 调整大小 50% | 1.5 | 0.3 | 5.0× |
| 形态学膨胀 | 1.2 | 0.4 | 3.0× |
| 颜色转换 | 0.8 | 0.2 | 4.0× |
| 直方图计算 | 0.5 | 0.15 | 3.3× |
| DNN 推理 (YOLOv8s) | 150ms | 15ms | 10× |
注意: 数据基于 RTX 3060 + OpenCV 4.10,实际性能因硬件和图像尺寸而异。GPU 加速对小图像(< 720p)可能不明显,传输开销可能抵消加速收益。
15.7 选择指南
需要 GPU 加速?
├─ 图像尺寸 ≥ 1080p?
│ ├─ 是 → 考虑 CUDA
│ └─ 否 → CPU 可能已足够
├─ 需要处理视频流?
│ └─ → CUDA(帧率要求高)
├─ DNN 推理?
│ ├─ 有 CUDA GPU → DNN_BACKEND_CUDA
│ └─ 无 GPU → DNN_BACKEND_OPENCV + OpenVINO
├─ 复杂图像处理管线?
│ └─ → UMat 透明 API(代码改动最小)
└─ 内存受限?
└─ → UMat(自动管理 GPU 内存)
15.8 常见问题
| 问题 | 原因 | 解决方案 |
|---|
getCudaEnabledDeviceCount()=0 | 未编译 CUDA | 源码编译 WITH_CUDA=ON |
| GPU 内存不足 | 图像太大 | 分块处理或减小尺寸 |
| UMat 比 Mat 更慢 | 小图像 + 传输开销 | 仅对大图像使用 GPU |
| CUDA 版本不匹配 | 驱动/CUDA/编译版本不一致 | 统一版本 |
| DNN CUDA 很慢 | 未启用 FP16 | 使用 DNN_TARGET_CUDA_FP16 |
15.9 扩展阅读
本章小结: 掌握了 OpenCV 三种 GPU 加速方式(UMat、CUDA 模块、DNN CUDA),理解了性能特点和选择策略,能够在合适的场景下大幅加速图像处理。