pdf_image_tools.py 3.2 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103
  1. # Copyright (c) Opendatalab. All rights reserved.
  2. from io import BytesIO
  3. import pypdfium2 as pdfium
  4. from loguru import logger
  5. from PIL import Image
  6. from mineru.data.data_reader_writer import FileBasedDataWriter
  7. from mineru.utils.pdf_reader import image_to_b64str, image_to_bytes, page_to_image
  8. from .hash_utils import str_sha256
  9. def pdf_page_to_image(page: pdfium.PdfPage, dpi=200) -> dict:
  10. """Convert pdfium.PdfDocument to image, Then convert the image to base64.
  11. Args:
  12. page (_type_): pdfium.PdfPage
  13. dpi (int, optional): reset the dpi of dpi. Defaults to 200.
  14. Returns:
  15. dict: {'img_base64': str, 'img_pil': pil_img, 'scale': float }
  16. """
  17. pil_img, scale = page_to_image(page, dpi=dpi)
  18. img_base64 = image_to_b64str(pil_img)
  19. image_dict = {
  20. "img_base64": img_base64,
  21. "img_pil": pil_img,
  22. "scale": scale,
  23. }
  24. return image_dict
  25. def load_images_from_pdf(
  26. pdf_bytes: bytes,
  27. dpi=200,
  28. start_page_id=0,
  29. end_page_id=None,
  30. ):
  31. images_list = []
  32. pdf_doc = pdfium.PdfDocument(pdf_bytes)
  33. pdf_page_num = len(pdf_doc)
  34. end_page_id = end_page_id if end_page_id is not None and end_page_id >= 0 else pdf_page_num - 1
  35. if end_page_id > pdf_page_num - 1:
  36. logger.warning("end_page_id is out of range, use images length")
  37. end_page_id = pdf_page_num - 1
  38. for index in range(0, pdf_page_num):
  39. if start_page_id <= index <= end_page_id:
  40. page = pdf_doc[index]
  41. image_dict = pdf_page_to_image(page, dpi=dpi)
  42. images_list.append(image_dict)
  43. return images_list, pdf_doc
  44. def cut_image(bbox: tuple, page_num: int, page_pil_img, return_path, image_writer: FileBasedDataWriter, scale=2):
  45. """从第page_num页的page中,根据bbox进行裁剪出一张jpg图片,返回图片路径 save_path:需要同时支持s3和本地,
  46. 图片存放在save_path下,文件名是:
  47. {page_num}_{bbox[0]}_{bbox[1]}_{bbox[2]}_{bbox[3]}.jpg , bbox内数字取整。"""
  48. # 拼接文件名
  49. filename = f"{page_num}_{int(bbox[0])}_{int(bbox[1])}_{int(bbox[2])}_{int(bbox[3])}"
  50. # 老版本返回不带bucket的路径
  51. img_path = f"{return_path}_{filename}" if return_path is not None else None
  52. # 新版本生成平铺路径
  53. img_hash256_path = f"{str_sha256(img_path)}.jpg"
  54. # img_hash256_path = f'{img_path}.jpg'
  55. crop_img = get_crop_img(bbox, page_pil_img, scale=scale)
  56. img_bytes = image_to_bytes(crop_img, image_format="JPEG")
  57. image_writer.write(img_hash256_path, img_bytes)
  58. return img_hash256_path
  59. def get_crop_img(bbox: tuple, pil_img, scale=2):
  60. scale_bbox = (
  61. int(bbox[0] * scale),
  62. int(bbox[1] * scale),
  63. int(bbox[2] * scale),
  64. int(bbox[3] * scale),
  65. )
  66. return pil_img.crop(scale_bbox)
  67. def images_bytes_to_pdf_bytes(image_bytes):
  68. # 内存缓冲区
  69. pdf_buffer = BytesIO()
  70. # 载入并转换所有图像为 RGB 模式
  71. image = Image.open(BytesIO(image_bytes)).convert("RGB")
  72. # 第一张图保存为 PDF,其余追加
  73. image.save(pdf_buffer, format="PDF", save_all=True)
  74. # 获取 PDF bytes 并重置指针(可选)
  75. pdf_bytes = pdf_buffer.getvalue()
  76. pdf_buffer.close()
  77. return pdf_bytes