Впровадження генерування аватарів з фотографії з штучним інтелектом в мобільному додатку
Завдання звучить просто: користувач завантажує селфі — програма повертає набір стилізованих аватарів. Насправді це кілька пов'язаних проблем: вибір моделі (Stable Diffusion + LoRA проти спеціалізованих API), управління чергами генерування, прийнятний час очікування та робота з дозволами фото.
Архітектура: чому не варто генерувати на пристрої
Stable Diffusion 1.5 у FLOAT16 важить ~2.5 GB. Apple ML Stable Diffusion Swift package дозволяє запустити на iPhone 14 Pro (Neural Engine + 6 GB RAM). 20 кроків DDIM на 512×512 — близько 8 секунд. Це на топовому пристрої. На iPhone 12 або будь-якому середньобюджетному Android — нереально.
Для генерування аватарів у продакшен-програмі розумний вибір — серверна генерація через спеціалізований сервіс. Варіанти:
| Сервіс | Підхід | Час | Якість |
|---|---|---|---|
| Replicate (SDXL + IP-Adapter) | REST API | 15–40 сек | Висока |
| Fal.ai | REST + WebSocket | 5–15 сек | Висока |
| Leonardo.ai | REST API | 10–30 сек | Дуже висока |
| Astria.ai | Fine-tune + генерування | 10–30 хв (fine-tune) + 15 сек | Максимальна |
Для аватарів «схожих на користувача» найкращий результат дає IP-Adapter або InstantID — вони зберігають риси обличчя без повноцінного fine-tune LoRA. Якщо потрібна максимальна точність (як в Lensa App) — Dreambooth LoRA з 10–20 фото користувача, але це займе 10–20 хвилин обробки.
Мобільний клієнт: флоу завантаження та очікування
Генерування займає час — користувачу потрібен зрозумілий зворотний зв'язок. Архітектура асинхронного флоу:
// iOS: запуск генерування та polling статусу
class AvatarGenerationService {
private let apiClient: APIClient
func generateAvatar(photo: UIImage, style: AvatarStyle) async throws -> [UIImage] {
// 1. Compress + upload фото
let photoData = photo.jpegData(compressionQuality: 0.85)!
let uploadURL = try await apiClient.uploadPhoto(data: photoData)
// 2. Start generation job
let jobId = try await apiClient.startGeneration(
photoURL: uploadURL,
style: style.rawValue,
count: 6
)
// 3. Poll з експоненціальною затримкою
return try await pollJobResult(jobId: jobId)
}
private func pollJobResult(jobId: String) async throws -> [UIImage] {
var delay: TimeInterval = 2.0
for _ in 0..<30 {
try await Task.sleep(nanoseconds: UInt64(delay * 1_000_000_000))
let status = try await apiClient.checkJob(id: jobId)
switch status.state {
case .completed: return try await downloadResults(urls: status.resultURLs)
case .failed: throw AvatarError.generationFailed(status.error)
case .pending, .processing: delay = min(delay * 1.5, 8.0)
}
}
throw AvatarError.timeout
}
}
На Android аналогічно через Kotlin Coroutines + kotlinx.coroutines.delay.
Підготовка фото на клієнті
Якість аватара безпосередньо залежить від вхідного фото. Потрібно валідувати перед відправкою:
- Обличчя детектовано (iOS:
VNDetectFaceRectanglesRequest, Android: ML KitFaceDetector) - Освітлення прийнятне — перевіряємо середнє значення яскравості через
CIAreaAverage - Дозвіл мінімум 512×512
- Одне обличчя в кадрі (якщо кілька — показуємо попередження)
Фото компресуємо до 1024×1024 JPEG 85% перед відправкою — надмірний дозвіл не поліпшує результат, лише збільшує час завантаження та вартість.
Кеширування та галерея результатів
Згенеровані аватари потрібно зберігати. Не варто генерувати повторно при кожному відкритті — дорого. iOS: зберігаємо у FileManager з метаданими у Core Data (стиль, дата, jobId для traceability). Android — Room + FileProvider.
Важливо: якщо програма йде у фон під час генерування — polling переривається. Рішення: зберегти jobId у UserDefaults / SharedPreferences, при наступному відкритті програми перевірити статус незавершених завдань.
Push-сповіщення про готовність
Чекати 20–40 секунд з відкритою програмою — добре. Але якщо користувач згорнув програму — потрібен push. Сервер відправляє FCM/APNs-сповіщення після завершення генерування. На клієнті — UNNotificationAction з deep link у галерею аватарів.
Права та конфіденціальність
Privacy Nutrition Labels в App Store вимагають декларувати збір фотографій. Якщо фото йде на сервер — це тип даних Photos, використання: App Functionality. Обов'язково: явна згода користувача, політика збереження (видалити вихідне фото після генерування), не передавати третім сторонам для навчання без згоди.
На Android з targetSdk 33+ запитуємо READ_MEDIA_IMAGES замість застарілого READ_EXTERNAL_STORAGE.
Терміни
Базовий флоу (завантаження фото, API вклик, polling, показ результатів) — 3–5 днів. З валідацією обличчя, галереєю, push-сповіщеннями та підтримкою кількох стилів — 2–3 тижні. Вартість залежить від платформи (iOS/Android/обох) та вибраного AI-провайдера.







