690530:1345 ADR-030-231-ocr-sandbox-two-step-flow #05
CI / CD Pipeline / build (push) Successful in 5m43s
CI / CD Pipeline / deploy (push) Successful in 9m8s

This commit is contained in:
2026-05-30 13:45:18 +07:00
parent c9edd62a0b
commit 30d9d721fb
6 changed files with 118 additions and 32 deletions
@@ -3,7 +3,8 @@
// - 2026-05-15: เพิ่ม OCR auto-detection service สำหรับ ADR-023A. // - 2026-05-15: เพิ่ม OCR auto-detection service สำหรับ ADR-023A.
// - 2026-05-25: แก้ไข AggregateError (empty message) จาก axios โดย wrap เป็น Error พร้อม context ที่ชัดเจน. // - 2026-05-25: แก้ไข AggregateError (empty message) จาก axios โดย wrap เป็น Error พร้อม context ที่ชัดเจน.
// - 2026-05-25: เพิ่ม path remapping (OCR_UPLOAD_BASE_PATH) เพื่อแปลง local upload path เป็น path ที่ sidecar เห็นผ่าน CIFS. // - 2026-05-25: เพิ่ม path remapping (OCR_UPLOAD_BASE_PATH) เพื่อแปลง local upload path เป็น path ที่ sidecar เห็นผ่าน CIFS.
// - 2026-05-29: เพิ่ม checkHealth() เพื่อตรวจสอบสุขภาพของ PaddleOCR sidecar สำหรับ getSystemHealth() (ADR-027) // - 2026-05-29: เพิ่ม checkHealth() เพื่อตรวจสอบสุขภาพของ OCR sidecar สำหรับ getSystemHealth() (ADR-027)
// - 2026-05-30: เปลี่ยนจาก PaddleOCR เป็น Tesseract OCR เพื่อความเข้ากันได้กับ CPU เก่า
import { Injectable, Logger } from '@nestjs/common'; import { Injectable, Logger } from '@nestjs/common';
import { ConfigService } from '@nestjs/config'; import { ConfigService } from '@nestjs/config';
@@ -20,7 +21,7 @@ export interface OcrDetectionResult {
ocrUsed: boolean; ocrUsed: boolean;
} }
interface PaddleOcrResponse { interface OcrSidecarResponse {
text?: string; text?: string;
} }
@@ -31,7 +32,7 @@ export interface OcrHealthResult {
error?: string; error?: string;
} }
/** บริการเลือก fast path หรือ PaddleOCR sidecar ตามจำนวนตัวอักษรที่ extract ได้ */ /** บริการเลือก fast path หรือ OCR sidecar (Tesseract) ตามจำนวนตัวอักษรที่ extract ได้ */
@Injectable() @Injectable()
export class OcrService { export class OcrService {
private readonly logger = new Logger(OcrService.name); private readonly logger = new Logger(OcrService.name);
@@ -64,7 +65,7 @@ export class OcrService {
return localPath; return localPath;
} }
/** ตรวจสอบสุขภาพและ latency ของ PaddleOCR sidecar ผ่าน GET /health */ /** ตรวจสอบสุขภาพและ latency ของ OCR sidecar (Tesseract) ผ่าน GET /health */
async checkHealth(): Promise<OcrHealthResult> { async checkHealth(): Promise<OcrHealthResult> {
const startTime = Date.now(); const startTime = Date.now();
try { try {
@@ -105,7 +106,7 @@ export class OcrService {
try { try {
const sidecarPath = this.remapPath(input.pdfPath); const sidecarPath = this.remapPath(input.pdfPath);
this.logger.debug(`OCR path remap: ${input.pdfPath}${sidecarPath}`); this.logger.debug(`OCR path remap: ${input.pdfPath}${sidecarPath}`);
const response = await axios.post<PaddleOcrResponse>( const response = await axios.post<OcrSidecarResponse>(
`${this.ocrApiUrl}/ocr`, `${this.ocrApiUrl}/ocr`,
{ pdfPath: sidecarPath }, { pdfPath: sidecarPath },
{ timeout: 90000 } { timeout: 90000 }
@@ -124,7 +125,7 @@ export class OcrService {
? err.message ? err.message
: String(err); : String(err);
throw new Error( throw new Error(
`PaddleOCR sidecar unreachable at ${this.ocrApiUrl}${cause}` `OCR sidecar (Tesseract) unreachable at ${this.ocrApiUrl}${cause}`
); );
} }
} }
@@ -508,7 +508,7 @@ export default function OcrSandboxPromptManager() {
OCR Raw Text (Step 1 Result) OCR Raw Text (Step 1 Result)
</CardTitle> </CardTitle>
<Badge variant="outline" className="text-xs"> <Badge variant="outline" className="text-xs">
{ocrResult.ocrUsed ? 'PaddleOCR' : 'Fast Path (Text Layer)'} {ocrResult.ocrUsed ? 'Tesseract' : 'Fast Path (Text Layer)'}
</Badge> </Badge>
</CardHeader> </CardHeader>
<CardContent className="pt-4"> <CardContent className="pt-4">
+54 -4
View File
@@ -10,6 +10,7 @@
- 2026-05-25 (Session 7): PaddleOCR Sidecar setup บน Desk-5439 — สร้าง FastAPI sidecar (port 8765) รองรับ `/ocr` + `/normalize`, แก้ AggregateError ใน ocr.service.ts, เพิ่ม path remapping (`OCR_SIDECAR_UPLOAD_BASE`), CIFS volume mount จาก QNAP. - 2026-05-25 (Session 7): PaddleOCR Sidecar setup บน Desk-5439 — สร้าง FastAPI sidecar (port 8765) รองรับ `/ocr` + `/normalize`, แก้ AggregateError ใน ocr.service.ts, เพิ่ม path remapping (`OCR_SIDECAR_UPLOAD_BASE`), CIFS volume mount จาก QNAP.
- 2026-05-26: เพิ่ม system memories ที่หายไป — QNAP SSH Key Authentication, TransformInterceptor double registration, ADR-021 Transmittals/Circulation integration, Correspondence detail fixes, Playwright E2E setup, Tag/Contract UUID fixes. - 2026-05-26: เพิ่ม system memories ที่หายไป — QNAP SSH Key Authentication, TransformInterceptor double registration, ADR-021 Transmittals/Circulation integration, Correspondence detail fixes, Playwright E2E setup, Tag/Contract UUID fixes.
- 2026-05-27: Context-Aware Prompts & DB CC Typo Cleanup (ADR-030) — นำเสนอการผูก Master Data เข้ากับ Prompt Extraction, ออกแบบ JSON Context-Aware configuration, อัปเดต Entity/DTOs, ออกแบบ JSON format ผู้รับเป็น Object Array ป้องกันบัค และแก้ whitespace typo 'CC ' ในฐานข้อมูล - 2026-05-27: Context-Aware Prompts & DB CC Typo Cleanup (ADR-030) — นำเสนอการผูก Master Data เข้ากับ Prompt Extraction, ออกแบบ JSON Context-Aware configuration, อัปเดต Entity/DTOs, ออกแบบ JSON format ผู้รับเป็น Object Array ป้องกันบัค และแก้ whitespace typo 'CC ' ในฐานข้อมูล
- 2026-05-30 (Session 8): OCR Engine Migration — เปลี่ยนจาก PaddleOCR เป็น Tesseract OCR เพื่อแก้ปัญหา SIGILL (Illegal Instruction) บน CPU เก่าที่ไม่รองรับ AVX: อัปเดต requirements.txt (ลบ paddlepaddle/paddleocr, เพิ่ม pytesseract), app.py (เปลี่ยนใช้ pytesseract, OCR_LANG=tha+eng), Dockerfile (ติดตั้ง tesseract-ocr + ภาษาไทย/อังกฤษ), docker-compose.yml (OCR_LANG=tha+eng, ลบ paddleocr_models volume), backend ocr.service.ts (เปลี่ยน comment/error message), frontend OcrSandboxPromptManager.tsx (เปลี่ยน Badge text)
--> -->
# 🧠 Agent Long-term Project Memory # 🧠 Agent Long-term Project Memory
@@ -185,7 +186,7 @@ docker compose ps # Check status
## 🌐 7. Environment & Services ## 🌐 7. Environment & Services
| Service | Local URL / Port | Production | Notes | | Service | Local URL / Port | Production | Notes |
| ---------------- | ----------------------------- | ------------------------- | ------------------------------------ | | ----------------- | ----------------------------- | ------------------------- | ------------------------------------ |
| **Backend API** | `http://localhost:3001` | QNAP `192.168.10.8` | NestJS — `/api` prefix | | **Backend API** | `http://localhost:3001` | QNAP `192.168.10.8` | NestJS — `/api` prefix |
| **Frontend** | `http://localhost:3000` | QNAP `192.168.10.8` | Next.js | | **Frontend** | `http://localhost:3000` | QNAP `192.168.10.8` | Next.js |
| **MariaDB** | `localhost:3307` | QNAP internal | DB: `lcbp3`, root via docker | | **MariaDB** | `localhost:3307` | QNAP internal | DB: `lcbp3`, root via docker |
@@ -193,7 +194,7 @@ docker compose ps # Check status
| **n8n** | `http://localhost:5678` | QNAP `192.168.10.8:5678` | Migration orchestrator only | | **n8n** | `http://localhost:5678` | QNAP `192.168.10.8:5678` | Migration orchestrator only |
| **Ollama** | `http://192.168.10.100:11434` | Admin Desktop (Desk-5439) | gemma4:e2b + nomic-embed-text | | **Ollama** | `http://192.168.10.100:11434` | Admin Desktop (Desk-5439) | gemma4:e2b + nomic-embed-text |
| **Qdrant** | `http://localhost:6333` | Admin Desktop (Desk-5439) | Vector DB — requires projectPublicId | | **Qdrant** | `http://localhost:6333` | Admin Desktop (Desk-5439) | Vector DB — requires projectPublicId |
| **PaddleOCR** | `http://192.168.10.100:8765` | Admin Desktop (Desk-5439) | `/ocr` + `/normalize` (FastAPI) | | **Tesseract OCR** | `http://192.168.10.100:8765` | Admin Desktop (Desk-5439) | `/ocr` + `/normalize` (FastAPI) |
| **Gitea** | `https://git.np-dms.work` | QNAP `192.168.10.8` | Source + CI/CD | | **Gitea** | `https://git.np-dms.work` | QNAP `192.168.10.8` | Source + CI/CD |
| **Gitea Runner** | ASUSTOR `192.168.10.9` | — | CI runner | | **Gitea Runner** | ASUSTOR `192.168.10.9` | — | CI runner |
@@ -212,7 +213,7 @@ QDRANT_URL
## 🚀 8. Recent Rollouts ## 🚀 8. Recent Rollouts
| วันที่ | Version | รายการ | สถานะ | | วันที่ | Version | รายการ | สถานะ |
| ---------- | ------- | ---------------------------------------------------------------------------------------------------- | --------------------------- | | ---------- | ------- | ---------------------------------------------------------------------------------------------------- | ----------------------------- |
| 2026-05-23 | v1.9.6 | Specs reorganization (`100/200/300-*` folders), AGENTS.md v1.9.6 update | ✅ Complete | | 2026-05-23 | v1.9.6 | Specs reorganization (`100/200/300-*` folders), AGENTS.md v1.9.6 update | ✅ Complete |
| 2026-05-23 | v1.9.6 | N8N Workflow v2 (`n8n.workflow.v2.json`) — ADR-023A compliant, ลบ Ollama direct | ⏳ Pending import to n8n UI | | 2026-05-23 | v1.9.6 | N8N Workflow v2 (`n8n.workflow.v2.json`) — ADR-023A compliant, ลบ Ollama direct | ⏳ Pending import to n8n UI |
| 2026-05-24 | v1.9.6 | AGENTS.md Project Memory Override rule (Windsurf / Antigravity / Codex) | ✅ Complete | | 2026-05-24 | v1.9.6 | AGENTS.md Project Memory Override rule (Windsurf / Antigravity / Codex) | ✅ Complete |
@@ -220,6 +221,7 @@ QDRANT_URL
| 2026-05-25 | v1.9.6 | Migration error normalization + `job_id` logging — workflow + backend + SQL/delta (Session 4) | ✅ Complete | | 2026-05-25 | v1.9.6 | Migration error normalization + `job_id` logging — workflow + backend + SQL/delta (Session 4) | ✅ Complete |
| 2026-05-25 | v1.9.6 | PaddleOCR Sidecar บน Desk-5439 — FastAPI `/ocr`+`/normalize`, CIFS mount, path remapping (Session 7) | ✅ Running | | 2026-05-25 | v1.9.6 | PaddleOCR Sidecar บน Desk-5439 — FastAPI `/ocr`+`/normalize`, CIFS mount, path remapping (Session 7) | ✅ Running |
| 2026-05-27 | v1.9.7 | Context-Aware Prompt Templates & DB CC Whitespace Cleanup (ADR-030) (Session 9) | ✅ Complete (v1.9.7 main) | | 2026-05-27 | v1.9.7 | Context-Aware Prompt Templates & DB CC Whitespace Cleanup (ADR-030) (Session 9) | ✅ Complete (v1.9.7 main) |
| 2026-05-30 | v1.9.7 | OCR Engine Migration — PaddleOCR → Tesseract (Session 8) | ✅ Complete (pending rebuild) |
--- ---
@@ -450,7 +452,51 @@ OCR_SIDECAR_UPLOAD_BASE=/mnt/uploads (env var)
--- ---
### Session 8 — 2026-05-26 (System Memories Consolidation) ### Session 8 — 2026-05-30 (OCR Engine Migration — PaddleOCR → Tesseract)
#### ปัญหาที่พบ (Root Cause)
**Bug 1: PaddleOCR SIGILL (Illegal Instruction)**
- PaddleOCR 2.6.2 ต้องการ AVX instruction set ซึ่ง CPU บน Desk-5439 ไม่รองรับ
- ลองลดรุ่นเป็น 2.5.2 → ยังมี dependency conflict กับ paddleocr 2.7.3
- ลองใช้ paddlepaddle-cpu → ยังคงมีปัญหา dependency
**Bug 2: OCR ภาษาไทยผิด**
- PaddleOCR ตั้งค่า `lang="en"` ทำให้ข้อความภาษาไทยถูกแปลงเป็นอักษรละตินผิดๆ
- เช่น: "10 กุมภาพันธ์ 2568" → "10 qunnwus 2568"
#### การแก้ไข (Fix)
เปลี่ยนจาก PaddleOCR เป็น Tesseract OCR เพื่อ:
1. แก้ปัญหา SIGILL บน CPU เก่าที่ไม่รองรับ AVX
2. รองรับภาษาไทยด้วย `tha+eng` language code
| ไฟล์ | การเปลี่ยนแปลง |
| ------------------------------------------------------------------------------------------- | ---------------------------------------------------------------------------- |
| `specs/04-Infrastructure-OPS/04-00-docker-compose/Desk-5439/ocr-sidecar/requirements.txt` | ลบ paddlepaddle/paddleocr, เพิ่ม pytesseract, Pillow |
| `specs/04-Infrastructure-OPS/04-00-docker-compose/Desk-5439/ocr-sidecar/app.py` | เปลี่ยนใช้ pytesseract, OCR_LANG เป็น `tha+eng`, ลบ PaddleOCR initialization |
| `specs/04-Infrastructure-OPS/04-00-docker-compose/Desk-5439/ocr-sidecar/Dockerfile` | ติดตั้ง tesseract-ocr, tesseract-ocr-tha, tesseract-ocr-eng |
| `specs/04-Infrastructure-OPS/04-00-docker-compose/Desk-5439/ocr-sidecar/docker-compose.yml` | OCR_LANG เป็น `tha+eng`, ลบ paddleocr_models volume |
| `backend/src/modules/ai/services/ocr.service.ts` | เปลี่ยน comment/error message จาก PaddleOCR เป็น Tesseract |
| `frontend/components/admin/ai/OcrSandboxPromptManager.tsx` | เปลี่ยน Badge text จาก PaddleOCR เป็น Tesseract |
#### กฎที่ Lock แล้ว
- Tesseract OCR ไม่ต้องการ AVX instruction set → ทำงานได้บน CPU ทุกรุ่น
- Tesseract รองรับภาษาไทยด้วย `tha+eng` language code
- API contract ยังเหมือนเดิม (`POST /ocr` คืน `{ text, ocrUsed }`) → backend/frontend ไม่ต้องเปลี่ยน logic
#### Verification ที่ต้องทำ
- Rebuild container บน Desk-5439: `docker compose down && docker compose up -d --build`
- ทดสอบ OCR ภาษาไทย: "10 กุมภาพันธ์ 2568" ควรออกมาถูกต้อง
---
### Session 9 — 2026-05-26 (System Memories Consolidation)
#### QNAP SSH Key Authentication & CI/CD Deployment #### QNAP SSH Key Authentication & CI/CD Deployment
@@ -649,6 +695,7 @@ npx playwright show-report # Generate report
**Summary:** ดำเนินการอิมพลีเมนต์ ADR-030 (Context-Aware Prompt Templates สำหรับการสกัดข้อมูลเอกสาร) และทำการแก้ไขบัคช่องว่างประเภทผู้รับ `'CC '` ในฐานข้อมูล **Summary:** ดำเนินการอิมพลีเมนต์ ADR-030 (Context-Aware Prompt Templates สำหรับการสกัดข้อมูลเอกสาร) และทำการแก้ไขบัคช่องว่างประเภทผู้รับ `'CC '` ในฐานข้อมูล
**Backend Changes (B1-B6):** **Backend Changes (B1-B6):**
- **AiPrompt Entity**: เพิ่มการแมปคอลัมน์ `contextConfig` ไปยัง JSON ฟิลด์ `context_config` ในฐานข้อมูลเพื่อควบคุม master data resolution - **AiPrompt Entity**: เพิ่มการแมปคอลัมน์ `contextConfig` ไปยัง JSON ฟิลด์ `context_config` ในฐานข้อมูลเพื่อควบคุม master data resolution
- **CreateAiPromptDto / Response DTO**: ปรับแต่งให้รองรับการรับและส่งออกคอลัมน์ `contextConfig` - **CreateAiPromptDto / Response DTO**: ปรับแต่งให้รองรับการรับและส่งออกคอลัมน์ `contextConfig`
- **AiPromptsService**: - **AiPromptsService**:
@@ -662,14 +709,17 @@ npx playwright show-report # Generate report
- แก้ไข mock dependencies ของ `AiPromptsService` ใน `ai-batch.processor.spec.ts` ป้องกันปัญหา `TypeError: getActive is not a function` ทำให้ผ่าน unit tests 100% - แก้ไข mock dependencies ของ `AiPromptsService` ใน `ai-batch.processor.spec.ts` ป้องกันปัญหา `TypeError: getActive is not a function` ทำให้ผ่าน unit tests 100%
**Database & Schema Changes (ADR-009):** **Database & Schema Changes (ADR-009):**
- **schema-02-tables.sql**: แก้ไข line 338 ปรับปรุง `ENUM('TO', 'CC ')` เป็น `ENUM('TO', 'CC')` - **schema-02-tables.sql**: แก้ไข line 338 ปรับปรุง `ENUM('TO', 'CC ')` เป็น `ENUM('TO', 'CC')`
- **SQL Delta**: สร้าง `2026-05-27-add-context-aware-prompts-and-cleanup.sql` ดำเนินการ `UPDATE` ข้อมูลเก่าที่เป็น `'CC '` ให้เป็น `'CC'` เพื่อล้างช่องว่าง จากนั้นสั่ง `ALTER TABLE` ปรับปรุงฟิลด์ enum และ Seed template ภาษาไทยเวอร์ชัน 2 - **SQL Delta**: สร้าง `2026-05-27-add-context-aware-prompts-and-cleanup.sql` ดำเนินการ `UPDATE` ข้อมูลเก่าที่เป็น `'CC '` ให้เป็น `'CC'` เพื่อล้างช่องว่าง จากนั้นสั่ง `ALTER TABLE` ปรับปรุงฟิลด์ enum และ Seed template ภาษาไทยเวอร์ชัน 2
- **Rollback SQL**: สร้างไฟล์ย้อนกลับ `2026-05-27-add-context-aware-prompts-and-cleanup.rollback.sql` เรียบร้อย - **Rollback SQL**: สร้างไฟล์ย้อนกลับ `2026-05-27-add-context-aware-prompts-and-cleanup.rollback.sql` เรียบร้อย
**Frontend Changes:** **Frontend Changes:**
- **detail.tsx**: ตรวจสอบการใช้งาน `normalizeRecipientType` ซึ่งครอบคลุมการล้างช่องว่างและการกรองผู้รับ TO/CC ได้อย่างทนทาน - **detail.tsx**: ตรวจสอบการใช้งาน `normalizeRecipientType` ซึ่งครอบคลุมการล้างช่องว่างและการกรองผู้รับ TO/CC ได้อย่างทนทาน
**Verification:** **Verification:**
- `pnpm --filter backend build` — ✅ Compile ผ่านแบบ Strict Mode - `pnpm --filter backend build` — ✅ Compile ผ่านแบบ Strict Mode
- unit tests AI module & backend suites — ✅ ผ่านทั้งหมด 60 suites / 521 tests - unit tests AI module & backend suites — ✅ ผ่านทั้งหมด 60 suites / 521 tests
@@ -4,10 +4,11 @@
# Change Log: # Change Log:
# - 2026-05-25: Initial Dockerfile สำหรับ PaddleOCR sidecar (port 8765) # - 2026-05-25: Initial Dockerfile สำหรับ PaddleOCR sidecar (port 8765)
# - 2026-05-30: เปลี่ยนจาก PaddleOCR เป็น Tesseract OCR เพื่อความเข้ากันได้กับ CPU เก่า # - 2026-05-30: เปลี่ยนจาก PaddleOCR เป็น Tesseract OCR เพื่อความเข้ากันได้กับ CPU เก่า
# - 2026-05-30: เพิ่ม system dependencies สำหรับ OpenCV (libsm6, libxext6, libxrender1, libfontconfig1, libx11-6)
FROM python:3.10-slim FROM python:3.10-slim
# ติดตั้ง system dependencies สำหรับ PDF processing, Tesseract OCR และภาษาไทย # ติดตั้ง system dependencies สำหรับ PDF processing, Tesseract OCR, ภาษาไทย และ OpenCV
RUN apt-get update && apt-get install -y --no-install-recommends \ RUN apt-get update && apt-get install -y --no-install-recommends \
libglib2.0-0 \ libglib2.0-0 \
libgl1 \ libgl1 \
@@ -16,6 +17,11 @@ RUN apt-get update && apt-get install -y --no-install-recommends \
tesseract-ocr \ tesseract-ocr \
tesseract-ocr-tha \ tesseract-ocr-tha \
tesseract-ocr-eng \ tesseract-ocr-eng \
libsm6 \
libxext6 \
libxrender1 \
libfontconfig1 \
libx11-6 \
&& rm -rf /var/lib/apt/lists/* && rm -rf /var/lib/apt/lists/*
WORKDIR /app WORKDIR /app
@@ -5,6 +5,7 @@
# - 2026-05-25: Initial FastAPI server สำหรับ PaddleOCR sidecar # - 2026-05-25: Initial FastAPI server สำหรับ PaddleOCR sidecar
# - 2026-05-30: เปลี่ยน lang='en' เป็น lang='ch' (CTJK) เพื่อรองรับภาษาไทย # - 2026-05-30: เปลี่ยน lang='en' เป็น lang='ch' (CTJK) เพื่อรองรับภาษาไทย
# - 2026-05-30: เปลี่ยนจาก PaddleOCR เป็น Tesseract OCR เพื่อความเข้ากันได้กับ CPU เก่า # - 2026-05-30: เปลี่ยนจาก PaddleOCR เป็น Tesseract OCR เพื่อความเข้ากันได้กับ CPU เก่า
# - 2026-05-30: เพิ่ม OpenCV preprocessing (threshold, denoise) และ DPI 300 เพื่อเพิ่มความแม่นยำ
import os import os
import logging import logging
@@ -15,6 +16,8 @@ from typing import Optional
from PIL import Image from PIL import Image
import pytesseract import pytesseract
import io import io
import cv2
import numpy as np
from fastapi import FastAPI, HTTPException from fastapi import FastAPI, HTTPException
from pydantic import BaseModel from pydantic import BaseModel
@@ -34,6 +37,26 @@ OCR_LANG = os.getenv("OCR_LANG", "tha+eng") # Tesseract language code (tha+eng
logger.info(f"Tesseract OCR Sidecar initialized (lang={OCR_LANG})") logger.info(f"Tesseract OCR Sidecar initialized (lang={OCR_LANG})")
def preprocess_image(pil_image: Image.Image) -> Image.Image:
"""Preprocess image ด้วย OpenCV เพื่อเพิ่มความแม่นยำ OCR"""
# แปลง PIL Image เป็น numpy array (OpenCV format)
img_array = np.array(pil_image)
# แปลงเป็น grayscale
gray = cv2.cvtColor(img_array, cv2.COLOR_RGB2GRAY)
# Denoise ด้วย median blur
denoised = cv2.medianBlur(gray, 3)
# Adaptive threshold เพื่อแยก background จาก text
thresh = cv2.adaptiveThreshold(
denoised, 255, cv2.ADAPTIVE_THRESH_GAUSSIAN_C, cv2.THRESH_BINARY, 11, 2
)
# แปลงกลับเป็น PIL Image
return Image.fromarray(thresh)
class OcrRequest(BaseModel): class OcrRequest(BaseModel):
pdfPath: str pdfPath: str
maxPages: Optional[int] = None maxPages: Optional[int] = None
@@ -89,10 +112,14 @@ def ocr_extract(req: OcrRequest):
ocr_text_parts = [] ocr_text_parts = []
for i in pages_to_process: for i in pages_to_process:
page = doc[i] page = doc[i]
pix = page.get_pixmap(dpi=200) pix = page.get_pixmap(dpi=300) # เพิ่ม DPI เป็น 300 เพื่อความชัด
img_bytes = pix.tobytes("png") img_bytes = pix.tobytes("png")
img = Image.open(io.BytesIO(img_bytes)) img = Image.open(io.BytesIO(img_bytes))
text = pytesseract.image_to_string(img, lang=OCR_LANG)
# Preprocess ด้วย OpenCV เพื่อเพิ่มความแม่นยำ
processed_img = preprocess_image(img)
text = pytesseract.image_to_string(processed_img, lang=OCR_LANG)
ocr_text_parts.append(text.strip()) ocr_text_parts.append(text.strip())
ocr_text = "\n".join(ocr_text_parts).strip() ocr_text = "\n".join(ocr_text_parts).strip()
@@ -2,6 +2,7 @@
# Change Log: # Change Log:
# - 2026-05-30: เปลี่ยนจาก PaddleOCR เป็น Tesseract OCR เพื่อความเข้ากันได้กับ CPU เก่า (ไม่ต้องการ AVX) # - 2026-05-30: เปลี่ยนจาก PaddleOCR เป็น Tesseract OCR เพื่อความเข้ากันได้กับ CPU เก่า (ไม่ต้องการ AVX)
# - 2026-05-30: ลบ paddlepaddle/paddleocr dependencies เนื่องจาก SIGILL บน CPU ที่ไม่รองรับ AVX # - 2026-05-30: ลบ paddlepaddle/paddleocr dependencies เนื่องจาก SIGILL บน CPU ที่ไม่รองรับ AVX
# - 2026-05-30: เพิ่ม opencv-python สำหรับ image preprocessing (threshold, denoise) เพื่อเพิ่มความแม่นยำ OCR
numpy<2.0 numpy<2.0
PyMuPDF==1.24.0 PyMuPDF==1.24.0
@@ -12,3 +13,4 @@ python-multipart==0.0.9
pythainlp==5.0.4 pythainlp==5.0.4
httpx==0.27.0 httpx==0.27.0
Pillow==10.0.0 Pillow==10.0.0
opencv-python==4.8.1.78