Combate de aprendizaje profundo de Keras (14) - Detección de objetivos R-CNN desde cero

0. Prefacio

R-CNN( Regions with CNN features), es el modelo de primera generación de una R-CNNserie algoritmos de detección de objetivos, que combina el "aprendizaje profundo" y la "visión artificial" tradicional para detectar objetos objetivo basados ​​en regiones candidatas ( ). En Fundamentos de la detección de objetos , hemos visto cómo el concepto de una región candidata puede generar una región candidata a partir de una imagen. En esta sección, utilizaremos las regiones candidatas para completar la detección y localización de objetos objetivo en imágenes.Region proposal

1. Modelo de detección de objetivos R-CNN

1.1 Análisis del conjunto de datos

Para entrenar el modelo, descargamos y usamos el conjunto de datos VOCtrainval_11-May-2012.tar , que contiene los objetos de la imagen y sus cuadros delimitadores correspondientes, que es uno de los conjuntos de datos más utilizados en la detección de objetos.
Primero analizamos la información relevante del conjunto de datos para construir mejor un modelo de red neuronal, tomando como ejemplo la siguiente imagen y las correspondientes coordenadas del cuadro delimitador y categorías de objetos en la imagen:

Ejemplo de conjunto de datos

Las etiquetas de clase y las coordenadas del cuadro delimitador de los objetos se proporcionan en el XMLarchivo y se pueden XMLextraer del archivo de la siguiente manera:

archivo de muestra XML
Si xml["annotation"]["object"]es una lista, hay varios objetos de destino en la imagen. xml["annotation"]["object"]["bndbox"]contiene el cuadro delimitador del objeto de destino existente en la imagen, donde las coordenadas del cuadro delimitador incluyen ”xmin”, y ”ymin”, que representan las coordenadas de la esquina superior izquierda y inferior derecha del cuadro delimitador, respectivamente . Se puede utilizar para extraer las etiquetas de clase de los objetos presentes en la imagen. Para simplificar, en esta sección, el conjunto de datos que usamos contiene solo un objeto de destino para localizar por imagen.”xmax””ymax”(xmin, ymin)(xmax, ymax)xml["annotation"]["object"]["name"]

1.2 Análisis del modelo R-CNN

Después de comprender el conjunto de datos de entrenamiento, a continuación presentamos el proceso de detección y localización del modelo de detección de R-CNNobjetivos :

  • Extraer regiones candidatas en una imagen
  • Calcule la distancia entre el área candidata y el área real del objeto de destino:
    • Esencialmente, la relación de intersección ( Intersection over Union, IoU) del área candidata y el área real del objeto se calcula como la métrica de distancia
  • Si la relación de intersección y unión es mayor a un cierto umbral, se puede considerar que el área candidata contiene el objetivo a ubicar, en caso contrario, se considera que no contiene el objetivo a ubicar:
    • Esto significa que generamos una etiqueta para cada región candidata (ya sea que contenga el objetivo a ubicar), donde la imagen de la región candidata es la entrada y la etiqueta de salida se obtiene utilizando el IoUumbral
  • Cambie el tamaño de la imagen e ingrese cada imagen de la región candidata en el VGG16modelo para extraer las características de la imagen de la región candidata
  • Corrija el cuadro delimitador de la región candidata comparando la posición de la región candidata y la posición real del objeto de destino, y utilícelo como una nueva región candidata para el entrenamiento posterior
  • Establecer un modelo de clasificación para asignar las características de la región candidata a si la región contiene el objeto de destino
  • Usando las imágenes en la región candidata, construya un modelo de regresión que mapee las características de entrada del objeto candidato al desplazamiento requerido para extraer el cuadro delimitador preciso del objeto, que se usa para rectificar el cuadro delimitador candidato.
  • Use supresión no máxima en el cuadro delimitador final obtenido:
    • El uso de supresión no máxima garantiza que se reduzca una gran cantidad de regiones candidatas superpuestas para 1mantener solo las candidatas con la mayor probabilidad de contener objetos.
  • Mediante el uso de supresión no máxima, podemos extender la estrategia anterior para construir modelos de detección de objetos para imágenes que contienen múltiples objetos de destino.

A continuación se muestra un diagrama esquemático del proceso anterior:

R-CNN

A continuación, implementaremos R-CNNel .

2. Implemente la detección de objetos R-CNN desde cero

2.1 Ver el conjunto de datos de entrenamiento

Importe las bibliotecas relevantes y, antes de crear el modelo de detección de objetivos, primero cree el directorio del conjunto de datos y vea el volumen total de datos del conjunto de datos:

import json, scipy, os, time
import sys, cv2, xmltodict
import matplotlib.pyplot as plt
# import tensorflow as tf
import selectivesearch
import numpy as np
import pandas as pd
import gc, scipy, argparse
from copy import deepcopy

xmls_root ="VOCtrainval_11-May-2012/VOCdevkit/VOC2012/"  # 数据集父目录
annotations = xmls_root + "Annotations/"
jpegs = xmls_root + "JPEGImages/"
XMLs = os.listdir(annotations)
print(XMLs[:10])
print(len(XMLs))

A continuación, para tener una idea del conjunto de datos, muestree aleatoriamente un XMLarchivo en el conjunto de datos, xmltodictanalícelo usando la biblioteca y vea el resultado:

ix = np.random.randint(len(XMLs))
sample_xml = XMLs[ix]
sample_xml = '{}/{}'.format(annotations, sample_xml)
with open(sample_xml, "rb") as f:    # notice the "rb" mode
    d = xmltodict.parse(f, xml_attribs=True)
print(d)

Como puede ver, la información relacionada con la entrada es la siguiente:

OrderedDict([('annotation', OrderedDict([('filename', '2010_000495.jpg'), ('folder', 'VOC2012'), ('object', OrderedDict([('name', 'motorbike'), ('bndbox', OrderedDict([('xmax', '482'), ('xmin', '13'), ('ymax', '328'), ('ymin', '32')])), ('difficult', '0'), ('occluded', '0'), ('pose', 'Left'), ('truncated', '0')])), ('segmented', '0'), ('size', OrderedDict([('depth', '3'), ('height', '375'), ('width', '500')])), ('source', OrderedDict([('annotation', 'PASCAL VOC2010'), ('database', 'The VOC2010 Database'), ('image', 'flickr')]))]))])

2.2 Realizar el modelo de detección de objetivos R-CNN desde cero

(1) Una vez que esté familiarizado con el conjunto de datos, puede comenzar a implementar el modelo de detección de objetivos desde cero R-CNN. Primero, defina las funciones para calcular IoUy obtener regiones candidatas (para métodos de cálculo específicos, consulte "Conceptos básicos de detección de objetivos" ). :

def calc_iou(candidate, current_y, img_shape):
    boxA = deepcopy(candidate)
    boxB = deepcopy(current_y)
    boxA[2] += boxA[0]
    boxA[3] += boxA[1]
    iou_img1 = np.zeros(img_shape)
    iou_img1[int(boxA[1]):int(boxA[3]), int(boxA[0]):int(boxA[2])] = 1
    iou_img2 = np.zeros(img_shape)
    iou_img2[int(boxB[1]):int(boxB[3]), int(boxB[0]):int(boxB[2])] = 1
    iou = np.sum(iou_img1*iou_img2) / (np.sum(iou_img1) + np.sum(iou_img2) - np.sum(iou_img1*iou_img2))
    return iou
    
def extract_candidates(img):
    """
    排除所有占图像面积不到 5% 的候选区域
    """
    img_lbl, regions = selectivesearch.selective_search(img, scale=100, min_size=100)
    img_area = np.prod(img.shape[:2])
    candidates = []
    for region in regions:
        if region['rect'] in candidates:
            continue
        if region['size'] < (0.05 * img_area):
            continue
        x, y, w, h = region['rect']
        candidates.append(list(region['rect']))
    return candidates

(2) Importe el VGG16modelo para extraer las características de la imagen:

from keras.applications import vgg16
from keras.utils.vis_utils import plot_model
from keras.applications.vgg16 import preprocess_input
vgg16_model = vgg16.VGG16(include_top=False, weights='imagenet')

(3) A continuación, cree una lista de entradas y salidas correspondientes requeridas por el modelo de detección de objetos; para simplificar, solo consideramos las imágenes que contienen un objeto de destino.
Primero inicialice la lista requerida:

train_data_length = 1000
final_cls = []
final_delta = []
iou_list = []
imgs = []

Repita las imágenes y solo considere las imágenes que contienen un objeto de destino:

for ix, xml in enumerate(XMLs[:train_data_length]):
    print('Extracted data from {} xmls...'.format(ix), end='\r')
    xml_file = annotations + xml
    fname = xml.split('.')[0]
    with open(xml_file, 'rb') as f:
        xml = xmltodict.parse(f, xml_attribs=True)
        l = []
        if isinstance(xml["annotation"]["object"], list):
            # 本任务仅考虑包含单目标对象的图像
            continue

En el código anterior, primero recorremos para obtener la imagen correspondiente al xmlarchivo y verificamos si la imagen contiene múltiples objetos (si xml["annotation"]["object"]la salida de es una lista, la imagen contiene múltiples objetos).
Normalice las coordenadas de posición del objeto. Esto se hace porque el cuadro delimitador normalizado es relativamente estable, y el cuadro delimitador normalizado del objeto no cambiará aunque cambie la forma de la imagen. Por ejemplo, si el objeto xminestá en el eje de toda la imagen yx en el eje , será el mismo incluso si la imagen está escalada (sin embargo, si tratamos con posiciones de valores de píxeles sin procesar, el valor cambiará cuando la imagen está escalado):20%y50%xmin

        bndbox = xml["annotation"]["object"]["bndbox"]
        for key in bndbox:
            bndbox[key] = float(bndbox[key])
        x1, x2, y1, y2 = [bndbox[key] for key in ['xmin', 'xmax', 'ymin', 'ymax']]

        img_size = xml["annotation"]["size"]
        for key in img_size:
            img_size[key] = float(img_size[key])
        w, h = img_size['width'], img_size['height']

        # 根据图片尺寸归一化边界框坐标
        x1 /= w
        x2 /= w
        y1 /= h
        y2 /= h
        label = xml["annotation"]["object"]["name"]
        y = [x1, y1, x2-x1, y2-y1, label] # 图像左上角x和y坐标,图像宽度和高度,目标对象标签

Usando la extract_candidates()función , extraiga regiones candidatas en la imagen:

        # 图片路径
        file_name = jpegs + fname + '.jpg'
        # 读取并预处理图片
        img = cv2.resize(cv2.imread(file_name), (224, 224))
        candidates = extract_candidates(img)

En el código anterior, usamos la extract_candidatesfunción para extraer propuestas de región para la imagen redimensionada.
Las regiones candidatas se recorren para calcular la relación de intersección de cada región candidata con el cuadro delimitador de la región real del objeto en la imagen, y el desplazamiento correspondiente entre el cuadro delimitador real y el objeto candidato para el entrenamiento posterior del modelo de regresión para corregir el cuadro delimitador de la región candidata:

        for jx, candidate in enumerate(candidates):
            current_y2 = [int(i*224) for i in [x1, y1, x2, y2]]
            iou = calc_iou(candidate, current_y2, (224, 224))
            candidate_region_coordinates = c_x1, c_y1, c_w, c_h = np.array(candidate)/224

            dx = c_x1 - x1
            dy = c_y1 - y1
            dw = c_w - (x2 - x1)
            dh = c_h - (y2 - y1)

            final_delta.append([dx, dy, dw, dh])

El cálculo utiliza el preentrenado para VGG16 extraer características para cada región candidata y IoUasignar etiquetas de clase en función de su correlación con el cuadro delimitador real:

            if iou > 0.3:
                final_cls.append(label)
            else:
                final_cls.append('background')
            
            # 使用VGG16作为特征提取网络
            l = int(c_x1 * 224)
            r = int((c_x1 + c_w) * 224)
            t = int(c_y1 * 224)
            b = int((c_y1 + c_h) * 224)

            img2 = img[t:b,l:r,:3]
            img3 = cv2.resize(img2,(224,224)) # /255
            img3 = preprocess_input(img3.reshape(1,224,224,3))
            img4 = vgg16_model.predict(img3)
            imgs.append(img4)
            iou_list.append(iou)

Crear matrices de entrada y salida:

targets = pd.DataFrame(final_cls, columns=['label'])
# 使用get_dummies方法获取标签,因为这些类别值是分类文本值
labels = pd.get_dummies(targets['label']).columns
y_train = pd.get_dummies(targets['label']).values.astype(float)
x_train = np.array(imgs)
x_train = x_train.reshape(x_train.shape[0], x_train.shape[2], x_train.shape[3], x_train.shape[4])

