В этом посте мы рассмотрим:

  • Увеличение изображения: что это такое? Почему это важно?
  • Керас: Как использовать его для базового увеличения изображения.
  • Выравнивание гистограммы: что это такое? Чем это полезно?
  • Реализация методов выравнивания гистограммы: один из способов изменить файл keras.preprocessing image.py.

Увеличение изображения: что это такое? Почему это важно?

Глубокие нейронные сети, особенно сверточные нейронные сети (CNN), особенно хорошо справляются с задачами классификации изображений. Было даже показано, что современные CNN превосходят человеческие возможности в распознавании изображений.

Однако, как мы узнали из приложения г-на Цзянь-Яна для распознавания еды Хот-дог, а не хот-дог в популярном телешоу Кремниевая долина, (приложение теперь доступно в магазине приложений) Сбор изображений в качестве обучающих данных может быть дорогостоящим и трудоемким.

Если вы не знакомы с телешоу "Кремниевая долина", имейте в виду, что в следующем ролике используется язык NSFW:

Чтобы избежать высоких затрат на сбор тысяч обучающих изображений, было разработано увеличение изображений для генерации обучающих данных из существующего набора данных. Увеличение изображения - это процесс получения изображений, которые уже есть в наборе обучающих данных, и манипулирования ими для создания множества измененных версий одного и того же изображения. Это дает больше изображений для обучения, но также может помочь раскрыть наш классификатор более широкому спектру ситуаций освещения и окраски, чтобы сделать наш классификатор более надежным. Вот несколько примеров различных дополнений из библиотеки imgaug.

Использование Keras для базового увеличения изображения

Есть много способов предварительной обработки изображений. В этом посте мы рассмотрим некоторые из наиболее распространенных готовых методов, которые библиотека глубокого обучения keras предоставляет для увеличения изображений, а затем мы покажем, как изменить файл keras.preprocessing image.py. для включения методов выравнивания гистограммы. Мы будем использовать набор данных cifar10, который поставляется с keras. Однако мы будем использовать только изображения кошек и собак из набора данных, чтобы задача оставалась достаточно маленькой, чтобы ее можно было выполнять на процессоре - на случай, если вы захотите продолжить. Вы можете просмотреть записную книжку исходного кода IPython в этом посте.

Загрузка и форматирование данных

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

from __future__ import print_function
import keras
from keras.datasets import cifar10
from keras import backend as K
import matplotlib
from matplotlib import pyplot as plt
import numpy as np
#Input image dimensions
img_rows, img_cols = 32, 32
#The data, shuffled and split between train and test sets
(x_train, y_train), (x_test, y_test) = cifar10.load_data()
#Only look at cats [=3] and dogs [=5]
train_picks = np.ravel(np.logical_or(y_train==3,y_train==5))
test_picks = np.ravel(np.logical_or(y_test==3,y_test==5))
y_train = np.array(y_train[train_picks]==5,dtype=int)
y_test = np.array(y_test[test_picks]==5,dtype=int)
x_train = x_train[train_picks]
x_test = x_test[test_picks]
if K.image_data_format() == 'channels_first':
x_train = x_train.reshape(x_train.shape[0], 3, img_rows, img_cols)
x_test = x_test.reshape(x_test.shape[0], 3, img_rows, img_cols)
input_shape = (3, img_rows, img_cols)
else:
x_train = x_train.reshape(x_train.shape[0], img_rows, img_cols, 3)
x_test = x_test.reshape(x_test.shape[0], img_rows, img_cols, 3)
input_shape = (img_rows, img_cols, 3)
x_train = x_train.astype('float32')
x_test = x_test.astype('float32')
x_train /= 255
x_test /= 255
print('x_train shape:', x_train.shape)
print(x_train.shape[0], 'train samples')
print(x_test.shape[0], 'test samples')
#Convert class vectors to binary class matrices
y_train = keras.utils.to_categorical(np.ravel(y_train), num_classes)
y_test = keras.utils.to_categorical(np.ravel(y_test), num_classes)
#Look at the first 9 images from the dataset
images = range(0,9)
for i in images:
plt.subplot(330 + 1 + i)
plt.imshow(x_train[i], cmap=pyplot.get_cmap('gray'))
#Show the plot
plt.show()

