Rozpoznawanie jadących aut.
Zastosowanie biblioteki OpenCV i sieci neuronowej do rozpoznawania jadących aut.
Po rozpoznawaniu twarzy za pomocą kaskad Haara, teraz przyszła kolej na analizę wideo. Nasz problem sprowadza się do rozpoznania jadących autostradą samochodów osobowych. W tym celu wykorzystamy wytrenowany model YOLO, który rozpoznaję m.in. samochody. Oprócz samochodów potrafi sklasyfikować jeszcze 79 innych obiektów.
YOLO, czyli "You Only Look Once"
, to innowacyjna architektura sieci neuronowej stosowana w obszarze detekcji obiektów w czasie rzeczywistym. Opracowany przez zespół badawczy na Uniwersytecie Waszyngtońskim, YOLO zmienił podejście do rozpoznawania obiektów na obrazach i wideo.
To, co wyróżnia YOLO spośród innych metod, to jego zdolność do dokonywania detekcji jednocześnie na całym obrazie, a nie poprzez analizę fragmentów. Działa to znacznie szybciej niż tradycyjne metody, co sprawia, że YOLO jest idealny do zastosowań wymagających detekcji w czasie rzeczywistym, takich jak nadzór wideo, autonomiczne pojazdy czy nawet rozpoznawanie obiektów w grach wideo.
YOLO korzysta z sieci neuronowej typu konwolucyjnego (CNN) i dzieli obraz na siatkę komórek. Każda komórka jest odpowiedzialna za detekcję pewnej liczby klas obiektów. Ten innowacyjny sposób podejścia do detekcji obiektów sprawia, że YOLO jest skuteczny, precyzyjny i wyjątkowo szybki, co przyczyniło się do jego popularności wśród społeczności badawczej i praktyków zajmujących się przetwarzaniem obrazów.
Przykład działania skryptu wykorzystujący YOLO do zaznaczenia jadących samochodów.
Wokół każdego rozpoznanego samochodu osobowego program rysuje zielony prostokąt i opisuje go jakoSamochod
.
Pewnie zwróciliście uwagę, że program nie zaznacza samochodów ciężarowych ani autobusów. Nie zaznacza, bo nie został
wytrenowany aby to robić, więc tego nie robi.
Warto zwrócić jeszcze uwagę na fakt, że rozpoznawane są samochody w sytuacji, gdy duża ich część jest przysłonięta barierą oddzielającą pasy ruchu!
Aby lepiej się przyjrzeć można spowolnić film. W tym celu klikamy w zębate kółeczko, czyli Ustawienia
i wybieramy prędkość
0.25, wtedy łatwiej dostrzeżemy zielone prostokąty wokół samochodów.
Poniżej przedstawiam skrypt, który był odpowiedzialny za rozpoznawanie samochodów. Do działania skryptu niezbędna jest zainstalowana biblioteka OpenCV, NumPy oraz w folderze, z którego będzie uruchamiany skrypt muszą znajdować się trzy pliki:
- coco_bez_pl.names zawiera nazwy wykrywanych obiektów
- yolov4.cfg konfiguracja modelu
- yolov4.weight wytrenowany model
Plik skryptu oraz pliki coco_bez_pl.names
i yolov4.cfg
są do pobrania z GitHuba: Link.
Plik yolov4.weights
można pobrać tutaj.
import cv2
import numpy as np
# Wczytaj konfigurację i wytrenowane wagi YOLOv4
net = cv2.dnn.readNet("yolov4.cfg", "yolov4.weights")
# Odczytaj plik z nazwami klas
with open("coco_bez_pl.names", "r") as f:
classes = [line.strip() for line in f.readlines()]
layer_names = net.getUnconnectedOutLayersNames()
# Wczytaj plik wideo
cap = cv2.VideoCapture("output.mp4")
# Ustal rozmiar klatki
frame_width = int(cap.get(3))
frame_height = int(cap.get(4))
fps = 20
# Inicjalizacja VideoWriter
out1 = cv2.VideoWriter('rozpoznane.mp4', cv2.VideoWriter_fourcc(*'mp4v'), fps, (frame_width, frame_height))
while True:
# Pobieranie obrazu z pliku
ret, frame = cap.read()
if not ret:
break
height, width, _ = frame.shape
# Przetwarzanie obrazu przez sieć YOLOv4
blob = cv2.dnn.blobFromImage(frame, 0.00392, (416, 416), (0, 0, 0), True, crop=False)
net.setInput(blob)
outs = net.forward(layer_names)
# Przetwarzanie wyników detekcji
class_ids = []
confidences = []
boxes = []
for out in outs:
for detection in out:
scores = detection[5:]
class_id = np.argmax(scores)
confidence = scores[class_id]
if confidence > 0.5 and classes[class_id] == 'Samochod':
center_x = int(detection[0] * width)
center_y = int(detection[1] * height)
w = int(detection[2] * width)
h = int(detection[3] * height)
x = int(center_x - w / 2)
y = int(center_y - h / 2)
boxes.append([x, y, w, h])
confidences.append(float(confidence))
class_ids.append(class_id)
# Filtrowanie detekcji z użyciem tresholdu
indexes = cv2.dnn.NMSBoxes(boxes, confidences, 0.5, 0.4)
# Wyświetlanie wyników
font = cv2.FONT_HERSHEY_PLAIN
object_count = {}
for i in range(len(boxes)):
if i in indexes:
x, y, w, h = boxes[i]
label = str(classes[class_ids[i]])
cv2.rectangle(frame, (x, y), (x + w, y + h), (0, 255, 0), 3)
cv2.putText(frame, label, (x, y + 30), font, 1, (0, 0, 255), 2)
# Liczenie ilości obiektów danej klasy
if label in object_count:
object_count[label] += 1
else:
object_count[label] = 1
# Wyświetl obraz na ekranie
cv2.imshow('Detekcja aut', frame)
# Zapisz klatkę do pliku
out1.write(frame)
# Przerwij pętlę po naciśnięciu klawisza 'q'
if cv2.waitKey(1) & 0xFF == ord('q'):
break
# Zwolnij zasoby
out1.release()
cap.release()
cv2.destroyAllWindows()
# Wyświetlanie liczby obiektów klasy Samochod
for label, count in object_count.items():
print(f"Ilość obiektów klasy {label}: {count}")
Objaśnienie programu:
Importowanie bibliotek:
import cv2
import numpy as np
W tej części programu importowane są biblioteki OpenCV (cv2) oraz NumPy (numpy), które będą używane do przetwarzania obrazu.
Ładowanie modelu YOLOv4:
net = cv2.dnn.readNet('yolov4.cfg', 'yolov4.weights')
W tej części programu wczytywany jest wytrenowany model YOLOv4 z plików: konfiguracyjnego yolov4.cfg
i wytrenowanego modelu yolov4.weights
.
Ładowanie nazw klasyfikatorów:
classes = []
with open('coco_bez_pl.names', 'r') as f:
classes = [line.strip() for line in f.readlines()]
Wczytywane są nazwy klasyfikatorów z pliku coco_pl.names
.
Ustawienia pliku źródłowego wideo:
cap = cv2.VideoCapture("output.mp4")
Pętla główna:
while True:
ret, frame = cap.read()
if not ret:
break
Program znajduje się w pętli nieskończonej, w której kolejno odczytywany jest obraz z kamery.
Przetwarzanie obrazu przez sieć YOLOv4:
blob = cv2.dnn.blobFromImage(frame, 0.00392, (416, 416), (0, 0, 0), True, crop=False)
net.setInput(blob)
outs = net.forward(layer_names)
Przetwarzanie obrazu przez sieć YOLOv4 w celu wykrywania obiektów.
Wyświetlanie wyników na obrazie:
cv2.imshow("", frame)
Zaznaczone obiekty są wyświetlane na obrazie.
Zakończenie programu:
if cv2.waitKey(1) & 0xFF == ord('q'):
break
Pętla główna zostanie przerwana, gdy użytkownik naciśnie klawisz ‘q’.
Zamknięcie kamery i okien:
cap.release()
cv2.destroyAllWindows()
Po zakończeniu działania programu, zasoby kamery są zwalniane, a okna zamykane.
Liczenie i wyświetlanie ilości obiektów danej klasy:
for label, count in object_count.items():
print(f"Ilość obiektów klasy {label}: {count}")
Mam nadzieję, że powyższe wyjaśnienie pozwala Ci lepiej zrozumieć, jak działa ten program.
Krótkie wyjaśnienie działania funkcji cv2.dnn.blobFromImage
:
Funkcja cv2.dnn.blobFromImage
służy do przygotowywania danych obrazu przed przekazaniem ich do sieci neuronowej w bibliotece OpenCV. Poniżej wyjaśniam, co oznaczają poszczególne parametry tej funkcji:
cv2.dnn.blobFromImage(image, scalefactor, size, mean, swapRB, crop)
-
image: To jest obraz wejściowy, który chcemy przekazać do sieci neuronowej.
-
scalefactor: Skaluje wartości pikseli obrazu. W praktyce wartość 0.00392 jest często używana, ponieważ odpowiada wartości 1/255, co normalizuje wartości pikseli do zakresu od 0 do 1.
-
size: Rozmiar obrazu, który będzie przekazany do sieci neuronowej. W tym przypadku (416, 416) oznacza, że obraz będzie miał rozmiar 416 pikseli na 416 pikseli. To jest typowy rozmiar dla wielu modeli YOLO.
-
mean: Średnie wartości pikseli, które są odejmowane od pikseli obrazu. Często używane są wartości (0, 0, 0), co oznacza brak średniej korekty.
-
swapRB: Flaga określająca, czy zamienić miejsca wartości kanałów czerwonego (R) i niebieskiego (B) w obrazie. Wartość True oznacza zamianę.
-
crop: Flaga określająca, czy należy przyciąć obraz do wymaganego rozmiaru. W tym przypadku jest ustawiona na False, co oznacza brak przycinania.
Te parametry są dostosowywane w zależności od wymagań konkretnego modelu lub zastosowania. Wartości użyte w przykładowym kodzie są dość standardowe dla modeli YOLO.
Wartość piksela to liczba reprezentująca jasność lub kolor danego piksela na obrazie. W kontekście obrazu cyfrowego, obraz jest zazwyczaj podzielony na siatkę pikseli, gdzie każdy piksel reprezentuje najmniejszy element obrazu. Wartość piksela określa, jak jasny lub kolorowy jest ten konkretny punkt obrazu.
W przypadku obrazów w odcieniach szarości, każdy piksel ma jedną wartość, która określa jego jasność. Ta wartość zazwyczaj mieści się w zakresie od 0 do 255, gdzie 0 oznacza czarny, 255 oznacza biały, a wartości pośrednie reprezentują różne stopnie szarości.
W przypadku obrazów kolorowych, każdy piksel składa się z trzech składowych koloru: czerwonej (R), zielonej (G) i niebieskiej (B). Wartości każdej składowej mieszczą się również w zakresie od 0 do 255. Na przykład, wartość piksela (255, 0, 0) oznacza czerwony, (0, 255, 0) oznacza zielony, a (0, 0, 255) oznacza niebieski.
Wartości pikseli są kluczowe dla przetwarzania obrazów, a analiza i manipulacja tymi wartościami umożliwia wiele operacji, takich jak detekcja krawędzi, segmentacja obiektów, czy też korekcja kolorów.