{ "cells": [ { "cell_type": "code", "execution_count": null, "id": "8a01fdc1", "metadata": {}, "outputs": [], "source": [ "#| hide\n", "#| eval: false\n", "! [ -e /content ] && pip install -Uqq fastai # upgrade fastai on colab" ] }, { "cell_type": "raw", "id": "493f7e72", "metadata": {}, "source": [ "---\n", "skip_exec: true\n", "---" ] }, { "cell_type": "code", "execution_count": null, "id": "b62b0f6e", "metadata": {}, "outputs": [], "source": [ "#| default_exp vision.utils" ] }, { "cell_type": "code", "execution_count": null, "id": "a4ba4284", "metadata": {}, "outputs": [], "source": [ "#| export\n", "import uuid\n", "from fastai.torch_basics import *\n", "from fastai.data.all import *\n", "from fastai.vision.core import *\n", "from fastdownload import download_url\n", "from pathlib import Path" ] }, { "cell_type": "code", "execution_count": null, "id": "0c6961ab", "metadata": {}, "outputs": [], "source": [ "#| hide\n", "from nbdev.showdoc import *" ] }, { "cell_type": "markdown", "id": "7b41b88b", "metadata": {}, "source": [ "# Vision utils\n", "\n", "> Some utils function to quickly download a bunch of images, check them and pre-resize them" ] }, { "cell_type": "code", "execution_count": null, "id": "f534f4c7", "metadata": {}, "outputs": [], "source": [ "#| export\n", "def _get_downloaded_image_filename(dest, name, suffix):\n", " start_index = 1\n", " candidate_name = name\n", "\n", " while (dest/f\"{candidate_name}{suffix}\").is_file():\n", " candidate_name = f\"{candidate_name}{start_index}\"\n", " start_index += 1\n", "\n", " return candidate_name" ] }, { "cell_type": "code", "execution_count": null, "id": "e94c0aa9", "metadata": {}, "outputs": [], "source": [ "#| export\n", "def _download_image_inner(dest, inp, timeout=4, preserve_filename=False):\n", " i,url = inp\n", " url = url.split(\"?\")[0]\n", " url_path = Path(url)\n", " suffix = url_path.suffix if url_path.suffix else '.jpg'\n", " name = _get_downloaded_image_filename(dest, url_path.stem, suffix) if preserve_filename else str(uuid.uuid4())\n", " try: download_url(url, dest/f\"{name}{suffix}\", show_progress=False, timeout=timeout)\n", " except Exception as e: f\"Couldn't download {url}.\"" ] }, { "cell_type": "code", "execution_count": null, "id": "d2889065", "metadata": {}, "outputs": [], "source": [ "#| hide\n", "with tempfile.TemporaryDirectory() as d:\n", " d = Path(d)\n", " url = \"https://www.fast.ai/images/jh-head.jpg\"\n", " _download_image_inner(d, (125,url))\n", " test_eq(len(d.ls()), 1)\n", "\n", "with tempfile.TemporaryDirectory() as d:\n", " d = Path(d)\n", " url = \"https://www.fast.ai/images/jh-head.jpg\"\n", "\n", " _download_image_inner(d, (125,url), preserve_filename=True)\n", " assert (d/'jh-head.jpg').is_file()\n", " assert not (d/'jh-head.jpg1').exists()\n", "\n", " _download_image_inner(d, (125,url), preserve_filename=True)\n", " assert (d/'jh-head.jpg').is_file()\n", " assert (d/'jh-head1.jpg').is_file()" ] }, { "cell_type": "code", "execution_count": null, "id": "d0593ffc", "metadata": {}, "outputs": [], "source": [ "#| export\n", "def download_images(dest, url_file=None, urls=None, max_pics=1000, n_workers=8, timeout=4, preserve_filename=False):\n", " \"Download images listed in text file `url_file` to path `dest`, at most `max_pics`\"\n", " if urls is None: urls = url_file.read_text().strip().split(\"\\n\")[:max_pics]\n", " dest = Path(dest)\n", " dest.mkdir(exist_ok=True)\n", " parallel(partial(_download_image_inner, dest, timeout=timeout, preserve_filename=preserve_filename),\n", " list(enumerate(urls)), n_workers=n_workers, threadpool=True)" ] }, { "cell_type": "code", "execution_count": null, "id": "4164341c", "metadata": {}, "outputs": [], "source": [ "#| hide\n", "with tempfile.TemporaryDirectory() as d:\n", " d = Path(d)\n", " url_file = d/'urls.txt'\n", " url_file.write_text(\"\\n\".join([f\"https://www.fast.ai/images/{n}\" for n in \"jh-head.jpg headshot-small.jpg\".split()]))\n", " \n", " download_images(d, url_file, preserve_filename=True)\n", " assert (d/'jh-head.jpg').is_file()\n", " assert (d/'headshot-small.jpg').is_file()\n", " assert not (d/'jh-head1.jpg').exists()" ] }, { "cell_type": "code", "execution_count": null, "id": "41c04937", "metadata": {}, "outputs": [], "source": [ "#| export\n", "def resize_to(img, targ_sz, use_min=False):\n", " \"Size to resize to, to hit `targ_sz` at same aspect ratio, in PIL coords (i.e w*h)\"\n", " w,h = img.size\n", " min_sz = (min if use_min else max)(w,h)\n", " ratio = targ_sz/min_sz\n", " return int(w*ratio),int(h*ratio)" ] }, { "cell_type": "code", "execution_count": null, "id": "6a6a4d9e", "metadata": {}, "outputs": [], "source": [ "class _FakeImg():\n", " def __init__(self, size): self.size=size\n", "\n", "img = _FakeImg((200,500))\n", "test_eq(resize_to(img, 400), [160,400])\n", "test_eq(resize_to(img, 400, use_min=True), [400,1000])" ] }, { "cell_type": "code", "execution_count": null, "id": "3bff393f", "metadata": {}, "outputs": [], "source": [ "#| export\n", "def verify_image(fn):\n", " \"Confirm that `fn` can be opened\"\n", " try:\n", " im = Image.open(fn)\n", " im.draft(im.mode, (32,32))\n", " im.load()\n", " return True\n", " except: return False" ] }, { "cell_type": "code", "execution_count": null, "id": "f418bded", "metadata": {}, "outputs": [], "source": [ "#| export\n", "def verify_images(fns):\n", " \"Find images in `fns` that can't be opened\"\n", " return L(fns[i] for i,o in enumerate(parallel(verify_image, fns)) if not o)" ] }, { "cell_type": "code", "execution_count": null, "id": "aad88d0a", "metadata": {}, "outputs": [], "source": [ "#| export\n", "def resize_image(file, dest, src='.', max_size=None, n_channels=3, ext=None,\n", " img_format=None, resample=BILINEAR, resume=False, **kwargs ):\n", " \"Resize file to dest to max_size\"\n", " dest = Path(dest)\n", " \n", " dest_fname = dest/file\n", " dest_fname.parent.mkdir(exist_ok=True, parents=True)\n", " file = Path(src)/file\n", " if resume and dest_fname.exists(): return\n", " if not verify_image(file): return\n", "\n", " img = Image.open(file)\n", " imgarr = np.array(img)\n", " img_channels = 1 if len(imgarr.shape) == 2 else imgarr.shape[2]\n", " if ext is not None: dest_fname=dest_fname.with_suffix(ext)\n", " if (max_size is not None and (img.height > max_size or img.width > max_size)) or img_channels != n_channels:\n", " if max_size is not None:\n", " new_sz = resize_to(img, max_size)\n", " img = img.resize(new_sz, resample=resample)\n", " if n_channels == 3: img = img.convert(\"RGB\")\n", " img.save(dest_fname, img_format, **kwargs)\n", " elif file != dest_fname : shutil.copy2(file, dest_fname)" ] }, { "cell_type": "code", "execution_count": null, "id": "027d46db", "metadata": {}, "outputs": [], "source": [ "file = 'puppy.jpg'\n", "dest = Path('.')\n", "resize_image(file, dest, src='images', max_size=400)\n", "im = Image.open(dest/file)\n", "test_eq(im.shape[1],400)\n", "(dest/file).unlink()" ] }, { "cell_type": "code", "execution_count": null, "id": "3ec6f281", "metadata": {}, "outputs": [], "source": [ "file = 'puppy.jpg'\n", "dest = Path('images')\n", "resize_image(file, dest, src=dest, max_size=None)" ] }, { "cell_type": "code", "execution_count": null, "id": "e9103784", "metadata": {}, "outputs": [], "source": [ "#| export\n", "def resize_images(path, max_workers=defaults.cpus, max_size=None, recurse=False,\n", " dest=Path('.'), n_channels=3, ext=None, img_format=None, resample=BILINEAR,\n", " resume=None, **kwargs):\n", " \"Resize files on path recursively to dest to max_size\"\n", " path = Path(path)\n", " if resume is None and dest != Path('.'): resume=False\n", " os.makedirs(dest, exist_ok=True)\n", " files = get_image_files(path, recurse=recurse)\n", " files = [o.relative_to(path) for o in files]\n", " parallel(resize_image, files, src=path, n_workers=max_workers, max_size=max_size, dest=dest, n_channels=n_channels, ext=ext,\n", " img_format=img_format, resample=resample, resume=resume, **kwargs)" ] }, { "cell_type": "code", "execution_count": null, "id": "04990b6c", "metadata": {}, "outputs": [], "source": [ "with tempfile.TemporaryDirectory() as d:\n", " dest = Path(d)/'resized_images'\n", " resize_images('images', max_size=100, dest=dest, max_workers=0, recurse=True)" ] }, { "cell_type": "markdown", "id": "d519e6ff", "metadata": {}, "source": [ "# Export -" ] }, { "cell_type": "code", "execution_count": null, "id": "a675cf17", "metadata": {}, "outputs": [], "source": [ "#| hide\n", "from nbdev import nbdev_export\n", "nbdev_export()" ] }, { "cell_type": "code", "execution_count": null, "id": "8d585434", "metadata": {}, "outputs": [], "source": [] } ], "metadata": { "jupytext": { "split_at_heading": true }, "kernelspec": { "display_name": "python3", "language": "python", "name": "python3" } }, "nbformat": 4, "nbformat_minor": 5 }