Изображения cifar10 имеют размер всего 32 x 32 пикселя, поэтому при увеличении они выглядят зернистыми, но CNN не знает, что они зернистые, все, что они видят, - это ДАННЫЕ.

Создайте генератор изображений из ImageDataGenerator ()

Дополнить данные изображения с помощью keras очень просто. Привет Джейсону Браунли, который предоставил отличный учебник по этому поводу. Сначала нам нужно создать генератор изображений, вызвав функцию ImageDataGenerator() и передать ей список параметров, описывающих изменения, которые мы хотим выполнить с изображениями. Затем мы вызовем функцию fit() в нашем генераторе изображений, которая будет применять изменения к изображениям пакет за пакетом. По умолчанию модификации применяются случайным образом, поэтому не каждое изображение будет изменяться каждый раз. Вы также можете использовать keras.preprocessing для экспорта файлов расширенных изображений в папку, чтобы создать гигантский набор данных измененных изображений, если вы захотите это сделать.

Здесь мы рассмотрим некоторые из наиболее интересных визуальных дополнений. Описание всех возможных ImageDataGenerator() параметров, а также список других методов, доступных в keras.preprocessing, можно увидеть в документации keras.

Произвольный поворот изображений

# Rotate images by 90 degrees
datagen = ImageDataGenerator(rotation_range=90)
# fit parameters from data
datagen.fit(x_train)
# Configure batch size and retrieve one batch of images
for X_batch, y_batch in datagen.flow(x_train, y_train, batch_size=9):
# Show 9 images
for i in range(0, 9):
pyplot.subplot(330 + 1 + i)
pyplot.imshow(X_batch[i].reshape(img_rows, img_cols, 3))
# show the plot
pyplot.show()
break
view raw rotate.py hosted with ❤ by GitHub

Переворачивать изображения по вертикали

# Flip images vertically
datagen = ImageDataGenerator(vertical_flip=True)
# fit parameters from data
datagen.fit(x_train)
# Configure batch size and retrieve one batch of images
for X_batch, y_batch in datagen.flow(x_train, y_train, batch_size=9):
# Show 9 images
for i in range(0, 9):
pyplot.subplot(330 + 1 + i)
pyplot.imshow(X_batch[i].reshape(img_rows, img_cols, 3))
# show the plot
pyplot.show()
break
view raw flip_vert.py hosted with ❤ by GitHub

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

Сдвиг изображения по вертикали или горизонтали на 20%

# Shift images vertically or horizontally
# Fill missing pixels with the color of the nearest pixel
datagen = ImageDataGenerator(width_shift_range=.2,
height_shift_range=.2,
fill_mode='nearest')
# fit parameters from data
datagen.fit(x_train)
# Configure batch size and retrieve one batch of images
for X_batch, y_batch in datagen.flow(x_train, y_train, batch_size=9):
# Show 9 images
for i in range(0, 9):
pyplot.subplot(330 + 1 + i)
pyplot.imshow(X_batch[i].reshape(img_rows, img_cols, 3))
# show the plot
pyplot.show()
break
view raw shift_vert.py hosted with ❤ by GitHub

Методы выравнивания гистограммы

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

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

Выравнивание гистограммы

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

Контрастная растяжка

Растягивание контраста использует подход к анализу распределения плотности пикселей в изображении, а затем изменяет масштаб изображения, чтобы включить в него все интенсивности, которые попадают в 2-й и 98-й процентили.

Адаптивная коррекция

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

Следующий код взят из документации библиотеки изображений sci-kit и был изменен для выполнения трех вышеуказанных дополнений к первому изображению нашего набора данных cifar10. Сначала мы импортируем необходимые модули из библиотеки изображений sci-kit (skimage), а затем изменим код из документации изображений sci-kit, чтобы просмотреть дополнения к первому изображению нашего набора данных.

