|
|
@@ -403,7 +403,7 @@ def call_paddleocr(image_path: str) -> Optional[Dict[str, Any]]:
|
|
|
try:
|
|
|
# 检查图片文件是否存在
|
|
|
if not os.path.exists(image_path):
|
|
|
- logger.error(f"[PaddleOCR] 图片文件不存在: {image_path}")
|
|
|
+ logger.error(f"[PaddleOCR 图表识别] 图片文件不存在: {image_path}")
|
|
|
return None
|
|
|
|
|
|
# 生成输出目录和基础文件名
|
|
|
@@ -411,8 +411,7 @@ def call_paddleocr(image_path: str) -> Optional[Dict[str, Any]]:
|
|
|
image_basename = os.path.splitext(os.path.basename(image_path))[0]
|
|
|
save_path_base = os.path.join(image_dir, image_basename)
|
|
|
|
|
|
- # 构建paddleocr命令,添加所有参数(NPU 下需加 --device npu:0,否则走 CPU 易段错误)
|
|
|
- # PaddleOCR会在save_path下创建目录,文件保存在该目录内
|
|
|
+ # 构建paddleocr命令(图表识别:开启 use_chart_recognition / use_layout_detection)
|
|
|
cmd = [
|
|
|
_get_paddleocr_executable(), "doc_parser", "-i", image_path,
|
|
|
"--precision", "fp32",
|
|
|
@@ -434,7 +433,7 @@ def call_paddleocr(image_path: str) -> Optional[Dict[str, Any]]:
|
|
|
# env["FLAGS_fraction_of_gpu_memory_to_use"] = "0.3" # 只使用30%的GPU内存
|
|
|
# env["FLAGS_allocator_strategy"] = "auto_growth" # 使用自动增长策略,避免一次性分配过多内存
|
|
|
|
|
|
- logger.info(f"[PaddleOCR] 执行命令: {' '.join(cmd)}")
|
|
|
+ logger.info(f"[PaddleOCR 图表识别] 执行命令: {' '.join(cmd)}")
|
|
|
|
|
|
# 执行命令(env 含 LD_PRELOAD 与 PADDLE_PDX_DISABLE_MODEL_SOURCE_CHECK,避免 static TLS / 模型源检查)
|
|
|
result = subprocess.run(
|
|
|
@@ -447,59 +446,52 @@ def call_paddleocr(image_path: str) -> Optional[Dict[str, Any]]:
|
|
|
)
|
|
|
|
|
|
if result.returncode != 0:
|
|
|
- logger.error(f"[PaddleOCR] 命令执行失败,返回码: {result.returncode}")
|
|
|
+ logger.error(f"[PaddleOCR 图表识别] 命令执行失败,返回码: {result.returncode}")
|
|
|
# doc_parser 已知问题:PP-DocLayoutV3 返回 3 值而管道按 2 值解包,报 "too many values to unpack (expected 2)"
|
|
|
if result.stderr and ("too many values to unpack" in result.stderr or "Exception from the 'cv' worker" in result.stderr):
|
|
|
logger.warning(
|
|
|
- "[PaddleOCR] doc_parser 报 cv worker 解包错误,多为 PaddleX 与 PP-DocLayoutV3 不兼容。"
|
|
|
- " 可尝试: pip install -U paddlex;或仅需文字时改用 ocr 模式。详见 README_STARTUP.md。"
|
|
|
+ "[PaddleOCR 图表识别] doc_parser 报 cv worker 解包错误,多为 PaddleX 与 PP-DocLayoutV3 不兼容。"
|
|
|
+ " 可尝试: pip install -U paddlex;或仅需文字时改用 文本识别。详见 README_STARTUP.md。"
|
|
|
)
|
|
|
# 完整 stderr 便于排查(NPU 初始化日志较长,真正错误常在末尾)
|
|
|
if result.stderr:
|
|
|
- logger.error(f"[PaddleOCR] stderr: {result.stderr}")
|
|
|
+ logger.error(f"[PaddleOCR 图表识别] stderr: {result.stderr}")
|
|
|
if result.stdout:
|
|
|
- logger.error(f"[PaddleOCR] stdout(末 2000 字符): {result.stdout[-2000:] if len(result.stdout) > 2000 else result.stdout}")
|
|
|
+ logger.error(f"[PaddleOCR 图表识别] stdout(末 2000 字符): {result.stdout[-2000:] if len(result.stdout) > 2000 else result.stdout}")
|
|
|
return None
|
|
|
|
|
|
# 从保存的Markdown文件中读取结果
|
|
|
- # PaddleOCR会在save_path下创建目录,文件路径为: {save_path}/{basename}.md
|
|
|
md_file = os.path.join(save_path_base, f"{image_basename}.md")
|
|
|
if os.path.exists(md_file):
|
|
|
- logger.info(f"[PaddleOCR] 从Markdown文件读取结果: {md_file}")
|
|
|
+ logger.info(f"[PaddleOCR 图表识别] 从Markdown文件读取结果: {md_file}")
|
|
|
try:
|
|
|
with open(md_file, 'r', encoding='utf-8') as f:
|
|
|
markdown_content = f.read()
|
|
|
if markdown_content.strip():
|
|
|
- # 将markdown内容转换为标准格式
|
|
|
- # 为了兼容现有代码,我们需要将markdown转换回parsing_res_list格式
|
|
|
- # 但实际上,我们可以直接返回markdown内容,让调用方处理
|
|
|
- # 这里我们返回一个特殊标记,表示这是markdown格式
|
|
|
- logger.info(f"[PaddleOCR] 成功读取Markdown文件,内容长度: {len(markdown_content)} 字符")
|
|
|
- # 返回markdown内容,使用特殊键标记
|
|
|
+ logger.info(f"[PaddleOCR 图表识别] 成功读取Markdown文件,内容长度: {len(markdown_content)} 字符")
|
|
|
return {"markdown_content": markdown_content}
|
|
|
else:
|
|
|
- logger.warning("[PaddleOCR] Markdown文件内容为空")
|
|
|
+ logger.warning("[PaddleOCR 图表识别] Markdown文件内容为空")
|
|
|
except Exception as e:
|
|
|
- logger.exception(f"[PaddleOCR] 读取Markdown文件失败: {e}")
|
|
|
+ logger.exception(f"[PaddleOCR 图表识别] 读取Markdown文件失败: {e}")
|
|
|
else:
|
|
|
- logger.warning(f"[PaddleOCR] Markdown文件不存在: {md_file}")
|
|
|
+ logger.warning(f"[PaddleOCR 图表识别] Markdown文件不存在: {md_file}")
|
|
|
|
|
|
- # 如果Markdown文件不存在或读取失败,尝试从stdout解析
|
|
|
output_text = result.stdout.strip()
|
|
|
if output_text:
|
|
|
- logger.info("[PaddleOCR] 从stdout解析输出")
|
|
|
+ logger.info("[PaddleOCR 图表识别] 从stdout解析输出")
|
|
|
parsed_result = parse_paddleocr_output(output_text)
|
|
|
- logger.info(f"[PaddleOCR] 解析成功,获得 {len(parsed_result.get('parsing_res_list', []))} 个区块")
|
|
|
+ logger.info(f"[PaddleOCR 图表识别] 解析成功,获得 {len(parsed_result.get('parsing_res_list', []))} 个区块")
|
|
|
return parsed_result
|
|
|
else:
|
|
|
- logger.warning("[PaddleOCR] stdout输出为空,且未找到Markdown文件")
|
|
|
+ logger.warning("[PaddleOCR 图表识别] stdout输出为空,且未找到Markdown文件")
|
|
|
return None
|
|
|
|
|
|
except subprocess.TimeoutExpired:
|
|
|
- logger.error("[PaddleOCR] 命令执行超时")
|
|
|
+ logger.error("[PaddleOCR 图表识别] 命令执行超时")
|
|
|
return None
|
|
|
except Exception as e:
|
|
|
- logger.exception(f"[PaddleOCR] 调用失败: {e}")
|
|
|
+ logger.exception(f"[PaddleOCR 图表识别] 调用失败: {e}")
|
|
|
return None
|
|
|
|
|
|
|
|
|
@@ -764,14 +756,14 @@ def call_paddleocr_ocr(image_path: str, save_path: str) -> tuple[Optional[List[s
|
|
|
"""
|
|
|
try:
|
|
|
if not os.path.exists(image_path):
|
|
|
- logger.error(f"[PaddleOCR OCR] 图片文件不存在: {image_path}")
|
|
|
+ logger.error(f"[PaddleOCR 文本识别] 图片文件不存在: {image_path}")
|
|
|
return None, None
|
|
|
|
|
|
image_basename = os.path.splitext(os.path.basename(image_path))[0]
|
|
|
save_path_base = os.path.join(save_path, image_basename)
|
|
|
os.makedirs(save_path_base, exist_ok=True)
|
|
|
|
|
|
- # 使用与 call_paddleocr 一致的不识别图表的 doc_parser 参数(无 --use_table_recognition)
|
|
|
+ # 使用不识别图表的 doc_parser 参数(文本识别,无 --use_table_recognition)
|
|
|
cmd = [
|
|
|
_get_paddleocr_executable(), "doc_parser", "-i", image_path,
|
|
|
"--precision", "fp32",
|
|
|
@@ -786,7 +778,7 @@ def call_paddleocr_ocr(image_path: str, save_path: str) -> tuple[Optional[List[s
|
|
|
if VL_REC_SERVER_URL:
|
|
|
cmd.extend(["--vl_rec_server_url", VL_REC_SERVER_URL])
|
|
|
|
|
|
- logger.info(f"[PaddleOCR OCR] 执行命令(doc_parser): {' '.join(cmd)}")
|
|
|
+ logger.info(f"[PaddleOCR 文本识别] 执行命令(doc_parser): {' '.join(cmd)}")
|
|
|
|
|
|
result = subprocess.run(
|
|
|
cmd,
|
|
|
@@ -798,11 +790,11 @@ def call_paddleocr_ocr(image_path: str, save_path: str) -> tuple[Optional[List[s
|
|
|
)
|
|
|
|
|
|
if result.returncode != 0:
|
|
|
- logger.error(f"[PaddleOCR OCR] doc_parser 执行失败,返回码: {result.returncode}")
|
|
|
+ logger.error(f"[PaddleOCR 文本识别] doc_parser 执行失败,返回码: {result.returncode}")
|
|
|
if result.stderr and ("too many values to unpack" in result.stderr or "Exception from the 'cv' worker" in result.stderr):
|
|
|
- logger.warning("[PaddleOCR OCR] doc_parser 报 cv worker 解包错误,详见 README_STARTUP.md。")
|
|
|
+ logger.warning("[PaddleOCR 文本识别] doc_parser 报 cv worker 解包错误,详见 README_STARTUP.md。")
|
|
|
if result.stderr:
|
|
|
- logger.error(f"[PaddleOCR OCR] 错误输出: {result.stderr}")
|
|
|
+ logger.error(f"[PaddleOCR 文本识别] 错误输出: {result.stderr}")
|
|
|
return None, None
|
|
|
|
|
|
texts = []
|
|
|
@@ -814,7 +806,7 @@ def call_paddleocr_ocr(image_path: str, save_path: str) -> tuple[Optional[List[s
|
|
|
if md_content.strip():
|
|
|
texts = markdown_to_plain_text(md_content)
|
|
|
except Exception as e:
|
|
|
- logger.exception(f"[PaddleOCR OCR] 读取 Markdown 失败: {e}")
|
|
|
+ logger.exception(f"[PaddleOCR 文本识别] 读取 Markdown 失败: {e}")
|
|
|
if not texts and result.stdout.strip():
|
|
|
parsed = parse_paddleocr_output(result.stdout.strip())
|
|
|
for item in parsed.get("parsing_res_list", []):
|
|
|
@@ -825,7 +817,7 @@ def call_paddleocr_ocr(image_path: str, save_path: str) -> tuple[Optional[List[s
|
|
|
else:
|
|
|
texts.append(block)
|
|
|
if not texts:
|
|
|
- logger.warning("[PaddleOCR OCR] doc_parser 未得到文本")
|
|
|
+ logger.warning("[PaddleOCR 文本识别] doc_parser 未得到文本")
|
|
|
return None, None
|
|
|
|
|
|
json_file = os.path.join(save_path, f"{image_basename}_res.json")
|
|
|
@@ -833,17 +825,17 @@ def call_paddleocr_ocr(image_path: str, save_path: str) -> tuple[Optional[List[s
|
|
|
with open(json_file, "w", encoding="utf-8") as f:
|
|
|
json.dump({"rec_texts": texts}, f, ensure_ascii=False, indent=0)
|
|
|
except Exception as e:
|
|
|
- logger.exception(f"[PaddleOCR OCR] 写入 rec_texts JSON 失败: {e}")
|
|
|
+ logger.exception(f"[PaddleOCR 文本识别] 写入 rec_texts JSON 失败: {e}")
|
|
|
return texts, None
|
|
|
|
|
|
- logger.info(f"[PaddleOCR OCR] doc_parser 成功提取 {len(texts)} 个文本片段,JSON: {json_file}")
|
|
|
+ logger.info(f"[PaddleOCR 文本识别] doc_parser 成功提取 {len(texts)} 个文本片段,JSON: {json_file}")
|
|
|
return texts, json_file
|
|
|
|
|
|
except subprocess.TimeoutExpired:
|
|
|
- logger.error("[PaddleOCR OCR] 命令执行超时")
|
|
|
+ logger.error("[PaddleOCR 文本识别] 命令执行超时")
|
|
|
return None, None
|
|
|
except Exception as e:
|
|
|
- logger.exception(f"[PaddleOCR OCR] 调用失败: {e}")
|
|
|
+ logger.exception(f"[PaddleOCR 文本识别] 调用失败: {e}")
|
|
|
return None, None
|
|
|
|
|
|
|
|
|
@@ -859,16 +851,15 @@ def call_paddleocr_doc_parser_for_text(image_path: str, save_path: str) -> tuple
|
|
|
"""
|
|
|
try:
|
|
|
if not os.path.exists(image_path):
|
|
|
- logger.error(f"[PaddleOCR DocParser] 图片文件不存在: {image_path}")
|
|
|
+ logger.error(f"[PaddleOCR 图表识别] 图片文件不存在: {image_path}")
|
|
|
return None, None
|
|
|
|
|
|
- # 生成输出目录和基础文件名
|
|
|
+ # 生成输出目录和基础文件名(图表识别:开启 use_chart_recognition)
|
|
|
image_dir = os.path.dirname(image_path)
|
|
|
image_basename = os.path.splitext(os.path.basename(image_path))[0]
|
|
|
save_path_base = os.path.join(save_path, image_basename)
|
|
|
os.makedirs(save_path_base, exist_ok=True)
|
|
|
|
|
|
- # 构建paddleocr doc_parser命令(NPU 下需加 --device npu:0,否则走 CPU 易段错误)
|
|
|
cmd = [
|
|
|
_get_paddleocr_executable(), "doc_parser", "-i", image_path,
|
|
|
"--precision", "fp32",
|
|
|
@@ -877,14 +868,12 @@ def call_paddleocr_doc_parser_for_text(image_path: str, save_path: str) -> tuple
|
|
|
"--use_chart_recognition", "True",
|
|
|
"--save_path", save_path_base
|
|
|
] + _paddle_ocr_device_args()
|
|
|
-
|
|
|
- # 添加 VL 识别后端配置(如果已配置)
|
|
|
if VL_REC_BACKEND:
|
|
|
cmd.extend(["--vl_rec_backend", VL_REC_BACKEND])
|
|
|
if VL_REC_SERVER_URL:
|
|
|
cmd.extend(["--vl_rec_server_url", VL_REC_SERVER_URL])
|
|
|
|
|
|
- logger.info(f"[PaddleOCR DocParser] 执行命令: {' '.join(cmd)}")
|
|
|
+ logger.info(f"[PaddleOCR 图表识别] 执行命令: {' '.join(cmd)}")
|
|
|
|
|
|
# 执行命令(env 含 LD_PRELOAD 与 PADDLE_PDX_DISABLE_MODEL_SOURCE_CHECK)
|
|
|
result = subprocess.run(
|
|
|
@@ -897,53 +886,47 @@ def call_paddleocr_doc_parser_for_text(image_path: str, save_path: str) -> tuple
|
|
|
)
|
|
|
|
|
|
if result.returncode != 0:
|
|
|
- logger.error(f"[PaddleOCR DocParser] 命令执行失败,返回码: {result.returncode}")
|
|
|
+ logger.error(f"[PaddleOCR 图表识别] 命令执行失败,返回码: {result.returncode}")
|
|
|
if result.stderr and ("too many values to unpack" in result.stderr or "Exception from the 'cv' worker" in result.stderr):
|
|
|
logger.warning(
|
|
|
- "[PaddleOCR DocParser] 报 cv worker 解包错误,多为 PaddleX 与 PP-DocLayoutV3 不兼容。"
|
|
|
- " 可尝试: pip install -U paddlex;或改用 ocr 模式提取文字。详见 README_STARTUP.md。"
|
|
|
+ "[PaddleOCR 图表识别] 报 cv worker 解包错误,多为 PaddleX 与 PP-DocLayoutV3 不兼容。"
|
|
|
+ " 可尝试: pip install -U paddlex;或改用 文本识别 提取文字。详见 README_STARTUP.md。"
|
|
|
)
|
|
|
- logger.error(f"[PaddleOCR DocParser] 错误输出: {result.stderr}")
|
|
|
+ logger.error(f"[PaddleOCR 图表识别] 错误输出: {result.stderr}")
|
|
|
return None, None
|
|
|
|
|
|
- # 查找保存的Markdown文件
|
|
|
- # PaddleOCR会在save_path下创建目录,文件路径为: {save_path}/{basename}.md
|
|
|
md_file = os.path.join(save_path_base, f"{image_basename}.md")
|
|
|
-
|
|
|
- # 也可能在子目录中
|
|
|
if not os.path.exists(md_file):
|
|
|
md_files = sorted(Path(save_path_base).rglob("*.md"))
|
|
|
if md_files:
|
|
|
md_file = str(md_files[0])
|
|
|
- logger.info(f"[PaddleOCR DocParser] 在子目录中找到Markdown文件: {md_file}")
|
|
|
+ logger.info(f"[PaddleOCR 图表识别] 在子目录中找到Markdown文件: {md_file}")
|
|
|
|
|
|
if not os.path.exists(md_file):
|
|
|
- logger.warning(f"[PaddleOCR DocParser] Markdown文件不存在: {md_file}")
|
|
|
+ logger.warning(f"[PaddleOCR 图表识别] Markdown文件不存在: {md_file}")
|
|
|
return None, None
|
|
|
|
|
|
- # 读取Markdown文件并转换为纯文本
|
|
|
try:
|
|
|
with open(md_file, 'r', encoding='utf-8') as f:
|
|
|
markdown_content = f.read()
|
|
|
|
|
|
if not markdown_content.strip():
|
|
|
- logger.warning("[PaddleOCR DocParser] Markdown文件内容为空")
|
|
|
+ logger.warning("[PaddleOCR 图表识别] Markdown文件内容为空")
|
|
|
return [], md_file
|
|
|
|
|
|
- # 将Markdown转换为纯文本列表
|
|
|
plain_text_lines = markdown_to_plain_text(markdown_content)
|
|
|
- logger.info(f"[PaddleOCR DocParser] 成功提取 {len(plain_text_lines)} 行纯文本,Markdown文件: {md_file}")
|
|
|
+ logger.info(f"[PaddleOCR 图表识别] 成功提取 {len(plain_text_lines)} 行纯文本,Markdown文件: {md_file}")
|
|
|
return plain_text_lines, md_file
|
|
|
|
|
|
except Exception as e:
|
|
|
- logger.exception(f"[PaddleOCR DocParser] 读取Markdown文件失败: {e}")
|
|
|
+ logger.exception(f"[PaddleOCR 图表识别] 读取Markdown文件失败: {e}")
|
|
|
return None, md_file
|
|
|
|
|
|
except subprocess.TimeoutExpired:
|
|
|
- logger.error("[PaddleOCR DocParser] 命令执行超时")
|
|
|
+ logger.error("[PaddleOCR 图表识别] 命令执行超时")
|
|
|
return None, None
|
|
|
except Exception as e:
|
|
|
- logger.exception(f"[PaddleOCR DocParser] 调用失败: {e}")
|
|
|
+ logger.exception(f"[PaddleOCR 图表识别] 调用失败: {e}")
|
|
|
return None, None
|
|
|
|
|
|
|
|
|
@@ -1732,7 +1715,7 @@ def fallback_parse_with_paddleocr(
|
|
|
return None
|
|
|
|
|
|
# 使用doc_parser模式解析文档结构
|
|
|
- logger.info("[PaddleOCR备用] 使用doc_parser模式解析文档结构")
|
|
|
+ logger.info("[PaddleOCR备用] 使用doc_parser模式解析文档结构(图表识别)")
|
|
|
paddleocr_result = call_paddleocr(image_path)
|
|
|
if not paddleocr_result:
|
|
|
logger.error("[PaddleOCR备用] PaddleOCR解析失败")
|
|
|
@@ -1787,7 +1770,7 @@ def fallback_parse_with_paddleocr(
|
|
|
return None
|
|
|
|
|
|
# 调用paddleocr ocr提取关键词来补充数据(作为doc_parser的补充)
|
|
|
- logger.info("[PaddleOCR备用] 调用OCR提取关键词补充数据")
|
|
|
+ logger.info("[PaddleOCR备用] 调用文本识别提取关键词补充数据")
|
|
|
ocr_save_path = os.path.dirname(image_path) # 使用图片所在目录作为保存路径
|
|
|
ocr_texts, _ = call_paddleocr_ocr(image_path, ocr_save_path)
|
|
|
|