D2
Администратор
- Регистрация
- 19 Фев 2025
- Сообщения
- 4,380
- Реакции
- 0
Что ж, дамы и господа, в последнее время нейронные сети захватывают все больше сфер деятельности, и в данной теме я хочу немного помочь вам разобраться в том, как устроено обучение нейронных сетей, работающих с графикой, а также привести один из примеров того, как можно применить это на практике. В данном случае - в переделывании документов, удостоверяющих личность.
Спойлер: P.S
Код, который будет предоставлен, не претендует на идеальное качество. Он написан с целью привести пример того, как использовать нейронные сети и обучать их. Замена лица также не происходит идеально, так как для этого требуется хорошее обучение. А для хорошего обучения требуются вычислительные мощности и терпение при разметке обучающих материалов. Но обо всем по порядку.
Для начала нам нужно разобраться, как мы будем определять лица на документах для последующей замены. В этом нам поможет библиотека на Python с названием "ultralytics". Она содержит в себе метод компьютерного зрения под названием YOLO. В данный момент мы будем использовать YOLOv8 - это самая последняя и лучшая версия, которая опережает предыдущие в скорости определения объекта и в точности определения объектов.
Теперь следует понять, как обучать нейронную сеть на YOLO. Однако разработчики YOLO уже предоставляют обученные модели, способные успешно определять огромное количество объектов.
Для начала я покажу сам код программы, а затем разберу каждый участок кода подробнее. В конце предоставлю инструкцию о том, как самому обучить нейронную сеть на своих данных.
Спойлер: Полный код нейронной сети
Код: Скопировать в буфер обмена
Код:
import os
from ultralytics import YOLO
from PIL import Image, ImageFilter
# Загрузка обученной модели
model = YOLO("yolov8n.pt")
# Загрузить изображение с лицом, которое вы хотите вставить
insert_image = Image.open("insert_image.png").convert("RGBA")
# Папка с документами
input_folder = "docs"
# Создаем папку для результатов, если она не существует
output_folder = "results"
os.makedirs(output_folder, exist_ok=True)
# Перебор файлов в папке docs
for filename in os.listdir(input_folder):
if filename.endswith(".png") or filename.endswith(".jpg"):
# Загружаем документ
source_image = Image.open(os.path.join(input_folder, filename))
# Определяем объекты на изображении
results = model.predict(source_image)
# Создаем копию исходного изображения для работы
result_img = source_image.copy()
result = results[0]
# Был ли обнаружен объект "person" с уверенностью больше или равной 0.8
person_detected = False
# Перебираем найденные объекты
for box in result.boxes:
class_id = result.names[box.cls[0].item()]
cords = box.xyxy[0].tolist()
cords = [round(x) for x in cords]
conf = round(box.conf[0].item(), 2)
print("Object type:", class_id)
print("Coordinates:", cords)
print("Probability:", conf)
# Если найден объект с идентификатором 0 (person) и уверенность больше или равна 0.8, вставляем другую картинку на те же координаты
if class_id == "person" and conf >= 0.8:
# Получаем координаты объекта person
x_min, y_min, x_max, y_max = cords
# Вычисляем размеры вставляемого изображения
insert_width = x_max - x_min
insert_height = y_max - y_min
# Изменяем размер вставляемого изображения
insert_resized = insert_image.resize((insert_width, insert_height))
# Вставляем изображение на те же координаты
result_img.paste(insert_resized, (x_min, y_min), mask=insert_resized)
# Объект был обнаружен
person_detected = True
# Если объект "person" не был обнаружен, или не был обнаружен с уверенностью меньше 0.8, не сохраняем измененное изображение
if person_detected:
# Применяем размытие к результату
blurred_result_img = result_img.filter(ImageFilter.GaussianBlur(radius=1))
blurred_result_img.save(os.path.join(output_folder, f"blurred_result_{filename}"))
print("Процесс завершен. Результаты сохранены в папке 'results'.")
Теперь будем разбирать этот код по частям:
Спойлер: Объяснение кода
Код: Скопировать в буфер обмена
model = YOLO("yolov8n.pt")
Данную строку всегда нужно указывать первым делом. Она отвечает за загрузку обученной модели, благодаря которой и происходит определение объектов. Можно сказать, это мозг нейронной сети. Если указать yolov8n.pt, то тогда при запуске программы будет подкачиваться готовая обученная модель, которую любезно предоставили разработчики YOLO. Данная модель называется нано-моделью. Из готовых решений от разработчиков есть также YOLOv8s (маленькая модель) и YOLOv8m (средняя модель). Они отличаются количеством данных, на которых были обучены, и соответственно точностью определения. Чем большее количество данных было поглащено, тем сильнее будет нагружаться система при определении нужных нам объектов но и точность определения будет выше. Подходящую модель нужно выбирать под конкретные задачи и мощность железа. Лично я всегда использую yolov8n, так как это легковесная модель, которая достаточно хорошо определяет, при этом не нагружая систему. У остальных же моделей разница в точности определения, по моему мнению, не сильно повышается, зато они потребляют больше ресурсов компьютера, особенно это заметно на слабых системах.
Код: Скопировать в буфер обмена
Код:
# Загружаем документ
source_image = Image.open(os.path.join(input_folder, filename))
# Определяем объекты на изображении
results = model.predict(source_image)
Код: Скопировать в буфер обмена
Код:
class_id = result.names[box.cls[0].item()]
cords = box.xyxy[0].tolist()
cords = [round(x) for x in cords]
conf = round(box.conf[0].item(), 2)
Вторая строка отвечает за извлечение координат прямоугольника, которым выделяется объект.
Третья строка округляет координаты.
Четвертая строка определяет точность определения того или иного объекта. Если проще, то эти данные показывают, насколько нейронная сеть уверена в том, что объект, который она нашла, является тем объектом, которым она его считает.
Код: Скопировать в буфер обмена
if class_id == "person" and conf >= 0.8:
Эта строка устанавливает условие: "если объект является человеком (person) и нейронная сеть определила это с точностью не менее 0.8". Если это условие выполняется, то получаются координаты прямоугольника, в котором находится обнаруженный объект. Затем вычисляются размеры вставляемого изображения (нового лица), которое изменяется до таких размеров, чтобы поместиться внутри прямоугольника, где был найден объект. Затем это новое лицо вставляется на место старого.
Также в коде присутствует добавление размытия на получившуюся новую картинку, чтобы вставленное изображение лица выглядело более естественно. Однако это уже работа другой библиотеки, не связанной с нейронной сетью, а просто инструментом для работы с изображениями.
Спойлер: Полный список объектов и их id
Код: Скопировать в буфер обмена
Код:
{0: 'person',
1: 'bicycle',
2: 'car',
3: 'motorcycle',
4: 'airplane',
5: 'bus',
6: 'train',
7: 'truck',
8: 'boat',
9: 'traffic light',
10: 'fire hydrant',
11: 'stop sign',
12: 'parking meter',
13: 'bench',
14: 'bird',
15: 'cat',
16: 'dog',
17: 'horse',
18: 'sheep',
19: 'cow',
20: 'elephant',
21: 'bear',
22: 'zebra',
23: 'giraffe',
24: 'backpack',
25: 'umbrella',
26: 'handbag',
27: 'tie',
28: 'suitcase',
29: 'frisbee',
30: 'skis',
31: 'snowboard',
32: 'sports ball',
33: 'kite',
34: 'baseball bat',
35: 'baseball glove',
36: 'skateboard',
37: 'surfboard',
38: 'tennis racket',
39: 'bottle',
40: 'wine glass',
41: 'cup',
42: 'fork',
43: 'knife',
44: 'spoon',
45: 'bowl',
46: 'banana',
47: 'apple',
48: 'sandwich',
49: 'orange',
50: 'broccoli',
51: 'carrot',
52: 'hot dog',
53: 'pizza',
54: 'donut',
55: 'cake',
56: 'chair',
57: 'couch',
58: 'potted plant',
59: 'bed',
60: 'dining table',
61: 'toilet',
62: 'tv',
63: 'laptop',
64: 'mouse',
65: 'remote',
66: 'keyboard',
67: 'cell phone',
68: 'microwave',
69: 'oven',
70: 'toaster',
71: 'sink',
72: 'refrigerator',
73: 'book',
74: 'clock',
75: 'vase',
76: 'scissors',
77: 'teddy bear',
78: 'hair drier',
79: 'toothbrush'}
Ну а теперь поговорим о самостоятельном обучении нейронной сети на своих собственных данных, в нашем случае на своих картинках.
Спойлер: Создание датасета
1. Первое что нам нужно так это налутать фотографии которые в последствии будут использованы для разметки а затем для обучения. Просто заходим в гугл картинки и выкачиваем все фотографии такого типа:
2. Теперь нам понадобится зайти на сайт https://app.roboflow.com/ и зарегестрироваться. На главной странице данного сайта нам нужно создать новый проект, нажимаем на кнопку:
3. Затем, в открывшемся окне мы можем ввести абсолютно любое название проекта, а в Annotation Group нам нужно ввести названия объектов, которые мы будем выделять. Можно, конечно, использовать рандомные символы, но при наличии множества объектов можно запутаться.
4. После создания проекта нам потребуется загрузить наши изображения, на которых нейронная сеть в последствии будет обучаться определять лица людей. По моим наблюдениям, для адекватного определения объектов нужно не менее 1500 изображений. Также изображения не должны иметь один и тот же постоянный задний фон. Так же не является вариантом простое размножение одного и того же изображения, так как нейронная сеть деградирует и будет распознавать объект только на том изображении, которое было загружено. Поэтому очень важно иметь разнообразие изображений.
5. После сохранения проекта ждем пока все наши изображения загрузятся на сервера roboflow а затем нажимаем на "Annotate images" и приступаем к самой нудной части всей разработки какой либо нейронной сети - к разметке (после сотни с лишним тысяч размеченных фотографий у меня начинается нервный тик от одной только мысли о том что мне придется делать это снова), можно конечно обойтись и без ручной разметки, благо в интернете да и на том же roboflow есть уже готовые датасеты, но проблема таких датасетов в том что они сделаны на фотографиях ужасного качества и разметка тоже сделана автоматическая, из-за чего она неточная и соответственно результаты обучения будут не утешительными, поэтому всегда делайте разметку вручную
6. Разметка это в принципе не сложно технически, вам просто на просто нужно как в пайнте выделять все нужные вам объекты на фотографии и назначать им классы:
7. После того как вы закончили разметку, время собрать из всего этого датасет нажатием на кнопку Add images to Dataset и распределяем фотографии в таком же процентом соотношении как и на скриншоте ниже:
Что же означают эти ваши "Train", "Valid", "Test" ? Сейчас объясню.
- Train это всегда самый большой набор изображений, именно на них нейронка смотрит разметку и обучается.
- Valid позволяет сравнить то как нейросеть определила самостоятельно и то как на самом деле.
- Test это то на чем нейросеть проверяет свои знания и пытается определить объекты.
9. После нажатия на кнопку откроется меню с редактированием размера изображений, аугментацией и прочим.
Размеры советую ставить квадратными и не очень большими иначе, если изображения будут больших размеров, это скажется на скорости работы софта, 640 на 640 это можно сказать ГОСТ и я его постоянно придерживаюсь.
10. Далее следует меню аугментации, что же это опять такое, спросите вы? А я отвечу - аугментация позволяет увеличить количество изображений для обучения в несколько раз. Она берет ваши изображения и добавляет на них различные помехи, например, блюр, шум, поворот картинки, добавление черных квадратов на фото и т.д. Это позволяет нейронной сети получить большую вариативность в ваших скриншотах, больше различных ситуаций, где нужно определить объект, но с аугментацией также не стоит перебарщивать, т.к. если накидать множество эффектов, объект может быть супер плохо виден или вообще полностью перекрыт каким-либо эффектом, а разметка при этом остается на месте. Соответственно, нейронка запутается и в последствии ее может триггерить на объекты, которых на самом деле нету. Лично я добавляю лишь небольшой поворот фотографий и темные квадратные области.
11. На этом подготовка датасета закончена и осталось его только Скачать