Почему Topaz AI — не всегда лучший выбор?
Когда я впервые столкнулся с задачей реставрации архивного видео 80-х годов, моей первой мыслью было использовать Topaz Video AI. Дорогой, но эффективный «чёрный ящик», который обещал волшебное превращение. Однако после нескольких экспериментов я столкнулся с проблемой: нейросеть Topaz отлично справлялась с цифровыми артефактами, но совершенно не понимала физическую природу плёночных дефектов.
Проблема коммерческих решений в их универсальности. Они обучены на смешанных данных и не учитывают специфику конкретных носителей — 8-мм плёнка, VHS, Betamax или ранние цифровые форматы имеют совершенно разные физические артефакты.
Царапины на плёнке, выцветание эмульсии, зернистость серебра, магнитные полосы на VHS — всё это имеет физические причины, которые можно смоделировать. Именно тогда я решил пойти по пути DIY и обучить собственную нейросеть, которая понимала бы не просто «картинку», а физику процесса деградации.
Физика vs. Статистика: почему важно понимать природу дефектов
Большинство современных моделей для реставрации (включая Real-ESRGAN) используют подход «из коробки»: они обучаются на парах «искажённое-чистое» изображение, где искажения генерируются случайным образом. Это работает для общих случаев, но даёт сбой на специфичных артефактах.
Это похоже на подход, который используют в прогнозировании временных рядов, где важно понимать не просто паттерны, а underlying процессы. В нашем случае underlying process — это физика и химия старения носителя.
Архитектура решения: от теории к практике
Я построил pipeline, состоящий из трёх ключевых компонентов:
- Генератор физически-корректных искажений — Python-скрипт, который имитирует реальные дефекты плёнки
- Модифицированная архитектура Real-ESRGAN — с учётом временной согласованности (temporal consistency) для видео
- Post-processing pipeline — восстановление цветовых профилей и устранение остаточных артефактов
| Тип дефекта | Физическая причина | Как моделировали |
|---|---|---|
| Царапины | Механический контакт с направляющими | Линейные фильтры с заданным углом + вариация глубины |
| Выцветание | Окисление красителей эмульсии | Канальное искажение в LAB-пространстве |
| Зернистость | Случайное распределение частиц серебра | Perlin-шум с grain-паттернами реальной плёнки |
| Магнитные полосы (VHS) | Намагничивание головок | Горизонтальные полосы с цветовым смещением |
1Подготовка данных: создаём реалистичный датасет
Вместо того чтобы искать пары «испорченное-чистое» видео (что практически невозможно для архивных материалов), я использовал современные HD-видео в качестве «чистых» источников и программно накладывал на них физически корректные искажения.
import numpy as np
import cv2
from perlin_noise import PerlinNoise
class FilmDegradationSimulator:
def __init__(self, film_type="8mm"):
self.film_type = film_type
# Параметры для разных типов плёнки
self.params = {
"8mm": {"scratch_angle": 5, "grain_size": 0.8},
"16mm": {"scratch_angle": 3, "grain_size": 0.5},
"VHS": {"scratch_angle": 0, "color_bleed": 0.7}
}
def add_scratch(self, image, intensity=0.3):
"""Добавляет царапины с учётом направления движения плёнки"""
h, w = image.shape[:2]
angle = self.params[self.film_type]["scratch_angle"]
# Создаём маску царапин с заданным углом
y_coords, x_coords = np.indices((h, w))
mask = np.sin(x_coords * 0.05 + y_coords * angle * 0.01)
mask = (mask > 0.9).astype(np.float32) * intensity
# Для цветных царапин (разные каналы повреждаются по-разному)
if len(image.shape) == 3:
for c in range(3):
image[:,:,c] = np.clip(image[:,:,c] * (1 - mask * (0.7 + c*0.1)), 0, 1)
return imageВажно: я использовал реальные образцы деградировавшей плёнки для калибровки параметров симулятора. Это обеспечивало физическую корректность — например, царапины на 8-мм плёнке действительно идут под углом 5-7 градусов из-за конструкции проектора.
2Модификация Real-ESRGAN для видео
Стандартный Real-ESRGAN работает с отдельными кадрами, что для видео приводит к мерцанию и временной несогласованности. Я добавил temporal module, который анализирует несколько последовательных кадров.
import torch
import torch.nn as nn
class TemporalConsistencyModule(nn.Module):
"""Модуль для обеспечения временной согласованности между кадрами"""
def __init__(self, in_channels=64):
super().__init__()
self.flow_net = nn.Sequential(
nn.Conv2d(in_channels*2, 128, 3, padding=1),
nn.ReLU(),
nn.Conv2d(128, 64, 3, padding=1),
nn.ReLU(),
nn.Conv2d(64, 2, 3, padding=1) # optical flow (dx, dy)
)
def forward(self, prev_features, curr_features):
"""Вычисляет optical flow между feature maps"""
# Объединяем фичи предыдущего и текущего кадра
combined = torch.cat([prev_features, curr_features], dim=1)
flow = self.flow_net(combined)
# Warp предыдущие фичи с учётом flow
warped = self.warp_features(prev_features, flow)
# Смешиваем с текущими фичами
return 0.7 * curr_features + 0.3 * warped
def warp_features(self, features, flow):
"""Деформация feature map согласно optical flow"""
# Реализация spatial transformer network
# ... (пропущено для краткости)
return warped_featuresЭтот подход похож на методы, используемые в обработке последовательностей в RAG-системах, где важна контекстуальная согласованность.
3Обучение на специфичных данных
Вместо обучения на общем датасете DIV2K, я создал специализированный датасет, отражающий именно мои нужды:
- 1000+ пар кадров с физически корректными искажениями
- Разделение по типам носителей: 8mm, 16mm, VHS, ранние DV
- Вариация степени деградации: от лёгкого выцветания до сильных повреждений
- Временные последовательности для обучения temporal module
# Команды для обучения
python basicsr/train.py -opt options/train/RealESRGAN/train_RealESRNet_x4plus_film.yml
# Специфичная конфигурация для плёнки:
datasets:
train:
name: FilmDegradationDataset
type: FilmPairedDataset
dataroot_gt: ./datasets/clean_frames
dataroot_lq: ./datasets/degraded_frames
io_backend:
type: disk
gt_size: 256
use_flip: true
use_rot: true
# Увеличиваем вес loss для сохранения деталей
losses:
pixel:
type: L1Loss
loss_weight: 1.0
reduction: mean
perceptual:
type: PerceptualLoss
layer_weights:
'conv5_4': 1.0 # Более глубокие слои для сохранения структуры
vgg_type: vgg19
loss_weight: 0.14Инференс и постобработка
После получения восстановленных кадров важна правильная постобработка:
def film_specific_postprocess(restored_frames, film_type):
"""Постобработка с учётом характеристик плёнки"""
# 1. Восстановление цветового профиля
if film_type == "Kodachrome":
# Специфичная цветовая коррекция для Kodachrome
restored_frames = apply_kodachrome_lut(restored_frames)
elif film_type == "VHS":
# Устранение цветового bleeding характерного для VHS
restored_frames = reduce_color_bleeding(restored_frames)
# 2. Добавление лёгкой зернистости для естественности
# (полное устранение grain выглядит неестественно)
grain = generate_film_grain(restored_frames.shape, film_type)
restored_frames = np.clip(restored_frames + grain * 0.03, 0, 1)
# 3. Temporal smoothing для устранения остаточного мерцания
restored_frames = temporal_median_filter(restored_frames, window_size=3)
return restored_framesРезультаты: сравнение с Topaz Video AI
После 3 недель обучения на RTX 3090 я получил модель, которая на моих специфичных архивных материалах показала себя лучше Topaz AI:
| Критерий | Topaz Video AI | Моя DIY-модель |
|---|---|---|
| Сохранение плёночной текстуры | Частично (over-smooth) | Отлично |
| Удаление царапин | Хорошо, но с артефактами | Отлично (понимает направление) |
| Временная согласованность | Средне (мерцание на fast motion) | Отлично (temporal module) |
| Цветовое восстановление | Универсальное | Адаптировано под тип плёнки |
| Стоимость | $299 + подписка | $0 (кроме электричества) |
Важное замечание: моя модель специализирована под конкретные типы плёнки. Для универсального использования (разные форматы в одном проекте) Topaz всё ещё может быть удобнее. Но для архивных проектов с однотипным материалом DIY-подход выигрывает.
Практические советы и возможные ошибки
Если вы решите повторить этот эксперимент, вот что нужно учесть:
- Начинайте с малого: не пытайтесь сразу охватить все типы плёнки. Начните с одного (например, 8mm) и добейтесь хороших результатов.
- Качество данных важнее количества: 500 хорошо смоделированных пар лучше, чем 5000 случайных.
- Валидация на реальных данных: обязательно тестируйте на реальных фрагментах плёнки, а не только на синтетических.
- Вычислительные ресурсы: обучение требует серьёзных GPU. Можно использовать облака или, как в гайде по RAG на слабом железе, оптимизировать под доступные ресурсы.
FAQ: частые вопросы
Сколько времени нужно на обучение?
Для базовой модели на одном типе плёнки: 2-3 дня на RTX 3090 (около 100k итераций). Для продвинутой модели с temporal module: 5-7 дней.
Можно ли использовать pre-trained веса Real-ESRGAN?
Да, и это рекомендуется. Используйте transfer learning: загрузите pre-trained модель и дообучите на своих данных. Это ускорит сходимость в 3-5 раз.
Как интегрировать такую модель в продакшн?
Для батч-обработки архивов можно использовать Python-скрипты. Для интерактивного использования рекомендую посмотреть гайд по интеграции ML-моделей в продакшн.
Что делать, если нет GPU для обучения?
Можно использовать Google Colab Pro или облачные инстансы (AWS, GCP). Также можно начать с более лёгких архитектур (ESRGAN вместо Real-ESRGAN).
Заключение: когда DIY имеет смысл?
Создание собственной нейросети для реставрации видео — это не просто техническое упражнение. Это осознанный выбор, который имеет смысл когда:
- Вы работаете с большим архивом однотипного материала (например, семейная хроника на 8-мм плёнке)
- Вам важна максимальная аутентичность, а не просто «чистота» изображения
- Коммерческие решения дают неудовлетворительные результаты на ваших специфичных данных
- Вы хотите полностью контролировать процесс и иметь возможность тонкой настройки
Как и в случае с созданием движков исполнения на LLM, ключ к успеху — в понимании фундаментальных принципов, а не просто в использовании готовых инструментов.
Физика плёнки — это не магия, а совокупность измеримых процессов. И нейросеть, обученная с учётом этих процессов, способна на удивительные результаты, которые превосходят универсальные коммерческие решения в их специализированной нише.