common.py 12 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340
  1. import os
  2. import click
  3. import fitz
  4. from loguru import logger
  5. import magic_pdf.model as model_config
  6. from magic_pdf.config.enums import SupportedPdfParseMethod
  7. from magic_pdf.config.make_content_config import DropMode, MakeMode
  8. from magic_pdf.data.data_reader_writer import FileBasedDataWriter
  9. from magic_pdf.data.dataset import Dataset, PymuDocDataset
  10. from magic_pdf.libs.draw_bbox import draw_char_bbox
  11. from magic_pdf.model.doc_analyze_by_custom_model import (batch_doc_analyze,
  12. doc_analyze)
  13. # from io import BytesIO
  14. # from pypdf import PdfReader, PdfWriter
  15. def prepare_env(output_dir, pdf_file_name, method):
  16. local_parent_dir = os.path.join(output_dir, pdf_file_name, method)
  17. local_image_dir = os.path.join(str(local_parent_dir), 'images')
  18. local_md_dir = local_parent_dir
  19. os.makedirs(local_image_dir, exist_ok=True)
  20. os.makedirs(local_md_dir, exist_ok=True)
  21. return local_image_dir, local_md_dir
  22. # def convert_pdf_bytes_to_bytes_by_pypdf(pdf_bytes, start_page_id=0, end_page_id=None):
  23. # # 将字节数据包装在 BytesIO 对象中
  24. # pdf_file = BytesIO(pdf_bytes)
  25. # # 读取 PDF 的字节数据
  26. # reader = PdfReader(pdf_file)
  27. # # 创建一个新的 PDF 写入器
  28. # writer = PdfWriter()
  29. # # 将所有页面添加到新的 PDF 写入器中
  30. # end_page_id = end_page_id if end_page_id is not None and end_page_id >= 0 else len(reader.pages) - 1
  31. # if end_page_id > len(reader.pages) - 1:
  32. # logger.warning("end_page_id is out of range, use pdf_docs length")
  33. # end_page_id = len(reader.pages) - 1
  34. # for i, page in enumerate(reader.pages):
  35. # if start_page_id <= i <= end_page_id:
  36. # writer.add_page(page)
  37. # # 创建一个字节缓冲区来存储输出的 PDF 数据
  38. # output_buffer = BytesIO()
  39. # # 将 PDF 写入字节缓冲区
  40. # writer.write(output_buffer)
  41. # # 获取字节缓冲区的内容
  42. # converted_pdf_bytes = output_buffer.getvalue()
  43. # return converted_pdf_bytes
  44. def convert_pdf_bytes_to_bytes_by_pymupdf(pdf_bytes, start_page_id=0, end_page_id=None):
  45. document = fitz.open('pdf', pdf_bytes)
  46. output_document = fitz.open()
  47. end_page_id = (
  48. end_page_id
  49. if end_page_id is not None and end_page_id >= 0
  50. else len(document) - 1
  51. )
  52. if end_page_id > len(document) - 1:
  53. logger.warning('end_page_id is out of range, use pdf_docs length')
  54. end_page_id = len(document) - 1
  55. output_document.insert_pdf(document, from_page=start_page_id, to_page=end_page_id)
  56. output_bytes = output_document.tobytes()
  57. return output_bytes
  58. def _do_parse(
  59. output_dir,
  60. pdf_file_name,
  61. pdf_bytes_or_dataset,
  62. model_list,
  63. parse_method,
  64. debug_able=False,
  65. f_draw_span_bbox=True,
  66. f_draw_layout_bbox=True,
  67. f_dump_md=True,
  68. f_dump_middle_json=True,
  69. f_dump_model_json=True,
  70. f_dump_orig_pdf=True,
  71. f_dump_content_list=True,
  72. f_make_md_mode=MakeMode.MM_MD,
  73. f_draw_model_bbox=False,
  74. f_draw_line_sort_bbox=False,
  75. f_draw_char_bbox=False,
  76. start_page_id=0,
  77. end_page_id=None,
  78. lang=None,
  79. layout_model=None,
  80. formula_enable=None,
  81. table_enable=None,
  82. ):
  83. from magic_pdf.operators.models import InferenceResult
  84. if debug_able:
  85. logger.warning('debug mode is on')
  86. f_draw_model_bbox = True
  87. f_draw_line_sort_bbox = True
  88. # f_draw_char_bbox = True
  89. if isinstance(pdf_bytes_or_dataset, bytes):
  90. pdf_bytes = convert_pdf_bytes_to_bytes_by_pymupdf(
  91. pdf_bytes_or_dataset, start_page_id, end_page_id
  92. )
  93. ds = PymuDocDataset(pdf_bytes, lang=lang)
  94. else:
  95. ds = pdf_bytes_or_dataset
  96. pdf_bytes = ds._raw_data
  97. local_image_dir, local_md_dir = prepare_env(output_dir, pdf_file_name, parse_method)
  98. image_writer, md_writer = FileBasedDataWriter(local_image_dir), FileBasedDataWriter(local_md_dir)
  99. image_dir = str(os.path.basename(local_image_dir))
  100. if len(model_list) == 0:
  101. if model_config.__use_inside_model__:
  102. if parse_method == 'auto':
  103. if ds.classify() == SupportedPdfParseMethod.TXT:
  104. infer_result = ds.apply(
  105. doc_analyze,
  106. ocr=False,
  107. lang=ds._lang,
  108. layout_model=layout_model,
  109. formula_enable=formula_enable,
  110. table_enable=table_enable,
  111. )
  112. pipe_result = infer_result.pipe_txt_mode(
  113. image_writer, debug_mode=True, lang=ds._lang
  114. )
  115. else:
  116. infer_result = ds.apply(
  117. doc_analyze,
  118. ocr=True,
  119. lang=ds._lang,
  120. layout_model=layout_model,
  121. formula_enable=formula_enable,
  122. table_enable=table_enable,
  123. )
  124. pipe_result = infer_result.pipe_ocr_mode(
  125. image_writer, debug_mode=True, lang=ds._lang
  126. )
  127. elif parse_method == 'txt':
  128. infer_result = ds.apply(
  129. doc_analyze,
  130. ocr=False,
  131. lang=ds._lang,
  132. layout_model=layout_model,
  133. formula_enable=formula_enable,
  134. table_enable=table_enable,
  135. )
  136. pipe_result = infer_result.pipe_txt_mode(
  137. image_writer, debug_mode=True, lang=ds._lang
  138. )
  139. elif parse_method == 'ocr':
  140. infer_result = ds.apply(
  141. doc_analyze,
  142. ocr=True,
  143. lang=ds._lang,
  144. layout_model=layout_model,
  145. formula_enable=formula_enable,
  146. table_enable=table_enable,
  147. )
  148. pipe_result = infer_result.pipe_ocr_mode(
  149. image_writer, debug_mode=True, lang=ds._lang
  150. )
  151. else:
  152. logger.error('unknown parse method')
  153. exit(1)
  154. else:
  155. logger.error('need model list input')
  156. exit(2)
  157. else:
  158. infer_result = InferenceResult(model_list, ds)
  159. if parse_method == 'ocr':
  160. pipe_result = infer_result.pipe_ocr_mode(
  161. image_writer, debug_mode=True, lang=ds._lang
  162. )
  163. elif parse_method == 'txt':
  164. pipe_result = infer_result.pipe_txt_mode(
  165. image_writer, debug_mode=True, lang=ds._lang
  166. )
  167. else:
  168. if ds.classify() == SupportedPdfParseMethod.TXT:
  169. pipe_result = infer_result.pipe_txt_mode(
  170. image_writer, debug_mode=True, lang=ds._lang
  171. )
  172. else:
  173. pipe_result = infer_result.pipe_ocr_mode(
  174. image_writer, debug_mode=True, lang=ds._lang
  175. )
  176. if f_draw_model_bbox:
  177. infer_result.draw_model(
  178. os.path.join(local_md_dir, f'{pdf_file_name}_model.pdf')
  179. )
  180. if f_draw_layout_bbox:
  181. pipe_result.draw_layout(
  182. os.path.join(local_md_dir, f'{pdf_file_name}_layout.pdf')
  183. )
  184. if f_draw_span_bbox:
  185. pipe_result.draw_span(os.path.join(local_md_dir, f'{pdf_file_name}_spans.pdf'))
  186. if f_draw_line_sort_bbox:
  187. pipe_result.draw_line_sort(
  188. os.path.join(local_md_dir, f'{pdf_file_name}_line_sort.pdf')
  189. )
  190. if f_draw_char_bbox:
  191. draw_char_bbox(pdf_bytes, local_md_dir, f'{pdf_file_name}_char_bbox.pdf')
  192. if f_dump_md:
  193. pipe_result.dump_md(
  194. md_writer,
  195. f'{pdf_file_name}.md',
  196. image_dir,
  197. drop_mode=DropMode.NONE,
  198. md_make_mode=f_make_md_mode,
  199. )
  200. if f_dump_middle_json:
  201. pipe_result.dump_middle_json(md_writer, f'{pdf_file_name}_middle.json')
  202. if f_dump_model_json:
  203. infer_result.dump_model(md_writer, f'{pdf_file_name}_model.json')
  204. if f_dump_orig_pdf:
  205. md_writer.write(
  206. f'{pdf_file_name}_origin.pdf',
  207. pdf_bytes,
  208. )
  209. if f_dump_content_list:
  210. pipe_result.dump_content_list(
  211. md_writer,
  212. f'{pdf_file_name}_content_list.json',
  213. image_dir
  214. )
  215. logger.info(f'local output dir is {local_md_dir}')
  216. def do_parse(
  217. output_dir,
  218. pdf_file_name,
  219. pdf_bytes_or_dataset,
  220. model_list,
  221. parse_method,
  222. debug_able=False,
  223. f_draw_span_bbox=True,
  224. f_draw_layout_bbox=True,
  225. f_dump_md=True,
  226. f_dump_middle_json=True,
  227. f_dump_model_json=True,
  228. f_dump_orig_pdf=True,
  229. f_dump_content_list=True,
  230. f_make_md_mode=MakeMode.MM_MD,
  231. f_draw_model_bbox=False,
  232. f_draw_line_sort_bbox=False,
  233. f_draw_char_bbox=False,
  234. start_page_id=0,
  235. end_page_id=None,
  236. lang=None,
  237. layout_model=None,
  238. formula_enable=None,
  239. table_enable=None,
  240. ):
  241. parallel_count = 1
  242. if os.environ.get('MINERU_PARALLEL_INFERENCE_COUNT'):
  243. parallel_count = int(os.environ['MINERU_PARALLEL_INFERENCE_COUNT'])
  244. if parallel_count > 1:
  245. if isinstance(pdf_bytes_or_dataset, bytes):
  246. pdf_bytes = convert_pdf_bytes_to_bytes_by_pymupdf(
  247. pdf_bytes_or_dataset, start_page_id, end_page_id
  248. )
  249. ds = PymuDocDataset(pdf_bytes, lang=lang)
  250. else:
  251. ds = pdf_bytes_or_dataset
  252. batch_do_parse(output_dir, [pdf_file_name], [ds], parse_method, debug_able, f_draw_span_bbox=f_draw_span_bbox, f_draw_layout_bbox=f_draw_layout_bbox, f_dump_md=f_dump_md, f_dump_middle_json=f_dump_middle_json, f_dump_model_json=f_dump_model_json, f_dump_orig_pdf=f_dump_orig_pdf, f_dump_content_list=f_dump_content_list, f_make_md_mode=f_make_md_mode, f_draw_model_bbox=f_draw_model_bbox, f_draw_line_sort_bbox=f_draw_line_sort_bbox, f_draw_char_bbox=f_draw_char_bbox, lang=lang)
  253. else:
  254. _do_parse(output_dir, pdf_file_name, pdf_bytes_or_dataset, model_list, parse_method, debug_able, start_page_id=start_page_id, end_page_id=end_page_id, lang=lang, layout_model=layout_model, formula_enable=formula_enable, table_enable=table_enable, f_draw_span_bbox=f_draw_span_bbox, f_draw_layout_bbox=f_draw_layout_bbox, f_dump_md=f_dump_md, f_dump_middle_json=f_dump_middle_json, f_dump_model_json=f_dump_model_json, f_dump_orig_pdf=f_dump_orig_pdf, f_dump_content_list=f_dump_content_list, f_make_md_mode=f_make_md_mode, f_draw_model_bbox=f_draw_model_bbox, f_draw_line_sort_bbox=f_draw_line_sort_bbox, f_draw_char_bbox=f_draw_char_bbox)
  255. def batch_do_parse(
  256. output_dir,
  257. pdf_file_names: list[str],
  258. pdf_bytes_or_datasets: list[bytes | Dataset],
  259. parse_method,
  260. debug_able=False,
  261. f_draw_span_bbox=True,
  262. f_draw_layout_bbox=True,
  263. f_dump_md=True,
  264. f_dump_middle_json=True,
  265. f_dump_model_json=True,
  266. f_dump_orig_pdf=True,
  267. f_dump_content_list=True,
  268. f_make_md_mode=MakeMode.MM_MD,
  269. f_draw_model_bbox=False,
  270. f_draw_line_sort_bbox=False,
  271. f_draw_char_bbox=False,
  272. lang=None,
  273. layout_model=None,
  274. formula_enable=None,
  275. table_enable=None,
  276. ):
  277. dss = []
  278. for v in pdf_bytes_or_datasets:
  279. if isinstance(v, bytes):
  280. dss.append(PymuDocDataset(v, lang=lang))
  281. else:
  282. dss.append(v)
  283. infer_results = batch_doc_analyze(dss, parse_method, lang=lang, layout_model=layout_model, formula_enable=formula_enable, table_enable=table_enable)
  284. for idx, infer_result in enumerate(infer_results):
  285. _do_parse(
  286. output_dir = output_dir,
  287. pdf_file_name = pdf_file_names[idx],
  288. pdf_bytes_or_dataset = dss[idx],
  289. model_list = infer_result.get_infer_res(),
  290. parse_method = parse_method,
  291. debug_able = debug_able,
  292. f_draw_span_bbox = f_draw_span_bbox,
  293. f_draw_layout_bbox = f_draw_layout_bbox,
  294. f_dump_md=f_dump_md,
  295. f_dump_middle_json=f_dump_middle_json,
  296. f_dump_model_json=f_dump_model_json,
  297. f_dump_orig_pdf=f_dump_orig_pdf,
  298. f_dump_content_list=f_dump_content_list,
  299. f_make_md_mode=MakeMode.MM_MD,
  300. f_draw_model_bbox=f_draw_model_bbox,
  301. f_draw_line_sort_bbox=f_draw_line_sort_bbox,
  302. f_draw_char_bbox=f_draw_char_bbox,
  303. lang=lang,
  304. )
  305. parse_pdf_methods = click.Choice(['ocr', 'txt', 'auto'])