(4) Construya, compile el modelo y ajuste el modelo:

from keras.models import Sequential
from keras.layers import Flatten, Dropout, Dense, Conv2D
model = Sequential()
model.add(Flatten(input_shape=((7, 7, 512))))
model.add(Dense(1024, activation='relu'))
model.add(Dense(y_train.shape[1], activation='softmax'))

model.compile(loss='categorical_crossentropy', optimizer='adam', metrics=['acc'])

history = model.fit(x_train/x_train.max(), y_train,
                validation_split=0.1,
                epochs=10,
                batch_size=32,
                verbose=1)

Se puede lograr la precisión de la clasificación en el conjunto de datos de prueba 80%. Dividimos la matriz de entrada por x_train.max()para la normalización.La normalización de los datos de entrada puede entrenar el modelo más rápido.
(5) Seleccione imágenes del conjunto de datos para validar los resultados del modelo de clasificación de imágenes (asegúrese de no utilizar imágenes del conjunto de datos de entrenamiento).
Elija una imagen para probar:

import matplotlib.patches as mpatches

ix = np.random.randint(train_data_length, len(XMLs))
filename = jpegs + XMLs[ix].replace('xml', 'jpg')

Escriba una función de prueba test_predictions, realice un preprocesamiento de imagen en la imagen de prueba para extraer candidatos, realice predicciones de modelo en los candidatos redimensionados, filtre las regiones de clase de fondo pronosticadas y, finalmente, dibuje los límites de región de las clases de objetos con la probabilidad más alta excluyendo la clase de fondo Como región candidata final:

def test_predictions(file_name):
    image = cv2.imread(file_name)
    img = cv2.resize(image, (224, 224))
    candidates = extract_candidates(img)

En el código anterior, cambiamos el tamaño de la imagen de entrada y extraemos regiones candidatas de ella. A continuación, trace la imagen de lectura e inicialice la lista que almacena las clases pronosticadas y sus probabilidades correspondientes:

    _, ax = plt.subplots(1, 2)
    ax[0].imshow(cv2.cvtColor(image, cv2.COLOR_BGR2RGB))
    ax[0].set_title(file_name.split('/')[-1])

    pred_class = []
    pred = []

A continuación, recorremos estas regiones candidatas, las redimensionamos y las pasamos al VGG16modelo . Además, alimentamos VGG16las características de salida del modelo de clasificación para obtener la probabilidad de que las imágenes de la región candidata pertenezcan a diferentes categorías:

    for ix, candidate in enumerate(candidates):
        l, t, w, h = np.array(candidate).astype(int)
        img2 = img[t:t+h, l:l+w, :3]
        img3 = cv2.resize(img2, (224, 224)) #/255
        img3 = preprocess_input(img3.reshape(1, 224, 224, 3))
        img4 = vgg16_model.predict(img3)
        final_pred = model.predict(img4/x_train.max())
        pred.append(np.max(final_pred))
        pred_class.append(np.argmax(final_pred))

Luego, la región con la mayor posibilidad de contener objetos que no sean de fondo en los objetos de la región candidata extraídos se toma como la región candidata final, donde 1la corresponde a la imagen de fondo, y calcularemos las coordenadas correspondientes de esta región en la imagen original:

    pred = np.array(pred)
    pred_class = np.array(pred_class)
    pred2 = pred[pred_class!=1]
    pred_class2 = pred_class[pred_class!=1]
    candidates2 = np.array(candidates)[pred_class!=1]
    x, y, w, h = candidates2[np.argmax(pred2)]
    new_x = int(x * image.shape[1] / 224)
    new_y = int(y * image.shape[0] / 224)
    new_w = int(w * image.shape[1] / 224)
    new_h = int(h * image.shape[0] / 224)

Finalmente, dibujamos la imagen y el cuadro delimitador rectangular de la región candidata final.

    ax[1].set_title(labels[pred_class2[np.argmax(pred2)]])
    ax[1].imshow(cv2.cvtColor(image, cv2.COLOR_BGR2RGB))
    rect = mpatches.Rectangle((new_x, new_y), new_w, new_h, fill=False, edgecolor='red', linewidth=1)
    ax[1].add_patch(rect)

