Реалізація AI-групування фотографій за локаціями у мобільному додатку

TRUETECH займається розробкою, підтримкою та обслуговуванням мобільних додатків iOS, Android, PWA. Маємо великий досвід та експертизу для публікації мобільних додатків до популярних маркетів Google Play, App Store, Amazon, AppGallery та інші.

Розробка та підтримка будь-яких видів мобільних додатків:

Інформаційні та розважальні мобільні програми
Новинки, ігри, довідники, онлайн-каталоги, погодні, фітнес та здоров'я, туристичні, освітні, соціальні мережі та месенджери, квіз, блоги та подкасти, форуми, агрегатори
Мобільні програми електронної комерції
Інтернет-магазини, B2B-додатки, маркетплейси, онлайн-обмінники, кешбек-сервіси, біржі, дропшиппінг-платформи, програми лояльності, доставка їжі та товарів, платіжні системи
Мобільні програми для управління бізнес-процесами
CRM-системи, ERP-системи, управління проектами, інструменти для команди продажів, облік фінансів, управління виробництвом, логістика та доставка, управління персоналом, системи моніторингу даних
Мобільні програми електронних послуг
Дошки оголошень, онлайн-школи, онлайн-кінотеатри, платформи надання електронних послуг, платформи кешбеку, відеохостинги, тематичні портали, платформи онлайн-бронювання та запису, платформи онлайн-торгівлі

Це лише деякі з типів мобільних додатків, з якими ми працюємо, і кожен із них може мати свої специфічні особливості та функціональність, а також бути адаптованим під конкретні потреби та цілі клієнта.

Послуги, які ми пропонуємо
Показано 1 з 1Усі 1735 послуг
Реалізація AI-групування фотографій за локаціями у мобільному додатку
Середній
~5 днів
Часті запитання

Наші компетенції:

Етапи розробки

Останні роботи

  • image_mobile-applications_feedme_467_0.webp
    Розробка мобільного додатка для компанії FEEDME
    792
  • image_mobile-applications_xoomer_471_0.webp
    Розробка мобільного додатку для компанії XOOMER
    671
  • image_mobile-applications_rhl_428_0.webp
    Розробка мобільного додатку для компанії RHL
    1097
  • image_mobile-applications_zippy_411_0.webp
    Розробка мобільного додатку для компанії ZIPPY
    969
  • image_mobile-applications_affhome_429_0.webp
    Розробка мобільного додатку для компанії Affhome
    914
  • image_mobile-applications_flavors_409_0.webp
    Розробка мобільного додатку для компанії FLAVORS
    495

AI-групування фотографій за локаціями в мобільних додатках

Групування за локаціями здається простим — у кожного фото є GPS. Але реальне завдання складніше: координати з розкидом 10–50 метрів потрібно склеїти в "місце" (кафе, парк, пляж), різні поїздки в одне місце — розділити за часом, а тисячі фото без GPS — віднести до локації за контекстом.

Крок 1: CLLocation кластеризація

Більшість фото з сучасних смартфонів містять GPS в EXIF. На iOS читаємо через PHAsset.location:

let fetchOptions = PHFetchOptions()
let photos = PHAsset.fetchAssets(with: .image, options: fetchOptions)
var locationData: [(PHAsset, CLLocation)] = []

photos.enumerateObjects { asset, _, _ in
    if let location = asset.location {
        locationData.append((asset, location))
    }
}

Для Android: ExifInterface з TAG_GPS_LATITUDE / TAG_GPS_LONGITUDE, або MediaStore MediaColumns.LATITUDE (застаріло в API 29+, тепер через ContentResolver запит з MediaStore.Images.Media.LATITUDE).

Кластеризація GPS-координат — знов DBSCAN, але в метричному просторі (відстань гаверсинуса):

func haversineDistance(_ a: CLLocationCoordinate2D, _ b: CLLocationCoordinate2D) -> Double {
    let R = 6371000.0  // метри
    let dLat = (b.latitude - a.latitude) * .pi / 180
    let dLon = (b.longitude - a.longitude) * .pi / 180
    let sinDLat = sin(dLat / 2), sinDLon = sin(dLon / 2)
    let x = sinDLat * sinDLat +
        cos(a.latitude * .pi / 180) * cos(b.latitude * .pi / 180) * sinDLon * sinDLon
    return R * 2 * atan2(sqrt(x), sqrt(1 - x))
}

Радіус кластера eps = 200 метрів добре працює для міської зйомки. Для туристичних поїздок краще 500–1000 метрів.

Розділення за часом: одне місце, різні поїздки

Користувач був у Барселоні тричі. GPS-кластер один, але візити — різні. Розділіть за часовими проміжками в кластері:

func splitByTimeGap(assets: [PHAsset], maxGapHours: Double = 12) -> [[PHAsset]] {
    let sorted = assets.sorted { $0.creationDate! < $1.creationDate! }
    var groups: [[PHAsset]] = [[sorted[0]]]

    for i in 1..<sorted.count {
        let gap = sorted[i].creationDate!.timeIntervalSince(sorted[i-1].creationDate!) / 3600
        if gap > maxGapHours {
            groups.append([sorted[i]])
        } else {
            groups[groups.count - 1].append(sorted[i])
        }
    }
    return groups
}

12-годинний розрив — стандартно. Можна адаптувати: в межах одного міста 6 годин достатньо, для різних днів поїздки — 24 години.

Зворотне геокодування: координати → назва місця

У нас кластер з центроїдом (lat, lon). Потрібна людська назва: "Барселона, Іспанія" або "Центральний парк, Нью-Йорк".

Apple MapKitCLGeocoder().reverseGeocodeLocation(). Безплатно, але обмежено для пакетних запитів. Не перевищуйте 1 запит на секунду.

Google Places API — платно, але повертає назву закладу (кафе, готель), а не просто вулицю:

func reverseGeocode(coordinate: CLLocationCoordinate2D) async throws -> PlaceName {
    let location = CLLocation(latitude: coordinate.latitude, longitude: coordinate.longitude)
    let placemarks = try await CLGeocoder().reverseGeocodeLocation(location)
    guard let placemark = placemarks.first else { throw GeoError.noResult }
    return PlaceName(
        city: placemark.locality,
        country: placemark.country,
        name: placemark.name
    )
}

Кешируйте результати геокодування — одні й ті ж координати можуть зустрічатися у сотень фото. Словник [String: PlaceName] з ключем "\(lat.1)_\(lon.1)" (округлення до 1 знака ≈ 11 км, достатньо для групування по міству).

Фото без GPS: візуальна класифікація

15–30% фото не містять GPS (старі фото, зйомка в приміщенні при відключеній геолокації). Для них — Vision VNClassifyImageRequest + словник категорій з геосемантикою.

Якщо фото класифіковано як "beach", "mountain", "cityscape" — показуйте в секції "Природа" або "Міста" без конкретної адреси. Якщо є часова мітка і рядом є інші фото з GPS — привласніть найближчому кластеру за часом (+/- 1 година).

UI: відображення

Два паттерни для UI:

  • На основі карти: MKMapView з кластерними пінами. Тап на піна — галерея локації
  • На основі списку: сортування за датою, секції = локації. Як Apple Photos "Спогади"

Для map-based: MKClusterAnnotation на iOS нативно підтримує кластеризацію пінів при zoom-out. На Android — Google Maps SDK ClusterManager.

Часові рамки

GPS-кластеризація з зворотним геокодуванням та базовим UI — 1–1,5 тижні. Повна реалізація з розділенням за поїздками, візуальною групуванням фото без GPS та картою — 3–4 тижні. Вартість розраховується індивідуально.