# Import skimage modules
from skimage import data, img_as_float
from skimage import exposure
# Lets try augmenting a cifar10 image using these techniques
from skimage import data, img_as_float
from skimage import exposure
# Load an example image from cifar10 dataset
img = images[0]
# Set font size for images
matplotlib.rcParams['font.size'] = 8
# Contrast stretching
p2, p98 = np.percentile(img, (2, 98))
img_rescale = exposure.rescale_intensity(img, in_range=(p2, p98))
# Histogram Equalization
img_eq = exposure.equalize_hist(img)
# Adaptive Equalization
img_adapteq = exposure.equalize_adapthist(img, clip_limit=0.03)
#### Everything below here is just to create the plot/graphs ####
# Display results
fig = plt.figure(figsize=(8, 5))
axes = np.zeros((2, 4), dtype=np.object)
axes[0, 0] = fig.add_subplot(2, 4, 1)
for i in range(1, 4):
axes[0, i] = fig.add_subplot(2, 4, 1+i, sharex=axes[0,0], sharey=axes[0,0])
for i in range(0, 4):
axes[1, i] = fig.add_subplot(2, 4, 5+i)
ax_img, ax_hist, ax_cdf = plot_img_and_hist(img, axes[:, 0])
ax_img.set_title('Low contrast image')
y_min, y_max = ax_hist.get_ylim()
ax_hist.set_ylabel('Number of pixels')
ax_hist.set_yticks(np.linspace(0, y_max, 5))
ax_img, ax_hist, ax_cdf = plot_img_and_hist(img_rescale, axes[:, 1])
ax_img.set_title('Contrast stretching')
ax_img, ax_hist, ax_cdf = plot_img_and_hist(img_eq, axes[:, 2])
ax_img.set_title('Histogram equalization')
ax_img, ax_hist, ax_cdf = plot_img_and_hist(img_adapteq, axes[:, 3])
ax_img.set_title('Adaptive equalization')
ax_cdf.set_ylabel('Fraction of total intensity')
ax_cdf.set_yticks(np.linspace(0, 1, 5))
# prevent overlap of y-axis labels
fig.tight_layout()
plt.show()
view raw equalization.py hosted with ❤ by GitHub

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

Изменение keras.preprocessing для включения методов выравнивания гистограммы.

Теперь, когда мы успешно изменили одно изображение из набора данных cifar10, мы продемонстрируем, как изменить файл keras.preprocessing image.py, чтобы выполнить эти различные методы модификации гистограммы, точно так же, как мы делали нестандартные дополнения keras. используя ImageDataGenerator(). Вот общие шаги, которые мы предпримем для реализации этой функции:

Обзор

  • Найдите файл keras.preprocessing image.py на своем компьютере.
  • Скопируйте файл image.py в свой файл или записную книжку.
  • Добавьте один атрибут для каждого метода выравнивания в функцию инициализации DataImageGenerator ().
  • Добавьте предложения оператора IF в метод random_transform, чтобы дополнения реализовывались, когда мы вызываем datagen.fit().

Один из самых простых способов внести изменения в image.py файл keras.preprocessing - просто скопировать и вставить его содержимое в наш код. Это избавит от необходимости импортировать его. Вы можете просмотреть содержимое image.py файла на github здесь. Однако, чтобы быть уверенным, что вы получаете ту же версию файла, которую вы импортировали ранее, лучше взять image.py файл, который уже находится на вашем компьютере. Запуск print(keras.__file__) распечатает путь к библиотеке keras, которая находится на вашем компьютере. Путь (для пользователей Mac) может выглядеть примерно так:

/usr/local/lib/python3.5/dist-packages/keras/__init__.pyc