(6) Usando la nueva imagen como el valor del parámetro, llame a la test_predictionsfunción :

file_name = '10.png'
test_predictions(file_name)
plt.show()

Resultados de la prueba

Como puede ver, el modelo puede calcular con precisión la clase del objeto en la imagen, pero el cuadro delimitador con la mayor probabilidad de contener el objeto de destino (la región candidata final) aún debe corregirse. Esto es exactamente lo que debemos hacer en el siguiente paso: ajustar el cuadro delimitador del objeto de destino.

2.3 Ajustar el cuadro delimitador del objeto de destino

(1) Cree y compile un modelo que tome las características de la imagen VGG16extraídas como entrada y prediga el desplazamiento del cuadro delimitador:

model2 = Sequential()
model2.add(Flatten(input_shape=((7, 7, 512))))
model2.add(Dense(1024, activation='relu'))
model2.add(Dense(4, activation='linear'))

model2.compile(loss='mean_absolute_error', optimizer='adam')
# 预测候选区域图像类别
pred = model.predict(x_train/x_train.max())
pred_class = np.argmax(pred, axis=1)

(2) Al construir un modelo para predecir los desplazamientos del cuadro delimitador, solo debemos asegurarnos de que los desplazamientos del cuadro delimitador se pronostiquen para aquellas regiones de la imagen que pueden contener el objeto de destino:

for i in range(1000):
    samp=random.sample(range(len(x_train)),500)
    x_train2 = [x_train[i] for i in samp if pred_class[i]!=1]
    x_train2 = np.array(x_train2)
    final_delta2 = [final_delta[i] for i in samp if pred_class[i]!=1]
    model2.fit(x_train2/x_train.max(), np.array(final_delta2),
                validation_split = 0.1,
                epochs=1,
                batch_size=32,
                verbose=1)

En el código anterior, iteramos sobre el conjunto de datos de matriz de entrada y creamos un nuevo conjunto de datos que incluye solo aquellas regiones que pueden no ser clases de fondo. Además, 1000repetimos para afinar el modelo.
(3) Cree una función que use la ruta de la imagen como parámetro, prediga la categoría de la imagen y corrija el cuadro delimitador:

def test_predictions2(file_name):
    image = cv2.imread(file_name)
    img = cv2.resize(image, (224, 224))
    candidates = extract_candidates(img)
    _, ax = plt.subplots(1, 2)
    ax[0].imshow(cv2.cvtColor(image, cv2.COLOR_BGR2RGB))
    ax[0].set_title(file_name.split('/')[-1])
    pred = []
    pred_class = []
    del_new = []

    for ix, candidate in enumerate(candidates):
        l, t, w, h = np.array(candidate).astype(int)
        img2 = img[t:t+h, l:l+w, :3]
        img3 = cv2.resize(img2, (224, 224)) # /255
        img3 = preprocess_input(img3.reshape(1, 224, 224, 3))
        img4 = vgg16_model.predict(img3)
        final_pred = model.predict(img4/x_train.max())
        delta_new = model2.predict(img4/x_train.max())[0]
        pred.append(np.max(final_pred))
        pred_class.append(np.argmax(final_pred))
        del_new.append(delta_new)
    
    pred = np.array(pred)
    pred_class = np.array(pred_class)
    non_bgs = (pred_class!=1)
    pred = pred[non_bgs]
    pred_class = pred_class[non_bgs]
    del_new = np.array(del_new)
    del_new = del_new[non_bgs]
    del_pred = del_new * 224
    candidates = C = np.array(candidates)[non_bgs]
    C = np.clip(C, 0, 224)
    C[:, 2] += C[:, 0]
    C[:, 3] += C[:, 1]

    bbs_pred = candidates - del_pred
    bbs_pred = np.clip(bbs_pred, 0, 224)
    bbs_pred[:, 2] -= bbs_pred[:, 0]
    bbs_pred[:, 3] -= bbs_pred[:, 1]
    final_bbs_pred = bbs_pred[np.argmax(pred)]

    x, y, w, h = final_bbs_pred
    new_x = int(x * image.shape[1] / 224)
    new_y = int(y * image.shape[0] / 224)
    new_w = int(w * image.shape[1] / 224)
    new_h = int(h * image.shape[0] / 224)

    ax[1].imshow(cv2.cvtColor(image, cv2.COLOR_BGR2RGB))
    rect = mpatches.Rectangle((new_x,new_y), new_w, new_h, fill=False, edgecolor='red', linewidth=1)
    ax[1].add_patch(rect)
    ax[1].set_title(labels[pred_class[np.argmax(pred)]])

