Реализация AI-стилизации фотографий в мобильном приложении
Neural Style Transfer на мобиле — это не просто «прогнать через Core ML». Основная боль: модель класса VGG-19 с полными весами весит 500+ MB, а NST в реальном времени требует GPU-ускорения. На iPhone 12 без Metal Performance Shaders обработка одного кадра 512×512 займёт 3–4 секунды. Пользователь это не примет.
Два архитектурных пути — и почему чаще выбирают второй
Серверная обработка через API (Replicate, Stability AI, собственный бэкенд с PyTorch) — самый простой путь. Фото уходит на сервер, возвращается стилизованный результат. Latency — от 3 до 15 секунд в зависимости от очереди и размера. Подходит для разовой обработки в редакторе, не подходит для видеопотока.
On-device через CoreML / TFLite — нужна дистиллированная модель. Вместо полного NST берём Fast Neural Style Transfer (Johnson et al.) с MobileNet-backbone. Размер модели — 6–8 MB, время инференса на iPhone 12 Neural Engine — 80–120 ms на кадр 512×512. Под Android через TFLite с делегатом GPU — сопоставимые показатели на Snapdragon 870+.
На практике разумен гибридный подход: on-device для preview в реальном времени (уменьшенное разрешение 256×256), серверная обработка для финального экспорта в 4K.
Как готовим модель для мобильного деплоя
Стандартный PyTorch-чекпоинт нельзя взять и скормить Core ML Tools. Цепочка:
- Обучаем или берём готовый Fast NST в PyTorch (torchvision.models или кастомный)
-
torch.onnx.export→ ONNX граф -
coremltools.convert(onnx_model, compute_precision=ct.precision.FLOAT16)→.mlpackage - Тестируем на симуляторе через
MLModel.prediction(from:)
FLOAT16 квантизация даёт 2× уменьшение размера без заметной потери качества. INT8 — ещё меньше, но артефакты на текстурах становятся видны.
Для Android: tf.lite.TFLiteConverter.from_keras_model() → .tflite, затем post-training quantization с DEFAULT или FLOAT16 стратегией.
Интеграция в iOS
import CoreML
import Vision
class StyleTransferProcessor {
private let model: VNCoreMLModel
init() throws {
let mlModel = try FastNST(configuration: MLModelConfiguration()).model
model = try VNCoreMLModel(for: mlModel)
}
func process(image: CGImage, completion: @escaping (CGImage?) -> Void) {
let request = VNCoreMLRequest(model: model) { req, _ in
guard let obs = req.results?.first as? VNPixelBufferObservation else {
completion(nil); return
}
let ciImage = CIImage(cvPixelBuffer: obs.pixelBuffer)
completion(CIContext().createCGImage(ciImage, from: ciImage.extent))
}
request.imageCropAndScaleOption = .scaleFill
try? VNImageRequestHandler(cgImage: image).perform([request])
}
}
Metal Performance Shaders задействуются автоматически через Neural Engine — не нужно писать шейдеры вручную.
Управление памятью и батареей
Самая частая ошибка — держать модель загруженной всё время жизни приложения. На устройствах с 3 GB RAM (iPhone SE 2nd gen, бюджетные Android) это провоцирует jetsam-килл при переходе в бэкграунд. Правило: MLModel инициализируется ленькво, при первом обращении, и выгружается если пользователь не работал с фичей 10 минут.
Батарея: NST — это Neural Engine под нагрузкой. Для live preview ограничиваем частоту инференса до 10–15 fps через CADisplayLink с preferredFramesPerSecond. Полный realtime 30 fps на iPhone 14 Pro реален, но разряжает батарею заметно быстрее.
Проблема с большими разрешениями
CoreML модели принимают фиксированный input shape. Если модель обучена на 512×512, а пользователь загрузил фото с iPhone 14 Pro Max (48 MP, 8064×6048) — нужно downscale перед инференсом и upscale стилизованного результата обратно. Простой UIImage(cgImage:) resize работает, но теряет детали.
Лучше: Guided Upsample через MPSImageBilinearScale или Real-ESRGAN для апскейла результата. Это добавляет шаг обработки, но финальное фото выглядит достоверно даже в 4K.
Серверный путь: Replicate и собственный бэкенд
Если не хочется возиться с Core ML, Replicate API даёт доступ к Neural Style Transfer моделям:
POST https://api.replicate.com/v1/predictions
Authorization: Token <key>
{
"version": "<model_version_hash>",
"input": {
"content_image": "<base64_or_url>",
"style_image": "<base64_or_url>",
"output_image_size": 1024
}
}
Polling статуса через GET /v1/predictions/{id} каждые 2 секунды. Обычно результат готов за 8–20 секунд. Из мобильного клиента — только через backend-proxy (API ключ на клиенте хранить нельзя).
Сроки
On-device интеграция готовой модели (CoreML или TFLite) — 3–5 дней. Полный цикл с подбором/обучением модели, квантизацией, live preview и серверным экспортом — 2–4 недели. Стоимость рассчитывается индивидуально после уточнения требований по платформе и качеству.







