Классификация изображений с использованием предварительно обученной модели

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

Открыть новый Juypter notebook и следуйте инструкциям по разработке этого приложения для классификации изображений.

Импорт библиотек

Сначала мы импортируем необходимые пакеты, используя приведенный ниже код -

from caffe2.proto import caffe2_pb2
from caffe2.python import core, workspace, models
import numpy as np
import skimage.io
import skimage.transform
from matplotlib import pyplot
import os
import urllib.request as urllib2
import operator

Далее мы настроили несколько variables -

INPUT_IMAGE_SIZE = 227
mean = 128

Очевидно, что изображения, используемые для обучения, будут разных размеров. Все эти изображения необходимо преобразовать в фиксированный размер для точного обучения. Точно так же тестовые изображения и изображение, которое вы хотите прогнозировать в производственной среде, также должны быть преобразованы в размер, такой же, как тот, который использовался во время обучения. Таким образом, мы создаем переменную выше, названнуюINPUT_IMAGE_SIZE имеющий ценность 227. Следовательно, мы конвертируем все наши изображения в размер227x227 перед использованием в нашем классификаторе.

Мы также объявляем переменную с именем mean имеющий ценность 128, который позже используется для улучшения результатов классификации.

Далее мы разработаем две функции для обработки изображения.

Обработка изображения

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

Изменение размера изображения

Сначала мы напишем функцию для изменения размера изображения. Как было сказано ранее, мы изменим размер изображения на227x227. Итак, давайте определим функциюresize следующим образом -

def resize(img, input_height, input_width):

Соотношение сторон изображения получается делением ширины на высоту.

original_aspect = img.shape[1]/float(img.shape[0])

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

if(original_aspect>1):
   new_height = int(original_aspect * input_height)
   return skimage.transform.resize(img, (input_width,
   new_height), mode='constant', anti_aliasing=True, anti_aliasing_sigma=None)

Если соотношение сторон less than 1, это указывает portrait mode. Теперь мы настраиваем ширину, используя следующий код -

if(original_aspect<1):
   new_width = int(input_width/original_aspect)
   return skimage.transform.resize(img, (new_width,
   input_height), mode='constant', anti_aliasing=True, anti_aliasing_sigma=None)

Если соотношение сторон равно 1, мы не делаем никаких корректировок по высоте / ширине.

if(original_aspect == 1):
   return skimage.transform.resize(img, (input_width,
   input_height), mode='constant', anti_aliasing=True, anti_aliasing_sigma=None)

Полный код функции приведен ниже для вашего быстрого ознакомления -

def resize(img, input_height, input_width):
   original_aspect = img.shape[1]/float(img.shape[0])
   if(original_aspect>1):
      new_height = int(original_aspect * input_height)
      return skimage.transform.resize(img, (input_width,
	   new_height), mode='constant', anti_aliasing=True, anti_aliasing_sigma=None)
   if(original_aspect<1):
         new_width = int(input_width/original_aspect)
         return skimage.transform.resize(img, (new_width,
         input_height), mode='constant', anti_aliasing=True, anti_aliasing_sigma=None)
   if(original_aspect == 1):
         return skimage.transform.resize(img, (input_width,
         input_height), mode='constant', anti_aliasing=True, anti_aliasing_sigma=None)

Теперь напишем функцию обрезки изображения вокруг его центра.

Обрезка изображения

Мы объявляем crop_image функционируют следующим образом -

def crop_image(img,cropx,cropy):

Мы извлекаем размеры изображения, используя следующее утверждение -

y,x,c = img.shape

Мы создаем новую отправную точку для изображения, используя следующие две строки кода:

startx = x//2-(cropx//2)
starty = y//2-(cropy//2)

Наконец, мы возвращаем обрезанное изображение, создавая объект изображения с новыми размерами -

return img[starty:starty+cropy,startx:startx+cropx]

Полный код функции приведен ниже для вашего быстрого ознакомления -

