? Распознавание объектов с помощью YOLO v3 на Tensorflow 2.0

МЕНЮ


Искусственный интеллект
Поиск
Регистрация на сайте
Помощь проекту

ТЕМЫ


Новости ИИРазработка ИИВнедрение ИИРабота разума и сознаниеМодель мозгаРобототехника, БПЛАТрансгуманизмОбработка текстаТеория эволюцииДополненная реальностьЖелезоКиберугрозыНаучный мирИТ индустрияРазработка ПОТеория информацииМатематикаЦифровая экономика

Авторизация



RSS


RSS новости


Распознавание объектов с помощью YOLO v3 на Tensorflow 2.0

До Yolo большинство подходов к распознаванию объектов заключалось в попытках адаптировать классификаторы к распознаванию. В YOLO, распознавание объектов было реализовано как задача регрессии к раздельным ограничивающим рамкам, с которыми связаны вероятности принадлежности к разным классам. Ниже мы познакомимся с моделью распознавания объектов YOLO и методом ее реализации в Tensorflow 2.0.

О YOLO: Наша обобщенная архитектура чрезвычайно быстра. Базовая модель YOLO обрабатывает изображения в реальном времени со скоростью 45 фреймов в секунду. Меньшая версия модели, Fast YOLO, обрабатывает целых 155 фреймов в секунду…

— You Only Look Once: Unified, Real-Time Object Detection, 2015

Что такое YOLO?

YOLO – это передовая сеть для распознавания объектов (object detection), разработанная Джозефом Редмоном (Joseph Redmon). Главное, что отличает ее от других популярных архитектур – это скорость. Модели семейства YOLO действительно быстрые, намного быстрее R-CNN и других. Это значит, что мы можем распознавать объекты в реальном времени.

Во время первой публикации (в 2016 году) YOLO имела передовую mAP (mean Average Precision), по сравнению с такими системами, как R-CNN и DPM. С другой стороны, YOLO с трудом локализует объекты точно. Тем не менее, она обучается общему представлению объектов. В новой версии как скорость, так и точность системы были улучшены.

Альтернативы (на момент публикации):

Другие подходы в основном использовали метод плавающего над изображением окна, и классификатора для этих регионов (DPM – deformable part models). Кроме этого, R-CNN использовал метод предложения регионов (region proposal). Этот метод сначала генерировал потенциальные содержащие рамки, после чего для них вызывался классификатор, а потом производилась пост-обработка для удаления двойных распознаваний и усовершенствования содержащих рамок.

YOLO преобразовала задачу распознавания объектов к единой задаче регрессии. Она проходит прямо от пикселей изображения до координат содержащих рамок и вероятностей классов. Таким образом, единая CNN предсказывает множество содержащих рамок и вероятности классов для этих рамок.

Теория

Поскольку YOLO смотрит на изображение только один раз, плавающее окно – это неправильный подход. Вместо этого, все изображение разбивается с помощью сетки на ячейки размером ???. После этого каждая ячейка отвечает за предсказание нескольких вещей

Во-первых, каждая ячейка отвечает за предсказание нескольких содержащих рамок и показателя уверенности (confidence) для каждой из них – другими словами, это вероятность того, что данная рамка содержит объект. Если в какой-то ячейке сетки объектов нет, то очень важно, чтобы confidence для этой ячейки был очень малым.

Когда мы визуализируем все эти предсказания, мы получаем карту всех объектов и набор содержащих рамок, ранжированных по их confidence.

Во-вторых, каждая ячейка отвечает за предсказание вероятностей классов. Это не значит, что какая-то ячейка содержит какой-то объект, это всего лишь вероятность. Таким образом, если ячейка сети предсказывает автомобиль, это не значит, что он там есть, но это значит, что если там есть какой-то объект, то это автомобиль.

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

В YOLO для предсказания содержащих рамок используются якорные рамки (anchor boxes). Их основная идея заключается в предопределении двух разных рамок, называемых якорными рамками или формой якорных рамок. Это позволяет нам ассоциировать два предсказания с этими якорными рамками. В общем, мы можем использовать и большее количество якорных рамок (пять или даже больше). Якоря были рассчитаны на датасете COCO с помощью k-means кластеризации.

