| 123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244 |
- import re
- from magic_pdf.libs.boxbase import _is_in_or_part_overlap, _is_part_overlap, find_bottom_nearest_text_bbox, find_left_nearest_text_bbox, find_right_nearest_text_bbox, find_top_nearest_text_bbox
- from magic_pdf.libs.textbase import get_text_block_base_info
- def fix_image_vertical(image_bboxes:list, text_blocks:list):
- """
- 修正图片的位置
- 如果图片与文字block发生一定重叠(也就是图片切到了一部分文字),那么减少图片边缘,让文字和图片不再重叠。
- 只对垂直方向进行。
- """
- for image_bbox in image_bboxes:
- for text_block in text_blocks:
- text_bbox = text_block["bbox"]
- if _is_part_overlap(text_bbox, image_bbox) and any([text_bbox[0]>=image_bbox[0] and text_bbox[2]<=image_bbox[2], text_bbox[0]<=image_bbox[0] and text_bbox[2]>=image_bbox[2]]):
- if text_bbox[1] < image_bbox[1]:#在图片上方
- image_bbox[1] = text_bbox[3]+1
- elif text_bbox[3]>image_bbox[3]:#在图片下方
- image_bbox[3] = text_bbox[1]-1
-
- return image_bboxes
- def __merge_if_common_edge(bbox1, bbox2):
- x_min_1, y_min_1, x_max_1, y_max_1 = bbox1
- x_min_2, y_min_2, x_max_2, y_max_2 = bbox2
- # 检查是否有公共的水平边
- if y_min_1 == y_min_2 or y_max_1 == y_max_2:
- # 确保一个框的x范围在另一个框的x范围内
- if max(x_min_1, x_min_2) <= min(x_max_1, x_max_2):
- return [min(x_min_1, x_min_2), min(y_min_1, y_min_2), max(x_max_1, x_max_2), max(y_max_1, y_max_2)]
- # 检查是否有公共的垂直边
- if x_min_1 == x_min_2 or x_max_1 == x_max_2:
- # 确保一个框的y范围在另一个框的y范围内
- if max(y_min_1, y_min_2) <= min(y_max_1, y_max_2):
- return [min(x_min_1, x_min_2), min(y_min_1, y_min_2), max(x_max_1, x_max_2), max(y_max_1, y_max_2)]
- # 如果没有公共边
- return None
- def fix_seperated_image(image_bboxes:list):
- """
- 如果2个图片有一个边重叠,那么合并2个图片
- """
- new_images = []
- droped_img_idx = []
-
- for i in range(0, len(image_bboxes)):
- for j in range(i+1, len(image_bboxes)):
- new_img = __merge_if_common_edge(image_bboxes[i], image_bboxes[j])
- if new_img is not None:
- new_images.append(new_img)
- droped_img_idx.append(i)
- droped_img_idx.append(j)
- break
-
- for i in range(0, len(image_bboxes)):
- if i not in droped_img_idx:
- new_images.append(image_bboxes[i])
-
- return new_images
- def __check_img_title_pattern(text):
- """
- 检查文本段是否是表格的标题
- """
- patterns = [r"^(fig|figure).*", r"^(scheme).*"]
- text = text.strip()
- for pattern in patterns:
- match = re.match(pattern, text, re.IGNORECASE)
- if match:
- return True
- return False
- def __get_fig_caption_text(text_block):
- txt = " ".join(span['text'] for line in text_block['lines'] for span in line['spans'])
- line_cnt = len(text_block['lines'])
- txt = txt.replace("Ž . ", '')
- return txt, line_cnt
- def __find_and_extend_bottom_caption(text_block, pymu_blocks, image_box):
- """
- 继续向下方寻找和图片caption字号,字体,颜色一样的文字框,合并入caption。
- text_block是已经找到的图片catpion(这个caption可能不全,多行被划分到多个pymu block里了)
- """
- combined_image_caption_text_block = list(text_block.copy()['bbox'])
- base_font_color, base_font_size, base_font_type = get_text_block_base_info(text_block)
- while True:
- tb_add = find_bottom_nearest_text_bbox(pymu_blocks, combined_image_caption_text_block)
- if not tb_add:
- break
- tb_font_color, tb_font_size, tb_font_type = get_text_block_base_info(tb_add)
- if tb_font_color==base_font_color and tb_font_size==base_font_size and tb_font_type==base_font_type:
- combined_image_caption_text_block[0] = min(combined_image_caption_text_block[0], tb_add['bbox'][0])
- combined_image_caption_text_block[2] = max(combined_image_caption_text_block[2], tb_add['bbox'][2])
- combined_image_caption_text_block[3] = tb_add['bbox'][3]
- else:
- break
-
- image_box[0] = min(image_box[0], combined_image_caption_text_block[0])
- image_box[1] = min(image_box[1], combined_image_caption_text_block[1])
- image_box[2] = max(image_box[2], combined_image_caption_text_block[2])
- image_box[3] = max(image_box[3], combined_image_caption_text_block[3])
- text_block['_image_caption'] = True
-
- def include_img_title(pymu_blocks, image_bboxes: list):
- """
- 向上方和下方寻找符合图片title的文本block,合并到图片里
- 如果图片上下都有fig的情况怎么办?寻找标题距离最近的那个。
- ---
- 增加对左侧和右侧图片标题的寻找
- """
-
- for tb in image_bboxes:
- # 优先找下方的
- max_find_cnt = 3 # 向上,向下最多找3个就停止
- temp_box = tb.copy()
- while max_find_cnt>0:
- text_block_btn = find_bottom_nearest_text_bbox(pymu_blocks, temp_box)
- if text_block_btn:
- txt, line_cnt = __get_fig_caption_text(text_block_btn)
- if len(txt.strip())>0:
- if not __check_img_title_pattern(txt) and max_find_cnt>0 and line_cnt<3: # 设置line_cnt<=2目的是为了跳过子标题,或者有时候图片下方文字没有被图片识别模型放入图片里
- max_find_cnt = max_find_cnt - 1
- temp_box[3] = text_block_btn['bbox'][3]
- continue
- else:
- break
- else:
- temp_box[3] = text_block_btn['bbox'][3] # 宽度不变,扩大
- max_find_cnt = max_find_cnt - 1
- else:
- break
-
- max_find_cnt = 3 # 向上,向下最多找3个就停止
- temp_box = tb.copy()
- while max_find_cnt>0:
- text_block_top = find_top_nearest_text_bbox(pymu_blocks, temp_box)
- if text_block_top:
- txt, line_cnt = __get_fig_caption_text(text_block_top)
- if len(txt.strip())>0:
- if not __check_img_title_pattern(txt) and max_find_cnt>0 and line_cnt <3:
- max_find_cnt = max_find_cnt - 1
- temp_box[1] = text_block_top['bbox'][1]
- continue
- else:
- break
- else:
- b = text_block_top['bbox']
- temp_box[1] = b[1] # 宽度不变,扩大
- max_find_cnt = max_find_cnt - 1
- else:
- break
-
- if text_block_btn and text_block_top and text_block_btn.get("_image_caption", False) is False and text_block_top.get("_image_caption", False) is False :
- btn_text, _ = __get_fig_caption_text(text_block_btn)
- top_text, _ = __get_fig_caption_text(text_block_top)
- if __check_img_title_pattern(btn_text) and __check_img_title_pattern(top_text):
- # 取距离图片最近的
- btn_text_distance = text_block_btn['bbox'][1] - tb[3]
- top_text_distance = tb[1] - text_block_top['bbox'][3]
- if btn_text_distance<top_text_distance: # caption在下方
- __find_and_extend_bottom_caption(text_block_btn, pymu_blocks, tb)
- else:
- text_block = text_block_top
- tb[0] = min(tb[0], text_block['bbox'][0])
- tb[1] = min(tb[1], text_block['bbox'][1])
- tb[2] = max(tb[2], text_block['bbox'][2])
- tb[3] = max(tb[3], text_block['bbox'][3])
- text_block_btn['_image_caption'] = True
- continue
-
- text_block = text_block_btn # find_bottom_nearest_text_bbox(pymu_blocks, tb)
- if text_block and text_block.get("_image_caption", False) is False:
- first_text_line, _ = __get_fig_caption_text(text_block)
- if __check_img_title_pattern(first_text_line):
- # 发现特征之后,继续向相同方向寻找(想同颜色,想同大小,想同字体)的textblock
- __find_and_extend_bottom_caption(text_block, pymu_blocks, tb)
- continue
-
- text_block = text_block_top # find_top_nearest_text_bbox(pymu_blocks, tb)
- if text_block and text_block.get("_image_caption", False) is False:
- first_text_line, _ = __get_fig_caption_text(text_block)
- if __check_img_title_pattern(first_text_line):
- tb[0] = min(tb[0], text_block['bbox'][0])
- tb[1] = min(tb[1], text_block['bbox'][1])
- tb[2] = max(tb[2], text_block['bbox'][2])
- tb[3] = max(tb[3], text_block['bbox'][3])
- text_block['_image_caption'] = True
- continue
-
- """向左、向右寻找,暂时只寻找一次"""
- left_text_block = find_left_nearest_text_bbox(pymu_blocks, tb)
- if left_text_block and left_text_block.get("_image_caption", False) is False:
- first_text_line, _ = __get_fig_caption_text(left_text_block)
- if __check_img_title_pattern(first_text_line):
- tb[0] = min(tb[0], left_text_block['bbox'][0])
- tb[1] = min(tb[1], left_text_block['bbox'][1])
- tb[2] = max(tb[2], left_text_block['bbox'][2])
- tb[3] = max(tb[3], left_text_block['bbox'][3])
- left_text_block['_image_caption'] = True
- continue
-
- right_text_block = find_right_nearest_text_bbox(pymu_blocks, tb)
- if right_text_block and right_text_block.get("_image_caption", False) is False:
- first_text_line, _ = __get_fig_caption_text(right_text_block)
- if __check_img_title_pattern(first_text_line):
- tb[0] = min(tb[0], right_text_block['bbox'][0])
- tb[1] = min(tb[1], right_text_block['bbox'][1])
- tb[2] = max(tb[2], right_text_block['bbox'][2])
- tb[3] = max(tb[3], right_text_block['bbox'][3])
- right_text_block['_image_caption'] = True
- continue
- return image_bboxes
- def combine_images(image_bboxes:list):
- """
- 合并图片,如果图片有重叠,那么合并
- """
- new_images = []
- droped_img_idx = []
-
- for i in range(0, len(image_bboxes)):
- for j in range(i+1, len(image_bboxes)):
- if j not in droped_img_idx and _is_in_or_part_overlap(image_bboxes[i], image_bboxes[j]):
- # 合并
- image_bboxes[i][0], image_bboxes[i][1],image_bboxes[i][2],image_bboxes[i][3] = min(image_bboxes[i][0], image_bboxes[j][0]), min(image_bboxes[i][1], image_bboxes[j][1]), max(image_bboxes[i][2], image_bboxes[j][2]), max(image_bboxes[i][3], image_bboxes[j][3])
- droped_img_idx.append(j)
-
- for i in range(0, len(image_bboxes)):
- if i not in droped_img_idx:
- new_images.append(image_bboxes[i])
-
- return new_images
|