Máscara-R CNN desde principiante hasta profesional


Una vez que necesitaba analizar la información de la imagen y en la salida para tener el tipo del objeto, su tipo, y también, analizar el conjunto de cuadros, necesitaba dar el identificador del objeto y el tiempo que pasó en el cuadro, era necesario determinar cómo se movía el objeto y qué cámaras aparecían a la vista. Comencemos, quizás, con los dos primeros, el análisis del personal en conjunto se discutirá en la siguiente parte.


Bueno, describiremos con más detalle nuestras tareas:


  • Repare personas y automóviles: selecciónelos en la imagen y genere las instancias de clase correspondientes con los campos necesarios.
  • Determine el número del automóvil, si cayó en el marco de una cámara específica
  • Compare el marco actual con el anterior para la igualdad de objetos, para que podamos descubrir

Ok, pensé, y tomé una serpiente gruesa, pitón, eso significa. Se decidió utilizar la red neuronal Mask R-Cnn en relación con su simplicidad y características modernas . Además, por supuesto, utilizaremos OpenCV para la manipulación de imágenes.


Configuración del entorno


Usaremos Windows 10, porque es más probable que lo use.
Se entiende que ya tienes Python de 64 bits. De lo contrario, puede descargar el paquete, por ejemplo, desde aquí.


Instalación de paquete


git clone https://github.com/matterport/Mask_RCNN cd Mask_RCNN pip3 install -r requirements.txt python3 setup.py install 

Si por alguna razón no es posible compilar desde la fuente, hay una versión de pip:


 pip3 install mrcnn --user 

El paquete, por supuesto, viene con todas las dependencias .


Etapa 1. Crear un reconocedor simple.


Haremos las importaciones necesarias


 import os import cv2 import mrcnn.config import mrcnn from mrcnn.model import MaskRCNN 

La red neuronal requiere crear una configuración con campos anulados


 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 

Indique la ubicación del archivo con las escalas. Deje que en este ejemplo esté en la carpeta con este archivo. Si no es así, se descargará.


 import mrcnn.utils DATASET_FILE = "mask_rcnn_coco.h5" if not os.path.exists(DATASET_FILE): mrcnn.utils.download_trained_weights(DATASET_FILE) 

Creemos nuestro modelo con la configuración anterior


 model = MaskRCNN(mode="inference", model_dir="logs", config=MaskRCNNConfig()) model.load_weights(DATASET_FILE, by_name=True) 

Y quizás comencemos a procesar todas las imágenes en el directorio de images en el directorio actual.


 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] 

¿Qué veremos en las detecciones?


  print(detections) 

Por ejemplo, algo similar:


 {'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()} 

En este caso, se encontraron 2 objetos.
rois : conjuntos de coordenadas de la esquina inferior izquierda y superior derecha
class_ids son los identificadores numéricos de los objetos encontrados, mientras que necesitamos saber que 1 es una persona, 3 es un automóvil, 8 es un camión.
scores : en la medida en que el modelo confíe en la solución, este parámetro puede DETECTION_MIN_CONFIDENCE mediante DETECTION_MIN_CONFIDENCE en la configuración, cortando todas las opciones inapropiadas.
masks : el contorno del objeto. Los datos se usan para dibujar una máscara de objeto. Porque son bastante voluminosos y no están destinados a la comprensión humana; no los citaré en el artículo.


Ok, podríamos detenernos allí, pero ¿queremos ver la imagen que guía sobre el uso de redes neuronales con objetos bellamente seleccionados?


Sería más simple llamar a la función mrcnn.visualize.display_instances , pero no haremos esto, escribiremos la nuestra.


La función tomará una imagen y los principales parámetros obtenidos del diccionario de los primeros pasos.


 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) 


Imagen de origen


Aunque una de las principales ventajas de esta red neuronal es la solución a los problemas de segmentación de instancias: obtener los contornos de los objetos, aún no lo hemos utilizado, lo analizaremos.


Para implementar máscaras, agregue un par de líneas antes de dibujar un rectángulo para cada objeto encontrado.


 mask = masks[:, :, i] #   image = mrcnn.visualize.apply_mask(image, mask, color, alpha=0.6) #   

