merge_dotsocr_paddleocr.py 9.9 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313
  1. """
  2. 合并 DotsOCR 和 PaddleOCR 的结果
  3. 主程序入口
  4. """
  5. import json
  6. import argparse
  7. from pathlib import Path
  8. try:
  9. from .dotsocr_merger import DotsOCRMerger
  10. except ImportError:
  11. from dotsocr_merger import DotsOCRMerger
  12. def merge_single_file(dotsocr_file: Path, paddle_file: Path, output_dir: Path,
  13. output_type: str, merger: DotsOCRMerger) -> bool:
  14. """
  15. 合并单个文件
  16. Args:
  17. dotsocr_file: DotsOCR JSON 文件路径
  18. paddle_file: PaddleOCR JSON 文件路径
  19. output_dir: 输出目录
  20. output_type: 输出格式
  21. merger: 合并器实例
  22. Returns:
  23. 是否成功
  24. """
  25. print(f"📄 处理: {dotsocr_file.name}")
  26. # 输出文件路径
  27. merged_md_path = output_dir / f"{dotsocr_file.stem}.md"
  28. merged_json_path = output_dir / f"{dotsocr_file.stem}.json"
  29. try:
  30. # ✅ 合并数据 (统一输出为MinerU格式)
  31. merged_data = merger.merge_table_with_bbox(
  32. str(dotsocr_file),
  33. str(paddle_file),
  34. data_format='mineru' # 强制使用MinerU格式
  35. )
  36. # ✅ 生成 Markdown (基于MinerU格式)
  37. if output_type in ['markdown', 'both']:
  38. markdown = merger.generate_enhanced_markdown(
  39. merged_data,
  40. str(merged_md_path),
  41. str(dotsocr_file),
  42. data_format='mineru' # 强制使用MinerU格式
  43. )
  44. # ✅ 保存 JSON (MinerU格式)
  45. if output_type in ['json', 'both']:
  46. with open(merged_json_path, 'w', encoding='utf-8') as f:
  47. json.dump(merged_data, f, ensure_ascii=False, indent=2)
  48. print(f" ✅ 合并完成 (MinerU格式)")
  49. print(f" 📊 共处理了 {len(merged_data)} 个对象")
  50. print(f" 💾 输出文件:")
  51. if output_type in ['markdown', 'both']:
  52. print(f" - {merged_md_path.name}")
  53. if output_type in ['json', 'both']:
  54. print(f" - {merged_json_path.name}")
  55. return True
  56. except Exception as e:
  57. print(f" ❌ 处理失败: {e}")
  58. import traceback
  59. traceback.print_exc()
  60. return False
  61. def merge_dotsocr_batch(dotsocr_dir: str, paddle_dir: str, output_dir: str,
  62. output_type: str = 'both',
  63. look_ahead_window: int = 10,
  64. similarity_threshold: int = 80):
  65. """
  66. 批量合并 DotsOCR 和 PaddleOCR 的结果
  67. Args:
  68. dotsocr_dir: DotsOCR 结果目录
  69. paddle_dir: PaddleOCR 结果目录
  70. output_dir: 输出目录
  71. output_type: 输出格式
  72. look_ahead_window: 向前查找窗口大小
  73. similarity_threshold: 相似度阈值
  74. """
  75. dotsocr_path = Path(dotsocr_dir)
  76. paddle_path = Path(paddle_dir)
  77. output_path = Path(output_dir)
  78. output_path.mkdir(parents=True, exist_ok=True)
  79. merger = DotsOCRMerger(look_ahead_window, similarity_threshold)
  80. # 查找所有 DotsOCR 的 JSON 文件
  81. dotsocr_files = list(dotsocr_path.glob('*_page_*[0-9].json'))
  82. dotsocr_files.sort()
  83. print(f"\n🔍 找到 {len(dotsocr_files)} 个 DotsOCR 文件")
  84. print(f"📂 DotsOCR 目录: {dotsocr_dir}")
  85. print(f"📂 PaddleOCR 目录: {paddle_dir}")
  86. print(f"📂 输出目录: {output_dir}")
  87. print(f"⚙️ 查找窗口: {look_ahead_window}")
  88. print(f"⚙️ 相似度阈值: {similarity_threshold}%\n")
  89. success_count = 0
  90. failed_count = 0
  91. for dotsocr_file in dotsocr_files:
  92. # 查找对应的 PaddleOCR 文件
  93. paddle_file = paddle_path / dotsocr_file.name
  94. if not paddle_file.exists():
  95. print(f"⚠️ 跳过: 未找到对应的 PaddleOCR 文件: {paddle_file.name}\n")
  96. failed_count += 1
  97. continue
  98. if merge_single_file(dotsocr_file, paddle_file, output_path, output_type, merger):
  99. success_count += 1
  100. else:
  101. failed_count += 1
  102. print()
  103. print("=" * 60)
  104. print(f"✅ 处理完成!")
  105. print(f"📊 统计信息:")
  106. print(f" - 总文件数: {len(dotsocr_files)}")
  107. print(f" - 成功: {success_count}")
  108. print(f" - 失败: {failed_count}")
  109. print("=" * 60)
  110. def main():
  111. """主函数"""
  112. parser = argparse.ArgumentParser(
  113. description='合并 DotsOCR 和 PaddleOCR 的识别结果,统一输出为MinerU格式',
  114. formatter_class=argparse.RawDescriptionHelpFormatter,
  115. epilog="""
  116. 示例用法:
  117. 1. 批量处理整个目录:
  118. python merge_dotsocr_paddleocr.py \\
  119. --dotsocr-dir /path/to/dotsocr/results \\
  120. --paddle-dir /path/to/paddle/results \\
  121. --output-dir /path/to/output
  122. 2. 处理单个文件:
  123. python merge_dotsocr_paddleocr.py \\
  124. --dotsocr-file /path/to/file_page_001.json \\
  125. --paddle-file /path/to/file_page_001.json \\
  126. --output-dir /path/to/output
  127. 3. 自定义参数:
  128. python merge_dotsocr_paddleocr.py \\
  129. --dotsocr-dir /path/to/dotsocr \\
  130. --paddle-dir /path/to/paddle \\
  131. --output-dir /path/to/output \\
  132. --window 15 \\
  133. --threshold 85
  134. 输出格式说明:
  135. - JSON: 统一的MinerU格式JSON文件
  136. - Markdown: 基于MinerU格式生成的Markdown文件
  137. """
  138. )
  139. # 文件/目录参数
  140. file_group = parser.add_argument_group('文件参数')
  141. file_group.add_argument(
  142. '--dotsocr-file',
  143. type=str,
  144. help='DotsOCR 输出的 JSON 文件路径(单文件模式)'
  145. )
  146. file_group.add_argument(
  147. '--paddle-file',
  148. type=str,
  149. help='PaddleOCR 输出的 JSON 文件路径(单文件模式)'
  150. )
  151. dir_group = parser.add_argument_group('目录参数')
  152. dir_group.add_argument(
  153. '--dotsocr-dir',
  154. type=str,
  155. help='DotsOCR 结果目录(批量模式)'
  156. )
  157. dir_group.add_argument(
  158. '--paddle-dir',
  159. type=str,
  160. help='PaddleOCR 结果目录(批量模式)'
  161. )
  162. # 输出参数
  163. output_group = parser.add_argument_group('输出参数')
  164. output_group.add_argument(
  165. '-o', '--output-dir',
  166. type=str,
  167. required=True,
  168. help='输出目录(必需)'
  169. )
  170. output_group.add_argument(
  171. '-f', '--output-type',
  172. choices=['json', 'markdown', 'both'],
  173. default='both',
  174. help='输出格式'
  175. )
  176. # 算法参数
  177. algo_group = parser.add_argument_group('算法参数')
  178. algo_group.add_argument(
  179. '-w', '--window',
  180. type=int,
  181. default=15,
  182. help='向前查找的窗口大小(默认: 15)'
  183. )
  184. algo_group.add_argument(
  185. '-t', '--threshold',
  186. type=int,
  187. default=80,
  188. help='文本相似度阈值(0-100,默认: 80)'
  189. )
  190. args = parser.parse_args()
  191. output_type = args.output_type.lower()
  192. # 验证参数
  193. if args.dotsocr_file and args.paddle_file:
  194. # 单文件模式
  195. dotsocr_file = Path(args.dotsocr_file)
  196. paddle_file = Path(args.paddle_file)
  197. output_dir = Path(args.output_dir)
  198. if not dotsocr_file.exists():
  199. print(f"❌ 错误: DotsOCR 文件不存在: {dotsocr_file}")
  200. return
  201. if not paddle_file.exists():
  202. print(f"❌ 错误: PaddleOCR 文件不存在: {paddle_file}")
  203. return
  204. output_dir.mkdir(parents=True, exist_ok=True)
  205. print("\n🔧 单文件处理模式")
  206. print(f"📄 DotsOCR 文件: {dotsocr_file}")
  207. print(f"📄 PaddleOCR 文件: {paddle_file}")
  208. print(f"📂 输出目录: {output_dir}\n")
  209. merger = DotsOCRMerger(
  210. look_ahead_window=args.window,
  211. similarity_threshold=args.threshold
  212. )
  213. success = merge_single_file(dotsocr_file, paddle_file, output_dir, output_type, merger)
  214. if success:
  215. print("\n✅ 处理完成!")
  216. else:
  217. print("\n❌ 处理失败!")
  218. elif args.dotsocr_dir and args.paddle_dir:
  219. # 批量模式
  220. if not Path(args.dotsocr_dir).exists():
  221. print(f"❌ 错误: DotsOCR 目录不存在: {args.dotsocr_dir}")
  222. return
  223. if not Path(args.paddle_dir).exists():
  224. print(f"❌ 错误: PaddleOCR 目录不存在: {args.paddle_dir}")
  225. return
  226. print("\n🔧 批量处理模式")
  227. merge_dotsocr_batch(
  228. args.dotsocr_dir,
  229. args.paddle_dir,
  230. args.output_dir,
  231. output_type=output_type,
  232. look_ahead_window=args.window,
  233. similarity_threshold=args.threshold
  234. )
  235. else:
  236. parser.print_help()
  237. print("\n❌ 错误: 请指定单文件模式或批量模式的参数")
  238. if __name__ == "__main__":
  239. print("🚀 启动 DotsOCR + PaddleOCR 合并程序 (统一输出MinerU格式)...")
  240. import sys
  241. if len(sys.argv) == 1:
  242. # 默认配置
  243. default_config = {
  244. "dotsocr-file": "/Users/zhch158/workspace/data/流水分析/A用户_单元格扫描流水/dotsocr_vllm_results/A用户_单元格扫描流水_page_002.json",
  245. "paddle-file": "/Users/zhch158/workspace/data/流水分析/A用户_单元格扫描流水/ppstructurev3_client_results/A用户_单元格扫描流水_page_002.json",
  246. "output-dir": "/Users/zhch158/workspace/data/流水分析/A用户_单元格扫描流水/dotsocr_vllm_results_cell_bbox",
  247. "output-type": "both",
  248. "window": "15",
  249. "threshold": "85"
  250. }
  251. print("ℹ️ 未提供命令行参数,使用默认配置运行...")
  252. print("⚙️ 默认参数:")
  253. for key, value in default_config.items():
  254. print(f" --{key}: {value}")
  255. sys.argv = [sys.argv[0]]
  256. for key, value in default_config.items():
  257. sys.argv.extend([f"--{key}", str(value)])
  258. sys.exit(main())