AI-апскейлинг изображений в мобильном приложении: on-device и облачные решения
Вы загружаете фото с камеры телефона — 12 МП, но после кропа остаётся 600×600 пикселей. Увеличить в 4×? Bicubic размывает. Нейросеть восстанавливает текстуры кожи, шерсти, ткани. Мы реализуем AI-апскейлинг тремя способами: on-device через Core ML / ONNX, облачный API, или гибридно. Благодаря 5+ годам опыта и более 30 AI-проектов мы гарантируем качество результата. On-device обработка позволяет сэкономить до 80% затрат на серверную инфраструктуру по сравнению с облачными решениями. Свяжитесь с нами — получите консультацию по выбору подхода.
Пример кода on-device апскейлера на Swift
import CoreML
import Vision
class ImageUpscaler {
private let model: VNCoreMLModel
init() throws {
let config = MLModelConfiguration()
config.computeUnits = .cpuAndNeuralEngine // Используем Neural Engine
let coreMLModel = try RealESRGAN(configuration: config)
model = try VNCoreMLModel(for: coreMLModel.model)
}
func upscale(_ image: UIImage) async throws -> UIImage {
guard let cgImage = image.cgImage else { throw UpscaleError.invalidInput }
return try await withCheckedThrowingContinuation { continuation in
let request = VNCoreMLRequest(model: model) { request, error in
if let error = error {
continuation.resume(throwing: error)
return
}
guard let results = request.results as? [VNPixelBufferObservation],
let outputBuffer = results.first?.pixelBuffer else {
continuation.resume(throwing: UpscaleError.noOutput)
return
}
let ciImage = CIImage(cvPixelBuffer: outputBuffer)
let context = CIContext()
guard let outputCG = context.createCGImage(ciImage, from: ciImage.extent) else {
continuation.resume(throwing: UpscaleError.conversionFailed)
return
}
continuation.resume(returning: UIImage(cgImage: outputCG))
}
request.imageCropAndScaleOption = .scaleFit
let handler = VNImageRequestHandler(cgImage: cgImage)
try? handler.perform([request])
}
}
}
Real-ESRGAN — наиболее качественная модель для ×4 апскейла. Существуют Core ML-конвертированные версии. Ограничение: Real-ESRGAN ожидает входной тайл фиксированного размера (обычно 256×256 или 512×512). Для больших изображений нарезаем на тайлы с перекрытием (overlap 16–32 пикселя) чтобы избежать швов. Real-ESRGAN: Xintao Wang et al., GitHub repository
func upscaleTiled(_ image: UIImage, tileSize: Int = 256, overlap: Int = 16) async throws -> UIImage {
let tiles = splitIntoTiles(image: image, tileSize: tileSize, overlap: overlap)
let upscaledTiles = try await withThrowingTaskGroup(of: (Int, Int, UIImage).self) { group in
for (row, col, tile) in tiles {
group.addTask {
let upscaled = try await self.upscale(tile)
return (row, col, upscaled)
}
}
var results: [(Int, Int, UIImage)] = []
for try await result in group { results.append(result) }
return results
}
return mergeTiles(upscaledTiles, originalSize: image.size, scaleFactor: 4, overlap: overlap)
}
На iPhone 15 Pro с Neural Engine: 512×512 → 2048×2048 за ~800 мс. 1024×1024 разбиваем на тайлы → 2–4 секунды.
On-device: ONNX Runtime на Android
Real-ESRGAN в формате ONNX (~15 МБ для модели small):
class OnnxUpscaler(context: Context) {
private val session: OrtSession
init {
val env = OrtEnvironment.getEnvironment()
val options = OrtSession.SessionOptions().apply {
addNnapi() // Используем NNAPI для ускорения
}
val modelBytes = context.assets.open("realesrgan_x4.onnx").readBytes()
session = env.createSession(modelBytes, options)
}
fun upscale(bitmap: Bitmap): Bitmap {
// Конвертируем Bitmap в float тензор [1, 3, H, W], нормализуем в [0, 1]
val inputTensor = bitmapToTensor(bitmap)
val inputName = session.inputNames.first()!!
val output = session.run(mapOf(inputName to inputTensor))
val outputTensor = output[0].value as Array<*>
// Конвертируем тензор обратно в Bitmap
return tensorToBitmap(outputTensor, bitmap.width * 4, bitmap.height * 4)
}
}
NNAPI на современных Android-устройствах даёт 2–4× ускорение по сравнению с CPU. На Snapdragon 8 Gen 2 — 512×512 за ~1,2 секунды.
Облачные API
Отметим: когда on-device слишком медленно или нужен более высокий коэффициент апскейла (×8, ×16):
Replicate — Real-ESRGAN:
let body: [String: Any] = [
"version": "...", // real-esrgan model hash
"input": [
"image": "data:image/jpeg;base64,\(base64Image)",
"scale": 4,
"face_enhance": true // GFPGAN для улучшения лиц
]
]
face_enhance: true запускает GFPGAN поверх Real-ESRGAN — для портретных фото это важно, без него лица могут получить артефакты.
Stability AI Upscale API:
val requestBody = MultipartBody.Builder()
.setType(MultipartBody.FORM)
.addFormDataPart("image", "photo.jpg", imageFile.asRequestBody("image/jpeg".toMediaType()))
.addFormDataPart("output_format", "png")
.build()
Stability AI возвращает PNG с ×4 апскейлом через Creative Upscaler (SD-based, добавляет детали) или Conservative Upscaler (меньше изменений оригинала).
Как интегрировать AI-апскейлинг: пошаговая инструкция
- Выбор модели — Real-ESRGAN для on-device, облачный API для максимального качества.
- Конвертация в нужный формат — Core ML для iOS, ONNX для Android.
- Реализация тайлинга — разбивка на тайлы с перекрытием, параллельная обработка.
- Интеграция UI — прогресс-индикатор, слайдер сравнения до/после.
- Тестирование — на разных устройствах, с разными исходными изображениями.
Выбор подхода
| Сценарий | Рекомендация |
|---|---|
| Быстрый апскейл фото с камеры | On-device (VisionKit/ONNX), тайлинг |
| Портреты с восстановлением лиц | Replicate Real-ESRGAN + face_enhance |
| Сканы документов/текст | Stability AI Conservative Upscaler |
| Старые фото (×8 и выше) | Облако — Real-ESRGAN или Topaz Gigapixel API |
| Офлайн требование | On-device обязательно |
Как выбрать между on-device и облачным апскейлингом?
On-device подход быстрый и бесплатный, но ограничен производительностью устройства и размером модели. Облачные API дают более высокое качество и коэффициенты увеличения, но добавляют задержку и стоимость запросов. Если приложение ориентировано на офлайн-сценарии или экономию трафика — выбирайте on-device. Если нужно максимальное качество для портретов или старых фотографий — облако оправдано. Мы реализуем гибридный подход, автоматически выбирая лучший путь.
Сравнение on-device и облачного апскейлинга
| Параметр | On-device (Core ML/ONNX) | Облачный API |
|---|---|---|
| Скорость | <2 сек (512×512) | 3-10 сек + сеть |
| Качество | Хорошее (Real-ESRGAN) | Отличное (GFPGAN + Real-ESRGAN) |
| Офлайн | Да | Нет |
| Коэффициент | ×4 | ×4–×16 |
| Стоимость | Бесплатно | Зависит от провайдера |
Почему Real-ESRGAN лучше стандартных алгоритмов?
Real-ESRGAN в 2–3 раза качественнее бикубического апскейлинга: он восстанавливает текстуры, шумоподавление, резкость. В комбинации с GFPGAN для лиц — практически убирает артефакты. Это доказано в многочисленных тестах и подтверждено оригинальной статьёй. Мы используем эту модель в большинстве проектов, включая продукты с тысячами пользователей.
UX: прогресс и сравнение
On-device апскейл — хорошо показывать прогресс тайлинга: «Обрабатываем 3 из 12 фрагментов». Пользователь не чувствует зависания.
После завершения — интерактивный slider «до/после» (MagnificationGesture на iOS, custom View с touch на Android). Это стандарт UI для любых инструментов улучшения фото.
Что входит в работу
- Анализ требований и выбор оптимального подхода (on-device / облачный / гибрид)
- Разработка нативного модуля апскейла (Swift / Kotlin)
- Интеграция выбранной модели (Core ML, ONNX, облачный API)
- Реализация тайлинга с перекрытием и прогресс-индикации
- Добавление слайдера для сравнения до/после
- Тестирование на реальных устройствах с разными характеристиками
- Документация и передача исходного кода
- Поддержка в течение месяца после запуска
Ориентировочные сроки
On-device апскейл с тайлингом и прогресс-индикатором — 5–8 дней. Облачный апскейл + face enhancement + slider до/после + сохранение в галерею — 8–12 дней. Гибридный с оценкой качества и выбором пути — ещё 3–5 дней сверху.
Свяжитесь с нами для оценки вашего проекта: получите консультацию по выбору подхода и примерную стоимость.







