Классификация и обнаружение объектов - основные части компьютерного зрения. Классификация - это обнаружение того, что находится на изображении, а обнаружение объекта и локализация - поиск того, где находится этот объект на этом изображении. Обнаружение - более сложная проблема, поскольку нам нужно найти координаты объекта на изображении.

Чтобы решить эту проблему, R-CNN был введен Россом Гиршиком, Джеффом Донахью, Тревором Дарреллом и Джитендрой Малик в 2014 году. R-CNN обозначает регионы с CNN. В R-CNN вместо того, чтобы проводить классификацию по огромному количеству регионов, мы пропускаем изображение через выборочный поиск и выбираем первые 2000 предложений региона из результата и запускаем классификацию по нему. Таким образом, вместо того, чтобы классифицировать огромное количество регионов, нам нужно просто классифицировать первые 2000 регионов. Это делает этот алгоритм более быстрым по сравнению с предыдущими методами обнаружения объектов. В R-CNN есть 4 шага. Вот они:

  1. Пропустите изображение через выборочный поиск и сгенерируйте предложение региона.
  2. Рассчитайте IOU (пересечение над объединением) в предлагаемой области с наземными данными и добавьте метку к предлагаемым областям.
  3. Сделайте переносное обучение, используя предложенные регионы с метками.
  4. Передайте тестовое изображение для выборочного поиска, а затем передайте первые 2000 предложенных областей из обученной модели и спрогнозируйте класс этих областей.

Я собираюсь реализовать полную R-CNN с нуля в Керасе, используя набор данных Airplane из http://www.escience.cn/people/JunweiHan/NWPU-RESISC45.html. Чтобы получить аннотированный набор данных, вы можете скачать его по ссылке ниже. Код для реализованной RCNN также можно найти в указанном ниже репозитории.

Https://github.com/1297rohit/RCNN.git

После того, как вы загрузили набор данных, вы можете приступить к шагам, описанным ниже.

import os,cv2,keras
import pandas as pd
import matplotlib.pyplot as plt
import numpy as np
import tensorflow as tf

Первый шаг - импортировать все библиотеки, которые потребуются для реализации R-CNN. Нам нужен cv2 для выполнения выборочного поиска по изображениям. Чтобы использовать выборочный поиск, нам нужно скачать opencv-contrib-python. Чтобы загрузить это, просто запустите pip install opencv-contrib-python в терминале и установите его из pypi.

ss = cv2.ximgproc.segmentation.createSelectiveSearchSegmentation()

После загрузки opencv-contrib нам нужно инициализировать выборочный поиск. Для этого мы добавили шаг выше.

def get_iou(bb1, bb2):
    assert bb1['x1'] < bb1['x2']
    assert bb1['y1'] < bb1['y2']
    assert bb2['x1'] < bb2['x2']
    assert bb2['y1'] < bb2['y2']
    x_left = max(bb1['x1'], bb2['x1'])
    y_top = max(bb1['y1'], bb2['y1'])
    x_right = min(bb1['x2'], bb2['x2'])
    y_bottom = min(bb1['y2'], bb2['y2'])
    if x_right < x_left or y_bottom < y_top:
        return 0.0
    intersection_area = (x_right - x_left) * (y_bottom - y_top)
    bb1_area = (bb1['x2'] - bb1['x1']) * (bb1['y2'] - bb1['y1'])
    bb2_area = (bb2['x2'] - bb2['x1']) * (bb2['y2'] - bb2['y1'])
    iou = intersection_area / float(bb1_area + bb2_area - intersection_area)
    assert iou >= 0.0
    assert iou <= 1.0
    return iou

Теперь мы инициализируем функцию для вычисления IOU (Intersection Over Union) наземного блока истинности из блока, вычисленного путем выборочного поиска. Чтобы узнать больше о расчете долговых обязательств, вы можете перейти по ссылке ниже.

Https://www.pyimagesearch.com/2016/11/07/intersection-over-union-iou-for-object-detection/

