[PYTHON] Utilisez dHash pour localiser sur le parcours à partir d'une scène dans un jeu de course

1.Tout d'abord

L'un des algorithmes de recherche d'images similaires est dHash. Pour le contenu de l'algorithme, voir "Calculer la similitude des images à l'aide de Perceptual Hash". C'est facile à comprendre, mais je comprends qu'il s'agit d'un algorithme de recherche d'images similaire avec les caractéristiques suivantes.

J'ai donc essayé d'utiliser ce dHash pour identifier la position de la scène sur le parcours à partir d'une scène (vidéo embarquée) du jeu du jeu de course Assetto Corsa.

Plus précisément, le flux est le suivant.

概要.png

① Tout d'abord, toutes les images sont extraites et enregistrées en tant qu'images PNG à partir d'une vidéo de lecture (vidéo intégrée) qui fait le tour du parcours.

② Calcule la valeur de hachage dHash pour toutes les images de cadre enregistrées. De plus, comme il est possible d'identifier la position du véhicule à un certain moment à partir des données de télémétrie acquises lors de la prise de vue de la vidéo de lecture, la valeur de hachage calculée en combinaison avec les informations de position est enregistrée dans le fichier CSV de recherche.

③ D'autre part, sélectionnez une scène dans laquelle vous souhaitez identifier la position sur le parcours à partir d'une autre vidéo de lecture.

④ Calcule la valeur de hachage dHash pour l'image à une scène sélectionnée.

⑤ Recherchez le fichier CSV pour rechercher l'image avec la valeur de hachage la plus proche de la valeur de hachage dHash calculée. Puisque les informations de position sont liées à l'image touchée par la recherche en (2), cette position est considérée comme la position sur le parcours de la scène sélectionnée.

Dans une scène d'un jeu de course, même si vous êtes près du parcours, il y aura un léger désalignement dû aux différences dans les lignes qui passent à travers chaque jeu. Je pense que le but de cette époque est d'absorber ces différences et de rechercher des images similaires.

2. Code de mise en œuvre

Cette fois, nous allons implémenter le processus ci-dessus en Python.

2-1. Extraction d'images d'images à partir de vidéos de lecture

J'ai utilisé OpenCV cette fois pour extraire toutes les images d'image du fichier vidéo de lecture (fichier mp4). Je me réfère à la page suivante.

L'image du cadre est enregistrée sous le nom de fichier "(numéro du cadre) .png ".

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)

Ce script est également utilisé dans ③.

2-2. Lier le calcul dHash et les informations de localisation

Parmi les images de trame extraites, la valeur de hachage de dHash est calculée à l'aide de la fonction dhash du package ImageHash pour l'image correspondant à une certaine période (une partie de tour spécifique). En outre, il est lié aux données de télémétrie (extrayant uniquement la partie pertinente à l'avance) acquises par le script d'application du jeu suivant et générées dans le fichier CSV de recherche.

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):

    #Lire le fichier de données de télémétrie
    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):
        #Lisez l'image du cadre extraite et coupez-la (pour effacer l'heure, etc. affichée au bord de l'image)
        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))

        #Calcul de la valeur dHash
        dhash_value = str(imagehash.dhash(trimed_frame, hash_size = dhash_size))
        
        #Liaison avec les données de télémétrie
        #Comme les images et la télémétrie sont produites à intervalles réguliers, elles sont simplement liées proportionnellement au nombre de lignes.
        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)

À la suite de ce script, les informations suivantes (numéro d'image), (valeur de hachage) et (position des coordonnées 2D en mètres) sont générées dans le fichier CSV.

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

Ce script est également utilisé dans ④.

2-3. Rechercher la valeur de hachage la plus proche

L'image avec la valeur de hachage la plus proche de la valeur de hachage spécifiée est recherchée à partir de la sortie du fichier CSV de recherche en 2-2.

La distance de bourdonnement est utilisée pour la proximité des valeurs de hachage. J'utilise popcount du package gmpy2 pour calculer la distance de bourdonnement (car cela semble être très rapide).

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)

Comme indiqué ci-dessous, les informations de position associées aux informations de l'image avec la valeur de hachage la plus proche sont sorties. (S'il y a plusieurs images avec la même distance, toutes les informations sur l'image seront affichées.)

> 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. Résultats de la recherche

