Увы! Время пришло!

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

Как видно, в каталоге найдено 5 классов листьев. Следовательно, мы остаемся с этой проблемой машинного обучения:

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

Прочтите эту статью до конца, чтобы узнать.

Извлечение функций

Первый вопрос, который вы можете задать: какие функции мы собираемся использовать в анализе? Чтобы машинное обучение работало, нам необходимо изучить особенности, которые являются общими для каждой категории листьев, чтобы алгоритм затем мог решить, что отличает лист от другого. Достижения в области глубокого обучения, в частности сверточных нейронных сетей (CNN), позволяют нам уже извлекать множество функций и в большинстве случаев получать высокие оценки точности. Однако, поскольку это блог об использовании методов обработки изображений, мы отложим использование CNN в анализе.

Итак, если нам не разрешено использовать CNN для извлечения признаков из листьев, то как мы вообще собираемся их получить? И это основная тема этого блога - использование обработки изображений для извлечения листовых функций для машинного обучения в Python.

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

 from skimage.io import imshow, imread
 from skimage.color import rgb2gray
 from skimage.filters import threshold_otsu
 from skimage.morphology import closing
 from skimage.measure import label, regionprops, regionprops_table
 from sklearn.ensemble import GradientBoostingClassifier,      
    RandomForestClassifier
 from sklearn.model_selection import train_test_split
 from sklearn.metrics import classification_report
 from matplotlib import pyplot as plt
 import pandas as pd
 import numpy as np
 from tqdm import tqdm
 import os

Чтобы следить за реализацией кода, вы можете скачать изображения по этой ссылке: Leaves

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

Давайте посмотрим на одно из наших изображений в оттенках серого. Запустите приведенный ниже код:

 # get the filenames of the leaves under the directory “Leaves”
 image_path_list = os.listdir("Leaves")
 # looking at the first image
 i = 0
 image_path = image_path_list[i]
 image = rgb2gray(imread("Leaves/"+image_path))
 imshow(image)

Теперь давайте приступим к извлечению объектов с помощью "свойств региона" скимэджа. Но перед этим нам нужно предварительно обработать наше изображение, мы предварительно обрабатываем изображение, преобразуя его в двоичную форму, используя метод Оцу, и очищаем его, используя морфологическую операцию закрытия. Реализация показана ниже:

 binary = image < threshold_otsu(image)
 binary = closing(binary)
 imshow(binary)

Затем мы можем пометить предварительно обработанное изображение для подготовки к извлечению признаков.

 label_img = label(binary)
 imshow(label_img)

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

table = pd.DataFrame(regionprops_table(label_img, image,
                                       ['convex_area', 'area',
                                        'eccentricity', 'extent',                   
                                        'inertia_tensor',
                                        'major_axis_length', 
                                        'minor_axis_length']))
 table['convex_ratio'] = table['area']/table['convex_area']
 table['label'] = image_path[5]
 table

Это даст нам фрейм данных с функциями, которые мы вызвали из функции regionprops_table.

Блестяще! Действительно возможно получить функции. Однако это только для одного изображения. Получим характеристики для остальных. Как это сделать? Не беспокойтесь, этот блог поможет вам. Реализация кода о том, как это сделать, представлена ​​ниже:

image_path_list = os.listdir("Leaves")
df = pd.DataFrame()
for i in range(len(image_path_list)):
   
  image_path = image_path_list[i]
  image = rgb2gray(imread("Leaves/"+image_path))
  binary = image < threshold_otsu(image)
  binary = closing(binary)
  label_img = label(binary)
  
  table = pd.DataFrame(regionprops_table(label_img, image
                          ['convex_area', 'area', 'eccentricity',
                           'extent', 'inertia_tensor',                         
                           'major_axis_length', 'minor_axis_length',
                           'perimeter', 'solidity', 'image',
                           'orientation', 'moments_central',
                           'moments_hu', 'euler_number',
                           'equivalent_diameter',
                           'mean_intensity', 'bbox']))
  table['perimeter_area_ratio'] = table['perimeter']/table['area']
  real_images = []
  std = []
  mean = []
  percent25 = []
  percent75 = []
  for prop in regionprops(label_img): 
      
      min_row, min_col, max_row, max_col = prop.bbox
      img = image[min_row:max_row,min_col:max_col]
      real_images += [img]
      mean += [np.mean(img)]
      std += [np.std(img)]
      percent25 += [np.percentile(img, 25)] 
      percent75 += [np.percentile(img, 75)]
  table['real_images'] = real_images
  table['mean_intensity'] = mean
  table['std_intensity'] = std
  table['25th Percentile'] = mean
  table['75th Percentile'] = std
  table['iqr'] = table['75th Percentile'] - table['25th Percentile']
  table['label'] = image_path[5]
  df = pd.concat([df, table], axis=0)
df.head()

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

Мы рассчитали 4 характеристики на основе свойств региона.

1 . inertia_tensor - это кортеж, представляющий тензор инерции. Это относится к вращению сегмента вокруг своей массы.

2. minor_axis_length - длина вспомогательной оси или более короткой оси сегмента.

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

4. эксцентриситет - эксцентриситет - это отношение фокусного расстояния (расстояния между фокусными точками) к длине главной оси.

Мы также извлекли эти свойства из сегмента необработанного изображения в градациях серого. Все приведенные ниже функции - это просто статистика значений оттенков серого. IQR - это просто разница 25-го и 75-го процентилей.

  • 25-й процентиль
  • 75-й процентиль
  • mean_intensity
  • std_intensity
  • IQR

Реализация машинного обучения

Круто Приступим к реализации машинного обучения.

X = df.drop(columns=['label', 'image', 'real_images'])
#features
X = X[['iqr','75th Percentile','inertia_tensor-1-1',
       'std_intensity','mean_intensity','25th Percentile',
       'minor_axis_length', 'solidity', 'eccentricity']]
#target
y = df['label']
columns = X.columns
#train-test-split
X_train, X_test, y_train, y_test = train_test_split(
X, y, test_size=0.25, random_state=123, stratify=y)

Выберите алгоритм классификатора. Для нашей команды наиболее эффективным оказался классификатор повышения градиента. Реализация следующая:

clf = GradientBoostingClassifier(n_estimators=50, max_depth=3, random_state=123)
clf.fit(X_train, y_train)
#print confusion matrix of test set
print(classification_report(clf.predict(X_test), y_test))
#print accuracy score of the test set
print(f"Test Accuracy: {np.mean(clf.predict(X_test) ==            
                             y_test)*100:.2f}%")

Выполнение описанного выше алгоритма дает следующие результаты:

Выводы

Самым эффективным алгоритмом был GBM, так как он имеет лучшую точность тестирования, чем все классификаторы, с точностью тестирования около 87% по сравнению со вторым лучшим из 82% от Random Forest. Мы достигли довольно высокой точности, учитывая, что PCC всего 20%. Кроме того, ограниченное количество выборок позволяет легко переобучать обучающий набор с помощью классификаторов. Эту проблему можно решить, увеличив набор данных для большей обобщаемости.

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