Mask-R CNN do iniciante ao profissional


Uma vez que eu precisava analisar as informações da imagem e na saída para ter o tipo do objeto, seu tipo e também analisar a totalidade dos quadros, precisava fornecer o identificador do objeto e o tempo gasto no quadro, era necessário determinar como o objeto estava se movendo e quais câmeras foram exibidas. Vamos começar, talvez, com os dois primeiros, a análise do pessoal no conjunto será discutida na próxima parte.


Bem, descreveremos com mais detalhes nossas tarefas:


  • Corrija pessoas e carros - selecione-os na imagem e gere as instâncias de classe correspondentes com os campos necessários.
  • Determine o número do carro, se ele cair no quadro de uma câmera específica
  • Compare o quadro atual com o anterior para igualdade de objetos, para que possamos descobrir

Ok, pensei, e peguei uma cobra grossa, python, isso significa. Decidiu-se usar a rede neural Mask R-Cnn em conexão com sua simplicidade e recursos modernos . Além disso, é claro, usaremos o OpenCV para manipulação de imagens.


Configuração do ambiente


Usaremos o Windows 10, porque é mais provável que você o use.
Entende-se que você já possui Python de 64 bits. Caso contrário, você pode fazer o download do pacote, por exemplo, aqui


Instalação do pacote


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

Se, por algum motivo, não for possível compilar a partir do código-fonte, existe uma versão do pip:


 pip3 install mrcnn --user 

O pacote, é claro, vem com todas as dependências .


Etapa 1. Criando um reconhecedor simples.


Faremos as importações necessárias


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

A rede neural requer a criação de uma configuração com campos substituídos


 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 a localização do arquivo com as balanças. Deixe neste exemplo, ele estará na pasta com este arquivo. Caso contrário, ele será baixado.


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

Vamos criar nosso modelo com as configurações acima


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

E talvez começaremos a processar todas as imagens no diretório images no diretório atual.


 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] 

O que veremos nas detecções?


  print(detections) 

Por exemplo, algo semelhante:


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

Nesse caso, 2 objetos foram encontrados.
rois - matrizes de coordenadas do canto inferior esquerdo e superior direito
class_ids são os identificadores numéricos dos objetos encontrados, enquanto precisamos saber que 1 é uma pessoa, 3 é um carro, 8 é um caminhão.
scores - desde que o modelo esteja confiante na solução, esse parâmetro pode ser DETECTION_MIN_CONFIDENCE através de DETECTION_MIN_CONFIDENCE na configuração, cortando todas as opções inadequadas.
masks - o contorno do objeto. Os dados são usados ​​para desenhar uma máscara de objeto. Porque são bastante volumosos e não se destinam à compreensão humana; não os citarei no artigo.


Ok, poderíamos parar por aí, mas queremos ver a imagem que orienta o uso de redes neurais com objetos lindamente selecionados, geralmente são exibidos?


Seria mais simples chamar a função mrcnn.visualize.display_instances , mas não faremos isso, escreveremos os nossos.


A função irá capturar uma imagem e os principais parâmetros obtidos no dicionário desde os primeiros passos.


 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) 


Imagem de origem


Embora uma das principais vantagens dessa rede neural seja a solução para os problemas de segmentação de instâncias - obtendo os contornos dos objetos, ainda não a usamos, vamos analisá-la.


Para implementar máscaras, adicione algumas linhas antes de desenhar um retângulo para cada objeto encontrado.


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

Resultado:


Versão com máscaras brancas


Estágio II. Primeiros sucessos. Reconhecimento de números de carros.


Para o reconhecimento, precisamos de um quadro claro do carro próximo, por isso foi decidido tirar apenas os quadros do ponto de verificação e compará-los à semelhança (mais sobre isso no próximo capítulo). Esse método, no entanto, gera muita imprecisão, porque máquinas podem ser muito semelhantes visualmente e meu algoritmo ainda não pode evitar tais situações.


Foi decidido usar uma lib pronta do fabricante ucraniano nomeroff-net (sem publicidade). Porque quase todo o código pode ser encontrado nos exemplos do modelo, então não darei uma descrição completa.


Só posso dizer que esta função pode ser iniciada com a imagem original ou a máquina reconhecida pode ser cortada do quadro e passada para esta função.


 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 

o textArr de saída representará uma matriz de strings com o número de máquinas encontradas no quadro, por exemplo:
["293163"] ou [""] , [] - se nenhum número correspondente for encontrado.


Estágio III. Identifique objetos por similaridade.


Agora precisamos entender como consertar um objeto uma vez, entender que é ele no próximo quadro. Nesse estágio, assumiremos que temos apenas uma câmera e apenas distinguiremos os quadros diferentes dela.


Para fazer isso, você precisa descobrir como compararemos os dois objetos.


Vou propor um algoritmo de peneiração para esses propósitos. Fazemos uma reserva de que não faz parte da parte principal do OpenCV, portanto, precisamos fornecer módulos de contribuição adicional. Infelizmente, o algoritmo é patenteado e seu uso em programas comerciais é limitado. Mas estamos focados em atividades de pesquisa, certo?


 pip3 install opencv-contrib-python --user 

~~ Sobrecarregar o operador == ~~ Escrevemos uma função que recebe 2 objetos comparados na forma de matrizes. Por exemplo, nós os obtemos após chamar a função cv2.open(path)


Escreveremos uma implementação do nosso algoritmo.


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

Encontre os principais pontos e descritores usando o SIFT. Talvez eu não forneça uma ajuda para essas funções, porque você sempre pode chamá-lo no shell interativo como help(somefunc)


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

Configure nosso algoritmo.


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

Agora corra.


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

Conte as semelhanças entre as imagens.


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

Agora, tente usá-lo
Para fazer isso, depois de detectar objetos, precisamos cortá-los da imagem original


Não consegui escrever nada melhor do que salvá-lo para memória lenta e depois ler a partir daí.


  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) 

Agora temos os objetos no <outputImageDirectory>/objectsOn<imageFilename>


Agora, se tivermos pelo menos 2 desses diretórios, poderemos comparar os objetos neles. Execute a função escrita anteriormente


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

Ou podemos executar outra ação, como marcar esses objetos com o mesmo identificador.


Obviamente, como todas as redes neurais, essa tende a dar resultados às vezes errôneos.


Em geral, concluímos as 3 tarefas definidas no início, portanto, concluiremos. Duvido que este artigo tenha aberto os olhos de pessoas que escreveram pelo menos um programa que resolve os problemas de reconhecimento / segmentação de imagens, mas espero ter ajudado pelo menos um desenvolvedor iniciante.


O código fonte completo do projeto pode ser encontrado aqui .

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


All Articles