(4) Pruebe el modelo y también extraiga una imagen de prueba que contenga solo un objeto:

single_object_images = []
for ix, xml in enumerate(XMLs[N:]):
    xml_file = annotations + xml
    fname = xml.split('.')[0]
    with open(xml_file, 'rb') as f:
        xml = xmltodict.parse(f, xml_attribs=True)
        l = []
        if isinstance(xml["annotation"]["object"], list):
            continue
        single_object_images.append(xml["annotation"]["filename"])
        if ix > 100:
            break

Iteramos a través de las imágenes e identificamos y localizamos imágenes que contienen un solo objeto de destino.
(5) Finalmente, prediga nuestra imagen de prueba:

# 使用测试集图像
# ix = 2
# filename = jpegs + single_object_images[ix]
# test_predictions2(filename)
# plt.show()
# 使用其他图像
file_name = '10.png'
test_predictions2(file_name)
plt.show()

Calibrar cuadro delimitador

Como se muestra arriba, aunque el modelo de ajuste del cuadro delimitador puede corregir el cuadro delimitador. Sin embargo, aún se requieren algunas correcciones adicionales en los cuadros delimitadores, y podemos entrenarnos con más datos para lograrlo.

3. Supresión no máxima (NMS)

En el modelo de detección de R-CNNobjetos , solo consideramos regiones candidatas que no son de fondo y solo usamos la región candidata con la probabilidad de objeto más alta para la tarea de detección de objetos. Sin embargo, en presencia de múltiples objetos en la imagen, el uso de la estrategia anterior no tiene éxito en la localización de múltiples objetos.
Para poder extraer tantos objetos como sea posible en la imagen, podemos usar supresión no máxima para filtrar candidatos de las regiones candidatas para que podamos realizar la detección de múltiples objetos.

3.1 Supresión No Máxima

En el modelo de detección de objetivos, normalmente se obtienen múltiples regiones candidatas, y se predicen las categorías y las compensaciones para estas regiones candidatas y, finalmente, los cuadros delimitadores predichos se obtienen utilizando las regiones candidatas y las compensaciones. Sin embargo, cuando el número de regiones candidatas es grande, se pueden obtener cuadros delimitadores predichos más similares en el mismo objeto. Para mantener los nodos concisos, generalmente necesitamos eliminar los cuadros delimitadores predichos similares, un método común se denomina supresión no máxima ( non-maximum suppression, NMS).

3.2 Flujo de algoritmo de supresión no máximo

non-maximum suppressionHay varios algoritmos para implementar la supresión no máxima, el algoritmo de supresión no máxima ( , ) que implementaremos es NMSel siguiente:

  • (1) Extraer regiones candidatas de la imagen
  • (2) Dar forma a la región candidata y predecir la clase de objetos contenidos en la imagen
  • (3) Si el objeto candidato no es un objeto de clase de fondo, mantendremos el objeto candidato
  • (4) Para todos los candidatos de clase sin antecedentes, los clasificamos según su probabilidad de contener objetos
  • (5) Según IoU, compare el primer objeto candidato (secuencia ordenada por probabilidad de clase en orden descendente) con todos los demás candidatos
  • (6) Si el área de superposición entre cualquier otra región candidata y la primera región candidata es mayor que cierto umbral, deséchela
  • (7) Entre los objetos candidatos restantes, considere el objeto de la región candidata con la mayor probabilidad de contener el objeto nuevamente
  • (8) En 步骤 (7) la lista de áreas candidatas filtradas, continúe comparando el primer objeto candidato con los objetos del área candidata restantes
  • (9) Repita 步骤 (5) ~ (8)hasta que no haya candidatos para comparar
  • (10) Después de realizar los pasos anteriores, dibuje el objeto candidato retenido final como un cuadro delimitador

En la siguiente subsección, Pythonimplementaremos .

3.3 Implementación de supresión no máxima

(1) Extraiga todas las regiones con alta confianza que contengan objetos que no sean de fondo de la imagen:

