[PYTHON] Verwenden Sie dHash, um eine Szene in einem Rennspiel auf der Strecke zu lokalisieren

1. Zuallererst

Einer der ähnlichen Bildsuchalgorithmen ist dHash. Informationen zum Inhalt des Algorithmus finden Sie unter "Berechnen der Ähnlichkeit von Bildern mit Perceptual Hash". Ist leicht zu verstehen, aber ich verstehe, dass es sich um einen ähnlichen Bildsuchalgorithmus mit den folgenden Eigenschaften handelt.

Also habe ich versucht, mit diesem dHash die Position der Szene auf der Strecke anhand einer Szene (Onboard-Video) des Spiels des Rennspiels Assetto Corsa zu identifizieren.

Insbesondere ist der Fluss wie folgt.

概要.png

① Zunächst werden alle Frames extrahiert und als PNG-Bilder aus einem Wiedergabevideo (Onboard-Video) gespeichert, das den Kurs umgibt.

② Berechnet den dHash-Hashwert für alle gespeicherten Frame-Bilder. Da es außerdem möglich ist, die Position des Fahrzeugs zu einem bestimmten Zeitpunkt anhand der Telemetriedaten zu identifizieren, die beim Aufnehmen des Wiedergabevideos erfasst wurden, wird der in Kombination mit den Positionsinformationen berechnete Hashwert in der Such-CSV-Datei gespeichert.

③ Wählen Sie andererseits eine Szene aus, in der Sie die Position auf dem Kurs anhand eines anderen Wiedergabevideos identifizieren möchten.

④ Berechnet den dHash-Hashwert für das ausgewählte Bild mit einer Szene.

⑤ Durchsuchen Sie die CSV-Datei nach dem Bild mit dem Hashwert, der dem berechneten dHash-Hashwert am nächsten kommt. Da die Positionsinformationen mit dem Bild verknüpft sind, das von der Suche in (2) getroffen wurde, wird diese Position als Position im Verlauf der ausgewählten Szene angesehen.

In einer Szene eines Rennspiels kommt es aufgrund von Unterschieden in den Linien, die durch jedes Spiel verlaufen, zu einer leichten Fehlausrichtung, selbst wenn Sie sich in der Nähe der Strecke befinden. Ich denke, der Punkt dieser Zeit ist, ob es möglich ist, nach ähnlichen Bildern zu suchen, indem solche Unterschiede absorbiert werden.

2. Implementierungscode

Dieses Mal werden wir den obigen Prozess in Python implementieren.

2-1. Extrahieren von Einzelbildern aus Wiedergabevideos

Diesmal habe ich OpenCV verwendet, um alle Einzelbilder aus der Wiedergabevideodatei (MP4-Datei) zu extrahieren. Ich verweise auf die folgende Seite.

Das Bild wird mit dem Dateinamen "(Bildnummer) .png " gespeichert.

01_extract_frames.py


import cv2
import sys

def extract_frame(video_file, save_dir):
    capture = cv2.VideoCapture(video_file)

    frame_no = 0

    while True:
        retval, frame = capture.read()

        if retval:
            cv2.imwrite(r'{}\{:06d}.png'.format(save_dir, frame_no), frame)
            frame_no = frame_no + 1
        else:
            break

if __name__ == '__main__':
    video_file = sys.argv[1]
    save_dir = sys.argv[2]

    extract_frame(video_file, save_dir)

Dieses Skript wird auch in ③ verwendet.

2-2. Verknüpfen von dHash-Berechnungs- und Standortinformationen

Von den extrahierten Rahmenbildern wird der Hashwert von dHash unter Verwendung der Dhash-Funktion des ImageHash-Pakets für das Bild berechnet, das einer bestimmten Periode (einem bestimmten Überlappungsteil) entspricht. Darüber hinaus wird es mit den Telemetriedaten verknüpft (wobei nur der relevante Teil im Voraus extrahiert wird), die vom folgenden In-Game-App-Skript erfasst und in die Such-CSV-Datei ausgegeben werden.

02_calc_dhash.py


from PIL import Image, ImageFilter
import imagehash
import csv
import sys

frame_width = 1280
frame_hight = 720
trim_lr = 140
trim_tb = 100

dhash_size = 8

