@@ -4,7 +4,8 @@ Local-first OCR pipeline for passports and Indian KYC documents. It
44preprocesses scans, classifies the document, runs targeted OCR with RapidOCR
55(PP-OCRv5), and extracts structured fields—including passport MRZ data, Indian
66passport back-page fields, and identifier/holder fields for PAN, Aadhaar,
7- driving licences, and voter IDs.
7+ driving licences, voter IDs, MGNREGA/NREGA job cards, and National Population
8+ Register (NPR) name-and-address letters.
89
910Ships as a Python package with a FastAPI server, plus an npm wrapper at [ ` packages/passport-ocr ` ] ( packages/passport-ocr ) that auto-spawns the Python server for Node.js consumers.
1011
@@ -24,8 +25,28 @@ Ships as a Python package with a FastAPI server, plus an npm wrapper at [`packag
2425| Aadhaar (front/back) | Aadhaar no. (+ VID, masked-card support), name, DOB/YOB, gender, address, pincode | Verhoeff checksum |
2526| Driving licence | DL no., name, DOB, issue/validity dates (NT + TR), address, blood group, vehicle class | DL format |
2627| Voter ID (EPIC) | EPIC no., name, relation name + type, gender, DOB/age | EPIC format |
28+ | MGNREGA/NREGA job card | job-card no., household head, registration/validity, location, category/BPL, adult members | Conservative hierarchical format |
29+ | NPR name/address letter | reference no., resident name, address, pincode, issue date | Not offline-verifiable |
2730
28- The document type is detected automatically; ` /scan ` returns the matching field block (` fields ` /` backPageFields ` for passports, ` panFields ` /` aadhaarFields ` /` drivingLicenceFields ` /` voterIdFields ` for the others) keyed by ` documentType ` .
31+ Together with the existing passport path, this covers the officially valid
32+ document categories listed in the
33+ [ RBI KYC Master Direction] ( https://www.rbi.org.in/Scripts/BS_ViewMasDirections.aspx?id=11566 ) ;
34+ PAN is supported as a separate tax identifier.
35+
36+ The document type is detected automatically; ` /scan ` returns the matching
37+ field block (` fields ` /` backPageFields ` for passports, or ` panFields ` ,
38+ ` aadhaarFields ` , ` drivingLicenceFields ` , ` voterIdFields ` ,
39+ ` nregaJobCardFields ` , or ` nprLetterFields ` ) keyed by ` documentType ` .
40+
41+ NREGA and NPR support is experimental until measured against a representative
42+ private image dataset. The extraction layer handles multiple label/layout
43+ variants, but deterministic text-region tests are not evidence of real-image
44+ accuracy.
45+
46+ To keep passport OCR behavior unchanged, a positive passport-page probe is
47+ never overridden by the KYC router. A driving licence or voter card whose crop
48+ contains multiple passport-like labels can therefore remain ambiguous; an
49+ explicit KYC-only entry point is tracked as future work.
2950
3051## Quickstart
3152
@@ -86,14 +107,16 @@ The package auto-creates a `.venv`, installs the Python deps, and manages the lo
86107internally. Set ` DOCUMENT_OCR_API_TOKEN ` to require
87108` Authorization: Bearer <token> ` on ` /scan ` . For internet-facing deployments,
88109use platform IAM or an API gateway in addition to application-level controls.
110+ Incomplete or semantically invalid non-passport extractions return HTTP ` 422 `
111+ with the full structured failure result.
89112
90113## Output
91114
92115``` jsonc
93116{
94117 " status" : " success" , // success | failure | unsupported_page
95- " documentType" : " passport" , // passport | pan | aadhaar | driving_licence | voter_id | unknown
96- " pageType" : " passport_biodata" , // passport_biodata | passport_non_biodata | pan | aadhaar | driving_licence | voter_id | unknown
118+ " documentType" : " passport" , // passport | pan | aadhaar | driving_licence | voter_id | nrega_job_card | npr_letter | unknown
119+ " pageType" : " passport_biodata" , // same document values, plus passport_biodata | passport_non_biodata | unknown
97120 " confidence" : 0.91 ,
98121 " fields" : {
99122 " surname" : " ..." , " givenNames" : " ..." , " fullName" : " ..." ,
@@ -119,8 +142,18 @@ use platform IAM or an API gateway in addition to application-level controls.
119142 " classOfVehicle" : " ..." , " validityDateTransport" : null },
120143 " voterIdFields" : { " epicNumber" : " ..." , " name" : " ..." , " relationName" : " ..." , " relationType" : " father" ,
121144 " gender" : " ..." , " dateOfBirth" : " ..." , " age" : null },
145+ " nregaJobCardFields" : { " jobCardNumber" : " ..." , " headOfHousehold" : " ..." , " category" : " SC" ,
146+ " registrationDate" : " ..." , " validityFrom" : " ..." , " validityTo" : " ..." ,
147+ " address" : " ..." , " village" : " ..." , " gramPanchayat" : " ..." , " block" : " ..." ,
148+ " district" : " ..." , " state" : " ..." , " bplStatus" : true , " familyId" : " ..." ,
149+ " members" : [{ " serialNumber" : " 1" , " name" : " ..." ,
150+ " fatherOrHusbandName" : " ..." , " gender" : " FEMALE" , " age" : 39 }] },
151+ " nprLetterFields" : { " referenceNumber" : " ..." , " name" : " ..." , " address" : " ..." ,
152+ " pincode" : " ..." , " issueDate" : " ..." },
122153 " mrzRaw" : [" P<IND..." , " ..." ], " mrzValid" : true ,
123154 " lowConfidence" : false ,
155+ " identifierValid" : null , // format/checksum only; never an authenticity verdict
156+ " missingRequiredFields" : [],
124157 " errors" : [], " warnings" : [],
125158 " processingMs" : 412
126159}
@@ -131,7 +164,37 @@ use platform IAM or an API gateway in addition to application-level controls.
1311641 . ` preprocess ` — orientation, document boundary detection, perspective correction, quality checks
1321652 . ` classify_passport_page ` — biodata vs non-biodata vs not-a-passport (cheap bottom-crop probe)
1331663 . passport path: ` run_ocr ` (RapidOCR PP-OCRv5, full-page fallback when MRZ is missing) → ` parse_mrz ` (TD3 MRZ with per-field + overall checksum validation) → ` extract_back_page ` (bilingual label-aware extraction) → ` validate ` (cross-checks MRZ vs visual fields, computes confidence)
134- 4 . non-passport path: ` classify_document ` routes full-page OCR to the matching extractor (` pan ` / ` aadhaar ` / ` driving_licence ` / ` voter_id ` ), validating each document's identifier (PAN format, Verhoeff for Aadhaar, EPIC/DL format)
167+ 4 . non-passport path: ` classify_document ` routes full-page OCR to the matching
168+ extractor (` pan ` / ` aadhaar ` / ` driving_licence ` / ` voter_id ` /
169+ ` nrega_job_card ` / ` npr_letter ` ), checks minimum required fields, validates
170+ identifiers where possible, and fails closed on partial or semantically
171+ implausible records
172+
173+ For non-passport documents, ` status: "success" ` means the extractor returned
174+ the document-specific minimum field set and passed a conservative OCR-region
175+ confidence gate. Alternate model readings for the same detected geometry count
176+ once. ` identifierValid ` means only an offline format/checksum check passed. NPR
177+ references have no universal public checksum, so this value is ` null ` ; it is
178+ never evidence of authenticity.
179+
180+ ### Non-passport OCR languages
181+
182+ The default recognition behavior is unchanged (` en ` , with the existing
183+ automatic fallback). If the expected KYC population uses a known script, run
184+ up to four recognition passes only on the non-passport path:
185+
186+ ``` bash
187+ DOCUMENT_OCR_KYC_LANGS=en,devanagari make dev
188+ ```
189+
190+ Available values are ` en ` , ` latin ` , ` devanagari ` , ` ka ` (Kannada), ` ta `
191+ (Tamil), and ` te ` (Telugu). Extra models increase latency and memory. The
192+ bundled RapidOCR version has no Bengali recognition model: Bengali-labelled
193+ extractor fixtures prove parsing behavior only, not Bengali image OCR. Select
194+ languages and release thresholds from measured benchmark slices. Server
195+ readiness initializes every configured model so model-download or startup
196+ failures are reported before the first scan. Unsupported names or more than
197+ four configured models fail readiness instead of silently falling back.
135198
136199Single entry point: ` core.pipeline.scan(image_input) ` .
137200
@@ -183,15 +246,32 @@ The per-document extractors are covered by deterministic `TextRegion` fixtures
183246under ` tests/python/test_*_extractor.py ` . These tests verify parsing and
184247validation behavior; they are not a claim of real-world OCR accuracy.
185248
186- For image-level evaluation , place a private dataset and ` manifest.json ` under
187- ` benchmark-data/ ` and run:
249+ For the legacy passport image benchmark , place its private dataset and
250+ ` manifest.json ` under ` benchmark-data/ ` and run:
188251
189252``` bash
190253make benchmark
191254```
192255
193- The directory is ignored by Git. Never commit identity documents or personal
194- data. See [ CONTRIBUTING.md] ( CONTRIBUTING.md ) for fixture rules.
256+ For a non-passport KYC dataset, use the versioned manifest and release gates:
257+
258+ ``` bash
259+ make benchmark-kyc \
260+ KYC_MANIFEST=/secure/kyc-eval/manifest.json \
261+ KYC_DATASET_ROOT=/secure/kyc-eval \
262+ KYC_REPORT=/secure/kyc-eval/reports/main.json
263+ ```
264+
265+ The KYC evaluator reports classification, acceptance/false-success,
266+ exact/normalized field, complete-record, runtime, per-document, and
267+ design/year/issuer/language/capture-quality slice metrics without copying
268+ ground-truth values into its report. Required per-document slices fail manifest
269+ validation when a declared variant is absent. See
270+ [ ` benchmarks/KYC_DATASET.md ` ] ( benchmarks/KYC_DATASET.md ) for the variant matrix
271+ and annotation workflow.
272+
273+ ` benchmark-data/ ` is ignored by Git. Never commit identity documents or
274+ personal data. See [ CONTRIBUTING.md] ( CONTRIBUTING.md ) for fixture rules.
195275
196276## Privacy and security
197277
0 commit comments