У нас есть сетка, каждая ячейка которой должна предсказать:

  • для каждой содержащей рамки: 4 координаты (??,??,??,??) и одну "ошибку объектности" – то есть, метрику confidence, определяющую, есть объект или нет.
  • некоторое количество вероятностей классов.

Если есть некоторое смещение относительно верхнего левого угла ??,??, то эти предсказания соответствуют следующим формулам:

bx=?(tx)+cxby=?(ty)+cybw=pwetwbh=pheth

где ?? и ?? соответствуют ширине и высоте содержащей рамки. Вместо предсказания смещений, как было во второй версии YOLO, авторы предсказывают координаты локации относительно расположения ячейки сети.

Этот вывод – это вывод нашей нейронной сети. Всего там ????[??(4+1+?)] выводов, где ? – количество содержащих рамок, предсказываемых каждой ячейкой (зависит от того, в скольких масштабах мы хотим делать наши предсказания), ? – количество классов, 4 – количество содержащих рамок, а 1 – предсказание объектности. За один проход мы можем пройти от исходного изображения до выходного тензора, соответствующего распознанным объектам изображения. Стоит также упомянуть, что YOLO v3 предсказывает рамки в трех разных масштабах.

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

Простое сравнение с порогом позволит нам избавиться от предсказаний с низкой confidence. Для следующего шага важно определить, что такое пересечение относительно объединения (intersection over union). Это отношение площади пересечения прямоугольников к площади их объединения:

После этого у нас еще могут быть дубликаты, и чтобы от них избавиться, мы применяем подавление не-максимумов. Подавление не-максимумов берет содержащую рамку с максимальной вероятностью и смотрит на другие содержащие рамки, расположенные близко к первой. Ближайшие рамки с максимальным пересечением относительно объединения с первой рамкой будут подавлены.

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

Мы настоятельно рекомендуем изучить все три документа YOLO:

Реализация YOLO в Tensorflow

Мы создадим полную сверточную нейронную сеть (Fully Convolutional Network, FCN) без тренировки. Чтобы что-то предсказать с помощью этой сети, нужно загрузить веса от заранее тренированной модели. Эти веса получены после тренировки YOLOv3 на датасете COCO (Common Objects in Context), и их можно загрузить с официального сайта.

         !wget https://pjreddie.com/media/files/yolov3.weights     

Создаем папку для хранения весов

         #!rm -rf checkpoints !mkdir checkpoints     

Импортируем все необходимые библиотеки

         import cv2 import numpy as np import tensorflow as tf  from absl import logging from itertools import repeat  from tensorflow.keras import Model from tensorflow.keras.layers import Add, Concatenate, Lambda from tensorflow.keras.layers import Conv2D, Input, LeakyReLU from tensorflow.keras.layers import MaxPool2D, UpSampling2D, ZeroPadding2D from tensorflow.keras.regularizers import l2 from tensorflow.keras.losses import binary_crossentropy from tensorflow.keras.losses import sparse_categorical_crossentropy     

Проверяем версию Tensorflow. Она должна быть не ниже 2.0:

         print(tf.__version__) # 2.1.0     

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

         yolo_iou_threshold   = 0.6 # порог пересечения относительно объединения (iou) yolo_score_threshold = 0.6   weightsyolov3 = 'yolov3.weights' # путь к файлу весов weights= 'checkpoints/yolov3.tf' # путь к файлу checkpoint'ов size= 416             # приводим изображения к этому размеру  checkpoints = 'checkpoints/yolov3.tf' num_classes = 80      # количество классов в модели     

Список слоев в YOLOv3 FCN — Fully Convolutional Network

         YOLO_V3_LAYERS = [   'yolo_darknet',   'yolo_conv_0',   'yolo_output_0',   'yolo_conv_1',   'yolo_output_1',   'yolo_conv_2',   'yolo_output_2', ]     

Очень трудно загрузить веса с помощью чисто функционального API, поскольку порядок слоев в Darknet и tf.keras различаются. Здесь лучшее решение – создание подмоделей в keras. Для сохранения подмоделей рекомендуется использовать Checkpoint'ы Tensorflow, поскольку они официально поддерживаются Tensorflow.

Вот функция для загрузки весов из оригинальной тренированной модели YOLO в Darknet:

         def load_darknet_weights(model, weights_file):   wf = open(weights_file, 'rb')   major, minor, revision, seen, _ = np.fromfile(wf, dtype=np.int32, count=5)   layers = YOLO_V3_LAYERS    for layer_name in layers:     sub_model = model.get_layer(layer_name)     for i, layer in enumerate(sub_model.layers):       if not layer.name.startswith('conv2d'):         continue       batch_norm = None       if i + 1 < len(sub_model.layers) and              sub_model.layers[i + 1].name.startswith('batch_norm'):         batch_norm = sub_model.layers[i + 1]        logging.info("{}/{} {}".format(         sub_model.name, layer.name, 'bn' if batch_norm else 'bias'))        filters = layer.filters       size = layer.kernel_size[0]       in_dim = layer.input_shape[-1]        if batch_norm is None:         conv_bias = np.fromfile(wf, dtype=np.float32, count=filters)       else:         bn_weights = np.fromfile(           wf, dtype=np.float32, count=4 * filters)          bn_weights = bn_weights.reshape((4, filters))[[1, 0, 2, 3]]        conv_shape = (filters, in_dim, size, size)       conv_weights = np.fromfile(         wf, dtype=np.float32, count=np.product(conv_shape))        conv_weights = conv_weights.reshape(         conv_shape).transpose([2, 3, 1, 0])        if batch_norm is None:         layer.set_weights([conv_weights, conv_bias])       else:         layer.set_weights([conv_weights])         batch_norm.set_weights(bn_weights)    assert len(wf.read()) == 0, 'не удалось прочитать все данные'   wf.close()     

Функция для расчета пересечения относительно объединения

         def interval_overlap(interval_1, interval_2):   x1, x2 = interval_1   x3, x4 = interval_2   if x3 < x1:     return 0 if x4 < x1 else (min(x2,x4) - x1)   else:     return 0 if x2 < x3 else (min(x2,x4) - x3)  def intersectionOverUnion(box1, box2):   intersect_w = interval_overlap([box1.xmin, box1.xmax], [box2.xmin, box2.xmax])   intersect_h = interval_overlap([box1.ymin, box1.ymax], [box2.ymin, box2.ymax])   intersect_area = intersect_w * intersect_h      w1, h1 = box1.xmax-box1.xmin, box1.ymax-box1.ymin   w2, h2 = box2.xmax-box2.xmin, box2.ymax-box2.ymin      union_area = w1*h1 + w2*h2 - intersect_area   return float(intersect_area) / union_area     

Функция для отрисовки содержащей рамки, имени класса и вероятности:

         def draw_outputs(img, outputs, class_names):   boxes, score, classes, nums = outputs   boxes, score, classes, nums = boxes[0], score[0], classes[0], nums[0]   wh = np.flip(img.shape[0:2])   for i in range(nums):     x1y1 = tuple((np.array(boxes[i][0:2]) * wh).astype(np.int32))     x2y2 = tuple((np.array(boxes[i][2:4]) * wh).astype(np.int32))     img = cv2.rectangle(img, x1y1, x2y2, (255, 0, 0), 2)     img = cv2.putText(img, '{} {:.4f}'.format(       class_names[int(classes[i])], score[i]),       x1y1, cv2.FONT_HERSHEY_COMPLEX_SMALL, 1, (0, 0, 255), 2)   return img     

Мы используем пакетную нормализацию (batch normalization), чтобы нормализовать результаты для ускорения тренировки. К сожалению, tf.keras.layers.BatchNormalization работает не очень хорошо для transfer learning, поэтому здесь предлагается другое решение этой проблемы.

         class BatchNormalization(tf.keras.layers.BatchNormalization):   def call(self, x, training=False):     if training is None: training = tf.constant(False)     training = tf.logical_and(training, self.trainable)     return super().call(x, training)     

Для каждого масштаба мы определяем три якорные рамки для каждой ячейки. В этом примере маска такова:

  • 0,1,2 – мы будем использовать три первые якорные рамки
  • 3,4,5 – мы используем четвертую, пятую и шестую рамки
  • 6,7,8 – мы используем седьмую, восьмую и девятую рамки

