Реализация AI-трекинга объектов в видеопотоке мобильного приложения

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

Разработка и поддержка любых видов мобильных приложений:

Информационные и развлекательные мобильные приложения
Новостные приложения, игры, справочники, онлайн-каталоги, погодные, фитнес и здоровье, туристические, образовательные, социальные сети и мессенджеры, квиз, блоги и подкасты, форумы, агрегаторы
Мобильные приложения электронной коммерции
Интернет-магазины, B2B-приложения, маркетплейсы, онлайн-обменники, кэшбэк-сервисы, биржи, дропшиппинг-платформы, программы лояльности, доставка еды и товаров, платежные системы
Мобильные приложения для управления бизнес-процессами
CRM-системы, ERP-системы, управление проектами, инструменты для команды продаж, учет финансов, управление производством, логистика и доставка, управление персоналом, системы мониторинга данных
Мобильные приложения электронных услуг
Доски объявлений, онлайн-школы, онлайн-кинотеатры, платформы предоставления электронных услуг, платформы кешбека, видеохостинги, тематические порталы, платформы онлайн-бронирования и записи, платформы онлайн-торговли

Это лишь некоторые из типы мобильных приложений, с которыми мы работаем, и каждый из них может иметь свои специфические особенности и функциональность, а также быть адаптированным под конкретные потребности и цели клиента.

Услуги, которые мы предлагаем
Показано 1 из 1Все 1735 услуг
Реализация AI-трекинга объектов в видеопотоке мобильного приложения
Сложный
~1-2 недели
Часто задаваемые вопросы

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

Этапы разработки

Последние работы

  • 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-трекинга объектов в видеопотоке мобильного приложения

Трекинг объектов — отдельная задача от детекции. Детектор говорит «здесь машина» на каждом кадре независимо. Трекер говорит «это та же машина #7, что была на прошлом кадре слева». Потеря идентичности объекта — типичная ошибка при наивном подходе: объект вышел за край кадра и вернулся — трекер присвоил ему новый ID.

Классификация задач трекинга

SOT (Single Object Tracking) — трекинг одного выбранного объекта. Пользователь тапает на объект → приложение следит за ним. Применение: спортивные трансляции, слежение за конкретным человеком в кадре. Алгоритмы: SiamFC, OSTrack, STARK.

MOT (Multi-Object Tracking) — одновременный трекинг всех объектов нужного класса. Применение: подсчёт посетителей, контроль трафика, производственные конвейеры. Алгоритмы: SORT, ByteTrack, StrongSORT, OC-SORT.

MOT: связка детектор + трекер

Стандартный pipeline для мобильных:

// iOS: YOLOv8 детекция + SORT трекинг
class MultiObjectTracker {

    private let detector: YOLOv8Detector
    private let tracker: SORTTracker

    // SORT параметры — важно подобрать под задачу
    init(targetClass: String,
         maxAge: Int = 10,          // кадров без детекции до удаления трека
         minHits: Int = 3,          // кадров детекции для подтверждения трека
         iouThreshold: Float = 0.3) {
        self.detector = YOLOv8Detector(targetClass: targetClass)
        self.tracker = SORTTracker(maxAge: maxAge,
                                   minHits: minHits,
                                   iouThreshold: iouThreshold)
    }

    func processFrame(_ pixelBuffer: CVPixelBuffer) async -> [TrackedObject] {
        // 1. Детекция на текущем кадре
        let detections = await detector.detect(pixelBuffer)

        // 2. Обновление трекера
        let tracks = tracker.update(detections: detections.map { det in
            Detection(bbox: det.boundingBox, confidence: det.confidence)
        })

        // 3. Преобразование в TrackedObject
        return tracks.map { track in
            TrackedObject(
                id: track.trackId,
                boundingBox: track.bbox,
                isConfirmed: track.hitStreak >= tracker.minHits,
                velocity: track.kalmanFilter.velocity  // из Kalman state
            )
        }
    }
}

maxAge = 10 — трек живёт 10 кадров без детекции (объект за препятствием). При 30 FPS это 333 мс — достаточно для большинства случаев кратких окклюзий.

ByteTrack: лучше SORT при окклюзиях

