ocr_detect_layout.py 5.7 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133
  1. import fitz
  2. from magic_pdf.layout.layout_sort import get_bboxes_layout
  3. from magic_pdf.libs.boxbase import _is_part_overlap, _is_in
  4. from magic_pdf.libs.coordinate_transform import get_scale_ratio
  5. def get_center_point(bbox):
  6. """
  7. 根据边界框坐标信息,计算出该边界框的中心点坐标。
  8. Args:
  9. bbox (list): 边界框坐标信息,包含四个元素,分别为左上角x坐标、左上角y坐标、右下角x坐标、右下角y坐标。
  10. Returns:
  11. list: 中心点坐标信息,包含两个元素,分别为x坐标和y坐标。
  12. """
  13. return [(bbox[0] + bbox[2]) / 2, (bbox[1] + bbox[3]) / 2]
  14. def get_area(bbox):
  15. """
  16. 根据边界框坐标信息,计算出该边界框的面积。
  17. Args:
  18. bbox (list): 边界框坐标信息,包含四个元素,分别为左上角x坐标、左上角y坐标、右下角x坐标、右下角y坐标。
  19. Returns:
  20. float: 该边界框的面积。
  21. """
  22. return (bbox[2] - bbox[0]) * (bbox[3] - bbox[1])
  23. def adjust_layouts(layout_bboxes, page_boundry, page_id):
  24. # 遍历所有布局框
  25. for i in range(len(layout_bboxes)):
  26. # 遍历当前布局框之后的布局框
  27. for j in range(i + 1, len(layout_bboxes)):
  28. # 判断两个布局框是否重叠
  29. if _is_part_overlap(layout_bboxes[i], layout_bboxes[j]):
  30. # 计算每个布局框的中心点坐标和面积
  31. area_i = get_area(layout_bboxes[i])
  32. area_j = get_area(layout_bboxes[j])
  33. # 较大布局框和较小布局框的赋值
  34. if area_i > area_j:
  35. larger_layout, smaller_layout = layout_bboxes[i], layout_bboxes[j]
  36. else:
  37. larger_layout, smaller_layout = layout_bboxes[j], layout_bboxes[i]
  38. center_large = get_center_point(larger_layout)
  39. center_small = get_center_point(smaller_layout)
  40. # 计算横向和纵向的距离差
  41. distance_x = center_large[0] - center_small[0]
  42. distance_y = center_large[1] - center_small[1]
  43. # 根据距离差判断重叠方向并修正边界
  44. if abs(distance_x) > abs(distance_y): # 左右重叠
  45. if distance_x > 0 and larger_layout[0] < smaller_layout[2]:
  46. larger_layout[0] = smaller_layout[2]+1
  47. if distance_x < 0 and larger_layout[2] > smaller_layout[0]:
  48. larger_layout[2] = smaller_layout[0]-1
  49. else: # 上下重叠
  50. if distance_y > 0 and larger_layout[1] < smaller_layout[3]:
  51. larger_layout[1] = smaller_layout[3]+1
  52. if distance_y < 0 and larger_layout[3] > smaller_layout[1]:
  53. larger_layout[3] = smaller_layout[1]-1
  54. # 排序调整布局边界框列表
  55. new_bboxes = []
  56. for layout_bbox in layout_bboxes:
  57. new_bboxes.append([layout_bbox[0], layout_bbox[1], layout_bbox[2], layout_bbox[3], None, None, None, None, None, None, None, None, None])
  58. layout_bboxes, layout_tree = get_bboxes_layout(new_bboxes, page_boundry, page_id)
  59. # 返回排序调整后的布局边界框列表
  60. return layout_bboxes, layout_tree
  61. def layout_detect(layout_info, page: fitz.Page, ocr_page_info):
  62. """
  63. 对输入的布局信息进行解析,提取出每个子布局的边界框,并对所有子布局进行排序调整。
  64. Args:
  65. layout_info (list): 包含子布局信息的列表,每个子布局信息为字典类型,包含'poly'字段,表示子布局的边界框坐标信息。
  66. Returns:
  67. list: 经过排序调整后的所有子布局边界框信息的列表,每个边界框信息为字典类型,包含'layout_bbox'字段,表示边界框的坐标信息。
  68. """
  69. page_id = ocr_page_info['page_info']['page_no']-1
  70. horizontal_scale_ratio, vertical_scale_ratio = get_scale_ratio(ocr_page_info, page)
  71. # 初始化布局边界框列表
  72. layout_bboxes = []
  73. # 遍历每个子布局
  74. for sub_layout in layout_info:
  75. # 提取子布局的边界框坐标信息
  76. x0, y0, _, _, x1, y1, _, _ = sub_layout['poly']
  77. bbox = [int(x0 / horizontal_scale_ratio), int(y0 / vertical_scale_ratio),
  78. int(x1 / horizontal_scale_ratio), int(y1 / vertical_scale_ratio)]
  79. # 将子布局的边界框添加到列表中
  80. layout_bboxes.append(bbox)
  81. # 初始化新的布局边界框列表
  82. new_layout_bboxes = []
  83. # 遍历每个布局边界框
  84. for i in range(len(layout_bboxes)):
  85. # 初始化标记变量,用于判断当前边界框是否需要保留
  86. keep = True
  87. # 获取当前边界框的坐标信息
  88. box_i = layout_bboxes[i]
  89. # 遍历其他边界框
  90. for j in range(len(layout_bboxes)):
  91. # 排除当前边界框自身
  92. if i != j:
  93. # 获取其他边界框的坐标信息
  94. box_j = layout_bboxes[j]
  95. # 检测box_i是否被box_j包含
  96. if _is_in(box_i, box_j):
  97. # 如果当前边界框被其他边界框包含,则标记为不需要保留
  98. keep = False
  99. # 跳出内层循环
  100. break
  101. # 如果当前边界框需要保留,则添加到新的布局边界框列表中
  102. if keep:
  103. new_layout_bboxes.append(layout_bboxes[i])
  104. # 对新的布局边界框列表进行排序调整
  105. page_width = page.rect.width
  106. page_height = page.rect.height
  107. page_boundry = [0, 0, page_width, page_height]
  108. layout_bboxes, layout_tree = adjust_layouts(new_layout_bboxes, page_boundry, page_id)
  109. # 返回排序调整后的布局边界框列表
  110. return layout_bboxes, layout_tree