Реализация YOLO v3

Пришло время реализовать сеть YOLOv3. Вот как выглядит ее структура:

Darknet 53 – YOLO v3
Darknet 53 – YOLO v3

Здесь основная идея – использовать только сверточные слои. Их там 53, так что проще всего создать функцию, в которую мы будем передавать важные параметры, изменяющиеся от слоя к слою.

Остаточные блоки (Residual blocks) на диаграмме архитектуры YOLOv3 используются для обучения признакам. Остаточный блок состоит из нескольких сверточных слоев и обходных путей:

Мы строим нашу модель с помощью Функционального API, простого в использовании. С ним мы можем легко задавать ветви в нашей архитектуре (блок ResNet) и легко использовать одни и те же слои несколько раз внутри архитектуры.

         def DarknetConv(x, filters, size, strides=1, batch_norm=True):   if strides == 1:     padding = 'same'   else:     x = ZeroPadding2D(((1, 0), (1, 0)))(x)  # top left half-padding     padding = 'valid'   x = Conv2D(filters=filters, kernel_size=size,           strides=strides, padding=padding,           use_bias=not batch_norm, kernel_regularizer=l2(0.0005))(x)   if batch_norm:     x = BatchNormalization()(x)     x = LeakyReLU(alpha=0.1)(x)   return x  def DarknetResidual(x, filters):   previous  = x   x = DarknetConv(x, filters // 2, 1)   x = DarknetConv(x, filters, 3)   x = Add()([previous , x])   return x   def DarknetBlock(x, filters, blocks):   x = DarknetConv(x, filters, 3, strides=2)   for _ in repeat(None, blocks):     x = DarknetResidual(x, filters)          return x   def Darknet(name=None):   x = inputs = Input([None, None, 3])   x = DarknetConv(x, 32, 3)   x = DarknetBlock(x, 64, 1)   x = DarknetBlock(x, 128, 2)   x = x_36 = DarknetBlock(x, 256, 8)   x = x_61 = DarknetBlock(x, 512, 8)   x = DarknetBlock(x, 1024, 4)   return tf.keras.Model(inputs, (x_36, x_61, x), name=name)  def YoloConv(filters, name=None):   def yolo_conv(x_in):     if isinstance(x_in, tuple):       inputs = Input(x_in[0].shape[1:]), Input(x_in[1].shape[1:])       x, x_skip = inputs        x = DarknetConv(x, filters, 1)       x = UpSampling2D(2)(x)       x = Concatenate()([x, x_skip])     else:       x = inputs = Input(x_in.shape[1:])      x = DarknetConv(x, filters, 1)     x = DarknetConv(x, filters * 2, 3)     x = DarknetConv(x, filters, 1)     x = DarknetConv(x, filters * 2, 3)     x = DarknetConv(x, filters, 1)     return Model(inputs, x, name=name)(x_in)   return yolo_conv  def YoloOutput(filters, anchors, classes, name=None):   def yolo_output(x_in):     x = inputs = Input(x_in.shape[1:])     x = DarknetConv(x, filters * 2, 3)     x = DarknetConv(x, anchors * (classes + 5), 1, batch_norm=False)     x = Lambda(lambda x: tf.reshape(x, (-1, tf.shape(x)[1], tf.shape(x)[2],                                         anchors, classes + 5)))(x)     return tf.keras.Model(inputs, x, name=name)(x_in)   return yolo_output  def yolo_boxes(pred, anchors, classes):   grid_size = tf.shape(pred)[1]   box_xy, box_wh, score, class_probs = tf.split(pred, (2, 2, 1, classes), axis=-1)    box_xy = tf.sigmoid(box_xy)   score = tf.sigmoid(score)   class_probs = tf.sigmoid(class_probs)   pred_box = tf.concat((box_xy, box_wh), axis=-1)    grid = tf.meshgrid(tf.range(grid_size), tf.range(grid_size))   grid = tf.expand_dims(tf.stack(grid, axis=-1), axis=2)    box_xy = (box_xy + tf.cast(grid, tf.float32)) /  tf.cast(grid_size, tf.float32)   box_wh = tf.exp(box_wh) * anchors    box_x1y1 = box_xy - box_wh / 2   box_x2y2 = box_xy + box_wh / 2   bbox = tf.concat([box_x1y1, box_x2y2], axis=-1)    return bbox, score, class_probs, pred_box     

