Реализация AI-классификации фотографий в галерее мобильного приложения
Мы внедряем on-device AI-классификацию фотографий в галерею вашего мобильного приложения. Никакой загрузки фото на сервер — всё работает локально, на iPhone или Android. Это про приватность и скорость. За 5+ лет мы реализовали такие решения для десятков проектов — от стартапов до крупных медиапорталов. Apple Vision Framework и Google ML Kit — основа нашего стека.
Пользователи ожидают, что фотографии автоматически сортируются по категориям, но реализация on-device классификации сталкивается с проблемами производительности, конфиденциальности и точности. Без правильной архитектуры приложение тормозит, батарея садится, а фото остаются неотсортированными. Наш подход решает эти задачи через инкрементальную обработку, батчинг и использование встроенных нейросетевых ускорителей. Наша команда имеет 5 лет опыта в мобильной разработке и реализовала более 20 проектов с on-device ML. Экономия на облачных вычислениях может достигать значительных сумм, а данные пользователей остаются под контролем.
Как оптимизировать классификацию для больших галерей?
Ключевой вызов — не заблокировать главный поток и не перегреть устройство. Работаем через PHFetchResult с инкрементальной обработкой в DispatchQueue.global(qos: .background). Размер батча — 50 фото, между батчами — пауза 0.1 с. Это позволяет избежать CPU throttle в 3–5 раз. Vision на iOS обрабатывает фото в 2-3 раза быстрее, чем ML Kit на Android (5–15 ms против 10–30 ms).
func classifyGallery() {
let fetchOptions = PHFetchOptions()
fetchOptions.sortDescriptors = [NSSortDescriptor(key: "creationDate", ascending: false)]
let allPhotos = PHAsset.fetchAssets(with: .image, options: fetchOptions)
let batchSize = 50
let processingQueue = DispatchQueue(label: "photo.classification", qos: .background)
processingQueue.async {
var offset = 0
while offset < allPhotos.count {
let batch = (offset..<min(offset + batchSize, allPhotos.count))
.map { allPhotos.object(at: $0) }
self.processBatch(assets: batch)
offset += batchSize
Thread.sleep(forTimeInterval: 0.1) // Даём системе отдышаться
}
}
}
On-device классификация: что доступно из коробки
На iOS работает Vision framework с VNClassifyImageRequest. Не нужны сторонние модели — встроенная классификация покрывает 1000+ категорий:
import Vision
func classifyPhoto(cgImage: CGImage, completion: @escaping ([String]) -> Void) {
let request = VNClassifyImageRequest { request, error in
guard let results = request.results as? [VNClassificationObservation] else { return }
// Берём категории с confidence > 0.5
let labels = results
.filter { $0.confidence > 0.5 }
.map { $0.identifier }
completion(labels)
}
try? VNImageRequestHandler(cgImage: cgImage, options: [:]).perform([request])
}
Время инференса — 5–15 ms на фото. На iPhone 13+ Neural Engine обрабатывает всю галерею в 1000 фото за ~15–20 секунд.
На Android: ML Kit ImageLabeler с ImageLabelerOptions:
val labeler = ImageLabeling.getClient(
ImageLabelerOptions.Builder()
.setConfidenceThreshold(0.5f)
.build()
)
labeler.process(InputImage.fromBitmap(bitmap, 0))
.addOnSuccessListener { labels ->
val categories = labels.map { it.text }
// "Dog", "Outdoor", "Sky", "Food", etc.
}
ML Kit поддерживает 400+ категорий без сети.
Сравнение iOS и Android
| Параметр | iOS (Vision) | Android (ML Kit) |
|---|---|---|
| Количество категорий | 1000+ | 400+ |
| Инференс на фото | 5–15 ms | 10–30 ms |
| Кастомные модели | CoreML | TensorFlow Lite |
| Фоновая обработка | DispatchQueue | Coroutines + WorkManager |
Сравнение on-device и серверной классификации
| Параметр | On-device | Серверная |
|---|---|---|
| Приватность | Данные на устройстве | Передача фото на сервер |
| Задержка | Мгновенно | 1–5 сек с учётом сети |
| Стоимость инфраструктуры | Нет | Затраты на GPU/API |
| Работа офлайн | Да | Нет |
Почему on-device ML безопаснее чем облачные решения?
Серверная классификация требует передачи фото, задержки на сеть и затрат на инфраструктуру. On-device подход работает офлайн, мгновенно и безопасно. Мы гарантируем, что пользовательские данные не покидают устройство.
Какие инструменты нужны для кастомных категорий?
Встроенный Vision не всегда покрывает все нужные категории. Для кастомных (например, «рецепт», «скриншот документа», «чек», «виза») — обучаем CreateML модель:
let dataSource = MLImageClassifier.DataSource.labeledDirectories(at: trainingDir)
let model = try MLImageClassifier(trainingData: dataSource)
try model.write(to: modelURL)
Точность на кастомных категориях при 20+ примерах — 85–95%. Модель в CoreML формате — 5–15 MB. Можно доставлять через Core ML Model Deployment без обновления приложения.
Хранение результатов классификации
Результаты сохраняем локально — не на сервер. Core Data с NSPersistentContainer:
// Entity: PhotoClassification
// Attributes: assetLocalIdentifier (String), labels (Transformable: [String]), classifiedAt (Date)
Индекс на assetLocalIdentifier + индекс на labels для быстрого поиска. При 50k фото таблица весит ~5–10 MB.
Типичные ошибки
- Classifying на main thread — самое частое.
VNImageRequestHandler.performвыполняется синхронно. Всегда в background queue. - Запрашивать full-resolution для классификации — лишнее. PHAsset requestImage с
targetSize: CGSize(width: 224, height: 224)достаточно. - Не обновлять классификации при изменении галереи —
PHPhotoLibraryChangeObserverдолжен запускать инкрементальную обработку только для новых/изменённых фото.
Что входит в работу
- Разработка on-device классификации (Vision / ML Kit)
- Интеграция кастомных моделей CoreML / TFLite
- Инкрементальная обработка галереи с батчингом
- Хранение результатов в CoreData / Room
- Тестирование на устройствах (iOS 16+ / Android 12+)
- Документация и обучение команды
- Поддержка после релиза (2 недели)
Сроки и стоимость
Базовая классификация с Vision + хранение в Core Data — 4–6 дней. Полная реализация с кастомными категориями, инкрементальными обновлениями и быстрым поиском по тегам — 2–3 недели. Стоимость рассчитывается индивидуально после аудита вашего проекта.
Закажите разработку AI-галереи — получите консультацию по вашему проекту. Свяжитесь с нами, чтобы оценить возможности для вашего приложения.







