OpenCV mit CUDA einrichten: GPU-beschleunigte Bildverarbeitung
Symbolbild, KI-generiert (gpt-image-1)
Die OpenCV-Pakete aus pip und aus den Ubuntu-Quellen sind ohne CUDA gebaut. Wer in der
Bildverarbeitung die GPU nutzen will, kommt deshalb um eine eigene Übersetzung aus dem
Quellcode nicht herum. Am Ende dieser Anleitung steht ein OpenCV, dessen cv2.cuda-Module
verfügbar sind, und ein Benchmark, der zeigt, ob sich der Aufwand für die eigene Pipeline
überhaupt gelohnt hat. Woran es typischerweise scheitert: an einer falsch gesetzten Compute
Capability und an der Erwartung, dass jede Operation schneller wird.
Vorher klären: Lohnt sich die GPU für diese Pipeline?
Nicht jede Operation profitiert. Der Weg vom Hauptspeicher in den GPU-Speicher und zurück kostet Zeit, und bei einer einzelnen kleinen Operation frisst dieser Transfer den Gewinn vollständig auf. Tragfähig wird es bei hochauflösenden Bildern, bei mehreren aufeinander folgenden Filtern und dann, wenn der Frame ohnehin schon auf der GPU liegt — etwa weil danach ein Netz darauf rechnet.
Die Regel dahinter: Es zählt nicht die einzelne Operation, sondern wie lange ein Frame am Stück auf der GPU bleibt. Für Resize und Crop auf Full-HD ist die CPU schnell genug — und spart die Bauerei.
Voraussetzungen
- Ubuntu 22.04 LTS, NVIDIA-Treiber und CUDA Toolkit installiert
-
nvcc --versionundnvidia-smiliefern beide eine Ausgabe - 8 GB freier Speicher auf
/tmpund etwas Geduld
Sind Treiber und Toolkit noch nicht eingerichtet, führen die Anleitungen zum NVIDIA-Treiber unter Linux und zur CUDA-Einrichtung dorthin.
Schritt 1: Abhängigkeiten installieren
sudo apt update
sudo apt install -y \
cmake g++ wget unzip \
libgtk2.0-dev pkg-config \
libjpeg-dev libpng-dev libtiff-dev \
libavcodec-dev libavformat-dev libswscale-dev \
libgstreamer1.0-dev libgstreamer-plugins-base1.0-dev \
python3-dev python3-numpy
Schritt 2: Quellcode laden
Haupt- und Contrib-Repository müssen dieselbe Version haben — die CUDA-Module liegen im Contrib-Teil.
cd /tmp
VER=4.9.0
wget -O opencv.zip https://github.com/opencv/opencv/archive/$VER.zip
wget -O opencv_contrib.zip https://github.com/opencv/opencv_contrib/archive/$VER.zip
unzip -q opencv.zip
unzip -q opencv_contrib.zip
Schritt 3: Compute Capability ermitteln
Der häufigste Fehlgriff im ganzen Ablauf. Wird hier ein falscher Wert eingetragen, übersetzt der Build fehlerfrei — zur Laufzeit findet OpenCV dann aber kein passendes Gerät.
nvidia-smi --query-gpu=compute_cap --format=csv,noheader
# 8.6 = Ampere (z. B. RTX 3060), 8.9 = Ada (z. B. RTX 4060)
Schritt 4: Build konfigurieren
Den ermittelten Wert bei CUDA_ARCH_BIN eintragen. Innerhalb des Befehls stehen keine
Kommentare — ein # hinter einem Zeilenumbruch-Backslash bricht den Aufruf ab.
mkdir -p /tmp/opencv-4.9.0/build
cd /tmp/opencv-4.9.0/build
cmake -D CMAKE_BUILD_TYPE=RELEASE \
-D CMAKE_INSTALL_PREFIX=/usr/local \
-D OPENCV_EXTRA_MODULES_PATH=/tmp/opencv_contrib-4.9.0/modules \
-D WITH_CUDA=ON \
-D CUDA_ARCH_BIN="8.6" \
-D WITH_CUDNN=ON \
-D OPENCV_DNN_CUDA=ON \
-D ENABLE_FAST_MATH=ON \
-D CUDA_FAST_MATH=ON \
-D WITH_CUBLAS=ON \
-D BUILD_opencv_python3=ON \
-D BUILD_TESTS=OFF \
-D BUILD_EXAMPLES=OFF \
..
Bevor es weitergeht, die CMake-Ausgabe lesen. Steht dort NVIDIA CUDA: NO, ist jede weitere
Minute Rechenzeit verschenkt:
-- NVIDIA CUDA: YES (ver 12.3, CUFFT CUBLAS FAST_MATH)
-- cuDNN: YES (ver 8.x.x)
-- CUDA modules: cudaarithm cudabgsegm cudacodec ...
Schritt 5: Übersetzen und installieren
make -j$(nproc)
sudo make install
sudo ldconfig
Das dauert je nach Prozessor 20 bis 60 Minuten. Auf einem Industrie-PC mit vier Kernen ist
eher das obere Ende zu erwarten — und ein -j$(nproc) kann bei knappem Arbeitsspeicher
kippen. Als grober Anhalt: rund 1,5 GB RAM je paralleler Übersetzungseinheit. Bei 8 GB
also besser make -j4 als make -j8.
Schritt 6: Prüfen, ob CUDA wirklich drin ist
import cv2
print(f"OpenCV: {cv2.__version__}")
print(f"CUDA-Geräte: {cv2.cuda.getCudaEnabledDeviceCount()}")
if cv2.cuda.getCudaEnabledDeviceCount() > 0:
cv2.cuda.setDevice(0)
print(f"GPU: {cv2.cuda.DeviceInfo(0).name()}")
Steht dort 0, wurde entweder das falsche Python gefunden oder der Build lief ohne CUDA.
Schritt 7: Bilder auf der GPU verarbeiten
import cv2
img_cpu = cv2.imread("frame.png")
img_gpu = cv2.cuda_GpuMat()
img_gpu.upload(img_cpu)
img_gray_gpu = cv2.cuda.cvtColor(img_gpu, cv2.COLOR_BGR2GRAY)
img_resized_gpu = cv2.cuda.resize(img_gray_gpu, (640, 640))
blur = cv2.cuda.createGaussianFilter(cv2.CV_8UC1, cv2.CV_8UC1, (5, 5), 1.5)
img_blur_gpu = blur.apply(img_resized_gpu)
img_result = img_blur_gpu.download()
Entscheidend ist die Reihenfolge: ein upload, dann alle Operationen, dann ein download.
Wird zwischendurch heruntergeladen, um eine CPU-Funktion einzuschieben, ist der Vorteil weg.
Schritt 8: Nachmessen statt glauben
import time
N = 100
start = time.perf_counter()
for _ in range(N):
gray = cv2.cvtColor(img_cpu, cv2.COLOR_BGR2GRAY)
resized = cv2.resize(gray, (640, 640))
cpu_ms = (time.perf_counter() - start) / N * 1000
img_gpu.upload(img_cpu)
start = time.perf_counter()
for _ in range(N):
gray_gpu = cv2.cuda.cvtColor(img_gpu, cv2.COLOR_BGR2GRAY)
resized_gpu = cv2.cuda.resize(gray_gpu, (640, 640))
cv2.cuda.Stream_Null().waitForCompletion()
gpu_ms = (time.perf_counter() - start) / N * 1000
print(f"CPU: {cpu_ms:.2f} ms | GPU: {gpu_ms:.2f} ms | Faktor {cpu_ms/gpu_ms:.1f}")
Der Aufruf von waitForCompletion() gehört zwingend dazu: CUDA arbeitet asynchron, ohne
diese Zeile misst man die Zeit, die das Absetzen der Befehle braucht — nicht ihre Ausführung.
Auf einem 4K-Frame (3840 × 2160) liegt der Faktor auf einer Karte der RTX-3060-Klasse bei
etwa 5. Das ist ein realistischer Wert für eine solche Kette; bei einer einzelnen Operation
auf Full-HD kann er unter 1 fallen — die GPU ist dann langsamer als die CPU.
Wenn es nicht funktioniert
cv2.cuda existiert nicht. Das neue OpenCV liegt in /usr/local, pip hat aber sein
eigenes Paket installiert. Mit python3 -c "import cv2; print(cv2.__file__)" prüfen, welches
gefunden wird, und ein pip uninstall opencv-python opencv-contrib-python vorschalten.
getCudaEnabledDeviceCount() liefert 0. Meist zeigt die CMake-Ausgabe schon
NVIDIA CUDA: NO — dann fehlte das Toolkit im Suchpfad. Seltener stimmt die Compute
Capability nicht, dann läuft der Build durch und das Gerät wird trotzdem nicht erkannt.
Der Build bricht mit c++: fatal error: Killed ab. Das ist der Speicher, nicht der Code:
Der Kernel hat den Compiler abgeschossen. Mit kleinerem -j-Wert erneut starten.
Und wo das hinführt
Steht die GPU-Pipeline, ist der nächste Schritt meist die Inferenz auf demselben Frame, ohne ihn zwischendurch zurückzuholen — der Weg dorthin führt über TensorRT und in der Anwendung über die Echtzeit-Objekterkennung am Edge. Welche Karte die Kette trägt, ist in der Auswahl der RTX-Karten für Inferenz aufgeschlüsselt.
Passende Hardware: PCIe-AI- und GPU-Karten sowie Box-PCs und Industrie-PCs als Plattform dafür.
Zurück zum Wiki-Inhaltsverzeichnis.