Подавление не-максимумов

         def nonMaximumSuppression(outputs, anchors, masks, classes):   boxes, conf, out_type = [], [], []    for output in outputs:     boxes.append(tf.reshape(output[0], (tf.shape(output[0])[0], -1, tf.shape(output[0])[-1])))     conf.append(tf.reshape(output[1], (tf.shape(output[1])[0], -1, tf.shape(output[1])[-1])))     out_type.append(tf.reshape(output[2], (tf.shape(output[2])[0], -1, tf.shape(output[2])[-1])))    bbox = tf.concat(boxes, axis=1)   confidence = tf.concat(conf, axis=1)   class_probs = tf.concat(out_type, axis=1)    scores = confidence * class_probs      boxes, scores, classes, valid_detections = tf.image.combined_non_max_suppression(     boxes=tf.reshape(bbox, (tf.shape(bbox)[0], -1, 1, 4)),     scores=tf.reshape(         scores, (tf.shape(scores)[0], -1, tf.shape(scores)[-1])),     max_output_size_per_class=100,     max_total_size=100,     iou_threshold=yolo_iou_threshold,     score_threshold=yolo_score_threshold   )      return boxes, scores, classes, valid_detections     

Основная функция, создающая всю модель:

         def YoloV3(size=None, channels=3, anchors=yolo_anchors,           masks=yolo_anchor_masks, classes=80, training=False):   x = inputs = Input([size, size, channels])    x_36, x_61, x = Darknet(name='yolo_darknet')(x)    x = YoloConv(512, name='yolo_conv_0')(x)   output_0 = YoloOutput(512, len(masks[0]), classes, name='yolo_output_0')(x)    x = YoloConv(256, name='yolo_conv_1')((x, x_61))   output_1 = YoloOutput(256, len(masks[1]), classes, name='yolo_output_1')(x)    x = YoloConv(128, name='yolo_conv_2')((x, x_36))   output_2 = YoloOutput(128, len(masks[2]), classes, name='yolo_output_2')(x)    if training:     return Model(inputs, (output_0, output_1, output_2), name='yolov3')    boxes_0 = Lambda(lambda x: yolo_boxes(x, anchors[masks[0]], classes),                   name='yolo_boxes_0')(output_0)   boxes_1 = Lambda(lambda x: yolo_boxes(x, anchors[masks[1]], classes),                   name='yolo_boxes_1')(output_1)   boxes_2 = Lambda(lambda x: yolo_boxes(x, anchors[masks[2]], classes),                   name='yolo_boxes_2')(output_2)    outputs = Lambda(lambda x: nonMaximumSuppression(x, anchors, masks, classes),                   name='nonMaximumSuppression')((boxes_0[:3], boxes_1[:3], boxes_2[:3]))    return Model(inputs, outputs, name='yolov3')     

