OpenCV 提供了專門針對 CUDA 優化的模組,這些模組使用 cv2.cuda
命名空間,並且可以直接使用 GPU 進行加速。,cv2.cuda
模塊需要在 OpenCV 編譯時啟用 CUDA 支援才能使用。
本文主要比較經過CMAKE重新編譯OpenCV使其支援Cuda,原OpenCV只支援CPU運算,重新編譯過的OpenCV,某些模組就可支援CUDA使用GPU來運算。
[OpenCV][Python]Win10+Cmake+VS2022編譯 OpenCV 及opencv_contrib
opencv_cudaimgproc
:圖像處理模組,包含濾波、邊緣檢測、幾何轉換等操作。opencv_cudaarithm
:算術運算,提供基本的矩陣和數學運算。opencv_cudafilters
:濾波運算,如高斯模糊、邊緣檢測等。opencv_cudawarping
:圖像幾何轉換,如旋轉、縮放、透視變換等。opencv_cudafeatures2d
:特徵檢測和匹配模組。opencv_cudaoptflow
:光流估算,適合視頻處理中的動態場景分析。opencv_cudastereo
:立體匹配和視差估計。cv2.cuda
提供的函數。通常來說,這些函數會有 GPU 專屬版本,比如 cv2.cuda_GpuMat
用來替代 cv2.Mat
。dnn
模塊本身可以進行 CUDA 加速,但其他模型(如 DnnSuperResImpl_create
)可能無法直接支持 GPU 加速。import cv2
import time
# CPU 版本的 Canny 邊緣檢測
def canny_edge_cpu(image):
if len(image.shape) == 3:
image = cv2.cvtColor(image, cv2.COLOR_BGR2GRAY)
return cv2.Canny(image, 60, 150)
# CUDA 版本的 Canny 邊緣檢測,使用 opencv_cudaimgproc 模組
def canny_edge_cuda(image):
gpu_img = cv2.cuda_GpuMat()
# 如果需要,將圖片轉換為灰階
if len(image.shape) == 3: # 檢查圖片是否為彩色 (BGR)
image = cv2.cvtColor(image, cv2.COLOR_BGR2GRAY)
# 將圖片上傳到 GPU 記憶體
gpu_img.upload(image)
# 創建 CUDA 版本的 Canny 邊緣檢測器
canny = cv2.cuda.createCannyEdgeDetector(60, 150)
# 執行 Canny 邊緣檢測
edges_gpu = canny.detect(gpu_img)
# 將結果從 GPU 下載回 CPU
result = edges_gpu.download()
return result
# 載入圖片
image = cv2.imread('D:/python/crab/Dnn_superres/111_out.png')
# 測量 CPU 版本 Canny 邊緣檢測的時間
start_time = time.time()
edges_cpu = canny_edge_cpu(image)
cpu_time = time.time() - start_time
print(f"CPU Canny 邊緣檢測時間:{cpu_time} 秒")
# 測量 CUDA 版本 Canny 邊緣檢測的時間
start_time = time.time()
edges_cuda = canny_edge_cuda(image)
cuda_time = time.time() - start_time
print(f"CUDA Canny 邊緣檢測時間:{cuda_time} 秒")
# 保存結果以進行比較
cv2.imwrite('edges_cpu.jpg', edges_cpu)
cv2.imwrite('edges_cuda.jpg', edges_cuda)
左邊為CPU
運算的結果,右邊為cuda.createCannyEdgeDetector
的結果,結果是差不多的,但明顯CPU
運算快很多。
將圖片移動X +20在存起來,來計算光流圖
import cv2
import numpy as np
import time
import matplotlib.pyplot as plt
def cpu_dense_optical_flow(prev_frame, next_frame):
# 使用 Farneback 光流法進行稠密光流計算
flow = cv2.calcOpticalFlowFarneback(prev_frame, next_frame, None, 0.5, 3, 15, 3, 5, 1.2, 0)
return flow
def cuda_dense_optical_flow(prev_frame, next_frame):
# 將圖像上傳到 GPUMat
gpu_prev_frame = cv2.cuda_GpuMat()
gpu_next_frame = cv2.cuda_GpuMat()
gpu_prev_frame.upload(prev_frame)
gpu_next_frame.upload(next_frame)
# 創建 Farneback 光流實例
farneback = cv2.cuda_FarnebackOpticalFlow.create(5, 0.5, False, 15, 3, 5, 1.2, 0)
# 計算光流
flow = farneback.calc(gpu_prev_frame, gpu_next_frame, None)
# 將結果下載回 CPU
flow_cpu = flow.download()
return flow_cpu
# 讀取連續兩幀的灰度圖像
prev_frame = cv2.imread(r'D:\python\crab\Dnn_superres\111_out.png', cv2.IMREAD_GRAYSCALE)
next_frame = cv2.imread(r'D:\python\crab\Dnn_superres\111_out_1.png', cv2.IMREAD_GRAYSCALE)
# 計算 CPU 光流
start_time = time.time()
cpu_flow = cpu_dense_optical_flow(prev_frame, next_frame)
cpu_time = time.time() - start_time
print(f"CPU Dense Optical Flow time: {cpu_time} seconds")
# 計算 CUDA 光流
start_time = time.time()
gpu_flow = cuda_dense_optical_flow(prev_frame, next_frame)
gpu_time = time.time() - start_time
print(f"CUDA Dense Optical Flow time: {gpu_time} seconds")
# 計算兩者的差異
difference = np.abs(cpu_flow - gpu_flow)
# 可視化差異圖
plt.figure(figsize=(10, 10))
# 展示 CPU 光流結果
plt.subplot(1, 3, 1)
plt.title("CPU Optical Flow")
plt.imshow(np.sqrt(cpu_flow[..., 0]**2 + cpu_flow[..., 1]**2), cmap='gray')
# 展示 GPU 光流結果
plt.subplot(1, 3, 2)
plt.title("GPU Optical Flow")
plt.imshow(np.sqrt(gpu_flow[..., 0]**2 + gpu_flow[..., 1]**2), cmap='gray')
# 展示差異圖
plt.subplot(1, 3, 3)
plt.title("Difference (CPU - GPU)")
plt.imshow(np.sqrt(difference[..., 0]**2 + difference[..., 1]**2), cmap='hot')
plt.show()
差異圖會用熱圖 (hot
colormap) 的形式展示差異,差異越大,顏色越亮。這樣可以很直觀地看到兩者之間的不同。
CUDA 可以顯著加速某些高並行的運算,特別是那些對 GPU 高效能設計有充分利用的任務(如稠密光流、DNN)。
但對於簡單或低並行度的任務,CPU 的執行速度可能會更快,因為它沒有資料傳輸開銷,而且 CPU 的時脈速度通常更快。
因此,是否使用 CUDA 來加速 OpenCV 運算取決於任務的類型、資料量大小、影像解析度以及 GPU 的硬體條件。