קיבוץ קואורדינטות GPS לאלבומי תמונות עם DBSCAN ו-Vision

מיון ידני של אלפי תמונות לפי מיקום הוא תהליך גוזל זמן ומאמץ, במיוחד כאשר נתוני ה-GPS חלקיים או לא מדויקים. אנחנו מפתחים אלגוריתמים חכמים שמקבצים אוטומטית תמונות לפי מיקום גיאוגרפי ותוכן ויזואלי. הצוות שלנו מספק פתרונות כאלה במתכונת turnkey, תוך הבטחת פעילות אמינה ותמיכה מתמשכת.

פיתוח ותמיכה בכל סוגי היישומים הניידים:

יישומים ניידים למידע ובידור
אפליקציות חדשות, משחקים, מדריכים, קטלוגים מקוונים, אפליקציות מזג אוויר, אפליקציות כושר ובריאות, אפליקציות נסיעות, אפליקציות חינוכיות, רשתות חברתיות ומסנג'רים, חידונים, בלוגים ופודקאסטים, פורומים, אגרגטורים
יישומים ניידים למסחר אלקטרוני
חנויות מקוונות, אפליקציות B2B, מרקטפלייסים, בורסות מקוונות, שירותי קאשבק, בורסות, פלטפורמות דרופשיפינג, תוכניות נאמנות, משלוחי מזון ומוצרים, מערכות תשלום.
יישומים ניידים לניהול תהליכים עסקיים
מערכות CRM, מערכות ERP, ניהול פרויקטים, כלים לצוותי מכירות, ניהול פיננסי, ניהול ייצור, ניהול לוגיסטיקה ומשלוחים, ניהול משאבי אנוש, מערכות ניטור נתונים
יישומים ניידים לשירותים אלקטרוניים
פלטפורמות מודעות מסווגות, בתי ספר מקוונים, בתי קולנוע מקוונים, פלטפורמות שירות אלקטרוניות, פלטפורמות קאשבק, אירוח וידאו, פורטלים נושאיים, פלטפורמות הזמנות ותזמון מקוונות, פלטפורמות מסחר מקוונות

אלה הם רק חלק מסוגי היישומים הניידים שאנו עובדים איתם, ולכל אחד מהם עשויים להיות מאפיינים ופונקציונליות ספציפיים משלו, המותאמים לצרכים ולמטרות הספציפיות של הלקוח.

השירותים שאנו מציעים
מציג 1 מתוך 1כל 1734 השירותים
קיבוץ קואורדינטות GPS לאלבומי תמונות עם DBSCAN ו-Vision
בינוני
~5 ימים

הכישורים שלנו:

שאלות נפוצות

העבודות האחרונות

  • פיתוח אפליקציית מובייל עבור FEEDME
    פיתוח אפליקציית מובייל עבור FEEDME
    971
  • פיתוח אפליקציית מובייל עבור XOOMER
    פיתוח אפליקציית מובייל עבור XOOMER
    868
  • פיתוח אפליקציית מובייל עבור RHL
    פיתוח אפליקציית מובייל עבור RHL
    1288
  • פיתוח אפליקציית מובייל עבור ZIPPY
    פיתוח אפליקציית מובייל עבור ZIPPY
    1143
  • פיתוח אפליקציית מובייל עבור Affhome
    פיתוח אפליקציית מובייל עבור Affhome
    1076
  • פיתוח אפליקציית מובייל עבור חברת FLAVORS
    פיתוח אפליקציית מובייל עבור חברת FLAVORS
    677

קיבוץ קואורדינטות GPS עד למיקום המדויק?

כאשר המשתמשים שלכם מייבאים אלפי תמונות מהטלפון שלהם, מיון ידני לפי מיקום הוא מותרות שאינכם יכולים להרשות לעצמכם. אתם זקוקים לאלגוריתם שיבין היכן כל צילום צולם. 15–30% מהתמונות חסרות קואורדינטות GPS, והשאר מפוזרות עם מרווח שגיאה של עד 50 מטרים. כיצד מקבצים אותן לפי מקומות בפועל? אנו משתמשים בקיבוץ מבוסס צפיפות DBSCAN עם מרחק haversine, גיאוקודינג הפוך, וסיווג סצנה באמצעות Vision API. במשך 5 שנים יישמנו למעלה מ-50 פרויקטים למובייל שבהם השילוב הזה השיג דיוק של 97%. לדוגמה, אפליקציית נסיעות צריכה לשלב אוטומטית צילומים מאותו טיול לאלבומים, אך בשל רעש GPS או קואורדינטות חסרות, זו הופכת למשימה לא טריוויאלית. הגישה שלנו פותרת זאת ביעילות.

בעיות שאנו פותרים

פיזור קואורדינטות. נקודות GPS מאותו מיקום יכולות לסטות ב-10–50 מטרים. DBSCAN עמיד לרעש כזה ואינו דורש ציון מספר הקבוצות מראש. זה מקצץ בעלויות מיון ידני של תמונות.

מיזוג טיולים. אם ביקרתם באותו פארק שלוש פעמים בימים שונים, האלגוריתם חייב להפריד בין הביקורים. לאחר הקיבוץ, אנו ממיינים תמונות לפי זמן ומפצלים לפי סף הניתן להגדרה (ברירת מחדל 12 שעות). זה חוסך עד שבועיים של זמן פיתוח לשילוב מודול כזה.