def calc_dhash(frame_dir, frame_no_from, frame_no_to, telemetry_file, output_file):

    #Telemetriedatendatei lesen
    position_data = [row for row in csv.reader(open(telemetry_file), delimiter = '\t')]

    writer = csv.writer(open(output_file, mode = 'w', newline=''))

    for i in range(frame_no_from, frame_no_to + 1):
        #Lesen Sie das extrahierte Bild und schneiden Sie es zu (um die am Bildrand angezeigte Zeit usw. zu löschen).
        frame = Image.open(r'{}\{:06d}.png'.format(frame_dir, i))
        trimed_frame = frame.crop((
            trim_lr, 
            trim_tb, 
            frame_width - trim_lr, 
            frame_hight - trim_tb))

        #Berechnung des dHash-Wertes
        dhash_value = str(imagehash.dhash(trimed_frame, hash_size = dhash_size))
        
        #Verknüpfung mit Telemetriedaten
        #Da sowohl Bilder als auch Telemetrie in regelmäßigen Abständen ausgegeben werden, werden sie einfach proportional zur Anzahl der Zeilen verknüpft.
        position_no = round((len(position_data) - 1) * (i - frame_no_from) / (frame_no_to - frame_no_from)) 

        writer.writerow([
                i,
                dhash_value,
                position_data[position_no][9], 
                position_data[position_no][10]
        ])

if __name__ == '__main__':
    frame_dir = sys.argv[1]
    frame_no_from = int(sys.argv[2])
    frame_no_to = int(sys.argv[3])
    telemetry_file = sys.argv[4]
    output_file = sys.argv[5]
    
    calc_dhash(frame_dir, frame_no_from, frame_no_to, telemetry_file, output_file)

Als Ergebnis dieses Skripts werden die folgenden Informationen (Frame-Nummer), (Hash-Wert) und (2D-Koordinatenposition in Metern) in die CSV-Datei ausgegeben.

731,070b126ee741c080,-520.11,139.89
732,070b126ee7c1c080,-520.47,139.90
733,070b126ee7c1c480,-520.84,139.92

Dieses Skript wird auch in ④ verwendet.

2-3. Suchen Sie nach dem nächstgelegenen Hashwert

Das Bild mit dem Hash-Wert, der dem angegebenen Hash-Wert am nächsten kommt, wird in der Ausgabe der CSV-Suchdatei in 2-2 gesucht.

Der Brummabstand wird für die Nähe von Hashwerten verwendet. Ich benutze popcount aus dem gmpy2-Paket, um die Brummentfernung zu berechnen (weil es sehr schnell zu sein scheint).

03_match_frame.py


import csv
import gmpy2
import sys

def match_frame(base_file, search_hash):

    base_data = [row for row in csv.reader(open(base_file))]

    min_distance = 64
    min_line = None

    results = []

    for base_line in base_data:
        distance = gmpy2.popcount(
            int(base_line[1], 16) ^
            int(search_hash, 16)
        )

        if distance < min_distance:
            min_distance = distance
            results = [base_line]
        elif distance == min_distance:
            results.append(base_line)
    
    print("Distance = {}".format(min_distance))
    for min_line in results:
        print(min_line)

if __name__ == '__main__':
    base_file = sys.argv[1]
    search_hash = sys.argv[2]

    match_frame(base_file, search_hash)

Wie unten gezeigt, werden die Positionsinformationen ausgegeben, die den Informationen des Bildes mit dem nächstgelegenen Hashwert zugeordnet sind. (Wenn mehrere Bilder mit derselben Entfernung vorhanden sind, werden alle Bildinformationen angezeigt.)

> python.exe 03_match_frame.py dhash_TOYOTA_86.csv cdc9cebc688f3f47
Distance = 8
['13330', 'c9cb4cb8688f3f7f', '-1415.73', '-58.39']
['13331', 'c9eb4cbc688f3f7f', '-1415.39', '-58.44']

3. Suchergebnisse

Dieses Mal wurden alle Frames des Spielvideos extrahiert, das auf dem Nürburgring-Nordkurs (Gesamtlänge 20,81 km) mit TOYOTA 86 GT lief. ⇒ Die Berechnung des Hash-Werts wurde durchgeführt und entspricht in etwa dem Bild einiger Szenen eines anderen Spielvideos, das auf dem BMW Z4 lief. Ich werde nach dem Bild suchen.

Lassen Sie uns zunächst prüfen, ob die Bilder der drei berühmten Ecken richtig durchsucht werden können.

Bild für die Suche verwendet Hit Bild
Bild BMW_005666.png 86_008031.png
Hashwert ced06061edcf9f2d 0c90e064ed8f1f3d
Standortinformationen (-2388.29, 69.74) (-2416.50, 66.67)

Hash-Wert Abstand = 10, Positionsinformationsabweichung = 28,4 m