Это дает нам путь к keras на нашей локальной машине. Идите вперед и перейдите туда, а затем в папку preprocessing. Внутри preprocessing вы увидите файл image.py. Затем вы можете скопировать его содержимое в свой код. Файл длинный, но для начинающих это, вероятно, один из самых простых способов внести в него изменения.

Редактирование image.py

В верхней части image.py вы можете закомментировать строку: from ..import backend as K, если вы уже включили ее выше.

На этом этапе также дважды проверьте, что вы импортируете необходимые модули scikit-image, чтобы скопированный image.py мог их увидеть.

from skimage import data, img_as_float
from skimage import exposure

Теперь нам нужно добавить шесть строк в __init__ method класса ImageDataGenerator, чтобы у него были три свойства, которые представляют типы дополнений, которые мы собираемся добавить. Код ниже скопирован из моего текущего image.py. Строки с ##### сбоку - это строки, которые я добавил.

def __init__(self,
contrast_stretching=False, #####
histogram_equalization=False, #####
adaptive_equalization=False, #####
featurewise_center=False,
samplewise_center=False,
featurewise_std_normalization=False,
samplewise_std_normalization=False,
zca_whitening=False,
rotation_range=0.,
width_shift_range=0.,
height_shift_range=0.,
shear_range=0.,
zoom_range=0.,
channel_shift_range=0.,
fill_mode=’nearest’,
cval=0.,
horizontal_flip=False,
vertical_flip=False,
rescale=None,
preprocessing_function=None,
data_format=None):
if data_format is None:
data_format = K.image_data_format()
self.counter = 0
self.contrast_stretching = contrast_stretching #####
self.adaptive_equalization = adaptive_equalization #####
self.histogram_equalization = histogram_equalization #####
self.featurewise_center = featurewise_center
self.samplewise_center = samplewise_center
self.featurewise_std_normalization = featurewise_std_normalization
self.samplewise_std_normalization = samplewise_std_normalization
self.zca_whitening = zca_whitening
self.rotation_range = rotation_range
self.width_shift_range = width_shift_range
self.height_shift_range = height_shift_range
self.shear_range = shear_range
self.zoom_range = zoom_range
self.channel_shift_range = channel_shift_range
self.fill_mode = fill_mode
self.cval = cval
self.horizontal_flip = horizontal_flip
self.vertical_flip = vertical_flip
self.rescale = rescale
self.preprocessing_function = preprocessing_function

Функция random_transform() (ниже) отвечает на аргументы, которые мы передали в функцию ImageDataGenerator(). Если мы установили для параметров contrast_stretching, adaptive_equalization или histogram_equalization значение True, когда мы вызываем ImageDataGenerator(), (точно так же, как и для других увеличений изображений) random_transform() затем применит желаемое увеличение изображения.