גיאוגים חסרים. עבור צילומים ללא GPS, אנו משתמשים ב-VNClassifyImageRequest (iOS) או ML Kit (אנדרואיד). אנו מסווגים את הסצנה ומשייכים אותה לקבוצה זמנית אם התמונה צולמה בתוך שעה מקבוצה עם קואורדינטות.

מדוע DBSCAN עדיף על K-means למשימה זו?

K-means דורש ציון מספר הקבוצות מראש ורגיש לחריגים. DBSCAN מוצא אוטומטית קבוצות בצורה שרירותית ומסמן רעש. על נתונים דלילים עם אלפי נקודות, DBSCAN מהיר פי 5 מ-K-means כי הוא אינו מחשב מחדש צנטרואידים.

איסוף נתוני GPS

ב-iOS אנו קוראים מיקום דרך PHAsset.location. באנדרואיד — דרך ExifInterface עם תגי GPS, או MediaStore (בהתחשב בהתיישנות ב-API 29+). חשוב: ב-iOS 14+ עליכם לבקש הרשאה PHPhotoLibrary.requestAuthorization.

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))
    }
}

קיבוץ DBSCAN עם מרחק haversine

כדי לקבץ נקודות אנו משתמשים ב-DBSCAN — אלגוריתם מבוסס צפיפות שאינו דורש ציון מספר הקבוצות מראש. המרחק מחושב באמצעות נוסחת haversine כדי להתחשב בכדוריות כדור הארץ.

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))
}

רדיוס הקבוצה 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)) } } מכוונן למשימה: 200 מטר לעיר, 500–1000 מטר לטיולים תיירותיים. מספר נקודות מינימלי הוא 2. עבור ספריות גדולות (>10,000 תמונות) אנו משתמשים בעיבוד מקבילי דרך 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 ומפצלים לפי פערי זמן.

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.

גיאוקודינג: קואורדינטות → שם מקום

כל קבוצה מקבלת שם קריא באמצעות גיאוקודינג הפוך. לפי תיעוד Apple, CLGeocoder חינמי אך מוגבל ל-1 בקשה/שנייה. Google Places API בתשלום אך מחזיר שמות עסקיים (קפה, מלון). אנו ממליצים להשתמש תחילה ב-CLGeocoder, ולאחר מכן ב-Google אם יש צורך.

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
    )
}

התוצאות נשמרות במטמון במילון עם מפתח DispatchQueue.concurrentPerform המעוגל לדיוק של 1 (דיוק ~11 ק"מ — מספיק לקיבוץ ברמת עיר).

מה לעשות עם תמונות ללא GPS?

עבור תמונות ללא קואורדינטות אנו משתמשים ב-VNClassifyImageRequest (iOS) או מקבילות. אנו מסווגים את הסצנה: חוף, הרים, עיר. אם לא ניתן לשייך תמונה לקבוצה ידועה לפי זמן, אנו מכניסים אותה לקטע נושאי ללא קישור לכתובת.

תרחיש שיטה דיוק לדוגמה
נוף עירוני Vision Classify 85%
חוף / טבע Vision Classify 90%
תמונה עם חותמת זמן ליד קבוצת GPS שיוך זמן (±שעה) 95%

ממשק משתמש: תצוגת מפה ורשימה

אנו מיישמים שני מצבים:

  • מפה: MKMapView עם MKClusterAnnotation ב-iOS, ClusterManager באנדרואיד. נעצים מתקבצים/מתפרקים אוטומטית בזום. עבור SwiftUI, מפה עם נעצים זמינה דרך creationDate.
  • רשימה: קטעים לפי מיקומים, ממוינים לפי תאריך התמונה הראשונה. דומה ל-Memories ב-Apple Photos. עבור ספריות גדולות (>5000 תמונות) אנו משתמשים ב-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 } לגלילה חלקה.
דוגמה להגדרת רדיוס קיבוץ
  • עיר: 200 מ', minPoints=2
  • פארק: 300 מ', minPoints=3
  • טיול: 500–1000 מ', minPoints=2
  • פיצול לפי זמן: 6 שעות (עיר), 12 שעות (ברירת מחדל), 24 שעות (טיול)

תהליך ולוחות זמנים

  1. ניתוח — לימוד נתוני המקור, מבנה EXIF, דרישות ממשק משתמש.
  2. עיצוב — בחירת מודלי קיבוץ, גיאוקודר, סכמת מטמון.
  3. יישום — כתיבת מודולים לאיסוף, קיבוץ, גיאוקודינג, סיווג.
  4. בדיקות — אימות על ספריות תמונות אמיתיות (עד 10,000 צילומים).
  5. השקה — שילוב באפליקציה, הגדרת TestFlight / Firebase Distribution.
מודול לוח זמנים
קיבוץ בסיסי + גיאוקודינג + רשימה 1–1.5 שבועות
גרסה מלאה עם מפה, פיצול טיולים וסיווג לתמונות ללא GPS 3–4 שבועות

התמחור מחושב באופן אישי. צרו קשר להערכת פרויקט. אנו מבטיחים תוצאות מגובות בהסמכות Apple ו-Google וחמש שנות ניסיון. קבלו ייעוץ כבר עכשיו.

מה כלול בעבודה

  • קוד מקור של מודולים ב-Swift / Kotlin עם הערות.
  • הגדרת מטמון וספי קיבוץ.
  • שילוב עם ממשק משתמש קיים (מפה או רשימה).
  • תיעוד API והגדרות.
  • תמיכה טכנית למשך שבועיים לאחר המסירה.

צרו קשר — נמצא את הפתרון האופטימלי לפרויקט שלכם.