Cette fois, la vidéo de lecture qui a couru sur le parcours nord du Nürburgring (longueur totale 20,81 km) avec TOYOTA 86 GT a été extraite de toutes les images ⇒ Le calcul de la valeur de hachage a été effectué, et il est proche de l'image de certaines scènes d'une autre vidéo de lecture qui a couru sur BMW Z4 Je chercherai l'image.

Tout d'abord, vérifions si les images des trois célèbres coins peuvent être recherchées correctement.

Image utilisée pour la recherche Image frappée
image BMW_005666.png 86_008031.png
Valeur de hachage ced06061edcf9f2d 0c90e064ed8f1f3d
Information de Lieu (-2388.29, 69.74) (-2416.50, 66.67)

Distance de valeur de hachage = 10, écart d'informations de position = 28,4 m

Dans dHash, il est dit que si la distance de valeur de hachage est de 10 ou moins, elle est considérée comme la même image, mais c'est à peine. Même sur l'image, la forme des coins est similaire, mais les positions des arbres environnants sont légèrement différentes, il est donc difficile de juger s'ils sont similaires ou non.

Image utilisée pour la recherche Image frappée
image BMW_014187.png 86_017358.png
Valeur de hachage 7c5450640c73198c 7c7c50642d361b0a
Information de Lieu (317.58, -121.52) (316.18, -121.45)

Distance de valeur de hachage = 11, écart des informations de position = 1,4 m

C'est assez proche de l'image. Cependant, la distance de valeur de hachage est de 11, ce qui est plus grand qu'avant.

Image utilisée pour la recherche Image frappée
image BMW_018404.png 86_022388.png
Valeur de hachage 665d1d056078cde6 665c1d856050da8d
Information de Lieu (2071.48, 77.01) (2071.23, 77.12)

Distance de valeur de hachage = 13, écart des informations de position = 0,27 m

La position du véhicule est également très proche et les images se ressemblent beaucoup, mais si vous regardez de près, l'orientation est légèrement différente. La distance de valeur de hachage est de 13, ce qui est assez grand.

Comme mentionné ci-dessus, je l'ai essayé dans trois virages célèbres, mais il semble que je puisse identifier la position la plus proche.

De plus, je l'ai essayé dans 10 scènes sélectionnées au hasard, et c'est devenu comme suit.

Un seul cas où seule la mauvaise position a été atteinte est illustré ci-dessous.

Image utilisée pour la recherche Image frappée
image BMW_016175.png 86_020877.png
Valeur de hachage b7b630b24c1e1f1e b7b43839481e3f1f
Information de Lieu (1439.61, -18.69) (2059.41, 37.44)

Distance de valeur de hachage = 9, écart des informations de position = 622,34 m

Il semble que la façon dont les arbres poussent à gauche et à droite et la pointe du parcours soient assez différentes de la courbe gauche ou droite, mais la distance de valeur de hachage est relativement proche de 9.

Au fait, les images que j'aimerais que vous voyiez sont les suivantes.

Image utilisée pour la recherche Image frappée
BMW_016175.png 86_019818_Bonneréponse.png

Différence de valeur de hachage = 13

À première vue, les images se ressemblent, mais si vous regardez de près, elles ne sont pas alignées et, par conséquent, je pense que la différence est relativement grande.

4. Enfin

Dans cet article, j'ai recherché des images similaires avec dHash dans la scène du jeu de course.

La précision est relativement bonne pour les scènes caractéristiques telles que les coins célèbres, mais lorsque j'ai sélectionné au hasard les scènes, le taux de victoire était de 70% (veuillez pardonner le fait que le nombre de cas confirmés est aussi petit que 10).

Ce n'est pas une interprétation solide, mais comme une impression personnelle, c'est comme suit.

Si vous souhaitez améliorer la précision, je pense que vous pouvez penser aux mesures suivantes.

Recommended Posts

