Целью данного проекта является:

  • Создайте помеченный набор данных изображений Мстителей - Капитан Америка, Железный Человек, Черная Вдова, Халк, Тор.
  • Обучите CNN, способную классифицировать невидимое изображение с разумной точностью.

По сути, существует 4 основных этапа обработки данных в классификаторе изображений. Эти:

  1. Сбор данных
  2. Предварительная обработка данных
  3. Извлечение функций
  4. Модельное обучение

Теперь давайте рассмотрим пошаговую реализацию и различные доступные методы. Будет выбран наиболее подходящий метод.

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

Шаг: 1 - Сбор данных

В этом проекте нам нужно собирать данные в виде изображений. Изображения могут быть получены путем ручного удаления отдельных (статических и / или динамических) веб-сайтов. Но поскольку требуется большое количество изображений, многие веб-сайты придется отказаться. Вместо того, чтобы вручную переходить на разные веб-сайты, можно выбросить изображения Google или Bing.

Способов удаления изображений может быть много. Три из них широко используются:

  1. Использование инструментов python и веб-скраппинга для автоматизации загрузки определенных типов изображений напрямую из Google. Этот метод является совершенно незаконным, поэтому веб-сайты и сам Google следят за тем, чтобы эти поисковые роботы не работали. По этой причине коды необходимо регулярно обновлять.
  2. Используя расширения браузера Chrome, такие как fatkun. Эти расширения гораздо более стабильны в использовании, чем предыдущий метод. Но в соответствии с требованиями этого проекта изображения должны быть удалены из Интернета.
  3. Использование инструментов Python, таких как Bing Image Downloader, для прямого экспорта необходимых изображений в каталог.

В этом проекте для удобства используется Bing Image Downloader.

Сначала мы установим загрузчик и импортируем необходимые библиотеки: -

!pip install bing-image-downloader 
from bing_image_downloader import downloader

Теперь он используется для удаления изображений с помощью следующих строк кода: -

downloader.download('Captain America Chris Evans', output_dir= './drive/MyDrive/datasets/collection', limit = 400, adult_filter_off = False, force_replace = False, timeout = 6000)

Аналогичным образом удаляются изображения Железного человека, Тора, Халка и Черной вдовы.

Шаг: 2 - Предварительная обработка данных

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

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

В этом проекте OpenCV и метод под названием каскады хаара, которые используются для очистки данных. Они определят, хорошо ли видны лицо и два глаза. Если они видны, то изображение сохраняется, в противном случае изображение отбрасывается. Большая часть работы по очистке данных будет выполняться с использованием кода Python, но будет некоторая работа по очистке, которую придется выполнять вручную. Для удаления нежелательных лиц требуется ручная проверка изображений. Например, в папке «Железный человек» могут появиться лица других персонажей, что снижает эффективность Модели.

Шаги по очистке данных:

  1. Лица с двумя глазами извлекаются из необработанных изображений с помощью каскада Хаара.
  2. Снимки с двумя или более лицами вручную отбрасываются. Также фотографии с размытыми фотографиями и др.

Краткое описание функционирования и использования Haar Cascade: -

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

Например, чтобы обнаружить глаза, область глаз имеет тенденцию быть более темной, чем область ниже. Haar Cascades используют эту маску для обнаружения областей.

OpenCV имеет готовые API для обнаружения лиц, глаз и т. Д. 17 различных XML-файлов для запуска API загружаются вручную для использования каскадных функций haar для обнаружения различных функций.

Теперь давайте импортируем необходимые библиотеки и создадим функции для использования функций каскада лица и каскада глаз: -

import numpy as np
import cv2
import matplotlib
from matplotlib import pyplot as plt
%matplotlib inline

face_cascade = cv2.CascadeClassifier("./drive/MyDrive/Colab Notebooks/opencv/haarcascades/haarcascade_frontalface_default.xml")
eye_cascade = cv2.CascadeClassifier("./drive/MyDrive/Colab Notebooks/opencv/haarcascades/haarcascade_eye.xml")

Давайте проведем пробный запуск, чтобы убедиться, что функции работают правильно или нет:

img = cv2.imread('/content/drive/My Drive/datasets/collection/Thor Chris Hemsworth/Image_8.jpg')
#img.shape
plt.imshow(img)
gray = cv2.cvtColor(img, cv2.COLOR_BGR2GRAY)
gray.shape
plt.imshow(gray, cmap='gray')

face_cascade = cv2.CascadeClassifier('./drive/MyDrive/Colab Notebooks/opencv/haarcascades/haarcascade_frontalface_default.xml')
eye_cascade = cv2.CascadeClassifier('./drive/MyDrive/Colab Notebooks/opencv/haarcascades/haarcascade_eye.xml')
faces = face_cascade.detectMultiScale(gray)
faces

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

Теперь, поскольку функции работают правильно, давайте напишем код для проверки всех изображений в наборе данных. Изображения, соответствующие требованиям, будут преобразованы в серый цвет, а затем обрезаны. Эти изображения сохраняются в отдельной папке для использования в будущем.

