{ "cells": [ { "cell_type": "markdown", "metadata": {}, "source": [ "# An Identiconizer generator implementation in Python\n", "\n", "This small notebook implements a generator of small square icons like the ones in GitHub, as [implemented by identicon.js](https://github.com/stewartlord/identicon.js#identiconjs)." ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "## The function" ] }, { "cell_type": "code", "execution_count": 5, "metadata": {}, "outputs": [], "source": [ "import random\n", "import numpy as np\n", "from matplotlib.colors import hsv_to_rgb\n", "from PIL import Image as PILImage, ImageDraw as PILImageDraw" ] }, { "cell_type": "code", "execution_count": 6, "metadata": {}, "outputs": [ { "name": "stdout", "output_type": "stream", "text": [ "The watermark extension is already loaded. To reload it, use:\n", " %reload_ext watermark\n", "2017-11-26T12:04:33+01:00\n", "\n", "CPython 3.6.3\n", "IPython 6.2.1\n", "\n", "compiler : GCC 7.2.0\n", "system : Linux\n", "release : 4.13.0-17-generic\n", "machine : x86_64\n", "processor : x86_64\n", "CPU cores : 4\n", "interpreter: 64bit\n", "Lilian Besson (Naereen) \n", "\n", "numpy 1.13.3\n", "matplotlib 2.1.0\n", "PIL 4.3.0\n" ] } ], "source": [ "%load_ext watermark\n", "%watermark\n", "%watermark -a \"Lilian Besson (Naereen)\" -p numpy,matplotlib,PIL" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "## First try" ] }, { "cell_type": "code", "execution_count": 8, "metadata": {}, "outputs": [], "source": [ "def identicon(hashval=None,\n", " size=256,\n", " margin=0.11,\n", " foreground=(255,0,0,255),\n", " background=(240,240,240,255),\n", " saturation=0.7,\n", " brightness=0.5,\n", " xsym=True,\n", " ):\n", " if hashval is not None:\n", " if isinstance(hashval, str):\n", " hashval = abs(hash(hashval))\n", " random.seed(int(hashval))\n", "\n", " hue = random.random()\n", " foreground = tuple(np.array(hsv_to_rgb([hue, saturation, brightness]) * 256, dtype=int)) \n", "\n", " border = int(size * margin)\n", " ampl = size - 2 * border\n", " def cropfirst(x):\n", " return max(border, min(border + x, size - border))\n", " def cropsecond(x):\n", " return max(border, min(size - border - x, size - border))\n", "\n", " # make a blank image\n", " im = PILImage.new('RGBA', (size,size), background) \n", " # get a drawing context\n", " d = PILImageDraw.Draw(im)\n", "\n", " def rect(xy):\n", " x1, y1, x2, y2 = xy\n", " r1 = (cropfirst(x1), cropfirst(y1), cropfirst(x2), cropfirst(y2))\n", " if xsym:\n", " r2 = (cropsecond(x1), cropfirst(y1), cropsecond(x2), cropfirst(y2))\n", " else:\n", " r2 = (cropfirst(x1), cropsecond(y1), cropfirst(x2), cropsecond(y2))\n", " d.rectangle(r1, fill=foreground)\n", " d.rectangle(r2, fill=foreground)\n", "\n", " nbrectangle = random.randint(4, 8)\n", " for i in range(nbrectangle):\n", " x1, y1 = [random.randint(0, ampl) for _ in range(2)]\n", " dx, dy = [random.randint(ampl // 8, ampl // 2) for _ in range(2)]\n", " dirx, diry = [random.choice([+1, -1]) for _ in range(2)]\n", " rect((x1, y1, x1 + dirx * dx, y1 + diry * dy))\n", " return im" ] }, { "cell_type": "code", "execution_count": 9, "metadata": { "scrolled": true }, "outputs": [], "source": [ "im = identicon()" ] }, { "cell_type": "code", "execution_count": 10, "metadata": { "scrolled": true }, "outputs": [ { "data": { "image/png": "iVBORw0KGgoAAAANSUhEUgAAAQAAAAEACAYAAABccqhmAAADpklEQVR4nO3dwW3CQBBA0ThyTfTE\njVK40RPVbAfOJadIiSAJLPDfuyPGEvoaG8m7jDG2NyDpffYAwDwCAGECAGECAGECAGECAGECAGEC\nAGECAGECAGECAGECAGECAGECAGECAGECAGECAGECAGECAGECAGECAGECAGECAGECAGECAGECAGEC\nAGECAGECAGECAGECAGHr7AGe2W5/nD0Cn86nw+wRnpINAMIEAMIEAMIEAMIEAMIEAMIEAMIEAMIE\nAMIEAMIEAMIEAMIEAMIEAMIEAMIEAMIEAMIEAMIEAMIEAMIEAMIEAMIEAMIEAMIEAMIEAMIEAMIE\nAMIEAMIEAMIEAMIEAMIEAMIEAMIEAMIEAMIEAMIEAMIEAMKWMcY2e4hnsNsfZ4/Alc6nw+wRHp4N\nAMIEAMIEAMIEAMIEAMIEAMIEAMIEAMIEAMIEAMIEAMIEAMIEAMIEAMIEAMIEAMIEAMIEAMIEAMIE\nAMIEAMIEAMIEAMIEAMIEAMIEAMIe6mgwx29R8SjHltkAIEwAIEwAIEwAIEwAIEwAIEwAIEwAIEwA\nIEwAIEwAIEwAIEwAIEwAIEwAIEwAIEwAIEwAIEwAIEwAIGydPQC3cYuXTnpp6+uxAUCYAECYW4AX\nZV3nEjYACBMACBMACBMACBMACBMACBMACBMACBMACBMACBMACBMACBMACBMACBMACBMACBMACBMA\nCBMACBMACBMACBMACBMACBMACBMACBMACFvGGNs9v9CRVfCzW5zs/B0bAIQJAIQJAIS95PHgl9xD\neRbBV8XfjQ0AwgQAwgQAwgQAwgQAwgQAwgQAwgQAwgQAwgQAwgQAwgQAwgQAwgQAwgQAwgQAwgQA\nwgQAwgQAwgQAwgQAwgQAwgQAwgQAwgQAwgQAwgQAwgQAwgQAwgQAwgQAwgQAwgQAwgQAwgQAwgQA\nwgQAwgQAwgQAwgQAwgQAwgQAwgQAwgQAwgQAwgQAwgQAwgQAwgQAwgQAwgQAwgQAwgQAwtbZA8xy\nPh1+/dnd/vhvc9zTNddcuEZsAJAmABAmABAmABAmABCW/RfgLwpPmgvXiA0A0gQAwgQAwgQAwgQA\nwgQAwgQAwgQAwgQAwgQAwgQAwgQAwgQAwgQAwpYxxjZ7CGAOGwCECQCECQCECQCECQCECQCECQCE\nCQCECQCECQCECQCECQCECQCECQCECQCECQCECQCECQCECQCECQCECQCECQCECQCECQCECQCECQCE\nfQCc0Ta1wHXexQAAAABJRU5ErkJggg==\n", "text/plain": [ "" ] }, "execution_count": 10, "metadata": {}, "output_type": "execute_result" } ], "source": [ "im" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "## Second try" ] }, { "cell_type": "code", "execution_count": 11, "metadata": {}, "outputs": [], "source": [ "from itertools import product" ] }, { "cell_type": "code", "execution_count": 12, "metadata": {}, "outputs": [], "source": [ "def identicon2(hashval=None,\n", " size=256,\n", " nbsquares=5,\n", " margin=0.09,\n", " foreground=(255,0,0,255),\n", " background=(240,240,240,255),\n", " saturation=0.7,\n", " brightness=0.5,\n", " xsym=True,\n", " ):\n", " if hashval is not None:\n", " if isinstance(hashval, str):\n", " hashval = abs(hash(hashval))\n", " random.seed(int(hashval) % 1<<28)\n", " np.random.seed(int(hashval) % 1<<28)\n", "\n", " hue = random.random()\n", " foreground = tuple(np.array(hsv_to_rgb([hue, saturation, brightness]) * 256, dtype=int)) \n", "\n", " border = int(size * margin)\n", " ampl = size - 2 * border\n", " size_square = ampl // nbsquares \n", "\n", " def cropfirst(x):\n", " return max(border, min(border + x, size - border))\n", " def cropsecond(x):\n", " return max(border, min(size - border - x, size - border))\n", "\n", " # make a blank image\n", " im = PILImage.new('RGBA', (size,size), background) \n", " # get a drawing context\n", " d = PILImageDraw.Draw(im)\n", "\n", " def rect(xy, xsym=xsym):\n", " x1, y1, x2, y2 = xy\n", " r1 = (cropfirst(x1), cropfirst(y1), cropfirst(x2), cropfirst(y2))\n", " if xsym:\n", " r2 = (cropsecond(x1), cropfirst(y1), cropsecond(x2), cropfirst(y2))\n", " else:\n", " r2 = (cropfirst(x1), cropsecond(y1), cropfirst(x2), cropsecond(y2))\n", " d.rectangle(r1, fill=foreground)\n", " d.rectangle(r2, fill=foreground)\n", "\n", " nbrectangle = random.randint(5, nbsquares * (nbsquares // 2 + 1) - 2)\n", " possible_coordinates = [(x, y) for (x, y) in product(range(nbsquares), range(nbsquares // 2 + 1))]\n", " for i in np.random.choice(len(possible_coordinates), size=nbrectangle, replace=False):\n", " y, x = possible_coordinates[i]\n", " rect((x * size_square, y * size_square, (x+1) * size_square, (y+1) * size_square))\n", " return im" ] }, { "cell_type": "code", "execution_count": 13, "metadata": { "scrolled": true }, "outputs": [], "source": [ "im = identicon2()" ] }, { "cell_type": "code", "execution_count": 14, "metadata": {}, "outputs": [ { "data": { "image/png": "iVBORw0KGgoAAAANSUhEUgAAAQAAAAEACAYAAABccqhmAAADhUlEQVR4nO3XUW3EMBBAwaYKphAJ\nkYNyRI5I0JhBCqH9qOVe3wyA1cqynuxtjHF/AEmfqxcA1hEACBMACBMACBMACBMACBMACBMACBMA\nCBMACBMACBMACBMACBMACBMACBMACBMACBMACBMACBMACBMACBMACNtnDn+ex8zxkPF4XVPmegFA\nmABAmABAmABAmABAmABAmABAmABAmABAmABAmABAmABAmABAmABAmABAmABAmABAmABAmABAmABA\nmABAmABAmABAmABAmABAmABAmABAmABAmABAmABAmABAmABAmABAmABAmABAmABAmABAmABAmABA\nmABAmABAmABAmABAmABAmABAmABAmABAmABAmABAmABAmABAmABAmABAmABAmABAmABAmABAmABA\nmABAmABAmABAmABAmABAmABAmABAmABAmABAmABAmABAmABAmABAmABAmABAmABAmABAmABAmABA\nmABAmABAmABAmABAmABAmABAmABA2L56gb/i8bpWr/Ct53msXuHHnOd78AKAMAGAMAGAMAGAMAGA\nMAGAMAGAMAGAMAGAMAGAMAGAMAGAMAGAMAGAMAGAMAGAMAGAMAGAMAGAMAGAMAGAMAGAMAGAMAGA\nMAGAMAGAMAGAMAGAMAGAMAGAMAGAMAGAMAGAMAGAMAGAMAGAMAGAMAGAMAGAMAGAsH31An/F8zxW\nr/CvOM/3sI0x7tVLrOaydj1e1+oVlvIFgDABgDABgDABgDABgDABgDABgDABgDABgDABgDABgDAB\ngDABgDABgDABgDABgDABgDABgDABgDABgDABgDABgDABgDABgDABgDABgDABgDABgDABgDABgDAB\ngDABgDABgDABgDABgDABgDABgDABgDABgDABgLBtjHHPGv48j1mjIeXxuqbM3adMZZpZF+E3Cf/7\n8AWAMAGAMAGAMAGAMAGAMAGAMAGAMAGAMAGAMAGAMAGAMAGAMAGAMAGAMAGAMAGAMAGAMAGAMAGA\nMAGAMAGAMAGAMAGAMAGAMAGAMAGAMAGAMAGAMAGAMAGAMAGAMAGAMAGAMAGAMAGAMAGAMAGAMAGA\nMAGAMAGAMAGAsG2Mca9eAljDCwDCBADCBADCBADCBADCBADCBADCBADCBADCBADCBADCBADCBADC\nBADCBADCBADCBADCBADCBADCBADCBADCvgDqPCVzLn0ghQAAAABJRU5ErkJggg==\n", "text/plain": [ "" ] }, "execution_count": 14, "metadata": {}, "output_type": "execute_result" } ], "source": [ "im" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "## Tests" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "This second implementation is better: it is deterministic, when given a string or a hash:" ] }, { "cell_type": "code", "execution_count": 15, "metadata": { "scrolled": false }, "outputs": [ { "data": { "image/png": "iVBORw0KGgoAAAANSUhEUgAAAQAAAAEACAYAAABccqhmAAADbklEQVR4nO3WQW3EUBAFwTgKJuPy\naUH4ZFxG8xk4EJIcVhOpqwCM3qk121rr+QCSPqcHAHMEAMIEAMIEAMIEAMIEAMIEAMIEAMIEAMIE\nAMIEAMIEAMIEAMIEAMIEAMIEAMIEAMIEAMIEAMIEAMIEAMIEAMK+pgf8B9d+Tk9gyHG/pieM8gFA\nmABAmABAmABAmABAmABAmABAmABAmABAmABAmABAmABAmABAmABAmABAmABAmABAmABAmABAmABA\nmABAmABAmABAmABAmABAmABAmABAmABAmABAmABAmABAmABAmABAmABAmABAmABAmABAmABAmABA\nmABAmABAmABA2Nf0AP7muF/TE3507ef0BH7JBwBhAgBhAgBhAgBhAgBhAgBhAgBhAgBhAgBhAgBh\nAgBhAgBhAgBhAgBhAgBhAgBhAgBhAgBhAgBhAgBhAgBhAgBhAgBhAgBhAgBhAgBhAgBhAgBhAgBh\nAgBhAgBhAgBhAgBhAgBhAgBhAgBhAgBhAgBhAgBhAgBh21rredfxaz/fdRpSjvv1lrs+AAgTAAgT\nAAgTAAgTAAgTAAgTAAgTAAgTAAgTAAgTAAgTAAgTAAgTAAgTAAgTAAgTAAgTAAgTAAgTAAgTAAgT\nAAgTAAgTAAgTAAgTAAgTAAgTAAgTAAgTAAgTAAgTAAgTAAgTAAgTAAgTAAgTAAgTAAgTAAgTAAgT\nAAgTAAgTAAgTAAgTAAgTAAgTAAgTAAgTAAgTAAgTAAgTAAgTAAgTAAgTAAgTAAgTAAgTAAgTAAgT\nAAgTAAgTAAgTAAgTAAgTAAgTAAgTAAgTAAgTAAgTAAgTAAgTAAgTAAgTAAgTAAgTAAgTAAgTAAgT\nAAgTAAgTAAgTAAgTAAgTAAjb1lrP9Ihp135OT2DIcb+mJ4zyAUCYAECYAECYAECYAECYAECYAECY\nAECYAECYAECYAECYAECYAECYAECYAECYAECYAECYAECYAECYAECYAECYAECYAECYAECYAECYAECY\nAECYAECYAECYAECYAECYAECYAECYAECYAECYAECYAECYAECYAECYAECYAEDYttZ6pkcAM3wAECYA\nECYAECYAECYAECYAECYAECYAECYAECYAECYAECYAECYAECYAECYAECYAECYAECYAECYAECYAECYA\nEPYNgtkdPS7iIE8AAAAASUVORK5CYII=\n", "text/plain": [ "" ] }, "execution_count": 15, "metadata": {}, "output_type": "execute_result" }, { "data": { "image/png": "iVBORw0KGgoAAAANSUhEUgAAAQAAAAEACAYAAABccqhmAAADbklEQVR4nO3WQW3EUBAFwTgKJuPy\naUH4ZFxG8xk4EJIcVhOpqwCM3qk121rr+QCSPqcHAHMEAMIEAMIEAMIEAMIEAMIEAMIEAMIEAMIE\nAMIEAMIEAMIEAMIEAMIEAMIEAMIEAMIEAMIEAMIEAMIEAMIEAMK+pgf8B9d+Tk9gyHG/pieM8gFA\nmABAmABAmABAmABAmABAmABAmABAmABAmABAmABAmABAmABAmABAmABAmABAmABAmABAmABAmABA\nmABAmABAmABAmABAmABAmABAmABAmABAmABAmABAmABAmABAmABAmABAmABAmABAmABAmABAmABA\nmABAmABAmABA2Nf0AP7muF/TE3507ef0BH7JBwBhAgBhAgBhAgBhAgBhAgBhAgBhAgBhAgBhAgBh\nAgBhAgBhAgBhAgBhAgBhAgBhAgBhAgBhAgBhAgBhAgBhAgBhAgBhAgBhAgBhAgBhAgBhAgBhAgBh\nAgBhAgBhAgBhAgBhAgBhAgBhAgBhAgBhAgBhAgBhAgBh21rredfxaz/fdRpSjvv1lrs+AAgTAAgT\nAAgTAAgTAAgTAAgTAAgTAAgTAAgTAAgTAAgTAAgTAAgTAAgTAAgTAAgTAAgTAAgTAAgTAAgTAAgT\nAAgTAAgTAAgTAAgTAAgTAAgTAAgTAAgTAAgTAAgTAAgTAAgTAAgTAAgTAAgTAAgTAAgTAAgTAAgT\nAAgTAAgTAAgTAAgTAAgTAAgTAAgTAAgTAAgTAAgTAAgTAAgTAAgTAAgTAAgTAAgTAAgTAAgTAAgT\nAAgTAAgTAAgTAAgTAAgTAAgTAAgTAAgTAAgTAAgTAAgTAAgTAAgTAAgTAAgTAAgTAAgTAAgTAAgT\nAAgTAAgTAAgTAAgTAAgTAAjb1lrP9Ihp135OT2DIcb+mJ4zyAUCYAECYAECYAECYAECYAECYAECY\nAECYAECYAECYAECYAECYAECYAECYAECYAECYAECYAECYAECYAECYAECYAECYAECYAECYAECYAECY\nAECYAECYAECYAECYAECYAECYAECYAECYAECYAECYAECYAECYAECYAECYAEDYttZ6pkcAM3wAECYA\nECYAECYAECYAECYAECYAECYAECYAECYAECYAECYAECYAECYAECYAECYAECYAECYAECYAECYAECYA\nEPYNgtkdPS7iIE8AAAAASUVORK5CYII=\n", "text/plain": [ "" ] }, "execution_count": 15, "metadata": {}, "output_type": "execute_result" } ], "source": [ "for _ in range(2):\n", " im = identicon2(\"Naereen\")\n", " im" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "And every parameter can be changed and tuned." ] }, { "cell_type": "code", "execution_count": 16, "metadata": { "scrolled": false }, "outputs": [ { "data": { "image/png": "iVBORw0KGgoAAAANSUhEUgAAAIAAAACACAYAAADDPmHLAAABlklEQVR4nO3dwQ2CQBBAUTDWZBUW\nYykWYxVWQwfaAhjIBv97Z2Pm8DOnCTsvy/KZyLqMHoCxBBAngDgBxAkgTgBxAogTQJwA4gQQd93y\n4+f9dsgQj9d71/87as5pOs+sa+e0AeIEECeAOAHECSBOAHECiBNAnADiBBAngDgBxAkgTgBxAogT\nQJwA4gQQJ4A4AcRtOgo9ypFHnHs706xr2ABxAogTQJwA4gQQJ4A4AcQJIE4AcQKIE0CcAOIEECeA\nOAHECSBOAHECiBNAnADiBBA3j3417N+ubH+x9+dnt7AB4gQQJ4A4AcQJIE4AcQKIE0CcAOIEECeA\nOAHECSBOAHECiBNAnADiBBAngLhNN4Hu987D8/GsIoA4AcQJIE4AcQKIE0CcAOIEECeAOAHECSBO\nAHECiBNAnADiBBAngDgBxAkgTgBxAogTQJwA4gQQJ4A4AcQJIE4AcQKIE0CcAOIEECeAOAHECSBO\nAHECiBNAnADiBBAngLjhz8czlg0QJ4A4AcQJIE4AcQKIE0CcAOIEECeAuC8YfSFhxCJjEwAAAABJ\nRU5ErkJggg==\n", "text/plain": [ "" ] }, "execution_count": 16, "metadata": {}, "output_type": "execute_result" }, { "data": { "image/png": "iVBORw0KGgoAAAANSUhEUgAAAIAAAACACAYAAADDPmHLAAABhklEQVR4nO3cwQnCQBBAUSPWZF0e\nLcGjdVnNdhBbiJBlkf9eAcsQPnMaso0x9gtZ19UDsJYA4gQQJ4A4AcQJIE4AcQKIE0CcAOIEECeA\nOAHECSBOAHECiBNAnADiBBAngDgBxAkgTgBxAogTQJwA4gQQJ4A4AcQJIE4AcbfVA9zfrynvfh7P\n09/8p1mPsgHiBBAngDgBxAkgTgBxAogTQJwA4gQQJ4A4AcQJIE4AcQKIE0CcAOIEECeAOAHE/XQU\nOusosm7Gdz16aGoDxAkgTgBxAogTQJwA4gQQJ4A4AcQJIE4AcQKIE0CcAOIEECeAOAHECSBOAHEC\niFv+p9BZHLAeYwPECSBOAHECiBNAnADiBBAngDgBxAkgTgBxAogTQJwA4gQQJ4A4AcQJIE4AcQKI\nE0CcAOIEECeAOAHECSBOAHECiBNAnADiBBAngDgBxAkgTgBxAogTQJwA4gQQJ4A4AcQJIG4bY+yr\nh2AdGyBOAHECiBNAnADiBBAngDgBxAkgTgBxX+DTFD9kupSOAAAAAElFTkSuQmCC\n", "text/plain": [ "" ] }, "execution_count": 16, "metadata": {}, "output_type": "execute_result" }, { "data": { "image/png": "iVBORw0KGgoAAAANSUhEUgAAAIAAAACACAYAAADDPmHLAAABlklEQVR4nO3dUW1CQRBA0b4GTbVQ\nO0jBDhZQsw6oBV7CZlLuOQaYkJv5muw71lrPL7K+pwdglgDiBBAngDgBxAkgTgBxAogTQJwA4i7T\nA9x+fqdHGHd93Md+2waIE0CcAOIEECeAOAHECSBOAHECiBNAnADiBBAngDgBxAkgTgBxAogTQJwA\n4gQQJ4C44xMfiNh5aTx5wbuDDRAngDgBxAkgTgBxAogTQJwA4gQQJ4A4AcQJIE4AcQKIE0CcAOIE\nECeAOAHEnXopdNet3afd2Z0x/Z/aAHECiBNAnADiBBAngDgBxAkgTgBxAogTQJwA4gQQJ4A4AcQJ\nIE4AcQKIE0CcAOIEECeAOAHECSBOAHECiBNAnADiBBAngDgBxAkgTgBxAogTQJwA4gQQJ4A4AcQJ\nIE4AcadeCt1l59e+3+0/zfoKGyBOAHECiBNAnADiBBAngDgBxAkgTgBxAogTQJwA4gQQJ4A4AcQJ\nIE4AcQKIE0CcAOKOtdZzegjm2ABxAogTQJwA4gQQJ4A4AcQJIE4AcQKI+wP/xBqLeOhHHAAAAABJ\nRU5ErkJggg==\n", "text/plain": [ "" ] }, "execution_count": 16, "metadata": {}, "output_type": "execute_result" }, { "data": { "image/png": "iVBORw0KGgoAAAANSUhEUgAAAIAAAACACAYAAADDPmHLAAABpElEQVR4nO3dQW3DQBBAUacKphIq\ngUIJgRIKGjNoKdhSRlvnv3e2rDl8rS+r8W3f99+NrI/VA7CWAOIEECeAOAHECSBOAHECiBNAnADi\n7mce/vx6jAzx/Pl+6fum5ty268x6dE4nQJwA4gQQJ4A4AcQJIE4AcQKIE0CcAOIEECeAOAHECSBO\nAHECiBNAnADiBBAngDgBxAkgTgBxAogTQJwA4gQQJ4A4AcQJIE4AcQKIE0CcAOIEECeAOAHECSBO\nAHECiBNA3KlNoVMmN3u+2pVmPeK2+q9hq1elnnGlWY/yCYgTQJwA4gQQJ4A4AcQJIE4AcQKIE0Cc\nAOIEECeAOAHECSBOAHECiBNAnADi/sWl0AnvdnlzihMgTgBxAogTQJwA4gQQJ4A4AcQJIE4AcQKI\nE0CcAOIEECeAOAHECSBOAHECiBNAnADilm8KnTB5I3jlVs8JToA4AcQJIE4AcQKIE0CcAOIEECeA\nOAHECSBOAHECiBNAnADiBBAngDgBxAkgTgBxb3kplOOcAHECiBNAnADiBBAngDgBxAkgTgBxAoj7\nA7KXJbrOBAHUAAAAAElFTkSuQmCC\n", "text/plain": [ "" ] }, "execution_count": 16, "metadata": {}, "output_type": "execute_result" }, { "data": { "image/png": "iVBORw0KGgoAAAANSUhEUgAAAIAAAACACAYAAADDPmHLAAABiklEQVR4nO3d0QnCMBRAUSudybkE\n5xCcy2myQV2hQkvQe84A4X1c3ldIljHGdiHrOnsA5hJAnADiBBAngDgBxAkgTgBxAogTQNw6e4Db\n83XKue/H/fAzf2nWvWyAOAHECSBOAHECiBNAnADiBBAngDgBxAkgTgBxAogTQJwA4gQQJ4A4AcQJ\nIE4AcQKIE0CcAOIEECeAOAHECSBOAHECiBNAnADiBBAngDgBxAkgTgBxAogTQJwA4gQQJ4C45ZtP\no856KZPj7X191AaIE0CcAOIEECeAOAHECSBOAHECiBNAnADiBBAngDgBxAkgTgBxAogTQJwA4gQQ\nJ4C4dfYAZ9l7K/Yb/3gr2gaIE0CcAOIEECeAOAHECSBOAHECiBNAnADiBBAngDgBxAkgTgBxAogT\nQJwA4gQQJ4A4AcQJIE4AcQKIE0CcAOIEECeAOAHECSBOAHECiBNAnADiBBAngDgBxAkgTgBxAohb\nxhjb7CGYxwaIE0CcAOIEECeAOAHECSBOAHECiBNA3Af8dBhv0fi3FwAAAABJRU5ErkJggg==\n", "text/plain": [ "" ] }, "execution_count": 16, "metadata": {}, "output_type": "execute_result" }, { "data": { "image/png": "iVBORw0KGgoAAAANSUhEUgAAAIAAAACACAYAAADDPmHLAAABj0lEQVR4nO3dQQ0CMRBAUZagCV04\nIDhAF2rqADRwaCbZ/56BbTY/c2qmx1rreyHrOn0AZgkgTgBxAogTQJwA4gQQJ4A4AcQJIO42fYD3\n6z59hHGP52fs2yZAnADiBBAngDgBxAkgTgBxAogTQJwA4gQQJ4A4AcQJIE4AcQKIE0CcAOIEECeA\nOAHEHRZEtJkAcQKIE0CcAOIEECeAOAHECSBOAHECiBNAnADiBBAngDgBxAkgTgBxAogTQNxfm0J3\nbfWc3JQ5bfqfmgBxAogTQJwA4gQQJ4A4AcQJIE4AcQKIE0CcAOIEECeAOAHECSBOAHECiBNAnADi\nxp+P32Hnk/Rnu8BqAsQJIE4AcQKIE0CcAOIEECeAOAHECSBOAHECiBNAnADiBBAngDgBxAkgTgBx\nAogTQNwpbwWf7ebuTiZAnADiBBAngDgBxAkgTgBxAogTQJwA4gQQJ4A4AcQJIE4AcQKIE0CcAOIE\nECeAuGOt9Z0+BHNMgDgBxAkgTgBxAogTQJwA4gQQJ4A4AcT9AJtbGixoXt5QAAAAAElFTkSuQmCC\n", "text/plain": [ "" ] }, "execution_count": 16, "metadata": {}, "output_type": "execute_result" }, { "data": { "image/png": "iVBORw0KGgoAAAANSUhEUgAAAIAAAACACAYAAADDPmHLAAABjUlEQVR4nO3cQQ3CQBBAUUrQhA6k\nIAUp6EDNOigSCIfNQv9756aZND9zmnQbY+wnss6rB2AtAcQJIE4AcQKIE0CcAOIEECeAOAHECSBO\nAHECiBNAnADiBBAngDgBxAkgTgBxAogTQJwA4gQQJ4A4AcQJIE4AcQKIE0CcAOIu3zx8vT2mDPF6\n3qe89x+s/qY2QJwA4gQQJ4A4AcQJIE4AcQKIE0CcAOIEECeAOAHECSBOAHECiBNAnADiBBAngLiv\njkJnmXUYyWc2QJwA4gQQJ4A4AcQJIE4AcQKIE0CcAOIEECeAOAHECSBOAHECiBNAnADiBBAngDgB\nxP3EVfAMM34/e8TrZRsgTgBxAogTQJwA4gQQJ4A4AcQJIE4AcQKIE0CcAOIEECeAOAHECSBOAHEC\niDvsTeAR7/dmsAHiBBAngDgBxAkgTgBxAogTQJwA4gQQJ4A4AcQJIE4AcQKIE0CcAOIEECeAOAHE\nCSBuG2Psq4dgHRsgTgBxAogTQJwA4gQQJ4A4AcQJIE4AcW+9RRPRzATQgAAAAABJRU5ErkJggg==\n", "text/plain": [ "" ] }, "execution_count": 16, "metadata": {}, "output_type": "execute_result" }, { "data": { "image/png": "iVBORw0KGgoAAAANSUhEUgAAAIAAAACACAYAAADDPmHLAAABk0lEQVR4nO3csQ3CQBQFQYyoiYye\nKIWeyKjmOoAWbAnrZO9M7OCC1Y+evIwxvheyrrMfwFwCiBNAnADiBBAngDgBxAkgTgBxAoi7bfn4\ndX/s9Q7+7Pl5r/rOBYgTQJwA4gQQJ4A4AcQJIE4AcQKIE0CcAOIEECeAOAHECSBOAHECiBNAnADi\nBBAngLhNq+AjWbuK3eKMq2gXIE4AcQKIE0CcAOIEECeAOAHECSBOAHECiBNAnADiBBAngDgBxAkg\nTgBxAog77SbwjPu9PbgAcQKIE0CcAOIEECeAOAHECSBOAHECiBNAnADiBBAngDgBxAkgTgBxAogT\nQJwA4gQQt4wxvjMfYL27z19N13IB4gQQJ4A4AcQJIE4AcQKIE0CcAOIEECeAOAHECSBOAHECiBNA\nnADiBBAngLhNm0D7veNYuzN0AeIEECeAOAHECSBOAHECiBNAnADiBBAngDgBxAkgTgBxAogTQJwA\n4gQQJ4A4AcQJIG76n0KZywWIE0CcAOIEECeAOAHECSBOAHECiBNA3A9yBx1CC6cjgwAAAABJRU5E\nrkJggg==\n", "text/plain": [ "" ] }, "execution_count": 16, "metadata": {}, "output_type": "execute_result" }, { "data": { "image/png": "iVBORw0KGgoAAAANSUhEUgAAAIAAAACACAYAAADDPmHLAAABr0lEQVR4nO3dwU3DMBiAURcxE1dW\nYpSuxJVpsgGsEEu1TPK9d64qN/3kXH7Zj+M4fgdZb7sXwF4CiBNAnADiBBAngDgBxAkgTgBxAoh7\nn/nw8+NzySK+fr6XfO8V7H6mdoA4AcQJIE4AcQKIE0CcAOIEECeAOAHECSBOAHECiBNAnADiBBAn\ngDgBxAkgTgBxU0OhV7Fq0HKM+w2wTgVwtx//H+x+pl4BcQKIE0CcAOIEECeAOAHECSBOAHECiBNA\nnADiBBAngDgBxAkgTgBxAogTQNxj5tKolcOWvJaTQjlFAHECiBNAnADiBBAngDgBxAkgTgBxAogT\nQJwA4gQQJ4A4AcQJIE4AcQKIE0CcAOKmpoJXWDVpvOIEziut9Sw7QJwA4gQQJ4A4AcQJIE4AcQKI\nE0CcAOIEECeAOAHECSBOAHECiBNAnADiBBB3y+vjx3Cq6Vnbh0JXWPnn777u/dW8AuIEECeAOAHE\nCSBOAHECiBNAnADiBBAngDgBxAkgTgBxAogTQJwA4gQQJ4A4AcTdciiU8+wAcQKIE0CcAOIEECeA\nOAHECSBOAHECiPsDBD0uCqCnNf8AAAAASUVORK5CYII=\n", "text/plain": [ "" ] }, "execution_count": 16, "metadata": {}, "output_type": "execute_result" }, { "data": { "image/png": "iVBORw0KGgoAAAANSUhEUgAAAIAAAACACAYAAADDPmHLAAABhElEQVR4nO3cQQ0CQRAAQY6cJgSg\nCCkoQgBq1gGo2ExyXWVg59GZ12SPtdbvRtZ9egBmCSBOAHECiBNAnADiBBAngDgBxAkg7pwe4Pl+\nTI8w7vP6jr1tA8QJIE4AcQKIE0CcAOIEECeAOAHECSBOAHECiBNAnADiBBAngDgBxAkgTgBxAogT\nQNxxxQ8idl4aT17w7mADxAkgTgBxAogTQJwA4gQQJ4A4AcQJIE4AcQKIE0CcAOIEECeAOAHECSBO\nAHHjN4F+CvVTKIMEECeAOAHECSBOAHECiBNAnADiBBAngDgBxAkgTgBxAogTQJwA4gQQJ4A4AcQJ\nIO6cHmCXHZe2V7xgtgHiBBAngDgBxAkgTgBxAogTQJwA4gQQJ4A4AcQJIE4AcQKIE0CcAOIEECeA\nOAHEjX8VyywbIE4AcQKIE0CcAOIEECeAOAHECSBOAHECiBNAnADiBBAngDgBxAkgTgBxAogTQJwA\n4gQQJ4A4AcQJIE4AcQKIE0CcAOIEECeAuD+qKBw9NL8E8gAAAABJRU5ErkJggg==\n", "text/plain": [ "" ] }, "execution_count": 16, "metadata": {}, "output_type": "execute_result" } ], "source": [ "for _ in range(10):\n", " im = identicon2(size=128)\n", " im" ] }, { "cell_type": "code", "execution_count": 39, "metadata": {}, "outputs": [], "source": [ "mylist = [identicon2() for _ in range(10)]" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "## List of vignettes\n", "\n", "Let's use [this technique](https://carreau.github.io/posts/29-JupyterCon-DisplayProtocol.ipynb.html)" ] }, { "cell_type": "code", "execution_count": 18, "metadata": {}, "outputs": [], "source": [ "import base64\n", "\n", "from IPython.display import Image\n", "from IPython.display import HTML\n", "\n", "def tag_from_data(data, size='100%'):\n", " return (\n", " '''\n", " ''').format(''.join(base64.encodebytes(data).decode().split('\\n')), size)" ] }, { "cell_type": "code", "execution_count": 19, "metadata": {}, "outputs": [], "source": [ "import io\n", "\n", "def im2bytes(im):\n", " buffer = io.BytesIO()\n", " im.save(buffer, format=\"png\")\n", " return buffer.getvalue()" ] }, { "cell_type": "code", "execution_count": 20, "metadata": {}, "outputs": [ { "data": { "text/html": [ "\n", " " ], "text/plain": [ "" ] }, "execution_count": 20, "metadata": {}, "output_type": "execute_result" } ], "source": [ "HTML(tag_from_data(im2bytes(im)))" ] }, { "cell_type": "code", "execution_count": 21, "metadata": {}, "outputs": [], "source": [ "class VignetteList: \n", " def __init__(self, *images, size=None):\n", " self.images = images\n", " self.size = size\n", " \n", " def _repr_html_(self):\n", " return '

'+','.join(tag_from_data(im2bytes(im), self.size) for im in self.images)+'

'" ] }, { "cell_type": "code", "execution_count": 41, "metadata": { "scrolled": false }, "outputs": [ { "data": { "text/html": [ "

\n", " ,\n", " ,\n", " ,\n", " ,\n", " ,\n", " ,\n", " ,\n", " ,\n", " ,\n", "

" ], "text/plain": [ "<__main__.VignetteList at 0x7fc51e0e0c88>" ] }, "execution_count": 41, "metadata": {}, "output_type": "execute_result" } ], "source": [ "VignetteList(*mylist, size='200px')" ] }, { "cell_type": "code", "execution_count": 42, "metadata": {}, "outputs": [], "source": [ "def tag_from_data_II(data, size='100%'):\n", " return ''''''.format(''.join(base64.encodebytes(data).decode().split('\\n')), size)\n", "\n", "def html_list_formatter(ll):\n", " html = get_ipython().display_formatter.formatters['text/html']\n", " reps = []\n", " for o in ll:\n", " if isinstance(o, Image):\n", " reps.append(tag_from_data_II(o.data, '200px') )\n", " elif isinstance(im, PILImage.Image):\n", " reps.append(tag_from_data_II(im2bytes(o), '200px') )\n", " else:\n", " h = html(o)\n", " if h: \n", " reps.append(h)\n", " else:\n", " reps.append(repr(o)+'')\n", " \n", " return '['+','.join(reps)+']'" ] }, { "cell_type": "code", "execution_count": 43, "metadata": {}, "outputs": [ { "data": { "text/plain": [ "" ] }, "execution_count": 43, "metadata": {}, "output_type": "execute_result" } ], "source": [ "ipython = get_ipython()\n", "html = ipython.display_formatter.formatters['text/html']\n", "html.for_type(list, html_list_formatter)" ] }, { "cell_type": "code", "execution_count": 44, "metadata": {}, "outputs": [ { "data": { "text/html": [ "[,,,,,,,,,]" ], "text/plain": [ "[,\n", " ,\n", " ,\n", " ,\n", " ,\n", " ,\n", " ,\n", " ,\n", " ,\n", " ]" ] }, "execution_count": 44, "metadata": {}, "output_type": "execute_result" } ], "source": [ "mylist" ] }, { "cell_type": "code", "execution_count": null, "metadata": {}, "outputs": [], "source": [] }, { "cell_type": "markdown", "metadata": {}, "source": [ "## Conclusion\n", "\n", "> *That's it for today, folks!* [See on my GitHub for more notebooks](https://github.com/Naereen/notebooks)" ] } ], "metadata": { "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.3" }, "toc": { "colors": { "hover_highlight": "#DAA520", "running_highlight": "#FF0000", "selected_highlight": "#FFD700" }, "moveMenuLeft": true, "nav_menu": { "height": "108px", "width": "251px" }, "navigate_menu": true, "number_sections": true, "sideBar": true, "threshold": 4, "toc_cell": false, "toc_section_display": "block", "toc_window_display": false } }, "nbformat": 4, "nbformat_minor": 2 }