Created
March 24, 2020 11:31
-
-
Save Jhsmit/72811eec4e48b4948cb4918b5bbd582a to your computer and use it in GitHub Desktop.
blog cell radius
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
{ | |
"cells": [ | |
{ | |
"cell_type": "markdown", | |
"metadata": {}, | |
"source": [ | |
"# Cell radius and width\n", | |
"How to extract a cell's radius from brightfield images." | |
] | |
}, | |
{ | |
"cell_type": "code", | |
"execution_count": 1, | |
"metadata": {}, | |
"outputs": [ | |
{ | |
"name": "stdout", | |
"output_type": "stream", | |
"text": [ | |
"Warning: Version number not found.\n" | |
] | |
} | |
], | |
"source": [ | |
"%matplotlib notebook\n", | |
"import matplotlib.pyplot as plt\n", | |
"from colicoords import load, CellPlot, Cell\n", | |
"import colicoords.config as config" | |
] | |
}, | |
{ | |
"cell_type": "markdown", | |
"metadata": {}, | |
"source": [ | |
"First we load some cell objects:" | |
] | |
}, | |
{ | |
"cell_type": "code", | |
"execution_count": 2, | |
"metadata": {}, | |
"outputs": [ | |
{ | |
"data": { | |
"text/plain": [ | |
"76" | |
] | |
}, | |
"execution_count": 2, | |
"metadata": {}, | |
"output_type": "execute_result" | |
} | |
], | |
"source": [ | |
"cells = load('test_cells.hdf5')\n", | |
"len(cells)" | |
] | |
}, | |
{ | |
"cell_type": "code", | |
"execution_count": 3, | |
"metadata": {}, | |
"outputs": [ | |
{ | |
"name": "stdout", | |
"output_type": "stream", | |
"text": [ | |
"<colicoords.cell.Cell object at 0x0000020FF7FD0278>\n" | |
] | |
} | |
], | |
"source": [ | |
"cell = Cell(cells[16].data)\n", | |
"print(cell)" | |
] | |
}, | |
{ | |
"cell_type": "markdown", | |
"metadata": {}, | |
"source": [ | |
"Lets have a look at one of the cells. Here I've chosen cell 16 and re-initialized a new cell object from its data object. The coordinate system is for the newly initialized cell is based on initial guesses from the binary image. This includes the initial guess for the radius of the cell." | |
] | |
}, | |
{ | |
"cell_type": "code", | |
"execution_count": 4, | |
"metadata": {}, | |
"outputs": [ | |
{ | |
"data": { | |
"application/javascript": [ | |
"/* Put everything inside the global mpl namespace */\n", | |
"window.mpl = {};\n", | |
"\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('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", | |
"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 = $('<div/>');\n", | |
" this._root_extra_style(this.root)\n", | |
" this.root.attr('style', 'display: inline-block');\n", | |
"\n", | |
" $(parent_element).append(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 (mpl.ratio != 1) {\n", | |
" fig.send_message(\"set_dpi_ratio\", {'dpi_ratio': mpl.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 = $(\n", | |
" '<div class=\"ui-dialog-titlebar ui-widget-header ui-corner-all ' +\n", | |
" 'ui-helper-clearfix\"/>');\n", | |
" var titletext = $(\n", | |
" '<div class=\"ui-dialog-title\" style=\"width: 100%; ' +\n", | |
" 'text-align: center; padding: 3px;\"/>');\n", | |
" titlebar.append(titletext)\n", | |
" this.root.append(titlebar);\n", | |
" this.header = titletext[0];\n", | |
"}\n", | |
"\n", | |
"\n", | |
"\n", | |
"mpl.figure.prototype._canvas_extra_style = function(canvas_div) {\n", | |
"\n", | |
"}\n", | |
"\n", | |
"\n", | |
"mpl.figure.prototype._root_extra_style = function(canvas_div) {\n", | |
"\n", | |
"}\n", | |
"\n", | |
"mpl.figure.prototype._init_canvas = function() {\n", | |
" var fig = this;\n", | |
"\n", | |
" var canvas_div = $('<div/>');\n", | |
"\n", | |
" canvas_div.attr('style', 'position: relative; clear: both; outline: 0');\n", | |
"\n", | |
" function canvas_keyboard_event(event) {\n", | |
" return fig.key_event(event, event['data']);\n", | |
" }\n", | |
"\n", | |
" canvas_div.keydown('key_press', canvas_keyboard_event);\n", | |
" canvas_div.keyup('key_release', canvas_keyboard_event);\n", | |
" this.canvas_div = canvas_div\n", | |
" this._canvas_extra_style(canvas_div)\n", | |
" this.root.append(canvas_div);\n", | |
"\n", | |
" var canvas = $('<canvas/>');\n", | |
" canvas.addClass('mpl-canvas');\n", | |
" canvas.attr('style', \"left: 0; top: 0; z-index: 0; outline: 0\")\n", | |
"\n", | |
" this.canvas = canvas[0];\n", | |
" this.context = canvas[0].getContext(\"2d\");\n", | |
"\n", | |
" var backingStore = this.context.backingStorePixelRatio ||\n", | |
"\tthis.context.webkitBackingStorePixelRatio ||\n", | |
"\tthis.context.mozBackingStorePixelRatio ||\n", | |
"\tthis.context.msBackingStorePixelRatio ||\n", | |
"\tthis.context.oBackingStorePixelRatio ||\n", | |
"\tthis.context.backingStorePixelRatio || 1;\n", | |
"\n", | |
" mpl.ratio = (window.devicePixelRatio || 1) / backingStore;\n", | |
"\n", | |
" var rubberband = $('<canvas/>');\n", | |
" rubberband.attr('style', \"position: absolute; left: 0; top: 0; z-index: 1;\")\n", | |
"\n", | |
" var pass_mouse_events = true;\n", | |
"\n", | |
" canvas_div.resizable({\n", | |
" start: function(event, ui) {\n", | |
" pass_mouse_events = false;\n", | |
" },\n", | |
" resize: function(event, ui) {\n", | |
" fig.request_resize(ui.size.width, ui.size.height);\n", | |
" },\n", | |
" stop: function(event, ui) {\n", | |
" pass_mouse_events = true;\n", | |
" fig.request_resize(ui.size.width, ui.size.height);\n", | |
" },\n", | |
" });\n", | |
"\n", | |
" function mouse_event_fn(event) {\n", | |
" if (pass_mouse_events)\n", | |
" return fig.mouse_event(event, event['data']);\n", | |
" }\n", | |
"\n", | |
" rubberband.mousedown('button_press', mouse_event_fn);\n", | |
" rubberband.mouseup('button_release', mouse_event_fn);\n", | |
" // Throttle sequential mouse events to 1 every 20ms.\n", | |
" rubberband.mousemove('motion_notify', mouse_event_fn);\n", | |
"\n", | |
" rubberband.mouseenter('figure_enter', mouse_event_fn);\n", | |
" rubberband.mouseleave('figure_leave', mouse_event_fn);\n", | |
"\n", | |
" canvas_div.on(\"wheel\", function (event) {\n", | |
" event = event.originalEvent;\n", | |
" event['data'] = 'scroll'\n", | |
" if (event.deltaY < 0) {\n", | |
" event.step = 1;\n", | |
" } else {\n", | |
" event.step = -1;\n", | |
" }\n", | |
" mouse_event_fn(event);\n", | |
" });\n", | |
"\n", | |
" canvas_div.append(canvas);\n", | |
" canvas_div.append(rubberband);\n", | |
"\n", | |
" this.rubberband = rubberband;\n", | |
" this.rubberband_canvas = rubberband[0];\n", | |
" this.rubberband_context = rubberband[0].getContext(\"2d\");\n", | |
" this.rubberband_context.strokeStyle = \"#000000\";\n", | |
"\n", | |
" this._resize_canvas = function(width, height) {\n", | |
" // Keep the size of the canvas, canvas container, and rubber band\n", | |
" // canvas in synch.\n", | |
" canvas_div.css('width', width)\n", | |
" canvas_div.css('height', height)\n", | |
"\n", | |
" canvas.attr('width', width * mpl.ratio);\n", | |
" canvas.attr('height', height * mpl.ratio);\n", | |
" canvas.attr('style', 'width: ' + width + 'px; height: ' + height + 'px;');\n", | |
"\n", | |
" rubberband.attr('width', width);\n", | |
" rubberband.attr('height', height);\n", | |
" }\n", | |
"\n", | |
" // Set the figure to an initial 600x600px, this will subsequently be updated\n", | |
" // upon first draw.\n", | |
" this._resize_canvas(600, 600);\n", | |
"\n", | |
" // Disable right mouse context menu.\n", | |
" $(this.rubberband_canvas).bind(\"contextmenu\",function(e){\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 nav_element = $('<div/>');\n", | |
" nav_element.attr('style', 'width: 100%');\n", | |
" this.root.append(nav_element);\n", | |
"\n", | |
" // Define a callback function for later on.\n", | |
" function toolbar_event(event) {\n", | |
" return fig.toolbar_button_onclick(event['data']);\n", | |
" }\n", | |
" function toolbar_mouse_event(event) {\n", | |
" return fig.toolbar_button_onmouseover(event['data']);\n", | |
" }\n", | |
"\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", | |
" // put a spacer in here.\n", | |
" continue;\n", | |
" }\n", | |
" var button = $('<button/>');\n", | |
" button.addClass('ui-button ui-widget ui-state-default ui-corner-all ' +\n", | |
" 'ui-button-icon-only');\n", | |
" button.attr('role', 'button');\n", | |
" button.attr('aria-disabled', 'false');\n", | |
" button.click(method_name, toolbar_event);\n", | |
" button.mouseover(tooltip, toolbar_mouse_event);\n", | |
"\n", | |
" var icon_img = $('<span/>');\n", | |
" icon_img.addClass('ui-button-icon-primary ui-icon');\n", | |
" icon_img.addClass(image);\n", | |
" icon_img.addClass('ui-corner-all');\n", | |
"\n", | |
" var tooltip_span = $('<span/>');\n", | |
" tooltip_span.addClass('ui-button-text');\n", | |
" tooltip_span.html(tooltip);\n", | |
"\n", | |
" button.append(icon_img);\n", | |
" button.append(tooltip_span);\n", | |
"\n", | |
" nav_element.append(button);\n", | |
" }\n", | |
"\n", | |
" var fmt_picker_span = $('<span/>');\n", | |
"\n", | |
" var fmt_picker = $('<select/>');\n", | |
" fmt_picker.addClass('mpl-toolbar-option ui-widget ui-widget-content');\n", | |
" fmt_picker_span.append(fmt_picker);\n", | |
" nav_element.append(fmt_picker_span);\n", | |
" this.format_dropdown = fmt_picker[0];\n", | |
"\n", | |
" for (var ind in mpl.extensions) {\n", | |
" var fmt = mpl.extensions[ind];\n", | |
" var option = $(\n", | |
" '<option/>', {selected: fmt === mpl.default_extension}).html(fmt);\n", | |
" fmt_picker.append(option);\n", | |
" }\n", | |
"\n", | |
" // Add hover states to the ui-buttons\n", | |
" $( \".ui-button\" ).hover(\n", | |
" function() { $(this).addClass(\"ui-state-hover\");},\n", | |
" function() { $(this).removeClass(\"ui-state-hover\");}\n", | |
" );\n", | |
"\n", | |
" var status_bar = $('<span class=\"mpl-message\"/>');\n", | |
" nav_element.append(status_bar);\n", | |
" this.message = status_bar[0];\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", | |
"\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", | |
"\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]);\n", | |
" fig.send_message(\"refresh\", {});\n", | |
" };\n", | |
"}\n", | |
"\n", | |
"mpl.figure.prototype.handle_rubberband = function(fig, msg) {\n", | |
" var x0 = msg['x0'] / mpl.ratio;\n", | |
" var y0 = (fig.canvas.height - msg['y0']) / mpl.ratio;\n", | |
" var x1 = msg['x1'] / mpl.ratio;\n", | |
" var y1 = (fig.canvas.height - msg['y1']) / mpl.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, 0, fig.canvas.width / mpl.ratio, fig.canvas.height / mpl.ratio);\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", | |
" {\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.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", | |
" /* 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", | |
" evt.data.type = \"image/png\";\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", | |
" fig.imageObj.src = (window.URL || window.webkitURL).createObjectURL(\n", | |
" evt.data);\n", | |
" fig.updated_canvas_event();\n", | |
" fig.waiting = false;\n", | |
" return;\n", | |
" }\n", | |
" else if (typeof evt.data === 'string' && evt.data.slice(0, 21) == \"data:image/png;base64\") {\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(\"No handler for the '\" + msg_type + \"' message type: \", msg);\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(\"Exception inside the 'handler_\" + msg_type + \"' callback:\", e, e.stack, msg);\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", | |
" if (e.target)\n", | |
" targ = e.target;\n", | |
" else if (e.srcElement)\n", | |
" targ = e.srcElement;\n", | |
" if (targ.nodeType == 3) // defeat Safari bug\n", | |
" targ = targ.parentNode;\n", | |
"\n", | |
" // jQuery normalizes the pageX and pageY\n", | |
" // pageX,Y are the mouse positions relative to the document\n", | |
" // offset() returns the position of the element relative to the document\n", | |
" var x = e.pageX - $(targ).offset().left;\n", | |
" var y = e.pageY - $(targ).offset().top;\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", | |
" 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", | |
" {\n", | |
" this.canvas.focus();\n", | |
" this.canvas_div.focus();\n", | |
" }\n", | |
"\n", | |
" var x = canvas_pos.x * mpl.ratio;\n", | |
" var y = canvas_pos.y * mpl.ratio;\n", | |
"\n", | |
" this.send_message(name, {x: x, y: y, button: event.button,\n", | |
" step: event.step,\n", | |
" guiEvent: simpleKeys(event)});\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", | |
"\n", | |
" // Prevent repeat events\n", | |
" if (name == 'key_press')\n", | |
" {\n", | |
" if (event.which === this._key)\n", | |
" return;\n", | |
" else\n", | |
" this._key = event.which;\n", | |
" }\n", | |
" if (name == 'key_release')\n", | |
" this._key = null;\n", | |
"\n", | |
" var value = '';\n", | |
" if (event.ctrlKey && event.which != 17)\n", | |
" value += \"ctrl+\";\n", | |
" if (event.altKey && event.which != 18)\n", | |
" value += \"alt+\";\n", | |
" if (event.shiftKey && event.which != 16)\n", | |
" value += \"shift+\";\n", | |
"\n", | |
" value += 'k';\n", | |
" value += event.which.toString();\n", | |
"\n", | |
" this._key_event_extra(event, name);\n", | |
"\n", | |
" this.send_message(name, {key: value,\n", | |
" 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", | |
"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\", \"Pan axes with left mouse, zoom with right\", \"fa fa-arrows icon-move\", \"pan\"], [\"Zoom\", \"Zoom to rectangle\", \"fa fa-square-o icon-check-empty\", \"zoom\"], [\"\", \"\", \"\", \"\"], [\"Download\", \"Download plot\", \"fa fa-floppy-o icon-save\", \"download\"]];\n", | |
"\n", | |
"mpl.extensions = [\"eps\", \"pdf\", \"png\", \"ps\", \"raw\", \"svg\"];\n", | |
"\n", | |
"mpl.default_extension = \"png\";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.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", | |
" // Pass the mpl event to the overridden (by mpl) onmessage function.\n", | |
" ws.onmessage(msg['content']['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 = $(\"#\" + id);\n", | |
" var ws_proxy = comm_websocket_adapter(comm)\n", | |
"\n", | |
" function ondownload(figure, format) {\n", | |
" window.open(figure.imageObj.src);\n", | |
" }\n", | |
"\n", | |
" var fig = new mpl.figure(id, ws_proxy,\n", | |
" ondownload,\n", | |
" element.get(0));\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.get(0);\n", | |
" fig.cell_info = mpl.find_output_cell(\"<div id='\" + id + \"'></div>\");\n", | |
" if (!fig.cell_info) {\n", | |
" console.error(\"Failed to find cell for figure\", id, fig);\n", | |
" return;\n", | |
" }\n", | |
"\n", | |
" var output_index = fig.cell_info[2]\n", | |
" var cell = fig.cell_info[0];\n", | |
"\n", | |
"};\n", | |
"\n", | |
"mpl.figure.prototype.handle_close = function(fig, msg) {\n", | |
" var width = fig.canvas.width/mpl.ratio\n", | |
" fig.root.unbind('remove')\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).html('<img src=\"' + dataURL + '\" width=\"' + width + '\">');\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/mpl.ratio\n", | |
" var dataURL = this.canvas.toDataURL();\n", | |
" this.cell_info[1]['text/html'] = '<img src=\"' + dataURL + '\" width=\"' + width + '\">';\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 () { fig.push_to_output() }, 1000);\n", | |
"}\n", | |
"\n", | |
"mpl.figure.prototype._init_toolbar = function() {\n", | |
" var fig = this;\n", | |
"\n", | |
" var nav_element = $('<div/>');\n", | |
" nav_element.attr('style', 'width: 100%');\n", | |
" this.root.append(nav_element);\n", | |
"\n", | |
" // Define a callback function for later on.\n", | |
" function toolbar_event(event) {\n", | |
" return fig.toolbar_button_onclick(event['data']);\n", | |
" }\n", | |
" function toolbar_mouse_event(event) {\n", | |
" return fig.toolbar_button_onmouseover(event['data']);\n", | |
" }\n", | |
"\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) { continue; };\n", | |
"\n", | |
" var button = $('<button class=\"btn btn-default\" href=\"#\" title=\"' + name + '\"><i class=\"fa ' + image + ' fa-lg\"></i></button>');\n", | |
" button.click(method_name, toolbar_event);\n", | |
" button.mouseover(tooltip, toolbar_mouse_event);\n", | |
" nav_element.append(button);\n", | |
" }\n", | |
"\n", | |
" // Add the status bar.\n", | |
" var status_bar = $('<span class=\"mpl-message\" style=\"text-align:right; float: right;\"/>');\n", | |
" nav_element.append(status_bar);\n", | |
" this.message = status_bar[0];\n", | |
"\n", | |
" // Add the close button to the window.\n", | |
" var buttongrp = $('<div class=\"btn-group inline pull-right\"></div>');\n", | |
" var button = $('<button class=\"btn btn-mini btn-primary\" href=\"#\" title=\"Stop Interaction\"><i class=\"fa fa-power-off icon-remove icon-large\"></i></button>');\n", | |
" button.click(function (evt) { fig.handle_close(fig, {}); } );\n", | |
" button.mouseover('Stop Interaction', toolbar_mouse_event);\n", | |
" buttongrp.append(button);\n", | |
" var titlebar = this.root.find($('.ui-dialog-titlebar'));\n", | |
" titlebar.prepend(buttongrp);\n", | |
"}\n", | |
"\n", | |
"mpl.figure.prototype._root_extra_style = function(el){\n", | |
" var fig = this\n", | |
" el.on(\"remove\", function(){\n", | |
"\tfig.close_ws(fig, {});\n", | |
" });\n", | |
"}\n", | |
"\n", | |
"mpl.figure.prototype._canvas_extra_style = function(el){\n", | |
" // this is important to make the div 'focusable\n", | |
" el.attr('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", | |
" }\n", | |
" else {\n", | |
" // location in version 2\n", | |
" IPython.keyboard_manager.register_events(el);\n", | |
" }\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", | |
" // 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", | |
"\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('matplotlib', mpl.mpl_figure_comm);\n", | |
"}\n" | |
], | |
"text/plain": [ | |
"<IPython.core.display.Javascript object>" | |
] | |
}, | |
"metadata": {}, | |
"output_type": "display_data" | |
}, | |
{ | |
"data": { | |
"text/html": [ | |
"<img src=\"data:image/png;base64,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\" width=\"600\">" | |
], | |
"text/plain": [ | |
"<IPython.core.display.HTML object>" | |
] | |
}, | |
"metadata": {}, | |
"output_type": "display_data" | |
}, | |
{ | |
"data": { | |
"text/plain": [ | |
"<matplotlib.lines.Line2D at 0x20ff82ca2e8>" | |
] | |
}, | |
"execution_count": 4, | |
"metadata": {}, | |
"output_type": "execute_result" | |
} | |
], | |
"source": [ | |
"fig, (ax1, ax2) = plt.subplots(1,2, figsize=(6, 3))\n", | |
"cp = CellPlot(cell)\n", | |
"cp.imshow('binary', ax=ax1)\n", | |
"cp.plot_outline(ax=ax1)\n", | |
"\n", | |
"cp.imshow('brightfield', ax=ax2)\n", | |
"cp.plot_outline(ax=ax2)" | |
] | |
}, | |
{ | |
"cell_type": "markdown", | |
"metadata": {}, | |
"source": [ | |
"We'll optimize the coordinate system based on the brightfield image. This procedure will make the midline of the cell more closely describe the actual cell, but the value for the cell's radius isnt optimized in this procedure." | |
] | |
}, | |
{ | |
"cell_type": "code", | |
"execution_count": 5, | |
"metadata": {}, | |
"outputs": [ | |
{ | |
"data": { | |
"application/javascript": [ | |
"/* Put everything inside the global mpl namespace */\n", | |
"window.mpl = {};\n", | |
"\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('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", | |
"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 = $('<div/>');\n", | |
" this._root_extra_style(this.root)\n", | |
" this.root.attr('style', 'display: inline-block');\n", | |
"\n", | |
" $(parent_element).append(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 (mpl.ratio != 1) {\n", | |
" fig.send_message(\"set_dpi_ratio\", {'dpi_ratio': mpl.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 = $(\n", | |
" '<div class=\"ui-dialog-titlebar ui-widget-header ui-corner-all ' +\n", | |
" 'ui-helper-clearfix\"/>');\n", | |
" var titletext = $(\n", | |
" '<div class=\"ui-dialog-title\" style=\"width: 100%; ' +\n", | |
" 'text-align: center; padding: 3px;\"/>');\n", | |
" titlebar.append(titletext)\n", | |
" this.root.append(titlebar);\n", | |
" this.header = titletext[0];\n", | |
"}\n", | |
"\n", | |
"\n", | |
"\n", | |
"mpl.figure.prototype._canvas_extra_style = function(canvas_div) {\n", | |
"\n", | |
"}\n", | |
"\n", | |
"\n", | |
"mpl.figure.prototype._root_extra_style = function(canvas_div) {\n", | |
"\n", | |
"}\n", | |
"\n", | |
"mpl.figure.prototype._init_canvas = function() {\n", | |
" var fig = this;\n", | |
"\n", | |
" var canvas_div = $('<div/>');\n", | |
"\n", | |
" canvas_div.attr('style', 'position: relative; clear: both; outline: 0');\n", | |
"\n", | |
" function canvas_keyboard_event(event) {\n", | |
" return fig.key_event(event, event['data']);\n", | |
" }\n", | |
"\n", | |
" canvas_div.keydown('key_press', canvas_keyboard_event);\n", | |
" canvas_div.keyup('key_release', canvas_keyboard_event);\n", | |
" this.canvas_div = canvas_div\n", | |
" this._canvas_extra_style(canvas_div)\n", | |
" this.root.append(canvas_div);\n", | |
"\n", | |
" var canvas = $('<canvas/>');\n", | |
" canvas.addClass('mpl-canvas');\n", | |
" canvas.attr('style', \"left: 0; top: 0; z-index: 0; outline: 0\")\n", | |
"\n", | |
" this.canvas = canvas[0];\n", | |
" this.context = canvas[0].getContext(\"2d\");\n", | |
"\n", | |
" var backingStore = this.context.backingStorePixelRatio ||\n", | |
"\tthis.context.webkitBackingStorePixelRatio ||\n", | |
"\tthis.context.mozBackingStorePixelRatio ||\n", | |
"\tthis.context.msBackingStorePixelRatio ||\n", | |
"\tthis.context.oBackingStorePixelRatio ||\n", | |
"\tthis.context.backingStorePixelRatio || 1;\n", | |
"\n", | |
" mpl.ratio = (window.devicePixelRatio || 1) / backingStore;\n", | |
"\n", | |
" var rubberband = $('<canvas/>');\n", | |
" rubberband.attr('style', \"position: absolute; left: 0; top: 0; z-index: 1;\")\n", | |
"\n", | |
" var pass_mouse_events = true;\n", | |
"\n", | |
" canvas_div.resizable({\n", | |
" start: function(event, ui) {\n", | |
" pass_mouse_events = false;\n", | |
" },\n", | |
" resize: function(event, ui) {\n", | |
" fig.request_resize(ui.size.width, ui.size.height);\n", | |
" },\n", | |
" stop: function(event, ui) {\n", | |
" pass_mouse_events = true;\n", | |
" fig.request_resize(ui.size.width, ui.size.height);\n", | |
" },\n", | |
" });\n", | |
"\n", | |
" function mouse_event_fn(event) {\n", | |
" if (pass_mouse_events)\n", | |
" return fig.mouse_event(event, event['data']);\n", | |
" }\n", | |
"\n", | |
" rubberband.mousedown('button_press', mouse_event_fn);\n", | |
" rubberband.mouseup('button_release', mouse_event_fn);\n", | |
" // Throttle sequential mouse events to 1 every 20ms.\n", | |
" rubberband.mousemove('motion_notify', mouse_event_fn);\n", | |
"\n", | |
" rubberband.mouseenter('figure_enter', mouse_event_fn);\n", | |
" rubberband.mouseleave('figure_leave', mouse_event_fn);\n", | |
"\n", | |
" canvas_div.on(\"wheel\", function (event) {\n", | |
" event = event.originalEvent;\n", | |
" event['data'] = 'scroll'\n", | |
" if (event.deltaY < 0) {\n", | |
" event.step = 1;\n", | |
" } else {\n", | |
" event.step = -1;\n", | |
" }\n", | |
" mouse_event_fn(event);\n", | |
" });\n", | |
"\n", | |
" canvas_div.append(canvas);\n", | |
" canvas_div.append(rubberband);\n", | |
"\n", | |
" this.rubberband = rubberband;\n", | |
" this.rubberband_canvas = rubberband[0];\n", | |
" this.rubberband_context = rubberband[0].getContext(\"2d\");\n", | |
" this.rubberband_context.strokeStyle = \"#000000\";\n", | |
"\n", | |
" this._resize_canvas = function(width, height) {\n", | |
" // Keep the size of the canvas, canvas container, and rubber band\n", | |
" // canvas in synch.\n", | |
" canvas_div.css('width', width)\n", | |
" canvas_div.css('height', height)\n", | |
"\n", | |
" canvas.attr('width', width * mpl.ratio);\n", | |
" canvas.attr('height', height * mpl.ratio);\n", | |
" canvas.attr('style', 'width: ' + width + 'px; height: ' + height + 'px;');\n", | |
"\n", | |
" rubberband.attr('width', width);\n", | |
" rubberband.attr('height', height);\n", | |
" }\n", | |
"\n", | |
" // Set the figure to an initial 600x600px, this will subsequently be updated\n", | |
" // upon first draw.\n", | |
" this._resize_canvas(600, 600);\n", | |
"\n", | |
" // Disable right mouse context menu.\n", | |
" $(this.rubberband_canvas).bind(\"contextmenu\",function(e){\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 nav_element = $('<div/>');\n", | |
" nav_element.attr('style', 'width: 100%');\n", | |
" this.root.append(nav_element);\n", | |
"\n", | |
" // Define a callback function for later on.\n", | |
" function toolbar_event(event) {\n", | |
" return fig.toolbar_button_onclick(event['data']);\n", | |
" }\n", | |
" function toolbar_mouse_event(event) {\n", | |
" return fig.toolbar_button_onmouseover(event['data']);\n", | |
" }\n", | |
"\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", | |
" // put a spacer in here.\n", | |
" continue;\n", | |
" }\n", | |
" var button = $('<button/>');\n", | |
" button.addClass('ui-button ui-widget ui-state-default ui-corner-all ' +\n", | |
" 'ui-button-icon-only');\n", | |
" button.attr('role', 'button');\n", | |
" button.attr('aria-disabled', 'false');\n", | |
" button.click(method_name, toolbar_event);\n", | |
" button.mouseover(tooltip, toolbar_mouse_event);\n", | |
"\n", | |
" var icon_img = $('<span/>');\n", | |
" icon_img.addClass('ui-button-icon-primary ui-icon');\n", | |
" icon_img.addClass(image);\n", | |
" icon_img.addClass('ui-corner-all');\n", | |
"\n", | |
" var tooltip_span = $('<span/>');\n", | |
" tooltip_span.addClass('ui-button-text');\n", | |
" tooltip_span.html(tooltip);\n", | |
"\n", | |
" button.append(icon_img);\n", | |
" button.append(tooltip_span);\n", | |
"\n", | |
" nav_element.append(button);\n", | |
" }\n", | |
"\n", | |
" var fmt_picker_span = $('<span/>');\n", | |
"\n", | |
" var fmt_picker = $('<select/>');\n", | |
" fmt_picker.addClass('mpl-toolbar-option ui-widget ui-widget-content');\n", | |
" fmt_picker_span.append(fmt_picker);\n", | |
" nav_element.append(fmt_picker_span);\n", | |
" this.format_dropdown = fmt_picker[0];\n", | |
"\n", | |
" for (var ind in mpl.extensions) {\n", | |
" var fmt = mpl.extensions[ind];\n", | |
" var option = $(\n", | |
" '<option/>', {selected: fmt === mpl.default_extension}).html(fmt);\n", | |
" fmt_picker.append(option);\n", | |
" }\n", | |
"\n", | |
" // Add hover states to the ui-buttons\n", | |
" $( \".ui-button\" ).hover(\n", | |
" function() { $(this).addClass(\"ui-state-hover\");},\n", | |
" function() { $(this).removeClass(\"ui-state-hover\");}\n", | |
" );\n", | |
"\n", | |
" var status_bar = $('<span class=\"mpl-message\"/>');\n", | |
" nav_element.append(status_bar);\n", | |
" this.message = status_bar[0];\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", | |
"\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", | |
"\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]);\n", | |
" fig.send_message(\"refresh\", {});\n", | |
" };\n", | |
"}\n", | |
"\n", | |
"mpl.figure.prototype.handle_rubberband = function(fig, msg) {\n", | |
" var x0 = msg['x0'] / mpl.ratio;\n", | |
" var y0 = (fig.canvas.height - msg['y0']) / mpl.ratio;\n", | |
" var x1 = msg['x1'] / mpl.ratio;\n", | |
" var y1 = (fig.canvas.height - msg['y1']) / mpl.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, 0, fig.canvas.width / mpl.ratio, fig.canvas.height / mpl.ratio);\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", | |
" {\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.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", | |
" /* 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", | |
" evt.data.type = \"image/png\";\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", | |
" fig.imageObj.src = (window.URL || window.webkitURL).createObjectURL(\n", | |
" evt.data);\n", | |
" fig.updated_canvas_event();\n", | |
" fig.waiting = false;\n", | |
" return;\n", | |
" }\n", | |
" else if (typeof evt.data === 'string' && evt.data.slice(0, 21) == \"data:image/png;base64\") {\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(\"No handler for the '\" + msg_type + \"' message type: \", msg);\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(\"Exception inside the 'handler_\" + msg_type + \"' callback:\", e, e.stack, msg);\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", | |
" if (e.target)\n", | |
" targ = e.target;\n", | |
" else if (e.srcElement)\n", | |
" targ = e.srcElement;\n", | |
" if (targ.nodeType == 3) // defeat Safari bug\n", | |
" targ = targ.parentNode;\n", | |
"\n", | |
" // jQuery normalizes the pageX and pageY\n", | |
" // pageX,Y are the mouse positions relative to the document\n", | |
" // offset() returns the position of the element relative to the document\n", | |
" var x = e.pageX - $(targ).offset().left;\n", | |
" var y = e.pageY - $(targ).offset().top;\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", | |
" 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", | |
" {\n", | |
" this.canvas.focus();\n", | |
" this.canvas_div.focus();\n", | |
" }\n", | |
"\n", | |
" var x = canvas_pos.x * mpl.ratio;\n", | |
" var y = canvas_pos.y * mpl.ratio;\n", | |
"\n", | |
" this.send_message(name, {x: x, y: y, button: event.button,\n", | |
" step: event.step,\n", | |
" guiEvent: simpleKeys(event)});\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", | |
"\n", | |
" // Prevent repeat events\n", | |
" if (name == 'key_press')\n", | |
" {\n", | |
" if (event.which === this._key)\n", | |
" return;\n", | |
" else\n", | |
" this._key = event.which;\n", | |
" }\n", | |
" if (name == 'key_release')\n", | |
" this._key = null;\n", | |
"\n", | |
" var value = '';\n", | |
" if (event.ctrlKey && event.which != 17)\n", | |
" value += \"ctrl+\";\n", | |
" if (event.altKey && event.which != 18)\n", | |
" value += \"alt+\";\n", | |
" if (event.shiftKey && event.which != 16)\n", | |
" value += \"shift+\";\n", | |
"\n", | |
" value += 'k';\n", | |
" value += event.which.toString();\n", | |
"\n", | |
" this._key_event_extra(event, name);\n", | |
"\n", | |
" this.send_message(name, {key: value,\n", | |
" 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", | |
"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\", \"Pan axes with left mouse, zoom with right\", \"fa fa-arrows icon-move\", \"pan\"], [\"Zoom\", \"Zoom to rectangle\", \"fa fa-square-o icon-check-empty\", \"zoom\"], [\"\", \"\", \"\", \"\"], [\"Download\", \"Download plot\", \"fa fa-floppy-o icon-save\", \"download\"]];\n", | |
"\n", | |
"mpl.extensions = [\"eps\", \"pdf\", \"png\", \"ps\", \"raw\", \"svg\"];\n", | |
"\n", | |
"mpl.default_extension = \"png\";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.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", | |
" // Pass the mpl event to the overridden (by mpl) onmessage function.\n", | |
" ws.onmessage(msg['content']['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 = $(\"#\" + id);\n", | |
" var ws_proxy = comm_websocket_adapter(comm)\n", | |
"\n", | |
" function ondownload(figure, format) {\n", | |
" window.open(figure.imageObj.src);\n", | |
" }\n", | |
"\n", | |
" var fig = new mpl.figure(id, ws_proxy,\n", | |
" ondownload,\n", | |
" element.get(0));\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.get(0);\n", | |
" fig.cell_info = mpl.find_output_cell(\"<div id='\" + id + \"'></div>\");\n", | |
" if (!fig.cell_info) {\n", | |
" console.error(\"Failed to find cell for figure\", id, fig);\n", | |
" return;\n", | |
" }\n", | |
"\n", | |
" var output_index = fig.cell_info[2]\n", | |
" var cell = fig.cell_info[0];\n", | |
"\n", | |
"};\n", | |
"\n", | |
"mpl.figure.prototype.handle_close = function(fig, msg) {\n", | |
" var width = fig.canvas.width/mpl.ratio\n", | |
" fig.root.unbind('remove')\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).html('<img src=\"' + dataURL + '\" width=\"' + width + '\">');\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/mpl.ratio\n", | |
" var dataURL = this.canvas.toDataURL();\n", | |
" this.cell_info[1]['text/html'] = '<img src=\"' + dataURL + '\" width=\"' + width + '\">';\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 () { fig.push_to_output() }, 1000);\n", | |
"}\n", | |
"\n", | |
"mpl.figure.prototype._init_toolbar = function() {\n", | |
" var fig = this;\n", | |
"\n", | |
" var nav_element = $('<div/>');\n", | |
" nav_element.attr('style', 'width: 100%');\n", | |
" this.root.append(nav_element);\n", | |
"\n", | |
" // Define a callback function for later on.\n", | |
" function toolbar_event(event) {\n", | |
" return fig.toolbar_button_onclick(event['data']);\n", | |
" }\n", | |
" function toolbar_mouse_event(event) {\n", | |
" return fig.toolbar_button_onmouseover(event['data']);\n", | |
" }\n", | |
"\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) { continue; };\n", | |
"\n", | |
" var button = $('<button class=\"btn btn-default\" href=\"#\" title=\"' + name + '\"><i class=\"fa ' + image + ' fa-lg\"></i></button>');\n", | |
" button.click(method_name, toolbar_event);\n", | |
" button.mouseover(tooltip, toolbar_mouse_event);\n", | |
" nav_element.append(button);\n", | |
" }\n", | |
"\n", | |
" // Add the status bar.\n", | |
" var status_bar = $('<span class=\"mpl-message\" style=\"text-align:right; float: right;\"/>');\n", | |
" nav_element.append(status_bar);\n", | |
" this.message = status_bar[0];\n", | |
"\n", | |
" // Add the close button to the window.\n", | |
" var buttongrp = $('<div class=\"btn-group inline pull-right\"></div>');\n", | |
" var button = $('<button class=\"btn btn-mini btn-primary\" href=\"#\" title=\"Stop Interaction\"><i class=\"fa fa-power-off icon-remove icon-large\"></i></button>');\n", | |
" button.click(function (evt) { fig.handle_close(fig, {}); } );\n", | |
" button.mouseover('Stop Interaction', toolbar_mouse_event);\n", | |
" buttongrp.append(button);\n", | |
" var titlebar = this.root.find($('.ui-dialog-titlebar'));\n", | |
" titlebar.prepend(buttongrp);\n", | |
"}\n", | |
"\n", | |
"mpl.figure.prototype._root_extra_style = function(el){\n", | |
" var fig = this\n", | |
" el.on(\"remove\", function(){\n", | |
"\tfig.close_ws(fig, {});\n", | |
" });\n", | |
"}\n", | |
"\n", | |
"mpl.figure.prototype._canvas_extra_style = function(el){\n", | |
" // this is important to make the div 'focusable\n", | |
" el.attr('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", | |
" }\n", | |
" else {\n", | |
" // location in version 2\n", | |
" IPython.keyboard_manager.register_events(el);\n", | |
" }\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", | |
" // 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", | |
"\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('matplotlib', mpl.mpl_figure_comm);\n", | |
"}\n" | |
], | |
"text/plain": [ | |
"<IPython.core.display.Javascript object>" | |
] | |
}, | |
"metadata": {}, | |
"output_type": "display_data" | |
}, | |
{ | |
"data": { | |
"text/html": [ | |
"<img src=\"data:image/png;base64,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\" width=\"600\">" | |
], | |
"text/plain": [ | |
"<IPython.core.display.HTML object>" | |
] | |
}, | |
"metadata": {}, | |
"output_type": "display_data" | |
}, | |
{ | |
"data": { | |
"text/plain": [ | |
"<matplotlib.lines.Line2D at 0x20ff8363b38>" | |
] | |
}, | |
"execution_count": 5, | |
"metadata": {}, | |
"output_type": "execute_result" | |
} | |
], | |
"source": [ | |
"cell.optimize('brightfield')\n", | |
"fig, (ax1, ax2) = plt.subplots(1,2, figsize=(6, 3))\n", | |
"cp = CellPlot(cell)\n", | |
"cp.imshow('binary', ax=ax1)\n", | |
"cp.plot_outline(ax=ax1)\n", | |
"\n", | |
"cp.imshow('brightfield', ax=ax2)\n", | |
"cp.plot_outline(ax=ax2)\n", | |
"cp.plot_midline(ax=ax2)" | |
] | |
}, | |
{ | |
"cell_type": "markdown", | |
"metadata": {}, | |
"source": [ | |
"The radius of the cell can instead be measured with the ``measure_r`` function. An image is selected to perform the measurement on, which is typically the brightfield image. The radius is then measured from the radial distribution curve of the image. Three different modes can be selected; 'min', 'mid' and 'max', which refers to where in the curve the radius of the cell is defined.\n", | |
"\n", | |
"The three options and their effect on the measured radius is shown below." | |
] | |
}, | |
{ | |
"cell_type": "code", | |
"execution_count": 6, | |
"metadata": {}, | |
"outputs": [ | |
{ | |
"data": { | |
"application/javascript": [ | |
"/* Put everything inside the global mpl namespace */\n", | |
"window.mpl = {};\n", | |
"\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('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", | |
"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 = $('<div/>');\n", | |
" this._root_extra_style(this.root)\n", | |
" this.root.attr('style', 'display: inline-block');\n", | |
"\n", | |
" $(parent_element).append(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 (mpl.ratio != 1) {\n", | |
" fig.send_message(\"set_dpi_ratio\", {'dpi_ratio': mpl.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 = $(\n", | |
" '<div class=\"ui-dialog-titlebar ui-widget-header ui-corner-all ' +\n", | |
" 'ui-helper-clearfix\"/>');\n", | |
" var titletext = $(\n", | |
" '<div class=\"ui-dialog-title\" style=\"width: 100%; ' +\n", | |
" 'text-align: center; padding: 3px;\"/>');\n", | |
" titlebar.append(titletext)\n", | |
" this.root.append(titlebar);\n", | |
" this.header = titletext[0];\n", | |
"}\n", | |
"\n", | |
"\n", | |
"\n", | |
"mpl.figure.prototype._canvas_extra_style = function(canvas_div) {\n", | |
"\n", | |
"}\n", | |
"\n", | |
"\n", | |
"mpl.figure.prototype._root_extra_style = function(canvas_div) {\n", | |
"\n", | |
"}\n", | |
"\n", | |
"mpl.figure.prototype._init_canvas = function() {\n", | |
" var fig = this;\n", | |
"\n", | |
" var canvas_div = $('<div/>');\n", | |
"\n", | |
" canvas_div.attr('style', 'position: relative; clear: both; outline: 0');\n", | |
"\n", | |
" function canvas_keyboard_event(event) {\n", | |
" return fig.key_event(event, event['data']);\n", | |
" }\n", | |
"\n", | |
" canvas_div.keydown('key_press', canvas_keyboard_event);\n", | |
" canvas_div.keyup('key_release', canvas_keyboard_event);\n", | |
" this.canvas_div = canvas_div\n", | |
" this._canvas_extra_style(canvas_div)\n", | |
" this.root.append(canvas_div);\n", | |
"\n", | |
" var canvas = $('<canvas/>');\n", | |
" canvas.addClass('mpl-canvas');\n", | |
" canvas.attr('style', \"left: 0; top: 0; z-index: 0; outline: 0\")\n", | |
"\n", | |
" this.canvas = canvas[0];\n", | |
" this.context = canvas[0].getContext(\"2d\");\n", | |
"\n", | |
" var backingStore = this.context.backingStorePixelRatio ||\n", | |
"\tthis.context.webkitBackingStorePixelRatio ||\n", | |
"\tthis.context.mozBackingStorePixelRatio ||\n", | |
"\tthis.context.msBackingStorePixelRatio ||\n", | |
"\tthis.context.oBackingStorePixelRatio ||\n", | |
"\tthis.context.backingStorePixelRatio || 1;\n", | |
"\n", | |
" mpl.ratio = (window.devicePixelRatio || 1) / backingStore;\n", | |
"\n", | |
" var rubberband = $('<canvas/>');\n", | |
" rubberband.attr('style', \"position: absolute; left: 0; top: 0; z-index: 1;\")\n", | |
"\n", | |
" var pass_mouse_events = true;\n", | |
"\n", | |
" canvas_div.resizable({\n", | |
" start: function(event, ui) {\n", | |
" pass_mouse_events = false;\n", | |
" },\n", | |
" resize: function(event, ui) {\n", | |
" fig.request_resize(ui.size.width, ui.size.height);\n", | |
" },\n", | |
" stop: function(event, ui) {\n", | |
" pass_mouse_events = true;\n", | |
" fig.request_resize(ui.size.width, ui.size.height);\n", | |
" },\n", | |
" });\n", | |
"\n", | |
" function mouse_event_fn(event) {\n", | |
" if (pass_mouse_events)\n", | |
" return fig.mouse_event(event, event['data']);\n", | |
" }\n", | |
"\n", | |
" rubberband.mousedown('button_press', mouse_event_fn);\n", | |
" rubberband.mouseup('button_release', mouse_event_fn);\n", | |
" // Throttle sequential mouse events to 1 every 20ms.\n", | |
" rubberband.mousemove('motion_notify', mouse_event_fn);\n", | |
"\n", | |
" rubberband.mouseenter('figure_enter', mouse_event_fn);\n", | |
" rubberband.mouseleave('figure_leave', mouse_event_fn);\n", | |
"\n", | |
" canvas_div.on(\"wheel\", function (event) {\n", | |
" event = event.originalEvent;\n", | |
" event['data'] = 'scroll'\n", | |
" if (event.deltaY < 0) {\n", | |
" event.step = 1;\n", | |
" } else {\n", | |
" event.step = -1;\n", | |
" }\n", | |
" mouse_event_fn(event);\n", | |
" });\n", | |
"\n", | |
" canvas_div.append(canvas);\n", | |
" canvas_div.append(rubberband);\n", | |
"\n", | |
" this.rubberband = rubberband;\n", | |
" this.rubberband_canvas = rubberband[0];\n", | |
" this.rubberband_context = rubberband[0].getContext(\"2d\");\n", | |
" this.rubberband_context.strokeStyle = \"#000000\";\n", | |
"\n", | |
" this._resize_canvas = function(width, height) {\n", | |
" // Keep the size of the canvas, canvas container, and rubber band\n", | |
" // canvas in synch.\n", | |
" canvas_div.css('width', width)\n", | |
" canvas_div.css('height', height)\n", | |
"\n", | |
" canvas.attr('width', width * mpl.ratio);\n", | |
" canvas.attr('height', height * mpl.ratio);\n", | |
" canvas.attr('style', 'width: ' + width + 'px; height: ' + height + 'px;');\n", | |
"\n", | |
" rubberband.attr('width', width);\n", | |
" rubberband.attr('height', height);\n", | |
" }\n", | |
"\n", | |
" // Set the figure to an initial 600x600px, this will subsequently be updated\n", | |
" // upon first draw.\n", | |
" this._resize_canvas(600, 600);\n", | |
"\n", | |
" // Disable right mouse context menu.\n", | |
" $(this.rubberband_canvas).bind(\"contextmenu\",function(e){\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 nav_element = $('<div/>');\n", | |
" nav_element.attr('style', 'width: 100%');\n", | |
" this.root.append(nav_element);\n", | |
"\n", | |
" // Define a callback function for later on.\n", | |
" function toolbar_event(event) {\n", | |
" return fig.toolbar_button_onclick(event['data']);\n", | |
" }\n", | |
" function toolbar_mouse_event(event) {\n", | |
" return fig.toolbar_button_onmouseover(event['data']);\n", | |
" }\n", | |
"\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", | |
" // put a spacer in here.\n", | |
" continue;\n", | |
" }\n", | |
" var button = $('<button/>');\n", | |
" button.addClass('ui-button ui-widget ui-state-default ui-corner-all ' +\n", | |
" 'ui-button-icon-only');\n", | |
" button.attr('role', 'button');\n", | |
" button.attr('aria-disabled', 'false');\n", | |
" button.click(method_name, toolbar_event);\n", | |
" button.mouseover(tooltip, toolbar_mouse_event);\n", | |
"\n", | |
" var icon_img = $('<span/>');\n", | |
" icon_img.addClass('ui-button-icon-primary ui-icon');\n", | |
" icon_img.addClass(image);\n", | |
" icon_img.addClass('ui-corner-all');\n", | |
"\n", | |
" var tooltip_span = $('<span/>');\n", | |
" tooltip_span.addClass('ui-button-text');\n", | |
" tooltip_span.html(tooltip);\n", | |
"\n", | |
" button.append(icon_img);\n", | |
" button.append(tooltip_span);\n", | |
"\n", | |
" nav_element.append(button);\n", | |
" }\n", | |
"\n", | |
" var fmt_picker_span = $('<span/>');\n", | |
"\n", | |
" var fmt_picker = $('<select/>');\n", | |
" fmt_picker.addClass('mpl-toolbar-option ui-widget ui-widget-content');\n", | |
" fmt_picker_span.append(fmt_picker);\n", | |
" nav_element.append(fmt_picker_span);\n", | |
" this.format_dropdown = fmt_picker[0];\n", | |
"\n", | |
" for (var ind in mpl.extensions) {\n", | |
" var fmt = mpl.extensions[ind];\n", | |
" var option = $(\n", | |
" '<option/>', {selected: fmt === mpl.default_extension}).html(fmt);\n", | |
" fmt_picker.append(option);\n", | |
" }\n", | |
"\n", | |
" // Add hover states to the ui-buttons\n", | |
" $( \".ui-button\" ).hover(\n", | |
" function() { $(this).addClass(\"ui-state-hover\");},\n", | |
" function() { $(this).removeClass(\"ui-state-hover\");}\n", | |
" );\n", | |
"\n", | |
" var status_bar = $('<span class=\"mpl-message\"/>');\n", | |
" nav_element.append(status_bar);\n", | |
" this.message = status_bar[0];\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", | |
"\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", | |
"\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]);\n", | |
" fig.send_message(\"refresh\", {});\n", | |
" };\n", | |
"}\n", | |
"\n", | |
"mpl.figure.prototype.handle_rubberband = function(fig, msg) {\n", | |
" var x0 = msg['x0'] / mpl.ratio;\n", | |
" var y0 = (fig.canvas.height - msg['y0']) / mpl.ratio;\n", | |
" var x1 = msg['x1'] / mpl.ratio;\n", | |
" var y1 = (fig.canvas.height - msg['y1']) / mpl.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, 0, fig.canvas.width / mpl.ratio, fig.canvas.height / mpl.ratio);\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", | |
" {\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.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", | |
" /* 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", | |
" evt.data.type = \"image/png\";\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", | |
" fig.imageObj.src = (window.URL || window.webkitURL).createObjectURL(\n", | |
" evt.data);\n", | |
" fig.updated_canvas_event();\n", | |
" fig.waiting = false;\n", | |
" return;\n", | |
" }\n", | |
" else if (typeof evt.data === 'string' && evt.data.slice(0, 21) == \"data:image/png;base64\") {\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(\"No handler for the '\" + msg_type + \"' message type: \", msg);\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(\"Exception inside the 'handler_\" + msg_type + \"' callback:\", e, e.stack, msg);\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", | |
" if (e.target)\n", | |
" targ = e.target;\n", | |
" else if (e.srcElement)\n", | |
" targ = e.srcElement;\n", | |
" if (targ.nodeType == 3) // defeat Safari bug\n", | |
" targ = targ.parentNode;\n", | |
"\n", | |
" // jQuery normalizes the pageX and pageY\n", | |
" // pageX,Y are the mouse positions relative to the document\n", | |
" // offset() returns the position of the element relative to the document\n", | |
" var x = e.pageX - $(targ).offset().left;\n", | |
" var y = e.pageY - $(targ).offset().top;\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", | |
" 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", | |
" {\n", | |
" this.canvas.focus();\n", | |
" this.canvas_div.focus();\n", | |
" }\n", | |
"\n", | |
" var x = canvas_pos.x * mpl.ratio;\n", | |
" var y = canvas_pos.y * mpl.ratio;\n", | |
"\n", | |
" this.send_message(name, {x: x, y: y, button: event.button,\n", | |
" step: event.step,\n", | |
" guiEvent: simpleKeys(event)});\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", | |
"\n", | |
" // Prevent repeat events\n", | |
" if (name == 'key_press')\n", | |
" {\n", | |
" if (event.which === this._key)\n", | |
" return;\n", | |
" else\n", | |
" this._key = event.which;\n", | |
" }\n", | |
" if (name == 'key_release')\n", | |
" this._key = null;\n", | |
"\n", | |
" var value = '';\n", | |
" if (event.ctrlKey && event.which != 17)\n", | |
" value += \"ctrl+\";\n", | |
" if (event.altKey && event.which != 18)\n", | |
" value += \"alt+\";\n", | |
" if (event.shiftKey && event.which != 16)\n", | |
" value += \"shift+\";\n", | |
"\n", | |
" value += 'k';\n", | |
" value += event.which.toString();\n", | |
"\n", | |
" this._key_event_extra(event, name);\n", | |
"\n", | |
" this.send_message(name, {key: value,\n", | |
" 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", | |
"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\", \"Pan axes with left mouse, zoom with right\", \"fa fa-arrows icon-move\", \"pan\"], [\"Zoom\", \"Zoom to rectangle\", \"fa fa-square-o icon-check-empty\", \"zoom\"], [\"\", \"\", \"\", \"\"], [\"Download\", \"Download plot\", \"fa fa-floppy-o icon-save\", \"download\"]];\n", | |
"\n", | |
"mpl.extensions = [\"eps\", \"pdf\", \"png\", \"ps\", \"raw\", \"svg\"];\n", | |
"\n", | |
"mpl.default_extension = \"png\";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.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", | |
" // Pass the mpl event to the overridden (by mpl) onmessage function.\n", | |
" ws.onmessage(msg['content']['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 = $(\"#\" + id);\n", | |
" var ws_proxy = comm_websocket_adapter(comm)\n", | |
"\n", | |
" function ondownload(figure, format) {\n", | |
" window.open(figure.imageObj.src);\n", | |
" }\n", | |
"\n", | |
" var fig = new mpl.figure(id, ws_proxy,\n", | |
" ondownload,\n", | |
" element.get(0));\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.get(0);\n", | |
" fig.cell_info = mpl.find_output_cell(\"<div id='\" + id + \"'></div>\");\n", | |
" if (!fig.cell_info) {\n", | |
" console.error(\"Failed to find cell for figure\", id, fig);\n", | |
" return;\n", | |
" }\n", | |
"\n", | |
" var output_index = fig.cell_info[2]\n", | |
" var cell = fig.cell_info[0];\n", | |
"\n", | |
"};\n", | |
"\n", | |
"mpl.figure.prototype.handle_close = function(fig, msg) {\n", | |
" var width = fig.canvas.width/mpl.ratio\n", | |
" fig.root.unbind('remove')\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).html('<img src=\"' + dataURL + '\" width=\"' + width + '\">');\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/mpl.ratio\n", | |
" var dataURL = this.canvas.toDataURL();\n", | |
" this.cell_info[1]['text/html'] = '<img src=\"' + dataURL + '\" width=\"' + width + '\">';\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 () { fig.push_to_output() }, 1000);\n", | |
"}\n", | |
"\n", | |
"mpl.figure.prototype._init_toolbar = function() {\n", | |
" var fig = this;\n", | |
"\n", | |
" var nav_element = $('<div/>');\n", | |
" nav_element.attr('style', 'width: 100%');\n", | |
" this.root.append(nav_element);\n", | |
"\n", | |
" // Define a callback function for later on.\n", | |
" function toolbar_event(event) {\n", | |
" return fig.toolbar_button_onclick(event['data']);\n", | |
" }\n", | |
" function toolbar_mouse_event(event) {\n", | |
" return fig.toolbar_button_onmouseover(event['data']);\n", | |
" }\n", | |
"\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) { continue; };\n", | |
"\n", | |
" var button = $('<button class=\"btn btn-default\" href=\"#\" title=\"' + name + '\"><i class=\"fa ' + image + ' fa-lg\"></i></button>');\n", | |
" button.click(method_name, toolbar_event);\n", | |
" button.mouseover(tooltip, toolbar_mouse_event);\n", | |
" nav_element.append(button);\n", | |
" }\n", | |
"\n", | |
" // Add the status bar.\n", | |
" var status_bar = $('<span class=\"mpl-message\" style=\"text-align:right; float: right;\"/>');\n", | |
" nav_element.append(status_bar);\n", | |
" this.message = status_bar[0];\n", | |
"\n", | |
" // Add the close button to the window.\n", | |
" var buttongrp = $('<div class=\"btn-group inline pull-right\"></div>');\n", | |
" var button = $('<button class=\"btn btn-mini btn-primary\" href=\"#\" title=\"Stop Interaction\"><i class=\"fa fa-power-off icon-remove icon-large\"></i></button>');\n", | |
" button.click(function (evt) { fig.handle_close(fig, {}); } );\n", | |
" button.mouseover('Stop Interaction', toolbar_mouse_event);\n", | |
" buttongrp.append(button);\n", | |
" var titlebar = this.root.find($('.ui-dialog-titlebar'));\n", | |
" titlebar.prepend(buttongrp);\n", | |
"}\n", | |
"\n", | |
"mpl.figure.prototype._root_extra_style = function(el){\n", | |
" var fig = this\n", | |
" el.on(\"remove\", function(){\n", | |
"\tfig.close_ws(fig, {});\n", | |
" });\n", | |
"}\n", | |
"\n", | |
"mpl.figure.prototype._canvas_extra_style = function(el){\n", | |
" // this is important to make the div 'focusable\n", | |
" el.attr('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", | |
" }\n", | |
" else {\n", | |
" // location in version 2\n", | |
" IPython.keyboard_manager.register_events(el);\n", | |
" }\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", | |
" // 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", | |
"\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('matplotlib', mpl.mpl_figure_comm);\n", | |
"}\n" | |
], | |
"text/plain": [ | |
"<IPython.core.display.Javascript object>" | |
] | |
}, | |
"metadata": {}, | |
"output_type": "display_data" | |
}, | |
{ | |
"data": { | |
"text/html": [ | |
"<img src=\"data:image/png;base64,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\" width=\"700\">" | |
], | |
"text/plain": [ | |
"<IPython.core.display.HTML object>" | |
] | |
}, | |
"metadata": {}, | |
"output_type": "display_data" | |
} | |
], | |
"source": [ | |
"fig, (ax1, ax2) = plt.subplots(1, 2, figsize=(7, 2.5), )\n", | |
"cp = CellPlot(cell)\n", | |
"cp.imshow('brightfield', ax=ax1)\n", | |
"cp.plot_r_dist(data_name='brightfield', ax=ax2, color='k')\n", | |
"\n", | |
"colors = ['g', 'r', 'b']\n", | |
"for c, mode in zip(colors, ['min', 'mid', 'max']):\n", | |
" cell.measure_r(data_name='brightfield', mode=mode)\n", | |
"\n", | |
" cp.plot_outline(ax=ax1, color=c)\n", | |
" ax2.axvline(cell.radius * config.cfg.IMG_PIXELSIZE / 1000, color=c)\n", | |
" \n", | |
"plt.tight_layout()" | |
] | |
}, | |
{ | |
"cell_type": "markdown", | |
"metadata": {}, | |
"source": [ | |
"The ``measure_r`` function can also return the measured value, instead of modifiying the cell's coordinate system value." | |
] | |
}, | |
{ | |
"cell_type": "code", | |
"execution_count": 7, | |
"metadata": {}, | |
"outputs": [ | |
{ | |
"name": "stdout", | |
"output_type": "stream", | |
"text": [ | |
"The cell's radius is 6.6 pixels or 0.53 μm\n", | |
"The cell's width is 13.2 pixels or 1.1 μm\n" | |
] | |
} | |
], | |
"source": [ | |
"radius = cell.measure_r(data_name='brightfield', mode='mid', in_place=False)\n", | |
"radius\n", | |
"\n", | |
"print(\"The cell's radius is {:.2} pixels or {: .2} μm\".format(radius, radius * config.cfg.IMG_PIXELSIZE / 1000))\n", | |
"print(\"The cell's width is {:.3} pixels or {: .2} μm\".format(2*radius, 2*radius * config.cfg.IMG_PIXELSIZE / 1000))" | |
] | |
} | |
], | |
"metadata": { | |
"kernelspec": { | |
"display_name": "Python [conda env:py36_cc]", | |
"language": "python", | |
"name": "conda-env-py36_cc-py" | |
}, | |
"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.10" | |
}, | |
"widgets": { | |
"application/vnd.jupyter.widget-state+json": { | |
"state": {}, | |
"version_major": 2, | |
"version_minor": 0 | |
} | |
} | |
}, | |
"nbformat": 4, | |
"nbformat_minor": 4 | |
} |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment