强曰为道
与天地相似,故不违。知周乎万物,而道济天下,故不过。旁行而不流,乐天知命,故不忧.
文档目录

OpenCV 计算机视觉完全教程 / 第 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 CUDA 文档 docs.opencv.org/4.x/d2/dbc/cuda_intro CUDA 入门
CUDA 编程指南 docs.nvidia.com/cuda NVIDIA 官方
下一章 第 16 章 — Docker 部署 容器化/服务化

本章小结: 掌握了 OpenCV 三种 GPU 加速方式(UMat、CUDA 模块、DNN CUDA),理解了性能特点和选择策略,能够在合适的场景下大幅加速图像处理。