In dHash wird gesagt, dass wenn der Hashwertabstand 10 oder weniger beträgt, er als dasselbe Bild angesehen wird, aber nur knapp. Selbst auf dem Bild ist die Form der Ecken ähnlich, aber die Positionen der umgebenden Bäume unterscheiden sich geringfügig, sodass es schwierig ist zu beurteilen, ob sie ähnlich sind oder nicht.

Bild für die Suche verwendet Hit Bild
Bild BMW_014187.png 86_017358.png
Hashwert 7c5450640c73198c 7c7c50642d361b0a
Standortinformationen (317.58, -121.52) (316.18, -121.45)

Hash-Wert Abstand = 11, Positionsinformationsabweichung = 1,4 m

Dies ist in Bezug auf Bilder ziemlich nah. Der Hashwertabstand beträgt jedoch 11, was größer als zuvor ist.

Bild für die Suche verwendet Hit Bild
Bild BMW_018404.png 86_022388.png
Hashwert 665d1d056078cde6 665c1d856050da8d
Standortinformationen (2071.48, 77.01) (2071.23, 77.12)

Hash-Wert Abstand = 13, Positionsinformationsabweichung = 0,27 m

Die Position des Fahrzeugs ist ebenfalls sehr nah und die Bilder sehen ziemlich ähnlich aus, aber wenn Sie genau hinschauen, ist die Ausrichtung etwas anders. Der Hashwertabstand beträgt 13, was ziemlich groß ist.

Wie oben erwähnt, habe ich es in drei berühmten Ecken versucht, aber es scheint, dass ich die nächstgelegene Position identifizieren kann.

Außerdem habe ich es in 10 zufällig ausgewählten Szenen versucht und es wurde wie folgt.

Nur ein Fall, in dem nur die falsche Position getroffen wurde, ist unten dargestellt.

Bild für die Suche verwendet Hit Bild
Bild BMW_016175.png 86_020877.png
Hashwert b7b630b24c1e1f1e b7b43839481e3f1f
Standortinformationen (1439.61, -18.69) (2059.41, 37.44)

Hash-Wert Abstand = 9, Positionsinformationsabweichung = 622,34 m

Es scheint, dass die Art und Weise, wie die Bäume links und rechts wachsen, und die Spitze des Kurses sich stark von der linken Kurve oder der Geraden unterscheiden, aber der Abstand der Hashwerte liegt relativ nahe bei 9.

Übrigens, die Bilder, die ich möchte, dass Sie treffen, sind wie folgt.

Bild für die Suche verwendet Hit Bild
BMW_016175.png 86_019818_RichtigeAntwort.png

Hash-Wertdifferenz = 13

Auf den ersten Blick sehen die Bilder ähnlich aus, aber wenn Sie genau hinschauen, sind sie nicht ausgerichtet, und daher denke ich, dass der Unterschied relativ groß ist.

4. Schließlich

In diesem Artikel habe ich dHash nach ähnlichen Bildern von Rennspielszenen durchsucht.

Die Genauigkeit ist für charakteristische Szenen wie berühmte Ecken relativ gut, aber als ich die Szenen zufällig auswählte, betrug die Gewinnrate 70% (bitte verzeihen Sie mir die geringe Anzahl bestätigter Fälle).

Es ist keine solide Interpretation, aber als persönlicher Eindruck ist es wie folgt.

Wenn Sie die Genauigkeit verbessern möchten, können Sie sich die folgenden Maßnahmen vorstellen.

Recommended Posts