Функция потерь с декоратором:

         def YoloLoss(anchors, classes=80, ignore_thresh=0.5):     def yolo_loss(y_true, y_pred):         # 1. transform all pred outputs         # y_pred: (batch_size, grid, grid, anchors, (x, y, w, h, obj, ...cls))         pred_box, pred_obj, pred_class, pred_xywh = yolo_boxes(             y_pred, anchors, classes)         pred_xy = pred_xywh[..., 0:2]         pred_wh = pred_xywh[..., 2:4]          # 2. transform all true outputs         # y_true: (batch_size, grid, grid, anchors, (x1, y1, x2, y2, obj, cls))         true_box, true_obj, true_class_idx = tf.split(             y_true, (4, 1, 1), axis=-1)         true_xy = (true_box[..., 0:2] + true_box[..., 2:4]) / 2         true_wh = true_box[..., 2:4] - true_box[..., 0:2]          # give higher weights to small boxes         box_loss_scale = 2 - true_wh[..., 0] * true_wh[..., 1]          # 3. inverting the pred box equations         grid_size = tf.shape(y_true)[1]         grid = tf.meshgrid(tf.range(grid_size), tf.range(grid_size))         grid = tf.expand_dims(tf.stack(grid, axis=-1), axis=2)         true_xy = true_xy * tf.cast(grid_size, tf.float32) -              tf.cast(grid, tf.float32)         true_wh = tf.math.log(true_wh / anchors)         true_wh = tf.where(tf.math.is_inf(true_wh),                            tf.zeros_like(true_wh), true_wh)          # 4. calculate all masks         obj_mask = tf.squeeze(true_obj, -1)         # ignore false positive when iou is over threshold         true_box_flat = tf.boolean_mask(true_box, tf.cast(obj_mask, tf.bool))         best_iou = tf.reduce_max(broadcast_iou(             pred_box, true_box_flat), axis=-1)         ignore_mask = tf.cast(best_iou < ignore_thresh, tf.float32)          # 5. calculate all losses         xy_loss = obj_mask * box_loss_scale *              tf.reduce_sum(tf.square(true_xy - pred_xy), axis=-1)         wh_loss = obj_mask * box_loss_scale *              tf.reduce_sum(tf.square(true_wh - pred_wh), axis=-1)         obj_loss = binary_crossentropy(true_obj, pred_obj)         obj_loss = obj_mask * obj_loss +              (1 - obj_mask) * ignore_mask * obj_loss         # TODO: use binary_crossentropy instead         class_loss = obj_mask * sparse_categorical_crossentropy(             true_class_idx, pred_class)          # 6. sum over (batch, gridx, gridy, anchors) => (batch, 1)         xy_loss = tf.reduce_sum(xy_loss, axis=(1, 2, 3))         wh_loss = tf.reduce_sum(wh_loss, axis=(1, 2, 3))         obj_loss = tf.reduce_sum(obj_loss, axis=(1, 2, 3))         class_loss = tf.reduce_sum(class_loss, axis=(1, 2, 3))          return xy_loss + wh_loss + obj_loss + class_loss     return yolo_loss     

Следующая функция трансформирует целевые выводы к кортежу (tuple) следующей формы:

([N,13,13,3,6],[N,26,26,3,6],[N,52,52,3,6])

Здесь N – количество меток в пакете (batch), а 6 представляет [x, y, w, h, obj, class] содержащих рамок.

         @tf.function def transform_targets_for_output(y_true, grid_size, anchor_idxs, classes):    N = tf.shape(y_true)[0]    y_true_out = tf.zeros(       (N, grid_size, grid_size, tf.shape(anchor_idxs)[0], 6))    anchor_idxs = tf.cast(anchor_idxs, tf.int32)    indexes = tf.TensorArray(tf.int32, 1, dynamic_size=True)   updates = tf.TensorArray(tf.float32, 1, dynamic_size=True)   idx = 0   for i in tf.range(N):     for j in tf.range(tf.shape(y_true)[1]):       if tf.equal(y_true[i][j][2], 0):         continue       anchor_eq = tf.equal(         anchor_idxs, tf.cast(y_true[i][j][5], tf.int32))        if tf.reduce_any(anchor_eq):         box = y_true[i][j][0:4]         box_xy = (y_true[i][j][0:2] + y_true[i][j][2:4]) / 2          anchor_idx = tf.cast(tf.where(anchor_eq), tf.int32)         grid_xy = tf.cast(box_xy // (1/grid_size), tf.int32)          indexes = indexes.write(             idx, [i, grid_xy[1], grid_xy[0], anchor_idx[0][0]])         updates = updates.write(           idx, [box[0], box[1], box[2], box[3], 1, y_true[i][j][4]])         idx += 1    return tf.tensor_scatter_nd_update(     y_true_out, indexes.stack(), updates.stack())   def transform_targets(y_train, anchors, anchor_masks, classes):   outputs = []   grid_size = 13    anchors = tf.cast(anchors, tf.float32)   anchor_area = anchors[..., 0] * anchors[..., 1]   box_wh = y_train[..., 2:4] - y_train[..., 0:2]   box_wh = tf.tile(tf.expand_dims(box_wh, -2),                     (1, 1, tf.shape(anchors)[0], 1))   box_area = box_wh[..., 0] * box_wh[..., 1]   intersection = tf.minimum(box_wh[..., 0], anchors[..., 0]) *      tf.minimum(box_wh[..., 1], anchors[..., 1])   iou = intersection / (box_area + anchor_area - intersection)   anchor_idx = tf.cast(tf.argmax(iou, axis=-1), tf.float32)   anchor_idx = tf.expand_dims(anchor_idx, axis=-1)    y_train = tf.concat([y_train, anchor_idx], axis=-1)    for anchor_idxs in anchor_masks:     outputs.append(transform_targets_for_output(       y_train, grid_size, anchor_idxs, classes))     grid_size *= 2    return tuple(outputs) # [x, y, w, h, obj, class]   def preprocess_image(x_train, size):   return (tf.image.resize(x_train, (size, size))) / 255     

Теперь мы создаем экземпляр нашей модели, загружаем веса и имена классов. В датасете COCO их 80.

         yolo = YoloV3(classes=num_classes)  load_darknet_weights(yolo, weightsyolov3)  yolo.save_weights(checkpoints)  class_names =  ["person", "bicycle", "car", "motorbike", "aeroplane", "bus", "train", "truck",   "boat", "traffic light", "fire hydrant", "stop sign", "parking meter", "bench",   "bird", "cat", "dog", "horse", "sheep", "cow", "elephant", "bear", "zebra", "giraffe",   "backpack", "umbrella", "handbag", "tie", "suitcase", "frisbee", "skis", "snowboard",   "sports ball", "kite", "baseball bat", "baseball glove", "skateboard", "surfboard",   "tennis racket", "bottle", "wine glass", "cup", "fork", "knife", "spoon", "bowl",   "banana","apple", "sandwich", "orange", "broccoli", "carrot", "hot dog", "pizza", "donut",   "cake","chair", "sofa", "pottedplant", "bed", "diningtable", "toilet", "tvmonitor", "laptop",   "mouse","remote", "keyboard", "cell phone", "microwave", "oven", "toaster", "sink",   "refrigerator","book", "clock", "vase", "scissors", "teddy bear", "hair drier", "toothbrush"]     

Вот и все! Прямо сейчас мы можем запустить и протестировать нашу модель на каком-нибудь изображении.

Сохраните это изображение в файл detect.jpg
Сохраните это изображение в файл detect.jpg
         name = 'detect.jpg' img = tf.image.decode_image(open(name, 'rb').read(), channels=3) img = tf.expand_dims(img, 0) img = preprocess_image(img, size)  boxes, scores, classes, nums = yolo(img) #eager mode  img = cv2.imread(name) img = draw_outputs(img, (boxes, scores, classes, nums), class_names) cv2.imwrite('output.jpg', img)     

После выполнения этого кода в файле output.jpg окажется то же изображение с рамками, отмечающими объекты, распознанные нашей нейронной сетью:

Распознавание видео с камеры

Мы уже добились впечатляющего результата, но главное еще впереди! Самое важное в архитектуре YOLO не то, что она довольно неплохо умеет распознавать объекты, а то, что она делает это быстро. Настолько быстро, что успевает обработать все кадры, поступающие от веб-камеры. Включите веб-камеру и запустите следующий код:

         cap = cv2.VideoCapture(0)  while(True):     # Capture frame-by-frame     ret, frame = cap.read()      # Our operations on the frame come here     #img = cv2.cvtColor(frame, cv2.COLOR_BGR2RGB)     img = tf.expand_dims(frame, 0)     img = preprocess_image(img, size)     boxes, scores, classes, nums = yolo(img) #eager mode     img = draw_outputs(frame, (boxes, scores, classes, nums), class_names)      # Display the resulting frame     cv2.imshow('frame',img)     if cv2.waitKey(1) & 0xFF == ord('q'):         break  # When everything done, release the capture cap.release() cv2.destroyAllWindows()     

Вы увидите на экране изменяющуюся картинку с камеры, на которой будут отмечены все распознанные объекты. Теперь вы можете перемещать свою камеру или двигать объекты в кадре, и нейронная сеть будет успевать обрабатывать меняющиеся изображения.

Желаю удачных и забавных экспериментов с YOLO!

Ссылки:

Источники


Источник: proglib.io

Комментарии: