DIY реставрация видео: обучаем нейросеть физике плёнки вместо Topaz AI | AiManual
AiManual Logo Ai / Manual.
28 Дек 2025 Гайд

Как я учил нейросеть физике плёнки: DIY-подход к реставрации видео вместо Topaz AI

Пошаговый гайд по созданию собственной нейросети для реставрации видео с учётом физики плёнки. Альтернатива дорогим Topaz AI и NeatVideo.

Почему Topaz AI — не всегда лучший выбор?

Когда я впервые столкнулся с задачей реставрации архивного видео 80-х годов, моей первой мыслью было использовать Topaz Video AI. Дорогой, но эффективный «чёрный ящик», который обещал волшебное превращение. Однако после нескольких экспериментов я столкнулся с проблемой: нейросеть Topaz отлично справлялась с цифровыми артефактами, но совершенно не понимала физическую природу плёночных дефектов.

Проблема коммерческих решений в их универсальности. Они обучены на смешанных данных и не учитывают специфику конкретных носителей — 8-мм плёнка, VHS, Betamax или ранние цифровые форматы имеют совершенно разные физические артефакты.

Царапины на плёнке, выцветание эмульсии, зернистость серебра, магнитные полосы на VHS — всё это имеет физические причины, которые можно смоделировать. Именно тогда я решил пойти по пути DIY и обучить собственную нейросеть, которая понимала бы не просто «картинку», а физику процесса деградации.

Физика vs. Статистика: почему важно понимать природу дефектов

Большинство современных моделей для реставрации (включая Real-ESRGAN) используют подход «из коробки»: они обучаются на парах «искажённое-чистое» изображение, где искажения генерируются случайным образом. Это работает для общих случаев, но даёт сбой на специфичных артефактах.

💡
Ключевое отличие моего подхода: вместо случайных искажений я моделировал физические процессы деградации конкретных носителей. Например, царапины на плёнке имеют направленность (по движению плёнки в проекторе), а выцветание зависит от химического состава эмульсии и условий хранения.

Это похоже на подход, который используют в прогнозировании временных рядов, где важно понимать не просто паттерны, а underlying процессы. В нашем случае underlying process — это физика и химия старения носителя.

Архитектура решения: от теории к практике

Я построил pipeline, состоящий из трёх ключевых компонентов:

  1. Генератор физически-корректных искажений — Python-скрипт, который имитирует реальные дефекты плёнки
  2. Модифицированная архитектура Real-ESRGAN — с учётом временной согласованности (temporal consistency) для видео
  3. 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.1

4Инференс и постобработка

После получения восстановленных кадров важна правильная постобработка:

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 имеет смысл?

Создание собственной нейросети для реставрации видео — это не просто техническое упражнение. Это осознанный выбор, который имеет смысл когда:

  1. Вы работаете с большим архивом однотипного материала (например, семейная хроника на 8-мм плёнке)
  2. Вам важна максимальная аутентичность, а не просто «чистота» изображения
  3. Коммерческие решения дают неудовлетворительные результаты на ваших специфичных данных
  4. Вы хотите полностью контролировать процесс и иметь возможность тонкой настройки

Как и в случае с созданием движков исполнения на LLM, ключ к успеху — в понимании фундаментальных принципов, а не просто в использовании готовых инструментов.

Физика плёнки — это не магия, а совокупность измеримых процессов. И нейросеть, обученная с учётом этих процессов, способна на удивительные результаты, которые превосходят универсальные коммерческие решения в их специализированной нише.