Spaces:
Sleeping
Sleeping
| import os | |
| import textwrap | |
| from pathlib import Path | |
| from typing import List | |
| import cv2 | |
| import numpy as np | |
| import PIL | |
| from PIL import Image, ImageChops, ImageDraw, ImageFont | |
| kMinMargin = 10 | |
| def stack_images_horizontally(images: List, save_path=None): | |
| widths, heights = list(zip(*(i.size for i in images))) | |
| total_width = sum(widths) | |
| max_height = max(heights) | |
| new_im = Image.new("RGBA", (total_width, max_height)) | |
| x_offset = 0 | |
| for im in images: | |
| new_im.paste(im, (x_offset, 0)) | |
| x_offset += im.size[0] | |
| if save_path is not None: | |
| new_im.save(save_path) | |
| return new_im | |
| def stack_images_vertically(images: List, save_path=None): | |
| widths, heights = list(zip(*(i.size for i in images))) | |
| max_width = max(widths) | |
| total_height = sum(heights) | |
| new_im = Image.new("RGBA", (max_width, total_height)) | |
| y_offset = 0 | |
| for im in images: | |
| new_im.paste(im, (0, y_offset)) | |
| y_offset += im.size[1] | |
| if save_path is not None: | |
| new_im.save(save_path) | |
| return new_im | |
| def merge_images(images: List): | |
| if isinstance(images[0], Image.Image): | |
| return stack_images_horizontally(images) | |
| images = list(map(stack_images_horizontally, images)) | |
| return stack_images_vertically(images) | |
| def draw_text( | |
| image: PIL.Image, | |
| text: str, | |
| font_size=None, | |
| font_color=(0, 0, 0), | |
| max_seq_length=100, | |
| ): | |
| W, H = image.size | |
| S = max(W, H) | |
| font_path = os.path.join(cv2.__path__[0], "qt", "fonts", "DejaVuSans.ttf") | |
| font_size = max(int(S / 32), 20) if font_size is None else font_size | |
| font = ImageFont.truetype(font_path, size=font_size) | |
| text_wrapped = textwrap.fill(text, max_seq_length) | |
| w, h = font.getsize(text_wrapped) | |
| new_im = Image.new("RGBA", (W, H + h)) | |
| new_im.paste(image, (0, h)) | |
| draw = ImageDraw.Draw(new_im) | |
| draw.text((max((W - w) / 2, 0), 0), text_wrapped, font=font, fill=font_color) | |
| return new_im | |
| def to_white(img): | |
| new_img = Image.new("RGBA", img.size, "WHITE") | |
| new_img.paste(img, (0, 0), img) | |
| new_img.convert("RGB") | |
| return new_img | |
| def get_bbox(in_file, fuzz=17.5): | |
| im = Image.open(in_file) | |
| # bbox = im.convert("RGBa").getbbox() | |
| try: | |
| bg = Image.new(im.mode, im.size, im.getpixel((0, 0))) | |
| except OSError as err: | |
| print(f"error {in_file}") | |
| raise OSError | |
| diff = ImageChops.difference(im, bg) | |
| offset = int(round(float(fuzz) / 100.0 * 255.0)) | |
| diff = ImageChops.add(diff, diff, 2.0, -offset) | |
| bbox = diff.getbbox() | |
| bx_min = max(bbox[0] - kMinMargin, 0) | |
| by_min = max(bbox[1] - kMinMargin, 0) | |
| bx_max = min(bbox[2] + kMinMargin, im.size[0]) | |
| by_max = min(bbox[3] + kMinMargin, im.size[1]) | |
| bbox_margin = (bx_min, by_min, bx_max, by_max) | |
| return bbox_margin | |
| def get_largest_bbox(in_files): | |
| largest_bbox = (float("Inf"), float("Inf"), -float("Inf"), -float("Inf")) | |
| for in_file in in_files: | |
| bbox = get_bbox(in_file) | |
| largest_bbox = ( | |
| min(bbox[0], largest_bbox[0]), | |
| min(bbox[1], largest_bbox[1]), | |
| max(bbox[2], largest_bbox[2]), | |
| max(bbox[3], largest_bbox[3]), | |
| ) | |
| return largest_bbox | |
| def trim(in_file, out_file, keep_ratio): | |
| # im = Image.open(in_file) | |
| # bbox = im.convert("RGBa").getbbox() | |
| bbox = get_bbox(in_file) | |
| trim_with_bbox(in_file, out_file, bbox, keep_ratio) | |
| def trim_with_bbox(in_file, out_file, bbox, keep_ratio): | |
| im = Image.open(in_file) | |
| if keep_ratio: | |
| w, h = im.size | |
| r = float(w) / h | |
| bx_min, by_min, bx_max, by_max = bbox[0], bbox[1], bbox[2], bbox[3] | |
| bw, bh = bx_max - bx_min, by_max - by_min | |
| bcx, bcy = 0.5 * (bx_min + bx_max), 0.5 * (by_min + by_max) | |
| br = float(bw) / bh | |
| if br > r: | |
| bh = int(round(bw / r)) | |
| by_min, by_max = int(round(bcy - 0.5 * bh)), int(round(bcy + 0.5 * bh)) | |
| if by_min < 0: | |
| by_min = 0 | |
| by_max = bh | |
| elif by_max > h: | |
| by_max = h | |
| by_min = h - bh | |
| assert bh >= bh | |
| elif br < r: | |
| bw = int(round(bh * r)) | |
| bx_min, bx_max = int(round(bcx - 0.5 * bw)), int(round(bcx + 0.5 * bw)) | |
| if bx_min < 0: | |
| bx_min = 0 | |
| bx_max = bw | |
| elif bx_max > w: | |
| bx_max = w | |
| bx_min = w - bw | |
| bbox = (bx_min, by_min, bx_max, by_max) | |
| im.crop(bbox).save(out_file, "png") | |
| def trim_with_largest_bbox(in_files, out_files, keep_ratio): | |
| assert len(in_files) == len(out_files) | |
| bbox = get_largest_bbox(in_files) | |
| for i in range(len(in_files)): | |
| trim_with_bbox(in_files[i], out_files[i], bbox, keep_ratio) | |
| def create_image_table_tight_centering( | |
| in_img_files, out_img_file, max_total_width=2560, draw_col_lines=[] | |
| ): | |
| n_rows = len(in_img_files) | |
| n_cols = len(in_img_files[0]) | |
| # Compute width and height of each image. | |
| width = 0 | |
| row_top = [float("Inf")] * n_rows | |
| row_bottom = [-float("Inf")] * n_rows | |
| for row in range(n_rows): | |
| for col in range(n_cols): | |
| img_left, img_top, img_right, img_bottom = get_bbox(in_img_files[row][col]) | |
| img_width = img_right - img_left | |
| width = max(width, img_width) | |
| row_top[row] = min(row_top[row], img_top) | |
| row_bottom[row] = max(row_bottom[row], img_bottom) | |
| row_height = [bottom - top for bottom, top in zip(row_bottom, row_top)] | |
| # Combine images. | |
| cmd = "convert " | |
| for row in range(n_rows): | |
| cmd += " \( " | |
| for col in range(n_cols): | |
| img_left, img_top, img_right, img_bottom = get_bbox(in_img_files[row][col]) | |
| img_h_center = 0.5 * (img_left + img_right) | |
| left = int(img_h_center - 0.5 * width) | |
| cmd += " \( {} ".format(in_img_files[row][col]) | |
| cmd += "-gravity NorthWest -crop {}x{}+{}+{} +repage \) ".format( | |
| width, row_height[row], left, row_top[row] | |
| ) | |
| cmd += " -gravity center -background white +append \) " | |
| cmd += "-append " + out_img_file | |
| print(cmd) | |
| os.system(cmd) | |
| # Draw lines for columns. | |
| for col in draw_col_lines: | |
| if col <= 0 or col >= n_cols: | |
| continue | |
| strokewidth = max(int(round(width * 0.005)), 1) | |
| pos = col * width | |
| cmd = "convert " + out_img_file + " -stroke black " | |
| cmd += "-strokewidth {} ".format(strokewidth) | |
| cmd += '-draw "line {0},0 {0},10000000" '.format(pos) + out_img_file | |
| os.system(cmd) | |
| # Resize the combined image if it is too large. | |
| print(n_cols * width) | |
| if (n_cols * width) > max_total_width: | |
| cmd = "convert {0} -resize {1}x +repage {0}".format( | |
| out_img_file, max_total_width | |
| ) | |
| print(cmd) | |
| os.system(cmd) | |
| print("Saved '{}'.".format(out_img_file)) | |
| return width, row_height | |
| def create_image_table_tight_centering_per_row( | |
| in_img_files, out_img_dir, max_total_width=1280, draw_col_lines=[] | |
| ): | |
| n_rows = len(in_img_files) | |
| n_cols = len(in_img_files[0]) | |
| # Compute width and height of each image. | |
| width = 0 | |
| row_top = [float("Inf")] * n_rows | |
| row_bottom = [-float("Inf")] * n_rows | |
| for row in range(n_rows): | |
| for col in range(n_cols): | |
| img_left, img_top, img_right, img_bottom = get_bbox(in_img_files[row][col]) | |
| img_width = img_right - img_left | |
| width = max(width, img_width) | |
| row_top[row] = min(row_top[row], img_top) | |
| row_bottom[row] = max(row_bottom[row], img_bottom) | |
| row_height = [bottom - top for bottom, top in zip(row_bottom, row_top)] | |
| if not os.path.exists(out_img_dir): | |
| os.makedirs(out_img_dir) | |
| # Combine images. | |
| for row in range(n_rows): | |
| out_img_file = os.path.join(out_img_dir, "{:02d}.png".format(row)) | |
| cmd = "convert " | |
| for col in range(n_cols): | |
| img_left, img_top, img_right, img_bottom = get_bbox(in_img_files[row][col]) | |
| img_h_center = 0.5 * (img_left + img_right) | |
| left = int(img_h_center - 0.5 * width) | |
| cmd += " \( {} ".format(in_img_files[row][col]) | |
| cmd += "-gravity NorthWest -crop {}x{}+{}+{} +repage \) ".format( | |
| width, row_height[row], left, row_top[row] | |
| ) | |
| cmd += " -gravity center -background white +append " + out_img_file | |
| print(cmd) | |
| os.system(cmd) | |
| # Draw lines for columns. | |
| for col in draw_col_lines: | |
| if col <= 0 or col >= n_cols: | |
| continue | |
| strokewidth = max(int(round(width * 0.005)), 1) | |
| pos = col * width | |
| cmd = "convert " + out_img_file + " -stroke black " | |
| cmd += "-strokewidth {} ".format(strokewidth) | |
| cmd += '-draw "line {0},0 {0},10000000" '.format(pos) + out_img_file | |
| os.system(cmd) | |
| print(cmd) | |
| # Resize the combined image if it is too large. | |
| print(n_cols * width) | |
| if (n_cols * width) > max_total_width: | |
| cmd = "convert {0} -resize {1}x +repage {0}".format( | |
| out_img_file, max_total_width | |
| ) | |
| print(cmd) | |
| os.system(cmd) | |
| print("Saved '{}'.".format(out_img_file)) | |
| return width, row_height | |
| def create_image_table_tight_centering_per_col( | |
| in_img_files, out_img_dir, max_width=2560, draw_col_lines=[] | |
| ): | |
| n_rows = len(in_img_files) | |
| n_cols = len(in_img_files[0]) | |
| # Compute width and height of each image. | |
| width = 0 | |
| row_top = [float("Inf")] * n_rows | |
| row_bottom = [-float("Inf")] * n_rows | |
| for row in range(n_rows): | |
| for col in range(n_cols): | |
| img_left, img_top, img_right, img_bottom = get_bbox(in_img_files[row][col]) | |
| img_width = img_right - img_left | |
| width = max(width, img_width) | |
| row_top[row] = min(row_top[row], img_top) | |
| row_bottom[row] = max(row_bottom[row], img_bottom) | |
| row_height = [bottom - top for bottom, top in zip(row_bottom, row_top)] | |
| if not os.path.exists(out_img_dir): | |
| os.makedirs(out_img_dir) | |
| # Combine images. | |
| for col in range(n_cols): | |
| out_img_file = os.path.join(out_img_dir, "{:02d}.png".format(col)) | |
| cmd = "convert " | |
| for row in range(n_rows): | |
| img_left, img_top, img_right, img_bottom = get_bbox(in_img_files[row][col]) | |
| img_h_center = 0.5 * (img_left + img_right) | |
| left = int(img_h_center - 0.5 * width) | |
| cmd += " \( {} ".format(in_img_files[row][col]) | |
| cmd += "-gravity NorthWest -crop {}x{}+{}+{} +repage \) ".format( | |
| width, row_height[row], left, row_top[row] | |
| ) | |
| cmd += " -gravity center -background white -append " + out_img_file | |
| print(cmd) | |
| os.system(cmd) | |
| # Resize the combined image if it is too large. | |
| if width > max_width: | |
| cmd = "convert {0} -resize {1}x +repage {0}".format(out_img_file, max_width) | |
| print(cmd) | |
| os.system(cmd) | |
| print("Saved '{}'.".format(out_img_file)) | |
| return width, row_height | |
| def create_image_table_after_crop( | |
| in_img_files, | |
| out_img_file, | |
| lbox=None, | |
| tbox=None, | |
| rbox=None, | |
| dbox=None, | |
| max_total_width=2560, | |
| draw_col_lines=[], | |
| transpose=False, | |
| verbose=False, | |
| line_multi=None, | |
| ): | |
| out_img_file = str(out_img_file) | |
| if not isinstance(in_img_files[0], list): | |
| in_img_files = [in_img_files] | |
| in_img_files = [[x for x in row if len(str(x)) != 0] for row in in_img_files] | |
| if transpose: | |
| x = np.array(in_img_files) | |
| in_img_files = x.transpose().tolist() | |
| n_rows = len(in_img_files) | |
| n_cols = len(in_img_files[0]) | |
| # Compute width and height of each image. | |
| width = 0 | |
| row_top = [float("Inf")] * n_rows | |
| row_bottom = [-float("Inf")] * n_rows | |
| for row in range(n_rows): | |
| for col in range(n_cols): | |
| img_left, img_top, img_right, img_bottom = get_bbox(in_img_files[row][col]) | |
| # img_left, img_top, img_right, img_bottom = lbox, tbox, rbox, dbox | |
| img_left = img_left if lbox is None else lbox | |
| img_top = img_top if tbox is None else tbox | |
| img_right = img_right if rbox is None else rbox | |
| img_bottom = img_bottom if dbox is None else dbox | |
| img_width = img_right - img_left | |
| width = max(width, img_width) | |
| row_top[row] = min(row_top[row], img_top) | |
| row_bottom[row] = max(row_bottom[row], img_bottom) | |
| row_height = [bottom - top for bottom, top in zip(row_bottom, row_top)] | |
| # Combine images. | |
| cmd = "convert " | |
| for row in range(n_rows): | |
| cmd += " \( " | |
| for col in range(n_cols): | |
| # img_left, img_top, img_right, img_bottom = lbox, tbox, rbox, dbox | |
| img_left, img_top, img_right, img_bottom = get_bbox(in_img_files[row][col]) | |
| img_left = img_left if lbox is None else lbox | |
| img_top = img_top if tbox is None else tbox | |
| img_right = img_right if rbox is None else rbox | |
| img_bottom = img_bottom if dbox is None else dbox | |
| img_h_center = 0.5 * (img_left + img_right) | |
| left = int(img_h_center - 0.5 * width) | |
| cmd += " \( {} ".format(in_img_files[row][col]) | |
| cmd += "-gravity NorthWest -crop {}x{}+{}+{} +repage \) ".format( | |
| width, row_height[row], left, row_top[row] | |
| ) | |
| cmd += " -gravity center -background white +append \) " | |
| cmd += "-append " + out_img_file | |
| if verbose: | |
| print(cmd) | |
| os.system(cmd) | |
| # Draw lines for columns. | |
| for col in draw_col_lines: | |
| if col <= 0 or col >= n_cols: | |
| continue | |
| strokewidth = max(int(round(width * 0.005)), 1) | |
| if line_multi is not None: | |
| strokewidth *= line_multi | |
| pos = col * width | |
| cmd = "convert " + out_img_file + " -stroke black " | |
| cmd += "-strokewidth {} ".format(strokewidth) | |
| cmd += '-draw "line {0},0 {0},10000000" '.format(pos) + out_img_file | |
| if verbose: | |
| print(cmd) | |
| os.system(cmd) | |
| # Resize the combined image if it is too large. | |
| # print(n_cols * width) | |
| # if (n_cols * width) > max_total_width: | |
| # cmd = "convert {0} -resize {1}x +repage {0}".format( | |
| # out_img_file, max_total_width | |
| # ) | |
| # print(cmd) | |
| # os.system(cmd) | |
| print("Saved '{}'.".format(out_img_file)) | |
| return width, row_height | |
| def make_2dgrid(input_list, num_rows=None, num_cols=None): | |
| # if num_rows * num_cols != len(input_list): | |
| # raise Warning("Number of rows and columns do not match the length of the input list.") | |
| if num_rows is None and num_cols is not None: | |
| num_rows = len(input_list) // num_cols + 1 | |
| output_list = [] | |
| for i in range(num_rows): | |
| row = [] | |
| for j in range(num_cols): | |
| if i * num_cols + j >= len(input_list): | |
| break | |
| row.append(input_list[i * num_cols + j]) | |
| output_list.append(row) | |
| return output_list | |