Verwenden Sie dHash, um eine Szene in einem Rennspiel auf der Strecke zu lokalisieren
Ein Memorandum zur Verwendung von Keras 'keras.preprocessing.image
Verwendung der Methode __call__ in der Python-Klasse
Hinweise zur Verwendung von Marshmallow in der Schemabibliothek
Ein Spiel für ein Abenteuer im interaktiven Python-Modus
[Einführung in Python] Wie verwende ich den Operator in in der for-Anweisung?
Über den Fehler, den ich beim Versuch, Adafruit_DHT von Python auf Raspberry Pi zu verwenden, festgestellt habe
Stellen Sie von Python aus eine Verbindung zu postgreSQL her und verwenden Sie gespeicherte Prozeduren in einer Schleife.
Verwenden Sie eine Verknüpfung, um das Touchpad in Linux Mint zu aktivieren oder zu deaktivieren
Ein Memorandum, weil ich beim Versuch, MeCab mit Python zu verwenden, gestolpert bin
Von nichts unter Ubuntu 18.04 bis zum Einrichten einer Deep Learning-Umgebung auf Tensor
Ich möchte Python in der Umgebung von pyenv + pipenv unter Windows 10 verwenden
Skript zur ordnungsgemäßen Verwendung mehrerer Github-Konten im selben Repository auf demselben Computer
Verwenden Sie libsixel, um Sixel in Python auszugeben und das Matplotlib-Diagramm an das Terminal auszugeben.
Verwenden Sie PIL in Python, um nur die gewünschten Daten aus Exif zu extrahieren
Verwendung der C-Bibliothek in Python
Verwenden Sie den neuesten Pip in einer virtuellen Umgebung
Loggen Sie sich mit json mit pygogo ein.
So legen Sie einen freigegebenen Ordner mit dem Host-Betriebssystem in CentOS7 auf Virtual BOX fest
Es war ein Leben, das ich auf AWS Lambda OCR wollte, um die Charaktere zu lokalisieren.
Verwenden Sie Slackbot als Relais und kehren Sie im JSON-Format von Flasche zu Slack zurück.
Ich wollte die Python-Bibliothek von MATLAB verwenden
Verwendung der Exist-Klausel in Django Queryset
Praktisch, um Matplotlib-Unterzeichnungen in for-Anweisungen zu verwenden
Schreiben Sie in Python ein logarithmisches Histogramm auf die x-Achse
Verwendung des in Lobe in Python erlernten Modells
So veröffentlichen Sie ein Ticket über die Shogun-API
Ich möchte R-Datensatz mit Python verwenden
[C / C ++] Übergeben Sie den in C / C ++ berechneten Wert an eine Python-Funktion, um den Prozess auszuführen, und verwenden Sie diesen Wert in C / C ++.
26.12.2015 python2> datetime> Implementierung, um die Differenz in Sekunden von zwei datetime-Zeichenfolgen im ISO-Format zu ermitteln> Use .seconds ()
Wie kann man schnell die Häufigkeit des Auftretens von Zeichen aus einer Zeichenfolge in Python zählen?
Slack-Benachrichtigung, wenn ein bestimmtes Wort auf Twitter mit Heroku mit Python gemurmelt wird
Verwenden Sie die Blasensortierung, um zufällige Zufallszahlen basierend auf der Standardnormalverteilung aus einheitlichen Zufallszahlen zu generieren
Darstellung der Verteilung der Bakterienzusammensetzung aus Qiime2-Analysedaten in einem Box-Whisker-Diagramm
Ich schrieb einen Test in "Ich habe versucht, die Wahrscheinlichkeit eines Bingospiels mit Python zu simulieren".
Verwendung von GitHub auf einem Server für mehrere Personen ohne Kennwort
Definieren Sie eine Aufgabe zum Festlegen der Fabric-Umgebung in YAML
[Teil 4] Verwenden Sie Deep Learning, um das Wetter anhand von Wetterbildern vorherzusagen
Verwendung von Fujifilm X-T3 als Webcam unter Ubuntu 20.04
Hinweis zum Standardverhalten von collate_fn in PyTorch
[Teil 1] Verwenden Sie Deep Learning, um das Wetter anhand von Wetterbildern vorherzusagen
So schneiden Sie ein Block-Multiple-Array aus einem Multiple-Array in Python
Melden Sie sich von Selenium aus beim Fortigate (6.0) -Verwaltungsbildschirm an, um sich abzumelden
Umfrage zum Einsatz von maschinellem Lernen in realen Diensten
Ändern Sie das Standardausgabeziel in eine Datei in Python
[Hyperledger Iroha] Hinweise zur Verwendung des Python SDK
Wie erstelle ich ein Multiplayer-Online-Actionspiel mit Slack?
Verwendung von VS-Code in einer venv-Umgebung mit Windows
Verwenden Sie MeCab, um schlampige Sätze "langsam" zu übersetzen.
Kombinieren Sie Listen zu einem DataFrame mit Pandas
So melden Sie sich automatisch wie 1Password von der CLI an
Hinweise zum Laden einer virtuellen Umgebung mit PyCharm
So generieren Sie eine Abfrage mit dem IN-Operator in Django
So erhalten Sie den letzten (letzten) Wert in einer Liste in Python
[Teil 2] Verwenden Sie Deep Learning, um das Wetter anhand von Wetterbildern vorherzusagen
Ich habe versucht, das Python-Skript unter Windows 10 von 2.7.11 auf 3.6.0 zu ändern
Extrahieren Sie den Wert, der einem Wert am nächsten kommt, aus einem Listenelement in Python
So machen Sie einen Screenshot des Chrome-Bildschirms (verhindern Sie, dass er in der Mitte abgeschnitten wird)