Resultado:


Versión con máscaras blancas.


Etapa II Primeros éxitos. Reconocimiento de números de automóviles.


Para el reconocimiento, necesitamos un cuadro claro del automóvil cerca, por lo que se decidió tomar solo cuadros del punto de control y luego compararlos con la similitud (más sobre eso en el próximo capítulo). Este método, sin embargo, da demasiada imprecisión, porque Las máquinas pueden ser muy similares visualmente y mi algoritmo aún no puede evitar tales situaciones.


Se decidió utilizar una lib preparada del fabricante ucraniano nomeroff-net (no publicidad). Porque casi todo el código se puede encontrar en los ejemplos para el modelo, luego no daré una descripción completa.


Solo puedo decir que esta función se puede iniciar con la imagen original o que la máquina reconocida se puede cortar del marco y pasar a esta función.


 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 salida textArr representará una serie de cadenas con los números de máquinas que se encuentran en el marco, por ejemplo:
["293163"] , o [""] , [] - si no se encontraron números coincidentes.


Etapa III Identificar objetos por similitud.


Ahora necesitamos entender cómo arreglar un objeto una vez, entender que es él en el siguiente cuadro. En esta etapa, asumiremos que tenemos una sola cámara y solo distinguiremos entre diferentes marcos.


Para hacer esto, debe averiguar cómo compararemos los dos objetos.


Propondré un algoritmo de cribado para estos fines. Hacemos una reserva de que no es parte de la parte principal de OpenCV, por lo que necesitamos entregar módulos contrib adicionales. Desafortunadamente, el algoritmo está patentado y su uso en programas comerciales es limitado. Pero estamos enfocados en actividades de investigación, ¿verdad?


 pip3 install opencv-contrib-python --user 

~~ Sobrecargar el operador == ~~ Escribimos una función que toma 2 objetos comparados en forma de matrices. Por ejemplo, los obtenemos después de llamar a la función cv2.open(path)


Escribiremos una implementación de nuestro algoritmo.


 def compareImages(img1, img2) -> bool: sift = cv2.xfeatures2d.SIFT_create() 

Encuentre puntos clave y descriptores utilizando SIFT. Quizás no proporcione ayuda para estas funciones, porque siempre puede llamarlo en el shell interactivo como help(somefunc)


  kp1, des1 = sift.detectAndCompute(img1, None) kp2, des2 = sift.detectAndCompute(img2, None) 

Configura nuestro algoritmo.


  FLANN_INDEX_KDTREE = 0 indexParams = dict(algorithm=FLANN_INDEX_KDTREE, trees=5) searchParams = dict(checks=50) flann = cv2.FlannBasedMatcher(indexParams, searchParams) 

Ahora ejecútalo.


  matches = flann.knnMatch(des1, des2, k=2) 

Cuenta las similitudes entre las imágenes.


  matchesCount = 0 for m, n in matches: if m.distance < cfg.cencitivity*n.distance: matchesCount += 1 return matchesCount > cfg.MIN_MATCH_COUNT 

Ahora intenta usarlo
Para hacer esto, después de detectar objetos, necesitamos cortarlos de la imagen original.


No podría escribir nada mejor que guardarlo para memoria lenta, y luego leer desde allí.


  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) 

Ahora tenemos los objetos en el <outputImageDirectory>/objectsOn<imageFilename>


Ahora, si tenemos al menos 2 de esos directorios, podemos comparar los objetos en ellos. Ejecute la función escrita anteriormente


 if compareImages(previousObjects, currentObjects): print(“  !”) 

O podemos hacer otra acción, como marcar estos objetos con el mismo identificador.


Por supuesto, como todas las redes neuronales, esta tiende a dar resultados a veces erróneos.


En general, hemos completado las 3 tareas establecidas al principio, por lo que terminaremos. Dudo que este artículo haya abierto los ojos de personas que han escrito al menos un programa que resuelve los problemas de reconocimiento de imágenes / segmentación de imágenes, pero espero haber ayudado al menos a un desarrollador novato).


El código fuente completo del proyecto se puede encontrar aquí .

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


All Articles