SORT использует только детекции с confidence > порога. ByteTrack использует ВСЕ детекции — в том числе низкоуверенные — для ассоциации с уже существующими треками. Это резко снижает потери трека при окклюзиях:

// Android: ByteTrack ассоциация
class ByteTracker(
    private val trackThresh: Float = 0.5f,
    private val highThresh: Float = 0.6f,
    private val matchThresh: Float = 0.8f
) {
    private val trackedStracks = mutableListOf<STrack>()
    private val lostStracks = mutableListOf<STrack>()

    fun update(detections: List<Detection>): List<STrack> {
        // Разбиваем детекции на high/low confidence
        val highDetections = detections.filter { it.confidence >= highThresh }
        val lowDetections = detections.filter { it.confidence in trackThresh..<highThresh }

        // 1. Ассоциируем high-confidence с активными треками
        val (matches1, unmatched_tracks1, unmatched_dets1) =
            linearAssignment(trackedStracks, highDetections, matchThresh)

        // 2. Ассоциируем low-confidence с незасоченными треками из шага 1
        val (matches2, _, _) =
            linearAssignment(unmatched_tracks1, lowDetections, 0.5f)

        // 3. Инициализируем новые треки для непривязанных high-conf детекций
        val newTracks = unmatched_dets1.map { STrack(it) }

        return (matches1 + matches2).map { it.track } + newTracks
    }
}

SOT: тап-для-трекинга

// iOS: пользователь выбирает объект тапом, приложение следит
class SingleObjectTracker {

    // Используем Vision VNTrackObjectRequest
    private var trackingRequest: VNTrackObjectRequest?

    func initializeTracking(at point: CGPoint, in frame: CVPixelBuffer) {
        let observation = VNDetectedObjectObservation(
            boundingBox: CGRect(center: point, size: CGSize(width: 0.1, height: 0.1))
        )

        trackingRequest = VNTrackObjectRequest(
            detectedObjectObservation: observation
        ) { [weak self] request, _ in
            guard let obs = request.results?.first as? VNDetectedObjectObservation else { return }
            self?.delegate?.didUpdateTracking(boundingBox: obs.boundingBox,
                                              confidence: obs.confidence)
        }

        trackingRequest?.trackingLevel = .accurate  // vs .fast
    }

    func trackInFrame(_ pixelBuffer: CVPixelBuffer) {
        guard let request = trackingRequest else { return }
        let handler = VNImageRequestHandler(cvPixelBuffer: pixelBuffer)
        try? handler.perform([request])
    }
}

trackingLevel = .accurate использует более тяжёлый трекер (CorrelateBased vs Optical Flow). Разница: .fast — 50+ FPS, теряет трек при быстрых движениях. .accurate — 20–30 FPS, устойчивее к быстрым объектам. Выбирать под задачу.

Рендер треков

@Composable
fun TrackingOverlay(
    tracks: List<TrackedObject>,
    imageSize: Size,
    modifier: Modifier = Modifier
) {
    val colors = remember { generateTrackColors(maxTracks = 100) }

    Canvas(modifier = modifier) {
        tracks.forEach { track ->
            val color = colors[track.id % colors.size]
            val rect = track.boundingBox.toScreenRect(imageSize, size)

            // Bounding box
            drawRect(color = color, topLeft = rect.topLeft,
                     size = rect.size, style = Stroke(width = 3f))

            // ID badge
            drawIntoCanvas { canvas ->
                canvas.nativeCanvas.drawText(
                    "ID: ${track.id}",
                    rect.left + 4f,
                    rect.top + 20f,
                    Paint().apply { this.color = color.toArgb(); textSize = 32f }
                )
            }

            // Вектор скорости (опционально)
            if (track.velocity != null) {
                drawLine(
                    color = color.copy(alpha = 0.6f),
                    start = rect.center,
                    end = rect.center + track.velocity.toOffset(scale = 20f),
                    strokeWidth = 2f
                )
            }
        }
    }
}

Ориентиры по срокам

SOT (Vision VNTrackObjectRequest) с тапом для выбора объекта — 2–3 дня. MOT с YOLOv8 + ByteTrack, рендером треков, несколькими классами объектов и поддержкой iOS + Android — 1–2 недели.