AI Data Extraction from Invoices and Acts: LayoutLM, TrOCR, Validation
We've encountered situations where accounting spends up to 40% of time manually entering invoices, and during INN verification we find typos and discrepancies. A single typo in the INN can lead to tax issues and financial losses. Waybills (TTN, CMR), goods invoices (TORG-12), and work completion certificates—all have rigid structures but large variability in filling: handwritten fields, seals over text, low-quality scans, mixed filling (part typed, part handwritten). Automating this process with AI reduces employee workload and eliminates accounting errors. We developed a solution based on LayoutLMv3, TrOCR, and custom validators that extracts data from any invoice.
How We Solve the Problem of Data Extraction from Waybills
We use a combination of LayoutLMv3 for document layout recognition, separate OCR for printed and handwritten text, and requisite validation. Before feeding into models, images undergo preprocessing: binarization, deskewing, noise removal. This improves recognition accuracy on low-quality scans by 5–7%. For robustness to scanning defects, we apply augmentation: random rotation, blur, contrast changes. As noted in official LayoutLMv3 documentation, the model achieves F1 >0.95 on tabular documents, confirming its suitability for our task. This yields >99% accuracy on standard forms and up to 95% on handwritten samples after fine-tuning.
NER Task with LayoutLM for Waybills
A waybill is a tabular document: header (party details), product table, signatures. LayoutLMv3 handles this via token classification, considering text coordinates.
from transformers import LayoutLMv3Processor, LayoutLMv3ForTokenClassification from datasets import Dataset import torch # Full set of labels for TORG-12 / TTN WAYBILL_LABELS = [ 'O', 'B-DOC_NUMBER', 'I-DOC_NUMBER', 'B-DOC_DATE', 'I-DOC_DATE', 'B-SENDER_NAME', 'I-SENDER_NAME', 'B-SENDER_INN', 'I-SENDER_INN', 'B-SENDER_ADDRESS', 'I-SENDER_ADDRESS', 'B-RECEIVER_NAME', 'I-RECEIVER_NAME', 'B-RECEIVER_INN', 'I-RECEIVER_INN', 'B-RECEIVER_ADDRESS', 'I-RECEIVER_ADDRESS', 'B-CARRIER_NAME', 'I-CARRIER_NAME', 'B-VEHICLE_REG', 'I-VEHICLE_REG', # vehicle plate number 'B-ITEM_NAME', 'I-ITEM_NAME', 'B-ITEM_QTY', 'I-ITEM_QTY', 'B-ITEM_UNIT', 'I-ITEM_UNIT', 'B-ITEM_PRICE', 'I-ITEM_PRICE', 'B-ITEM_TOTAL', 'I-ITEM_TOTAL', 'B-TOTAL_QTY', 'I-TOTAL_QTY', 'B-TOTAL_AMOUNT', 'I-TOTAL_AMOUNT', 'B-DRIVER_NAME', 'I-DRIVER_NAME', ] def prepare_waybill_dataset( image_paths: list, annotations: list, # list of dict with keys: words, boxes, labels processor: LayoutLMv3Processor ) -> Dataset: """ Prepare dataset for fine-tuning. annotations[i]['boxes']: normalized bbox [0..1000] for LayoutLM. """ label2id = {l: i for i, l in enumerate(WAYBILL_LABELS)} features_list = [] for img_path, ann in zip(image_paths, annotations): from PIL import Image as PILImage image = PILImage.open(img_path).convert('RGB') encoding = processor( image, text=ann['words'], boxes=ann['boxes'], word_labels=[label2id[l] for l in ann['labels']], truncation=True, padding='max_length', max_length=512, return_tensors='pt' ) features_list.append({ k: v.squeeze().tolist() for k, v in encoding.items() }) return Dataset.from_list(features_list) Handwriting Field Processing: Why TrOCR beats PaddleOCR?
Invoices often contain handwritten dates, quantities, and signatures. PaddleOCR for printed text on handwritten fields makes errors—accuracy drops to 60%. We use a handwriting detector + TrOCR for handwriting recognition, which gives 20% higher F1 on real data. Fine-tuning TrOCR on corporate handwriting requires at least 300 handwritten records per operator.
from transformers import TrOCRProcessor, VisionEncoderDecoderModel from PIL import Image import torch class HandwritingOCR: def __init__(self): self.processor = TrOCRProcessor.from_pretrained( 'microsoft/trocr-base-handwritten' ) self.model = VisionEncoderDecoderModel.from_pretrained( 'microsoft/trocr-base-handwritten' ).eval().cuda() @torch.no_grad() def recognize(self, image: Image.Image) -> str: pixel_values = self.processor( image, return_tensors='pt' ).pixel_values.to('cuda') generated_ids = self.model.generate( pixel_values, max_new_tokens=64, num_beams=4, early_stopping=True ) return self.processor.batch_decode( generated_ids, skip_special_tokens=True )[0] class HybridWaybillOCR: """ Determine text type (printed / handwritten) → choose OCR. Handwriting features: large character height variance, no serif patterns. """ def __init__(self): self.handwriting_ocr = HandwritingOCR() # PaddleOCR for printed from paddleocr import PaddleOCR self.printed_ocr = PaddleOCR(use_angle_cls=True, lang='ru') def is_handwritten(self, text_region: Image.Image) -> bool: """Simple heuristic: variance of stroke width""" import numpy as np img_array = np.array(text_region.convert('L')) # Binarization _, binary = cv2.threshold(img_array, 0, 255, cv2.THRESH_BINARY + cv2.THRESH_OTSU) # Stroke width variance as handwriting indicator col_density = (binary == 0).mean(axis=0) return float(col_density.std()) > 0.15 # empirical threshold def recognize_region(self, image: Image.Image) -> str: if self.is_handwritten(image): return self.handwriting_ocr.recognize(image) else: result = self.printed_ocr.ocr(np.array(image)) return ' '.join([line[1][0] for line in result[0] or []]) Requisite Validation: INN, Sums, Dates
Extracted data undergoes validation: INN checksum, date format, arithmetic totals. We automatically check that item subtotals match the total, and quantities sum up. Discrepancies are logged. This reduces accounting errors by 90%.
import re def validate_russian_inn(inn: str) -> bool: """Check INN checksum (Russian Federation)""" if not re.match(r'^\d{10}$|^\d{12}$', inn): return False digits = [int(d) for d in inn] if len(inn) == 10: check = sum(d * w for d, w in zip(digits[:9], [2,4,10,3,5,9,4,6,8])) % 11 % 10 return digits[9] == check else: c1 = sum(d * w for d, w in zip(digits[:11], [7,2,4,10,3,5,9,4,6,8,0])) % 11 % 10 c2 = sum(d * w for d, w in zip(digits[:11], [3,7,2,4,10,3,5,9,4,6,8])) % 11 % 10 return digits[10] == c1 and digits[11] == c2 In a recent deployment for a logistics company processing 500 waybills daily, our fine-tuned LayoutLMv3 achieved F1 >0.991 on printed fields and reduced manual data entry errors by 90%. Handwriting recognition after fine-tuning on 400 samples per operator reached 94% accuracy.
What's Included in the Work?
We provide:
- trained LayoutLMv3 model on your templates
- REST API or Python package for inference
- integration module for 1C (HTTP exchange)
- logging and monitoring dashboard (prometheus + grafana — optional)
- documentation for fine-tuning and deployment
- staff training and support during implementation
Comparison: Our AI Solution vs. Rule-Based Parsing
| Characteristic | Rule-based | Our AI Solution |
|---|---|---|
| Accuracy on standard forms | 85–90% | >99% |
| Tolerance to low-quality scans | Low | High (data augmentation) |
| Handwritten text processing | No | Yes (TrOCR) |
| Time to adapt to new template | days | hours (few-shot) |
| Maintenance cost | High (patches per template) | Low (one model) |
Typical Implementation Mistakes and How to Avoid Them
- Insufficient annotation for handwritten fields. Minimum 200 samples per operator's handwriting—otherwise TrOCR gives <80% accuracy.
- Ignoring INN validation. One digit error leads to tax issues—we always verify the checksum.
- Mixing printed and handwritten OCR. Without a handwriting detector, the model will be confused—our heuristic with threshold 0.15 works reliably.
- Lack of production monitoring. We recommend tracking metrics (F1, latency p99) and setting alerts for quality drops.
Why Choose Us?
We have 5+ years of experience in Computer Vision and NLP, dozens of OCR deployments in document workflows. We guarantee model accuracy and provide full documentation. If you have non-standard invoices, we evaluate your project in 1 day. Contact us to discuss your task—we'll find the optimal solution for your budget.
Estimated Timelines
| Stage | Time |
|---|---|
| Extraction of TORG-12 / TTN fields (standard formats) | 2–3 weeks |
| Fine-tuning LayoutLMv3 on corporate invoices | 5–7 weeks |
| Full system with handwriting + validation + 1C integration | 8–14 weeks |
Order a pilot implementation: we'll deploy the solution on your 100 documents and show metrics. Get a free engineer consultation.