Utilisez dHash pour localiser sur le parcours à partir d'une scène dans un jeu de course
Un mémorandum sur l'utilisation de keras.preprocessing.image de Keras
Comment utiliser la méthode __call__ dans la classe Python
Remarques sur l'utilisation de la guimauve dans la bibliothèque de schémas
Un jeu pour partir à l'aventure en mode interactif de python
[Introduction à Python] Comment utiliser l'opérateur in dans l'instruction for?
À propos de l'erreur que j'ai rencontrée en essayant d'utiliser Adafruit_DHT à partir de Python sur Raspberry Pi
Connectez-vous à postgreSQL depuis Python et utilisez des procédures stockées dans une boucle.
Utilisez un raccourci pour activer ou désactiver le pavé tactile dans Linux Mint
Un mémorandum parce que j'ai trébuché en essayant d'utiliser MeCab avec Python
De rien sur Ubuntu 18.04 à la configuration d'un environnement Deep Learning sur Tensor
Je souhaite utiliser Python dans l'environnement de pyenv + pipenv sous Windows 10
Script pour utiliser correctement plusieurs comptes github dans le même référentiel sur la même machine
Utilisez libsixel pour générer Sixel en Python et générer le graphe Matplotlib vers le terminal.
Utilisez PIL en Python pour extraire uniquement les données souhaitées d'Exif
Comment utiliser la bibliothèque C en Python
Utilisez le dernier pip dans un environnement virtualenv
Connectez-vous avec json en utilisant pygogo.
Comment définir un dossier partagé avec le système d'exploitation hôte dans CentOS7 sur Virtual BOX
C'était une vie que je voulais faire de l'OCR sur AWS Lambda pour localiser les personnages.
Utilisez slackbot comme relais et retournez de bouteille à slack au format json.
Je voulais utiliser la bibliothèque Python de MATLAB
Comment utiliser la clause exist dans l'ensemble de requêtes Django
Pratique pour utiliser les sous-graphiques matplotlib dans l'instruction for
Ecrire un histogramme à l'échelle logarithmique sur l'axe des x en python
Comment utiliser le modèle appris dans Lobe en Python
Comment publier un ticket depuis l'API Shogun
Je veux utiliser le jeu de données R avec python
[C / C ++] Passez la valeur calculée en C / C ++ à une fonction python pour exécuter le processus et utilisez cette valeur en C / C ++.
2015-12-26 python2> datetime> Implémentation pour prendre la différence en secondes entre deux chaînes datetime au format ISO> Utiliser .seconds ()
Comment compter rapidement la fréquence d'apparition des caractères à partir d'une chaîne de caractères en Python?
Notification Slack lorsqu'un mot spécifique est murmuré sur Twitter en utilisant Heroku avec python
Utilisez le tri par bulles pour générer des nombres aléatoires basés sur une distribution normale standard à partir de nombres aléatoires uniformes
Comment représenter la distribution de la composition bactérienne à partir des données d'analyse Qiime2 dans un diagramme de moustaches
J'ai écrit un doctest dans "J'ai essayé de simuler la probabilité d'un jeu de bingo avec Python"
Comment utiliser GitHub sur un serveur multi-personnes sans mot de passe
Définir une tâche pour définir l'environnement de fabric dans YAML
[Partie 4] Utilisez le Deep Learning pour prévoir la météo à partir d'images météorologiques
Comment utiliser Fujifilm X-T3 comme webcam sur Ubuntu 20.04
Remarque sur le comportement par défaut de collate_fn dans PyTorch
[Partie 1] Utilisez le Deep Learning pour prévoir la météo à partir d'images météorologiques
Comment découper un bloc de plusieurs tableaux à partir d'un multiple en Python
Connectez-vous à l'écran de gestion fortigate (6.0) à partir de sélénium-essayez de vous déconnecter
Enquête sur l'utilisation du machine learning dans les services réels
Changer la destination de sortie standard en un fichier en Python
[Hyperledger Iroha] Remarques sur l'utilisation du SDK Python
Comment créer un jeu d'action multijoueur en ligne avec Slack
Comment utiliser VS Code dans un environnement Venv avec Windows
Utilisez MeCab pour traduire des phrases bâclées de manière "lente".
Combinez des listes dans un DataFrame avec des pandas
Comment se connecter automatiquement comme 1Password depuis CLI
Remarques sur la façon de charger un environnement virtuel avec PyCharm
Comment générer une requête à l'aide de l'opérateur IN dans Django
Comment obtenir la dernière (dernière) valeur d'une liste en Python
[Partie 2] Utilisez le Deep Learning pour prévoir la météo à partir d'images météorologiques
J'ai essayé de changer le script python de 2.7.11 à 3.6.0 sur Windows10
Extraire la valeur la plus proche d'une valeur à partir d'un élément de liste en Python
Comment prendre une capture d'écran de l'écran Chrome (l'empêcher de se couper au milieu)