Mask-R CNN du débutant au professionnel


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 #     NUM_CLASSES = 81 

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) 


Image source


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] #   image = mrcnn.visualize.apply_mask(image, mask, color, alpha=0.6) #   

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) # find text with postprocessing by standart textArr = textDetector.predict(zones) textArr = textPostprocessing(textArr, regionNames) return textArr 

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 #       cropped = binaryImage[y1:y2, x1:x2] beforePoint, afterPoint = filename.split(".") outputDirPath = os.path.join(os.path.split(outputImageDirectory)[0], "objectsOn" + beforePoint) if not os.path.exists(outputDirPath): os.mkdir(outputDirPath) coordinates = str(item).replace(" ", ",") pathToObjectImage = "{}{}.jpg".format(item.type, coordinates) cv2.imwrite(os.path.join(outputDirPath, str(pathToObjectImage)), cropped) 

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 .

Source: https://habr.com/ru/post/fr483018/


All Articles