train_images=[]
train_labels=[]
for e,i in enumerate(os.listdir(annot)):
    try:
        if i.startswith("airplane"):
            filename = i.split(".")[0]+".jpg"
            print(e,filename)
            image = cv2.imread(os.path.join(path,filename))
            df = pd.read_csv(os.path.join(annot,i))
            gtvalues=[]
            for row in df.iterrows():
                x1 = int(row[1][0].split(" ")[0])
                y1 = int(row[1][0].split(" ")[1])
                x2 = int(row[1][0].split(" ")[2])
                y2 = int(row[1][0].split(" ")[3])
                gtvalues.append({"x1":x1,"x2":x2,"y1":y1,"y2":y2})
            ss.setBaseImage(image)
            ss.switchToSelectiveSearchFast()
            ssresults = ss.process()
            imout = image.copy()
            counter = 0
            falsecounter = 0
            flag = 0
            fflag = 0
            bflag = 0
            for e,result in enumerate(ssresults):
                if e < 2000 and flag == 0:
                    for gtval in gtvalues:
                        x,y,w,h = result
                        iou = get_iou(gtval,{"x1":x,"x2":x+w,"y1":y,"y2":y+h})
                        if counter < 30:
                            if iou > 0.70:
                                timage = imout[y:y+h,x:x+w]
                                resized = cv2.resize(timage, (224,224), interpolation = cv2.INTER_AREA)
                                train_images.append(resized)
                                train_labels.append(1)
                                counter += 1
                        else :
                            fflag =1
                        if falsecounter <30:
                            if iou < 0.3:
                                timage = imout[y:y+h,x:x+w]
                                resized = cv2.resize(timage, (224,224), interpolation = cv2.INTER_AREA)
                                train_images.append(resized)
                                train_labels.append(0)
                                falsecounter += 1
                        else :
                            bflag = 1
                    if fflag == 1 and bflag == 1:
                        print("inside")
                        flag = 1
    except Exception as e:
        print(e)
        print("error in "+filename)
        continue

Приведенный выше код выполняет предварительную обработку и создает набор данных для передачи модели. Так как в этом случае у нас может быть 2 класса. Эти классы определяют, может ли предлагаемая область быть передним планом (например, самолетом) или фоном. Таким образом, мы установим метку переднего плана (т.е. Самолет) равной 1, а метку фона равной 0. Следующие шаги выполняются в приведенном выше блоке кода.

  1. Прокрутите папку с изображениями и установите каждое изображение одно за другим в качестве основы для выборочного поиска с помощью кода ss.setBaseImage (image).
  2. Инициализация быстрого выборочного поиска и получение предлагаемых регионов с использованием кода ss.switchToSelectiveSearchFast () и ssresults = ss.process ()
  3. Перебор всех первых 2000 результатов, полученных путем выборочного поиска, и вычисление долговых обязательств предложенной области и аннотированной области с использованием созданной выше функции get_iou ().
  4. Теперь, когда на одном изображении может быть много отрицательных образцов (то есть фона) и только несколько положительных образцов (например, самолет), поэтому нам нужно убедиться, что у нас есть хорошая пропорция как положительных, так и отрицательных образцов для обучения нашей модели. Поэтому мы установили, что мы будем собирать максимум 30 отрицательных образцов (т. Е. Фона) и положительных образцов (т. Е. От самолета) с одного изображения.

После запуска приведенного выше фрагмента кода наши данные для обучения будут готовы. Список train_images = [] будет содержать все изображения, а train_labels = [] будет содержать все метки, помечающие изображения самолетов как 1, а не изображения самолетов (например, фоновые изображения) как 0. .

X_new = np.array(train_images)
y_new = np.array(train_labels)

После завершения процесса создания набора данных мы преобразуем массив в массив numpy, чтобы мы могли легко пройти по нему и передать набор данных в модель эффективным способом.

from keras.layers import Dense
from keras import Model
from keras import optimizers
from keras.preprocessing.image import ImageDataGenerator
from keras.optimizers import Adam
from keras.applications.vgg16 import VGG16
vggmodel = VGG16(weights='imagenet', include_top=True)

Теперь займемся передачей обучения по весу имагенета. Мы импортируем модель VGG16, а также добавим в нее вес изображения. Чтобы узнать больше о трансферном обучении, вы можете обратиться к статье по ссылке ниже.

Https://medium.com/@1297rohit/transfer-learning-from-scratch-using-keras-339834b153b9

for layers in (vggmodel.layers)[:15]:
    print(layers)
    layers.trainable = False
X= vggmodel.layers[-2].output
predictions = Dense(2, activation="softmax")(X)
model_final = Model(input = vggmodel.input, output = predictions)
opt = Adam(lr=0.0001)
model_final.compile(loss = keras.losses.categorical_crossentropy, optimizer = opt, metrics=["accuracy"])
model_final.summary()

В этой части цикла мы замораживаем первые 15 слоев модели. После этого мы вынимаем второй последний слой модели и затем добавляем 2-х единичный слой softmax density, так как у нас есть только 2 класса для прогнозирования, то есть передний план или фон. После этого мы составляем модель с помощью оптимизатора Adam со скоростью обучения 0,001. Мы используем category_crossentropy как потерю, поскольку выходные данные модели категоричны. Наконец, краткое изложение модели будет напечатано с использованием model_final.summary (). Изображение резюме прилагается ниже.

