2020-07-14 23:59:48 +08:00
|
|
|
import os
|
|
|
|
|
import numpy as np
|
2026-03-22 19:22:53 +08:00
|
|
|
import cv2
|
2024-04-09 21:46:30 +08:00
|
|
|
from PIL import Image
|
|
|
|
|
|
|
|
|
|
from module.exception import RequestHumanTakeover
|
|
|
|
|
from module.logger import logger
|
2026-03-23 16:30:23 +08:00
|
|
|
from module.config.config import AzurLaneConfig
|
|
|
|
|
|
|
|
|
|
try:
|
|
|
|
|
from rapidocr import RapidOCR, OCRVersion
|
|
|
|
|
except Exception as e:
|
|
|
|
|
logger.critical(f'Failed to load OCR dependencies: {e}')
|
2026-03-26 13:10:59 +08:00
|
|
|
logger.critical('无法加载 OCR 依赖,请安装微软 C++ 运行库 https://aka.ms/vs/17/release/vc_redist.x64.exe')
|
|
|
|
|
logger.critical('也有可能是 GPU 不支持加速引起,请尝试关闭 GPU 加速')
|
|
|
|
|
logger.critical('如果上述方法都无法解决,请加群获取支持')
|
2026-03-23 16:30:23 +08:00
|
|
|
raise RequestHumanTakeover
|
|
|
|
|
|
|
|
|
|
USE_GPU = False
|
|
|
|
|
config_name = os.environ.get('ALAS_CONFIG_NAME')
|
|
|
|
|
if config_name:
|
|
|
|
|
config = AzurLaneConfig(config_name)
|
|
|
|
|
val = config.Optimization_UseOcrGpuAcceleration
|
|
|
|
|
if val is False:
|
|
|
|
|
logger.info(f'OCR GPU acceleration disabled by config/{config_name}.json')
|
|
|
|
|
USE_GPU = False
|
|
|
|
|
else:
|
|
|
|
|
USE_GPU = True
|
|
|
|
|
|
|
|
|
|
class CnModel:
|
|
|
|
|
def __init__(self):
|
|
|
|
|
self.params = {
|
|
|
|
|
"Global.use_det": False,
|
|
|
|
|
"Global.use_cls": False,
|
|
|
|
|
"Det.model_path": None,
|
|
|
|
|
"Cls.model_path": None,
|
|
|
|
|
"Rec.ocr_version": OCRVersion.PPOCRV5,
|
|
|
|
|
"Rec.model_path": "bin/ocr_models/zh-CN/alocr-zh-cn-v2.5.dtk.onnx",
|
|
|
|
|
"Rec.rec_keys_path": "bin/ocr_models/zh-CN/cn.txt",
|
|
|
|
|
"EngineConfig.onnxruntime.use_dml": USE_GPU
|
|
|
|
|
}
|
2026-03-24 13:13:16 +08:00
|
|
|
self.model = RapidOCR(params=self.params)
|
2026-03-23 16:30:23 +08:00
|
|
|
|
|
|
|
|
class EnModel:
|
|
|
|
|
def __init__(self):
|
|
|
|
|
self.params = {
|
|
|
|
|
"Global.use_det": False,
|
|
|
|
|
"Global.use_cls": False,
|
|
|
|
|
"Det.model_path": None,
|
|
|
|
|
"Cls.model_path": None,
|
|
|
|
|
"Rec.ocr_version": OCRVersion.PPOCRV4,
|
|
|
|
|
"Rec.model_path": "bin/ocr_models/en-US/alocr-en-us-v2.0.nvc.onnx",
|
|
|
|
|
"Rec.rec_keys_path": "bin/ocr_models/en-US/en.txt",
|
|
|
|
|
"EngineConfig.onnxruntime.use_dml": USE_GPU
|
|
|
|
|
}
|
2026-03-24 13:13:16 +08:00
|
|
|
self.model = RapidOCR(params=self.params)
|
2026-03-23 16:30:23 +08:00
|
|
|
|
|
|
|
|
cn_model = CnModel()
|
|
|
|
|
en_model = EnModel()
|
2024-04-09 21:46:30 +08:00
|
|
|
|
2026-03-22 19:22:53 +08:00
|
|
|
class AlOcr:
|
|
|
|
|
def __init__(self, **kwargs):
|
|
|
|
|
self.model = None
|
2026-03-23 09:31:39 +08:00
|
|
|
self.name = kwargs.get('name', 'en')
|
|
|
|
|
self.params = {}
|
2020-09-08 14:08:04 +08:00
|
|
|
self._model_loaded = False
|
2026-03-23 16:30:23 +08:00
|
|
|
logger.info(f"Created AlOcr instance: name='{self.name}', kwargs={kwargs}, PID={os.getpid()}")
|
2026-03-22 19:22:53 +08:00
|
|
|
|
|
|
|
|
def init(self):
|
2026-03-23 16:30:23 +08:00
|
|
|
if self.name in ['cn', 'zhcn']:
|
|
|
|
|
self.model = cn_model.model
|
2026-03-22 19:22:53 +08:00
|
|
|
else:
|
2026-03-23 16:30:23 +08:00
|
|
|
self.model = en_model.model
|
2026-03-22 19:22:53 +08:00
|
|
|
self._model_loaded = True
|
|
|
|
|
|
|
|
|
|
def _ensure_loaded(self):
|
2020-09-08 14:08:04 +08:00
|
|
|
if not self._model_loaded:
|
2026-03-22 19:22:53 +08:00
|
|
|
self.init()
|
|
|
|
|
|
|
|
|
|
def ocr(self, img_fp):
|
2026-03-23 11:31:53 +08:00
|
|
|
logger.info(f"[VERBOSE] AlOcr.ocr: Ensure loaded...")
|
2026-03-22 19:22:53 +08:00
|
|
|
self._ensure_loaded()
|
2026-03-24 13:13:16 +08:00
|
|
|
|
2026-03-23 11:31:53 +08:00
|
|
|
try:
|
2026-03-23 16:30:23 +08:00
|
|
|
res = self.model(img_fp)
|
|
|
|
|
if hasattr(res, 'txts') and res.txts:
|
2026-03-24 13:13:16 +08:00
|
|
|
return res.txts[0]
|
|
|
|
|
return ""
|
2026-03-23 16:30:23 +08:00
|
|
|
except Exception as e:
|
|
|
|
|
logger.error(f"AlOcr.ocr exception: {e}")
|
|
|
|
|
raise
|
2020-09-08 14:08:04 +08:00
|
|
|
|
|
|
|
|
def ocr_for_single_line(self, img_fp):
|
2026-03-22 19:22:53 +08:00
|
|
|
return self.ocr(img_fp)
|
2020-09-08 14:08:04 +08:00
|
|
|
|
|
|
|
|
def ocr_for_single_lines(self, img_list):
|
2026-03-22 19:22:53 +08:00
|
|
|
self._ensure_loaded()
|
|
|
|
|
results = []
|
2026-03-23 11:31:53 +08:00
|
|
|
for i, img in enumerate(img_list):
|
|
|
|
|
try:
|
2026-03-23 16:30:23 +08:00
|
|
|
res = self.model(img)
|
|
|
|
|
if hasattr(res, 'txts') and res.txts:
|
|
|
|
|
results.append(res.txts[0])
|
2026-03-22 19:22:53 +08:00
|
|
|
else:
|
2026-03-23 16:30:23 +08:00
|
|
|
results.append("")
|
2026-03-23 11:31:53 +08:00
|
|
|
except Exception as e:
|
2026-03-23 16:30:23 +08:00
|
|
|
logger.error(f"AlOcr.ocr_for_single_lines exception on image {i}: {e}")
|
|
|
|
|
raise
|
2026-03-22 19:22:53 +08:00
|
|
|
return results
|
2020-09-08 14:08:04 +08:00
|
|
|
|
|
|
|
|
def set_cand_alphabet(self, cand_alphabet):
|
2026-03-22 19:22:53 +08:00
|
|
|
pass
|
2024-05-09 02:24:28 +08:00
|
|
|
|
|
|
|
|
def atomic_ocr(self, img_fp, cand_alphabet=None):
|
2026-03-22 19:22:53 +08:00
|
|
|
res = self.ocr(img_fp)
|
|
|
|
|
if cand_alphabet:
|
|
|
|
|
res = ''.join([c for c in res if c in cand_alphabet])
|
|
|
|
|
return res
|
2024-05-09 02:24:28 +08:00
|
|
|
|
|
|
|
|
def atomic_ocr_for_single_line(self, img_fp, cand_alphabet=None):
|
2026-03-22 19:22:53 +08:00
|
|
|
res = self.ocr_for_single_line(img_fp)
|
|
|
|
|
if cand_alphabet:
|
|
|
|
|
res = ''.join([c for c in res if c in cand_alphabet])
|
|
|
|
|
return res
|
2024-05-09 02:24:28 +08:00
|
|
|
|
|
|
|
|
def atomic_ocr_for_single_lines(self, img_list, cand_alphabet=None):
|
2026-03-22 19:22:53 +08:00
|
|
|
results = self.ocr_for_single_lines(img_list)
|
|
|
|
|
if cand_alphabet:
|
|
|
|
|
results = [''.join([c for c in res if c in cand_alphabet]) for res in results]
|
2026-03-24 13:13:16 +08:00
|
|
|
return results
|