
Une fois que j'avais besoin d'analyser les informations de l'image et de la sortie pour avoir le type de l'objet, son type, et aussi, analyser la totalité des images, je devais donner l'identifiant de l'objet et le temps passé dans l'image, il fallait déterminer comment l'objet se déplaçait et quelles caméras étaient visibles. Commençons peut-être par les deux premiers, l'analyse du personnel dans son ensemble sera discutée dans la partie suivante.
Eh bien, nous décrirons plus en détail nos tâches:
- Réparez les personnes et les voitures - sélectionnez-les dans l'image et générez les instances de classe correspondantes avec les champs nécessaires.
- Déterminer le numéro de la voiture, si elle est tombée dans le cadre d'une caméra spécifique
- Comparez le cadre actuel avec le précédent pour l'égalité des objets, afin que nous puissions découvrir
Ok, je pensais, et j'ai ramassé un serpent épais, du python, ça veut dire. Il a été décidé d'utiliser le réseau neuronal Mask R-Cnn en lien avec sa simplicité et ses fonctionnalités modernes . Bien entendu, nous utiliserons également OpenCV pour la manipulation d'images.
Configuration de l'environnement
Nous utiliserons Windows 10, car vous ĂŞtes le plus susceptible de l'utiliser.
Il est entendu que vous disposez déjà de Python 64 bits. Sinon, vous pouvez télécharger le package, par exemple, à partir d'ici
Installation du package
git clone https://github.com/matterport/Mask_RCNN cd Mask_RCNN pip3 install -r requirements.txt python3 setup.py install
Si pour une raison quelconque, il n'est pas possible de compiler Ă partir des sources, il existe une version de pip:
pip3 install mrcnn --user
Le paquet, bien sûr, est livré avec toutes les dépendances .
Étape 1. Création d'un simple reconnaisseur.
Nous ferons les importations nécessaires
import os import cv2 import mrcnn.config import mrcnn from mrcnn.model import MaskRCNN
Le réseau de neurones nécessite la création d'une configuration avec des champs remplacés
class MaskRCNNConfig(mrcnn.config.Config): NAME = "coco_pretrained_model_config" GPU_COUNT = 1 IMAGES_PER_GPU = 1 DETECTION_MIN_CONFIDENCE = 0.8
Indiquez l'emplacement du fichier avec les échelles. Laissez dans cet exemple, il sera dans le dossier avec ce fichier. Si ce n'est pas le cas, il sera téléchargé.
import mrcnn.utils DATASET_FILE = "mask_rcnn_coco.h5" if not os.path.exists(DATASET_FILE): mrcnn.utils.download_trained_weights(DATASET_FILE)
Créons notre modèle avec les paramètres ci-dessus
model = MaskRCNN(mode="inference", model_dir="logs", config=MaskRCNNConfig()) model.load_weights(DATASET_FILE, by_name=True)
Et peut-être que nous allons commencer à traiter toutes les images dans le répertoire images
répertoire actuel.
IMAGE_DIR = os.path.join(os.getcwd(), "images") for filename in os.listdir(IMAGE_DIR): image = cv2.imread(os.path.join(IMAGE_DIR, filename)) rgb_image = image[:, :, ::-1] detections = model.detect([rgb_image], verbose=1)[0]
Que verrons-nous dans les détections?
print(detections)
Par exemple, quelque chose de similaire:
{'rois': array([[ 303, 649, 542, 1176],[ 405, 2, 701, 319]]), 'class_ids': array([3, 3]), 'scores': array([0.99896, 0.99770015], dtype=float32), 'masks': array()}
Dans ce cas, 2 objets ont été trouvés.
rois
- tableaux de coordonnées du coin inférieur gauche et supérieur droit
class_ids
sont les identifiants numériques des objets trouvés, alors que nous devons savoir que 1 est une personne, 3 est une voiture, 8 est un camion.
scores
- dans la mesure où le modèle est confiant dans la solution, ce paramètre peut être DETECTION_MIN_CONFIDENCE
via DETECTION_MIN_CONFIDENCE
dans la configuration, coupant toutes les options inappropriées.
masks
- le contour de l'objet. Les données sont utilisées pour dessiner un masque d'objet. Parce que ils sont assez volumineux et ne sont pas destinés à la compréhension humaine, je ne les citerai pas dans l'article.
Ok, nous pourrions nous arrêter là , mais nous voulons regarder l'image que les guides sur l'utilisation des réseaux de neurones avec des objets magnifiquement sélectionnés donnent habituellement?
Il serait plus simple d'appeler la fonction mrcnn.visualize.display_instances
, mais nous ne le ferons pas, nous écrirons la nôtre.
La fonction prendra une image, et les principaux paramètres obtenus à partir du dictionnaire dès les premières étapes.
def visualize_detections(image, masks, boxes, class_ids, scores): import numpy as np bgr_image = image[:, :, ::-1] CLASS_NAMES = ['BG',"person", "bicycle", "car", "motorcycle", "bus", "truck"] COLORS = mrcnn.visualize.random_colors(len(CLASS_NAMES)) for i in range(boxes.shape[0]): y1, x1, y2, x2 = boxes[i] classID = class_ids[i] label = CLASS_NAMES[classID] font = cv2.FONT_HERSHEY_DUPLEX color = [int(c) for c in np.array(COLORS[classID]) * 255] text = "{}: {:.3f}".format(label, scores[i]) size = 0.8 width = 2 cv2.rectangle(bgr_image, (x1, y1), (x2, y2), color, width) cv2.putText(bgr_image, text, (x1, y1-20), font, size, color, width)

