Bemerkung
Hallo, willkommen in der SunFounder Raspberry Pi & Arduino & ESP32 Enthusiasten-Community auf Facebook! Tauchen Sie mit anderen Enthusiasten tiefer in Raspberry Pi, Arduino und ESP32 ein.
Warum beitreten?
Expertenunterstützung: Lösen Sie Probleme nach dem Kauf und technische Herausforderungen mit Hilfe unserer Community und unseres Teams.
Lernen & Teilen: Tauschen Sie Tipps und Tutorials aus, um Ihre Fähigkeiten zu verbessern.
Exklusive Vorschauen: Erhalten Sie frühzeitigen Zugang zu neuen Produktankündigungen und Sneak Peeks.
Sonderrabatte: Genießen Sie exklusive Rabatte auf unsere neuesten Produkte.
Festliche Aktionen und Gewinnspiele: Nehmen Sie an Gewinnspielen und Feiertagsaktionen teil.
👉 Bereit, mit uns zu entdecken und zu gestalten? Klicken Sie auf [here] und treten Sie noch heute bei!
6. CAMShift-Objektverfolgung
Im vorherigen Kapitel haben wir den MeanShift-Algorithmus kennengelernt, der ein Zielobjekt in einem Video anhand seines Farbhistogramms kontinuierlich verfolgen kann. In diesem Abschnitt stellen wir CAMShift (Continuously Adaptive Mean Shift) vor. Dieser erweitert MeanShift, indem er Fenstergröße und Orientierung automatisch anpasst, wodurch er für reale Anwendungen deutlich praktischer wird. Zusätzlich verfolgen wir in diesem Beispiel ein Ziel auf Basis der Helligkeit statt der Farbe, was in der Praxis ebenfalls sehr häufig vorkommt.
1. Algorithmusmerkmale
MeanShift kann nur die Position eines Zielobjekts verfolgen und verwendet ein Fenster mit fester Größe. CAMShift verfolgt die Position und passt Fenstergröße und Winkel automatisch an.
Beispielsweise wächst das Tracking-Fenster, wenn sich das Zielobjekt der Kamera nähert; es verkleinert sich, wenn sich das Objekt entfernt; und wenn sich das Objekt dreht, rotiert auch das Tracking-Fenster entsprechend.
2. Code ausführen
Wichtig
Stellen Sie vor dem Start sicher, dass:
das Pan-Tilt-Modul montiert ist
Sie Zugriff auf den Raspberry-Pi-Desktop haben
das Codepaket installiert ist
das Fusion HAT+ installiert und konfiguriert ist
OpenCV installiert ist
Detaillierte Anweisungen finden Sie unter 0. OpenCV einrichten.
Öffnen Sie das Terminal und geben Sie den folgenden Befehl ein:
cd ~/ai-lab-kit/opencv_python python3 cv_6_camshift.py
Wenn Sie das Programm ausführen, erscheint ein OpenCV-Fenster mit dem Namen CAMShift Tracker und beginnt mit der Wiedergabe der Videodatei sample3.mp4.
Das Programm verfolgt die schwarze Katze mithilfe des CAMShift-Algorithmus (Continuously Adaptive Mean Shift).
Ein grünes, rotiertes Begrenzungsrechteck wird um das verfolgte Objekt gezeichnet. Während sich die Katze bewegt oder ihre Größe und Orientierung ändert, passt das Tracking-Fenster automatisch seine Position, Größe und seinen Winkel an.
Sie können das Programm auf zwei Arten beenden:
Drücken Sie die q-Taste auf der Tastatur
Schließen Sie das Fenster über die Schaltfläche zum Schließen (X)
Nach dem Beenden stoppt die Videowiedergabe und alle OpenCV-Fenster werden geschlossen.
3. Vollständiger Code
Öffnen Sie cv_6_camshift.py, um den vollständigen Code anzusehen.
# Python program to demonstrate CAMShift (tracking a dark object)
import numpy as np
import cv2
# Read video
cap = cv2.VideoCapture("sample3.mp4")
# Retrieve the first frame from the video
ret, frame = cap.read()
if not ret:
raise RuntimeError("Cannot read the video file.")
# Set the initial region for tracking window (x, y, width, height)
x, y, w, h = 100, 200, 40, 40
track_window = (x, y, w, h)
# Convert first frame to HSV
hsv = cv2.cvtColor(frame, cv2.COLOR_BGR2HSV)
# Extract ROI (only the target area) in HSV
hsv_roi = hsv[y:y+h, x:x+w]
# For tracking a black object, we keep dark pixels (low V) inside ROI
# V channel is hsv[..., 2], so we build a mask based on V <= 80
roi_mask = cv2.inRange(hsv_roi, np.array((0, 0, 0)), np.array((180, 255, 80)))
# Build histogram on V channel (channel index 2) within ROI
# Use 256 bins for V (0~256) to match back projection range
roi_hist = cv2.calcHist([hsv_roi], [2], roi_mask, [256], [0, 256])
cv2.normalize(roi_hist, roi_hist, 0, 255, cv2.NORM_MINMAX)
# Termination criteria for CAMShift
term_crit = (cv2.TERM_CRITERIA_EPS | cv2.TERM_CRITERIA_COUNT, 10, 1)
# FPS delay (fallback if FPS is unavailable)
fps = cap.get(cv2.CAP_PROP_FPS)
if not fps or fps <= 1e-3:
fps = 30.0
delay_ms = int(1000 / fps)
WINDOW_NAME = "CAMShift Tracker"
while True:
ret, frame = cap.read()
# If video ends, restart from beginning
if not ret:
cap.set(cv2.CAP_PROP_POS_FRAMES, 0)
continue
# Convert frame to HSV
hsv = cv2.cvtColor(frame, cv2.COLOR_BGR2HSV)
# Back projection on V channel using ROI histogram (range 0~256)
back_proj = cv2.calcBackProject([hsv], [2], roi_hist, [0, 256], 1)
# Apply CAMShift
rot_rect, track_window = cv2.CamShift(back_proj, track_window, term_crit)
# Draw rotated rectangle
pts = cv2.boxPoints(rot_rect).astype(np.int32)
cv2.polylines(frame, [pts], True, (0, 255, 0), 2)
cv2.putText(frame, "CAMShift Tracker", (10, 30),
cv2.FONT_HERSHEY_SIMPLEX, 1, (0, 255, 0), 2)
cv2.imshow(WINDOW_NAME, frame)
# Keyboard + GUI events
key = cv2.waitKey(delay_ms) & 0xFF
if key == ord("q"):
break
# Exit if user closes the window (click X)
if cv2.getWindowProperty(WINDOW_NAME, cv2.WND_PROP_VISIBLE) < 1:
break
cap.release()
cv2.destroyAllWindows()
4. Code-Erklärung
Videodatei öffnen und das erste Frame lesen:
cap = cv2.VideoCapture("sample3.mp4") ret, frame = cap.read() if not ret: raise RuntimeError("Cannot read the video file.")
CAMShift benötigt ein initiales Frame, um zu lernen, welches Objekt verfolgt werden soll.
Das anfängliche Tracking-Fenster (ROI) festlegen:
x, y, w, h = 100, 200, 40, 40 track_window = (x, y, w, h)
Dieses Rechteck sollte das Zielobjekt im ersten Frame vollständig abdecken. Während des Trackings wird dieses Fenster von CAMShift automatisch aktualisiert.
Das erste Frame in HSV umwandeln und die ROI extrahieren:
hsv = cv2.cvtColor(frame, cv2.COLOR_BGR2HSV) hsv_roi = hsv[y:y+h, x:x+w]
HSV ist für Tracking praktisch, da bestimmte Kanäle (zum Beispiel V für Helligkeit) gezielt verwendet werden können.
Eine Maske für ein dunkles Objekt erstellen (niedrige V-Werte):
roi_mask = cv2.inRange(hsv_roi, np.array((0, 0, 0)), np.array((180, 255, 80)))
Dadurch bleiben nur „dunkle“ Pixel innerhalb der ROI erhalten. Für schwarze oder dunkle Objekte ist die Helligkeit (V) häufig das wichtigste Merkmal.
Ein Histogramm des V-Kanals berechnen und normalisieren:
roi_hist = cv2.calcHist([hsv_roi], [2], roi_mask, [256], [0, 256]) cv2.normalize(roi_hist, roi_hist, 0, 255, cv2.NORM_MINMAX)
Kanal
2entspricht dem V-Kanal (Value/Helligkeit) im HSV-Farbraum.Das Histogramm beschreibt, wie „dunkel oder hell“ die Ziel-ROI ist.
Die Normalisierung verbessert die Stabilität des Trackings.
Abbruchkriterien für CAMShift festlegen:
term_crit = (cv2.TERM_CRITERIA_EPS | cv2.TERM_CRITERIA_COUNT, 10, 1)
CAMShift beendet die Aktualisierung, wenn entweder 10 Iterationen erreicht sind oder die Bewegung kleiner als 1 Pixel ist.
Wiedergabegeschwindigkeit anhand der FPS festlegen:
fps = cap.get(cv2.CAP_PROP_FPS) if not fps or fps <= 1e-3: fps = 30.0 delay_ms = int(1000 / fps)
Dadurch wird eine Verzögerung gesetzt, sodass das Video ungefähr mit seiner ursprünglichen Bildrate abgespielt wird.
Eine Wahrscheinlichkeitskarte mittels Back Projection erstellen (V-Kanal):
back_proj = cv2.calcBackProject([hsv], [2], roi_hist, [0, 256], 1)
Die Back Projection hebt Pixel im Frame hervor, deren V-Werte dem Histogramm der ROI entsprechen. Höhere Werte in
back_projbedeuten eine größere Wahrscheinlichkeit, dass es sich um das Zielobjekt handelt.Tracking mit CAMShift durchführen und das Fenster aktualisieren:
rot_rect, track_window = cv2.CamShift(back_proj, track_window, term_crit)
CAMShift basiert auf MeanShift, kann jedoch zusätzlich Größe und Rotation des Tracking-Fensters anpassen.
track_windowwird in jedem Frame aktualisiert.rot_rectenthält ein rotiertes Rechteck (Zentrum, Größe, Winkel).
Das rotierte Tracking-Rechteck zeichnen:
pts = cv2.boxPoints(rot_rect).astype(np.int32) cv2.polylines(frame, [pts], True, (0, 255, 0), 2)
Dadurch wird das rotierte Rechteck in vier Eckpunkte umgewandelt und im Frame eingezeichnet.
Abbruchbedingungen (Tastatur + Fensterschließen):
key = cv2.waitKey(delay_ms) & 0xFF if key == ord("q"): break if cv2.getWindowProperty(WINDOW_NAME, cv2.WND_PROP_VISIBLE) < 1: break
Drücken Sie
q, um das Programm zu beenden, oder schließen Sie das Fenster für einen sicheren Abbruch.Ressourcen freigeben:
cap.release() cv2.destroyAllWindows()
Geben Sie am Ende immer die Videodatei frei und schließen Sie alle Fenster.
5. CAMShift vs. MeanShift
Merkmal |
MeanShift |
CAMShift |
|---|---|---|
Fenstergröße |
Fest |
Adaptiv |
Winkel |
Nicht unterstützt |
Unterstützt Rotation |
Tracking-Genauigkeit |
Mittel |
Höher, anpassungsfähiger |
Anwendungen |
Statische Zielobjekte |
Komplexe Bewegungen, rotierende Zielobjekte |
CAMShift ist eine Weiterentwicklung von MeanShift und kann Zielverformungen, Rotation und Distanzänderungen besser handhaben – daher eignet es sich gut für reale Anwendungsszenarien.
6. Erweiterungen und Übungen
Passen Sie die
inRange-Schwellenwerte an, um grüne oder blaue Zielobjekte zu verfolgenKombinieren Sie das Programm mit einer Live-Kameraquelle, um ein Echtzeit-Tracking-System auf Farbbasis zu erstellen
7. Erweitert: Interaktive ROI-Auswahl und automatische HSV-Schwellenwerte
Wie im vorherigen Abschnitt kann dieses Projekt ebenfalls die Mausinteraktion verwenden, um die ROI auszuwählen und die HSV-Schwellenwerte automatisch anzupassen.
Führen Sie cv_6_camshift_auto.py aus, um den angepassten Code zu verwenden.
cd ~/ai-lab-kit/opencv_python
python3 cv_6_camshift_auto.py
Wenn Sie das Programm ausführen, wird das erste Frame des Videos angezeigt, und Sie werden aufgefordert, mit der Maus eine Region of Interest (ROI) auszuwählen.
Ziehen Sie mit der Maus ein Rechteck um das Zielobjekt und drücken Sie anschließend Enter oder Leertaste, um die Auswahl zu bestätigen. Drücken Sie Esc, um die Auswahl abzubrechen.
Nach der Auswahl der ROI erscheint ein Fenster mit dem Namen CAMShift Tracker. Das ausgewählte Objekt wird mit einem grünen, rotierten Rechteck verfolgt, und das Tracking-Fenster passt seine Position, Größe und Orientierung automatisch an, während sich das Objekt bewegt.
So beenden Sie das Programm:
Drücken Sie die q-Taste auf der Tastatur
Oder schließen Sie das Anzeigefenster über die Schaltfläche zum Schließen (X)
Nach dem Beenden stoppt die Videowiedergabe und alle OpenCV-Fenster werden geschlossen.
hsv0 = cv2.cvtColor(frame, cv2.COLOR_BGR2HSV)
roi_hsv = hsv0[y:y + h, x:x + w]
# Split ROI HSV channels
h_roi = roi_hsv[:, :, 0]
s_roi = roi_hsv[:, :, 1]
v_roi = roi_hsv[:, :, 2]
# Use percentiles to get robust ranges (ignore outliers)
h_low, h_high = np.percentile(h_roi, [5, 95])
s_low, s_high = np.percentile(s_roi, [5, 95])
v_low, v_high = np.percentile(v_roi, [5, 95])
# Add padding so the range is not too tight
pad_h, pad_s, pad_v = 10, 20, 20
lower = np.array([
max(int(h_low) - pad_h, 0),
max(int(s_low) - pad_s, 0),
max(int(v_low) - pad_v, 0)
], dtype=np.uint8)
upper = np.array([
min(int(h_high) + pad_h, 180),
min(int(s_high) + pad_s, 255),
min(int(v_high) + pad_v, 255)
], dtype=np.uint8)
# Mask ONLY the ROI (do not use the whole frame mask)
roi_mask = cv2.inRange(roi_hsv, lower, upper)
...