Spaces:
Sleeping
Sleeping
| #!/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 | |