def crop_image(img,cropx,cropy):
   y,x,c = img.shape
   startx = x//2-(cropx//2)
   starty = y//2-(cropy//2)
   return img[starty:starty+cropy,startx:startx+cropx]

Теперь мы напишем код для тестирования этих функций.

Обработка изображения

Сначала скопируйте файл изображения в images вложенная папка в каталоге вашего проекта. tree.jpgфайл копируется в проект. Следующий код Python загружает изображение и отображает его на консоли:

img = skimage.img_as_float(skimage.io.imread("images/tree.jpg")).astype(np.float32)
print("Original Image Shape: " , img.shape)
pyplot.figure()
pyplot.imshow(img)
pyplot.title('Original image')

Результат выглядит следующим образом -

Обратите внимание, что размер исходного изображения 600 x 960. Нам нужно изменить его размер в соответствии с нашей спецификацией227 x 227. Вызов нашего ранее определенногоresizeфункция выполняет эту работу.

img = resize(img, INPUT_IMAGE_SIZE, INPUT_IMAGE_SIZE)
print("Image Shape after resizing: " , img.shape)
pyplot.figure()
pyplot.imshow(img)
pyplot.title('Resized image')

Результат приведен ниже -

Обратите внимание, что теперь размер изображения 227 x 363. Нам нужно обрезать это до227 x 227для окончательной подачи нашего алгоритма. Для этого мы вызываем ранее определенную функцию кадрирования.

img = crop_image(img, INPUT_IMAGE_SIZE, INPUT_IMAGE_SIZE)
print("Image Shape after cropping: " , img.shape)
pyplot.figure()
pyplot.imshow(img)
pyplot.title('Center Cropped')

Ниже приведен вывод кода -

На данный момент изображение имеет размер 227 x 227и готов к дальнейшей обработке. Теперь мы меняем местами оси изображения, чтобы выделить три цвета в три разные зоны.

img = img.swapaxes(1, 2).swapaxes(0, 1)
print("CHW Image Shape: " , img.shape)

Ниже приведен результат -

CHW Image Shape: (3, 227, 227)

Обратите внимание, что последняя ось теперь стала первым измерением в массиве. Теперь мы построим три канала, используя следующий код -

pyplot.figure()
for i in range(3):
   pyplot.subplot(1, 3, i+1)
   pyplot.imshow(img[i])
   pyplot.axis('off')
   pyplot.title('RGB channel %d' % (i+1))

Результат указан ниже -

Наконец, мы делаем некоторую дополнительную обработку изображения, такую ​​как преобразование Red Green Blue к Blue Green Red (RGB to BGR), удалив среднее значение для лучших результатов и добавив ось размера партии, используя следующие три строки кода:

# convert RGB --> BGR
img = img[(2, 1, 0), :, :]
# remove mean
img = img * 255 - mean
# add batch size axis
img = img[np.newaxis, :, :, :].astype(np.float32)

На данный момент ваше изображение находится в NCHW formatи готов к использованию в нашей сети. Затем мы загрузим файлы наших предварительно обученных моделей и загрузим в них приведенное выше изображение для прогнозирования.

Прогнозирование объектов в обработанном изображении

Сначала мы настраиваем пути для init а также predict сети, определенные в предварительно обученных моделях Caffe.

Установка путей к файлам модели

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

CAFFE_MODELS = os.path.expanduser("/anaconda3/lib/python3.7/site-packages/caffe2/python/models")

Мы проложили путь к init_net protobuf файл squeezenet модель следующим образом -

INIT_NET = os.path.join(CAFFE_MODELS, 'squeezenet', 'init_net.pb')

Таким же образом мы настраиваем путь к predict_net protobuf следующим образом -

PREDICT_NET = os.path.join(CAFFE_MODELS, 'squeezenet', 'predict_net.pb')

Мы печатаем два пути для диагностики -

