diff --git a/.gitignore b/.gitignore index ccf61ec68afc49c8e726ea3533e0777fadd032d5..be4f5e98d6808f994d353a44523ce04d4154bf78 100644 --- a/.gitignore +++ b/.gitignore @@ -1,3 +1,5 @@ +*.onnx + # Created by https://www.toptal.com/developers/gitignore/api/visualstudiocode,python,macos,windows,jupyternotebooks # Edit at https://www.toptal.com/developers/gitignore?templates=visualstudiocode,python,macos,windows,jupyternotebooks diff --git a/lab-4/task_1.py b/lab-4/task_1.py new file mode 100644 index 0000000000000000000000000000000000000000..2a9b809212dd91491fe05b24b71eb971b2ed9e47 --- /dev/null +++ b/lab-4/task_1.py @@ -0,0 +1,118 @@ +import os +import requests + +import cv2 + + +DIRECTORY = "lab-4" + +# Рнициализация детектора лиц YuNET +if not os.path.exists(f"{DIRECTORY}/face_detection_yunet_2023mar.onnx"): + print("Загрузка face_detection_yunet_2023mar.onnx ...") + url = "https://github.com/opencv/opencv_zoo/raw/refs/heads/main/models/face_detection_yunet/face_detection_yunet_2023mar.onnx" + response = requests.get(url) + with open(f"{DIRECTORY}/face_detection_yunet_2023mar.onnx", "wb") as f: + f.write(response.content) + print("Загрузка face_detection_yunet_2023mar.onnx завершена") + +detector = cv2.FaceDetectorYN.create( + f"{DIRECTORY}/face_detection_yunet_2023mar.onnx", "", (320, 320), 0.9, 0.3, 5000 +) + +# Рнициализация распознавателя лиц +if not os.path.exists(f"{DIRECTORY}/face_recognition_sface_2021dec.onnx"): + print("Загрузка face_recognition_sface_2021dec.onnx ...") + url = "https://github.com/opencv/opencv_zoo/raw/refs/heads/main/models/face_recognition_sface/face_recognition_sface_2021dec.onnx" + response = requests.get(url) + with open(f"{DIRECTORY}/face_recognition_sface_2021dec.onnx", "wb") as f: + f.write(response.content) + print("Загрузка face_recognition_sface_2021dec.onnx завершена") + +recognizer = cv2.FaceRecognizerSF.create( + f"{DIRECTORY}/face_recognition_sface_2021dec.onnx", "" +) + +# Загрузка референсного изображения +ref_image = cv2.imread(f"{DIRECTORY}/task_1_reference.jpg") +if ref_image is None: + print("РќРµ удалось загрузить референсное изображение") + exit() + +# Обнаружение лица РЅР° референсном изображении +height, width = ref_image.shape[:2] +detector.setInputSize((width, height)) +_, faces = detector.detect(ref_image) + +if faces is None: + print("РќР° референсном изображении РЅРµ найдено лиц") + exit() + +# Рзвлечение признаков референсного лица +ref_face = faces[0] +aligned_ref_face = recognizer.alignCrop(ref_image, ref_face) +ref_feature = recognizer.feature(aligned_ref_face) + +# Рнициализация видеопотока СЃ веб-камеры +cap = cv2.VideoCapture(0) +if not cap.isOpened(): + print("РќРµ удалось открыть веб-камеру") + exit() + +# РџРѕСЂРѕРі РєРѕСЃРёРЅСѓСЃРЅРѕРіРѕ сходства для распознавания +COSINE_THRESHOLD = 0.4 +L2_THRESHOLD = 1.2 + +while True: + ret, frame = cap.read() + if not ret: + print("РќРµ удалось получить кадр") + break + + # Обнаружение лиц РІ кадре + frame_height, frame_width = frame.shape[:2] + detector.setInputSize((frame_width, frame_height)) + _, faces = detector.detect(frame) + + if faces is not None: + for face in faces: + # Получение координат ограничивающего прямоугольника + bbox = list(map(int, face[:4])) + x, y, w, h = bbox + + # Рзвлечение признаков текущего лица + aligned_face = recognizer.alignCrop(frame, face) + current_feature = recognizer.feature(aligned_face) + + # Сравнение СЃ референсным лицом + cosine_score = recognizer.match( + ref_feature, current_feature, cv2.FaceRecognizerSF_FR_COSINE + ) + l2_score = recognizer.match( + ref_feature, current_feature, cv2.FaceRecognizerSF_FR_NORM_L2 + ) + + # Определение цвета прямоугольника + color = (0, 0, 255) # Красный РїРѕ умолчанию + if cosine_score >= COSINE_THRESHOLD or l2_score <= L2_THRESHOLD: + print(f"{cosine_score=:.3f}, {l2_score=:.3f}") + if cosine_score >= COSINE_THRESHOLD and l2_score <= L2_THRESHOLD: + print("Сработали РѕР±Р° РїРѕСЂРѕРіР°") + elif cosine_score >= COSINE_THRESHOLD: + print("Сработал РїРѕСЂРѕРі РєРѕСЃРёРЅСѓСЃРЅРѕРіРѕ сходства") + elif l2_score <= L2_THRESHOLD: + print("Сработал РїРѕСЂРѕРі L2 сходства") + color = (0, 255, 0) # Зеленый для референсного лица + + # Отрисовка прямоугольника + cv2.rectangle(frame, (x, y), (x + w, y + h), color, 2) + + # Отображение результата + cv2.imshow("Face Detection", frame) + + # Выход РїРѕ нажатию 'q' + if cv2.waitKey(1) & 0xFF == ord("q"): + break + +# Освобождение ресурсов +cap.release() +cv2.destroyAllWindows() diff --git a/lab-4/task_1_reference.jpg b/lab-4/task_1_reference.jpg new file mode 100644 index 0000000000000000000000000000000000000000..725de14d0cd3206586c43fcfc7863babda90064b Binary files /dev/null and b/lab-4/task_1_reference.jpg differ diff --git a/lab-4/task_2.py b/lab-4/task_2.py new file mode 100644 index 0000000000000000000000000000000000000000..358beb19aa31459efdb54959ec7d4a9e029ee48e --- /dev/null +++ b/lab-4/task_2.py @@ -0,0 +1,143 @@ +# app.py +import os +import numpy as np +import streamlit as st +from deepface import DeepFace +from PIL import Image +import faiss +import pandas as pd + +# Конфигурации +EMBEDDING_SIZE = 128 # Для FaceNet +DATABASE_PATH = "lab-4/task_2_faces" +INDEX_PATH = "lab-4/task_2_faiss_index.index" +NAMES_PATH = "lab-4/task_2_names.npy" +STAFF_CSV = "lab-4/task_2_staff_photo.csv" # Добавим путь Рє CSV файлу + +def build_faiss_database(): + st.write("Построение базы данных...") + if not os.path.exists(DATABASE_PATH): + st.error("Папка СЃ лицами РЅРµ найдена, создаю папку") + st.write("Загрузите изображения лиц РІ папку Рё нажмите РєРЅРѕРїРєСѓ 'Построить/Обновить базу' ещё раз") + os.makedirs(DATABASE_PATH) + return + + # Загрузка данных Рѕ сотрудниках + try: + staff_df = pd.read_csv(STAFF_CSV) + filename_to_name = dict(zip(staff_df['filename'], staff_df['name'])) + except Exception as e: + st.error(f"Ошибка чтения CSV файла: {str(e)}") + return + + embeddings = [] + names = [] + + progress_bar = st.progress(0) + files = sorted(os.listdir(DATABASE_PATH)) + mx = len(files) + + for i, img_file in enumerate(files): + img_path = os.path.join(DATABASE_PATH, img_file) + try: + # Рзвлечение лица Рё эмбеддинга + img = Image.open(img_path) + face = DeepFace.extract_faces(img_path=img_path, detector_backend='opencv')[0] + embedding = DeepFace.represent( + img_path=img_path, + model_name='Facenet', + detector_backend='opencv' + )[0]['embedding'] + + embeddings.append(embedding) + # Рспользуем РёРјСЏ РёР· CSV вместо имени файла + person_name = filename_to_name.get(img_file, img_file) + names.append(person_name) + except Exception as e: + st.error(f"Ошибка обработки {img_path}: {str(e)}") + progress_bar.progress(i / mx) + + if len(embeddings) == 0: + return + + # Создание FAISS индекса + embeddings = np.array(embeddings).astype('float32') + index = faiss.IndexFlatL2(EMBEDDING_SIZE) + index.add(embeddings) + + # Сохранение индекса Рё имен + faiss.write_index(index, INDEX_PATH) + np.save(NAMES_PATH, np.array(names)) + st.success(f"База данных построена: {len(names)} лиц") + +def search_face(query_img, threshold=0.5): + if not os.path.exists(INDEX_PATH): + st.error("Сначала постройте базу данных") + return None, None + + try: + # Конвертация PIL Image РІ numpy array + query_img_array = np.array(query_img) + + # Рзвлечение эмбеддинга для запроса + face = DeepFace.extract_faces(img_path=query_img_array, detector_backend='opencv')[0] + query_embedding = DeepFace.represent( + img_path=query_img_array, + model_name='Facenet', + detector_backend='opencv' + )[0]['embedding'] + + # РџРѕРёСЃРє РІ FAISS + index = faiss.read_index(INDEX_PATH) + names = np.load(NAMES_PATH) + + # РџРѕРёСЃРє ближайшего соседа + query_embedding = np.array([query_embedding]).astype('float32') + distances, indices = index.search(query_embedding, 1) + + if distances[0][0] <= threshold: + return names[indices[0][0]], distances[0][0] + else: + return "Unknown", distances[0][0] + + except Exception as e: + st.error(f"Ошибка распознавания: {str(e)}") + return None, None + +# Streamlit интерфейс +st.title("рџ”Ќ Face Recognition System") + +# Секция для построения базы данных +st.sidebar.header("База данных лиц") +if st.sidebar.button("Построить/Обновить базу"): + build_faiss_database() + +# Секция для РїРѕРёСЃРєР° +st.header("РџРѕРёСЃРє лица") +uploaded_file = st.file_uploader("Загрузите изображение лица", type=['jpg', 'png', 'jpeg']) + +if uploaded_file is not None: + image = Image.open(uploaded_file) + st.image(image, caption="Загруженное изображение", use_container_width=True) + + if st.button("Найти совпадения"): + name, distance = search_face(image, threshold=1000) + if name: + st.subheader(f"Результат: {name}") + st.write(f"Расстояние: {distance:.4f}") + if name == "Unknown": + st.error("Совпадений РЅРµ найдено РІ базе данных") + else: + st.success("Совпадение найдено!") + # Загружаем данные Рѕ сотрудниках + staff_df = pd.read_csv(STAFF_CSV) + # Рщем запись сотрудника РїРѕ имени + staff_record = staff_df[staff_df['name'] == name] + if not staff_record.empty: + # Получаем РёРјСЏ файла фотографии + photo_filename = staff_record.iloc[0]['filename'] + photo_path = os.path.join(DATABASE_PATH, photo_filename) + if os.path.exists(photo_path): + matched_image = Image.open(photo_path) + st.image(matched_image, caption=f"Фотография сотрудника: {name}", + use_container_width=True) \ No newline at end of file diff --git a/lab-4/task_3_1.py b/lab-4/task_3_1.py new file mode 100644 index 0000000000000000000000000000000000000000..44e3e8f545291ab6dea8695909a30dc4551fc3bd --- /dev/null +++ b/lab-4/task_3_1.py @@ -0,0 +1,207 @@ +import cv2 +import pandas as pd +import numpy as np +from deepface import DeepFace +from tqdm import tqdm +import os +import pickle +import hashlib + +# Конфигурация +VIDEO_DIR = "lab-4/task_3_1_references/videos" +TIMECODE_CSV_DIR = "lab-4/task_3_1_references/labels" +REFERENCE_DIR = "lab-4/task_3_1_references/photos" +CACHE_FILE = "lab-4/task_3_1_cache/embeddings_cache.pkl" +DIR_HASH_FILE = "lab-4/task_3_1_cache/dir_hash.txt" +THRESHOLD = 0.1 # РџРѕСЂРѕРі РєРѕСЃРёРЅСѓСЃРЅРѕРіРѕ сходства +FRAME_SKIP = 50 # Каждый 50-Р№ кадр + +video_files = [f for f in os.listdir(VIDEO_DIR) if f.endswith((".mp4", ".avi", ".mov"))] +print("Доступные видео:") +for idx, video in enumerate(video_files): + print(f"{idx + 1}: {video}") + +video_choice = int(input("Выберите номер видео для обработки: ")) - 1 +VIDEO_PATH = os.path.join(VIDEO_DIR, video_files[video_choice]) + +# Загрузка соответствующего файла СЃ таймкодами +TIMECODE_CSV = os.path.join( + TIMECODE_CSV_DIR, + video_files[video_choice].replace(video_files[video_choice].split(".")[-1], "csv"), +) + + +def get_dir_hash(directory): + """Вычисляет С…СЌС€ директории РЅР° РѕСЃРЅРѕРІРµ имен файлов Рё РёС… размеров""" + hash_md5 = hashlib.md5() + for root, _, files in os.walk(directory): + for file in sorted(files): # сортировка для стабильности хэша + filepath = os.path.join(root, file) + file_size = os.path.getsize(filepath) + hash_md5.update(f"{filepath}{file_size}".encode()) + return hash_md5.hexdigest() + + +def normalize_embedding(embedding): + return embedding / np.linalg.norm(embedding) + + +def load_reference_embeddings(): + # Проверяем существующий РєСЌС€ + current_hash = get_dir_hash(REFERENCE_DIR) + if os.path.exists(CACHE_FILE) and os.path.exists(DIR_HASH_FILE): + with open(DIR_HASH_FILE, "r") as f: + cached_hash = f.read().strip() + if cached_hash == current_hash: + print("Загрузка эмбеддингов РёР· кэша...") + with open(CACHE_FILE, "rb") as f: + return pickle.load(f) + + print("Вычисление новых эмбеддингов...") + embeddings = {} + for person in tqdm(os.listdir(REFERENCE_DIR)): + person_dir = os.path.join(REFERENCE_DIR, person) + if os.path.isdir(person_dir): + for img_name in os.listdir(person_dir): + img_path = os.path.join(person_dir, img_name) + try: + # Сначала извлекаем лицо + faces = DeepFace.extract_faces( + img_path=img_path, + detector_backend="opencv", + enforce_detection=False, + ) + if faces: # Если лицо найдено + # Получаем эмбеддинг для найденного лица + embedding = normalize_embedding( + DeepFace.represent( + img_path=faces[0][ + "face" + ], # Рспользуем извлеченное лицо + model_name="Facenet", + detector_backend="opencv", + enforce_detection=False, + )[0]["embedding"] + ) + if person not in embeddings: + embeddings[person] = [] + embeddings[person].append(embedding) + except Exception as e: + print(f"Ошибка обработки {img_path}: {str(e)}") + continue + + # Сохраняем новый РєСЌС€ + with open(CACHE_FILE, "wb") as f: + pickle.dump(embeddings, f) + with open(DIR_HASH_FILE, "w") as f: + f.write(current_hash) + + return embeddings + + +print("\nЗагрузка эталонных эмбеддингов...") +reference_embeddings = load_reference_embeddings() +print(f"Загружено {len(reference_embeddings)} эталонных личностей") + +# Загрузка разметки +print("\nЗагрузка разметки РёР· CSV...") +timecodes = pd.read_csv(TIMECODE_CSV) +timecodes["from"] = pd.to_timedelta(timecodes["from"]).dt.total_seconds() +timecodes["to"] = pd.to_timedelta(timecodes["to"]).dt.total_seconds() +# Преобразование строки persons РІ СЃРїРёСЃРѕРє +timecodes["persons"] = timecodes["persons"].apply( + lambda x: [person.strip() for person in x.split(",")] if isinstance(x, str) else x +) +print(f"Загружено {len(timecodes)} временных меток") + +# Рнициализация видео +print("\nРнициализация видео...") +cap = cv2.VideoCapture(VIDEO_PATH) +fps = cap.get(cv2.CAP_PROP_FPS) +total_frames = int(cap.get(cv2.CAP_PROP_FRAME_COUNT)) +print(f"FPS: {fps:.2f}") +print(f"Всего кадров: {total_frames}") +print(f"Длительность видео: {total_frames/fps:.2f} секунд") + +# Метрики +true_positives = 0 +false_positives = 0 + +print("\nНачинаем обработку кадров...") +# Обработка кадров +for frame_num in tqdm(range(0, total_frames, FRAME_SKIP)): + cap.set(cv2.CAP_PROP_POS_FRAMES, frame_num) + ret, frame = cap.read() + if not ret: + break + + # Определение текущего времени + current_time = frame_num / fps + + # Получение ожидаемых лиц + current_frames = timecodes[ + (timecodes["from"] <= current_time) & (timecodes["to"] >= current_time) + ] + expected = [] + for persons_list in current_frames["persons"]: + if isinstance(persons_list, list): + expected.extend(persons_list) + expected = list(set(expected)) # удаление дубликатов + + try: + # Обнаружение лиц + detected_faces = DeepFace.extract_faces( + frame, detector_backend="opencv", enforce_detection=False + ) + except Exception as exc: + print(f"РџРѕРёСЃРє РЅР° кадре {frame_num} РЅРµ удался: {exc=}") + continue + + # print(f"{expected=}") + + for face in detected_faces: + try: + # Получение эмбеддинга + query_embedding = normalize_embedding( + DeepFace.represent( + img_path=face["face"], + model_name="Facenet", + detector_backend="opencv", + enforce_detection=False, + )[0]["embedding"] + ) + + # РџРѕРёСЃРє совпадений + best_match = ("Unknown", 1) + for person, embeddings in reference_embeddings.items(): + avg_distance = np.mean( + [np.linalg.norm(query_embedding - emb) for emb in embeddings] + ) + if avg_distance < best_match[1]: + best_match = (person, avg_distance) + # Проверка совпадения + # print(f"{best_match=}") + if best_match[1] > THRESHOLD: + if best_match[0] in expected: + true_positives += 1 + else: + false_positives += 1 + else: + false_positives += 1 + + except Exception as exc: + print(f"Сопоставление РЅР° кадре {frame_num} РЅРµ удалось: {exc=}") + false_positives += 1 + +# Расчет точности +precision = ( + true_positives / (true_positives + false_positives) + if (true_positives + false_positives) > 0 + else 0 +) +print("\nРезультаты анализа:") +print(f"Правильные обнаружения (True Positives): {true_positives}") +print(f"Ложные обнаружения (False Positives): {false_positives}") +print(f"Precision: {precision:.4f}") +cap.release() +print("\nОбработка завершена!") diff --git a/lab-4/task_3_2.py b/lab-4/task_3_2.py new file mode 100644 index 0000000000000000000000000000000000000000..79ebec4f7392c4a88bdbb41f328260211e6f7e82 --- /dev/null +++ b/lab-4/task_3_2.py @@ -0,0 +1,310 @@ +"""## Часть 2. Оценить точность работы методов РёР· DeepFace РЅР° аугментированных данных (2 балла). + +Необходимо собрать собственный набор данных РёР· **различных** изображений Вашего лица СЃ разных ракурсов, желательно настоящие фотографии РёР· личного архива (20 штук)\ +Возьмите эталонное изображение (как РІ паспорте) Рё РїСЂРё помощи библиотеки [DeepFace](https://github.com/serengil/deepface) проверьте его РЅР° соответствие всему датасету. Посчитайте метрику Precision. \ +\ +Примените каждую РёР· перечисленных ниже аугментаций (**РїРѕ-отдельности**) РєРѕ всему датасету Рё измерьте метрику Precision для измененнного датасета: +* РџРѕРІРѕСЂРѕС‚ изображения РЅР° 45В° Рё 90В°. +* Добавление шума (Gaussian Noise). +* Рзменение яркости (увеличение Рё уменьшение РЅР° 50%). +* Размытие СЃ различными параметрами. +\ +Реузультаты соберите РІ таблицу РІРёРґР°: + +Метод | Рсходный датасет | РџРѕРІРѕСЂРѕС‚ РЅР° 45В° | РџРѕРІРѕСЂРѕС‚ РЅР° 90В° | Рзображение СЃ шумом | +--- | ----|--- | --- | --- | +VGG-Face | 0 | 0 | 0 | 0 | +Facenet | 0 | 0 | 0 | 0 | +Facenet512 | 0 | 0 | 0 | 0 | +OpenFace | 0 | 0 | 0 | 0 | +DeepFace | 0 | 0 | 0 | 0 | +DeepID | 0 | 0 | 0 | 0 | +ArcFace | 0 | 0 | 0 | 0 | +Dlib | 0 | 0 | 0 | 0 | +SFace | 0 | 0 | 0 | 0 | +GhostFaceNet | 0 | 0 | 0 | 0 | +""" + +import os +import cv2 +import numpy as np +from deepface import DeepFace +from PIL import Image, ImageEnhance, ImageFilter +import pandas as pd +import matplotlib.pyplot as plt +from tqdm import tqdm + +# Путь Рє эталонному изображению +reference_image_path = "lab-4/task_3_2_reference.jpg" + +# Путь Рє папке СЃ изображениями датасета +dataset_folder = "lab-4/task_3_2_references" +image_paths = [os.path.join(dataset_folder, img) for img in os.listdir(dataset_folder)] + +"""РџРѕРІРѕСЂРѕС‚ РЅР° 45 Рё 90 градусов:""" + + +def rotate_image(image, angle): + (h, w) = image.shape[:2] + center = (w // 2, h // 2) + M = cv2.getRotationMatrix2D(center, angle, 1.0) + rotated = cv2.warpAffine(image, M, (w, h)) + return rotated + + +"""Добавление шума:""" + + +def add_noise(image): + row, col, ch = image.shape + mean = 0 + var = 0.1 + sigma = var**0.5 + gauss = np.random.normal(mean, sigma, (row, col, ch)) + gauss = gauss.reshape(row, col, ch) + noisy = image + gauss * 50 + return noisy.astype(np.uint8) + + +"""Рзменение яркости:""" + + +def adjust_brightness(image, factor): + img = Image.fromarray(cv2.cvtColor(image, cv2.COLOR_BGR2RGB)) + enhancer = ImageEnhance.Brightness(img) + brightened = enhancer.enhance(factor) + return cv2.cvtColor(np.array(brightened), cv2.COLOR_RGB2BGR) + + +"""Размытие:""" + + +def apply_blur(image, kernel_size=(5, 5)): + return cv2.GaussianBlur(image, kernel_size, 0) + + +def apply_average_blur(image, kernel_size=(5, 5)): + return cv2.blur(image, kernel_size) + + +def apply_bilateral_blur(image, d=9, sigma_color=75, sigma_space=75): + return cv2.bilateralFilter(image, d, sigma_color, sigma_space) + + +"""Распознавание лиц:""" + + +def calculate_recall(reference_embedding, dataset_embeddings, threshold=0.5): + print(f"Вычисление метрики recall СЃ РїРѕСЂРѕРіРѕРј {threshold}") + true_positives = 0 + false_negatives = 0 + + for i, embedding_list in enumerate(dataset_embeddings): + if isinstance(embedding_list, list) and len(embedding_list) > 0: + embedding_dict = embedding_list[0] + if isinstance(embedding_dict, dict) and "embedding" in embedding_dict: + embedding = np.array(embedding_dict["embedding"]) + distance = np.linalg.norm(reference_embedding - embedding) + if distance < threshold: + true_positives += 1 + else: + false_negatives += 1 + else: + print( + f"Предупреждение: Неожиданный формат embedding_dict РІ элементе {i+1}" + ) + false_negatives += 1 + else: + print(f"Предупреждение: Лицо РЅРµ обнаружено РІ элементе {i+1}") + false_negatives += 1 + + recall = true_positives / 20 + print( + f"Вычисление recall завершено, {recall=:.3f}. Рстинно положительных: {true_positives}, Ложно отрицательных: {false_negatives}" + ) + return recall + + +def test_transformations(image_paths): + results = { + model: {} + for model in [ + "Facenet", + "VGG-Face", + "Facenet512", + "OpenFace", + # "DeepFace", + "DeepID", + "ArcFace", + "Dlib", + "SFace", + "GhostFaceNet", + ] + } + + for model_name in results.keys(): + print(f"\nТестирование модели: {model_name}") + + # First extract face from reference image + print("Рзвлечение лица РёР· эталонного изображения...") + try: + faces = DeepFace.extract_faces( + img_path=reference_image_path, + detector_backend="opencv", + enforce_detection=False, + ) + + if faces: + # Get reference embedding using extracted face + print("Получение эталонного эмбеддинга...") + reference_embedding_list = DeepFace.represent( + img_path=faces[0]["face"], + model_name=model_name, + detector_backend="opencv", + enforce_detection=False, + ) + reference_embedding = np.array(reference_embedding_list[0]["embedding"]) + else: + print("Лицо РЅРµ обнаружено РІ эталонном изображении") + continue + except Exception as e: + print(f"Ошибка РїСЂРё обработке эталонного изображения: {str(e)}") + continue + + print("Обработка РёСЃС…РѕРґРЅРѕРіРѕ датасета...") + original_embeddings = process_dataset(image_paths, model_name=model_name) + results[model_name]["Рсходный датасет"] = calculate_recall( + reference_embedding, original_embeddings + ) + + print("Обработка поворота РЅР° 45В°...") + rotated_45_embeddings = process_dataset( + image_paths, + model_name=model_name, + transform=lambda img: rotate_image(img, 45), + transform_name="РџРѕРІРѕСЂРѕС‚ РЅР° 45В°", + ) + results[model_name]["РџРѕРІРѕСЂРѕС‚ РЅР° 45В°"] = calculate_recall( + reference_embedding, rotated_45_embeddings + ) + + print("Обработка поворота РЅР° 90В°...") + rotated_90_embeddings = process_dataset( + image_paths, + model_name=model_name, + transform=lambda img: rotate_image(img, 90), + transform_name="РџРѕРІРѕСЂРѕС‚ РЅР° 90В°", + ) + results[model_name]["РџРѕРІРѕСЂРѕС‚ РЅР° 90В°"] = calculate_recall( + reference_embedding, rotated_90_embeddings + ) + + print("Обработка изображений СЃ шумом...") + noisy_embeddings = [] + res = process_dataset( + image_paths, + model_name=model_name, + transform=add_noise, + transform_name="Рзображение СЃ шумом", + ) + noisy_embeddings.extend(res) + res = process_dataset( + image_paths, + model_name=model_name, + transform=lambda img: adjust_brightness(img, 0.5), + transform_name="Рзображение СЃ уменьшенной яркостью", + ) + noisy_embeddings.extend(res) + res = process_dataset( + image_paths, + model_name=model_name, + transform=lambda img: adjust_brightness(img, 1.5), + transform_name="Рзображение СЃ увеличенной яркостью", + ) + noisy_embeddings.extend(res) + res = process_dataset( + image_paths, + model_name=model_name, + transform=apply_blur, + transform_name="Размытие", + ) + noisy_embeddings.extend(res) + results[model_name]["Рзображение СЃ шумом"] = calculate_recall( + reference_embedding, noisy_embeddings + ) + + return results + + +def process_dataset(image_paths, model_name, transform=None, transform_name="Без трансформации"): + embeddings = [] + + for img_path in tqdm( + image_paths, + desc=f"Обработка изображений ({model_name}, трансформация={transform_name})", + ): + try: + img = cv2.imread(img_path) + if transform: + img = transform(img) + + # First extract face + faces = DeepFace.extract_faces( + img_path=img, detector_backend="opencv", enforce_detection=False + ) + + if faces: # If face was found + # Get embedding using the extracted face + embedding_list = DeepFace.represent( + img_path=faces[0]["face"], # Use the extracted face + model_name=model_name, + detector_backend="opencv", + enforce_detection=False, + ) + embeddings.append(embedding_list) + else: + print(f"Лицо РЅРµ обнаружено РІ {os.path.basename(img_path)}") + embeddings.append([]) + except Exception as e: + print( + f"Ошибка обработки {os.path.basename(img_path)} моделью {model_name}: {str(e)}" + ) + embeddings.append([]) + return embeddings + + +def create_results_table(results): + # Convert results dictionary to DataFrame format + data = { + "Метод": list(results.keys()), + "Рсходный датасет": [results[model]["Рсходный датасет"] for model in results], + "РџРѕРІРѕСЂРѕС‚ РЅР° 45В°": [results[model]["РџРѕРІРѕСЂРѕС‚ РЅР° 45В°"] for model in results], + "РџРѕРІРѕСЂРѕС‚ РЅР° 90В°": [results[model]["РџРѕРІРѕСЂРѕС‚ РЅР° 90В°"] for model in results], + "Рзображение СЃ шумом": [ + results[model]["Рзображение СЃ шумом"] for model in results + ], + } + + results_df = pd.DataFrame(data) + return results_df + + +def main(): + print("Запуск оценки распознавания лиц...") + print(f"Рспользуется эталонное изображение: {reference_image_path}") + print(f"Папка СЃ датасетом: {dataset_folder}") + + results = test_transformations(image_paths) + + print("\nСоздание таблицы результатов...") + results_df = create_results_table(results) + print("\nТаблица результатов:") + print(results_df) + + print("\nСохранение результатов РІ CSV...") + results_df.to_csv("lab-4/task_3_2_results.csv", index=False) + print("Результаты успешно сохранены") + + +if __name__ == "__main__": + main() diff --git a/lab-4/task_3_2_reference.jpg b/lab-4/task_3_2_reference.jpg new file mode 100644 index 0000000000000000000000000000000000000000..ab8e27f52c1513772335c883cdf309855ce8155a Binary files /dev/null and b/lab-4/task_3_2_reference.jpg differ diff --git a/lab-4/task_3_2_results.csv b/lab-4/task_3_2_results.csv new file mode 100644 index 0000000000000000000000000000000000000000..7ca691ba9ffa526942fb5bf797a9c5d81e1b2edd --- /dev/null +++ b/lab-4/task_3_2_results.csv @@ -0,0 +1,10 @@ +Метод,Рсходный датасет,РџРѕРІРѕСЂРѕС‚ РЅР° 45В°,РџРѕРІРѕСЂРѕС‚ РЅР° 90В°,Рзображение СЃ шумом +Facenet,1.0,1.0,1.0,4.0 +VGG-Face,0.0,0.0,0.0,0.0 +Facenet512,1.0,1.0,1.0,4.0 +OpenFace,1.0,1.0,1.0,4.0 +DeepID,0.95,0.95,0.95,3.8 +ArcFace,1.0,1.0,1.0,4.0 +Dlib,1.0,1.0,1.0,4.0 +SFace,1.0,1.0,1.0,3.4 +GhostFaceNet,0.95,0.65,0.5,3.65