{ "cells": [ { "cell_type": "code", "execution_count": null, "metadata": {}, "outputs": [], "source": [ "#hide\n", "#skip\n", "! [ -e /content ] && pip install -Uqq fastai # upgrade fastai on colab" ] }, { "cell_type": "code", "execution_count": null, "metadata": {}, "outputs": [], "source": [ "#default_exp vision.core\n", "#default_cls_lvl 3" ] }, { "cell_type": "code", "execution_count": null, "metadata": {}, "outputs": [], "source": [ "#export\n", "from fastai.torch_basics import *\n", "from fastai.data.all import *\n", "\n", "from PIL import Image" ] }, { "cell_type": "code", "execution_count": null, "metadata": {}, "outputs": [], "source": [ "from fastai.data.external import *" ] }, { "cell_type": "code", "execution_count": null, "metadata": {}, "outputs": [], "source": [ "#hide\n", "from nbdev.showdoc import *" ] }, { "cell_type": "code", "execution_count": null, "metadata": {}, "outputs": [], "source": [ "#export\n", "_all_ = ['Image','ToTensor']" ] }, { "cell_type": "code", "execution_count": null, "metadata": {}, "outputs": [], "source": [ "#It didn't use to be necessary to add ToTensor in all but we don't have the encodes methods defined here otherwise.\n", "#TODO: investigate" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "# Core vision\n", "> Basic image opening/processing functionality" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "## Helpers" ] }, { "cell_type": "code", "execution_count": null, "metadata": {}, "outputs": [], "source": [ "#export\n", "imagenet_stats = ([0.485, 0.456, 0.406], [0.229, 0.224, 0.225])\n", "cifar_stats = ([0.491, 0.482, 0.447], [0.247, 0.243, 0.261])\n", "mnist_stats = ([0.131], [0.308])" ] }, { "cell_type": "code", "execution_count": null, "metadata": {}, "outputs": [], "source": [ "im = Image.open(TEST_IMAGE).resize((30,20))" ] }, { "cell_type": "code", "execution_count": null, "metadata": {}, "outputs": [], "source": [ "#export\n", "if not hasattr(Image,'_patched'):\n", " _old_sz = Image.Image.size.fget\n", " @patch(as_prop=True)\n", " def size(x:Image.Image): return fastuple(_old_sz(x))\n", " Image._patched = True" ] }, { "cell_type": "code", "execution_count": null, "metadata": {}, "outputs": [], "source": [ "#export\n", "@patch(as_prop=True)\n", "def n_px(x: Image.Image): return x.size[0] * x.size[1]" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "#### `Image.n_px`" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "> `Image.n_px` (property)\n", "\n", "Number of pixels in image" ] }, { "cell_type": "code", "execution_count": null, "metadata": {}, "outputs": [], "source": [ "test_eq(im.n_px, 30*20)" ] }, { "cell_type": "code", "execution_count": null, "metadata": {}, "outputs": [], "source": [ "#export\n", "@patch(as_prop=True)\n", "def shape(x: Image.Image): return x.size[1],x.size[0]" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "#### `Image.shape`\n", "\n", "> `Image.shape` (property)\n", "\n", "Image (height,width) tuple (NB: opposite order of `Image.size()`, same order as numpy array and pytorch tensor)" ] }, { "cell_type": "code", "execution_count": null, "metadata": {}, "outputs": [], "source": [ "test_eq(im.shape, (20,30))" ] }, { "cell_type": "code", "execution_count": null, "metadata": {}, "outputs": [], "source": [ "#export\n", "@patch(as_prop=True)\n", "def aspect(x: Image.Image): return x.size[0]/x.size[1]" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "#### `Image.aspect`\n", "\n", "> `Image.aspect` (property)\n", "\n", "Aspect ratio of the image, i.e. `width/height`" ] }, { "cell_type": "code", "execution_count": null, "metadata": {}, "outputs": [], "source": [ "test_eq(im.aspect, 30/20)" ] }, { "cell_type": "code", "execution_count": null, "metadata": {}, "outputs": [], "source": [ "#export\n", "@patch\n", "def reshape(x: Image.Image, h, w, resample=0):\n", " \"`resize` `x` to `(w,h)`\"\n", " return x.resize((w,h), resample=resample)" ] }, { "cell_type": "code", "execution_count": null, "metadata": {}, "outputs": [ { "data": { "text/markdown": [ "
Image.reshape
[source]Image.reshape
(**`x`**:`Image`, **`h`**, **`w`**, **`resample`**=*`0`*)\n",
"\n",
"`resize` `x` to `(w,h)`"
],
"text/plain": [
"Image.to_bytes_format
[source]Image.to_bytes_format
(**`im`**:`Image`, **`format`**=*`'png'`*)\n",
"\n",
"Convert to bytes, default to PNG format"
],
"text/plain": [
"Image.to_thumb
[source]Image.to_thumb
(**`h`**, **`w`**=*`None`*)\n",
"\n",
"Same as `thumbnail`, but uses a copy"
],
"text/plain": [
"Image.resize_max
[source]Image.resize_max
(**`x`**:`Image`, **`resample`**=*`0`*, **`max_px`**=*`None`*, **`max_h`**=*`None`*, **`max_w`**=*`None`*)\n",
"\n",
"`resize` `x` to `max_px`, or `max_h`, or `max_w`"
],
"text/plain": [
"TensorPoint
object and embeds it in them. For this to work, those images need to be before any points in the order of your final tuple. If you don't have such images, you need to embed the size of the corresponding image when creating a TensorPoint
by passing it with `sz=...`."
]
},
{
"cell_type": "code",
"execution_count": null,
"metadata": {},
"outputs": [],
"source": [
"def _pnt_lbl(x): return TensorPoint.create(pnts)\n",
"def _pnt_open(fn): return PILImage(PILImage.create(fn).resize((28,35)))\n",
"pnt_tds = Datasets([mnist_fn], [_pnt_open, [_pnt_lbl]])\n",
"pnt_tdl = TfmdDL(pnt_tds, bs=1, after_item=[PointScaler(), ToTensor()])"
]
},
{
"cell_type": "code",
"execution_count": null,
"metadata": {},
"outputs": [],
"source": [
"test_eq(pnt_tdl.after_item.c, 10)"
]
},
{
"cell_type": "code",
"execution_count": null,
"metadata": {},
"outputs": [],
"source": [
"#hide\n",
"#Check the size was grabbed by PointScaler and added to y\n",
"tfm = PointScaler()\n",
"tfm.as_item=False\n",
"x,y = tfm(pnt_tds[0])\n",
"test_eq(tfm.sz, x.size)\n",
"test_eq(y.img_size, x.size)"
]
},
{
"cell_type": "code",
"execution_count": null,
"metadata": {},
"outputs": [],
"source": [
"x,y = pnt_tdl.one_batch()\n",
"#Scaling and flipping properly done\n",
"#NB: we added a point earlier at (9,17); formula below scales to (-1,1) coords\n",
"test_close(y[0], tensor([[-1., -1.], [-1., 1.], [1., -1.], [1., 1.], [9/14-1, 17/17.5-1]]))\n",
"a,b = pnt_tdl.decode_batch((x,y))[0]\n",
"test_eq(b, tensor(pnts).float())\n",
"#Check types\n",
"test_eq(type(x), TensorImage)\n",
"test_eq(type(y), TensorPoint)\n",
"test_eq(type(a), TensorImage)\n",
"test_eq(type(b), TensorPoint)\n",
"test_eq(b.img_size, (28,35)) #Automatically picked the size of the input"
]
},
{
"cell_type": "code",
"execution_count": null,
"metadata": {},
"outputs": [
{
"data": {
"image/png": "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\n",
"text/plain": [
"