ocr / ocr.py
borreooo's picture
Fix PaddleOCR constructor args
56f5eaf
Raw
History Blame Contribute Delete
4.37 kB
import cv2
import os
import json
import numpy as np
from preprocess import preprocess_chassis
CONFIG_PATH = "config.json"
_ocr = None
def load_config():
if os.path.exists(CONFIG_PATH):
with open(CONFIG_PATH) as f:
return json.load(f)
return {
"confusion_map": {},
"error_correction": {"max_errors": 2, "window_size": 3}
}
def get_ocr():
global _ocr
if _ocr is None:
from paddleocr import PaddleOCR
_ocr = PaddleOCR(use_angle_cls=True, lang='en')
return _ocr
def ocr_image(img):
ocr = get_ocr()
result = ocr.ocr(img, cls=True)
if not result or not result[0]:
return "", 0.0
texts = [line[1][0] for line in result[0]]
confs = [line[1][1] for line in result[0]]
full_text = "".join(texts).upper()
full_text = "".join(c for c in full_text if c.isalnum())
avg_conf = sum(confs) / len(confs) if confs else 0.0
return full_text, avg_conf
def read_chassis(image_path, save_comparison=False):
variations = preprocess_chassis(image_path, save_comparison=save_comparison)
best_text, best_conf, best_score = "", 0.0, -1
for var in variations:
text, conf = ocr_image(var)
score = conf * max(len(text), 1)
if score > best_score:
best_text, best_conf, best_score = text, conf, score
return best_text, best_conf
def can_substitute(got, want, confusion_map):
return want in confusion_map.get(got, []) or got in confusion_map.get(want, [])
def apply_substitutions(ocr_text, expected_text, confusion_map, max_errors):
if len(ocr_text) != len(expected_text):
return ocr_text, False
diffs = [(i, ocr_text[i], expected_text[i])
for i in range(len(ocr_text)) if ocr_text[i] != expected_text[i]]
if len(diffs) > max_errors:
return ocr_text, False
corrected = list(ocr_text)
for i, got, want in diffs:
if can_substitute(got, want, confusion_map):
corrected[i] = want
else:
return ocr_text, False
return "".join(corrected), True
def best_window_match(ocr_text, expected_text, window_size):
exp_len = len(expected_text)
best, best_diffs = None, exp_len + 1
for start in range(max(0, len(ocr_text) - exp_len) + 1):
candidate = ocr_text[start:start + exp_len]
if len(candidate) != exp_len:
continue
diffs = sum(1 for a, b in zip(candidate, expected_text) if a != b)
if diffs < best_diffs:
best_diffs = diffs
best = (candidate, diffs)
if best and best[1] <= window_size:
return best
return None
def postprocess_with_hint(ocr_text, expected_text):
config = load_config()
confusion_map = config.get("confusion_map", {})
ec = config.get("error_correction", {"max_errors": 2, "window_size": 3})
max_errors = ec["max_errors"]
window_size = ec["window_size"]
if not ocr_text:
return ocr_text, False
if ocr_text == expected_text:
return ocr_text, True
if expected_text in ocr_text:
return expected_text, True
if len(ocr_text) == len(expected_text):
corrected, fixed = apply_substitutions(
ocr_text, expected_text, confusion_map, max_errors)
if fixed:
return corrected, True
if abs(len(ocr_text) - len(expected_text)) <= window_size:
match = best_window_match(ocr_text, expected_text, window_size)
if match:
candidate, diffs = match
if diffs == 0:
return candidate, True
corrected, fixed = apply_substitutions(
candidate, expected_text, confusion_map, max_errors)
if fixed:
return corrected, True
if len(ocr_text) < len(expected_text):
suffix = expected_text[-len(ocr_text):]
corrected, fixed = apply_substitutions(
ocr_text, suffix, confusion_map, max_errors=1)
if fixed or ocr_text == suffix:
return expected_text, True
return ocr_text, False
if __name__ == "__main__":
import sys
if len(sys.argv) < 2:
print("Usage: python ocr.py <chassis_image_path>")
else:
text, conf = read_chassis(sys.argv[1], save_comparison=True)
print(f"Result : {text}")
print(f"Confidence : {conf:.2%}")