def random_transform(self, x):
img_row_axis = self.row_axis - 1
img_col_axis = self.col_axis - 1
img_channel_axis = self.channel_axis - 1
# use composition of homographies
# to generate final transform that needs to be applied
if self.rotation_range:
theta = np.pi / 180 * np.random.uniform(-self.rotation_range, self.rotation_range)
else:
theta = 0
if self.height_shift_range:
tx = np.random.uniform(-self.height_shift_range, self.height_shift_range) * x.shape[img_row_axis]
else:
tx = 0
if self.width_shift_range:
ty = np.random.uniform(-self.width_shift_range, self.width_shift_range) * x.shape[img_col_axis]
else:
ty = 0
if self.shear_range:
shear = np.random.uniform(-self.shear_range, self.shear_range)
else:
shear = 0
if self.zoom_range[0] == 1 and self.zoom_range[1] == 1:
zx, zy = 1, 1
else:
zx, zy = np.random.uniform(self.zoom_range[0], self.zoom_range[1], 2)
transform_matrix = None
if theta != 0:
rotation_matrix = np.array([[np.cos(theta), -np.sin(theta), 0],
[np.sin(theta), np.cos(theta), 0],
[0, 0, 1]])
transform_matrix = rotation_matrix
if tx != 0 or ty != 0:
shift_matrix = np.array([[1, 0, tx],
[0, 1, ty],
[0, 0, 1]])
transform_matrix = shift_matrix if transform_matrix is None else np.dot(transform_matrix, shift_matrix)
if shear != 0:
shear_matrix = np.array([[1, -np.sin(shear), 0],
[0, np.cos(shear), 0],
[0, 0, 1]])
transform_matrix = shear_matrix if transform_matrix is None else np.dot(transform_matrix, shear_matrix)
if zx != 1 or zy != 1:
zoom_matrix = np.array([[zx, 0, 0],
[0, zy, 0],
[0, 0, 1]])
transform_matrix = zoom_matrix if transform_matrix is None else np.dot(transform_matrix, zoom_matrix)
if transform_matrix is not None:
h, w = x.shape[img_row_axis], x.shape[img_col_axis]
transform_matrix = transform_matrix_offset_center(transform_matrix, h, w)
x = apply_transform(x, transform_matrix, img_channel_axis,
fill_mode=self.fill_mode, cval=self.cval)
if self.channel_shift_range != 0:
x = random_channel_shift(x, self.channel_shift_range, img_channel_axis)
if self.horizontal_flip:
if np.random.random() < 0.5:
x = flip_axis(x, img_col_axis)
if self.vertical_flip:
if np.random.random() < 0.5:
x = flip_axis(x, img_row_axis)
if self.contrast_stretching: #####
if np.random.random() < 0.5: #####
p2, p98 = np.percentile(x, (2, 98)) #####
x = exposure.rescale_intensity(x, in_range=(p2, p98)) #####
if self.adaptive_equalization: #####
if np.random.random() < 0.5: #####
x = exposure.equalize_adapthist(x, clip_limit=0.03) #####
if self.histogram_equalization: #####
if np.random.random() < 0.5: #####
x = exposure.equalize_hist(x) #####
return x

Теперь у нас есть весь необходимый код, и мы можем вызвать ImageDataGenerator () для выполнения наших методов модификации гистограммы. Вот как выглядят несколько изображений, если мы установим для всех трех значений True.

# Initialize Generator
datagen = ImageDataGenerator(contrast_stretching=True, adaptive_equalization=True, histogram_equalization=True)
# fit parameters from data
datagen.fit(x_train)
# Configure batch size and retrieve one batch of images
for x_batch, y_batch in datagen.flow(x_train, y_train, batch_size=9):
# Show the first 9 images
for i in range(0, 9):
pyplot.subplot(330 + 1 + i)
pyplot.imshow(x_batch[i].reshape(img_rows, img_cols, 3))
# show the plot
pyplot.show()
break
view raw initialize.py hosted with ❤ by GitHub

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

Обучите и подтвердите свой Keras CNN

Последний шаг - обучить нашу CNN и проверить модель с помощью model.fit_generator(), чтобы обучить и проверить нашу нейронную сеть на расширенных изображениях.

from keras.models import Sequential
from keras.layers import Dense, Dropout, Flatten
from keras.layers import Conv2D, MaxPooling2D
batch_size = 64
num_classes = 2
epochs = 10
model = Sequential()
model.add(Conv2D(4, kernel_size=(3, 3),activation='relu',input_shape=input_shape))
model.add(Conv2D(8, (3, 3), activation='relu'))
model.add(MaxPooling2D(pool_size=(2, 2)))
model.add(Dropout(0.25))
model.add(Flatten())
model.add(Dense(16, activation='relu'))
model.add(Dropout(0.5))
model.add(Dense(2, activation='softmax'))
model.compile(loss=keras.losses.categorical_crossentropy,
optimizer=keras.optimizers.Adadelta(),
metrics=['accuracy'])
datagen.fit(x_train)
history = model.fit_generator(datagen.flow(x_train, y_train, batch_size=batch_size),
steps_per_epoch=x_train.shape[0] // batch_size,
epochs=20,
validation_data=(x_test, y_test))