Bien que l'un des principaux avantages de ce réseau de neurones soit la solution aux problèmes de segmentation des instances - obtention des contours d'objets, nous ne l'avons pas encore utilisé, nous allons l'analyser.
Pour implémenter des masques, ajoutez quelques lignes avant de dessiner un rectangle pour chaque objet trouvé.
mask = masks[:, :, i]
Résultat:

Version avec masques blancs Étape II. Premiers succès. Reconnaissance du nombre de voitures.
Pour la reconnaissance, nous avons besoin d'un cadre clair de la voiture près, il a donc été décidé de ne prendre que des cadres du point de contrôle, puis de les comparer à la similitude (plus à ce sujet dans le chapitre suivant). Cette méthode donne cependant trop d'inexactitude, car les machines peuvent être très similaires visuellement et mon algorithme ne peut pas encore éviter de telles situations.
Il a été décidé d'utiliser une bibliothèque prête à l'emploi du fabricant ukrainien nomeroff-net (pas de publicité). Parce que presque tout le code peut être trouvé dans les exemples du modèle, alors je ne donnerai pas une description complète.
Je peux seulement dire que cette fonction peut être démarrée avec l'image d'origine ou que la machine reconnue peut être découpée du cadre et passée à cette fonction.
import sys import matplotlib.image as mpimg import os sys.path.append(cfg.NOMEROFF_NET_DIR) from NomeroffNet import filters, RectDetector, TextDetector, OptionsDetector, Detector, textPostprocessing nnet = Detector(cfg.MASK_RCNN_DIR, cfg.MASK_RCNN_LOG_DIR) nnet.loadModel("latest") rectDetector = RectDetector() optionsDetector = OptionsDetector() optionsDetector.load("latest") textDetector = TextDetector.get_static_module("ru")() textDetector.load("latest") def detectCarNumber(imgPath: str) -> str: img = mpimg.imread(imgPath) NP = nnet.detect([img]) cvImgMasks = filters.cv_img_mask(NP) arrPoints = rectDetector.detect(cvImgMasks) zones = rectDetector.get_cv_zonesBGR(img, arrPoints) regionIds, stateIds, _c = optionsDetector.predict(zones) regionNames = optionsDetector.getRegionLabels(regionIds)
la sortie textArr représentera un tableau de chaînes avec le nombre de machines trouvées sur le cadre, par exemple:
["293163"]
, ou [""]
, []
- si aucun numéro correspondant n'a été trouvé.
Étape III. Identifiez les objets par similitude.
Maintenant, nous devons comprendre comment réparer un objet une fois, pour comprendre que c'est lui dans le cadre suivant. À ce stade, nous supposerons que nous n'avons qu'une seule caméra et nous ne distinguerons que les différentes images de celle-ci.
Pour ce faire, vous devez découvrir comment nous comparerons les deux objets.
Je proposerai un algorithme de criblage à ces fins. Nous faisons une réservation qu'il ne fait pas partie de la partie principale d'OpenCV, nous devons donc fournir des modules contrib en plus. Malheureusement, l'algorithme est breveté et son utilisation dans les programmes commerciaux est limitée. Mais nous nous concentrons sur les activités de recherche, non?
pip3 install opencv-contrib-python --user
~~ Surcharge de l'opérateur == ~~ Nous écrivons une fonction qui prend 2 objets comparés sous forme de matrices. Par exemple, nous les obtenons après avoir appelé la fonction cv2.open(path)
Nous allons écrire une implémentation de notre algorithme.
def compareImages(img1, img2) -> bool: sift = cv2.xfeatures2d.SIFT_create()
Trouvez des points clés et des descripteurs à l'aide de SIFT. Je ne fournirai peut-être pas d'aide pour ces fonctions, car vous pouvez toujours l'appeler dans le shell interactif comme help(somefunc)
kp1, des1 = sift.detectAndCompute(img1, None) kp2, des2 = sift.detectAndCompute(img2, None)
Configurez notre algorithme.
FLANN_INDEX_KDTREE = 0 indexParams = dict(algorithm=FLANN_INDEX_KDTREE, trees=5) searchParams = dict(checks=50) flann = cv2.FlannBasedMatcher(indexParams, searchParams)
Maintenant, lancez-le.
matches = flann.knnMatch(des1, des2, k=2)
Comptez les similitudes entre les images.
matchesCount = 0 for m, n in matches: if m.distance < cfg.cencitivity*n.distance: matchesCount += 1 return matchesCount > cfg.MIN_MATCH_COUNT
Maintenant, essayez de l'utiliser
Pour ce faire, après avoir détecté des objets, nous devons les couper de l'image d'origine
Je ne pouvais rien écrire de mieux que de le sauvegarder pour une mémoire lente, puis de lire à partir de là .
def extractObjects(objects, binaryImage, outputImageDirectory, filename=None): for item in objects: y1, x1, y2, x2 = item.coordinates
Nous avons maintenant les objets dans le <outputImageDirectory>/objectsOn<imageFilename>
Maintenant, si nous avons au moins 2 de ces répertoires, nous pouvons comparer les objets qu'ils contiennent. Exécutez la fonction écrite précédemment
if compareImages(previousObjects, currentObjects): print(“ !”)
Ou nous pouvons faire une autre action, comme marquer ces objets avec le mĂŞme identifiant.
Bien sûr, comme tous les réseaux de neurones, celui-ci a tendance à donner des résultats parfois erronés.
En général, nous avons terminé les 3 tâches définies au début, nous allons donc terminer. Je doute que cet article ait ouvert les yeux des personnes qui ont écrit au moins un programme qui résout les problèmes de reconnaissance d'image / segmentation d'image, mais j'espère avoir aidé au moins un développeur novice).
Le code source complet du projet peut être trouvé ici .