file_name = '8.png'
image = cv2.imread(file_name)
img = cv2.resize(image, (224, 224))
img_area = img.shape[0] * img.shape[1]
candidates = extract_candidates(img)
plt.imshow(cv2.cvtColor(image, cv2.COLOR_BGR2RGB))
plt.show()

cargar imagen

(2) Preprocesar las regiones candidatas: pasarlas al VGG16modelo y luego predecir la clase de cada región candidata y el cuadro delimitador de la región:

pred = []
pred_class = []
del_new = []

for ix, candidate in enumerate(candidates):
    l, t, w, h = np.array(candidate).astype(int)
    img2 = img[t:t+h, l:l+w, :3]
    img3 = cv2.resize(img2, (224, 224)) # /255
    img3 = preprocess_input(img3.reshape(1, 224, 224, 3))
    img4 = vgg16_model.predict(img3)
    final_pred = model.predict(img4/x_train.max())
    delta_new = model2.predict(img4/x_train.max())[0]
    pred.append(np.max(final_pred))
    pred_class.append(np.argmax(final_pred))
    del_new.append(delta_new)

pred = np.array(pred)
pred_class = np.array(pred_class)

(3) Extraiga las regiones candidatas que no son de fondo y sus compensaciones de cuadro delimitador correspondientes, y filtre para obtener la probabilidad de categoría, la categoría y el desplazamiento de cuadro delimitador de todas las regiones candidatas que no son de fondo (la categoría predicha es para 1representar clase de fondo):

non_bgs = ((pred_class!=1))
pred = pred[non_bgs]
pred_class = pred_class[non_bgs]

del_new = np.array(del_new)
del_new = del_new[non_bgs]
del_pred = del_new * 224

(4) Corrija el área candidata utilizando el valor de desplazamiento del cuadro delimitador, asegurándose de que xmaxy ymaxcoordenadas no puedan ser mayores que 224, además, debemos asegurarnos de que el ancho y el alto del cuadro delimitador no puedan ser negativos:

candidates = C = np.array(candidates)[non_bgs]
C = np.clip(C, 0, 224)
C[:, 2] += C[:, 0]
C[:, 3] += C[:, 1]

bbs_pred = candidates - del_pred
bbs_pred = np.clip(bbs_pred, 0, 224)

bbs_pred[:, 2] -= bbs_pred[:, 0]
bbs_pred[:, 3] -= bbs_pred[:, 1]
bbs_pred = np.clip(bbs_pred, 0, 224)

bbs_pred2 = bbs_pred[(bbs_pred[:, 2] > 0) & (bbs_pred[:, 3] > 0)]
pred = pred[(bbs_pred[:, 2] > 0) & (bbs_pred[:, 3] >0)]
pred_class = pred_class[(bbs_pred[:, 2] > 0) & (bbs_pred[:, 3] > 0)]

(5) Finalmente, dibuje la imagen y el cuadro delimitador del objeto de destino:

fig, ax = plt.subplots(ncols=1, nrows=1, figsize=(6, 6))
ax.imshow(cv2.cvtColor(img, cv2.COLOR_BGR2RGB))
for ix, (x, y, w, h) in enumerate(bbs_pred2):
    new_x = int(x * image.shape[1] / 224)
    new_y = int(y * image.shape[0] / 224)
    new_w = int(w * image.shape[1] / 224)
    new_h = int(h * image.shape[0] / 224)
    rect = mpatches.Rectangle((new_x,new_y), new_w, new_h, fill=False, edgecolor='red', linewidth=1)

    ax.add_patch(rect)
plt.show()

área candidata

(6) Para realizar una supresión no máxima en los cuadros delimitadores, defina una función que se realice nms_boxesde acuerdo con un umbral (intersección mínima de dos cuadros delimitadores), las coordenadas del cuadro delimitador y un valor de probabilidad asociado con cada cuadro delimitador NMS. Calcule los valores de , y para cada
cuadro delimitador , sus áreas correspondientes y ordénelos por probabilidad:xywh

def nms_boxes(threshold, boxes, scores):
    x = boxes[:, 0]
    y = boxes[:, 1]
    w = boxes[:, 2]
    h = boxes[:, 3]
    areas = w * h
    order = scores.argsort()[::-1]

