Vision utils

Some utils function to quickly download a bunch of images, check them and pre-resize them

Downloading images

download_images uses random UUID filenames by default. With preserve_filename=True, it uses the URL’s stem. It adds a numeric suffix if a file with the same filename already exists in dest.


source

download_images

def download_images(
    dest, url_file:NoneType=None, urls:NoneType=None, max_pics:int=1000, n_workers:int=8, timeout:int=4,
    preserve_filename:bool=False
):

Download images listed in text file url_file to path dest, at most max_pics

Pass URLs in urls or use url_file with one URL per line. download_images downloads them in threads. It silently skips failed downloads.

Verifying images


source

verify_image

def verify_image(
    fn
):

Confirm that fn can be opened

verify_image uses PIL’s draft mode to decode JPEGs at reduced resolution. This keeps verification fast even for large images. It returns False if opening or decoding the file raises an exception.


source

verify_images

def verify_images(
    fns
):

Find images in fns that can’t be opened

Remove files that verify_images cannot open:

with tempfile.TemporaryDirectory() as d:
    d = Path(d)
    shutil.copy('images/puppy.jpg', d)
    (d/'bad.jpg').write_text('not an image')
    failed = verify_images(get_image_files(d))
    test_eq(failed, [d/'bad.jpg'])
    failed.map(Path.unlink)
    test_eq(get_image_files(d), [d/'puppy.jpg'])

Resizing images


source

resize_to

def resize_to(
    img, targ_sz, use_min:bool=False
):

Size to resize to, to hit targ_sz at same aspect ratio, in PIL coords (i.e wh)*

resize_to calculates dimensions at the original aspect ratio. By default, the larger side equals targ_sz and the result fits inside a targ_sz square. With use_min=True, the smaller side equals targ_sz and the result covers that square. The input needs a PIL-style size attribute:

class _FakeImg():
    def __init__(self, size): self.size=size

img = _FakeImg((200,500))
test_eq(resize_to(img, 400), [160,400])
test_eq(resize_to(img, 400, use_min=True), [400,1000])

source

resize_image

def resize_image(
    file, # Image path, relative to `src`
    dest, # Output directory
    src:str='.', # Directory that `file` is relative to
    max_size:NoneType=None, # Resize so the larger side is at most this many pixels
    n_channels:int=3, # Convert to RGB when 3
    ext:NoneType=None, # Output suffix, e.g. '.png'; default keeps the original
    img_format:NoneType=None, # PIL format for `save`; default inferred from the suffix
    resample:PIL.Image.Resampling=<Resampling.BILINEAR: 2>, # PIL resampling filter
    resume:bool=False, # Skip files already present in `dest`?
    **kwargs
):

Resize file to dest to max_size

resize_image re-encodes files that exceed max_size or have a different number of channels from n_channels. It copies other files unchanged to dest, unless source and destination are the same. Use resume=True to skip existing destination files when resuming an interrupted run.

Resize puppy.jpg to a maximum side of 400 pixels:

file = 'puppy.jpg'
dest = Path('.')
resize_image(file, dest, src='images', max_size=400)
im = Image.open(dest/file)
test_eq(im.shape[1],400)
(dest/file).unlink()

source

resize_images

def resize_images(
    path, # Directory of images
    max_workers:int=4, # Parallel workers; 0 runs in the current process
    max_size:NoneType=None, # Resize so the larger side is at most this many pixels
    recurse:bool=False, # Include subdirectories?
    dest:pathlib.PosixPath=Path('.'), # Output directory
    n_channels:int=3, # Convert to RGB when 3
    ext:NoneType=None, # Output suffix, e.g. '.png'; default keeps the original
    img_format:NoneType=None, # PIL format for `save`; default inferred from the suffix
    resample:PIL.Image.Resampling=<Resampling.BILINEAR: 2>, # PIL resampling filter
    resume:NoneType=None, # Skip files already present in `dest`?
    **kwargs
):

Resize the images in path to dest, so the larger side is at most max_size

resize_images processes images in parallel and preserves the directory structure under dest:

with tempfile.TemporaryDirectory() as d:
    dest = Path(d)/'resized_images'
    resize_images('images', max_size=100, dest=dest, max_workers=0, recurse=True)