Last active
July 12, 2020 13:13
-
-
Save sveitser/e263dd031b70c0f93a6e93f928fd7f69 to your computer and use it in GitHub Desktop.
Resize and center crop a directory of images.
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
#!/usr/bin/env python | |
"""Resize and crop images to square. | |
Install dependencies: pip install click Pillow | |
Usage: python resize.py [OPTIONS] --directory /my/image/dir --convert_directory /my/output/dir | |
Options: | |
--directory TEXT Directory with original images. [required] | |
--convert_directory TEXT Where to save converted images. [required] | |
--test Convert images one by one and examine them on | |
screen. [default: False] | |
-s, --size INTEGER Size of converted images. [default: 512] | |
-n, --n-proc INTEGER Number of processes (defaults to all) [default: | |
0] | |
-q, --quality INTEGER [default: 95] | |
--help Show this message and exit. | |
""" | |
import os | |
import multiprocessing | |
from multiprocessing.pool import Pool | |
from pathlib import Path | |
import click | |
from PIL import Image | |
N_PROC = None | |
ALLOWED_EXTENSIONS = set([".jpg", ".jpeg", ".png", ".tiff"]) | |
QUALITY = 95 | |
def square_bbox(img): | |
w, h = img.size | |
left = max((w - h) // 2, 0) | |
upper = 0 | |
right = min(w - (w - h) // 2, w) | |
lower = h | |
return (left, upper, right, lower) | |
def convert_square(fname, size): | |
img = Image.open(fname) | |
bbox = square_bbox(img) | |
cropped = img.crop(bbox) | |
resized = cropped.resize([size, size]) | |
return resized | |
def get_convert_fname(fname, directory, convert_directory): | |
return str(os.path.normpath(fname)).replace( | |
os.path.normpath(directory), os.path.normpath(convert_directory) | |
) | |
def process(args): | |
directory, convert_directory, fname, size = args | |
convert_fname = get_convert_fname(fname, directory, convert_directory) | |
print(f"{fname} -> {convert_fname}") | |
if os.path.exists(convert_fname): | |
print(f"File {convert_fname} exists already") | |
return | |
os.makedirs(os.path.dirname(convert_fname), exist_ok=True) | |
try: | |
img = convert_square(fname, size) | |
except OSError as exc: | |
print(f"{fname}: {exc}") | |
return | |
save(img, convert_fname) | |
def save(img, fname): | |
img.save(fname, quality=QUALITY) | |
@click.command() | |
@click.option( | |
"--directory", help="Directory with original images.", required=True, | |
) | |
@click.option( | |
"--convert-directory", help="Where to save converted images.", required=True, | |
) | |
@click.option( | |
"--test", | |
is_flag=True, | |
default=False, | |
show_default=True, | |
help="Convert images one by one and examine them on screen.", | |
) | |
@click.option( | |
"--size", "-s", default=512, show_default=True, help="Size of converted images." | |
) | |
@click.option( | |
"--n-proc", | |
"-n", | |
default=0, | |
show_default=True, | |
type=int, | |
help="Number of processes (defaults to all)", | |
) | |
@click.option("--quality", "-q", default=QUALITY, show_default=True) | |
def main(directory, convert_directory, test, size, n_proc, quality): | |
global N_PROC, QUALITY | |
N_PROC = n_proc or multiprocessing.cpu_count() | |
QUALITY = quality | |
os.makedirs(convert_directory, exist_ok=True) | |
filenames = list(Path(directory).rglob("*.*")) | |
print(f"Found {len(filenames)} files") | |
filenames = [ | |
f for f in filenames if os.path.splitext(f)[1].lower() in ALLOWED_EXTENSIONS | |
] | |
n = len(filenames) | |
print( | |
f"Resizing {n} images in {directory} to {convert_directory} with size {size}." | |
) | |
pool = Pool(N_PROC) | |
args = [] | |
for f in filenames: | |
args.append((directory, convert_directory, f, size)) | |
pool.map(process, args, chunksize=1000) | |
pool.close() | |
pool.join() | |
print("done") | |
if __name__ == "__main__": | |
main() |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment