salad-demo / salad /utils /imageutil.py
DveloperY0115's picture
init repo
801501a
raw
history blame
15.2 kB
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