print(INIT_NET)
print(PREDICT_NET)

Приведенный выше код вместе с выводом приведен здесь для вашего быстрого ознакомления -

CAFFE_MODELS = os.path.expanduser("/anaconda3/lib/python3.7/site-packages/caffe2/python/models")
INIT_NET = os.path.join(CAFFE_MODELS, 'squeezenet', 'init_net.pb')
PREDICT_NET = os.path.join(CAFFE_MODELS, 'squeezenet', 'predict_net.pb')
print(INIT_NET)
print(PREDICT_NET)

Вывод упомянут ниже -

/anaconda3/lib/python3.7/site-packages/caffe2/python/models/squeezenet/init_net.pb
/anaconda3/lib/python3.7/site-packages/caffe2/python/models/squeezenet/predict_net.pb

Далее мы создадим предсказатель.

Создание предиктора

Мы читаем файлы модели, используя следующие два оператора:

with open(INIT_NET, "rb") as f:
   init_net = f.read()
with open(PREDICT_NET, "rb") as f:
   predict_net = f.read()

Предиктор создается путем передачи указателей на два файла в качестве параметров в Predictor функция.

p = workspace.Predictor(init_net, predict_net)

В pобъект - это предсказатель, который используется для предсказания объектов в любом данном изображении. Обратите внимание, что каждое входное изображение должно быть в формате NCHW, как то, что мы сделали ранее с нашимtree.jpg файл.

Предсказание объектов

Предсказать объекты на данном изображении тривиально - достаточно выполнить единственную строку команды. Мы называемrun метод на predictor объект для обнаружения объекта на данном изображении.

results = p.run({'data': img})

Результаты прогнозов теперь доступны в results объект, который мы конвертируем в массив для удобства чтения.

results = np.asarray(results)

Распечатайте размеры массива для вашего понимания, используя следующий оператор -

print("results shape: ", results.shape)

Результат показан ниже -

results shape: (1, 1, 1000, 1, 1)

Теперь удалим ненужную ось -

preds = np.squeeze(results)

Теперь можно получить верхнее предикатное значение, взяв max ценность в preds массив.

curr_pred, curr_conf = max(enumerate(preds), key=operator.itemgetter(1))
print("Prediction: ", curr_pred)
print("Confidence: ", curr_conf)

Результат выглядит следующим образом -

Prediction: 984
Confidence: 0.89235985

Как видите, модель предсказала объект со значением индекса 984 с участием 89%уверенность. Индекс 984 не имеет большого смысла для понимания того, какой объект обнаружен. Нам нужно получить строковое имя для объекта, используя его значение индекса. Типы объектов, которые распознает модель, и соответствующие им значения индекса доступны в репозитории github.

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

Стрингирующий результат

Мы создаем объект URL для репозитория github следующим образом:

codes = "https://gist.githubusercontent.com/aaronmarkham/cd3a6b6ac0
71eca6f7b4a6e40e6038aa/raw/9edb4038a37da6b5a44c3b5bc52e448ff09bfe5b/alexnet_codes"

Читаем содержимое URL -

response = urllib2.urlopen(codes)

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

5: 'electric ray, crampfish, numbfish, torpedo',
6: 'stingray',
7: 'cock',
8: 'hen',
9: 'ostrich, Struthio camelus',
10: 'brambling, Fringilla montifringilla',

Теперь мы перебираем весь массив, чтобы найти желаемый код 984, используя for цикл следующим образом -

for line in response:
   mystring = line.decode('ascii')
   code, result = mystring.partition(":")[::2]
   code = code.strip()
   result = result.replace("'", "")
   if (code == str(curr_pred)):
      name = result.split(",")[0][1:]
      print("Model predicts", name, "with", curr_conf, "confidence")

Когда вы запустите код, вы увидите следующий вывод -

Model predicts rapeseed with 0.89235985 confidence

Теперь вы можете попробовать модель на другом изображении.