from sklearn.model_selection import train_test_split
from sklearn.preprocessing import LabelBinarizer
class MyLabelBinarizer(LabelBinarizer):
    def transform(self, y):
        Y = super().transform(y)
        if self.y_type_ == 'binary':
            return np.hstack((Y, 1-Y))
        else:
            return Y
    def inverse_transform(self, Y, threshold=None):
        if self.y_type_ == 'binary':
            return super().inverse_transform(Y[:, 0], threshold)
        else:
            return super().inverse_transform(Y, threshold)
lenc = MyLabelBinarizer()
Y =  lenc.fit_transform(y_new)
X_train, X_test , y_train, y_test = train_test_split(X_new,Y,test_size=0.10)

После создания модели нам нужно разделить набор данных на набор для обучения и тестирования. Перед этим нам нужно горячо закодировать этикетку. Для этого мы используем MyLabelBinarizer () и кодируем набор данных. Затем мы разделяем набор данных с помощью train_test_split из sklearn. Мы сохраняем 10% набора данных как тестовый набор и 90% как обучающий набор.

trdata = ImageDataGenerator(horizontal_flip=True, vertical_flip=True, rotation_range=90)
traindata = trdata.flow(x=X_train, y=y_train)
tsdata = ImageDataGenerator(horizontal_flip=True, vertical_flip=True, rotation_range=90)
testdata = tsdata.flow(x=X_test, y=y_test)

Теперь мы будем использовать Keras ImageDataGenerator для передачи набора данных в модель. Мы сделаем некоторые дополнения к набору данных, такие как горизонтальное отражение, вертикальное отражение и вращение, чтобы увеличить набор данных.

from keras.callbacks import ModelCheckpoint, EarlyStopping
checkpoint = ModelCheckpoint("ieeercnn_vgg16_1.h5", monitor='val_loss', verbose=1, save_best_only=True, save_weights_only=False, mode='auto', period=1)
early = EarlyStopping(monitor='val_loss', min_delta=0, patience=100, verbose=1, mode='auto')
hist = model_final.fit_generator(generator= traindata, steps_per_epoch= 10, epochs= 1000, validation_data= testdata, validation_steps=2, callbacks=[checkpoint,early])

Теперь приступим к обучению модели с помощью fit_generator.

z=0
for e,i in enumerate(os.listdir(path)):
    if i.startswith("4"):
        z += 1
        img = cv2.imread(os.path.join(path,i))
        ss.setBaseImage(img)
        ss.switchToSelectiveSearchFast()
        ssresults = ss.process()
        imout = img.copy()
        for e,result in enumerate(ssresults):
            if e < 2000:
                x,y,w,h = result
                timage = imout[y:y+h,x:x+w]
                resized = cv2.resize(timage, (224,224), interpolation = cv2.INTER_AREA)
                img = np.expand_dims(resized, axis=0)
                out= model_final.predict(img)
                if out[0][0] > 0.70:
                    cv2.rectangle(imout, (x, y), (x+w, y+h), (0, 255, 0), 1, cv2.LINE_AA)
        plt.figure()
        plt.imshow(imout)
        break

Теперь, когда мы создали модель. Нам нужно сделать прогноз по этой модели. Для этого нам нужно выполнить шаги, указанные ниже:

  1. передать изображение из выборочного поиска.
  2. передать все результаты выборочного поиска модели в качестве входных данных с помощью model_final.predict (img).
  3. Если в выходных данных модели указано, что область является изображением переднего плана (то есть изображением самолета), и если достоверность выше определенного порога, тогда создайте ограничивающую рамку на исходном изображении по координате предложенной области.

Как вы можете видеть выше, мы создали рамку для предложенной области, в которой точность модели была выше 0,70. Таким образом мы можем выполнить локализацию изображения и выполнить обнаружение объекта с помощью R-CNN. Вот как мы реализуем архитектуру R-CNN с нуля с помощью keras.

Вы можете получить полностью реализованный R-CNN по ссылке, представленной ниже.

Https://github.com/1297rohit/RCNN.git

Если вы хотите шаг за шагом узнать о Обнаружении лиц и Распознавании лиц с нуля, вы можете перейти к моей статье по этой теме по ссылке: https://medium.com/@1297rohit / шаг за шагом-реализация-кода-распознавания-лица-с нуля-в-python-cc95fa041120

Наслаждайтесь R-CNN!