diff --git a/LesnikovNA/lab3/README.md b/LesnikovNA/lab3/README.md new file mode 100644 index 0000000..bf35cbb --- /dev/null +++ b/LesnikovNA/lab3/README.md @@ -0,0 +1,62 @@ +# Практическая работа №3. Распознавание и классификация изображений с помощью машинного обучения + +Этот проект демонстрирует использование различных библиотек машинного обучения и компьютерного зрения для классификации изображений животных (кошек и собак). Он включает в себя предварительную обработку, обучение модели, визуализацию и анализ результатов. + +## 1. Предварительная обработка изображений + +Изображения подготавливаются следующим образом перед их использованием в модели классификации: + +- **Изменение размера**: Изображения приводятся к стандартному размеру (200x200 пикселей), что необходимо для унификации входных данных модели. +- **Преобразование в оттенки серого**: Для упрощения обработки и снижения вычислительной нагрузки изображения конвертируются в градации серого. + +## 2. Извлечение признаков + +Используя детекторы ключевых точек (например, SIFT или ORB), из изображений извлекаются ключевые точки и дескрипторы: + +- **Обнаружение ключевых точек**: Для каждого изображения определяются ключевые точки, которые представляют собой важные уникальные элементы. +- **Расчет дескрипторов**: Для каждой ключевой точки создается дескриптор, который описывает локальные особенности изображения вокруг этой точки. + +## 3. Кластеризация + +Дескрипторы всех обучающих изображений анализируются с помощью алгоритма KMeans для определения центров кластеров, которые будут использоваться для создания гистограмм: + +- **Формирование гистограмм**: Для каждого изображения создается гистограмма распределения его дескрипторов по кластерам, что позволяет сократить размерность данных и подготовить их к классификации. + +## 4. Обучение модели + +Скрипт поддерживает обучение моделей классификации с использованием следующих алгоритмов: + +- **Support Vector Classifier (SVC)**: Линейный классификатор на основе метода опорных векторов. +- **Random Forest Classifier**: Модель на основе ансамбля деревьев решений. + +Процесс обучения включает: + +- **Масштабирование данных**: Используется StandardScaler для приведения признаков к единому масштабу. +- **Обучение модели**: Настройка модели на обучающих данных. + +## 5. Оценка модели + +Модель оценивается на тестовых данных с использованием следующих метрик: + +- **Точность (Accuracy)**: Процент правильно классифицированных изображений. +- **Матрица ошибок (Confusion Matrix)**: Визуализация распределения правильных и ошибочных предсказаний. +- **Отчет о классификации (Classification Report)**: Метрика accuracy + +## 6. Визуализация результатов + +Скрипт поддерживает визуализацию следующих элементов: + +- **Матрица ошибок**: Графическое представление точности классификации. +- **Распределение дескрипторов**: Гистограммы кластеров для изображений кошек и собак. +- **Ключевые точки**: Отображение ключевых точек на исходных изображениях. + +## 7. Аргументы командной строки + +Для настройки и запуска алгоритма через командную строку поддерживаются следующие аргументы: + +- **`-extractAlg`**: Выбор алгоритма извлечения признаков (`sift` или `orb`). +- **`-classifier`**: Выбор классификатора (`svc` или `randomForest`). +- **`-train`**: Путь к директории с обучающими изображениями. +- **`-test`**: Путь к директории с тестовыми изображениями. +- **`-clusters`**: Количество кластеров для KMeans. +- **`-imagesCount`**: Количество изображений для обработки (по умолчанию обрабатываются все изображения). diff --git a/LesnikovNA/lab3/lab3.py b/LesnikovNA/lab3/lab3.py new file mode 100644 index 0000000..48cb95d --- /dev/null +++ b/LesnikovNA/lab3/lab3.py @@ -0,0 +1,185 @@ +import cv2 +import numpy as np +import os +import sys +from sklearn.svm import SVC +from sklearn.ensemble import RandomForestClassifier, GradientBoostingClassifier +from sklearn.model_selection import train_test_split +from sklearn.cluster import KMeans +from sklearn.preprocessing import StandardScaler +from sklearn.metrics import accuracy_score, classification_report, confusion_matrix +import matplotlib.pyplot as plt +import argparse +from sklearn.utils import shuffle + +Width = 200 +Height = 200 + +class Classifier: + def __init__(self, classifier='svc'): + if classifier == "randomForest": + self.Model = RandomForestClassifier(n_estimators=100, max_depth=5) + elif classifier == "svc": + self.Model = SVC(probability=True, kernel='linear', random_state=23) + else: + raise ValueError(f"Unknown classifier") + self.Scaler = StandardScaler() + + def Train(self, X_train, Y_train): + XTrainScaled = self.Scaler.fit_transform(X_train) + self.Model.fit(XTrainScaled, Y_train) + + def Stats(self, X_test, Y_test): + XTestScaled = self.Scaler.transform(X_test) + YPred = self.Model.predict(XTestScaled) + Accuracy = accuracy_score(Y_test, YPred) + return Accuracy, YPred + +def PlotClassificationResults(YTrue, YPred, ClassNames): + Cm = confusion_matrix(YTrue, YPred) + CatsCount = Cm[0, 0].sum() + DogsCount = Cm[1, 1].sum() + MisclassifiedCount = Cm[0, 1] + Cm[1, 0] + Counts = [CatsCount, DogsCount, MisclassifiedCount] + Labels = ['Cats', 'Dogs', 'Wrong'] + Colors = ['green', 'green', 'red'] + plt.figure(figsize=(8, 6)) + plt.bar(Labels, Counts, color=Colors) + plt.ylabel('Number of images') + for i, count in enumerate(Counts): + plt.text(i, count + 1, str(count), ha='center', fontsize=12) + plt.tight_layout() + plt.show() + +def VisualizeFeatures(Images, Descriptor): + for i, Img in enumerate(Images[:3]): + Gray = cv2.cvtColor(Img, cv2.COLOR_BGR2GRAY) + KeyPoints = Descriptor.detect(Gray, None) + ImgWithKeyPoints = cv2.drawKeypoints(Gray, KeyPoints, None, flags=cv2.DRAW_MATCHES_FLAGS_DRAW_RICH_KEYPOINTS) + plt.figure(figsize=(5, 4)) + plt.imshow(ImgWithKeyPoints, cmap='gray') + plt.title(f"{len(KeyPoints)} keypoints") + plt.show() + +def PlotConfusionMatrix(YTrue, YPred, ClassNames): + Cm = confusion_matrix(YTrue, YPred) + plt.figure(figsize=(8, 6)) + plt.imshow(Cm, interpolation='nearest', cmap='coolwarm') + plt.title('Confusion matrix') + plt.colorbar() + TickMarks = np.arange(len(ClassNames)) + plt.xticks(TickMarks, ClassNames) + plt.yticks(TickMarks, ClassNames) + plt.tight_layout() + plt.ylabel('True label') + plt.xlabel('Predicted label') + for i, j in np.ndindex(Cm.shape): + plt.text(j, i, format(Cm[i, j], 'd'), horizontalalignment="center", color="white" ) + plt.show() + +def ParseArguments(): + Parser = argparse.ArgumentParser() + Parser.add_argument('-extractAlg', type=str, choices=['sift', 'orb'], dest='Descriptor') + Parser.add_argument('-classifier', type=str, choices=['svc', 'randomForest'], dest='Classifier') + Parser.add_argument('-train', type=str, dest='TrainDir') + Parser.add_argument('-test', type=str, dest='TestDir') + Parser.add_argument('-clusters', type=int, dest='NClusters') + Parser.add_argument('-imagesCount', type=int, dest='ImagesCount') + Args = Parser.parse_args() + return Args + +def LoadImages(Folder, Label, MaxCount): + Images = [] + Labels = [] + for Filename in os.listdir(Folder): + if MaxCount < 0: + break + MaxCount -= 1 + Path = os.path.join(Folder, Filename) + Img = cv2.imread(Path) + if Img is not None: + Img = cv2.resize(Img, (Width, Height)) + Images.append(Img) + Labels.append(Label) + return Images, Labels + +class Extractor: + def __init__(self, NClusters, Descriptor): + self.NClusters = NClusters + self.KMeans = KMeans(n_clusters=self.NClusters, random_state=23) + if Descriptor == 'sift': + self.Detector = cv2.SIFT_create() + elif Descriptor == 'orb': + self.Detector = cv2.ORB_create() + else: + raise ValueError(f"Unknown extraction algorithm") + + def ExtractFeatures(self, Images): + DescriptorsList = [] + KeyPointsCounts = [] + for Img in Images: + Gray = cv2.cvtColor(Img, cv2.COLOR_BGR2GRAY) + KeyPoints, Descriptors = self.Detector.detectAndCompute(Gray, None) + if Descriptors is not None: + DescriptorsList.append(Descriptors) + KeyPointsCounts.append(len(KeyPoints)) + self.AverageKeyPoints = np.mean(KeyPointsCounts) + return DescriptorsList + + def ComputeBowHistograms(self, Images): + Features = [] + for Img in Images: + Gray = cv2.cvtColor(Img, cv2.COLOR_BGR2GRAY) + KeyPoints, Descriptors = self.Detector.detectAndCompute(Gray, None) + Histogram = np.zeros(self.NClusters) + if Descriptors is not None: + Predictions = self.KMeans.predict(Descriptors) + for Pred in Predictions: + Histogram[Pred] += 1 + Features.append(Histogram) + return np.array(Features) + + def FitKMeansClusterization(self, DescriptorsList): + AllDescriptors = [] + for Desc in DescriptorsList: + if Desc is not None: + AllDescriptors.append(Desc) + AllDescriptors = np.vstack(AllDescriptors) + self.KMeans.fit(AllDescriptors) + +def main(): + Args = ParseArguments() + Data = Extractor(NClusters=Args.NClusters, Descriptor=Args.Descriptor) + + CatsTrain, YCatsTrain = LoadImages(os.path.join(Args.TrainDir, 'Cat'), 0, Args.ImagesCount) + DogsTrain, YDogsTrain = LoadImages(os.path.join(Args.TrainDir, 'Dog'), 1, Args.ImagesCount) + CatsTest, YCatsTest = LoadImages(os.path.join(Args.TestDir, 'Cat'), 0, Args.ImagesCount / 3) + DogsTest, YDogsTest = LoadImages(os.path.join(Args.TestDir, 'Dog'), 1, Args.ImagesCount / 3) + + TrainImages = CatsTrain + DogsTrain + TrainLabels = YCatsTrain + YDogsTrain + TestImages = CatsTest + DogsTest + TestLabels = YCatsTest + YDogsTest + TrainImages, TrainLabels = shuffle(TrainImages, TrainLabels, random_state=23) + TestImages, TestLabels = shuffle(TestImages, TestLabels, random_state=12) + TrainDescriptors = Data.ExtractFeatures(TrainImages) + Data.FitKMeansClusterization(TrainDescriptors) + + print(f"Average number of control points: ", Data.AverageKeyPoints) + VisualizeFeatures(TrainImages, Data.Detector) + + XTrainFeatures = Data.ComputeBowHistograms(TrainImages) + XTestFeatures = Data.ComputeBowHistograms(TestImages) + + Model = Classifier(Args.Classifier) + Model.Train(XTrainFeatures, TrainLabels) + TrainAccuracy, YTrainPred = Model.Stats(XTrainFeatures, TrainLabels) + TestAccuracy, YTestPred = Model.Stats(XTestFeatures, TestLabels) + print("Train Accuracy:", TrainAccuracy) + print("Test Accuracy:", TestAccuracy) + ClassNames = ['Cats', 'Dogs'] + PlotConfusionMatrix(TestLabels, YTestPred, ClassNames) + PlotClassificationResults(TestLabels, YTestPred, ClassNames) + +if __name__ == "__main__": + sys.exit(main() or 0)