{ "cells": [ { "cell_type": "markdown", "id": "e7f2abd1", "metadata": {}, "source": [ "To install kernel run:\n", "\n", "```\n", "poetry run ipython kernel install --user --name=mpii\n", "```" ] }, { "cell_type": "code", "execution_count": 296, "id": "8b90027a", "metadata": {}, "outputs": [ { "name": "stdout", "output_type": "stream", "text": [ "Using matplotlib backend: nbAgg\n" ] } ], "source": [ "# imports\n", "import os\n", "from pathlib import Path\n", "from scipy.io import loadmat\n", "import scipy\n", "import tqdm\n", "import pickle\n", "import logging\n", "import time\n", "import gc\n", "%matplotlib\n", "%matplotlib notebook\n", "import matplotlib\n", "import matplotlib.pyplot as plt\n", "import numpy as np\n", "from PIL import Image\n", "from threading import Lock\n" ] }, { "cell_type": "code", "execution_count": 46, "id": "f5818c76", "metadata": {}, "outputs": [], "source": [ "# logging\n", "logging.basicConfig(level=logging.WARNING)\n", "logger = logging.getLogger('pose_ordering')\n", "logger.setLevel(logging.INFO)" ] }, { "cell_type": "code", "execution_count": 33, "id": "7f6fe12f", "metadata": {}, "outputs": [], "source": [ "# config\n", "\n", "mpii_idx_to_jnt = {0: 'rankl', 1: 'rknee', 2: 'rhip', 5: 'lankl', 4: 'lknee', 3: 'lhip',\n", " 6: 'pelvis', 7: 'thorax', 8: 'upper_neck', 11: 'relb', 10: 'rwri', 9: 'head',\n", " 12: 'rsho', 13: 'lsho', 14: 'lelb', 15: 'lwri'}\n", "\n", "angles = [\n", " ['rwri', 'relb', 'rsho'],\n", " ['relb', 'rsho','thorax'],\n", " ['rsho','thorax', 'pelvis'],\n", " ['thorax','pelvis', 'rhip'],\n", " ['pelvis', 'rhip', 'rknee'],\n", " [ 'rhip', 'rknee', 'rankl'],\n", " ['rsho','thorax','upper_neck'],\n", " ['lwri', 'lelb', 'lsho'],\n", " ['lelb', 'lsho','thorax'],\n", " ['lsho','thorax','pelvis'],\n", " ['thorax','pelvis','lhip'],\n", " ['pelvis','lhip', 'lknee'],\n", " ['lhip', 'lknee', 'lankl'],\n", " ['lsho','thorax','upper_neck'],\n", " ['thorax','upper_neck', 'head'],\n", "]\n", "\n", "bones = [\n", " ['rankl', 'rknee', 'orange'],\n", " ['rknee', 'rhip', 'orange'],\n", " ['rhip','pelvis', 'orange'],\n", " ['lankl', 'lknee', 'yellow'],\n", " ['lknee', 'lhip', 'yellow'],\n", " ['lhip','pelvis', 'yellow'],\n", "\n", " ['rwri', 'relb', 'red'],\n", " ['relb','rsho', 'red'],\n", " ['rsho','thorax', 'red'],\n", " ['lwri', 'lelb', 'blue'],\n", " ['lelb','lsho', 'blue'],\n", " ['lsho','thorax', 'blue'],\n", "\n", " ['thorax','upper_neck', 'pink'],\n", " ['upper_neck','head', 'pink'],\n", " ['thorax','pelvis', 'green'],\n", "]\n", "\n", "\n", "filename = 'mpii_human_pose_v1_u12_2/mpii_human_pose_v1_u12_1.mat'\n", "\n", "\n", "#matplotlib plot size\n", "default_dpi = matplotlib.rcParamsDefault['figure.dpi']\n", "matplotlib.rcParams['figure.dpi'] = default_dpi*2\n" ] }, { "cell_type": "code", "execution_count": 18, "id": "3d511d3a", "metadata": {}, "outputs": [ { "name": "stderr", "output_type": "stream", "text": [ "DEBUG:pose_ordering:Loading pickled version\n" ] } ], "source": [ "%timeit\n", "if os.path.exists(filename + '.p'):\n", " with open(filename + '.p', 'rb') as fp:\n", " gc.disable() # speeds up pickle.load by ~30%\n", " logger.debug('Loading pickled version')\n", " mat = pickle.load(fp)\n", " gc.enable()\n", "else:\n", " logger.debug(f'Loading {filename}')\n", " mat = loadmat(filename)\n", " with open(filename + '.p', 'wb') as fp:\n", " pickle.dump(mat, fp)" ] }, { "cell_type": "code", "execution_count": 19, "id": "4abd4ab9", "metadata": {}, "outputs": [ { "data": { "text/plain": [ "24987" ] }, "execution_count": 19, "metadata": {}, "output_type": "execute_result" } ], "source": [ "\n", "mpii = mat['RELEASE'][0,0]\n", "num_images = mpii['annolist'][0].shape[0]\n", "\n", "num_images" ] }, { "cell_type": "code", "execution_count": 20, "id": "7c985ce9", "metadata": {}, "outputs": [ { "data": { "image/png": "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\n", "text/plain": [ "
" ] }, "metadata": { "needs_background": "light" }, "output_type": "display_data" } ], "source": [ "plt.plot([1,2], [-1,4]) \n", " \n", "# function to show the plot \n", "plt.show()" ] }, { "cell_type": "code", "execution_count": 233, "id": "9b5f975e", "metadata": { "scrolled": true }, "outputs": [ { "name": "stderr", "output_type": "stream", "text": [ " 0%| | 0/24987 [00:00:43: FutureWarning: elementwise comparison failed; returning scalar instead, but in the future will perform elementwise comparison\n", " vis = point['is_visible'].flatten() if 'is_visible' in point else []\n", "100%|██████████| 24987/24987 [00:19<00:00, 1282.47it/s]\n" ] } ], "source": [ "has_values = 0\n", "visualise = False\n", "\n", "vectors = []\n", "vector_points = []\n", "\n", "for idx in tqdm.tqdm(range(num_images)):\n", "# for idx in range(num_images):\n", " # # is_training = mpii['img_train'][0,idx] # whether in train or test set\n", " # person_ids = mpii['single_person'][idx][0].flatten()\n", "\n", " # if not len(person_ids):\n", " # # skip, because not enough persons\n", " # continue\n", " \n", " annotations = mpii['annolist'][0,idx]\n", " anno_file = str(annotations[0]['name'][0][0][0])\n", " filename = '/home/ruben/Documents/Projecten/2020/Security Vision/tryouts/MPII Human Pose Dataset/images/'+anno_file\n", " logger.debug(filename)\n", " \n", " if visualise:\n", " image = Image.open(filename)\n", "\n", " if not len(annotations['annorect']):\n", " continue\n", "\n", " for annotation in annotations['annorect'][0]:\n", " # TODO : We might need to mirror the objects following a particular rule (see also Impett & Moretti)\n", " try:\n", " annotation_points = annotation['annopoints'][0,0]['point'][0]\n", " except Exception as e:\n", " # no points tagged for this one\n", " continue\n", "\n", " # logger.debug(points.shape[0])\n", "\n", " points = {}\n", "\n", " for point in annotation_points:\n", " x = float(point['x'].flatten()[0])\n", " y = float(point['y'].flatten()[0])\n", " id_ = point['id'][0][0]\n", " vis = point['is_visible'].flatten() if 'is_visible' in point else []\n", " joint = mpii_idx_to_jnt[id_]\n", " vis = int(vis[0]) if len(vis) else 0\n", " points[joint] = np.array([x,y, vis])\n", "\n", " \n", " if not all([joint in points for joint in mpii_idx_to_jnt.values()]):\n", " logger.debug(f\"Not enough points: {points=}\")\n", " break\n", "\n", "# if 'rhip' not in points or 'lhip' not in points or 'thorax' not in points:\n", "# logger.info(f\"Not enough points: {points=}\")\n", "# continue\n", " \n", "\n", " visible_joints = [joint for joint in mpii_idx_to_jnt.values() if joint in points]\n", " \n", " if visualise:\n", " plt.imshow(image)\n", " plt.plot(np.array([points[joint][0] for joint in visible_joints]), np.array([points[joint][1] for joint in visible_joints]), 'o')\n", " \n", " for bone in bones:\n", " if not all([bone[0] in points, bone[1] in points]):\n", " continue\n", " \n", " if visualise:\n", " plt.plot([points[bone[0]][0], points[bone[1]][0]], [points[bone[0]][1], points[bone[1]][1]], color=bone[2])\n", "\n", " \n", " annotation_vector = []\n", "\n", " for joints in angles:\n", " if not all([p in points for p in joints]):\n", " # check if all points to calculate joints are available \n", " annotation_vector.append(None) # CHOICE store null\n", " continue\n", " \n", " v1 = points[joints[0]] - points[joints[1]]\n", " v2 = points[joints[2]] - points[joints[1]]\n", "\n", " angle = np.arctan2(v2[1], v2[0]) - np.arctan2(v1[1], v1[0])\n", " annotation_vector.append(angle*angle) # CHOICE squared angle?\n", " if visualise:\n", " plt.text(int(points[joints[1]][0]), int(points[joints[1]][1]), f\"{angle:.4}\")\n", " \n", " vector_points.append(points)\n", " vectors.append([annotation_vector, idx, len(vector_points)-1])\n", " \n", " \n", " has_values += 1\n", "# print(annotations)\n", "# break\n", " if visualise:\n", " plt.show() # show image\n", " if has_values > 2:\n", " break\n" ] }, { "cell_type": "code", "execution_count": 144, "id": "41c21986", "metadata": {}, "outputs": [ { "name": "stdout", "output_type": "stream", "text": [ "17969 [[10.301455313174841, 0.8738769777401664, 7.932846045996372, 33.90556982877595, 4.788006833143501, 11.579460281021154, 6.330558392334423, 0.590823723139596, 5.670376354016367, 0.10566517419166079, 7.019067218895686, 0.9921823387865951, 7.12304248234401, 0.39129098417224434, 2.9091346049064142e-11], 4] [[2.199207456110774, 1.3408538112736343, 0.9907647738998692, 3.8666010567179896, 0.42119523559718064, 2.7174019285215865, 2.563494948040972, 1.3928467439375065, 2.901393929071837, 4.646466934360204, 27.37712157435963, 2.2728260851251068, 7.166142323804702, 22.581788879995116, 9.869588546386826], 24984]\n" ] } ], "source": [ "\n", "print(len(vectors), vectors[1], vectors[-1])" ] }, { "cell_type": "code", "execution_count": 118, "id": "6a91c2d3", "metadata": {}, "outputs": [], "source": [ "from sklearn.preprocessing import StandardScaler\n" ] }, { "cell_type": "code", "execution_count": 145, "id": "b9478c7a", "metadata": {}, "outputs": [], "source": [ "values = [v[0] for v in vectors]" ] }, { "cell_type": "code", "execution_count": 265, "id": "6e8d9b47", "metadata": {}, "outputs": [], "source": [ "x = StandardScaler().fit_transform(values) # normalizing the features\n" ] }, { "cell_type": "code", "execution_count": 266, "id": "821f483d", "metadata": {}, "outputs": [ { "data": { "text/plain": [ "(2.7179541485275698e-15, 1.0)" ] }, "execution_count": 266, "metadata": {}, "output_type": "execute_result" } ], "source": [ "x.mean(), np.std(x)" ] }, { "cell_type": "code", "execution_count": 267, "id": "e6b5e4ef", "metadata": {}, "outputs": [], "source": [ "from sklearn.decomposition import PCA" ] }, { "cell_type": "code", "execution_count": 268, "id": "2ee95e88", "metadata": {}, "outputs": [], "source": [ "# If 0 < n_components < 1 and svd_solver == 'full', select the number of components such that the amount of variance that needs to be explained is greater than the percentage specified by n_components.: https://medium.com/@ansjin/dimensionality-reduction-using-pca-on-multivariate-timeseries-data-b5cc07238dc4\n", "pca = PCA(n_components=None)\n", "principalComponents = pca.fit_transform(x)" ] }, { "cell_type": "code", "execution_count": 269, "id": "0d54404f", "metadata": {}, "outputs": [ { "data": { "text/plain": [ "array([0.1947121 , 0.14030244, 0.13287677, 0.09570534, 0.07844851,\n", " 0.06633996, 0.06156485, 0.0560413 , 0.04303254, 0.03463799,\n", " 0.02883156, 0.02305097, 0.01668491, 0.015404 , 0.01236676])" ] }, "execution_count": 269, "metadata": {}, "output_type": "execute_result" } ], "source": [ "pca.explained_variance_ratio_" ] }, { "cell_type": "code", "execution_count": 270, "id": "515999d3", "metadata": {}, "outputs": [], "source": [ "# pca.components_\n", "# pca.get_precision()\n", "\n", "plt.plot(np.cumsum(pca.explained_variance_ratio_))\n", "plt.xlabel('number of components')\n", "plt.ylabel('cumulative explained variance');" ] }, { "cell_type": "code", "execution_count": 271, "id": "8519803d", "metadata": {}, "outputs": [], "source": [ "def get_catname(act) -> str:\n", " return str(act['cat_name'][0][0]) if len(act['cat_name'][0]) else 'unknown'" ] }, { "cell_type": "code", "execution_count": 294, "id": "17631429", "metadata": {}, "outputs": [ { "data": { "text/plain": [ "array([[-2.35999232, 0.69057281],\n", " [-0.61853026, 1.07182264],\n", " [-1.18063219, -0.16670125],\n", " ...,\n", " [ 1.95397646, -2.77908482],\n", " [ 1.47253967, -0.71394096],\n", " [ 1.82195482, -2.03658075]])" ] }, "execution_count": 294, "metadata": {}, "output_type": "execute_result" } ], "source": [ "# cats = [mpii['act'][v[1]]['cat_name'][0] for v in vectors]\n", "cats= np.unique([get_catname(act) for act in mpii['act']]).tolist()\n", "cats" ] }, { "cell_type": "code", "execution_count": 326, "id": "b0a25c9c", "metadata": { "scrolled": true }, "outputs": [ { "data": { "application/javascript": [ "/* Put everything inside the global mpl namespace */\n", "/* global mpl */\n", "window.mpl = {};\n", "\n", "mpl.get_websocket_type = function () {\n", " if (typeof WebSocket !== 'undefined') {\n", " return WebSocket;\n", " } else if (typeof MozWebSocket !== 'undefined') {\n", " return MozWebSocket;\n", " } else {\n", " alert(\n", " 'Your browser does not have WebSocket support. ' +\n", " 'Please try Chrome, Safari or Firefox ≥ 6. ' +\n", " 'Firefox 4 and 5 are also supported but you ' +\n", " 'have to enable WebSockets in about:config.'\n", " );\n", " }\n", "};\n", "\n", "mpl.figure = function (figure_id, websocket, ondownload, parent_element) {\n", " this.id = figure_id;\n", "\n", " this.ws = websocket;\n", "\n", " this.supports_binary = this.ws.binaryType !== undefined;\n", "\n", " if (!this.supports_binary) {\n", " var warnings = document.getElementById('mpl-warnings');\n", " if (warnings) {\n", " warnings.style.display = 'block';\n", " warnings.textContent =\n", " 'This browser does not support binary websocket messages. ' +\n", " 'Performance may be slow.';\n", " }\n", " }\n", "\n", " this.imageObj = new Image();\n", "\n", " this.context = undefined;\n", " this.message = undefined;\n", " this.canvas = undefined;\n", " this.rubberband_canvas = undefined;\n", " this.rubberband_context = undefined;\n", " this.format_dropdown = undefined;\n", "\n", " this.image_mode = 'full';\n", "\n", " this.root = document.createElement('div');\n", " this.root.setAttribute('style', 'display: inline-block');\n", " this._root_extra_style(this.root);\n", "\n", " parent_element.appendChild(this.root);\n", "\n", " this._init_header(this);\n", " this._init_canvas(this);\n", " this._init_toolbar(this);\n", "\n", " var fig = this;\n", "\n", " this.waiting = false;\n", "\n", " this.ws.onopen = function () {\n", " fig.send_message('supports_binary', { value: fig.supports_binary });\n", " fig.send_message('send_image_mode', {});\n", " if (fig.ratio !== 1) {\n", " fig.send_message('set_dpi_ratio', { dpi_ratio: fig.ratio });\n", " }\n", " fig.send_message('refresh', {});\n", " };\n", "\n", " this.imageObj.onload = function () {\n", " if (fig.image_mode === 'full') {\n", " // Full images could contain transparency (where diff images\n", " // almost always do), so we need to clear the canvas so that\n", " // there is no ghosting.\n", " fig.context.clearRect(0, 0, fig.canvas.width, fig.canvas.height);\n", " }\n", " fig.context.drawImage(fig.imageObj, 0, 0);\n", " };\n", "\n", " this.imageObj.onunload = function () {\n", " fig.ws.close();\n", " };\n", "\n", " this.ws.onmessage = this._make_on_message_function(this);\n", "\n", " this.ondownload = ondownload;\n", "};\n", "\n", "mpl.figure.prototype._init_header = function () {\n", " var titlebar = document.createElement('div');\n", " titlebar.classList =\n", " 'ui-dialog-titlebar ui-widget-header ui-corner-all ui-helper-clearfix';\n", " var titletext = document.createElement('div');\n", " titletext.classList = 'ui-dialog-title';\n", " titletext.setAttribute(\n", " 'style',\n", " 'width: 100%; text-align: center; padding: 3px;'\n", " );\n", " titlebar.appendChild(titletext);\n", " this.root.appendChild(titlebar);\n", " this.header = titletext;\n", "};\n", "\n", "mpl.figure.prototype._canvas_extra_style = function (_canvas_div) {};\n", "\n", "mpl.figure.prototype._root_extra_style = function (_canvas_div) {};\n", "\n", "mpl.figure.prototype._init_canvas = function () {\n", " var fig = this;\n", "\n", " var canvas_div = (this.canvas_div = document.createElement('div'));\n", " canvas_div.setAttribute(\n", " 'style',\n", " 'border: 1px solid #ddd;' +\n", " 'box-sizing: content-box;' +\n", " 'clear: both;' +\n", " 'min-height: 1px;' +\n", " 'min-width: 1px;' +\n", " 'outline: 0;' +\n", " 'overflow: hidden;' +\n", " 'position: relative;' +\n", " 'resize: both;'\n", " );\n", "\n", " function on_keyboard_event_closure(name) {\n", " return function (event) {\n", " return fig.key_event(event, name);\n", " };\n", " }\n", "\n", " canvas_div.addEventListener(\n", " 'keydown',\n", " on_keyboard_event_closure('key_press')\n", " );\n", " canvas_div.addEventListener(\n", " 'keyup',\n", " on_keyboard_event_closure('key_release')\n", " );\n", "\n", " this._canvas_extra_style(canvas_div);\n", " this.root.appendChild(canvas_div);\n", "\n", " var canvas = (this.canvas = document.createElement('canvas'));\n", " canvas.classList.add('mpl-canvas');\n", " canvas.setAttribute('style', 'box-sizing: content-box;');\n", "\n", " this.context = canvas.getContext('2d');\n", "\n", " var backingStore =\n", " this.context.backingStorePixelRatio ||\n", " this.context.webkitBackingStorePixelRatio ||\n", " this.context.mozBackingStorePixelRatio ||\n", " this.context.msBackingStorePixelRatio ||\n", " this.context.oBackingStorePixelRatio ||\n", " this.context.backingStorePixelRatio ||\n", " 1;\n", "\n", " this.ratio = (window.devicePixelRatio || 1) / backingStore;\n", "\n", " var rubberband_canvas = (this.rubberband_canvas = document.createElement(\n", " 'canvas'\n", " ));\n", " rubberband_canvas.setAttribute(\n", " 'style',\n", " 'box-sizing: content-box; position: absolute; left: 0; top: 0; z-index: 1;'\n", " );\n", "\n", " // Apply a ponyfill if ResizeObserver is not implemented by browser.\n", " if (this.ResizeObserver === undefined) {\n", " if (window.ResizeObserver !== undefined) {\n", " this.ResizeObserver = window.ResizeObserver;\n", " } else {\n", " var obs = _JSXTOOLS_RESIZE_OBSERVER({});\n", " this.ResizeObserver = obs.ResizeObserver;\n", " }\n", " }\n", "\n", " this.resizeObserverInstance = new this.ResizeObserver(function (entries) {\n", " var nentries = entries.length;\n", " for (var i = 0; i < nentries; i++) {\n", " var entry = entries[i];\n", " var width, height;\n", " if (entry.contentBoxSize) {\n", " if (entry.contentBoxSize instanceof Array) {\n", " // Chrome 84 implements new version of spec.\n", " width = entry.contentBoxSize[0].inlineSize;\n", " height = entry.contentBoxSize[0].blockSize;\n", " } else {\n", " // Firefox implements old version of spec.\n", " width = entry.contentBoxSize.inlineSize;\n", " height = entry.contentBoxSize.blockSize;\n", " }\n", " } else {\n", " // Chrome <84 implements even older version of spec.\n", " width = entry.contentRect.width;\n", " height = entry.contentRect.height;\n", " }\n", "\n", " // Keep the size of the canvas and rubber band canvas in sync with\n", " // the canvas container.\n", " if (entry.devicePixelContentBoxSize) {\n", " // Chrome 84 implements new version of spec.\n", " canvas.setAttribute(\n", " 'width',\n", " entry.devicePixelContentBoxSize[0].inlineSize\n", " );\n", " canvas.setAttribute(\n", " 'height',\n", " entry.devicePixelContentBoxSize[0].blockSize\n", " );\n", " } else {\n", " canvas.setAttribute('width', width * fig.ratio);\n", " canvas.setAttribute('height', height * fig.ratio);\n", " }\n", " canvas.setAttribute(\n", " 'style',\n", " 'width: ' + width + 'px; height: ' + height + 'px;'\n", " );\n", "\n", " rubberband_canvas.setAttribute('width', width);\n", " rubberband_canvas.setAttribute('height', height);\n", "\n", " // And update the size in Python. We ignore the initial 0/0 size\n", " // that occurs as the element is placed into the DOM, which should\n", " // otherwise not happen due to the minimum size styling.\n", " if (fig.ws.readyState == 1 && width != 0 && height != 0) {\n", " fig.request_resize(width, height);\n", " }\n", " }\n", " });\n", " this.resizeObserverInstance.observe(canvas_div);\n", "\n", " function on_mouse_event_closure(name) {\n", " return function (event) {\n", " return fig.mouse_event(event, name);\n", " };\n", " }\n", "\n", " rubberband_canvas.addEventListener(\n", " 'mousedown',\n", " on_mouse_event_closure('button_press')\n", " );\n", " rubberband_canvas.addEventListener(\n", " 'mouseup',\n", " on_mouse_event_closure('button_release')\n", " );\n", " rubberband_canvas.addEventListener(\n", " 'dblclick',\n", " on_mouse_event_closure('dblclick')\n", " );\n", " // Throttle sequential mouse events to 1 every 20ms.\n", " rubberband_canvas.addEventListener(\n", " 'mousemove',\n", " on_mouse_event_closure('motion_notify')\n", " );\n", "\n", " rubberband_canvas.addEventListener(\n", " 'mouseenter',\n", " on_mouse_event_closure('figure_enter')\n", " );\n", " rubberband_canvas.addEventListener(\n", " 'mouseleave',\n", " on_mouse_event_closure('figure_leave')\n", " );\n", "\n", " canvas_div.addEventListener('wheel', function (event) {\n", " if (event.deltaY < 0) {\n", " event.step = 1;\n", " } else {\n", " event.step = -1;\n", " }\n", " on_mouse_event_closure('scroll')(event);\n", " });\n", "\n", " canvas_div.appendChild(canvas);\n", " canvas_div.appendChild(rubberband_canvas);\n", "\n", " this.rubberband_context = rubberband_canvas.getContext('2d');\n", " this.rubberband_context.strokeStyle = '#000000';\n", "\n", " this._resize_canvas = function (width, height, forward) {\n", " if (forward) {\n", " canvas_div.style.width = width + 'px';\n", " canvas_div.style.height = height + 'px';\n", " }\n", " };\n", "\n", " // Disable right mouse context menu.\n", " this.rubberband_canvas.addEventListener('contextmenu', function (_e) {\n", " event.preventDefault();\n", " return false;\n", " });\n", "\n", " function set_focus() {\n", " canvas.focus();\n", " canvas_div.focus();\n", " }\n", "\n", " window.setTimeout(set_focus, 100);\n", "};\n", "\n", "mpl.figure.prototype._init_toolbar = function () {\n", " var fig = this;\n", "\n", " var toolbar = document.createElement('div');\n", " toolbar.classList = 'mpl-toolbar';\n", " this.root.appendChild(toolbar);\n", "\n", " function on_click_closure(name) {\n", " return function (_event) {\n", " return fig.toolbar_button_onclick(name);\n", " };\n", " }\n", "\n", " function on_mouseover_closure(tooltip) {\n", " return function (event) {\n", " if (!event.currentTarget.disabled) {\n", " return fig.toolbar_button_onmouseover(tooltip);\n", " }\n", " };\n", " }\n", "\n", " fig.buttons = {};\n", " var buttonGroup = document.createElement('div');\n", " buttonGroup.classList = 'mpl-button-group';\n", " for (var toolbar_ind in mpl.toolbar_items) {\n", " var name = mpl.toolbar_items[toolbar_ind][0];\n", " var tooltip = mpl.toolbar_items[toolbar_ind][1];\n", " var image = mpl.toolbar_items[toolbar_ind][2];\n", " var method_name = mpl.toolbar_items[toolbar_ind][3];\n", "\n", " if (!name) {\n", " /* Instead of a spacer, we start a new button group. */\n", " if (buttonGroup.hasChildNodes()) {\n", " toolbar.appendChild(buttonGroup);\n", " }\n", " buttonGroup = document.createElement('div');\n", " buttonGroup.classList = 'mpl-button-group';\n", " continue;\n", " }\n", "\n", " var button = (fig.buttons[name] = document.createElement('button'));\n", " button.classList = 'mpl-widget';\n", " button.setAttribute('role', 'button');\n", " button.setAttribute('aria-disabled', 'false');\n", " button.addEventListener('click', on_click_closure(method_name));\n", " button.addEventListener('mouseover', on_mouseover_closure(tooltip));\n", "\n", " var icon_img = document.createElement('img');\n", " icon_img.src = '_images/' + image + '.png';\n", " icon_img.srcset = '_images/' + image + '_large.png 2x';\n", " icon_img.alt = tooltip;\n", " button.appendChild(icon_img);\n", "\n", " buttonGroup.appendChild(button);\n", " }\n", "\n", " if (buttonGroup.hasChildNodes()) {\n", " toolbar.appendChild(buttonGroup);\n", " }\n", "\n", " var fmt_picker = document.createElement('select');\n", " fmt_picker.classList = 'mpl-widget';\n", " toolbar.appendChild(fmt_picker);\n", " this.format_dropdown = fmt_picker;\n", "\n", " for (var ind in mpl.extensions) {\n", " var fmt = mpl.extensions[ind];\n", " var option = document.createElement('option');\n", " option.selected = fmt === mpl.default_extension;\n", " option.innerHTML = fmt;\n", " fmt_picker.appendChild(option);\n", " }\n", "\n", " var status_bar = document.createElement('span');\n", " status_bar.classList = 'mpl-message';\n", " toolbar.appendChild(status_bar);\n", " this.message = status_bar;\n", "};\n", "\n", "mpl.figure.prototype.request_resize = function (x_pixels, y_pixels) {\n", " // Request matplotlib to resize the figure. Matplotlib will then trigger a resize in the client,\n", " // which will in turn request a refresh of the image.\n", " this.send_message('resize', { width: x_pixels, height: y_pixels });\n", "};\n", "\n", "mpl.figure.prototype.send_message = function (type, properties) {\n", " properties['type'] = type;\n", " properties['figure_id'] = this.id;\n", " this.ws.send(JSON.stringify(properties));\n", "};\n", "\n", "mpl.figure.prototype.send_draw_message = function () {\n", " if (!this.waiting) {\n", " this.waiting = true;\n", " this.ws.send(JSON.stringify({ type: 'draw', figure_id: this.id }));\n", " }\n", "};\n", "\n", "mpl.figure.prototype.handle_save = function (fig, _msg) {\n", " var format_dropdown = fig.format_dropdown;\n", " var format = format_dropdown.options[format_dropdown.selectedIndex].value;\n", " fig.ondownload(fig, format);\n", "};\n", "\n", "mpl.figure.prototype.handle_resize = function (fig, msg) {\n", " var size = msg['size'];\n", " if (size[0] !== fig.canvas.width || size[1] !== fig.canvas.height) {\n", " fig._resize_canvas(size[0], size[1], msg['forward']);\n", " fig.send_message('refresh', {});\n", " }\n", "};\n", "\n", "mpl.figure.prototype.handle_rubberband = function (fig, msg) {\n", " var x0 = msg['x0'] / fig.ratio;\n", " var y0 = (fig.canvas.height - msg['y0']) / fig.ratio;\n", " var x1 = msg['x1'] / fig.ratio;\n", " var y1 = (fig.canvas.height - msg['y1']) / fig.ratio;\n", " x0 = Math.floor(x0) + 0.5;\n", " y0 = Math.floor(y0) + 0.5;\n", " x1 = Math.floor(x1) + 0.5;\n", " y1 = Math.floor(y1) + 0.5;\n", " var min_x = Math.min(x0, x1);\n", " var min_y = Math.min(y0, y1);\n", " var width = Math.abs(x1 - x0);\n", " var height = Math.abs(y1 - y0);\n", "\n", " fig.rubberband_context.clearRect(\n", " 0,\n", " 0,\n", " fig.canvas.width / fig.ratio,\n", " fig.canvas.height / fig.ratio\n", " );\n", "\n", " fig.rubberband_context.strokeRect(min_x, min_y, width, height);\n", "};\n", "\n", "mpl.figure.prototype.handle_figure_label = function (fig, msg) {\n", " // Updates the figure title.\n", " fig.header.textContent = msg['label'];\n", "};\n", "\n", "mpl.figure.prototype.handle_cursor = function (fig, msg) {\n", " var cursor = msg['cursor'];\n", " switch (cursor) {\n", " case 0:\n", " cursor = 'pointer';\n", " break;\n", " case 1:\n", " cursor = 'default';\n", " break;\n", " case 2:\n", " cursor = 'crosshair';\n", " break;\n", " case 3:\n", " cursor = 'move';\n", " break;\n", " }\n", " fig.rubberband_canvas.style.cursor = cursor;\n", "};\n", "\n", "mpl.figure.prototype.handle_message = function (fig, msg) {\n", " fig.message.textContent = msg['message'];\n", "};\n", "\n", "mpl.figure.prototype.handle_draw = function (fig, _msg) {\n", " // Request the server to send over a new figure.\n", " fig.send_draw_message();\n", "};\n", "\n", "mpl.figure.prototype.handle_image_mode = function (fig, msg) {\n", " fig.image_mode = msg['mode'];\n", "};\n", "\n", "mpl.figure.prototype.handle_history_buttons = function (fig, msg) {\n", " for (var key in msg) {\n", " if (!(key in fig.buttons)) {\n", " continue;\n", " }\n", " fig.buttons[key].disabled = !msg[key];\n", " fig.buttons[key].setAttribute('aria-disabled', !msg[key]);\n", " }\n", "};\n", "\n", "mpl.figure.prototype.handle_navigate_mode = function (fig, msg) {\n", " if (msg['mode'] === 'PAN') {\n", " fig.buttons['Pan'].classList.add('active');\n", " fig.buttons['Zoom'].classList.remove('active');\n", " } else if (msg['mode'] === 'ZOOM') {\n", " fig.buttons['Pan'].classList.remove('active');\n", " fig.buttons['Zoom'].classList.add('active');\n", " } else {\n", " fig.buttons['Pan'].classList.remove('active');\n", " fig.buttons['Zoom'].classList.remove('active');\n", " }\n", "};\n", "\n", "mpl.figure.prototype.updated_canvas_event = function () {\n", " // Called whenever the canvas gets updated.\n", " this.send_message('ack', {});\n", "};\n", "\n", "// A function to construct a web socket function for onmessage handling.\n", "// Called in the figure constructor.\n", "mpl.figure.prototype._make_on_message_function = function (fig) {\n", " return function socket_on_message(evt) {\n", " if (evt.data instanceof Blob) {\n", " var img = evt.data;\n", " if (img.type !== 'image/png') {\n", " /* FIXME: We get \"Resource interpreted as Image but\n", " * transferred with MIME type text/plain:\" errors on\n", " * Chrome. But how to set the MIME type? It doesn't seem\n", " * to be part of the websocket stream */\n", " img.type = 'image/png';\n", " }\n", "\n", " /* Free the memory for the previous frames */\n", " if (fig.imageObj.src) {\n", " (window.URL || window.webkitURL).revokeObjectURL(\n", " fig.imageObj.src\n", " );\n", " }\n", "\n", " fig.imageObj.src = (window.URL || window.webkitURL).createObjectURL(\n", " img\n", " );\n", " fig.updated_canvas_event();\n", " fig.waiting = false;\n", " return;\n", " } else if (\n", " typeof evt.data === 'string' &&\n", " evt.data.slice(0, 21) === 'data:image/png;base64'\n", " ) {\n", " fig.imageObj.src = evt.data;\n", " fig.updated_canvas_event();\n", " fig.waiting = false;\n", " return;\n", " }\n", "\n", " var msg = JSON.parse(evt.data);\n", " var msg_type = msg['type'];\n", "\n", " // Call the \"handle_{type}\" callback, which takes\n", " // the figure and JSON message as its only arguments.\n", " try {\n", " var callback = fig['handle_' + msg_type];\n", " } catch (e) {\n", " console.log(\n", " \"No handler for the '\" + msg_type + \"' message type: \",\n", " msg\n", " );\n", " return;\n", " }\n", "\n", " if (callback) {\n", " try {\n", " // console.log(\"Handling '\" + msg_type + \"' message: \", msg);\n", " callback(fig, msg);\n", " } catch (e) {\n", " console.log(\n", " \"Exception inside the 'handler_\" + msg_type + \"' callback:\",\n", " e,\n", " e.stack,\n", " msg\n", " );\n", " }\n", " }\n", " };\n", "};\n", "\n", "// from http://stackoverflow.com/questions/1114465/getting-mouse-location-in-canvas\n", "mpl.findpos = function (e) {\n", " //this section is from http://www.quirksmode.org/js/events_properties.html\n", " var targ;\n", " if (!e) {\n", " e = window.event;\n", " }\n", " if (e.target) {\n", " targ = e.target;\n", " } else if (e.srcElement) {\n", " targ = e.srcElement;\n", " }\n", " if (targ.nodeType === 3) {\n", " // defeat Safari bug\n", " targ = targ.parentNode;\n", " }\n", "\n", " // pageX,Y are the mouse positions relative to the document\n", " var boundingRect = targ.getBoundingClientRect();\n", " var x = e.pageX - (boundingRect.left + document.body.scrollLeft);\n", " var y = e.pageY - (boundingRect.top + document.body.scrollTop);\n", "\n", " return { x: x, y: y };\n", "};\n", "\n", "/*\n", " * return a copy of an object with only non-object keys\n", " * we need this to avoid circular references\n", " * http://stackoverflow.com/a/24161582/3208463\n", " */\n", "function simpleKeys(original) {\n", " return Object.keys(original).reduce(function (obj, key) {\n", " if (typeof original[key] !== 'object') {\n", " obj[key] = original[key];\n", " }\n", " return obj;\n", " }, {});\n", "}\n", "\n", "mpl.figure.prototype.mouse_event = function (event, name) {\n", " var canvas_pos = mpl.findpos(event);\n", "\n", " if (name === 'button_press') {\n", " this.canvas.focus();\n", " this.canvas_div.focus();\n", " }\n", "\n", " var x = canvas_pos.x * this.ratio;\n", " var y = canvas_pos.y * this.ratio;\n", "\n", " this.send_message(name, {\n", " x: x,\n", " y: y,\n", " button: event.button,\n", " step: event.step,\n", " guiEvent: simpleKeys(event),\n", " });\n", "\n", " /* This prevents the web browser from automatically changing to\n", " * the text insertion cursor when the button is pressed. We want\n", " * to control all of the cursor setting manually through the\n", " * 'cursor' event from matplotlib */\n", " event.preventDefault();\n", " return false;\n", "};\n", "\n", "mpl.figure.prototype._key_event_extra = function (_event, _name) {\n", " // Handle any extra behaviour associated with a key event\n", "};\n", "\n", "mpl.figure.prototype.key_event = function (event, name) {\n", " // Prevent repeat events\n", " if (name === 'key_press') {\n", " if (event.key === this._key) {\n", " return;\n", " } else {\n", " this._key = event.key;\n", " }\n", " }\n", " if (name === 'key_release') {\n", " this._key = null;\n", " }\n", "\n", " var value = '';\n", " if (event.ctrlKey && event.key !== 'Control') {\n", " value += 'ctrl+';\n", " }\n", " else if (event.altKey && event.key !== 'Alt') {\n", " value += 'alt+';\n", " }\n", " else if (event.shiftKey && event.key !== 'Shift') {\n", " value += 'shift+';\n", " }\n", "\n", " value += 'k' + event.key;\n", "\n", " this._key_event_extra(event, name);\n", "\n", " this.send_message(name, { key: value, guiEvent: simpleKeys(event) });\n", " return false;\n", "};\n", "\n", "mpl.figure.prototype.toolbar_button_onclick = function (name) {\n", " if (name === 'download') {\n", " this.handle_save(this, null);\n", " } else {\n", " this.send_message('toolbar_button', { name: name });\n", " }\n", "};\n", "\n", "mpl.figure.prototype.toolbar_button_onmouseover = function (tooltip) {\n", " this.message.textContent = tooltip;\n", "};\n", "\n", "///////////////// REMAINING CONTENT GENERATED BY embed_js.py /////////////////\n", "// prettier-ignore\n", "var _JSXTOOLS_RESIZE_OBSERVER=function(A){var t,i=new WeakMap,n=new WeakMap,a=new WeakMap,r=new WeakMap,o=new Set;function s(e){if(!(this instanceof s))throw new TypeError(\"Constructor requires 'new' operator\");i.set(this,e)}function h(){throw new TypeError(\"Function is not a constructor\")}function c(e,t,i,n){e=0 in arguments?Number(arguments[0]):0,t=1 in arguments?Number(arguments[1]):0,i=2 in arguments?Number(arguments[2]):0,n=3 in arguments?Number(arguments[3]):0,this.right=(this.x=this.left=e)+(this.width=i),this.bottom=(this.y=this.top=t)+(this.height=n),Object.freeze(this)}function d(){t=requestAnimationFrame(d);var s=new WeakMap,p=new Set;o.forEach((function(t){r.get(t).forEach((function(i){var r=t instanceof window.SVGElement,o=a.get(t),d=r?0:parseFloat(o.paddingTop),f=r?0:parseFloat(o.paddingRight),l=r?0:parseFloat(o.paddingBottom),u=r?0:parseFloat(o.paddingLeft),g=r?0:parseFloat(o.borderTopWidth),m=r?0:parseFloat(o.borderRightWidth),w=r?0:parseFloat(o.borderBottomWidth),b=u+f,F=d+l,v=(r?0:parseFloat(o.borderLeftWidth))+m,W=g+w,y=r?0:t.offsetHeight-W-t.clientHeight,E=r?0:t.offsetWidth-v-t.clientWidth,R=b+v,z=F+W,M=r?t.width:parseFloat(o.width)-R-E,O=r?t.height:parseFloat(o.height)-z-y;if(n.has(t)){var k=n.get(t);if(k[0]===M&&k[1]===O)return}n.set(t,[M,O]);var S=Object.create(h.prototype);S.target=t,S.contentRect=new c(u,d,M,O),s.has(i)||(s.set(i,[]),p.add(i)),s.get(i).push(S)}))})),p.forEach((function(e){i.get(e).call(e,s.get(e),e)}))}return s.prototype.observe=function(i){if(i instanceof window.Element){r.has(i)||(r.set(i,new Set),o.add(i),a.set(i,window.getComputedStyle(i)));var n=r.get(i);n.has(this)||n.add(this),cancelAnimationFrame(t),t=requestAnimationFrame(d)}},s.prototype.unobserve=function(i){if(i instanceof window.Element&&r.has(i)){var n=r.get(i);n.has(this)&&(n.delete(this),n.size||(r.delete(i),o.delete(i))),n.size||r.delete(i),o.size||cancelAnimationFrame(t)}},A.DOMRectReadOnly=c,A.ResizeObserver=s,A.ResizeObserverEntry=h,A}; // eslint-disable-line\n", "mpl.toolbar_items = [[\"Home\", \"Reset original view\", \"fa fa-home icon-home\", \"home\"], [\"Back\", \"Back to previous view\", \"fa fa-arrow-left icon-arrow-left\", \"back\"], [\"Forward\", \"Forward to next view\", \"fa fa-arrow-right icon-arrow-right\", \"forward\"], [\"\", \"\", \"\", \"\"], [\"Pan\", \"Left button pans, Right button zooms\\nx/y fixes axis, CTRL fixes aspect\", \"fa fa-arrows icon-move\", \"pan\"], [\"Zoom\", \"Zoom to rectangle\\nx/y fixes axis, CTRL fixes aspect\", \"fa fa-square-o icon-check-empty\", \"zoom\"], [\"\", \"\", \"\", \"\"], [\"Download\", \"Download plot\", \"fa fa-floppy-o icon-save\", \"download\"]];\n", "\n", "mpl.extensions = [\"eps\", \"jpeg\", \"pgf\", \"pdf\", \"png\", \"ps\", \"raw\", \"svg\", \"tif\"];\n", "\n", "mpl.default_extension = \"png\";/* global mpl */\n", "\n", "var comm_websocket_adapter = function (comm) {\n", " // Create a \"websocket\"-like object which calls the given IPython comm\n", " // object with the appropriate methods. Currently this is a non binary\n", " // socket, so there is still some room for performance tuning.\n", " var ws = {};\n", "\n", " ws.binaryType = comm.kernel.ws.binaryType;\n", " ws.readyState = comm.kernel.ws.readyState;\n", " function updateReadyState(_event) {\n", " if (comm.kernel.ws) {\n", " ws.readyState = comm.kernel.ws.readyState;\n", " } else {\n", " ws.readyState = 3; // Closed state.\n", " }\n", " }\n", " comm.kernel.ws.addEventListener('open', updateReadyState);\n", " comm.kernel.ws.addEventListener('close', updateReadyState);\n", " comm.kernel.ws.addEventListener('error', updateReadyState);\n", "\n", " ws.close = function () {\n", " comm.close();\n", " };\n", " ws.send = function (m) {\n", " //console.log('sending', m);\n", " comm.send(m);\n", " };\n", " // Register the callback with on_msg.\n", " comm.on_msg(function (msg) {\n", " //console.log('receiving', msg['content']['data'], msg);\n", " var data = msg['content']['data'];\n", " if (data['blob'] !== undefined) {\n", " data = {\n", " data: new Blob(msg['buffers'], { type: data['blob'] }),\n", " };\n", " }\n", " // Pass the mpl event to the overridden (by mpl) onmessage function.\n", " ws.onmessage(data);\n", " });\n", " return ws;\n", "};\n", "\n", "mpl.mpl_figure_comm = function (comm, msg) {\n", " // This is the function which gets called when the mpl process\n", " // starts-up an IPython Comm through the \"matplotlib\" channel.\n", "\n", " var id = msg.content.data.id;\n", " // Get hold of the div created by the display call when the Comm\n", " // socket was opened in Python.\n", " var element = document.getElementById(id);\n", " var ws_proxy = comm_websocket_adapter(comm);\n", "\n", " function ondownload(figure, _format) {\n", " window.open(figure.canvas.toDataURL());\n", " }\n", "\n", " var fig = new mpl.figure(id, ws_proxy, ondownload, element);\n", "\n", " // Call onopen now - mpl needs it, as it is assuming we've passed it a real\n", " // web socket which is closed, not our websocket->open comm proxy.\n", " ws_proxy.onopen();\n", "\n", " fig.parent_element = element;\n", " fig.cell_info = mpl.find_output_cell(\"
\");\n", " if (!fig.cell_info) {\n", " console.error('Failed to find cell for figure', id, fig);\n", " return;\n", " }\n", " fig.cell_info[0].output_area.element.on(\n", " 'cleared',\n", " { fig: fig },\n", " fig._remove_fig_handler\n", " );\n", "};\n", "\n", "mpl.figure.prototype.handle_close = function (fig, msg) {\n", " var width = fig.canvas.width / fig.ratio;\n", " fig.cell_info[0].output_area.element.off(\n", " 'cleared',\n", " fig._remove_fig_handler\n", " );\n", " fig.resizeObserverInstance.unobserve(fig.canvas_div);\n", "\n", " // Update the output cell to use the data from the current canvas.\n", " fig.push_to_output();\n", " var dataURL = fig.canvas.toDataURL();\n", " // Re-enable the keyboard manager in IPython - without this line, in FF,\n", " // the notebook keyboard shortcuts fail.\n", " IPython.keyboard_manager.enable();\n", " fig.parent_element.innerHTML =\n", " '';\n", " fig.close_ws(fig, msg);\n", "};\n", "\n", "mpl.figure.prototype.close_ws = function (fig, msg) {\n", " fig.send_message('closing', msg);\n", " // fig.ws.close()\n", "};\n", "\n", "mpl.figure.prototype.push_to_output = function (_remove_interactive) {\n", " // Turn the data on the canvas into data in the output cell.\n", " var width = this.canvas.width / this.ratio;\n", " var dataURL = this.canvas.toDataURL();\n", " this.cell_info[1]['text/html'] =\n", " '';\n", "};\n", "\n", "mpl.figure.prototype.updated_canvas_event = function () {\n", " // Tell IPython that the notebook contents must change.\n", " IPython.notebook.set_dirty(true);\n", " this.send_message('ack', {});\n", " var fig = this;\n", " // Wait a second, then push the new image to the DOM so\n", " // that it is saved nicely (might be nice to debounce this).\n", " setTimeout(function () {\n", " fig.push_to_output();\n", " }, 1000);\n", "};\n", "\n", "mpl.figure.prototype._init_toolbar = function () {\n", " var fig = this;\n", "\n", " var toolbar = document.createElement('div');\n", " toolbar.classList = 'btn-toolbar';\n", " this.root.appendChild(toolbar);\n", "\n", " function on_click_closure(name) {\n", " return function (_event) {\n", " return fig.toolbar_button_onclick(name);\n", " };\n", " }\n", "\n", " function on_mouseover_closure(tooltip) {\n", " return function (event) {\n", " if (!event.currentTarget.disabled) {\n", " return fig.toolbar_button_onmouseover(tooltip);\n", " }\n", " };\n", " }\n", "\n", " fig.buttons = {};\n", " var buttonGroup = document.createElement('div');\n", " buttonGroup.classList = 'btn-group';\n", " var button;\n", " for (var toolbar_ind in mpl.toolbar_items) {\n", " var name = mpl.toolbar_items[toolbar_ind][0];\n", " var tooltip = mpl.toolbar_items[toolbar_ind][1];\n", " var image = mpl.toolbar_items[toolbar_ind][2];\n", " var method_name = mpl.toolbar_items[toolbar_ind][3];\n", "\n", " if (!name) {\n", " /* Instead of a spacer, we start a new button group. */\n", " if (buttonGroup.hasChildNodes()) {\n", " toolbar.appendChild(buttonGroup);\n", " }\n", " buttonGroup = document.createElement('div');\n", " buttonGroup.classList = 'btn-group';\n", " continue;\n", " }\n", "\n", " button = fig.buttons[name] = document.createElement('button');\n", " button.classList = 'btn btn-default';\n", " button.href = '#';\n", " button.title = name;\n", " button.innerHTML = '';\n", " button.addEventListener('click', on_click_closure(method_name));\n", " button.addEventListener('mouseover', on_mouseover_closure(tooltip));\n", " buttonGroup.appendChild(button);\n", " }\n", "\n", " if (buttonGroup.hasChildNodes()) {\n", " toolbar.appendChild(buttonGroup);\n", " }\n", "\n", " // Add the status bar.\n", " var status_bar = document.createElement('span');\n", " status_bar.classList = 'mpl-message pull-right';\n", " toolbar.appendChild(status_bar);\n", " this.message = status_bar;\n", "\n", " // Add the close button to the window.\n", " var buttongrp = document.createElement('div');\n", " buttongrp.classList = 'btn-group inline pull-right';\n", " button = document.createElement('button');\n", " button.classList = 'btn btn-mini btn-primary';\n", " button.href = '#';\n", " button.title = 'Stop Interaction';\n", " button.innerHTML = '';\n", " button.addEventListener('click', function (_evt) {\n", " fig.handle_close(fig, {});\n", " });\n", " button.addEventListener(\n", " 'mouseover',\n", " on_mouseover_closure('Stop Interaction')\n", " );\n", " buttongrp.appendChild(button);\n", " var titlebar = this.root.querySelector('.ui-dialog-titlebar');\n", " titlebar.insertBefore(buttongrp, titlebar.firstChild);\n", "};\n", "\n", "mpl.figure.prototype._remove_fig_handler = function (event) {\n", " var fig = event.data.fig;\n", " if (event.target !== this) {\n", " // Ignore bubbled events from children.\n", " return;\n", " }\n", " fig.close_ws(fig, {});\n", "};\n", "\n", "mpl.figure.prototype._root_extra_style = function (el) {\n", " el.style.boxSizing = 'content-box'; // override notebook setting of border-box.\n", "};\n", "\n", "mpl.figure.prototype._canvas_extra_style = function (el) {\n", " // this is important to make the div 'focusable\n", " el.setAttribute('tabindex', 0);\n", " // reach out to IPython and tell the keyboard manager to turn it's self\n", " // off when our div gets focus\n", "\n", " // location in version 3\n", " if (IPython.notebook.keyboard_manager) {\n", " IPython.notebook.keyboard_manager.register_events(el);\n", " } else {\n", " // location in version 2\n", " IPython.keyboard_manager.register_events(el);\n", " }\n", "};\n", "\n", "mpl.figure.prototype._key_event_extra = function (event, _name) {\n", " var manager = IPython.notebook.keyboard_manager;\n", " if (!manager) {\n", " manager = IPython.keyboard_manager;\n", " }\n", "\n", " // Check for shift+enter\n", " if (event.shiftKey && event.which === 13) {\n", " this.canvas_div.blur();\n", " // select the cell after this one\n", " var index = IPython.notebook.find_cell_index(this.cell_info[0]);\n", " IPython.notebook.select(index + 1);\n", " }\n", "};\n", "\n", "mpl.figure.prototype.handle_save = function (fig, _msg) {\n", " fig.ondownload(fig, null);\n", "};\n", "\n", "mpl.find_output_cell = function (html_output) {\n", " // Return the cell and output element which can be found *uniquely* in the notebook.\n", " // Note - this is a bit hacky, but it is done because the \"notebook_saving.Notebook\"\n", " // IPython event is triggered only after the cells have been serialised, which for\n", " // our purposes (turning an active figure into a static one), is too late.\n", " var cells = IPython.notebook.get_cells();\n", " var ncells = cells.length;\n", " for (var i = 0; i < ncells; i++) {\n", " var cell = cells[i];\n", " if (cell.cell_type === 'code') {\n", " for (var j = 0; j < cell.output_area.outputs.length; j++) {\n", " var data = cell.output_area.outputs[j];\n", " if (data.data) {\n", " // IPython >= 3 moved mimebundle to data attribute of output\n", " data = data.data;\n", " }\n", " if (data['text/html'] === html_output) {\n", " return [cell, data, j];\n", " }\n", " }\n", " }\n", " }\n", "};\n", "\n", "// Register the function which deals with the matplotlib target/channel.\n", "// The kernel may be null if the page has been refreshed.\n", "if (IPython.notebook.kernel !== null) {\n", " IPython.notebook.kernel.comm_manager.register_target(\n", " 'matplotlib',\n", " mpl.mpl_figure_comm\n", " );\n", "}\n" ], "text/plain": [ "" ] }, "metadata": {}, "output_type": "display_data" }, { "data": { "text/html": [ "" ], "text/plain": [ "" ] }, "metadata": {}, "output_type": "display_data" }, { "data": { "text/plain": [ "" ] }, "execution_count": 326, "metadata": {}, "output_type": "execute_result" } ], "source": [ "# t = [mpii['act'][v[1]]['act_id'][0][0] for v in vectors]\n", "t = [cats.index(get_catname(mpii['act'][v[1]])) for v in vectors]\n", "\n", "fig = plt.figure()\n", "ax = fig.add_subplot(1,3,(1,2))\n", "ax_figures = fig.add_subplot(1,3,3)\n", "\n", "kdtree= scipy.spatial.KDTree(principalComponents[:,[0,1]]) # CHOICE only consider first two dimensions...\n", "\n", "\n", "def show_closest(event):\n", " closest = kdtree.query([event.xdata, event.ydata], workers=-1)\n", " ax_figures.clear()\n", " \n", " distance, vector_idx = closest\n", " annotation_idx = vectors[vector_idx][1]\n", " points_idx = vectors[vector_idx][2]\n", " anno_points = np.array(list(vector_points[points_idx].values()))\n", " ax_figures.plot(anno_points[:,0], anno_points[:,1]*-1, 'o')\n", " \n", " for bone in bones:\n", " if not all([bone[0] in vector_points[points_idx], bone[1] in vector_points[points_idx]]):\n", " continue\n", " ax_figures.plot([vector_points[points_idx][bone[0]][0], vector_points[points_idx][bone[1]][0]], [-vector_points[points_idx][bone[0]][1], -vector_points[points_idx][bone[1]][1]], color=bone[2])\n", "\n", "# ax_figures.plot(np.random.rand(10))\n", "\n", "\n", "def onclick(event):\n", " show_closest(event)\n", "\n", "processing = Lock()\n", "def onmove(event):\n", " if not event.xdata or not event.ydata:\n", " return\n", " \n", " if processing.acquire(blocking=False):\n", " try:\n", " show_closest(event)\n", " finally:\n", " processing.release()\n", " \n", "\n", "cid = fig.canvas.mpl_connect('motion_notify_event', onmove)\n", "cid = fig.canvas.mpl_connect('button_press_event', onclick)\n", "\n", "ax.scatter(principalComponents[:,0], principalComponents[:,1], c=t, cmap=\"viridis\",alpha=.3, marker='x', linewidth=.5)\n", "\n" ] }, { "cell_type": "code", "execution_count": 308, "id": "618794d1", "metadata": {}, "outputs": [ { "data": { "text/plain": [ "(17486, 17486)" ] }, "execution_count": 308, "metadata": {}, "output_type": "execute_result" } ], "source": [ "closest[1], closest_old[1]" ] }, { "cell_type": "code", "execution_count": 321, "id": "660af381", "metadata": {}, "outputs": [ { "data": { "text/plain": [ "[[18.323586368221292,\n", " 20.398791852918905,\n", " 4.987799082127334,\n", " 0.40959807507019436,\n", " 12.429795844069234,\n", " 16.96079790482587,\n", " 5.768238809731072,\n", " 3.234665667673784,\n", " 2.764261279400277,\n", " 28.889880637691366,\n", " 6.257973328552808,\n", " 0.954098950748553,\n", " 11.425504813879593,\n", " 30.728268671099034,\n", " 7.83325651958665e-12],\n", " 579,\n", " 379]" ] }, "execution_count": 321, "metadata": {}, "output_type": "execute_result" } ], "source": [ "import random" ] }, { "cell_type": "code", "execution_count": null, "id": "33a24435", "metadata": { "scrolled": true }, "outputs": [], "source": [ "nr = random.randint(0, len(vectors)-1)\n", "\n", "imgidx = vectors[nr][1]\n", "principalComponents[nr]\n", "# vectors[6]\n", "annotations = mpii['annolist'][0,imgidx]\n", "anno_file = str(annotations[0]['name'][0][0][0])\n", "filename = '/home/ruben/Documents/Projecten/2020/Security Vision/tryouts/MPII Human Pose Dataset/images/'+anno_file\n", "print(mpii['act'][imgidx]['cat_name'],\n", "mpii['act'][imgidx]['act_name'], mpii['act'][imgidx]['act_id'],\n", "int(mpii['act'][imgidx]['act_id'][0][0])\n", " )\n", "Image.open(filename)" ] }, { "cell_type": "code", "execution_count": null, "id": "c02cb9d3", "metadata": {}, "outputs": [], "source": [] } ], "metadata": { "kernelspec": { "display_name": "mpii", "language": "python", "name": "mpii" }, "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.9.2" } }, "nbformat": 4, "nbformat_minor": 5 }