ANPR/LPR Розпізнавання номерних знаків у мобільних застосунках
ANPR (Automatic Number Plate Recognition) на мобільному пристрою—зріла задача з зрозумілими інструментами. Основна інженерна проблема не в самому OCR, а в конвеєрі від захоплення кадру до надійного прочитаного номера: детекція пластини в реальних умовах (ніч, брудь, відблиски, нестандартні шрифти СНД).
Вибір підходу
Два шляхи в залежності від вимог:
On-device—для задач з високою частотою сканувань (паркінг, пропускний пункт) або без гарантованого інтернету. Моделі: OpenALPR (open source, підтримує 60+ країн, включаючи RU/BY/UA), Plate Recognizer Edge SDK, Google ML Kit Text Recognition v2 (для простих стандартних номерів).
Cloud API—Plate Recognizer API, OpenALPR Cloud, AWS Rekognition. Точніші на складних кейсах (нестандартний кут, нечіткий шрифт), краще справляються з різними регіонами СНД.
// iOS: on-device ANPR через Vision + кастомна YOLOv8 для детекції пластини
class LicensePlateRecognizer {
// Крок 1: детекція пластини через CoreML (YOLOv8n—швидкий варіант)
private let plateDetector: VNCoreMLModel
// Крок 2: OCR через Vision Text Recognition
private func recognizeText(in croppedImage: CGImage,
completion: @escaping (String?) -> Void) {
let request = VNRecognizeTextRequest { request, _ in
let text = (request.results as? [VNRecognizedTextObservation])?
.compactMap { $0.topCandidates(1).first?.string }
.joined()
completion(text)
}
request.recognitionLevel = .accurate
request.usesLanguageCorrection = false // відключити—номери не слова
request.minimumTextHeight = 0.1
try? VNImageRequestHandler(cgImage: croppedImage).perform([request])
}
func recognize(sampleBuffer: CMSampleBuffer) async -> PlateResult? {
guard let pixelBuffer = CMSampleBufferGetImageBuffer(sampleBuffer) else { return nil }
// Детектуємо пластину
let plateBBox = await detectPlate(in: pixelBuffer)
guard let bbox = plateBBox else { return nil }
// Crop та OCR
let croppedCGImage = cropCGImage(pixelBuffer: pixelBuffer, rect: bbox)
let rawText = await recognizeTextAsync(croppedCGImage)
// Нормалізація й валідація
return normalizePlate(rawText)
}
}
Нормалізація номерів СНД
OCR дає сирий текст. Для СНД-номерів важлива постобробка:
struct PlateNormalizer {
// Заміна візуально подібних символів (типові помилки OCR)
static let ocrCorrections: [Character: Character] = [
"0": "O", // іноді навпаки
"1": "I",
"8": "B",
]
// Паттерни для різних країн
static let patterns: [(country: String, regex: String)] = [
("RU", #"^[АВЕКМНОРСТУХ]{1}\d{3}[АВЕКМНОРСТУХ]{2}\d{2,3}$"#),
("BY", #"^\d{4}[ABCEHIKMOPTX]{2}-\d{1}$"#),
("UA", #"^[АВСЕКМНРОТХBCEKMNOPTX]{2}\d{4}[АВСЕКМНРОТХBCEKMNOPTX]{2}$"#),
("KZ", #"^\d{3}[A-Z]{3}\d{2}$"#)
]
func normalize(_ rawText: String) -> PlateResult? {
let cleaned = rawText.uppercased()
.replacingOccurrences(of: " ", with: "")
.replacingOccurrences(of: "-", with: "")
for (country, pattern) in Self.patterns {
if cleaned.range(of: pattern, options: .regularExpression) != nil {
return PlateResult(text: cleaned, country: country, confidence: .high)
}
}
// Не совпало з паттернами—low confidence, повернути як є
return PlateResult(text: cleaned, country: nil, confidence: .low)
}
}
Режим безперервного відеопотоку
Для паркінгу або КПП—безперервне сканування кадрів без натиску кнопки:
// Android: CameraX + безперервний аналіз через ImageAnalysis
class ContinuousPlateAnalyzer(
private val onPlateDetected: (PlateResult) -> Unit
) : ImageAnalysis.Analyzer {
private val frameThrottler = FrameThrottler(maxFps = 5) // 5 кадрів/сек достатньо
private val consecutiveMatchThreshold = 3 // 3 підряд однакових
private val recentResults = ArrayDeque<String>(maxOf = 5)
override fun analyze(image: ImageProxy) {
if (!frameThrottler.shouldProcess()) { image.close(); return }
val bitmap = image.toBitmap()
val result = plateRecognizer.recognize(bitmap)
image.close()
result?.let { plate ->
recentResults.add(plate.text)
if (recentResults.size >= consecutiveMatchThreshold &&
recentResults.takeLast(consecutiveMatchThreshold).all { it == plate.text }) {
onPlateDetected(plate)
recentResults.clear()
}
}
}
}
Поріг 3 послідовних однакових результатів усуває ложні спрацьовування на випадкових об'єктах, схожих на пластину.
Орієнтири за часом
On-device ANPR з Vision/ML Kit, нормалізацією для однієї країни й базовим UI—3–5 днів. Мультистрановая система з підтримкою RU/BY/UA/KZ, безперервним відеоаналізом, історією сканувань, інтеграцією з зовнішньою базою (база угнаних авто, база клієнтів) й iOS + Android—1–2 тижні.







