Spaces:
Running
on
Zero
Running
on
Zero
#!/usr/bin/env python | |
# -*- coding: utf-8 -*- | |
""" | |
These functions are work on a set of images in a directory. | |
""" | |
import cv2 | |
import copy | |
import glob | |
import os | |
import re | |
import sys | |
import numpy as np | |
from PIL import Image | |
from subprocess import check_output | |
def minify(datadir, destdir, factors=[], resolutions=[], extend='png'): | |
"""Using mogrify to resize rgb image | |
Args: | |
datadir(str): source data path | |
destdir(str): save path | |
factor(int): ratio of original width or height | |
resolutions(int): new width or height | |
""" | |
imgs = [os.path.join(datadir, f) for f in sorted(os.listdir(datadir))] | |
imgs = [f for f in imgs if any([f.endswith(ex) for ex in ['JPG', 'jpg', 'png', 'jpeg', 'PNG']])] | |
wd = os.getcwd() | |
for r in factors + resolutions: | |
if isinstance(r, int): | |
name = 'images_{}'.format(r) | |
resizearg = '{}%'.format(int(r)) | |
else: | |
name = 'images_{}x{}'.format(r[1], r[0]) | |
resizearg = '{}x{}'.format(r[1], r[0]) | |
if os.path.exists(destdir): | |
continue | |
print('Minifying', r, datadir) | |
os.makedirs(destdir) | |
check_output('cp {}/* {}'.format(datadir, destdir), shell=True) | |
ext = imgs[0].split('.')[-1] | |
args = ' '.join(['mogrify', '-resize', resizearg, '-format', extend, '*.{}'.format(ext)]) | |
print(args) | |
os.chdir(destdir) | |
check_output(args, shell=True) | |
os.chdir(wd) | |
if ext != extend: | |
check_output('rm {}/*.{}'.format(destdir, ext), shell=True) | |
print('Removed duplicates') | |
print('Done') | |
def resizemask(datadir, destdir, factors=[], resolutions=[]): | |
"""Using PIL.Image.resize to resize binary images with nearest-neighbor | |
Args: | |
datadir(str): source data path | |
destdir(str): save path | |
factor(float): 1/N original width or height | |
resolutions(int): new width or height | |
""" | |
mask_paths = sorted([p for p in glob.glob(os.path.join(datadir, '*')) | |
if re.search('/*\.(jpg|jpeg|png|gif|bmp)', str(p))]) | |
old_size = np.array(Image.open(mask_paths[0])).shape | |
if len(old_size) != 2: | |
old_size = old_size[:2] | |
for r in factors + resolutions: | |
if isinstance(r, int): | |
width = int(old_size[0] / r) | |
height = int(old_size[1] / r) | |
else: | |
width = r[0] | |
height = r[1] | |
if os.path.exists(destdir): | |
continue | |
else: | |
os.makedirs(destdir) | |
for i, mask_path in enumerate(mask_paths): | |
mask = Image.open(mask_path) | |
new_mask = mask.resize((width, height)) | |
base_filename = mask_path.split('/')[-1] | |
new_mask.save(os.path.join(destdir, base_filename)) | |
print('Done') | |
def getbbox(mask, exponent=1): | |
"""Computing bboxes of foreground in the masks | |
Args: | |
mask: binary image | |
exponent(int): the size (width or height) should be a multiple of exponent | |
""" | |
x_center = mask.shape[0] // 2 | |
y_center = mask.shape[1] // 2 | |
x, y = (mask != 0).nonzero() # x:height; y:width | |
bbox = [min(x), max(x), min(y), max(y)] | |
# nearest rectangle box that height/width is the multipler of a factor | |
x_min = np.max([bbox[1] - x_center, x_center - bbox[0]]) * 2 | |
y_min = np.max([bbox[3] - y_center, y_center - bbox[2]]) * 2 | |
new_x = int(np.ceil(x_min / exponent) * exponent) | |
new_y = int(np.ceil(y_min / exponent) * exponent) | |
# print("A rectangle to bound the object with width and height:", (new_y, new_x)) | |
bbox = [x_center - new_x // 2, x_center + new_x // 2, | |
y_center - new_y // 2, y_center + new_y // 2] | |
return bbox | |
def centercrop(img, new_size): | |
"""Computing bboxes of foreground in the masks | |
Args: | |
img: PIL image | |
exponent(int): the size (width or height) should be a multiple of exponent | |
""" | |
if len(new_size) == 2: | |
new_width = new_size[0] | |
new_height = new_size[1] | |
else: | |
print('ERROR: Valid size not found. Aborting') | |
sys.exit() | |
width, height = img.size | |
left = (width - new_width) // 2 | |
top = (height - new_height) // 2 | |
right = (width + new_width) // 2 | |
bottom = (height + new_height) // 2 | |
new_img = img.crop((left, top, right, bottom)) | |
return new_img | |
def invertmask(img, mask): | |
# mask only has 0 and 1, extract the foreground | |
fg = cv2.bitwise_and(img, img, mask=mask) | |
# create white background | |
black_bg = np.zeros(img.shape, np.uint8) | |
white_bg = ~black_bg | |
# masking the white background | |
white_bg = cv2.bitwise_and(white_bg, white_bg, mask=mask) | |
white_bg = ~white_bg | |
# foreground will be added to the black area | |
new_img = cv2.add(white_bg, img) | |
# invert mask to 0 for foreground and 255 for background | |
new_mask = np.where(mask == 0, 255, 0) | |
return new_img, new_mask | |
def gen_square_crops(img, bbox, padding_color=(255, 255, 255), upscale_quality=Image.LANCZOS): | |
""" | |
Generate square crops from an image based on a bounding box. | |
Args: | |
img: PIL Image object | |
bbox: Tuple of (x0, y0, x1, y1) coordinates | |
padding_color: Color for padding (default white) | |
upscale_quality: Resampling method for upscaling (default LANCZOS) | |
Returns: | |
PIL Image object with square crop | |
""" | |
img_width, img_height = img.size | |
x0, y0, x1, y1 = bbox | |
# Calculate original width and height of the bbox | |
bbox_width = x1 - x0 | |
bbox_height = y1 - y0 | |
# Determine the size of the square crop | |
new_size = max(bbox_width, bbox_height) | |
# Calculate center of the original bbox | |
center_x = x0 + bbox_width // 2 | |
center_y = y0 + bbox_height // 2 | |
# Calculate new coordinates that maintain the square aspect ratio | |
half_size = new_size // 2 | |
# Adjust coordinates to stay within image boundaries | |
new_x0 = max(0, center_x - half_size) | |
new_y0 = max(0, center_y - half_size) | |
new_x1 = min(img_width, center_x + half_size) | |
new_y1 = min(img_height, center_y + half_size) | |
# If we're at the edges, adjust the other side to maintain square size | |
if new_x0 == 0 and new_x1 < img_width: | |
new_x1 = min(img_width, new_x0 + new_size) | |
elif new_x1 == img_width and new_x0 > 0: | |
new_x0 = max(0, new_x1 - new_size) | |
if new_y0 == 0 and new_y1 < img_height: | |
new_y1 = min(img_height, new_y0 + new_size) | |
elif new_y1 == img_height and new_y0 > 0: | |
new_y0 = max(0, new_y1 - new_size) | |
# Crop the image | |
cropped_img = img.crop((new_x0, new_y0, new_x1, new_y1)) | |
# Create a new square image | |
square_img = Image.new('RGB', (new_size, new_size), padding_color) | |
# Calculate paste position (centered) | |
paste_x = (new_size - (new_x1 - new_x0)) // 2 | |
paste_y = (new_size - (new_y1 - new_y0)) // 2 | |
# Paste the cropped image onto the square canvas | |
square_img.paste(cropped_img, (paste_x, paste_y)) | |
# If the original crop was smaller than new_size, we need to resize with anti-aliasing | |
if (new_x1 - new_x0) < new_size or (new_y1 - new_y0) < new_size: | |
# Calculate the scale factor | |
scale = new_size / max(bbox_width, bbox_height) | |
# Resize the original crop with anti-aliasing | |
resized_crop = img.crop((x0, y0, x1, y1)).resize( | |
(int(bbox_width * scale), int(bbox_height * scale)), | |
resample=upscale_quality | |
) | |
# Create new square image | |
square_img = Image.new('RGB', (new_size, new_size), padding_color) | |
# Calculate centered position | |
paste_x = (new_size - resized_crop.width) // 2 | |
paste_y = (new_size - resized_crop.height) // 2 | |
# Paste the resized image | |
square_img.paste(resized_crop, (paste_x, paste_y)) | |
return square_img | |