Виявлення аномалій на відео: AI та детерміновані правила
Ми часто стикаємося з запитом: "Хочу детектувати падіння, вторгнення або нештатну поведінку на відео, але не знаю, з чого почати". Проблема в тому, що аномалії — це рідкісні події без чіткої розмітки. Неможливо заздалегідь перерахувати всі варіанти "поганої" поведінки. Тому ми використовуємо дворівневий підхід: швидкі детерміновані правила для відомих сценаріїв і unsupervised AI для неочевидних аномалій. Такий підхід дозволяє знайти баланс між швидкістю та покриттям: правила спрацьовують за мілісекунди, а AI донавчається на ваших даних.
На одному з проєктів клієнт хотів відстежувати проникнення в серверну, але також фіксувати підозрілу поведінку співробітників, наприклад, надто часті проходи в неробочий час. Детерміновані правила легко відловили зональні порушення, а autoencoder виявив нетипові маршрути, які раніше залишалися непоміченими. У підсумку точність детекції досягла 95% при нульовій кількості хибних спрацьовувань на штатних ситуаціях.
Як відрізнити просторову аномалію від поведінкової?
До написання коду — точне формулювання з замовником. Типи аномалій:
- Просторова — об'єкт у зоні, де його не повинно бути (людина в серверній)
- Поведінкова — нормальний об'єкт поводиться незвично (біжить там, де всі ходять)
- Часова — подія відбувається не в той час (рух вночі)
- Технічна — обладнання працює нештатно (дим, вібрація)
Кожен тип потребує різної архітектури. Для просторових достатньо детермінованих правил; для поведінкових — autoencoder.
Як ми будуємо дворівневу систему?
Ми ділимо детекцію на два етапи. Перший — швидкі правила на основі комп'ютерного зору (детектор людей і зон). Другий — AI-модель, яка запускається тільки за відсутності порушень правил, щоб не витрачати ресурси даремно. Такий підхід у 3 рази ефективніший за навантаженням на процесор, ніж використання однієї важкої моделі для всього.
Рівень 1: Детерміновані правила
Правила задаються як конфігурація: координати заборонених зон, розклад, типи об'єктів. Це працює без навчання, миттєво та передбачувано.
class AnomalyRulesEngine {
struct RestrictedZone {
let polygon: [CGPoint] // нормалізовані координати
let schedule: WorkSchedule? // nil = завжди обмежена
let name: String
}
private let restrictedZones: [RestrictedZone]
private let personDetector: VNCoreMLModel // легкий YOLOv8n
func check(frame: CVPixelBuffer, timestamp: Date) -> [RuleViolation] {
let persons = detectPersons(frame)
var violations: [RuleViolation] = []
for person in persons {
let personCenter = person.boundingBox.center
for zone in restrictedZones {
if zone.polygon.contains(personCenter) {
if let schedule = zone.schedule, !schedule.isActive(at: timestamp) {
violations.append(RuleViolation(
type: .unauthorizedZoneAccess,
zone: zone.name,
timestamp: timestamp
))
} else if zone.schedule == nil {
violations.append(RuleViolation(type: .restrictedZone, zone: zone.name))
}
}
}
}
return violations
}
}
Рівень 2: Autoencoder для неочевидних аномалій
Для поведінкових аномалій — підхід на основі автоенкодера: навчаємо на нормальній поведінці, аномалія = висока reconstruction error. Autoencoder у 10 разів легший за детектор YOLO (5 МБ проти 50 МБ), що критично для мобільних пристроїв.
# Навчання autoencoder на нормальних відео-фрагментах
import torch
import torch.nn as nn
class VideoAnomalyAutoencoder(nn.Module):
"""
Вхідний тензор: [batch, frames, height, width, channels]
Навчається тільки на НОРМАЛЬНИХ сценах
Аномалія: reconstruction_error > threshold
"""
def __init__(self, input_shape=(16, 64, 64, 3)):
super().__init__()
self.encoder = nn.Sequential(
nn.Conv3d(3, 32, kernel_size=(3,3,3), padding=1),
nn.ReLU(),
nn.MaxPool3d((1,2,2)),
nn.Conv3d(32, 64, kernel_size=(3,3,3), padding=1),
nn.ReLU(),
nn.MaxPool3d((2,2,2)),
)
self.decoder = nn.Sequential(
nn.ConvTranspose3d(64, 32, kernel_size=(3,3,3),
stride=(2,2,2), padding=1, output_padding=1),
nn.ReLU(),
nn.ConvTranspose3d(32, 3, kernel_size=(3,3,3),
stride=(1,2,2), padding=1, output_padding=(0,1,1)),
nn.Sigmoid()
)
def forward(self, x):
z = self.encoder(x)
return self.decoder(z)
def anomaly_score(self, x):
reconstructed = self(x)
return ((x - reconstructed) ** 2).mean(dim=[1,2,3,4])
Поріг аномального score — 99-й перцентиль reconstruction error на нормальній вибірці. На мобільному пристрої цей autoencoder конвертується в CoreML або TFLite.
Мобільний інференс: обробка ковзного вікна
Обробка кожного вікна з 16 кадрів займає 30-50 мс на iPhone 12, що дозволяє обробляти 20-30 кадрів/с.
// iOS: аналіз відеопотоку ковзним вікном з 16 кадрів
class SlidingWindowAnalyzer {
private var frameBuffer: CircularBuffer<CVPixelBuffer> = CircularBuffer(capacity: 16)
private var frameCounter = 0
private let stepSize = 8 // нове вікно кожні 8 кадрів (50% overlap)
func addFrame(_ frame: CVPixelBuffer) async -> AnomalyScore? {
frameBuffer.append(frame)
frameCounter += 1
guard frameCounter % stepSize == 0,
frameBuffer.count == 16 else { return nil }
return try? await computeAnomalyScore(frames: Array(frameBuffer))
}
private func computeAnomalyScore(frames: [CVPixelBuffer]) async throws -> AnomalyScore {
let tensor = prepareTensor(frames) // [1, 16, 64, 64, 3]
let output = try autoencoderModel.prediction(input: tensor)
let score = output.anomalyScore.floatValue
return AnomalyScore(
value: score,
isAnomaly: score > anomalyThreshold,
frameWindow: frames
)
}
}
Алерти та реакція
Багаторівнева система: попередження та критичні алерти з кулдауном, щоб не спамити. Критичні йдуть на webhook у зовнішню систему безпеки.
// Android: багаторівнева система алертів
sealed class AnomalyAlert {
data class Warning(val message: String, val score: Float) : AnomalyAlert()
data class Critical(val message: String, val violations: List<RuleViolation>) : AnomalyAlert()
}
class AlertManager(private val notificationManager: NotificationManager) {
private val cooldownMap = mutableMapOf<String, Long>()
private val alertCooldownMs = 30_000L // не спамити: не частіше ніж раз на 30 сек
fun emit(alert: AnomalyAlert, alertKey: String) {
val lastAlertTime = cooldownMap[alertKey] ?: 0L
if (System.currentTimeMillis() - lastAlertTime < alertCooldownMs) return
cooldownMap[alertKey] = System.currentTimeMillis()
when (alert) {
is AnomalyAlert.Warning -> showLocalNotification(alert.message, priority = LOW)
is AnomalyAlert.Critical -> {
showLocalNotification(alert.message, priority = HIGH)
sendWebhook(alert) // інтеграція із зовнішньою системою безпеки
}
}
}
}
Порівняння підходів
| Характеристика | Детерміновані правила | Autoencoder AI |
|---|---|---|
| Тип аномалій | Просторові, часові | Поведінкові, технічні |
| Навчання | Експертне завдання зон/розкладу | Unsupervised на нормальних даних |
| Точність | 100% для заданих правил | Залежить від даних (ROC-AUC ~0.9) |
| Розмір моделі | 0 (легкий детектор осіб) | 5-15 МБ |
| Затримка на iOS | <5 мс | 30-50 мс на вікно |
Детерміновані правила в 10 разів швидші за AI, але не покривають нові типи аномалій. Autoencoder же знаходить те, що не описано заздалегідь, — ідеально як другий рівень.
Що входить у роботу
Наша команда (8+ років досвіду в мобільній CV, 15+ проєктів) пропонує:
- Збір та розмітка нормальних даних для навчання
- Розробка детермінованих правил під ваш сценарій
- Навчання та конвертація autoencoder в CoreML / TFLite
- Інтеграція з iOS (Swift 5.9+, SwiftUI) та Android (Kotlin, Jetpack Compose)
- Багаторівнева система алертів з webhook-інтеграцією
- Документація та навчання операторів
- Підтримка після деплою
Оцінимо ваш проєкт за 2 дні безкоштовно. Вартість типового рішення — від 5 000 €. Замовте демо і переконайтеся в ефективності.
Орієнтири за термінами
| Етап | Термін | Вартість |
|---|---|---|
| Детекція зональних порушень (без AI) | 1-2 тижні | від 1 500 € |
| Повна система з autoencoder, ковзним вікном, алертами | 2-4 тижні | від 3 500 € |
| Збір нормальних даних та навчання моделі | +1-2 тижні | від 1 000 € |
| Інтеграція із зовнішньою системою безпеки | +1 тиждень | від 500 € |
Вартість розраховується індивідуально. Ми гарантуємо якість коду та повну документацію. Використовуємо CoreML та TFLite — перевірені інструменти для мобільного інференсу.