def get_cropped_image_if_2_eyes(image_path):
    img = cv2.imread(image_path)
    gray = cv2.cvtColor(img, cv2.COLOR_BGR2GRAY)
    faces = face_cascade.detectMultiScale(gray, 1.3, 5)
    for (x,y,w,h) in faces:
        roi_gray = gray[y:y+h, x:x+w]
        roi_color = img[y:y+h, x:x+w]
        eyes = eye_cascade.detectMultiScale(roi_gray)
        if len(eyes) >= 2:
            return roi_color

path_to_data = "./drive/My Drive/datasets/collection/"
path_to_cr_data = "./drive/My Drive/datasets/cropped/"

import os
img_dirs = []
for entry in os.scandir(path_to_data):
    if entry.is_dir():
        img_dirs.append(entry.path)

import shutil
if os.path.exists(path_to_cr_data):
     shutil.rmtree(path_to_cr_data)
os.mkdir(path_to_cr_data)

cropped_image_dirs = []
celebrity_file_names_dict = {}

for img_dir in img_dirs:
    count = 1
    celebrity_name = img_dir.split('/')[-1]
    print(celebrity_name)
    
    celebrity_file_names_dict[celebrity_name] = []
    
    for entry in os.scandir(img_dir):
        roi_color = get_cropped_image_if_2_eyes(entry.path)
        if roi_color is not None:
            cropped_folder = path_to_cr_data + celebrity_name
            if not os.path.exists(cropped_folder):
                os.makedirs(cropped_folder)
                cropped_image_dirs.append(cropped_folder)
                print("Generating cropped images in folder: ",cropped_folder) #Checking whether the code is running successfully or not
                
            cropped_file_name = celebrity_name + str(count) + ".png" #changing file type of every image to png
            cropped_file_path = cropped_folder + "/" + cropped_file_name 
            
            cv2.imwrite(cropped_file_path, roi_color)
            celebrity_file_names_dict[celebrity_name].append(cropped_file_path)
            count += 1

Шаг: 3 - Извлечение признаков с помощью вейвлет-преобразования

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

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

Изучая литературу по обработке изображений, было обнаружено, что вейвлет-преобразования часто являются наиболее эффективным способом извлечения. Итак, в этом проекте используется вейвлет-преобразование.

После ввода изображения он выполнит вейвлет-преобразование поверх него, используя PYWT (библиотека вейвлет-преобразования pi), и вернет ваше новое изображение, которое является вейвлет-преобразованием. Для применения вейвлет-преобразования в основных кодах использовались концепции обработки сигналов, частотной области, временной области, преобразования Фурье. Ниже кратко объясняются некоторые из этих концепций:

Любой сигнал, как и звуковой сигнал, изображение также можно рассматривать как сигнал. Он может быть представлен в домене двух типов. Таким образом, изображение может быть представлено в пространственной области, такой как пространство (x и y), или может быть представлено как частотная область. Аудиосигнал может быть представлен во временной или частотной области.

Преобразование Фурье принимает сложный сигнал и возвращает базовые сигналы, которые образуют этот сложный сигнал. Например, давайте рассмотрим какое-нибудь блюдо, допустим, Доса. Если провести обратный инжиниринг на Dosa, будут получены основные ингредиенты: вода, рисовая мука, урад-дал и, возможно, другие ингредиенты.

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

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

Для дальнейших шагов входом будет вертикально сложенное цветное изображение и его преобразованное вейвлет-преобразование изображение. Код для которого выглядит следующим образом:

import numpy as np 
import pywt 
import cv2      
def w2d(img, mode='haar', level=1):     
 imArray = img     
 #Datatype conversions     
 #convert to grayscale     
 imArray = cv2.cvtColor( imArray,cv2.COLOR_RGB2GRAY )     
 #convert to float     
 imArray =  np.float32(imArray)        
 imArray /= 255;     
 # compute coefficients      
 coeffs=pywt.wavedec2(imArray, mode, level=level)       
 #Process Coefficients     
 coeffs_H=list(coeffs)       
 coeffs_H[0] *= 0;        
 # reconstruction     
 imArray_H=pywt.waverec2(coeffs_H, mode);     
 imArray_H *= 255;     
 imArray_H =  np.uint8(imArray_H)      
 return imArray_H

Давайте назначим номер (или клавишу) каждому из 5 символов.

class_dict = {}
count = 0
for celebrity_name in celebrity_file_names_dict.keys():
    class_dict[celebrity_name] = count
    count = count + 1
class_dict

{«Черная вдова Скарлетт Йоханссон»: 2,
«Капитан Америка Крис Эванс»: 0,
«Халк Марк Руффало»: 3,
«Железный человек Тони Старк»: 1,
'Тор Крис Хемсворт': 4}

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

celebrity_file_names_dict = {}
for img_dir in cropped_image_dirs:
    celebrity_name = img_dir.split('/')[-1]
    file_list = []
    for entry in os.scandir(img_dir):
        file_list.append(entry.path)
    celebrity_file_names_dict[celebrity_name] = file_list

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

X, y = [], []
for celebrity_name, training_files in celebrity_file_names_dict.items():
    for training_image in training_files:
        img = cv2.imread(training_image)
        if img is None:
          continue
        scalled_raw_img = cv2.resize(img, (32, 32)) #resizing using openCV as images maybe of different sizes
        img_har = w2d(img,'db1',5) #getting the wavelet transformed image
        scalled_img_har = cv2.resize(img_har, (32, 32)) #resizing wavelet transformed image
        combined_img = np.vstack((scalled_raw_img.reshape(32*32*3,1),scalled_img_har.reshape(32*32,1))) #vertically stacking both the images
        X.append(combined_img)
        y.append(class_dict[celebrity_name])
X = np.array(X).reshape(len(X),4096).astype(float)

Шаг 4 - Обучение модели: использование SVM с эвристической тонкой настройкой

В этом проекте сначала SVM используется для обучения основной модели.

Затем с помощью GridSearch тестируются другие модели, чтобы решить, какая модель лучше всего подходит для проекта.

GridSearch CV используется для параметров Hypertuning. Это помогает решить, какая модель работает лучше всего.

В нашем проекте мы определяем модели-кандидаты для сравнения следующим образом:

  1. SVM с параметрами as - Значения C равны 1,10,100,1000, а значения ядра - rbf и linear.
  2. Случайный лес с параметрами as - Количество оценок (или деревьев решений) 1,5,10.
  3. Логистическая регрессия с параметрами as - Значения C равны 1,5,10.

Наконец, лучшая модель сохраняется в «Trained Model.pkl», а также сохраняется словарь классов.

Код для обучения SVM:

from sklearn.svm import SVC
from sklearn.preprocessing import StandardScaler
from sklearn.model_selection import train_test_split
from sklearn.pipeline import Pipeline
from sklearn.metrics import classification_report
X_train, X_test, y_train, y_test = train_test_split(X, y, random_state=0)

#Pipeline is created to scale the Data.  
pipe = Pipeline([('scaler', StandardScaler()), ('svc', SVC(kernel = 'rbf', C = 10))])
pipe.fit(X_train,y_train)
pipe.score(X_test,y_test)

Это дало 0,9770992366412213 баллов.

А теперь давайте получим полный отчет о классификации.

print(classification_report(y_test, pipe.predict(X_test)))

Это дало следующий результат:

Обучение и тестирование других моделей, как упоминалось ранее:

from sklearn import svm
from sklearn.ensemble import RandomForestClassifier
from sklearn.linear_model import LogisticRegression
from sklearn.pipeline import make_pipeline
from sklearn.model_selection import GridSearchCV
model_params = {
    'svm': {
        'model': svm.SVC(gamma='auto',probability=True),
        'params' : {
            'svc__C': [1,10,100,1000],
            'svc__kernel': ['rbf','linear']
        }  
    },
    'random_forest': {
        'model': RandomForestClassifier(),
        'params' : {
            'randomforestclassifier__n_estimators': [1,5,10]
        }
    },
    'logistic_regression' : {
        'model': LogisticRegression(solver='liblinear',multi_class='auto'),
        'params': {
            'logisticregression__C': [1,5,10]
        }
    }
}
scores = []
best_estimators = {}
import pandas as pd
for algo, mp in model_params.items():
    pipe = make_pipeline(StandardScaler(), mp['model'])
    clf =  GridSearchCV(pipe, mp['params'], cv=5, return_train_score=False) 
    # cv=5 => There will be 5 folds of testing the model and 
    # then will avereage out the scores 
    clf.fit(X_train, y_train)
    # Scores are appended and a data frame is created from it
    scores.append({
        'model': algo,
        'best_score': clf.best_score_,
        'best_params': clf.best_params_
    })
    best_estimators[algo] = clf.best_estimator_
    
df = pd.DataFrame(scores,columns=['model','best_score','best_params'])
df

Итоговый отчет о полученных баллах выглядит следующим образом:

Эти оценки были на данных обучения. Теперь давайте получим оценки моделей по данным тестирования:

best_estimators['svm'].score(X_test,y_test)
best_estimators['random_forest'].score(X_test,y_test)
best_estimators['logistic_regression'].score(X_test,y_test)

Полученные оценки были следующими:

SVM: 0,9770992366412213
Случайный лес: 0,9389312977099237
Логистическая регрессия: 0,9847328244274809

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

best_clf = best_estimators['svm']

Теперь рисуем матрицу путаницы:

from sklearn.metrics import confusion_matrix
cm = confusion_matrix(y_test, best_clf.predict(X_test))
import seaborn as sn
plt.figure(figsize = (10,7))
sn.heatmap(cm, annot=True)
plt.xlabel('Predicted')
plt.ylabel('Truth')

Сохранение модели в соответствующем файле pkl для будущего использования при создании веб-приложений и т. Д.

!pip install joblib
import joblib 
# Save the model as a pickle in a file 
joblib.dump(best_clf, 'saved_model.pkl')

Сохранение словаря в виде файла Json для использования в будущем:

import json
with open("class_dictionary.json","w") as f:
    f.write(json.dumps(class_dict))

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

Ссылка на полный код: Github

Спасибо, что потратили драгоценное время на чтение этой статьи. Сообщите мне свои мнения и предложения в комментариях.