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

OpenCV 计算机视觉完全教程 / 第 15 章 — GPU 加速

第 15 章 — GPU 加速

15.1 GPU 加速概述

OpenCV 提供三种 GPU 加速方式:

方式复杂度加速比适用场景
UMat(透明 API)最低2-10×通用图像处理
CUDA 模块中等10-100×高性能计算
DNN CUDA10-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::cvtColorcv2.cvtColor颜色转换
cuda::resizecv2.resize缩放
cuda::thresholdcv2.threshold阈值
cuda::Cannycv2.Canny边缘检测
cuda::warpAffinecv2.warpAffine仿射变换
cuda::warpPerspectivecv2.warpPerspective透视变换
cuda::meanShift无直接等价均值漂移
cuda::calcHistcv2.calcHist直方图
cuda::equalizeHistcv2.equalizeHist均衡化
cuda::bilateralFiltercv2.bilateralFilter双边滤波
cuda::HoughLinescv2.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×153.50.84.4×
Canny 边缘5.01.53.3×
调整大小 50%1.50.35.0×
形态学膨胀1.20.43.0×
颜色转换0.80.24.0×
直方图计算0.50.153.3×
DNN 推理 (YOLOv8s)150ms15ms10×

注意: 数据基于 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_introCUDA 入门
CUDA 编程指南docs.nvidia.com/cudaNVIDIA 官方
下一章第 16 章 — Docker 部署容器化/服务化

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