Calcular la relación de intersección de la región candidata con mayor probabilidad y el resto de las regiones candidatas:

    keep = []
    while order.size > 0:
        i = order[0]
        keep.append(i)
        xx1 = np.maximum(x[i], x[order[1:]])
        yy1 = np.maximum(y[i], y[order[1:]])
        xx2 = np.maximum(x[i]+w[i], x[order[1:]]+w[order[1:]])
        yy2 = np.maximum(y[i]+h[i], y[order[1:]]+h[order[1:]])
        w1 = np.maximum(0.0, xx2-xx1+1)
        h1 = np.maximum(0.0, yy2-yy1+1)
        inter = w1 * h1
        iou = inter / (areas[i] + areas[order[1:]] - inter)

Seleccione el área candidata IoUmenor que el umbral dado NMS, filtre la lista de áreas candidatas circularmente de acuerdo con el flujo de algoritmo presentado en la sección anterior y determine el siguiente grupo de lista de áreas candidatas de acuerdo con el área candidata con la probabilidad de categoría más alta en la lista:

        inds = np.where(iou <= threshold)[0]
        order = order[inds+1]

Devuelve los índices candidatos que deben conservarse:

    keep = np.array(keep)
    return keep

NMSLa función que realiza el cálculo nms_boxes:

keep_box_ixs = nms_boxes(0.3, bbs_pred2, pred)

print(pred, keep_box_ixs)

Al trazar el cuadro delimitador resultante NMSdespués , puede ver que eliminamos todos los demás cuadros delimitadores de regiones candidatas similares que se generaron:

fig, ax = plt.subplots(ncols=1, nrows=1, figsize=(6, 6))
ax.imshow(cv2.cvtColor(image, cv2.COLOR_BGR2RGB))

for ix, (x, y, w, h) in enumerate(bbs_pred2):
    if ix not in keep_box_ixs:
        continue
    new_x = int(x * image.shape[1] / 224)
    new_y = int(y * image.shape[0] / 224)
    new_w = int(w * image.shape[1] / 224)
    new_h = int(h * image.shape[0] / 224)
    rect = mpatches.Rectangle(
        (new_x, new_y), new_w, new_h, fill=False, edgecolor='red', linewidth=1,)

    ax.add_patch(rect)
    centerx = new_x# + new_w/2
    centery = new_y + 20# + new_h - 10
    plt.text(centerx, centery,labels[pred_class[ix]]+" "+str(round(pred[ix],2)),fontsize = 20,color='red')

plt.show()

Imagen de resultado después de realizar NMS

resumen

R-CNNEs un algoritmo clásico de detección de objetivos basado en regiones candidatas, que introduce redes neuronales convolucionales en el campo de la detección de objetivos. Este documento primero presenta la idea central del R-CNNmodelo y el proceso de detección de objetivos, luego Kerasimplementa R-CNNun utilizando y finalmente presenta la supresión no máxima para eliminar cuadros delimitadores predichos similares.

enlace de la serie

Práctica de aprendizaje profundo de Keras (1): explicación detallada de la base de la red neuronal y el proceso de entrenamiento del modelo
Práctica de aprendizaje profundo de Keras (2): uso de Keras para crear una red neuronal
Práctica de aprendizaje profundo de Keras (3): tecnología de optimización del rendimiento de la red neuronal
Práctica de aprendizaje profundo de Keras ( 4) -
Explicación detallada de las funciones de activación y funciones de pérdida comúnmente utilizadas
en el aprendizaje profundo
(7) - Explicación detallada e implementación de redes neuronales convolucionales
Práctica de aprendizaje profundo de Keras (8) - Uso del aumento de datos para mejorar el rendimiento de la red neuronal
Práctica de aprendizaje profundo de Keras (9) ) - Limitaciones de las redes neuronales convolucionales
Práctica de aprendizaje profundo de Keras (10 ) ) - Práctica de aprendizaje profundo de Keras de transferencia de aprendizaje
(11) - Visualización de la salida de la capa media de la red neuronal
Práctica de aprendizaje profundo de Keras (12) - Detección de puntos de características faciales
Práctica de aprendizaje profundo de Keras (13 ) - Explicación detallada de los conceptos básicos de detección de objetivos

Supongo que te gusta

Origin blog.csdn.net/LOVEmy134611/article/details/125566214
Recomendado
Clasificación