Предсказание другого изображения

Чтобы предсказать другое изображение, просто скопируйте файл изображения в imagesпапка в каталоге вашего проекта. Это каталог, в котором былиtree.jpgфайл сохраняется. Измените имя файла изображения в коде. Требуется только одно изменение, как показано ниже

img = skimage.img_as_float(skimage.io.imread("images/pretzel.jpg")).astype(np.float32)

Исходное изображение и результат прогноза показаны ниже -

Вывод упомянут ниже -

Model predicts pretzel with 0.99999976 confidence

Как видите, предварительно обученная модель способна обнаруживать объекты на заданном изображении с большой точностью.

Полный исходный код

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

def crop_image(img,cropx,cropy):
   y,x,c = img.shape
   startx = x//2-(cropx//2)
   starty = y//2-(cropy//2)
   return img[starty:starty+cropy,startx:startx+cropx]
img = skimage.img_as_float(skimage.io.imread("images/pretzel.jpg")).astype(np.float32)
print("Original Image Shape: " , img.shape)
pyplot.figure()
pyplot.imshow(img)
pyplot.title('Original image')
img = resize(img, INPUT_IMAGE_SIZE, INPUT_IMAGE_SIZE)
print("Image Shape after resizing: " , img.shape)
pyplot.figure()
pyplot.imshow(img)
pyplot.title('Resized image')
img = crop_image(img, INPUT_IMAGE_SIZE, INPUT_IMAGE_SIZE)
print("Image Shape after cropping: " , img.shape)
pyplot.figure()
pyplot.imshow(img)
pyplot.title('Center Cropped')
img = img.swapaxes(1, 2).swapaxes(0, 1)
print("CHW Image Shape: " , img.shape)
pyplot.figure()
for i in range(3):
pyplot.subplot(1, 3, i+1)
pyplot.imshow(img[i])
pyplot.axis('off')
pyplot.title('RGB channel %d' % (i+1))
# convert RGB --> BGR
img = img[(2, 1, 0), :, :]
# remove mean
img = img * 255 - mean
# add batch size axis
img = img[np.newaxis, :, :, :].astype(np.float32)
CAFFE_MODELS = os.path.expanduser("/anaconda3/lib/python3.7/site-packages/caffe2/python/models")
INIT_NET = os.path.join(CAFFE_MODELS, 'squeezenet', 'init_net.pb')
PREDICT_NET = os.path.join(CAFFE_MODELS, 'squeezenet', 'predict_net.pb')
print(INIT_NET)
print(PREDICT_NET)
with open(INIT_NET, "rb") as f:
   init_net = f.read()
with open(PREDICT_NET, "rb") as f:
   predict_net = f.read()
p = workspace.Predictor(init_net, predict_net)
results = p.run({'data': img})
results = np.asarray(results)
print("results shape: ", results.shape)
preds = np.squeeze(results)
curr_pred, curr_conf = max(enumerate(preds), key=operator.itemgetter(1))
print("Prediction: ", curr_pred)
print("Confidence: ", curr_conf)
codes = "https://gist.githubusercontent.com/aaronmarkham/cd3a6b6ac071eca6f7b4a6e40e6038aa/raw/9edb4038a37da6b5a44c3b5bc52e448ff09bfe5b/alexnet_codes"
response = urllib2.urlopen(codes)
for line in response:
   mystring = line.decode('ascii')
   code, result = mystring.partition(":")[::2]
   code = code.strip()
   result = result.replace("'", "")
   if (code == str(curr_pred)):
      name = result.split(",")[0][1:]
      print("Model predicts", name, "with", curr_conf, "confidence")

К этому времени вы знаете, как использовать предварительно обученную модель для прогнозирования вашего набора данных.

Что дальше - узнать, как определить свой neural network (NN) архитектуры в Caffe2и обучите их на своем наборе данных. Теперь мы узнаем, как создать простую однослойную NN.