Implements the foundation of the OCR Sprint service: - FastAPI app with /api/v1/health and /api/v1/documents (sync upload) - Pydantic v2 schemas for documents, extraction result, personnel - Pipeline: PDF/image ingest (PyMuPDF), preprocessing (resize, deskew, denoise, optional adaptive threshold), PaddleOCR wrapper, regex-based header extraction (nomor sprint, tanggal, satuan, perihal, dasar), signatory NRP, master-pangkat validation, confidence scoring + routing. - Tests: 61 unit tests covering regex rules, validators, preprocess, ingest, confidence, and API contract (PaddleOCR mocked). - Tooling: pyproject (setuptools), ruff, mypy strict, pytest, pre-commit, Dockerfile, docker-compose, Makefile. - Docs: README + docs/architecture.md (full hybrid stack rationale and 6-phase roadmap). Co-authored-by: adrian kuman firmansah <adriancuman@gmail.com>
260 lines
16 KiB
Markdown
260 lines
16 KiB
Markdown
# Plan & Arsitektur — OCR Service Surat Sprint Kepolisian
|
||
|
||
## 1. Penilaian Jujur Tech Stack yang Diusulkan
|
||
|
||
Tech stack Anda (FastAPI + PaddleOCR + OpenCV/Pillow + Regex) **sudah bagus dan layak produksi**, tapi **belum tentu paling optimal akurasinya** untuk kasus surat sprint. Ada beberapa gap yang perlu diisi sebelum bisa disebut "terbaik".
|
||
|
||
### Yang sudah tepat
|
||
| Komponen | Alasan |
|
||
|---|---|
|
||
| **FastAPI** | Async native, Pydantic validation, OpenAPI docs otomatis, ideal untuk ML serving. |
|
||
| **PaddleOCR (PP-OCRv4/v5)** | Salah satu OCR open-source terbaik untuk dokumen campuran teks + tabel, mendukung Latin (cocok untuk Bahasa Indonesia), bisa jalan on-premise (penting untuk dokumen kepolisian yang sensitif — **cloud OCR seperti Google Vision/AWS Textract sebaiknya dihindari** karena masalah kerahasiaan). |
|
||
| **OpenCV + Pillow** | Standar industri untuk preprocessing. |
|
||
| **Regex/rule-based** | Cocok untuk dokumen terstruktur seperti sprint yang format-nya relatif baku. |
|
||
|
||
### Yang masih kurang / perlu ditambah
|
||
|
||
1. **Table extraction belum tertangani**
|
||
Daftar personel di surat sprint hampir selalu berbentuk **tabel** (No, Pangkat, NRP, Nama, Jabatan, Keterangan). Regex pada teks linear dari OCR biasa **akan kacau** ketika baris tabel pecah atau kolom bergeser. Solusi: gunakan **PaddleOCR PP-Structure** (modul table recognition bawaan Paddle) atau model khusus seperti **TableTransformer (Microsoft)**.
|
||
|
||
2. **Document detection & dewarping untuk foto HP belum eksplisit**
|
||
Foto HP bermasalah karena: perspektif miring, lipatan, bayangan, lighting tidak rata, fokus tidak merata. OpenCV crop + perspective transform manual saja sering gagal. Tambahkan:
|
||
- **Document corner detection**: `DocTR` / `MobileSAM` / model edge-based, atau heuristik kontur OpenCV sebagai fallback.
|
||
- **Dewarping**: `DocTr` / `DewarpNet` untuk halaman yang melengkung (lipatan).
|
||
- **Shadow removal**: algoritma background division atau model spesialis.
|
||
|
||
3. **Strategi ekstraksi 100% regex itu rapuh**
|
||
Surat sprint dari satuan berbeda (Polda, Polres, Polsek, Mabes) punya **variasi format**: header berbeda, urutan field berbeda, kadang pangkat disingkat (`AKP`, `IPDA`) kadang ditulis penuh. Regex murni akan butuh ratusan rule dan tetap miss kasus baru.
|
||
**Rekomendasi pendekatan hybrid**:
|
||
- **Layer 1 — Regex/rule** untuk field deterministik (Nomor sprint, tanggal, dasar hukum) yang format-nya baku.
|
||
- **Layer 2 — Schema-aware extraction** menggunakan **LLM lokal** (Llama 3.1 8B / Qwen2.5 7B via Ollama atau vLLM) dengan structured output (JSON schema / Pydantic) untuk field yang variatif (jabatan, keterangan tugas).
|
||
- **Layer 3 — Validation** terhadap master data (daftar pangkat valid, format NRP 8 digit, dll).
|
||
|
||
4. **Tidak ada confidence scoring & human-in-the-loop**
|
||
Untuk dokumen kepolisian, **akurasi 100% otomatis itu mitos**. Sistem harus:
|
||
- Mengeluarkan confidence score per field.
|
||
- Otomatis flag dokumen low-confidence untuk review manusia.
|
||
- Sediakan UI/endpoint koreksi yang feedback-nya bisa dipakai retraining.
|
||
|
||
5. **Alternatif end-to-end yang patut dipertimbangkan**
|
||
Jika nanti volume dokumen besar dan format relatif stabil, fine-tuning model **Document Understanding** end-to-end bisa lebih akurat:
|
||
- **Donut** (OCR-free, langsung image → JSON).
|
||
- **LayoutLMv3** (kombinasi teks + layout + visual).
|
||
- **Surya OCR** (newer, sangat bagus untuk dokumen).
|
||
Untuk MVP, tetap pakai PaddleOCR. Donut/LayoutLM adalah opsi V2 setelah ada labeled dataset cukup (~500–1000 dokumen).
|
||
|
||
### Verdict
|
||
Stack Anda **bisa mencapai ~85–92% akurasi field-level** untuk surat sprint dengan kualitas scan baik, dan **~70–80%** untuk foto HP, **kalau ditambah** komponen di atas. Tanpa table extraction + dewarping + hybrid extraction, akurasinya akan jatuh di kondisi nyata.
|
||
|
||
---
|
||
|
||
## 2. Arsitektur yang Direkomendasikan
|
||
|
||
### 2.1 Diagram Logis
|
||
|
||
```
|
||
┌────────────────────────────────────────────────────────────────────┐
|
||
│ Client (Web/Mobile) │
|
||
└──────────────────────────────┬─────────────────────────────────────┘
|
||
│ HTTPS (multipart upload)
|
||
▼
|
||
┌────────────────────────────────────────────────────────────────────┐
|
||
│ FastAPI Gateway (stateless) │
|
||
│ - Auth (JWT/API key) - Rate limit - Request validation │
|
||
└──────────────────────────────┬─────────────────────────────────────┘
|
||
│ enqueue job
|
||
▼
|
||
┌────────────────────────────────────────────────────────────────────┐
|
||
│ Job Queue (Redis + Celery / RQ / Dramatiq) │
|
||
└──────────────────────────────┬─────────────────────────────────────┘
|
||
▼
|
||
┌────────────────────────────────────────────────────────────────────┐
|
||
│ OCR Worker Pipeline (GPU/CPU) │
|
||
│ ┌────────────┐ ┌──────────────┐ ┌───────────┐ ┌────────────┐ │
|
||
│ │ 1. Ingest │→ │ 2. Preproc │→ │ 3. OCR + │→ │ 4. Extract │ │
|
||
│ │ & detect │ │ (deskew, │ │ Layout │ │ (regex + │ │
|
||
│ │ PDF/IMG │ │ dewarp, │ │ PP-Struct│ │ LLM + │ │
|
||
│ │ │ │ denoise) │ │ + Table) │ │ validate) │ │
|
||
│ └────────────┘ └──────────────┘ └───────────┘ └─────┬──────┘ │
|
||
│ │ │
|
||
│ ┌──────────────────────────────┘ │
|
||
│ ▼ │
|
||
│ ┌─────────────┐ │
|
||
│ │ 5. Confidence│ → low conf? flag for review │
|
||
│ │ scoring │ │
|
||
│ └──────┬───────┘ │
|
||
└──────────────────────────┼─────────────────────────────────────────┘
|
||
▼
|
||
┌────────────────────────────────────────────────────────────────────┐
|
||
│ Storage: PostgreSQL (metadata) + MinIO/S3 (file) │
|
||
│ + Vector store opsional (untuk dedup / search) │
|
||
└────────────────────────────────────────────────────────────────────┘
|
||
│
|
||
▼
|
||
┌────────────────────────────────────────────────────────────────────┐
|
||
│ Review UI (optional) — koreksi manual + audit trail │
|
||
└────────────────────────────────────────────────────────────────────┘
|
||
```
|
||
|
||
### 2.2 Pipeline Detail per Tahap
|
||
|
||
**Tahap 1 — Ingest & Document Detection**
|
||
- PDF: render setiap halaman jadi image @ 300 DPI (`pdf2image` / `PyMuPDF`).
|
||
- Image (foto HP): deteksi sudut dokumen → crop → perspective transform.
|
||
- Library: OpenCV `findContours` (cepat) sebagai fallback, **DocTR document detector** (lebih akurat) sebagai utama.
|
||
|
||
**Tahap 2 — Preprocessing**
|
||
- Deskew (rotation correction) — Hough transform atau model.
|
||
- Dewarp (untuk foto buku/lipatan) — `DewarpNet` atau model RNN.
|
||
- Adaptive thresholding (untuk foto dengan lighting tidak rata).
|
||
- Shadow removal (background division).
|
||
- Denoise (Non-Local Means).
|
||
- Resize ke ukuran optimal OCR (~1500–2500 px sisi panjang).
|
||
|
||
**Tahap 3 — OCR + Layout Analysis**
|
||
- **PaddleOCR PP-Structure** dijalankan sekali → menghasilkan:
|
||
- Bounding boxes + teks + confidence per word/line.
|
||
- Table region detection + table-to-HTML/JSON.
|
||
- Layout type per region (title, paragraph, table, figure).
|
||
- Output ditampung sebagai struktur intermediate (mirip hOCR / ALTO XML).
|
||
|
||
**Tahap 4 — Information Extraction**
|
||
- **4a. Header parsing (regex)**: Nomor sprint, tanggal, satuan penerbit, dasar hukum, perihal. Format relatif baku → regex sangat cocok.
|
||
- **4b. Personnel table extraction**: ambil dari hasil PP-Structure table → mapping kolom (Pangkat, NRP, Nama, Jabatan, Keterangan).
|
||
- **4c. LLM fallback**: untuk field yang regex/table miss, kirim chunk teks + JSON schema ke LLM lokal (Ollama / vLLM) dengan **structured output** (Pydantic via `outlines` / `instructor`).
|
||
- **4d. Validation layer**:
|
||
- NRP: 8 digit numerik.
|
||
- Pangkat: harus ada di daftar master pangkat Polri.
|
||
- Tanggal: parse + sanity check.
|
||
- Cross-check: jumlah personel di body = jumlah baris tabel.
|
||
|
||
**Tahap 5 — Confidence Scoring & Routing**
|
||
- Aggregate confidence: weighted average dari OCR confidence + validation pass/fail + LLM logprob (kalau pakai).
|
||
- Threshold (mis. < 0.85) → status `NEEDS_REVIEW`.
|
||
- Threshold tinggi (≥ 0.95) + semua validasi pass → status `AUTO_APPROVED`.
|
||
|
||
### 2.3 API Endpoint (FastAPI)
|
||
|
||
```
|
||
POST /api/v1/documents # upload, kembalikan job_id
|
||
GET /api/v1/documents/{job_id} # poll status + hasil
|
||
GET /api/v1/documents/{job_id}/raw # raw OCR output (debug)
|
||
PATCH /api/v1/documents/{job_id} # koreksi manual (HITL)
|
||
GET /api/v1/health # liveness
|
||
GET /api/v1/metrics # Prometheus
|
||
```
|
||
|
||
Response shape (contoh):
|
||
```json
|
||
{
|
||
"job_id": "uuid",
|
||
"status": "completed | processing | needs_review | failed",
|
||
"confidence": 0.92,
|
||
"data": {
|
||
"nomor_sprint": "Sprin/123/IV/2025",
|
||
"tanggal": "2025-04-21",
|
||
"satuan_penerbit": "Polres Bandung",
|
||
"dasar": ["...", "..."],
|
||
"perihal": "...",
|
||
"personel": [
|
||
{"no": 1, "pangkat": "AKP", "nrp": "12345678", "nama": "...", "jabatan": "Kasat Reskrim", "confidence": 0.97},
|
||
...
|
||
],
|
||
"ttd": {"pejabat": "...", "pangkat": "...", "nrp": "..."}
|
||
},
|
||
"review_flags": []
|
||
}
|
||
```
|
||
|
||
### 2.4 Tech Stack Final yang Direkomendasikan
|
||
|
||
| Layer | Pilihan | Catatan |
|
||
|---|---|---|
|
||
| API | **FastAPI** + Uvicorn/Gunicorn | sesuai usulan |
|
||
| Validation | **Pydantic v2** | wajib |
|
||
| Queue | **Redis + Celery** atau **Dramatiq** | OCR berat, jangan blocking request |
|
||
| OCR | **PaddleOCR PP-OCRv4 + PP-Structure** | tambah PP-Structure untuk tabel |
|
||
| Preprocessing | **OpenCV + Pillow** + **DocTR** (detection) | DocTR untuk foto HP |
|
||
| Extraction | **Regex + Ollama (Llama 3.1 8B / Qwen2.5 7B)** + **instructor/outlines** | hybrid |
|
||
| Storage | **PostgreSQL** (metadata) + **MinIO** (file blob) | self-hosted, sesuai compliance |
|
||
| Observability | **Prometheus + Grafana + Loki** | wajib produksi |
|
||
| Container | **Docker + docker-compose** (dev) → **Kubernetes** (prod) | |
|
||
| GPU | NVIDIA T4/A10 (1× cukup untuk MVP) | PaddleOCR jauh lebih cepat di GPU |
|
||
|
||
---
|
||
|
||
## 3. Roadmap Pengembangan (Bertahap)
|
||
|
||
### Fase 0 — Persiapan (1 minggu)
|
||
- Kumpulkan **dataset sampel**: minimal 50 surat sprint (campur PDF scan + foto HP) dari beragam satuan.
|
||
- Buat **ground truth labelling** untuk 20 dokumen (untuk evaluasi).
|
||
- Definisikan **schema output final** (JSON) bersama stakeholder.
|
||
|
||
### Fase 1 — MVP Pipeline Sinkron (2 minggu)
|
||
- Setup FastAPI skeleton + Pydantic schemas.
|
||
- Integrasi PaddleOCR PP-OCRv4 (CPU dulu, GPU menyusul).
|
||
- Preprocessing dasar: deskew + denoise + resize.
|
||
- Regex extraction untuk field header.
|
||
- Endpoint sinkron `POST /documents` (untuk dev/testing saja).
|
||
- **Evaluasi akurasi** terhadap 20 ground truth.
|
||
|
||
### Fase 2 — Robustness untuk Foto HP (2 minggu)
|
||
- Integrasi document detection (DocTR atau OpenCV contour).
|
||
- Perspective transform + dewarping.
|
||
- Shadow removal.
|
||
- Re-evaluasi akurasi pada subset foto HP.
|
||
|
||
### Fase 3 — Table Extraction (1.5 minggu)
|
||
- Integrasi PP-Structure untuk personnel table.
|
||
- Mapping kolom + validation (NRP, pangkat).
|
||
- Master data tabel pangkat Polri.
|
||
|
||
### Fase 4 — Async + Production Ready (1.5 minggu)
|
||
- Pindahkan ke arsitektur async dengan Celery + Redis.
|
||
- Storage MinIO + PostgreSQL.
|
||
- Auth, rate limit, logging, metrics.
|
||
- Docker compose untuk deployment.
|
||
|
||
### Fase 5 — LLM Hybrid Extraction (2 minggu)
|
||
- Setup Ollama / vLLM dengan model lokal.
|
||
- Structured output via `instructor`.
|
||
- Confidence scoring + routing ke review.
|
||
|
||
### Fase 6 — HITL Review UI (opsional, 2 minggu)
|
||
- Endpoint koreksi.
|
||
- Simple web UI (Next.js) untuk reviewer.
|
||
- Audit trail & feedback loop.
|
||
|
||
### Fase 7 — Optimasi Lanjutan (ongoing)
|
||
- Fine-tune PaddleOCR detection/recognition pada dataset internal.
|
||
- Eksplorasi Donut/LayoutLMv3 jika dataset sudah cukup.
|
||
- Batch processing & GPU optimization.
|
||
|
||
**Total estimasi MVP fungsional (Fase 1–4): ~7 minggu** dengan 1 backend engineer + 1 ML engineer.
|
||
|
||
---
|
||
|
||
## 4. Risiko & Mitigasi
|
||
|
||
| Risiko | Mitigasi |
|
||
|---|---|
|
||
| Data sensitif (kepolisian) bocor | Wajib on-prem; tidak ada cloud OCR; enkripsi at-rest (LUKS/pgcrypto) + in-transit (mTLS); audit log lengkap. |
|
||
| Variasi format antar satuan | Hybrid extraction (regex + LLM); kumpulkan sample dari banyak satuan sejak awal. |
|
||
| Foto HP kualitas buruk | Validasi kualitas image di client (resolusi minimal, blur detection) sebelum upload. |
|
||
| Akurasi tidak sampai target | HITL review wajib untuk dokumen low-confidence; jangan deploy fully-automatic. |
|
||
| Tanggung jawab hukum atas hasil OCR | Selalu simpan original document + flag bahwa hasil ekstraksi adalah "draft, perlu verifikasi manusia". |
|
||
|
||
---
|
||
|
||
## 5. Pertanyaan Sebelum Implementasi
|
||
|
||
Sebelum saya lanjut ke implementasi, mohon konfirmasi:
|
||
|
||
1. **Volume**: berapa dokumen/hari yang ditargetkan? (mempengaruhi pilihan async vs sync, GPU vs CPU)
|
||
2. **Deployment target**: on-prem mutlak, atau private cloud (GovCloud) boleh?
|
||
3. **Source dokumen**: apakah ada akses ke 20–50 sample surat sprint untuk dijadikan dataset awal?
|
||
4. **Integrasi**: service ini akan dipanggil sistem apa? (mempengaruhi auth & API contract)
|
||
5. **HITL**: apakah ada SDM untuk review manual dokumen low-confidence?
|
||
6. **Hardware**: sudah ada server GPU, atau perlu sizing rekomendasi?
|
||
7. **Format output final**: ada schema yang sudah dipakai sistem downstream?
|