diff --git a/interactive_viewer.ipynb b/interactive_viewer.ipynb index c5d997c..7cd3bc4 100644 --- a/interactive_viewer.ipynb +++ b/interactive_viewer.ipynb @@ -1,310 +1,225 @@ { - "cells": [ - { - "cell_type": "markdown", - "metadata": { - "colab_type": "text", - "id": "view-in-github" - }, - "source": [ - "\"Open" - ] - }, - { - "cell_type": "code", - "execution_count": 1, - "metadata": { - "colab": {}, - "colab_type": "code", - "id": "iuV1r5o4WBcc" - }, - "outputs": [ - { - "data": { - "application/vnd.jupyter.widget-view+json": { - "model_id": "36893a71327842069312e51a1612a0bf", - "version_major": 2, - "version_minor": 0 - }, - "text/plain": [ - "FileUpload(value={}, description='Upload')" - ] - }, - "metadata": {}, - "output_type": "display_data" + "nbformat": 4, + "nbformat_minor": 0, + "metadata": { + "colab": { + "name": "interactive_viewer.ipynb", + "provenance": [], + "collapsed_sections": [], + "include_colab_link": true + }, + "kernelspec": { + "display_name": "Python 3", + "name": "python3" } - ], - "source": [ - "##### Copyright 2020 Google LLC. All Rights Reserved.\n", - "\n", - "# Licensed under the Apache License, Version 2.0 (the \"License\");\n", - "# you may not use this file except in compliance with the License.\n", - "# You may obtain a copy of the License at\n", - "#\n", - "# https://www.apache.org/licenses/LICENSE-2.0\n", - "#\n", - "# Unless required by applicable law or agreed to in writing, software\n", - "# distributed under the License is distributed on an \"AS IS\" BASIS,\n", - "# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n", - "# See the License for the specific language governing permissions and\n", - "# limitations under the License.\n", - "\n", - "# Load an image\n", - "import ipywidgets as widgets\n", - "from IPython.display import display\n", - "uploader = widgets.FileUpload(multiple=False)\n", - "display(uploader)" - ] }, - { - "cell_type": "code", - "execution_count": 8, - "metadata": { - "cellView": "form", - "colab": {}, - "colab_type": "code", - "id": "cXfefXlKOYQl" - }, - "outputs": [ + "cells": [ { - "data": { - "application/vnd.jupyter.widget-view+json": { - "model_id": "bcf3aaa9ad2046609f7e08e4bdf3d766", - "version_major": 2, - "version_minor": 0 + "cell_type": "markdown", + "metadata": { + "id": "view-in-github", + "colab_type": "text" }, - "text/plain": [ - "Button(description='Default', style=ButtonStyle())" + "source": [ + "\"Open" ] - }, - "metadata": {}, - "output_type": "display_data" }, { - "data": { - "application/vnd.jupyter.widget-view+json": { - "model_id": "79e8241b797340238431e34063f386af", - "version_major": 2, - "version_minor": 0 + "cell_type": "code", + "metadata": { + "colab_type": "code", + "id": "iuV1r5o4WBcc", + "colab": {} }, - "text/plain": [ - "HBox(children=(Button(description=\"Otsu's Method\", style=ButtonStyle()), Button(description='MET', style=Butto…" - ] - }, - "metadata": {}, - "output_type": "display_data" + "source": [ + "##### Copyright 2020 Google LLC. All Rights Reserved.\n", + "\n", + "# Licensed under the Apache License, Version 2.0 (the \"License\");\n", + "# you may not use this file except in compliance with the License.\n", + "# You may obtain a copy of the License at\n", + "#\n", + "# https://www.apache.org/licenses/LICENSE-2.0\n", + "#\n", + "# Unless required by applicable law or agreed to in writing, software\n", + "# distributed under the License is distributed on an \"AS IS\" BASIS,\n", + "# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n", + "# See the License for the specific language governing permissions and\n", + "# limitations under the License.\n", + "\n", + "# Load an image\n", + "import ipywidgets as widgets\n", + "from IPython.display import display\n", + "uploader = widgets.FileUpload(multiple=False)\n", + "display(uploader)" + ], + "execution_count": null, + "outputs": [] }, { - "data": { - "application/vnd.jupyter.widget-view+json": { - "model_id": "b1319d8b388b4fdd857a4fa95e4dd214", - "version_major": 2, - "version_minor": 0 + "cell_type": "code", + "metadata": { + "id": "cXfefXlKOYQl", + "colab_type": "code", + "colab": {}, + "cellView": "form" }, - "text/plain": [ - "Output()" - ] - }, - "metadata": {}, - "output_type": "display_data" - }, - { - "data": { - "application/vnd.jupyter.widget-view+json": { - "model_id": "b0a9eb2938804cc598954f82684aa39e", - "version_major": 2, - "version_minor": 0 - }, - "text/plain": [ - "interactive(children=(FloatLogSlider(value=961851.0, description='nu', max=30.0, min=-30.0, step=1.0), FloatLo…" - ] - }, - "metadata": {}, - "output_type": "display_data" + "source": [ + "#@title An interactive viewer for tuning hyperparameters\n", + "\n", + "# Load the image\n", + "from PIL import Image\n", + "import io\n", + "import numpy as np\n", + "\n", + "image_data = list(uploader.value.values())[0]['content']\n", + "im = np.array(Image.open(io.BytesIO(image_data)))\n", + "\n", + "# A fast numpy reference implementation of GHT, as per\n", + "# \"A Generalization of Otsu's Method and Minimum Error Thresholding\"\n", + "# Jonathan T. Barron, ECCV, 2020\n", + "\n", + "csum = lambda z: np.cumsum(z)[:-1]\n", + "dsum = lambda z: np.cumsum(z[::-1])[-2::-1]\n", + "argmax = lambda x, f: np.mean(x[:-1][f == np.max(f)]) # Use the mean for ties.\n", + "clip = lambda z: np.maximum(1e-30, z)\n", + "\n", + "def preliminaries(n, x):\n", + " \"\"\"Some math that is shared across multiple algorithms.\"\"\"\n", + " assert np.all(n >= 0)\n", + " x = np.arange(len(n), dtype=n.dtype) if x is None else x\n", + " assert np.all(x[1:] >= x[:-1])\n", + " w0 = clip(csum(n))\n", + " w1 = clip(dsum(n))\n", + " p0 = w0 / (w0 + w1)\n", + " p1 = w1 / (w0 + w1)\n", + " mu0 = csum(n * x) / w0\n", + " mu1 = dsum(n * x) / w1\n", + " d0 = csum(n * x**2) - w0 * mu0**2\n", + " d1 = dsum(n * x**2) - w1 * mu1**2\n", + " return x, w0, w1, p0, p1, mu0, mu1, d0, d1\n", + "\n", + "def GHT(n, x=None, nu=0, tau=0, kappa=0, omega=0.5, prelim=None):\n", + " assert nu >= 0\n", + " assert tau >= 0\n", + " assert kappa >= 0\n", + " assert omega >= 0 and omega <= 1\n", + " x, w0, w1, p0, p1, _, _, d0, d1 = prelim or preliminaries(n, x)\n", + " v0 = clip((p0 * nu * tau**2 + d0) / (p0 * nu + w0))\n", + " v1 = clip((p1 * nu * tau**2 + d1) / (p1 * nu + w1))\n", + " f0 = -d0 / v0 - w0 * np.log(v0) + 2 * (w0 + kappa * omega) * np.log(w0)\n", + " f1 = -d1 / v1 - w1 * np.log(v1) + 2 * (w1 + kappa * (1 - omega)) * np.log(w1)\n", + " return argmax(x, f0 + f1), f0 + f1\n", + "\n", + "def im2hist(im, zero_extents=False):\n", + " # Convert an image to grayscale, bin it, and optionally zero out the first and last bins.\n", + " max_val = np.iinfo(im.dtype).max\n", + " x = np.arange(max_val+1)\n", + " e = np.arange(-0.5, max_val+1.5)\n", + " assert len(im.shape) in [2, 3]\n", + " im_bw = np.amax(im[...,:3], -1) if len(im.shape) == 3 else im\n", + " n = np.histogram(im_bw, e)[0]\n", + " if zero_extents:\n", + " n[0] = 0\n", + " n[-1] = 0\n", + " return n, x, im_bw\n", + "\n", + "# Precompute a histogram and some integrals.\n", + "n, x, im_bw = im2hist(im)\n", + "prelim = preliminaries(n, x)\n", + "\n", + "import matplotlib.pyplot as plt\n", + "\n", + "default_nu = np.sum(n)\n", + "default_tau = np.sqrt(1/12)\n", + "default_kappa = np.sum(n)\n", + "default_omega = 0.5\n", + "\n", + "_nu = default_nu\n", + "_tau = default_tau\n", + "_kappa = default_kappa\n", + "_omega = default_omega\n", + "\n", + "continuous_update = True\n", + "nu_slider = widgets.FloatLogSlider(min=-30, max=30, value=_nu, continuous_update=continuous_update)\n", + "tau_slider = widgets.FloatLogSlider(min=-30, max=30, value=_tau, continuous_update=continuous_update)\n", + "kappa_slider = widgets.FloatLogSlider(min=-30, max=30, value=_kappa, continuous_update=continuous_update)\n", + "omega_slider = widgets.FloatSlider(min=0, max=1, value=_omega, step=0.01, continuous_update=continuous_update)\n", + "\n", + "def render():\n", + " global _nu, _tau, _kappa, _omega\n", + " t, score = GHT(n, x, _nu, _tau, _kappa, _omega, prelim)\n", + "\n", + " plt.figure(0, figsize=(16,5))\n", + " plt.subplot(1,3,1)\n", + " plt.imshow(im, cmap='gray')\n", + " plt.axis('off')\n", + "\n", + " plt.subplot(1,3,2)\n", + " plt.imshow(im_bw > t, cmap='gray', vmin=0, vmax=1)\n", + " plt.gca().set_xticks([])\n", + " plt.gca().set_yticks([])\n", + "\n", + " plt.subplot(1,3,3)\n", + " normalize = lambda x : (x - np.min(score)) * np.max(n) / (np.max(score) - np.min(score))\n", + " plt.plot((x[:-1] + x[1:])/2, normalize(score))\n", + " plt.scatter(t, normalize(score[int(t)]))\n", + " plt.bar(x, n, width=1)\n", + " plt.gca().set_yticks([]);\n", + "\n", + "def update(nu=None, tau=None, kappa=None, omega=None):\n", + " global _nu, _tau, _kappa, _omega\n", + " _nu = nu or _nu\n", + " _tau = tau or _tau\n", + " _kappa = kappa or _kappa\n", + " _omega = omega or _omega\n", + "\n", + "def reset(nu=None, tau=None, kappa=None, omega=None):\n", + " global nu_slider, tau_slider, kappa_slider, omega_slider\n", + " if nu:\n", + " nu_slider.value = nu\n", + " if tau:\n", + " tau_slider.value = tau\n", + " if kappa:\n", + " kappa_slider.value = kappa\n", + " if omega:\n", + " omega_slider.value = omega\n", + "\n", + "def update_and_render(nu=None, tau=None, kappa=None, omega=None):\n", + " update(nu, tau, kappa, omega)\n", + " render()\n", + "\n", + "\n", + "default_button = widgets.Button(description=\"Default\")\n", + "def default_fun(b):\n", + " reset(nu=default_nu, tau=default_tau, kappa=default_kappa, omega=default_omega)\n", + "default_button.on_click(default_fun)\n", + "\n", + "otsu_button = widgets.Button(description=\"Otsu's Method\")\n", + "def otsu_fun(b):\n", + " reset(nu=1e30, tau=1e-30, kappa=1e-30)\n", + "otsu_button.on_click(otsu_fun)\n", + "\n", + "met_button = widgets.Button(description=\"MET\")\n", + "def met_fun(b):\n", + " reset(nu=1e-30, kappa=1e-30,)\n", + "met_button.on_click(met_fun)\n", + "\n", + "percentile_button = widgets.Button(description=\"Percentile\")\n", + "def percentile_fun(b):\n", + " reset(nu=1e-30, kappa=1e30)\n", + "percentile_button.on_click(percentile_fun)\n", + "\n", + "display(default_button)\n", + "display(widgets.HBox([otsu_button, met_button, percentile_button]), widgets.Output())\n", + "\n", + "widgets.interact(update_and_render,\n", + " nu=nu_slider,\n", + " tau=tau_slider,\n", + " kappa=kappa_slider,\n", + " omega=omega_slider);\n" + ], + "execution_count": null, + "outputs": [] } - ], - "source": [ - "#@title An interactive viewer for tuning hyperparameters\n", - "\n", - "# Load the image\n", - "from PIL import Image\n", - "import io\n", - "import numpy as np\n", - "\n", - "image_data = list(uploader.value.values())[0]['content']\n", - "im = np.array(Image.open(io.BytesIO(image_data)))\n", - "\n", - "# A fast numpy reference implementation of GHT, as per\n", - "# \"A Generalization of Otsu's Method and Minimum Error Thresholding\"\n", - "# Jonathan T. Barron, ECCV, 2020\n", - "\n", - "csum = lambda z: np.cumsum(z)[:-1]\n", - "dsum = lambda z: np.cumsum(z[::-1])[-2::-1]\n", - "argmax = lambda x, f: np.mean(x[:-1][f == np.max(f)]) # Use the mean for ties.\n", - "clip = lambda z: np.maximum(1e-30, z)\n", - "\n", - "def preliminaries(n, x):\n", - " \"\"\"Some math that is shared across multiple algorithms.\"\"\"\n", - " assert np.all(n >= 0)\n", - " x = np.arange(len(n), dtype=n.dtype) if x is None else x\n", - " assert np.all(x[1:] >= x[:-1])\n", - " w0 = clip(csum(n))\n", - " w1 = clip(dsum(n))\n", - " p0 = w0 / (w0 + w1)\n", - " p1 = w1 / (w0 + w1)\n", - " mu0 = csum(n * x) / w0\n", - " mu1 = dsum(n * x) / w1\n", - " d0 = csum(n * x**2) - w0 * mu0**2\n", - " d1 = dsum(n * x**2) - w1 * mu1**2\n", - " return x, w0, w1, p0, p1, mu0, mu1, d0, d1\n", - "\n", - "def GHT(n, x=None, nu=0, tau=0, kappa=0, omega=0.5, prelim=None):\n", - " assert nu >= 0\n", - " assert tau >= 0\n", - " assert kappa >= 0\n", - " assert omega >= 0 and omega <= 1\n", - " x, w0, w1, p0, p1, _, _, d0, d1 = prelim or preliminaries(n, x)\n", - " v0 = clip((p0 * nu * tau**2 + d0) / (p0 * nu + w0))\n", - " v1 = clip((p1 * nu * tau**2 + d1) / (p1 * nu + w1))\n", - " f0 = -d0 / v0 - w0 * np.log(v0) + 2 * (w0 + kappa * omega) * np.log(w0)\n", - " f1 = -d1 / v1 - w1 * np.log(v1) + 2 * (w1 + kappa * (1 - omega)) * np.log(w1)\n", - " return argmax(x, f0 + f1), f0 + f1\n", - "\n", - "def im2hist(im, zero_extents=False):\n", - " # Convert an image to grayscale, bin it, and optionally zero out the first and last bins.\n", - " max_val = np.iinfo(im.dtype).max\n", - " x = np.arange(max_val+1)\n", - " e = np.arange(-0.5, max_val+1.5)\n", - " assert len(im.shape) in [2, 3]\n", - " im_bw = np.amax(im[...,:3], -1) if len(im.shape) == 3 else im\n", - " n = np.histogram(im_bw, e)[0]\n", - " if zero_extents:\n", - " n[0] = 0\n", - " n[-1] = 0\n", - " return n, x, im_bw\n", - "\n", - "# Precompute a histogram and some integrals.\n", - "n, x, im_bw = im2hist(im, True)\n", - "prelim = preliminaries(n, x)\n", - "\n", - "import matplotlib.pyplot as plt\n", - "\n", - "default_nu = np.sum(n)\n", - "default_tau = np.sqrt(1/12)\n", - "default_kappa = np.sum(n)\n", - "default_omega = 0.5\n", - "\n", - "_nu = default_nu\n", - "_tau = default_tau\n", - "_kappa = default_kappa\n", - "_omega = default_omega\n", - "\n", - "continuous_update = True\n", - "nu_slider = widgets.FloatLogSlider(min=-30, max=30, value=_nu, step=1., continuous_update=continuous_update)\n", - "tau_slider = widgets.FloatLogSlider(min=-30, max=30, value=_tau, step=1., continuous_update=continuous_update)\n", - "kappa_slider = widgets.FloatLogSlider(min=-30, max=30, value=_kappa, step=1., continuous_update=continuous_update)\n", - "omega_slider = widgets.FloatSlider(min=0, max=1, value=_omega, step=0.01, continuous_update=continuous_update)\n", - "\n", - "def render():\n", - " global _nu, _tau, _kappa, _omega\n", - " t, score = GHT(n, x, _nu, _tau, _kappa, _omega, prelim)\n", - "\n", - " plt.figure(0, figsize=(16,5))\n", - " plt.subplot(1,3,1)\n", - " plt.imshow(im, cmap='gray')\n", - " plt.axis('off')\n", - "\n", - " plt.subplot(1,3,2)\n", - " plt.imshow(im_bw > t, cmap='gray', vmin=0, vmax=1)\n", - " plt.gca().set_xticks([])\n", - " plt.gca().set_yticks([])\n", - "\n", - " plt.subplot(1,3,3)\n", - " normalize = lambda x : (x - np.min(score)) * np.max(n) / (np.max(score) - np.min(score))\n", - " plt.plot((x[:-1] + x[1:])/2, normalize(score))\n", - " plt.scatter(t, normalize(score[int(t)]))\n", - " plt.bar(x, n, width=1)\n", - " plt.gca().set_yticks([]);\n", - "\n", - "def update(nu=None, tau=None, kappa=None, omega=None):\n", - " global _nu, _tau, _kappa, _omega\n", - " _nu = nu or _nu\n", - " _tau = tau or _tau\n", - " _kappa = kappa or _kappa\n", - " _omega = omega or _omega\n", - "\n", - "def reset(nu=None, tau=None, kappa=None, omega=None):\n", - " global nu_slider, tau_slider, kappa_slider, omega_slider\n", - " if nu:\n", - " nu_slider.value = nu\n", - " if tau:\n", - " tau_slider.value = tau\n", - " if kappa:\n", - " kappa_slider.value = kappa\n", - " if omega:\n", - " omega_slider.value = omega\n", - "\n", - "def update_and_render(nu=None, tau=None, kappa=None, omega=None):\n", - " update(nu, tau, kappa, omega)\n", - " render()\n", - "\n", - "\n", - "default_button = widgets.Button(description=\"Default\")\n", - "def default_fun(b):\n", - " reset(nu=default_nu, tau=default_tau, kappa=default_kappa, omega=default_omega)\n", - "default_button.on_click(default_fun)\n", - "\n", - "otsu_button = widgets.Button(description=\"Otsu's Method\")\n", - "def otsu_fun(b):\n", - " reset(nu=1e30, tau=1e-30, kappa=1e-30)\n", - "otsu_button.on_click(otsu_fun)\n", - "\n", - "met_button = widgets.Button(description=\"MET\")\n", - "def met_fun(b):\n", - " reset(nu=1e-30, kappa=1e-30,)\n", - "met_button.on_click(met_fun)\n", - "\n", - "percentile_button = widgets.Button(description=\"Percentile\")\n", - "def percentile_fun(b):\n", - " reset(nu=1e-30, kappa=1e30)\n", - "percentile_button.on_click(percentile_fun)\n", - "\n", - "display(default_button)\n", - "display(widgets.HBox([otsu_button, met_button, percentile_button]), widgets.Output())\n", - "\n", - "widgets.interact(update_and_render,\n", - " nu=nu_slider,\n", - " tau=tau_slider,\n", - " kappa=kappa_slider,\n", - " omega=omega_slider);\n" - ] - } - ], - "metadata": { - "colab": { - "collapsed_sections": [], - "include_colab_link": true, - "name": "interactive_viewer.ipynb", - "provenance": [] - }, - "kernelspec": { - "display_name": "Python 3", - "language": "python", - "name": "python3" - }, - "language_info": { - "codemirror_mode": { - "name": "ipython", - "version": 3 - }, - "file_extension": ".py", - "mimetype": "text/x-python", - "name": "python", - "nbconvert_exporter": "python", - "pygments_lexer": "ipython3", - "version": "3.6.7" - } - }, - "nbformat": 4, - "nbformat_minor": 4 -} + ] +} \ No newline at end of file