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

Сначала было пятно. Вернее, сотни пятен на стенах старинного монастыря в окрестностях Перуджи. За несколько веков солнце, влага и человеческое невежество сделали своё дело: лицо святого Петра превратилось в серо-красную кашу, ангелы выгорели до неузнаваемости, а контуры зданий исчезли, как память после тяжёлой пятницы.
С тех пор как я впервые попал в архив Института цифровой реставрации, у меня свербело в мозгу одно: а можно ли доверить восстановление этих изображений алгоритму? Не просто научить его «дорисовывать», а сделать это осмысленно, с уважением к автору и эпохе. Ответ: можно, но придётся попотеть.
1. Сканирование и подготовка изображения
Всё начинается с цифры. Мы получаем фреску в виде многомегапиксельного TIFF-файла. Иногда — даже с LIDAR-данными, если повезло с финансированием. Для работы используется OpenCV и PIL. Первый шаг — привести всё это безобразие в читаемый вид.
# Язык: Python 3 from PIL import Image import cv2 import numpy as np # Загружаем изображение image_path = 'damaged_fresco.tiff' pil_image = Image.open(image_path).convert('RGB') image = np.array(pil_image) # Уменьшаем размер для предпросмотра preview = cv2.resize(image, (1024, 1024)) cv2.imshow("Fresco Preview", preview) cv2.waitKey(0) cv2.destroyAllWindows()
На этом этапе фреска уже выглядит не как грязная стена, а как загадка. Самое время заняться шумом.
2. Удаление шума и артефактов времени
Пятна, трещины, грибок, остатки штукатурки — всё это нужно убрать, но осторожно. Мы не хотим уничтожить след кисти мастера XIV века. Поэтому никакой агрессивной фильтрации. Только bilateral filtering и ручная маска повреждений.
# Фильтрация с сохранением краёв filtered = cv2.bilateralFilter(image, d=9, sigmaColor=75, sigmaSpace=75) # Простой способ маскировать пятна вручную (пока без ML) mask = cv2.inRange(filtered, (0, 0, 0), (50, 50, 50)) cv2.imshow("Damage Mask", mask) cv2.waitKey(0)
На практике приходится постоянно настраивать параметры вручную. Один из «забавных» случаев — когда трещина пересекала радужку глаза святого, и алгоритм упрямо её удалял, оставляя героя слепым.
3. Inpainting: цифровое восстановление утраченного
Тут начинается магия. OpenCV предоставляет две классические функции для inpainting: cv2.INPAINT_TELEA и cv2.INPAINT_NS. Первая работает быстрее, вторая — мягче. В большинстве случаев Telea даёт более «живую» текстуру, что особенно важно в живописи.
inpainted = cv2.inpaint(filtered, mask, 3, cv2.INPAINT_TELEA) cv2.imshow("Restored Fresco", inpainted) cv2.waitKey(0)
Это уже почти искусство: иногда результат получается настолько красивым, что хочется подписать — restored by algorithmic brush of 2025.
4. Углубляемся: GAN для генерации утраченных фрагментов
Для крупных утрат (например, полностью отсутствующего лица) одного Telea недостаточно. Мы использовали генеративно-состязательные сети (GAN), обученные на выборке фресок того же периода.
Модель обучалась на 50 000 патчах из разных музеев Европы. Использовали подход DeepFill v2 с модифицированной encoder-частью.
# Пример использования обученной модели (упрощённый псевдокод) import torch from model import InpaintingModel model = InpaintingModel() model.load_state_dict(torch.load("gan_fresco_model.pth")) model.eval() input_image = preprocess_for_model(inpainted) output = model(input_image) restored_final = postprocess(output)
На выходе — фрагмент, который стилистически соответствует оригиналу. Да, это всё ещё синтетика, но гораздо ближе к реальности, чем белое пятно.
5. Контроль качества: оценка результата
Вот где всё становится субъективным. Мы не можем сравнивать с оригиналом — его нет. Но можем считать метрики по соседним участкам: SSIM, perceptual loss, цветовую согласованность.
from skimage.metrics import structural_similarity as ssim ssim_value = ssim(original_crop, restored_crop, multichannel=True) print(f"SSIM: {ssim_value:.3f}")
И всё равно — финальное слово за искусствоведом. Иногда он говорит: «Хм, нос Петра получился подозрительно похожим на Джастина Бибера». Тогда откатываем и ищем другой подход.
Заключение
Алгоритмы не заменят художника. Но они становятся его соавторами. Python, OpenCV, PyTorch и немного человеческой одержимости — вот инструменты цифрового Рубенса. Мы не создаём, мы воскрешаем. И пусть наш код когда-нибудь тоже кто-то найдёт и отреставрирует.
ссылка на оригинал статьи https://habr.com/ru/articles/914516/
Добавить комментарий