def change_seal_shape(PIL_page, text_bbox_list, char_bbox_list, split_vertical=False): np_seal = np.array(PIL_page, dtype=np.uint8) height_seal, width_seal = np_seal.shape margin = min(round(random.uniform(0.05, 0.1) * width_seal), round(random.uniform(0.05, 0.1) * height_seal)) height_bg = height_seal + 4 * margin width_bg = width_seal + 4 * margin np_background = np.zeros(shape=(height_bg, width_bg), dtype=np.uint8) # 确定印章坐标 x1_seal = 2 * margin x2_seal = x1_seal + width_seal y1_seal = 2 * margin y2_seal = y1_seal + height_seal # 一半的概率阴阳印章 if random.random() < 0.5: np_seal = reverse_image_color(np_img=np_seal) shadow_seal = True else: shadow_seal = False outline_width = int(margin / 2) np_seal, np_outline, text_bbox_list, char_bbox_list = round_corner(np_seal, outline_width, text_bbox_list, char_bbox_list, shadow_seal, split_vertical) if random.random() < 0.5 or not shadow_seal: # 一半概率加边框,若是阳刻印章,则加上边框 if not shadow_seal: np_outline = resize_img_by_opencv(np_outline, obj_size=(width_seal, height_seal)) h_outline, w_outline = np_outline.shape w_margin = int((width_bg - w_outline)/2) h_margin = int((height_bg - h_outline)/2) np_background[h_margin:h_margin + h_outline, w_margin:w_margin + w_outline] |= np_outline PIL_page = Image.fromarray(np_seal) np_page, text_bbox_list, char_bbox_list = add_subpage_into_page( np_background, PIL_page, text_bbox_list, char_bbox_list, x1_seal, x2_seal, y1_seal, y2_seal ) # PIL_page = Image.fromarray(np_page) # draw = ImageDraw.Draw(PIL_page) # # rect_line_width = int(margin / 2) # draw.rectangle([(x1_seal - margin, y1_seal - margin), (x2_seal + margin, y2_seal + margin)], # fill=None, outline="white", width=rect_line_width) # np_page = np.array(PIL_page, dtype=np.uint8) np_page = reverse_image_color(np_img=np_page) PIL_page = Image.fromarray(np_page) return PIL_page, text_bbox_list, char_bbox_list
def create_two_text_line(shape=(64, 1280), text_type="horizontal", edges=False): """训练双行文本的切分,既需要生成双行数据,也需要生成单行数据(不切分的情况)""" text_type = check_text_type(text_type) check_text_line_shape(shape, text_type) text_h, text_w = shape # 生成黑色背景 np_text = np.zeros(shape=(text_h, text_w), dtype=np.uint8) # 随机确定是否画边框线 if edges: np_text, margin_w, margin_h = draw_edges_around_text(np_text, text_type, line_type="double") else: margin_w, margin_h = 0, 0 # 横向排列 if text_type == "h": # 随机决定字符间距占行距的比例 char_spacing = (random.uniform(0.0, 0.05), random.uniform(0.0, 0.2)) # (高方向, 宽方向) y1, y2 = margin_h, text_h - 1 - margin_h x = margin_w length = text_w - 2 * margin_w if random.random() < 0.7: # 生成两行汉字 _, text1_bbox, text2_bbox, split_pos = generate_two_rows_chars(x, y1, y2, length, np_text, char_spacing) else: # 生成单行汉字 _, text_bbox, _, _ = generate_one_row_chars(x, y1, y2, length, np_text, char_spacing) split_pos = [text_bbox[1], text_bbox[3]] # 纵向排列 else: # 随机决定字符间距占列距的比例 char_spacing = (random.uniform(0.0, 0.2), random.uniform(0.0, 0.05)) # (高方向, 宽方向) x1, x2 = margin_w, text_w - 1 - margin_w y = margin_h length = text_h - 2 * margin_h if random.random() < 0.7: # 生成两列汉字 _, text1_bbox, text2_bbox, split_pos = generate_two_cols_chars(x1, x2, y, length, np_text, char_spacing) else: # 生成单列汉字 _, text_bbox, _, _ = generate_one_col_chars(x1, x2, y, length, np_text, char_spacing) split_pos = [text_bbox[0], text_bbox[2]] np_text = reverse_image_color(np_img=np_text) PIL_text = Image.fromarray(np_text) # print(chinese_char_and_box_list) # print(len(chinese_char_and_box_list)) # PIL_text.show() return PIL_text, split_pos
def add_subpage_into_page(np_page, PIL_subpage, text_bbox_list, char_bbox_list, x1, x2, y1, y2, cover=False): np_subpage = np.array(PIL_subpage, dtype=np.uint8) np_subpage = reverse_image_color(np_img=np_subpage) if cover: # 完全覆盖 np_page[y1:y2, x1:x2] = np_subpage else: np_page[y1:y2, x1:x2] |= np_subpage bbox_list_list = [text_bbox_list, char_bbox_list] for bbox_list in bbox_list_list: for bbox in bbox_list: bbox[0] += x1 bbox[1] += y1 bbox[2] += x1 bbox[3] += y1 return np_page, text_bbox_list, char_bbox_list
def create_mix_text_line(shape=(64, 1280), text_type="horizontal", edges=False): text_type = check_text_type(text_type) check_text_line_shape(shape, text_type) text_h, text_w = shape # 生成黑色背景 np_text = np.zeros(shape=(text_h, text_w), dtype=np.uint8) # 随机确定是否画边框线 if edges: np_text, margin_w, margin_h = draw_edges_around_text(np_text, text_type, line_type="mix") else: margin_w, margin_h = 0, 0 # 横向排列 if text_type == "h": # 随机决定字符间距占行距的比例 char_spacing = (random.uniform(0.0, 0.05), random.uniform(0.0, 0.2)) # (高方向, 宽方向) # 生成单双行 y1, y2 = margin_h, text_h - 1 - margin_h x = margin_w length = text_w - 2 * margin_w _, text_bbox_list, split_pos = generate_mix_rows_chars(x, y1, y2, length, np_text, char_spacing) # 纵向排列 else: # 随机决定字符间距占列距的比例 char_spacing = (random.uniform(0.0, 0.2), random.uniform(0.0, 0.05)) # (高方向, 宽方向) # 生成单双列 x1, x2 = margin_w, text_w - 1 - margin_w y = margin_h length = text_h - 2 * margin_h _, text_bbox_list, split_pos = generate_mix_cols_chars(x1, x2, y, length, np_text, char_spacing) np_text = reverse_image_color(np_img=np_text) PIL_text = Image.fromarray(np_text) # print(chinese_char_and_box_list) # print(len(chinese_char_and_box_list)) # PIL_text.show() return PIL_text, text_bbox_list, split_pos
def round_corner(np_seal, line_width, text_bbox_list, char_bbox_list, shadow_seal, split_vertical=False): circle_d = min(np_seal.shape[0], np_seal.shape[1]) # 圆的直径 # 画实心圆,用于遮挡印章 np_circle = np.zeros(shape=(circle_d, circle_d), dtype=np.uint8) PIL_circle = Image.fromarray(np_circle) draw = ImageDraw.Draw(PIL_circle) draw.ellipse((0, 0, PIL_circle.size[0], PIL_circle.size[1]), fill="white", outline="white", width=line_width) np_circle = np.array(PIL_circle, dtype=np.uint8) # 画空心圆,用于边框 np_corner = np.zeros(shape=(circle_d, circle_d), dtype=np.uint8) PIL_corner = Image.fromarray(np_corner) draw = ImageDraw.Draw(PIL_corner) draw.ellipse((0, 0, PIL_corner.size[0], PIL_corner.size[1]), fill=None, outline="white", width=line_width) np_corner = np.array(PIL_corner, dtype=np.uint8) # 画矩形边框 np_outline = np.zeros(shape=(np_seal.shape[0], np_seal.shape[1]), dtype=np.uint8) PIL_outline = Image.fromarray(np_outline) draw = ImageDraw.Draw(PIL_outline) draw.rectangle((0, 0, PIL_outline.size[0], PIL_outline.size[1]), fill=None, outline="white", width=line_width) np_outline = np.array(PIL_outline, dtype=np.uint8) change_size_to_round = False change_shape_to_round = False if random.random() < 1/3 or split_vertical: # 矩形切角(若有半阴半阳,则只做矩形) cut_corner = (0.15, 0.2) else: # 圆形或椭圆形印章 if random.random() < 0.5: cut_corner = (0.3, 0.5) else: cut_corner = (0.5, 0.5) if random.random() < 1/3: change_shape_to_round = True elif random.random() < 0.5 : change_size_to_round = True if change_size_to_round: seal_reshape = 0.8 round_background = np.zeros(shape=np_seal.shape, dtype=np.uint8) new_width = int(np_seal.shape[1] * seal_reshape) new_height = int(np_seal.shape[0] * seal_reshape) np_seal = resize_img_by_opencv(np_seal, obj_size=(new_width, new_height)) bbox_list_list = [text_bbox_list, char_bbox_list] for bbox_list in bbox_list_list: for bbox in bbox_list: bbox[0] *= seal_reshape bbox[1] *= seal_reshape bbox[2] *= seal_reshape bbox[3] *= seal_reshape x1_round_seal = int((round_background.shape[1] - new_width) / 2) x2_round_seal = x1_round_seal + new_width y1_round_seal = int((round_background.shape[0] - new_height) / 2) y2_round_seal = y1_round_seal + new_height if shadow_seal: np_seal = reverse_image_color(np_seal) # 把seal缩小后加入新背景(白变黑) PIL_seal = Image.fromarray(np_seal) np_seal, text_bbox_list, char_bbox_list = add_subpage_into_page( round_background, PIL_seal, text_bbox_list, char_bbox_list, x1_round_seal, x2_round_seal, y1_round_seal, y2_round_seal ) if not shadow_seal: np_seal = reverse_image_color(np_seal) # 切出四个弧度角 for x in (0, circle_d): for y in (0, circle_d): corner_x = random.randint(int(circle_d * cut_corner[0]), int(circle_d * cut_corner[1])) corner_y = random.randint(int(circle_d * cut_corner[0]), int(circle_d * cut_corner[1])) x1_in_seal = 1 y1_in_seal = 1 if x == 0: x1 = x x2 = x + corner_x x1_in_seal = 0 else: x1 = x - corner_x x2 = x if y == 0: y1 = y y2 = y + corner_y y1_in_seal = 0 else: y1 = y - corner_y y2 = y np_corner_circle = np_circle[y1:y2, x1:x2] left, right, top, low = find_min_bound_box(np_corner_circle) np_corner_circle = np_corner_circle[top:low + 1, left:right + 1] np_corner_circle = reverse_image_color(np_corner_circle) # 留下的部分变为白色,即圆弧以外的部分 if x1_in_seal != 0: x1_in_seal = np_seal.shape[1] - np_corner_circle.shape[1] if y1_in_seal != 0: y1_in_seal = np_seal.shape[0] - np_corner_circle.shape[0] if change_shape_to_round: np_seal = follow_the_shape(np_corner_circle, np_seal, x1_in_seal, x1_in_seal+np_corner_circle.shape[1], y1_in_seal, y1_in_seal+np_corner_circle.shape[0]) np_seal[y1_in_seal:y1_in_seal+np_corner_circle.shape[0], x1_in_seal:x1_in_seal+np_corner_circle.shape[1]] |= np_corner_circle np_corner_line = np_corner[y1:y2, x1:x2] left, right, top, low = find_min_bound_box(np_corner_line) # if x1_in_seal != 0: # x1_in_seal += line_width * 4 # if y1_in_seal != 0: # y1_in_seal += line_width * 4 np_corner_line = np_corner_line[top:low + 1, left:right + 1] np_outline[y1_in_seal:y1_in_seal+np_corner_circle.shape[0], x1_in_seal:x1_in_seal+np_corner_circle.shape[1]] = np_corner_line np_outline = resize_img_by_opencv(np_outline, obj_size=(np_seal.shape[1] + 4*line_width, np_seal.shape[0] + 4*line_width)) return np_seal, np_outline, text_bbox_list, char_bbox_list
def create_book_page_with_img(shape=(960, 540), text_type="horizontal"): text_type = check_text_type(text_type) # 黑色背景书页 np_page = np.zeros(shape=shape, dtype=np.uint8) page_height, page_width = shape # 随机确定是否画边框线及行线 draw = None if random.random() < 0.7: PIL_page = Image.fromarray(np_page) draw = ImageDraw.Draw(PIL_page) # 随机确定书页边框 margin_w = round(random.uniform(0.01, 0.05) * page_width) margin_h = round(random.uniform(0.01, 0.05) * page_height) margin_line_thickness = random.randint(2, 6) line_thickness = round(margin_line_thickness / 2) if draw is not None: # 点的坐标格式为(x, y),不是(y, x) draw.rectangle( [(margin_w, margin_h), (page_width - 1 - margin_w, page_height - 1 - margin_h)], fill=None, outline="white", width=margin_line_thickness) # 记录下文本行的bounding-box text_bbox_records_list = [] head_tail_list = [] if text_type == "h": # 横向排列 # 随机确定文本的行数 rows_num = random.randint(6, 10) row_h = (page_height - 2 * margin_h) / rows_num # y-coordinate划分行 ys = [margin_h + round(i * row_h) for i in range(rows_num)] + [page_height - 1 - margin_h] # 画行线,第一条线和最后一条线是边框线,不需要画 if draw is not None: for y in ys[1:-1]: draw.line([(margin_w, y), (page_width - 1 - margin_w, y)], fill="white", width=line_thickness) np_page = np.array(PIL_page, dtype=np.uint8) # 随机决定字符间距占行距的比例 char_spacing = (random.uniform(0.02, 0.15), random.uniform(0.0, 0.2) ) # (高方向, 宽方向) # 逐行生成汉字 for i in range(len(ys) - 1): y1, y2 = ys[i] + 1, ys[i + 1] - 1 x = margin_w + int( random.uniform(1.0, 1.5) * margin_line_thickness) row_length = page_width - x - margin_w if random.random() < 0.3: row_length = int(random.uniform(0.2, 1) * row_length) if draw is not None: draw.line([(x, y1), (x, y2)], fill="white", width=line_thickness) _, text_bbox_list, _ = generate_mix_rows_chars(x, y1, y2, row_length, np_page, char_spacing, use_img=True) text_bbox_records_list.extend(text_bbox_list) if len(text_bbox_list) == 2: head_tail_list.extend([ (text_bbox_list[0][1], text_bbox_list[0][3]), (text_bbox_list[1][1], text_bbox_list[1][3]) ]) else: min_y1 = min([_y1 for _x1, _y1, _x2, _y2 in text_bbox_list]) max_y2 = max([_y2 for _x1, _y1, _x2, _y2 in text_bbox_list]) head_tail_list.append((min_y1, max_y2)) # 获取行之间的划分位置 split_pos = [ margin_h, ] for i in range(len(head_tail_list) - 1): y_cent = (head_tail_list[i][1] + head_tail_list[i + 1][0]) // 2 split_pos.append(y_cent) split_pos.append(page_height - 1 - margin_h) else: # 纵向排列 # 随机决定文本的列数 # cols_num = random.randint(6, 10) # cols_num = random.randint(18, 23) # cols_num = random.randint(14, 19) cols_num = random.randint(16, 20) col_w = (page_width - 2 * margin_w) / cols_num # x-coordinate划分列 xs = [margin_w + round(i * col_w) for i in range(cols_num)] + [ page_width - 1 - margin_w, ] # 画列线,第一条线和最后一条线是边缘线,不需要画 if draw is not None: for x in xs[1:-1]: draw.line([(x, margin_h), (x, page_height - 1 - margin_h)], fill="white", width=line_thickness) np_page = np.array(PIL_page, dtype=np.uint8) # 随机决定字符间距占列距的比例 char_spacing = (random.uniform(0.0, 0.2), random.uniform(0.02, 0.15) ) # (高方向, 宽方向) ys = [0 for i in range(len(xs))] col_lengths = [0 for i in range(len(xs))] for i in range(len(xs) - 1, 0, -1): x1, x2 = xs[i - 1] + 1, xs[i] - 1 ys[i] = margin_h + int( random.uniform(0.0, 1) * margin_line_thickness) col_lengths[i] = page_height - ys[i] - margin_h if random.random() < 0.3: col_lengths[i] = int(random.uniform(0.2, 1) * col_lengths[i]) yy = col_lengths[i] + margin_h + margin_line_thickness if draw is not None: draw.line([(x1, yy), (x2, yy)], fill="white", width=line_thickness) np_page = np.array(PIL_page, dtype=np.uint8) # Image.fromarray(np_page).show() # 逐列生成汉字,最右边为第一列 for i in range(len(xs) - 1, 0, -1): x1, x2 = xs[i - 1] + 1, xs[i] - 1 y = ys[i] col_length = col_lengths[i] _, text_bbox_list, _ = generate_mix_cols_chars(x1, x2, y, col_length, np_page, char_spacing, use_img=True) text_bbox_records_list.extend(text_bbox_list) if len(text_bbox_list) == 2: head_tail_list.extend([ (text_bbox_list[1][0], text_bbox_list[1][2]), (text_bbox_list[0][0], text_bbox_list[0][2]) ]) else: min_x1 = min([_x1 for _x1, _y1, _x2, _y2 in text_bbox_list]) max_x2 = max([_x2 for _x1, _y1, _x2, _y2 in text_bbox_list]) head_tail_list.append((min_x1, max_x2)) head_tail_list.reverse() # 由于最右边为第一列,需要反转 # 获取列之间的划分位置 split_pos = [ margin_w, ] for i in range(len(head_tail_list) - 1): x_cent = (head_tail_list[i][1] + head_tail_list[i + 1][0]) // 2 split_pos.append(x_cent) split_pos.append(page_width - 1 - margin_w) # 将黑底白字转换为白底黑字 np_page = reverse_image_color(np_img=np_page) PIL_page = Image.fromarray(np_page) # print(text_bbox_list) # print(len(text_bbox_list)) # PIL_page.show() return PIL_page, text_bbox_records_list, split_pos