Реалізація розпізнавання документів через камеру мобільного застосунку
Відсканувати документ камерою смартфона — завдання, яке виглядає простим, але містить десяток технічних нюансів. Перспективне спотворення, тіні від пальців, блискучі поверхні, дрижання руки при зніманні — все це потрібно обробляти до того, як результат потрапить до користувача.
Обнаруження меж документа
Перший крок — знайти чотири кути документа в кадрі. На iOS з iOS 13+ це робить VisionKit через VNDetectRectanglesRequest:
let request = VNDetectRectanglesRequest { request, error in
guard let results = request.results as? [VNRectangleObservation],
let rect = results.first else { return }
// rect.topLeft, topRight, bottomLeft, bottomRight у нормалізованих координатах [0,1]
DispatchQueue.main.async {
self.overlayView.drawQuadrilateral(observation: rect,
imageSize: self.previewLayer.frame.size)
}
}
request.minimumConfidence = 0.8
request.minimumAspectRatio = 0.5 // фільтруємо вузькі прямокутники
request.quadratureTolerance = 30 // допуск відхилення від прямокутника в градусах
З iOS 16 з'явився VNDocumentCameraViewController — готовий інтерфейс від Apple з автоматичним захопленням, перспективною коригуванням та мультистраничним сканування. Для більшості завдань це оптимальний вибір.
На Android — ML Kit Document Scanner API (beta, доступно через Google Play Services) або OpenCV через NDK для користувацьких рішень.
Перспективна корекція
Після обнаруження чотирьох кутів застосовуємо гомографічну трансформацію — вирівнюємо нахилений документ у прямокутник «як якби знімали зверху». На iOS це CIPerspectiveCorrection із Core Image:
func correctPerspective(image: CIImage, observation: VNRectangleObservation) -> CIImage {
let imageSize = image.extent.size
// Конвертуємо нормалізовані координати Vision в піксельні CIImage
func toPixel(_ point: CGPoint) -> CIVector {
return CIVector(x: point.x * imageSize.width,
y: point.y * imageSize.height)
}
let filter = CIFilter.perspectiveCorrection()
filter.inputImage = image
filter.topLeft = toPixel(observation.topLeft)
filter.topRight = toPixel(observation.topRight)
filter.bottomLeft = toPixel(observation.bottomLeft)
filter.bottomRight = toPixel(observation.bottomRight)
return filter.outputImage ?? image
}
Важливо: система координат CIImage перевернута по Y відносно UIKit — topLeft у Vision це bottomLeft у CIImage. Ця помилка трапляється у 90% перших реалізацій.
Постобробка зображення
Скан документа після геометричної корекції зазвичай вимагає покращення:
Grayscale + посилення контрасту — для розпізнавання тексту, документів для архіву:
let grayscaleFilter = CIFilter.colorControls()
grayscaleFilter.saturation = 0
grayscaleFilter.contrast = 1.3
Адаптивна пороговість — ефект «чорно-білий» як в Adobe Scan. Core Image не має вбудованої адаптивної пороговості, тому використовуємо CIKernel або Metal Compute Shader для обробки блоків 15×15 пікселів.
Покращення документа — на iOS 17+ доступен VNGeneratePersonInstanceMaskRequest, який допомагає прибрати тінь від руки. Для більш ранніх версій — GPUImage3 або користувацький Metal-шейдер для відновлення світлиниці.
Мультистраничне сканування та PDF
Користувач сканує кілька сторінок — вони об'єднуються в один документ. На iOS — PDFDocument + PDFPage із PDFKit:
func createPDF(from images: [UIImage]) -> Data? {
let pdfDocument = PDFDocument()
for (index, image) in images.enumerated() {
guard let page = PDFPage(image: image) else { continue }
pdfDocument.insert(page, at: index)
}
return pdfDocument.dataRepresentation()
}
Розмір PDF важливий: A4 при 300 DPI = ~2500×3500 пх. Для зберігання та передачі стискуємо JPEG всередину PDF з якістю 0.7–0.85. Для OCR-завдань — зберігаємо оригінальну розділення.
Автоматичний захват vs ручний
Автоматичний триггер при обнаруженні документа — хороший UX, але вимагає стабілізації: документ повинен бути в кадрі > 1.5 секунди з достатньою впевненістю перед автозахватом. Занадто агресивний триггер дратує — користувач ще вирівнює телефон, а застосунок уже зробив знімок.
Процес роботи
Визначення сценарію: типи документів (паспорт, чек, договір, мультистраничні матеріали), чи потрібний експорт у PDF, інтеграція OCR.
Реалізація детектора меж та live-preview із підсвітленням знайденого документа.
Перспективна корекція, постобробка зображення.
Мультистраничний режим, експорт у PDF або JPEG.
Тестування у реальних умовах: різне освітлення, різні типи паперу (глянцева, матова, старі документи).
Орієнтири за часом
Базовий сканер із VNDocumentCameraViewController (тільки iOS) — 1–2 дні. Користувацька реалізація з ручною перспективною коригуванням, постобробкою та мультистраничним PDF — 3–5 днів на кожну платформу.







