Классификация и обнаружение объектов - основные части компьютерного зрения. Классификация - это обнаружение того, что находится на изображении, а обнаружение объекта и локализация - поиск того, где находится этот объект на этом изображении. Обнаружение - более сложная проблема, поскольку нам нужно найти координаты объекта на изображении.
Чтобы решить эту проблему, R-CNN был введен Россом Гиршиком, Джеффом Донахью, Тревором Дарреллом и Джитендрой Малик в 2014 году. R-CNN обозначает регионы с CNN. В R-CNN вместо того, чтобы проводить классификацию по огромному количеству регионов, мы пропускаем изображение через выборочный поиск и выбираем первые 2000 предложений региона из результата и запускаем классификацию по нему. Таким образом, вместо того, чтобы классифицировать огромное количество регионов, нам нужно просто классифицировать первые 2000 регионов. Это делает этот алгоритм более быстрым по сравнению с предыдущими методами обнаружения объектов. В R-CNN есть 4 шага. Вот они:
- Пропустите изображение через выборочный поиск и сгенерируйте предложение региона.
- Рассчитайте IOU (пересечение над объединением) в предлагаемой области с наземными данными и добавьте метку к предлагаемым областям.
- Сделайте переносное обучение, используя предложенные регионы с метками.
- Передайте тестовое изображение для выборочного поиска, а затем передайте первые 2000 предложенных областей из обученной модели и спрогнозируйте класс этих областей.
Я собираюсь реализовать полную R-CNN с нуля в Керасе, используя набор данных Airplane из http://www.escience.cn/people/JunweiHan/NWPU-RESISC45.html. Чтобы получить аннотированный набор данных, вы можете скачать его по ссылке ниже. Код для реализованной RCNN также можно найти в указанном ниже репозитории.
После того, как вы загрузили набор данных, вы можете приступить к шагам, описанным ниже.
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. Следующие шаги выполняются в приведенном выше блоке кода.
- Прокрутите папку с изображениями и установите каждое изображение одно за другим в качестве основы для выборочного поиска с помощью кода ss.setBaseImage (image).
- Инициализация быстрого выборочного поиска и получение предлагаемых регионов с использованием кода ss.switchToSelectiveSearchFast () и ssresults = ss.process ()
- Перебор всех первых 2000 результатов, полученных путем выборочного поиска, и вычисление долговых обязательств предложенной области и аннотированной области с использованием созданной выше функции get_iou ().
- Теперь, когда на одном изображении может быть много отрицательных образцов (то есть фона) и только несколько положительных образцов (например, самолет), поэтому нам нужно убедиться, что у нас есть хорошая пропорция как положительных, так и отрицательных образцов для обучения нашей модели. Поэтому мы установили, что мы будем собирать максимум 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
Теперь, когда мы создали модель. Нам нужно сделать прогноз по этой модели. Для этого нам нужно выполнить шаги, указанные ниже:
- передать изображение из выборочного поиска.
- передать все результаты выборочного поиска модели в качестве входных данных с помощью model_final.predict (img).
- Если в выходных данных модели указано, что область является изображением переднего плана (то есть изображением самолета), и если достоверность выше определенного порога, тогда создайте ограничивающую рамку на исходном изображении по координате предложенной области.
Как вы можете видеть выше, мы создали рамку для предложенной области, в которой точность модели была выше 0,70. Таким образом мы можем выполнить локализацию изображения и выполнить обнаружение объекта с помощью R-CNN. Вот как мы реализуем архитектуру R-CNN с нуля с помощью keras.
Вы можете получить полностью реализованный R-CNN по ссылке, представленной ниже.
Если вы хотите шаг за шагом узнать о Обнаружении лиц и Распознавании лиц с нуля, вы можете перейти к моей статье по этой теме по ссылке: https://medium.com/@1297rohit / шаг за шагом-реализация-кода-распознавания-лица-с нуля-в-python-cc95fa041120
Наслаждайтесь R-CNN!