Реализация AI-улучшения качества фото (Enhancement) в мобильном приложении
Фотографии с камеры бюджетного смартфона или снятые в условиях плохого освещения — шум, смаз, потеря деталей. Фильтры в стиле «яркость +20, контраст +10» этого не решают. AI-апскейлинг и денойзинг восстанавливают детали, которые классический DSP просто не видит.
Что конкретно делают модели улучшения
Денойзинг — убирает шум матрицы при высоком ISO. Модели типа DnCNN, FFDNet работают на уровне патчей изображения, учатся различать «полезный» градиент (текстура) от шума.
Апскейлинг (Super Resolution) — Real-ESRGAN, ESRGAN, SRCNN восстанавливают пиксели при увеличении. Real-ESRGAN 4x превращает 512×512 в 2048×2048, восстанавливая текстуры волос, ткани, текст. Не «размазывает» как bicubic, а синтезирует детали.
Exposure/HDR коррекция — модели типа Zero-DCE или EnlightenGAN работают со слабоосвещёнными снимками, не вводя артефактов пересветки.
На мобильном устройстве все три запускаются через тот или иной ML-рантайм — вопрос только в том, какую модель и в каком формате.
Запуск Real-ESRGAN на iOS через Core ML
Real-ESRGAN x4 в оригинале — 16.7M параметров, ~2 ГБ RAM при инференсе на полном разрешении. На мобильный не влезет без оптимизации. Решение — тайловый инференс: нарезаем изображение на перекрывающиеся патчи (tile_size=256, overlap=16), обрабатываем по очереди, собираем с feather-blending на стыках.
Конвертируем PyTorch-модель в Core ML через coremltools:
import coremltools as ct
import torch
from realesrgan import RealESRGAN # pretrained model
model = RealESRGAN(device='cpu', scale=4)
model.load_weights('RealESRGAN_x4plus.pth')
model.model.eval()
example_input = torch.zeros(1, 3, 256, 256) # tile size
traced = torch.jit.trace(model.model, example_input)
mlmodel = ct.convert(
traced,
inputs=[ct.ImageType(name="input", shape=(1, 3, 256, 256),
color_layout=ct.colorlayout.RGB)],
compute_precision=ct.precision.FLOAT16, # FP16 для ANE
minimum_deployment_target=ct.target.iOS16
)
mlmodel.save("RealESRGAN_x4_tile256.mlpackage")
FLOAT16 с таргетом iOS16+ означает, что Core ML перекладывает вычисления на ANE (Apple Neural Engine). На iPhone 14 инференс одного тайла 256×256 — около 80–120 мс. Фото 12 Мп (4032×3024) нарезается в ~180 тайлов, но тайлы обрабатываются последовательно, итого — 15–25 секунд. Это нормально для разовой обработки «улучшить фото».
// Загрузка модели
let config = MLModelConfiguration()
config.computeUnits = .all // ANE + GPU + CPU
guard let model = try? RealESRGAN_x4_tile256(configuration: config) else { return }
// Инференс одного тайла
let pixelBuffer = tileImage.toCVPixelBuffer()!
let output = try model.prediction(input: .init(input: pixelBuffer))
let enhancedTile = output.output.cgImage // собираем обратно
Android: TFLite с ESRGAN
На Android — аналогичная схема через TensorFlow Lite. ESRGAN (упрощённая версия под mobile) доступна как .tflite файл размером 3–5 МБ. Запускаем через TFLite Interpreter с GpuDelegate:
val options = Interpreter.Options().apply {
addDelegate(GpuDelegate())
setNumThreads(4)
}
val interpreter = Interpreter(loadModelFile("esrgan_lite.tflite"), options)
val inputBuffer = ByteBuffer.allocateDirect(1 * 256 * 256 * 3 * 4) // FLOAT32
val outputBuffer = ByteBuffer.allocateDirect(1 * 1024 * 1024 * 3 * 4) // x4 output
interpreter.run(inputBuffer, outputBuffer)
GpuDelegate даёт 3–5× ускорение против CPU на большинстве устройств с OpenGL ES 3.1+. На устройствах без GPU делегата (некоторые старые MediaTek) — fallback на NNAPI или CPU с предупреждением о времени обработки.
Денойзинг: когда апскейлинг не нужен
Для денойзинга без изменения разрешения — FFDNet или DRUNet. Легче (1–3 МБ), быстрее. На iOS удобно через VNGenerateImageFeaturePrintRequest + кастомная Core ML модель, либо напрямую через MLModel с CVPixelBuffer входом.
Одна реальная деталь: при конвертации в Core ML важно нормализовать входные данные (0–1 вместо 0–255) и явно указать это в preprocessing модели, иначе модель выдаёт чёрный или засвеченный результат — популярная ошибка при первой конвертации.
UX: как показывать прогресс
Тайловая обработка удобна тем, что можно показывать прогресс-бар: tile N из M готов. Пользователь видит, что приложение работает. При последовательной обработке без UI-обновлений на iOS срабатывает watchdog — приложение кажется завшим и может быть принудительно завершено.
Весь инференс — в фоновом потоке (DispatchQueue.global(.userInitiated) или Task.detached(priority: .userInitiated) для Swift Concurrency). Обновление UI строго на main thread.
Процесс работы
Аудит требований: нужен апскейлинг, денойзинг или всё вместе. Подбор модели под целевые устройства (минимальный поддерживаемый CPU/GPU). Конвертация PyTorch/ONNX → Core ML/TFLite с замерами скорости на реальных девайсах. Реализация тайлового инференса с feather-blending. Интеграция в UI с прогресс-трекингом.
Ориентиры по срокам
Одна платформа, базовая модель (денойзинг или апскейлинг) — 2–3 недели. Обе платформы с несколькими моделями, настройкой качества и сложным feather-blending — 5–8 недель.







