Skip to content

Instantly share code, notes, and snippets.

Show Gist options
  • Star 0 You must be signed in to star a gist
  • Fork 1 You must be signed in to fork a gist
  • Save ricardodeazambuja/65bcfa955d448dd9136b71d737bfa607 to your computer and use it in GitHub Desktop.
Save ricardodeazambuja/65bcfa955d448dd9136b71d737bfa607 to your computer and use it in GitHub Desktop.
Display the source blob
Display the rendered blob
Raw
{
"cells": [
{
"cell_type": "markdown",
"metadata": {},
"source": [
"# Interactively playing with MNIST!\n",
"## https://ricardodeazambuja.com/\n",
"\n",
"I really like to be the person that tries to automate things, even when they don't pay off in the very short term... that's because it's fun and you always learn something new. So I'm republishing the image below straight from the place it seems to be [it's first appearance](https://i.imgur.com/Q8kV8.png):\n",
"\n",
"<img src=\"https://i.imgur.com/Q8kV8.png\" width=\"600\"/>"
]
},
{
"cell_type": "code",
"execution_count": 1,
"metadata": {},
"outputs": [],
"source": [
"import tensorflow as tf"
]
},
{
"cell_type": "markdown",
"metadata": {},
"source": [
"Keras has some famous datasets that you can import very easily. Below I'm showing a list with all names:"
]
},
{
"cell_type": "code",
"execution_count": 2,
"metadata": {},
"outputs": [
{
"data": {
"text/plain": [
"['boston_housing',\n",
" 'cifar10',\n",
" 'cifar100',\n",
" 'fashion_mnist',\n",
" 'imdb',\n",
" 'mnist',\n",
" 'reuters']"
]
},
"execution_count": 2,
"metadata": {},
"output_type": "execute_result"
}
],
"source": [
"[i for i in dir(tf.keras.datasets) if '__' not in i]"
]
},
{
"cell_type": "markdown",
"metadata": {},
"source": [
"Now I will list the useful methods available for each dataset:"
]
},
{
"cell_type": "code",
"execution_count": 3,
"metadata": {},
"outputs": [
{
"data": {
"text/plain": [
"[[('boston_housing', 'load_data')],\n",
" [('cifar10', 'load_data')],\n",
" [('cifar100', 'load_data')],\n",
" [('fashion_mnist', 'load_data')],\n",
" [('imdb', 'get_word_index'), ('imdb', 'load_data')],\n",
" [('mnist', 'load_data')],\n",
" [('reuters', 'get_word_index'), ('reuters', 'load_data')]]"
]
},
"execution_count": 3,
"metadata": {},
"output_type": "execute_result"
}
],
"source": [
"[[(i,j) for j in dir(getattr(tf.keras.datasets,i)) if '__' not in j] \n",
" for i in dir(tf.keras.datasets) if '__' not in i]"
]
},
{
"cell_type": "markdown",
"metadata": {},
"source": [
"The load_data method is quite obvious, but I had to check what [get_word_index](https://www.tensorflow.org/api_docs/python/tf/keras/datasets/imdb/get_word_index) was about.\n",
"\n",
"I'm interested in the MNIST numbers, so let's load it."
]
},
{
"cell_type": "code",
"execution_count": 4,
"metadata": {},
"outputs": [
{
"name": "stdout",
"output_type": "stream",
"text": [
"Downloading data from https://storage.googleapis.com/tensorflow/tf-keras-datasets/mnist.npz\n",
"11493376/11490434 [==============================] - 1s 0us/step\n"
]
}
],
"source": [
"mnist = tf.keras.datasets.mnist\n",
"\n",
"# download the dataset\n",
"# mnist.load_data already splits train/test for us\n",
"(x_train, y_train),(x_test, y_test) = mnist.load_data()"
]
},
{
"cell_type": "code",
"execution_count": 5,
"metadata": {},
"outputs": [
{
"data": {
"text/plain": [
"(0, 255, dtype('uint8'))"
]
},
"execution_count": 5,
"metadata": {},
"output_type": "execute_result"
}
],
"source": [
"# verify the range and type of the original data\n",
"x_train.min(),x_train.max(), x_train.dtype"
]
},
{
"cell_type": "code",
"execution_count": 6,
"metadata": {},
"outputs": [
{
"data": {
"text/plain": [
"(0.0, 1.0, dtype('float64'))"
]
},
"execution_count": 6,
"metadata": {},
"output_type": "execute_result"
}
],
"source": [
"x_train, x_test = x_train / 255.0, x_test / 255.0\n",
"\n",
"# now rescaled to be from 0.0 to 1.0 (float64)\n",
"x_train.min(),x_train.max(), x_train.dtype"
]
},
{
"cell_type": "markdown",
"metadata": {},
"source": [
"Now, I will reuse the very first example from [Tensorflow tutorials](https://www.tensorflow.org/tutorials) (Feb/2019). This example is interesting because it's NOT using convolutional layers and that will be useful to see the differences later on.\n",
"\n",
"The example is simple, but not totally super duper old school. It uses as activation the [RELU](https://github.com/Kulbear/deep-learning-nano-foundation/wiki/ReLU-and-Softmax-Activation-Functions) and a [Dropout](https://en.wikipedia.org/wiki/Dropout_(neural_networks)) layer... I didn't know it was patented by Google in 2012.\n",
"\n",
"This model is also smarter than old ones in respect to its input because it uses a `Flatten` layer as its first, so you can use your 28x28 values (pixels) matrix and it is transformed into an array with 784 values."
]
},
{
"cell_type": "code",
"execution_count": 7,
"metadata": {},
"outputs": [
{
"data": {
"text/plain": [
"784"
]
},
"execution_count": 7,
"metadata": {},
"output_type": "execute_result"
}
],
"source": [
"# Size of the flattened input:\n",
"x_train.shape[1]*x_train.shape[2]"
]
},
{
"cell_type": "markdown",
"metadata": {},
"source": [
"When we need to know more about a method, object, etc (e.g tf.keras.layers.Flatten), you can move the cursor over the word and press ```Shift+Tab``` or write: "
]
},
{
"cell_type": "code",
"execution_count": 8,
"metadata": {},
"outputs": [],
"source": [
"tf.keras.layers.Flatten?"
]
},
{
"cell_type": "markdown",
"metadata": {},
"source": [
"by the way, ?? will show you the source code instead of docstring ;)"
]
},
{
"cell_type": "markdown",
"metadata": {},
"source": [
"or the python interpreter way that prints it directly instead of creating the extra window:"
]
},
{
"cell_type": "code",
"execution_count": 38,
"metadata": {},
"outputs": [],
"source": [
"help(tf.keras.layers.Flatten)"
]
},
{
"cell_type": "markdown",
"metadata": {},
"source": [
"Now it's time to create the model. I will keep the sequential method, but it's also possible to use .add (e.g. model.add()) instead of passing all layers as a list:"
]
},
{
"cell_type": "code",
"execution_count": 11,
"metadata": {},
"outputs": [],
"source": [
"model = tf.keras.models.Sequential([\n",
" tf.keras.layers.Flatten(input_shape=(28, 28), name='Flatten1'),\n",
" tf.keras.layers.Dense(512, activation=tf.nn.relu, name='Dense1'),\n",
" tf.keras.layers.Dropout(0.2, name='Dropout1'),\n",
" tf.keras.layers.Dense(10, activation=tf.nn.softmax, name='Dense2')\n",
"])"
]
},
{
"cell_type": "markdown",
"metadata": {},
"source": [
"\n",
"https://github.com/tensorflow/tensorflow/blob/r1.11/tensorflow/python/keras/backend.py#L3546"
]
},
{
"cell_type": "markdown",
"metadata": {},
"source": [
"And compile the beast!"
]
},
{
"cell_type": "code",
"execution_count": 12,
"metadata": {},
"outputs": [],
"source": [
"model.compile(optimizer='adam',\n",
" loss='sparse_categorical_crossentropy',\n",
" metrics=['accuracy'])"
]
},
{
"cell_type": "code",
"execution_count": 13,
"metadata": {},
"outputs": [
{
"data": {
"text/plain": [
"((60000, 28, 28), (60000,))"
]
},
"execution_count": 13,
"metadata": {},
"output_type": "execute_result"
}
],
"source": [
"x_train.shape,y_train.shape"
]
},
{
"cell_type": "markdown",
"metadata": {},
"source": [
"Let's train it:"
]
},
{
"cell_type": "code",
"execution_count": 14,
"metadata": {},
"outputs": [
{
"name": "stdout",
"output_type": "stream",
"text": [
"Epoch 1/5\n",
"60000/60000 [==============================] - 3s 53us/sample - loss: 0.2202 - acc: 0.9352\n",
"Epoch 2/5\n",
"60000/60000 [==============================] - 3s 46us/sample - loss: 0.0971 - acc: 0.9706\n",
"Epoch 3/5\n",
"60000/60000 [==============================] - 3s 47us/sample - loss: 0.0700 - acc: 0.9777\n",
"Epoch 4/5\n",
"60000/60000 [==============================] - 3s 48us/sample - loss: 0.0549 - acc: 0.9829\n",
"Epoch 5/5\n",
"60000/60000 [==============================] - 3s 46us/sample - loss: 0.0438 - acc: 0.9853\n"
]
},
{
"data": {
"text/plain": [
"<tensorflow.python.keras.callbacks.History at 0x7f30b5091438>"
]
},
"execution_count": 14,
"metadata": {},
"output_type": "execute_result"
}
],
"source": [
"model.fit(x_train, y_train, epochs=5)"
]
},
{
"cell_type": "markdown",
"metadata": {},
"source": [
"And, last but not least, verify how it works on the never seen before test data:"
]
},
{
"cell_type": "code",
"execution_count": 15,
"metadata": {},
"outputs": [
{
"name": "stdout",
"output_type": "stream",
"text": [
"10000/10000 [==============================] - 0s 28us/sample - loss: 0.0650 - acc: 0.9807\n"
]
},
{
"data": {
"text/plain": [
"[0.06496644636945566, 0.9807]"
]
},
"execution_count": 15,
"metadata": {},
"output_type": "execute_result"
}
],
"source": [
"model.evaluate(x_test, y_test)"
]
},
{
"cell_type": "code",
"execution_count": 16,
"metadata": {},
"outputs": [
{
"data": {
"text/plain": [
"(10000, 28, 28)"
]
},
"execution_count": 16,
"metadata": {},
"output_type": "execute_result"
}
],
"source": [
"x_test.shape"
]
},
{
"cell_type": "markdown",
"metadata": {},
"source": [
"Below is a function that will run the prediction and, after that, show the results using matplotlib:"
]
},
{
"cell_type": "code",
"execution_count": 17,
"metadata": {},
"outputs": [],
"source": [
"%matplotlib inline\n",
"from matplotlib import pyplot as plt\n",
"import numpy as np\n",
"\n",
"def predict_from_image(input_img):\n",
" get_ipython().magic('matplotlib inline')\n",
" \n",
" prediction = model.predict(input_img.reshape((1,28,28)))[0]\n",
"\n",
" fig = plt.figure(figsize=(10,10))\n",
" ax1 = fig.add_subplot(1,2,1)\n",
" ax1.imshow(input_img, cmap='gray')\n",
" ax1.set_aspect(1.0)\n",
"\n",
" ax2 = fig.add_subplot(1,2,2)\n",
" _ = ax2.bar(range(10), prediction*10, alpha=0.8, color='b')\n",
" ax2.set_xticks(np.arange(10))\n",
" ax2.set_xticklabels(range(10))\n",
" ax2.set_title(\"Prediction Probability\")\n",
"\n",
" ax2.set_yticks(range(0, 11))\n",
" ax2.set_yticklabels([str(ri)+'%' for ri in range(0, 110, 10)])\n",
" ax2.set_aspect(1.0)\n",
"\n",
" plt.tight_layout()\n",
" plt.show()"
]
},
{
"cell_type": "code",
"execution_count": 18,
"metadata": {},
"outputs": [
{
"data": {
"image/png": "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\n",
"text/plain": [
"<Figure size 720x720 with 2 Axes>"
]
},
"metadata": {
"needs_background": "light"
},
"output_type": "display_data"
}
],
"source": [
"n = 21 #it can go from 0 to 9999\n",
"predict_from_image(x_test[n])"
]
},
{
"cell_type": "markdown",
"metadata": {},
"source": [
"## Finally this is the part that will allow us to test digits we draw directly on the screen!!"
]
},
{
"cell_type": "code",
"execution_count": 23,
"metadata": {},
"outputs": [],
"source": [
"#\n",
"# Interactive Matplotlib plot\n",
"#\n",
"# Based on https://matplotlib.org/users/event_handling.html\n",
"#\n",
"# Sadly, this will not work on Google Colab :(\n",
"\n",
"# import sys\n",
"\n",
"# if \"pyplot\" in sys.modules:\n",
"# from importlib import reload\n",
"# plt = reload(plt)\n",
"# else:\n",
"# from matplotlib import pyplot as plt\n",
"\n",
"%matplotlib inline\n",
"from matplotlib import pyplot as plt\n",
"# import matplotlib\n",
"# from importlib import reload\n",
" \n",
"from matplotlib.widgets import Button, Slider\n",
"import numpy as np\n",
"\n",
"import io\n",
"import PIL.Image\n",
"\n",
"class CaptureHandwriting:\n",
" def __init__(self):\n",
" # if it's called only once, it fails sometimes on my computer...\n",
" # if I don't call inline before calling notebook, it behaves weirdly\n",
" # e.g. figsize gives me a different size ?!?!\n",
" get_ipython().magic('matplotlib inline')\n",
" get_ipython().magic('matplotlib notebook')\n",
" get_ipython().magic('matplotlib notebook') # the third time's a charm!\n",
"\n",
" self.img_in = []\n",
" self.img_array = []\n",
" self.mouse_captured = False\n",
" \n",
" fig = plt.figure(figsize=(10,10))\n",
" ax = fig.add_subplot(1,1,1)\n",
" plt.subplots_adjust(bottom=0.2)\n",
"\n",
" canvas = ax.figure.canvas\n",
" ax.set_title('Click to draw your number from 0 to 9')\n",
"\n",
" ax.set_xticks([])\n",
" ax.set_xlim(0,1)\n",
"\n",
" ax.set_yticks([])\n",
" ax.set_ylim(0,1)\n",
"\n",
" ax.set_aspect(1.0)\n",
"\n",
" init_line_width = 20\n",
"\n",
" self.line, = ax.plot([0], [0], linewidth=init_line_width, linestyle='solid', color='black') # empty line\n",
"\n",
" # save the clean slate background -- everything but the animated line\n",
" # is drawn and saved in the pixel buffer background\n",
" self.background = canvas.copy_from_bbox(ax.bbox)\n",
" \n",
" self.xs = list(self.line.get_xdata())\n",
" self.ys = list(self.line.get_ydata())\n",
" \n",
" event_clickDown = self.line.figure.canvas.mpl_connect('button_press_event', self.onclickDown)\n",
" event_clickUp = self.line.figure.canvas.mpl_connect('button_release_event', self.onclickUp)\n",
" cid = self.line.figure.canvas.mpl_connect('motion_notify_event', self)\n",
" \n",
" # http://www.math.buffalo.edu/~badzioch/MTH337/PT/PT-matplotlib_subplots/\\\n",
" # PT-matplotlib_subplots.html#axes-objects\n",
" # ax1 = plt.axes([\n",
" # 0.1, # x-coordinate of the lower left corner of the axes object\n",
" # 0.1, # y-coordinate of the lower left corner of the axes object\n",
" # 0.5, # width of the object\n",
" # 0.4 # height of the object\n",
" # ])\n",
"\n",
" axsave = plt.axes([ax.get_position().bounds[0], 0.12, ax.get_position().bounds[2], 0.07])\n",
" self.b_save = Button(axsave, 'Convert to MNIST style')\n",
" self.b_save.on_clicked(self.button_update)\n",
"\n",
" axwidth = plt.axes([ax.get_position().bounds[0], 0.06, ax.get_position().bounds[2], 0.05])\n",
" self.s_width = Slider(axwidth, 'Line Width',5, 50, valinit=init_line_width)\n",
" self.s_width.on_changed(self.slider_update)\n",
" \n",
" self.ax = ax\n",
" self.fig = fig \n",
"\n",
" plt.show()\n",
"\n",
" def __call__(self, event):\n",
" if self.mouse_captured:\n",
" if event.inaxes!=self.line.axes: return\n",
" self.xs.append(event.xdata)\n",
" self.ys.append(event.ydata)\n",
" self.line.set_data(self.xs, self.ys)\n",
"\n",
" canvas = self.line.figure.canvas\n",
" axes = self.line.axes\n",
" \n",
" # restore the background region\n",
" canvas.restore_region(self.background)\n",
"\n",
" # redraw just the current rectangle\n",
" axes.draw_artist(self.line)\n",
"\n",
" # blit just the redrawn area\n",
" canvas.blit(axes.bbox)\n",
"\n",
" \n",
" def onclickDown(self, event): \n",
" self.mouse_captured = True\n",
" self.xs = []\n",
" self.ys = []\n",
" \n",
" def onclickUp(self, event):\n",
" self.mouse_captured = False\n",
"\n",
" def button_update(self, event):\n",
" f = io.BytesIO()\n",
"\n",
" self.ax.axis('off')\n",
" extent = self.ax.get_window_extent().transformed(self.fig.dpi_scale_trans.inverted())\n",
" self.fig.savefig(f, dpi='figure', transparent=False, pad_inches=0, bbox_inches=extent)\n",
"\n",
" img_in = PIL.Image.open(f)\n",
" img_in = img_in.resize((28,28),PIL.Image.BICUBIC)\n",
" img_in = img_in.convert(\"L\")\n",
" self.img_array = -(np.array(img_in)-255)/255.0\n",
"\n",
" self.ax.axis('on');\n",
" \n",
" self.ax.imshow(self.img_array, cmap='gray')\n",
" self.ax.set_xlim(0,27)\n",
" self.ax.set_ylim(0,27)\n",
" self.ax.invert_yaxis()\n",
" self.ax.figure.canvas.draw()\n",
" plt.pause(0.0001)\n",
" #self.ax.figure.canvas.draw_idle()\n",
" \n",
" plt.close(self.fig)\n",
"\n",
" def slider_update(self, val):\n",
" self.line.set_linewidth(val)"
]
},
{
"cell_type": "markdown",
"metadata": {},
"source": [
"Ok, I still need to debug it better because sometimes it needs to called many times until it actually works. It's something related to matplolib backend switching. Apparently, if you start with inline it has a hard time to switch to notebook (or the other way around)."
]
},
{
"cell_type": "code",
"execution_count": 36,
"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, fig.canvas.height);\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\", \"jpeg\", \"pdf\", \"png\", \"ps\", \"raw\", \"svg\", \"tif\"];\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",
" event.shiftKey = false;\n",
" // Send a \"J\" for go to next cell\n",
" event.which = 74;\n",
" event.keyCode = 74;\n",
" manager.command_mode();\n",
" manager.handle_keydown(event);\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,iVBORw0KGgoAAAANSUhEUgAAAtAAAALQCAYAAAC5V0ecAAAgAElEQVR4Xu3dC7i9V1Xf+xklQBChXIKkKCAiioqKIl5AuYjHFKUVpCigRQEp3ggUFbRqa0VBUeFEFC0k1opSpVxEThDxRsUjXpCbREEuQQ5VEUElBDRI+ox91obNzv7vudZ6xzvfOdf67OfxAfKfc8zxfn8jyXfN/e7tWcUXAggggAACCCCAAAIIrE3grLVXWogAAggggAACCCCAAAKFQBsCBBBAAAEEEEAAAQQ2IECgN4BlKQIIIIAAAggggAACBNoMIIAAAggggAACCCCwAQECvQEsSxFAAAEEEEAAAQQQINBmAAEEEEAAAQQQQACBDQgQ6A1gWYoAAggggAACCCCAAIE2AwgggAACCCCAAAIIbECAQG8Ay1IEEEAAAQQQQAABBAi0GUAAAQQQQAABBBBAYAMCBHoDWJYigAACCCCAAAIIIECgzQACCCCAAAIIIIAAAhsQINAbwLIUAQQQQAABBBBAAAECbQYQQAABBBBAAAEEENiAAIHeAJalCCCAAAIIIIAAAggQaDOAAAIIIIAAAggggMAGBAj0BrAsRQABBBBAAAEEEECAQJsBBBBAAAEEEEAAAQQ2IECgN4BlKQIIIIAAAggggAACBNoMIIAAAggggAACCCCwAQECvQEsSxFAAAEEEEAAAQQQINBmAAEEEEAAAQQQQACBDQgQ6A1gWYoAAggggAACCCCAAIE2AwgggAACCCCAAAIIbECAQG8Ay1IEEEAAAQQQQAABBAi0GUAAAQQQQAABBBBAYAMCBHoDWJYigAACCCCAAAIIIECgzQACCCCAAAIIIIAAAhsQINAbwLIUAQQQQAABBBBAAAECbQYQQAABBBBAAAEEENiAAIHeAJalCCCAAAIIIIAAAggQaDOAAAIIIIAAAggggMAGBAj0BrAsRQABBBBAAAEEEECAQJsBBBBAAAEEEEAAAQQ2IECgN4BlKQIIIIAAAggggAACBNoMIIAAAggggAACCCCwAQECvQEsSxFAAAEEEEAAAQQQINBmAAEEEEAAAQQQQACBDQgQ6A1gWYoAAggggAACCCCAAIE2AwgggAACCCCAAAIIbECAQG8Ay1IEEEAAAQQQQAABBAi0GUAAAQQQQAABBBBAYAMCBHoDWJYigAACCCCAAAIIIECgzQACCCCAAAIIIIAAAhsQINAbwLIUAQQQQAABBBBAAAECbQYQQAABBBBAAAEEENiAAIHeAJalCCCAAAIIIIAAAggQaDOAAAIIIIAAAggggMAGBAj0BrAsRQABBBBAAAEEEECAQJsBBBBAAAEEEEAAAQQ2IECgN4BlKQIIIIAAAggggAACBNoMIIAAAggggAACCCCwAQECvQEsSxFAAAEEEEAAAQQQINBmAAEEEEAAAQQQQACBDQgQ6A1gWYoAAggggAACCCCAAIE2AwgggAACCCCAAAIIbECAQG8Ay1IEEEAAAQQQQAABBAi0GUAAAQQQQAABBBBAYAMCBHoDWJYigAACCCCAAAIIIECgzQACCCCAAAIIIIAAAhsQINAbwLIUAQQQQAABBBBAAAECbQYQQAABBBBAAAEEENiAAIHeAJalCCCAAAIIIIAAAggQaDOAAAIIIIAAAggggMAGBAj0BrAsRQABBBBAAAEEEECAQJsBBBBAAAEEEEAAAQQ2IECgN4BlKQIIIIAAAggggAACBNoMIIAAAggggAACCCCwAQECvQEsSxFAAAEEEEAAAQQQINBmAAEEzkTgP5dSbl1K+ZpSys1LKZeWUq5fSvnnU5DdtZTyjFLKxy6AdcmzF3jc5kf+9irbp8908ieVUv7Haub+YynlwpnOURYBBBCYTIBAT0aoAAJDE3hAKeU/lFI+uZTy7lLKK0spP1BKeWkp5ahAr/uQm0js15VSHlpKufO6xSvrNjk76ci9KjO3QF9USvmHUsqjFqJ6rVLKU0sp9y2lXFFK+eFSyo+doZepsxtnPaGU8lWllHNKKc8spVxQSrlyoWd3LAIIbEiAQG8IzHIEdohAiPNjSykPL6W8qJTyT6WU80spX1RK+fYdE+hrlFLeP3h2H1m5/Z/78bIEOv69E//3gWMN//rqBvpMN9xzP//jVx/m/nUp5aallN8qpYQo/+oJYKcK9H8qpdyjlPJvSinxXL+y+nsw/rovBBAYgACBHiAkLSIwA4F4FeNtpZSvL6U86wz1j95A37KU8uZSytkrEb1hKeVHSylfurpBe0kp5StKKcdvgR+xEvT/q5Ty/x0557allFes6r13VfNfrF4R+fFSyr9a3QI+rZTygyfIVpSKm7u4MQwJ+ctSys+sbvEOXx+5bPXnDyylxOsBH1VK+bZSyjeUUm5SSnlrKSVeFXjuqq+3lFLuU0p5+eq1lZ8rpXzq6tWVuCn/8tUzHsX1OaWUF5RSbnZE0L+ylPI9pZTPLKXETeMPlVLut9r0S6WUx5RS/nElZ8dv4K8qpXxiKeUNpZT/VkoJNrcopdxl9ZwhmUe/Qmp/p5Ry91LKp5dSfq+UEt9VeMcJWcS+YBJnRp3IN54vegmG8WfRe/xf3ALHX39IKeXXVgfGWVH/i1c843/H/Lxz9eeft7qx/ZRSSrCMG9VYE1/xn7+76umzSim3Wz3j4bP85uoZ4wY2PujEmu864fn/sJRypvkIqY1s/+BIX/H60W1KKd+/yiI+GP7sGeb98O+Hw+eNPZHFVx9bnzG7f7Sai8O/9yKzmJOPm+HvdSURQGAGAgR6BqhKIjAAgbhpDvG79ik3s6cJ9P9TSrl8Jcfxn19QSgmJPirQIZH3Xkn235zA5KRbvP++kugQnxut5C3EIr69f/wrvgV+p5X8hRy/sJQSEn5UoP+ulHKvlVCGjP7blcj91eq/X7x65zYEPM5+1eqDwX9dieKPrCQ8/iyE/0kn9BHvhj96dX78cQh5vAITHzD+SyklPjzErWbI8S+XUn5jJdgnPf9xgQ5+91yJ6zVLKe87QaBDuuIDR3wgCAYvW31n4aRXWo4LdHwHIuQ5egoWX1hKiRvgeH0h+guJ/fgjEhxCGR+a4sNUMAmmkVV8gHh1KeVrVze2IdnxPnO8GhTZh0DfatXn61Y30MdfVzh+wx0fII4/f+QSH/5Omo/oN3oPiY7evq+U8u9Wt7vx3Zb4EPLsUsp5q9k9ivIGqw8CcfP816s/iFc54kY4ZP/419TZjQ9pMdfxgSq+4kNe/OxAzO/fD/DPDy0isPcECPTejwAAe0og/oUdghfCcKavMwn0uavb6xDcdx3bHNL286WUXyyl3LGU8mWnCMFxCYlvZce7p7df3fpG6X9fSrn/SsyP9/mmUso3HfkW+8NKKd97TKBDYEMMz/QV73yHJIXYxm1ryGTI7p+u+MS32eMGMm5UQ+b++IRCcaMct7/BNG7m46b9E1a34m8spXxrKeWS1b6Qz58upcSN/joC/RErCTxT/yGdcZv8uNWC4BH9xwekdQQ6PoB8yWpvfNCId3EPf1D0o1fvJIdcxgeROOtQzmNL3DQHv/hOQNzsf9pKoA97jdeCfmF14xt7/9cqn9OeJSTy8BWOEOijz1+bj+AZ31EIyY+vEN+Q+qNS/LerD0bR99Gv+BDyF6tnOfyQElziOyCR1fGvqbMbed1t9R2NeK6Yv/j75V+u5mZP/7HksREYhwCBHicrnSKQSWDKDXR8ez2E8MYnNBTSFrfTcZMaPyAV//1MX8cl5GNKKXEzfN1SyntWm6LP+Jb9oRQdrRWi89mllNeu/mLIadxUH72BjtvIFx/ZFDeScRt5KEVxVkh67IvfOBKvCMS36EP2onbcLsetbAhXyPHx93ajdNy+hnCH/MTN6OGte/xZ3NDe4UiPcSMbUhe3yesIdLxWEFJ4pq/jt7ZHa64j0Ie/ZSXqx4eFkNdDNvHeeNwSh1zGh4I4K145+IlVM3HrH999CEGNDyEPPnZDHq/7xGsQ8Z2C2BsfrEJI132WEOijz1+bj+M849n+fHXbfXhmPEd8IIrvEBz9OryBjjPevvqDeJUlPkSucwNd6+34M8eHjieuZiVelQkucWMef/2033KT+c8AtRBAYAIBAj0Bnq0IDEzg8B3okI7/eYbnqN1Ah1DGzeTRr0NpC5GMb0+HTMa7ryd9PWj17fbD38IRN3EhnPHucIhrfMWtcrwfGnWPf8VrBN945AY6ZDlE7qhAH77vG3vjXeLXr24g413eEJUQ46ccufUMYYt3ZOMd6dgbQh0CHjfM8Q70mb7itjVuT+MHMuO97Pjv8XX8Bjpe54jXEEJS43WS71y97xtrQ0TjVZKj70CH8H33KeeeJtDxfnb0FTnFV/CN33IRt+yH70BvKtBHb6Djg0a88hLS9x2rVzQig5O+1vkBxJNe4Tj6/LX5mCLQ0fP/LqXETB5+4IrvXsT708ffgY61U2f3OKOY83if/PNPydofIYBARwQIdEdhaAWBxgTiJjZeP4gb2PjBqbhtjFvI+NZyCFHtHeh4V/ObV7eQ8S/+uLU9eusZ3wKPW8d4NeD3T3i2uF3+qZWkxG8Aia8Qz7jZjJviEL8QwHgP+aTfzBDvkMYPrsUPL8aeuBWPPWcS6HjlIF7B+IzVD7DFGXHzF9J7WD9eOQhRjueKHyKMW8LgEzep8d/P9BWvb8T7xCHp8Y7t4Q16fKs+fsAvpDVu5Z+3uo0NKQ45e00p5XNLKX9WSnny6qwsgY4PSSHkIeqRb7zPHO+lB/dtBTqEOz4ExLvU8UEjZiY+4MQtdXzYCLGM2nH7HNnED0Me3l4ffT1jHcmOG+jjHyBOm4+pAh035THHMU9xoxy/hSOk9qTfwjF1duO7FjEPkU/kHzf7R39gs/E/ChyHAAKbEiDQmxKzHoHdIhDiF79xIW4T4/dAxw83xe+B/n8rAh2iGj9QFyIRryOEbMRvsDj+2kC8Ax3vIMcPwkXto1+xL37gLqQlXo2IV0LiW+nxyka8jhGvaITghoSe9OrEdVYCHu/8xu3hSb+F4+gNdJwdzxa31lEvftAsXtMIUT4U6JDlkPq4IY73nkOm41eMhQye9CHg8Hmil3j9JJ4nJPLwK35IM34gLyQ2vkKU4sPJ4Xu28XpG8I+b97iNjl6yBDrOC6mMX88Wt7fRx7cc+y0cm95AH/4WjngVJX5oNAQzfuNHfIUIxhnxykPc7sdvwwjW8W5xxg10nHHafEwV6KO/BzryiA9oZ/o90FNnN35VZMzf4W+Didvu+LDpCwEEBiFAoAcJSpsIINA9gXhdIwT8+K+a675xDSKAAAIIbEaAQG/Gy2oEEEDgJALxA2dxYxmvZZx0W44aAggggMAOESDQOxSmR0EAgUUIxOsJ8X51/A7keGfbFwIIIIDAjhMg0DsesMdDAAEEEEAAAQQQyCVAoHN5qoYAAggggAACCCCw4wQI9I4H7PEQQAABBBBAAAEEcgkQ6FyeI1SL3z3qCwEEEEAAAQT6JBC/GvLcPlvT1SEBAr1/s0Cg9y9zT4wAAgggMA6B+J35dxin3f3slEDvX+4Eev8y98QIIIAAAuMQINADZEWgBwgpuUUCnQxUOQQQQAABBBIJEOhEmHOVItBzke23LoHuNxudIYAAAgggQKAHmAECPUBIyS0S6GSgyiGAAAIIIJBIgEAnwpyrFIGei2y/dQl0v9noDAEEEEAAAQI9wAwQ6AFCSm6RQCcDVQ4BBBBAAIFEAgQ6EeZcpQj0XGT7rUug+81GZwgggAACCBDoAWaAQA8QUnKLBDoZqHIIIPAhAje84Q1TcLzzne9MqaMIAgMSINADhEagBwgpuUUCnQxUOQQQINBmAIFEAgQ6EeZcpQj0XGT7rUug+81GZwgMT8AN9PAReoDlCRDo5TOodkCgq4h2bgGB3rlIPRAC/RAg0P1koZNhCRDoAaIj0AOElNwigU4GqhwCCHyIAIE2DQhMJkCgJyOcvwCBnp9xbycQ6N4S0Q8CO0SAQO9QmB5lKQIEeinyG5xLoDeAtSNLCfSOBOkxEOiRAIHuMRU9DUaAQA8QGIEeIKTkFgl0MlDlEEDgQwQItGlAYDIBAj0Z4fwFCPT8jHs7gUD3loh+ENghAgR6h8L0KEsRINBLkd/gXAK9AawdWUqgdyRIj4FAjwQIdI+p6GkwAgR6gMAI9AAhJbdIoJOBKocAAh8iQKBNAwKTCRDoyQjnL0Cg52fc2wkEurdE9IPADhEg0DsUpkdZigCBXor8BucS6A1g7chSAr0jQXoMBHokQKB7TEVPgxEg0AMERqAHCCm5RQKdDFQ5BBD4EAECbRoQmEyAQE9GOH8BAj0/495OINC9JaIfBHaIAIHeoTA9ylIECPRS5Dc4l0BvAGtHlhLoHQnSYyDQIwEC3WMqehqMAIEeIDACPUBIyS0S6GSgyiGAwIcIEGjTgMBkAgR6MsL5CxDo+Rn3dgKB7i0R/SCwQwQI9A6F6VGWIkCglyK/wbkEegNYO7KUQO9IkB4DgUwC3/qt35pS7hGPeERKnfvf//4pdaLIH/3RH6XVUgiBBgQIdAPIU48g0FMJjrefQI+XmY4RmJ0AgZ4dsQMQWJcAgV6X1ILrCPSC8Bc6mkAvBN6xCPRMgED3nI7e9owAgR4gcAI9QEjJLRLoZKDKIbALBAj0LqToGXaEAIEeIEgCPUBIyS0S6GSgyiGwCwQI9C6k6Bl2hACBHiBIAj1ASMktEuhkoMohsAsECPQupOgZdoQAgR4gSAI9QEjJLRLoZKDKIbALBAj0LqToGXaEAIEeIEgCPUBIyS0S6GSgyiGwCwQI9C6k6Bl2hACBHiBIAj1ASMktEuhkoMohsAsECPQupOgZdoQAgR4gSAI9QEjJLRLoZKDKIbALBAj0LqToGXaEAIEeIEgCPUBIyS0S6GSgyiGwCwQI9C6k6Bl2hACBHiBIAj1ASMktEuhkoMohsAsECPQupOgZdoQAgR4gSAI9QEjJLRLoZKDKIbALBAj0LqToGXaEAIEeIEgCPUBIyS0S6GSgyiGwCwQI9C6k6Bl2hACBHiBIAj1ASMktEuhkoMohsAsECPQupOgZdoQAgR4gSAI9QEjJLRLoZKDKIbALBAj0LqToGXaEAIEeIEgCPUBIyS0S6GSgyiGwCwQI9C6k6Bl2hACBHiBIAj1ASMktEuhkoMohsAsECPQupOgZdoQAgR4gSAI9QEjJLRLoZKDKIbANgbPOyvnH7xOf+MRtjr/ankc/+tEpdbKKPOUpT8kqVbI+HKQ1pBACpxMg0ANMSM4/wQd4UC1+kACBNgwIdECAQJ8eAoHuYEi1sBQBAr0U+Q3OJdAbwNqRpQR6R4L0GGMTINAEeuwJ1v2MBAj0jHCzShPoLJLj1CHQ42Sl0x0mQKAJ9A6Pt0ebRoBAT+PXZDeBboK5q0MIdFdxaGZfCRBoAr2vs++5qwQIdBXR8gsI9PIZtO6AQLcm7jwETiBAoAm0vzEQOAMBAj3AaBDoAUJKbpFAJwNVDoFtCBBoAr3N3NizFwQI9AAxE+gBQkpukUAnA1UOgW0IEGgCvc3c2LMXBAj0ADET6AFCSm6RQCcDVQ6BbQgQaAK9zdzYsxcECPQAMRPoAUJKbpFAJwNVDoFtCBBoAr3N3NizFwQI9AAxE+gBQkpukUAnA1UOgW0IEGgCvc3c2LMXBAj0ADET6AFCSm6RQCcDVQ6BbQgQaAK9zdzYsxcECPQAMRPoAUJKbpFAJwNVDoFtCBBoAr3N3NizFwQI9AAxE+gBQkpukUAnA1UOgW0IEGgCvc3c2LMXBAj0ADET6AFCSm6RQCcDVQ6BbQgQaAK9zdzYsxcECPQAMRPoAUJKbpFAJwNVDoFtCBBoAr3N3NizFwQI9AAxE+gBQkpukUAnA1UOgW0IEGgCvc3c2LMXBAj0ADET6AFCSm6RQCcDVQ6BbQgQaAK9zdzYsxcECPQAMRPoAUJKbpFAJwNVbr8InH322SkPfOGFF6bUefjDH55SJ6vID//wD6eU+v7v//6UOlHk8ssvT6ulEAINCBDoBpCnHkGgpxIcbz+BHi8zHXdEgECfHgaB7mhYtTIqAQI9QHIEeoCQklsk0MlAldsvAgSaQO/XxHvaBQgQ6AWgb3okgd6U2PjrCfT4GXqCBQkQaAK94Pg5ej8IEOgBcibQA4SU3CKBTgaq3H4RINAEer8m3tMuQIBALwB90yMJ9KbExl9PoMfP0BMsSIBAE+gFx8/R+0GAQA+QM4EeIKTkFgl0MlDl9osAgSbQ+zXxnnYBAgR6AeibHkmgNyU2/noCPX6GnmBBAgSaQC84fo7eDwIEeoCcCfQAISW3SKCTgSq3XwQINIHer4n3tAsQINALQN/0SAK9KbHx1xPo8TP0BAsSINAEesHxc/R+ECDQA+RMoAcIKblFAp0MVLn9IkCgCfR+TbynXYAAgV4A+qZHEuhNiY2/nkCPn6EnWJAAgSbQC46fo/eDAIEeIGcCPUBIyS0S6GSgyu0XAQJNoPdr4j3tAgQI9ALQNz2SQG9KbPz1BHr8DD3BggQINIFecPwcvR8ECPQAORPoAUJKbpFAJwNVbr8IEGgCvV8T72kXIECgF4C+6ZEEelNi468n0ONn6AkWJECgCfSC4+fo/SBAoAfImUAPEFJyiwQ6Gahy+0WAQBPo/Zp4T7sAAQK9APRNjyTQmxIbfz2BHj9DT7AgAQJNoBccP0fvBwECPUDOBHqAkJJbJNDJQJXbLwIEmkDv18R72gUIEOgFoG96JIHelNj46wn0+Bl6ggUJPPnJT045/YILLkipc9VVOX9LP+Yxj0np54lPfGJKHUUQ2GMCBHqA8An0ACElt5jzb9vkppRDYBQCBPr0pAj0KJOsz44JEOiOwzlsjUAPEFJyiwQ6Gahy+0WAQBPo/Zp4T7sAAQK9APRNjyTQmxIbfz2BHj9DT7AgAQJNoBccP0fvBwECPUDOBHqAkJJbJNDJQJXbLwIEmkDv18R72gUIEOgFoG96JIHelNj46wn0+Bl6ggUJEGgCveD4OXo/CBDoAXIm0AOElNwigU4Gqtx+ESDQBHq/Jt7TLkCAQC8AfdMjCfSmxMZfT6DHz9ATLEiAQBPoBcfP0ftBgEAPkDOBHiCk5BYJdDJQ5faLAIEm0Ps18Z52AQIEegHomx5JoDclNv56Aj1+hp5gQQIEmkAvOH6O3g8CBHqAnAn0ACElt0igk4Eqt18ECDSB3q+J97QLECDQC0Df9EgCvSmx8dcT6PEz9AQLEiDQBHrB8XP0fhAg0APkTKAHCCm5RQKdDFS5/SJAoAn0fk28p12AAIFeAPqmRxLoTYmNv55Aj5+hJ1iQAIEm0AuOn6P3gwCBHiBnAj1ASMktEuhkoMrtFwECTaD3a+I97QIECPQC0Dc9kkBvSmz89QR6/Aw9wYIECDSBXnD8HL0fBAj0ADkT6AFCSm6RQCcDVW6/CBBoAr1fE+9pFyBAoBeAvumRBHpTYuOvJ9DjZ+gJFiRAoAn0guPn6P0gQKAHyJlADxBScosEOhmocmMQ+Pqv//qURi+++OKUOllFLrjggpRSF154YUodRRBAYDIBAj0Z4fwFCPT8jHs7gUD3loh+mhAg0KdjJtBNxtAhCKxDgECvQ2nhNQR64QAWOJ5ALwDdkcsTINAEevkp1AECaxEg0GthWnYRgV6W/xKnE+glqDtzcQIEmkAvPoQaQGA9AgR6PU6LriLQi+Jf5HACvQh2hy5NgEAT6KVn0PkIrEmAQK8JasllBHpJ+sucTaCX4e7UhQkQaAK98Ag6HoF1CRDodUktuI5ALwh/oaMJ9ELgHbssAQJNoJedQKcjsDYBAr02quUWEujl2C91MoFeirxzFyVAoAn0ogPocATWJ0Cg12e12EoCvRj6xQ4m0Iuhd/CSBAg0gV5y/pyNwAYECPQGsJZaSqCXIr/cuQR6OfZOXpAAgSbQC46foxHYhACB3oTWQmsJ9ELgFzyWQC8I39HLESDQBHq56XMyAhsRINAb4VpmMYFehvuSpxLoJek7ezECBJpALzZ8DkZgMwIEejNei6wm0ItgX/RQAr0ofocvRYBAE+ilZs+5CGxIgEBvCGyJ5QR6CerLnkmgl+Xv9IUIEGgCvdDoORaBTQkQ6E2JLbCeQC8AfeEjCfTCATh+GQIEmkAvM3lORWBjAgR6Y2TtNxDo9syXPpFAL52A8xchQKAJ9CKD51AENidAoDdn1nwHgW6OfPEDCfTiEWhgCQIEmkAvMXfORGALAgR6C2ittxDo1sSXP49AL5+BDhYgQKAJ9AJj50gEtiFAoLeh1ngPgW4MvIPjCHQHIWhhPQJ3uMMd1lu4xqpf+7VfW2NVfckNbnCD+qI1Vlx44YVrrKovueCCC+qLrEAAgZEIEOgB0iLQA4SU3CKBTgaq3HwECHSdLYGuM7ICgcEIEOgBAiPQA4SU3CKBTgaq3HwECHSdLYGuM7ICgcEIEOgBAiPQA4SU3CKBTgaq3HwECHSdLYGuM7ICgcEIEOgBAiPQA4SU3CKBTgaq3HwECHSdLYGuM7ICgcEIEOgBAiPQA4SU3CKBTgaq3HwECHSdLYGuM7ICgcEIEOgBAiPQA4SU3CKBTgaq3HwECHSdLYGuM7ICgcEIEOgBAiPQA4SU3CKBTgaq3HwECHSdLYGuM7ICgcEIEOgBAiPQA4SU3CKBTgaq3HwECHSdLYGuM7ICgcEIEOgBAiPQA4SU3CKBTgaq3HwECHSdLYGuM7ICgcEIEOgBAiPQA4SU3CKBTgaq3HwECHSdLYGuM7ICgcEIEOgBAiPQA4SU3CKBTgaq3HwECHSdLYGuM7ICgcEIEOgBAiPQA4SU3CKBTgaq3HwECHSdLYGuM7ICgcEIEOgBAiPQA4SU3CKBTgaq3HwECHSdLYGuM7ICgcEIEOgBAiPQA4SU3CKBTgaq3HwECHSdLYGuM7ICgcEIEOgBAiPQA4SU3CKBTgaq3HwECHSdLYGuM7ICgcEIEOgBAiPQA4SU3KdjA8YAACAASURBVCKBTgaq3HwECHSdLYGuM7ICgcEIEOgBAiPQA4SU3CKBTgaq3HwECHSdLYGuM7ICgcEIEOgBAiPQA4SU3CKBTgaq3NUJ3PGOd0zB8rznPS+lThQ577zzUmq97GUvS6nzxV/8xSl1rrjiipQ6iiCAQDcECHQ3UZy5EQI9QEjJLRLoZKDKEehtZoBAb0PNHgT2ggCBHiBmAj1ASMktEuhkoMoR6G1mgEBvQ80eBPaCAIEeIGYCPUBIyS0S6GSgyhHobWaAQG9DzR4E9oIAgR4gZgI9QEjJLRLoZKDKEehtZoBAb0PNHgT2ggCBHiBmAj1ASMktEuhkoMoR6G1mgEBvQ80eBPaCAIEeIGYCPUBIyS0S6GSgyhHobWaAQG9DzR4E9oIAgR4gZgI9QEjJLRLoZKDKEehtZoBAb0PNHgT2ggCBHiBmAj1ASMktEuhkoMoR6G1mgEBvQ80eBPaCAIEeIGYCPUBIyS0S6GSgyhHobWaAQG9DzR4E9oIAgR4gZgI9QEjJLRLoZKDKEehtZoBAb0PNHgT2ggCBHiBmAj1ASMktEuhkoMoR6G1mgEBvQ80eBPaCAIEeIGYCPUBIyS0S6GSgyhHobWaAQG9DzR4E9oIAgR4gZgI9QEjJLRLoZKDKEehtZoBAb0PNHgT2ggCBHiBmAj1ASMktEuhkoMoR6G1mgEBvQ80eBPaCAIEeIGYCPUBIyS0S6GSgyhHobWaAQG9DzR4E9oIAgR4gZgI9QEjJLRLoZKDKEehtZoBAb0PNHgT2ggCBHiBmAj1ASMktEuhkoMoR6G1mgEBvQ80eBPaCAIEeIGYCPUBIyS0S6GSgu1TuYz/2Y1Me5yUveUlKnVvd6lYpdaLIK1/5ypRa9773vVPqXHbZZSl1FEEAgZ0jQKAHiJRADxBScosEOhnoLpUj0PU0CXSdkRUIIDCJAIGehK/NZgLdhnNPpxDontLorBcCXQ+EQNcZWYEAApMIEOhJ+NpsJtBtOPd0CoHuKY3OeiHQ9UAIdJ2RFQggMIkAgZ6Er81mAt2Gc0+nEOie0uisFwJdD4RA1xlZgQACkwgQ6En42mwm0G0493QKge4pjc56IdD1QAh0nZEVCCAwiQCBnoSvzWYC3YZzT6cQ6J7S6KwXAl0PhEDXGVmBAAKTCBDoSfjabCbQbTj3dAqB7imNznoh0PVACHSdkRUIIDCJAIGehK/NZgLdhnNPpxDontLorBcCXQ+EQNcZWYEAApMIEOhJ+NpsJtBtOPd0CoHuKY3OeiHQ9UAIdJ2RFQggMIkAgZ6Er81mAt2Gc0+nEOie0uisFwJdD4RA1xlZgQACkwgQ6En42mwm0G0493QKge4pjc56IdD1QAh0nZEVCCAwiQCBnoSvzWYC3YZzT6cQ6J7S6KwXAl0PhEDXGVmBAAKTCBDoSfjabCbQbTj3dAqB7imNznoh0PVACHSdkRUIIDCJAIGehK/NZgLdhnNPpxDontLorBcCXQ+EQNcZWYEAApMIEOhJ+NpsJtBtOPd0CoHuKY3OeiHQ9UAIdJ2RFQggMIkAgZ6Er81mAt2Gc0+nEOie0uisFwJdD4RA1xlZgQACkwgQ6En42mwm0G0493QKge4pjc56IdD1QAh0nZEVCCAwiQCBnoSvzWYC3YZzT6cQ6J7S6KyXZz3rWSkd3fe+902p88Y3vjGlThS5293ullLrrW99a0odRRBAAIEzECDQA4wGgR4gpOQWCXQy0F0qR6DraRLoOiMrEEBgEgECPQlfm80Eug3nnk4h0D2l0VkvBLoeCIGuM7ICAQQmESDQk/C12Uyg23Du6RQC3VManfVCoOuBEOg6IysQQGASAQI9CV+bzQS6DeeeTiHQPaXRWS8Euh4Iga4zsgIBBCYRINCT8LXZTKDbcO7pFALdUxqd9UKg64EQ6DojKxBAYBIBAj0JX5vNBLoN555OIdA9pdFZLwS6HgiBrjOyAgEEJhEg0JPwtdlMoNtw7ukUAt1TGp31QqDrgRDoOiMrEEBgEgECPQlfm80Eug3nnk4h0D2l0VkvBLoeCIGuM7ICAQQmESDQk/C12Uyg23Du6RQC3VManfVCoOuBEOg6IysQQGASAQI9CV+bzQS6DeeeTiHQPaXRWS8Euh4Iga4zsgIBBCYRINCT8LXZTKDbcO7pFALdUxqd9UKg64EQ6DojKxBAYBIBAj0JX5vNBLoN555OIdA9pdFZLwS6HgiBrjOyAgEEJhEg0JPwtdlMoNtw7ukUAt1TGp31QqDrgRDoOiMrEEBgEgECPQlfm80Eug3nnk4h0D2l0VkvBLoeCIGuM7ICAQQmESDQk/C12Uyg23Du6RQC3VManfVCoOuBEOg6IysQQGASAQI9CV+bzQS6DeeeTiHQPaXRWS8Euh4Iga4zsgIBBCYRINCT8LXZTKDbcO7pFALdUxqd9UKg64EQ6DojKxBAYBIBAj0JX5vNBLoN555OIdA9pdFZLwS6HgiBrjOyAgEEJhEg0JPwtdlMoNtw7ukUAt1TGkm93OMe90ip9KIXvSilzpVXXplS5653vWtKnSjyspe9LKXW9a9//ZQ65557bkqd6173uil1bnCDG6TUufa1r51SJ7PIu9/97pRyr3zlK1PqXH755Sl1FNlZAgR6gGgJ9AAhJbdIoJOB9lCOQNdTINCnMyLQ9Rki0HVGVqQQINApGOctQqDn5dtjdQLdYyoTeyLQdYAEmkDXp+T0FQR6KkH71yRAoNcEteQyAr0k/WXOJtDLcJ/1VAJdx0ugCXR9Sgj0VEb2pxAg0CkY5y1CoOfl22N1At1jKhN7ItB1gASaQNenhEBPZWR/CgECnYJx3iIEel6+PVYn0D2mMrEnAl0HSKAJdH1KCPRURvanECDQKRjnLUKg5+XbY3UC3WMqE3si0HWABJpA16eEQE9lZH8KAQKdgnHeIgR6Xr49VifQPaYysScCXQdIoAl0fUoI9FRG9qcQINApGOctQqDn5dtjdQLdYyoTeyLQdYAEmkDXp4RAT2VkfwoBAp2Ccd4iBHpevj1WJ9A9pjKxJwJdB0igCXR9Sgj0VEb2pxAg0CkY5y1CoOfl22N1At1jKhN7ItB1gASaQNenhEBPZWR/CgECnYJx3iIEel6+PVYn0D2mMrEnAl0HSKAJdH1KCPRURvanECDQKRjnLUKg5+XbY3UC3WMqE3si0HWABJpA16eEQE9lZH8KAQKdgnHeIgR6Xr49VifQPaYysScCXQdIoAl0fUoI9FRG9qcQINApGOctQqDn5dtjdQLdYyoTeyLQdYAEmkDXp4RAT2VkfwoBAp2Ccd4iBHpevj1WJ9A9pjKxJwJdB0igCXR9Sgj0VEb2pxAg0CkY5y1CoOfl22N1At1jKhN7ItB1gASaQNenhEBPZWR/CgECnYJx3iIEel6+PVYn0D2mMrEnAl0HSKAJdH1KCPRURvanECDQKRjnLUKg5+XbY3UC3WMqE3t64QtfOLHC/7/9/PPPT6nz/Oc/P6XOc57znJQ6UeQBD3hASq1P+ZRPSalzk5vcJKXONa95zZQ6itQJvPrVr64vWmPFAx/4wDVW1Zf8yZ/8SX2RFSMSINADpEagBwgpuUUCnQy0h3IEup4Cga4zsuJ0AgTahDQiQKAbgZ5yDIGeQm/MvQR6zNxO7ZpA10Ml0HVGVhBoM9AFAQLdRQynN0GgBwgpuUUCnQy0h3IEup4Cga4zsoJAm4EuCBDoLmIg0APE0LRFAt0Ud5vDCHSdM4GuM7KCQJuBLggQ6C5iINADxNC0RQLdFHebwwh0nTOBrjOygkCbgS4IEOguYiDQA8TQtEUC3RR3m8MIdJ0zga4zsoJAm4EuCBDoLmIg0APE0LRFAt0Ud5vDCHSdM4GuM7KCQJuBLggQ6C5iINADxNC0RQLdFHebwwh0nTOBrjOygkCbgS4IEOguYiDQA8TQtEUC3RR3m8MIdJ0zga4zsoJAm4EuCBDoLmIg0APE0LRFAt0Ud5vDCHSdM4GuM7KCQJuBLggQ6C5iINADxNC0RQLdFHebwwh0nTOBrjOygkCbgS4IEOguYiDQA8TQtEUC3RR3m8MIdJ0zga4zsoJAm4EuCBDoLmIg0APE0LRFAt0Ud5vDCHSdM4GuM7KCQJuBLggQ6C5iINADxNC0RQLdFHebwwh0nTOBrjOygkCbgS4IEOguYiDQA8TQtEUC3RR3m8MIdJ0zga4zsoJAm4EuCBDoLmIg0APE0LRFAt0Ud5vDCHSdM4GuM7KCQJuBLggQ6C5iINADxNC0RQLdFHebwwh0nTOBrjOygkCbgS4IEOguYiDQA8TQtEUC3RT36Yede+65Kd1ceumlKXVufOMbp9R517velVLnBje4QUqdHotcccUVKW295z3vSanz7ne/O6XO+973vpQ6mUXOO++8lHJZ83jZZZel9PP5n//5KXX+6q/+KqWOImkECHQayvkKnTVfaZU7JUCgOwqGQJ8eRpawdBT5B1sh0O1SIdCnsybQ7WZxzZMI9JqgllxGoJekv8zZBHoZ7ieeSqAJ9NRxdANdJ0igCXR9SrpaQaC7iuPkZgj0ACElt0igk4FOKUegCfSU+Ym9BLpOkEAT6PqUdLWCQHcVB4EeII4mLRLoJpjXO4RAE+j1JuXMqwh0nSCBJtD1KelqBYHuKg4CPUAcTVok0E0wr3cIgSbQ600KgZ7CiUAT6Cnzs8BeAr0A9E2P9ArHpsTGX0+gO8qQQBPoqePoBrpOkEAT6PqUdLWCQHcVhxvoAeJo0iKBboJ5vUMINIFeb1LcQE/hRKAJ9JT5WWAvgV4A+qZHuoHelNj46wl0RxkSaAI9dRzdQNcJEmgCXZ+SrlYQ6K7icAM9QBxNWiTQTTCvdwiBJtDrTYob6CmcCDSBnjI/C+wl0AtA3/RIN9CbEht/PYHuKEMCTaCnjqMb6DpBAk2g61PS1QoC3VUcbqAHiKNJiwS6Ceb1DiHQBHq9SXEDPYUTgSbQU+Zngb0EegHomx7pBnpTYuOvJ9AdZUigCfTUcXQDXSdIoAl0fUq6WkGgu4rDDfQAcTRpkUA3wbzeIQSaQK83KW6gp3Ai0AR6yvwssJdALwB90yPdQG9KbPz1BLqjDAk0gZ46jm6g6wQJNIGuT0lXKwh0V3G4gR4gjiYtEugmmNc7hEAT6PUmxQ30FE4EmkBPmZ8F9hLoBaBveqQb6E2Jjb+eQHeUIYEm0FPH0Q10nSCBJtD1KelqBYHuKg430APE0aRFAt0E83qHEGgCvd6kuIGewolAE+gp87PAXgK9APRNj3QDvSmx8dcT6I4yvOUtb5nSzaWXXppS55xzzkmpk1Xk7W9/e1apctFFF6XUevGLX5xS501velNKncsvvzylzhVXXJFS5/3vf39Kncwit7nNbVLKXXLJJSl1bn7zm6fUefjDH55S56d/+qdT6iiSRoBAp6GcrxCBno9tr5UJdEfJEOjTwyDQ9WEl0HVGBPp0RgS6PkONVxDoxsC3OY5Ab0Nt7D0EuqP8CDSBnjqOBLpOkEAT6PqUdLWCQHcVx8nNEOgBQkpukUAnA51SjkAT6CnzE3sJdJ0ggSbQ9SnpagWB7ioOAj1AHE1aJNBNMK93CIEm0OtNyplXEeg6QQJNoOtT0tUKAt1VHAR6gDiatEigm2Be7xACTaDXmxQCPYUTgSbQU+Zngb0EegHomx7pFY5NiY2/nkB3lCGBJtBTx9ENdJ0ggSbQ9SnpagWB7ioON9ADxNGkRQLdBPN6hxBoAr3epLiBnsKJQBPoKfOzwF4CvQD0TY90A70psfHXE+iOMiTQBHrqOLqBrhMk0AS6PiVdrSDQXcXhBnqAOJq0SKCbYF7vEAJNoNebFDfQUzgRaAI9ZX4W2EugF4C+6ZFuoDclNv56At1RhgSaQE8dRzfQdYIEmkDXp6SrFQS6qzjcQA8QR5MWCXQTzOsdQqAJ9HqT4gZ6CicCTaCnzM8Cewn0AtA3PdIN9KbExl9PoDvKkEAT6Knj6Aa6TpBAE+j6lHS1gkB3FYcb6AHiaNIigW6Ceb1DCDSBXm9S3EBP4USgCfSU+VlgL4FeAPqmR7qB3pTY+OsJdEcZEmgCPXUc3UDXCRJoAl2fkq5WEOiu4nADPUAcTVok0E0wr3cIgSbQ602KG+gpnAg0gZ4yPwvsJdALQN/0SDfQmxIbfz2B7ihDAk2gp46jG+g6QQJNoOtT0tUKAt1VHG6gB4ijSYsEugnm9Q4h0AR6vUlxAz2FE4Em0FPmZ4G9BHoB6Jse6QZ6U2LjryfQHWV47rnnpnRz6aWXptS58Y1vnFLn7W9/e0qdu9/97il1oshrX/vatFoK7SeBJz3pSSkP/shHPjKlzsUXX5xS5yEPeUhKHUXSCBDoNJTzFSLQ87HttTKB7igZAn16GAS6o2HVSiHQhqARAQLdCPSUYwj0FHpj7iXQHeVGoAl0R+OolQoBAm1EGhEg0I1ATzmGQE+hN+ZeAt1RbgSaQHc0jloh0GagDwIEuo8cTu2CQA8QUnKLBDoZ6JRyBJpAT5kfe9sScAPdlvcen0agBwifQA8QUnKLBDoZ6JRyBJpAT5kfe9sSINBtee/xaQR6gPAJ9AAhJbdIoJOBTilHoAn0lPmxty0BAt2W9x6fRqAHCJ9ADxBScosEOhnolHIEmkBPmR972xIg0G157/FpBHqA8An0ACElt0igk4FOKUegCfSU+bG3LQEC3Zb3Hp9GoAcIn0APEFJyiwQ6GeiUcgSaQE+ZH3vbEiDQbXnv8WkEeoDwCfQAISW3SKCTgU4pR6AJ9JT5sbctAQLdlvcen0agBwifQA8QUnKLBDoZ6JRyBJpAT5kfe9sSINBtee/xaQR6gPAJ9AAhJbdIoJOBTilHoAn0lPmxty0BAt2W9x6fRqAHCJ9ADxBScosEOhnolHIEmkBPmR972xIg0G157/FpBHqA8An0ACElt0igk4FOKUegCfSU+bG3LQEC3Zb3Hp9GoAcIn0APEFJyiwQ6GeiUcgSaQE+ZH3vbEiDQbXnv8WkEeoDwCfQAISW3SKCTgU4pR6AJ9JT5sbctAQLdlvcen0agBwifQA8QUnKLBDoZ6JRyBJpAT5kfe9sSINBtee/xaQR6gPAJ9AAhJbdIoJOBTil31lk5fwtecsklU9r44N7zzz8/pc5zn/vclDr3uc99UuoogkAGgcc+9rEZZcrjH//4lDrPetazUurc7373S6mjSBoBAp2Gcr5COf/2nq8/lfMJEOh8pltXJNCnoyPQW4+WjTMQINAzQFXyJAIEeoC5INADhJTcIoFOBjqlHIEm0FPmx962BAh0W957fBqBHiB8Aj1ASMktEuhkoFPKEWgCPWV+7G1LgEC35b3HpxHoAcIn0AOElNwigU4GOqUcgSbQU+bH3rYECHRb3nt8GoEeIHwCPUBIyS0S6GSgU8oRaAI9ZX7sbUuAQLflvcenEegBwifQA4SU3CKBTgY6pRyBJtBT5sfetgQIdFvee3wagR4gfAI9QEjJLRLoZKBTyhFoAj1lfuxtS4BAt+W9x6cR6AHCJ9ADhJTcIoFOBjqlHIEm0FPmx962BAh0W957fBqBHiB8Aj1ASMktEuhkoFPKEWgCPWV+7G1LgEC35b3HpxHoAcIn0AOElNwigU4GOqUcgSbQU+bH3rYECHRb3nt8GoEeIHwCPUBIyS0S6GSgU8oRaAI9ZX7sbUuAQLflvcenEegBwifQA4SU3CKBTgY6pRyBJtBT5sfetgQIdFvee3wagR4gfAI9QEjJLRLoZKBTyhFoAj1lfuxtS4BAt+W9x6cR6AHCJ9ADhJTcIoFOBjqlHIEm0FPmx962BAh0W957fBqBHiB8Aj1ASMktEuhkoFPKEWgCPWV+7G1LgEC35b3HpxHoAcIn0AOElNwigU4GOqUcgSbQU+bH3rYECHRb3nt8GoEeIHwCPUBIyS0S6GSgU8oRaAI9ZX7sbUuAQLflvcenEegBwifQA4SU3CKBTgbaQ7kLLrggpY0nP/nJKXVe8YpXpNS5053ulFInirz3ve9Nq6XQfhJ42tOelvLgD33oQ1PqXHTRRSl1svpJaUaRIECgB5gDAj1ASMktEuhkoD2UI9D1FAh0nZEVpxMg0CakEQEC3Qj0lGMI9BR6Y+4l0GPmdmrXBLoeKoGuM7KCQJuBLggQ6C5iOL0JAj1ASMktEuhkoD2UI9D1FAh0nZEVBNoMdEGAQHcRA4EeIIamLRLoprjbHEag65wJdJ2RFQTaDHRBgEB3EQOBHiCGpi0S6Ka42xxGoOucCXSdkRUE2gx0QYBAdxEDgR4ghqYtEuimuNscRqDrnAl0nZEVBNoMdEGAQHcRA4EeIIamLRLoprjbHEag65wJdJ2RFQTaDHRBgEB3EQOBHiCGpi0S6Ka42xxGoOucCXSdkRUE2gx0QYBAdxEDgR4ghqYtEuimuNscRqDrnAl0nZEVBNoMdEGAQHcRA4EeIIamLRLoprjbHEag65wJdJ2RFQTaDHRBgEB3EQOBHiCGpi0S6Ka42xxGoOucCXSdkRUE2gx0QYBAdxEDgR4ghqYtEuimuNscRqDrnAl0nZEVBNoMdEGAQHcRA4EeIIamLRLoprjbHEag65wJdJ2RFQTaDHRBgEB3EQOBHiCGpi0S6Ka42xxGoOucCXSdkRUE2gx0QYBAdxEDgR4ghqYtEuimuNscRqDrnAl0nZEVBNoMdEGAQHcRA4EeIIamLRLoprjbHEag65wJdJ2RFQTaDHRBgEB3EQOBHiCGpi0S6Ka42xxGoOucCXSdkRUE2gx0QYBAdxEDgR4ghqYtEuimuNscRqDrnAl0nZEVBNoMdEGAQHcRA4EeIIamLRLoprjbHHarW90q5aBXvepVKXWue93rptR5xjOekVInijz4wQ9OqXXllVem1FGkHYG73OUuKYddcsklKXWuc53rpNR5wAMekFLnmc98ZkodRdIIEOg0lPMVOmu+0ip3SoBAdxrMlLYIdJ0ega4z2tUVBPr0ZAl0d5NPoLuL5OoNEegBQkpukUAnA+2hHIGup0Cg64x2dQWBJtCDzTaBHiAwAj1ASMktEuhkoD2UI9D1FAh0ndGuriDQBHqw2SbQAwRGoAcIKblFAp0MtIdyBLqeAoGuM9rVFQSaQA822wR6gMAI9AAhJbdIoJOB9lCOQNdTINB1Rru6gkAT6MFmm0APEBiBHiCk5BYJdDLQHsoR6HoKBLrOaFdXEGgCPdhsE+gBAiPQA4SU3CKBTgbaQzkCXU+BQNcZ7eoKAk2gB5ttAj1AYAR6gJCSWyTQyUB7KEeg6ykQ6DqjXV1BoAn0YLNNoAcIjEAPEFJyiwQ6GWgP5Qh0PQUCXWe0qysINIEebLYJ9ACBEegBQkpukUAnA+2hHIGup0Cg64x2dQWBJtCDzTaBHiAwAj1ASMktEuhkoD2UI9D1FAh0ndGuriDQBHqw2SbQAwRGoAcIKblFAp0MtIdyBLqeAoGuM9rVFQSaQA822wR6gMAI9AAhJbdIoJOB9lCOQNdTINB1Rru6gkAT6MFmm0APEBiBHiCk5BYJdDLQHsoR6HoKBLrOaFdXEGgCPdhsE+gBAiPQA4SU3CKBTgbaQzkCXU+BQNcZ7eoKAk2gB5ttAj1AYAR6gJCSWyTQyUB7KEeg6ykQ6DqjXV1BoAn0YLNNoAcIjEAPEFJyiwQ6GWgP5Qh0PQUCXWe0qysINIEebLYJ9ACBEegBQkpukUAnA92lcg960INSHufpT396Sp1rXOMaKXWiyLOf/eyUWo961KNS6rz1rW9NqbOrRT7mYz4m7dFe+tKXptS69a1vnVLn+c9/fkqd+973vil1rrzyypQ6iqQRINBpKOcrRKDnY9trZQLdazId9EWg6yEQ6DqjjBUEuk6RQNcZDbqCQA8QHIEeIKTkFgl0MtBdKkeg62kS6DqjjBUEuk6RQNcZDbqCQA8QHIEeIKTkFgl0MtBdKkeg62kS6DqjjBUEuk6RQNcZDbqCQA8QHIEeIKTkFgl0MtBdKkeg62kS6DqjjBUEuk6RQNcZDbqCQA8QHIEeIKTkFgl0MtBdKkeg62kS6DqjjBUEuk6RQNcZDbqCQA8QHIEeIKTkFgl0MtBdKkeg62kS6DqjjBUEuk6RQNcZDbqCQA8QHIEeIKTkFgl0MtBdKkeg62kS6DqjjBUEuk6RQNcZDbqCQA8QHIEeIKTkFgl0MtBdKkeg62kS6DqjjBUEuk6RQNcZDbqCQA8QHIEeIKTkFgl0MtBdKkeg62kS6DqjjBUEuk6RQNcZDbqCQA8QHIEeIKTkFgl0MtBdKkeg62kS6DqjjBUEuk6RQNcZDbqCQA8QHIEeIKTkFgl0MtBdKkeg62kS6DqjjBUEuk6RQNcZDbqCQA8QHIEeIKTkFgl0MtBdKkeg62kS6DqjjBUEuk6RQNcZDbqCQA8QHIEeIKTkFgl0MtBdKkeg62kS6DqjjBUEuk6RQNcZDbqCQA8QHIEeIKTkFgl0MtBdKkeg62kS6DqjjBUEuk6RQNcZDbqCQA8QHIEeIKTkFgl0MtBdKkeg62kS6DqjjBUEuk6RQNcZDbqCQA8QHIEeIKTkFgl0MtBdKkeg62kS6DqjjBUEuk6RQNcZDbqCQA8QHIEeIKTkFgl0MtBdKkeg62kS6DqjjBUEuk6RQNcZDbqCQA8QHIEeIKTkFgl0MlDlrk7gYQ97WAqWpzzlKSl1osjZZ5+dUuutb31rSp2nPvWpKXVe+MIXptR53etel1LnWte6Vkqdiy++OKVOFLn3ve+dUuuyyy5LqXPnO985pc7b3va2lDqKdEeAQHcXydUbItADhJTcIoFOBqocgd5mBgj06dQIdH2qCHSd0aArCPQAwRHoAUJKbpFAJwNVjkBvMwMEmkBvMzdH9xDoqQS73U+gu43mQ40R6AFCSm6RQCcDVY5AbzMDBJpAbzM3BHoqtSH2E+gBYiLQ4Q0qMAAAIABJREFUA4SU3CKBTgaqHIHeZgYINIHeZm4I9FRqQ+wn0APERKAHCCm5RQKdDFQ5Ar3NDBBoAr3N3BDoqdSG2E+gB4iJQA8QUnKLBDoZqHIEepsZINAEepu5IdBTqQ2xn0APEBOBHiCk5BYJdDJQ5Qj0NjNAoAn0NnNDoKdSG2I/gR4gJgI9QEjJLRLoZKDKEehtZoBAE+ht5oZAT6U2xH4CPUBMBHqAkJJbJNDJQJUj0NvMAIEm0NvMDYGeSm2I/QR6gJgI9AAhJbdIoJOBKkegt5kBAk2gt5kbAj2V2hD7CfQAMRHoAUJKbpFAJwNVjkBvMwMEmkBvMzcEeiq1IfYT6AFiItADhJTcIoFOBqocgd5mBgg0gd5mbgj0VGpD7CfQA8REoAcIKblFAp0MVDkCvc0MEGgCvc3cEOip1IbYT6AHiIlADxBScosEOhmocgR6mxkg0AR6m7kh0FOpDbGfQA8QE4EeIKTkFgl0MlDlCPQ2M0CgCfQ2c0Ogp1IbYj+BHiAmAj1ASMktEuhkoMoR6G1mgEAT6G3mhkBPpTbEfgI9QEwEeoCQklsk0MlAlSPQ28wAgSbQ28wNgZ5KbYj9BHqAmAj0ACElt0igk4EqNx+B888/P634j/3Yj6XUuu1tb5tSJ6vIBz7wgZRSb37zm1PqXPOa10yp83Ef93EpdaLI+9///pRa97znPVPqvPjFL06po8jOEiDQA0RLoAcIKblFAp0MVLn5CBDoOlsCXWdEoOuMrOiKAIHuKo6TmyHQA4SU3CKBTgaq3HwECHSdLYGuMyLQdUZWdEWAQHcVB4EeII4mLRLoJpgdkkGAQNcpEug6IwJdZ2RFVwQIdFdxEOgB4mjSIoFugtkhGQQIdJ0iga4zItB1RlZ0RYBAdxUHgR4gjiYtEugmmB2SQYBA1ykS6DojAl1nZEVXBAh0V3EQ6AHiaNIigW6C2SEZBAh0nSKBrjMi0HVGVnRFgEB3FQeBHiCOJi0S6CaYHZJBgEDXKRLoOiMCXWdkRVcECHRXcRDoAeJo0iKBboLZIRkECHSdIoGuMyLQdUZWdEWAQHcVB4EeII4mLRLoJpgdkkGAQNcpEug6IwJdZ2RFVwQIdFdxEOgB4mjSIoFugtkhGQQIdJ0iga4zItB1RlZ0RYBAdxUHgR4gjiYtEugmmB2SQYBA1ykS6DojAl1nZEVXBAh0V3EQ6AHiaNIigW6C2SEZBAh0nSKBrjMi0HVGVnRFgEB3FQeBHiCOJi0S6CaYHZJBgEDXKRLoOiMCXWdkRVcECHRXcRDoAeJo0iKBboLZIRkECHSdIoGuMyLQdUZWdEWAQHcVB4EeII4mLRLoJpgdkkGAQNcpEug6IwJdZ2RFVwQIdFdxEOgB4mjSIoFugtkhGQQIdJ0iga4zItB1RlZ0RYBAdxUHgR4gjiYtEugmmB2SQYBA1ykS6DojAl1nZEVXBAh0V3EQ6AHiaNIigW6C2SG9EbjRjW6U0tKDH/zglDr3ute9Uurc4Q53SKlzzjnnpNTJKvKOd7wjq1R57GMfm1LroosuSqmjCAIVAgR6gBE5a4AetZhLgEDn8lRtEAIE+vSgCHR9kAl0nZEVKQQIdArGeYsQ6Hn59lidQPeYip5mJ0CgCfTUISPQUwnavyYBAr0mqCWXEegl6S9zNoFehrtTFyZAoAn01BEk0FMJ2r8mAQK9JqgllxHoJekvczaBXoa7UxcmQKAJ9NQRJNBTCdq/JgECvSaoJZcR6CXpL3M2gV6Gu1MXJkCgCfTUESTQUwnavyYBAr0mqCWXEegl6S9zNoFehrtTFyZAoAn01BEk0FMJ2r8mAQK9JqgllxHoJekvczaBXoa7UxcmQKAJ9NQRJNBTCdq/JgECvSaoJZcR6CXpL3M2gV6Gu1MXJkCgCfTUESTQUwnavyYBAr0mqCWXEegl6S9zNoFehrtTFyZAoAn01BEk0FMJ2r8mAQK9JqgllxHoJekvczaBXoa7UxcmQKAJ9NQRJNBTCdq/JgECvSaoJZcR6CXpL3M2gV6Gu1MXJkCgCfTUESTQUwnavyYBAr0mqCWXEegl6S9zNoFehrtTFyZAoAn01BEk0FMJ2r8mAQK9JqgllxHoJekvczaBXoa7UxcmQKAJ9NQRJNBTCdq/JgECvSaoJZcR6CXpL3M2gV6Gu1MXJkCgCfTUESTQUwnavyYBAr0mqCWXEegl6S9zNoFehrtTFyZAoAn01BEk0FMJ2r8mAQK9JqgllxHoJekvczaBXoa7UxcmQKAJ9NQRJNBTCdq/JgECvSaoJZcR6CXpL3M2gV6Gu1MXJkCgCfTUESTQUwnavyYBAr0mqCWXEegl6S9zNoFehrtTEZiFwG1uc5uUure4xS1S6lx1Vc4/Yl7/+ten9BNF/uIv/iKtlkIINCBAoBtAnnoEgZ5KcLz9Of92G++5dYzAThIg0PVYCXSdkRVdESDQXcVxcjMEeoCQklsk0MlAlUNgSQIEuk6fQNcZWdEVAQLdVRwEeoA4mrRIoJtgdggCbQgQ6DpnAl1nZEVXBAh0V3EQ6AHiaNIigW6C2SEItCFAoOucCXSdkRVdESDQXcVBoAeIo0mLBLoJZocg0IYAga5zJtB1RlZ0RYBAdxUHgR4gjiYtEugmmB2CQBsCBLrOmUDXGVnRFQEC3VUcBHqAOJq0SKCbYHYIAm0IEOg6ZwJdZ2RFVwQIdFdxEOgB4mjSIoFugtkhCLQhQKDrnAl0nZEVXREg0F3FQaAHiKNJiwS6CWaHINCGAIGucybQdUZWdEWAQHcVB4EeII4mLRLoJpgdgkAbAgS6zplA1xlZ0RUBAt1VHAR6gDiatEigm2B2CAJtCBDoOmcCXWdkRVcECHRXcRDoAeJo0iKBboLZIQi0IUCg65wJdJ2RFV0RINBdxUGgB4ijSYsEuglmhyDQhgCBrnMm0HVGVnRFgEB3FQeBHiCOJi0S6CaYHYJAGwIEus6ZQNcZWdEVAQLdVRwEeoA4mrRIoJtgdggCbQgQ6DpnAl1nZEVXBAh0V3EQ6AHiaNIigW6C2SEItCFAoOucCXSdkRVdESDQXcVBoAeIo0mLBLoJZocg0IYAga5zJtB1RlZ0RYBAdxUHgR4gjiYtEugmmB2CAAIIIIDAVgQI9FbY2m46q+1xTuuAAIHuIAQtIIAAAgggcAYCBHqA0SDQA4SU3CKBTgaqHAIIIIAAAokECHQizLlKEei5yPZbl0D3m43OEEAAAQQQINADzACBHiCk5BYJdDJQ5RBAAAEEEEgkQKATYc5VikDPRbbfugS632x0hgACCCCAAIEeYAYI9AAhJbdIoJOBKocAAggggEAiAQKdCHOuUgR6LrL91iXQ/WajMwQQQAABBAj0ADNAoAcIKblFAp0MVDkEEEAAAQQSCRDoRJhzlSLQc5Htty6B7jcbnSGAAAIIIECgB5gBAj1ASMktEuhkoMohgAACCCCQSIBAJ8KcqxSBnotsv3UJdL/Z6AwBBBBAAAECPcAMEOgBQkpukUAnA1UOAQQQQACBRAIEOhHmXKUI9Fxk+61LoPvNRmcIIIAAAggQ6AFmgEAPEFJyiwQ6GahyCCCAAAIIJBIg0Ikw5ypFoOci229dAt1vNjpDAAEEEECAQA8wAwR6gJCSWyTQyUCVQwABBBBAIJEAgU6EOVcpAj0X2X7rEuh+s9EZAggggAACBHqAGSDQA4SU3CKBTgaqHAIIIIAAAokECHQizLlKEei5yPZbl0D3m43OEEAAAQQQINADzACBHiCk5BYJdDJQ5RBAAAEEEEgkQKATYc5VikDPRbbfugS632x0hgACCCCAAIEeYAYI9AAhJbdIoJOBKocAAggggEAiAQKdCHOuUgR6LrL91iXQ/WajMwQQQAABBAj0ADNAoAcIKblFAp0MVDkEEEAAAQQSCRDoRJhzlSLQc5Htty6B7jcbnSGAAAIIIECgB5gBAj1ASMktEuhkoMohgAACCCCQSIBAJ8KcqxSBnotsv3UJdL/Z6AwBBBBAAAECPcAMEOgBQkpukUAnA1UOAQQQQACBRAIEOhHmXKUI9Fxk+61LoPvNRmcIIIAAAggQ6AFmgEAPEFJyiwQ6GahyCCCAAAIIJBIg0Ikw5ypFoOci229dAt1vNjpDAAEEEECAQA8wAwR6gJCSWyTQyUCVQwABBBBAIJEAgU6EOVcpAj0X2X7rEuh+s9EZAggggAACBHqAGSDQA4SU3CKBTgaqHAIIIIAAAokECHQizLlKEei5yPZbl0D3m43OEEAAAQQQINADzACBHiCk5BYJdDJQ5RBAAAEEEEgkQKATYc5VikDPRbbfugS632x0hgACCCCAAIEeYAYI9AAhJbdIoJOBKocAAggggEAiAQKdCHOuUgR6LrL91iXQ/WajMwQQQAABBAj0ADNAoAcIKblFAp0MVDkEEEAAAQQSCRDoRJhzlSLQc5Htty6B7jcbnSGAAAIIIECgB5gBAj1ASMktEuhkoMohgAACCCCQSIBAJ8KcqxSBnotsv3UJdL/Z6AwBBBBAAAECPcAMEOgBQkpukUAnA1UOAQQQQACBRAIEOhHmXKUI9Fxk+61LoPvNRmcIIIAAAggQ6AFmgEAPEFJyiwQ6GahyCCCAAAIIJBIg0Ikw5ypFoOci229dAt1vNjpDAAEEEECAQA8wAwR6gJCSWyTQyUCVQwABBBBAIJEAgU6EOVcpAj0X2X7rEuh+s9EZAggggAACBHqAGSDQA4SU3CKBTgaqHAIIIIAAAokECHQizLlKEei5yPZbl0D3m43OEEAAAQQQINADzACBHiCk5BYJdDJQ5RBAAAEEEEgkQKATYc5VikDPRbbfugS632x0hgACCCCAAIEeYAYI9AAhJbdIoJOBKocAAggggEAiAQKdCHOuUgR6LrL91iXQ/WajMwQQQAABBAj0ADNAoAcIKblFAp0MVDkEEEAAAQQSCRDoRJhzlSLQc5Htty6B7jcbnSGAAAIIIECgB5gBAj1ASMktEuhkoMohgAACCCCQSIBAJ8KcqxSBnotsv3UJdL/Z6AwBBBBAAAECPcAMEOgBQkpukUAnA1UOAQQQQACBRAIEOhHmXKUI9Fxk+61LoPvNRmcIIIAAAggQ6AFmgEAPEFJyiwQ6GahyCCCAAAIIJBIg0Ikw5ypFoOci229dAt1vNjpDAAEEEECAQA8wAwR6gJCSW/ybUspbkmsqhwACCCCAAAI5BG5RSjk3p5QqcxEg0HORVRcBBBBAAAEEEEBgJwkQ6J2M1UMhgAACCCCAAAIIzEWAQM9FVl0EEEAAAQQQQACBnSRAoHcyVg+FAAIIIIAAAgggMBcBAj0XWXURQAABBBBAAAEEdpIAgd7JWD0UAggggAACCCCAwFwECPRcZDute8Mb3vCqW9wifkOOLwQQQAABBBDojcArXvGKd/g1dr2lcvV+CHT/GaV2ePvb3/6ql7zkJak1FUMAAQQQQACBHALXu971/D9SyUE5axUCPSve/ooT6P4y0RECCCCAAAKHBAj0GLNAoMfIKa1LAp2GUiEEEEAAAQTSCRDodKSzFCTQs2DttyiB7jcbnSGAAAIIIECgx5gBAj1GTmldEug0lAohgAACCCCQToBApyOdpSCBngVrv0UJdL/Z6AwBBBBAAAECPcYMEOgxckrrkkCnoVQIAQQQQACBdAIEOh3pLAUJ9CxY+y1KoPvNRmcIIIAAAggQ6DFmgECPkVNalwQ6DaVCCCCAAAIIpBMg0OlIZylIoGfB2m9RAt1vNjpDAAEEEECAQI8xAwR6jJzSuiTQaSgVQgABBBBAIJ0AgU5HOktBAj0L1n6LEuh+s9EZAggggAACBHqMGSDQY+SU1iWBTkOpEAIIIIAAAukECHQ60lkKEuhZsPZblED3m43OEEAAAQQQINBjzACBHiOntC4JdBpKhRBAAAEEEEgnQKDTkc5SkEDPgrXfogS632x0hgACCCCAAIEeYwYI9Bg5pXVJoNNQKoQAAggggEA6AQKdjnSWggR6Fqz9FiXQ/WajMwQQQAABBAj0GDNAoMfIKa1LAp2GUiEEEEAAAQTSCRDodKSzFCTQs2DttyiB7jcbnc1D4K//+q/LYx7zmPLHf/zH5VrXula5+c1vXp7whCeUT/zET5znwFOq/vzP/3y5+93vXs4777yrrTrtz85U8uEPf3h57nOfW97whjeUj/7ojz5Y9h3f8R3lp37qp8qb3/zmcqMb3ahc73rXK9/yLd9SfvAHf/Dgzy+88MJy+eWXl+/6ru86+GvXve51yyMe8YjyB3/wBwec/umf/qn84z/+Y7nPfe5zwOqpT33qwb4/+7M/O2D2kR/5keUe97hH+b7v+75T+f3d3/1dedaznlW+4Ru+4dR1v/M7v3PQU6z1hQACJf6efXkp5Q5Y9E2AQPedT3p3BDodqYIdE7jqqqsOZO8BD3hAechDHnLQ6atf/eoDgfyCL/iCpp3/8z//c7nXve5VHve4x5XP+qzPutrZ97znPc/4Z6cJ9Kte9apywQUXlK/+6q8uH/jAB8qd7nSn8q53vav87u/+7oFAn3vuueWmN71p+e3f/u2D/30mgY6efvZnf7bc7na3K9Hrn//5n5dP/uRP/uDRn/Zpn1Ze8pKXHNRY5+stb3lLud/97ld+//d/n0CvA8waBFYECPQYo0Cgx8gprUsCnYZSoQEIhPA9/vGPL7/6q796tW5Drr/ne76nvPjFLy5nnXVW+fZv//bylV/5lSVuRGNPiOKll15aPvMzP7M8/elPP1gXt8QhmfEV6378x3+8/NIv/VL5jd/4jYPb3Li9/fiP//jykz/5kwc3uyGdX/M1X1N+8zd/szz4wQ8uj3rUow5un88555zy67/+6wf/GV/Pe97zyjd+4zd+2J+FeH73d393ef/7338g3E960pMObtCPfsUNdNwKx9roI543akWvh7Ib533bt31bec973lO+93u/94wCHbfNL3/5yw+E+6Sv0wT6T//0Tw/6v/LKKw8k/ud+7ucOPgxccsklB/3d7W53K/GdgK/4iq8oX/ZlX3ZQPj7QBO+4OT+8gY4eI4fXvva1BxL/nd/5nR9cP8C4aRGBFAIEOgXj7EUI9OyI+zqAQPeVh27mJRCvH8RNaLyycfzrl3/5l8vFF19cnvOc55S//du/LXe9610PRDduXu9///sfSGnI55d8yZccyODnfM7nlE//9E8vf/iHf1g+6qM+6kCGP/dzP/fgzx/4wAeWZz/72Qd/PUQ3XoF47GMfeyDQD33oQ8sjH/nIg+NPu2U++mfve9/7yu1vf/vy/Oc//0BAH/awh5XP+IzPKN/8zd98NYE+//zzDwQ0zg9B/qqv+qoSYn1UoF/3utcd3LjHrXR8ADjpFY5gFOJ/5zvf+YO39te+9rU/eN5pAh2CHnzi7PgQEfL79re//cNuoF/60peWn/iJnyjPfOYzy9///d8fnPOKV7yi/N7v/d4HBTpeC/mkT/qkg9v0eAUkxDv2BVdfCOwLAQI9RtIEeoyc0rok0GkoFRqAwGkCHYL7qZ/6qeVrv/ZrD54k3tW9973vfXAj+iM/8iMlBDu+DkU5pC7eFf6iL/qig5vUkOmQ7BC8uH292c1udrA+BPKOd7zjgSyGdMYtbNzubiLQr3nNaw5uYg9vzuP1i6c97WkHN+BHv0KUQ6Avu+yygxvviy666ECSo7ejAv2Xf/mXBx8Czj777INb75MEOuq+6U1vOvgQETIet/LR++HXaQIdt9/BLD54xGsqt771rQ8+uBx/hSM+cLzgBS84+GAQZ/3AD/zAwU3+4Q30Xe5ylxIfHq5xjWscHBuvosQ73iHVvhDYFwIEeoykCfQYOaV1SaDTUCo0AIEQz7hZPekVjviBuZDCkwT66A+1PfrRjz54hSJumQ9FNl7H+Jmf+ZnyjGc8o7zwhS88eH0i/vfxr+PSue4NdLynHT8MuK5Af/Znf3b5wi/8woN3veNVkqPnxi16CPQ73/nOA/mP54jXV47/EOHR3uO1kVvd6lYHN8SH7zzX3oEOIX7Ri1508EOH8WrLLW95y6sJdNzOh8SHoMdt921ve9sPE+joLz4ELPEDngOMsxb3hACBHiNoAj1GTmldEug0lAoNQCBEMX7rxYMe9KDydV/3dQcdx3u+733vew+EMl7hCJmLm864/Yzb19e//vUf9lshjgp0vJoQr1KEUMctdPymine84x0HYvorv/Ir5RM+4RPKFVdcUd72trcdSOBx6Ywb2fiNGLH++NfRP4tb2DjjsGbcNMet8jd90zd92LbDG+joJZ4lXkMJ8T1JoGNjvPMdzxvvZR8X6JD1L/3SLz24eY5XPuK/v/GNbzz4rRvxdZpAx2/8CGGOvfHBJG7c48Y+njPeZz78itc64rWMm9zkJuW3fuu3Dv7y0RvoeIXjH/7hHw5us6NW/IBk8PaFwD4RINBjpE2gx8gprUsCnYZSoUEIxO1rvK4Rt6nxTu/hr7EL2T3TDxGe6QY6HjmE+hd+4RcO5PI617nOAYV4XSLeP47XNw5FNW6bj0tnvBYSknj8hwhjz/E/W/eHCOMVjhDoo19nEugQ2PgtG/FbO44LdHzACGGNZ4pXKIJN/AaTw6/TBPpHf/RHyy/+4i8e3C6HHMct8g1veMODH5wMgT58jzxqxWsyX/7lX/7B34pyVKDjg01kFc8eH34iK7/ebpC/0bSZRoBAp6GctRCBnhVvf8UJdH+Z6AiBfSEQt/Of93mfd3DrfP3rX39fHttzIrARAQK9Ea7FFhPoxdAvczCBXoa7UxHYdwLxykb8FpHD/9t3Hp4fgTMRINBjzAaBHiOntC4JdBpKhRBAAAEEEEgnQKDTkc5SkEDPgrXfogS632x0hgACCCCAAIEeYwYI9Bg5pXVJoNNQKoQAAggggEA6AQKdjnSWggR6Fqz9FiXQ/WajMwQQQAABBAj0GDNAoMfIKa1LAp2GUiEEEEAAAQTSCRDodKSzFCTQs2DttyiB7jcbnSGAAAIIIECgx5gBAj1GTmldEug0lAohgAACCCCQToBApyOdpSCBngVrv0UJdL/Z6AwBBBBAAAECPcYMEOgxckrrkkCnoVQIAQQQQACBdAIEOh3pLAUJ9CxY+y1KoPvNRmcIIIAAAggQ6DFmgECPkVNalwQ6DaVCCCCAAAIIpBMg0OlIZylIoGfB2m9RAt1vNjpDAAEEEECAQI8xAwR6jJzSuiTQaSgVQgABBBBAIJ0AgU5HOktBAj0L1n6LEuh+s9EZAggggAACBHqMGSDQY+SU1iWBTkOpEAIIIIAAAukECHQ60lkKEuhZsPZblED3m43OEEAAAQQQINBjzACBHiOntC4JdBpKhRBAAAEEEEgnQKDTkc5SkEDPgrXfogS632x0hgACCCCAAIEeYwYI9Bg5pXVJoNNQKoQAAggggEA6AQKdjnSWggR6Fqz9FiXQ/WajMwQQQAABBAj0GDNAoMfIKa1LAp2GUiEEEEAAAQTSCRDodKSzFCTQs2DtuujflFLe0nWHmkMAAQQQQGB/CdyilHLu/j7+GE9OoMfISZcIIIAAAggggAACnRAg0J0EoQ0EEEAAAQQQQACBMQgQ6DFy0iUCCCCAAAIIIIBAJwQIdCdBaAMBBBBAAAEEEEBgDAIEeoycdIkAAggggAACCCDQCQEC3UkQ2kAAAQQQQAABBBAYgwCBHiOntC4/4pzrXXWN698krZ5CCGxL4HZ/9Ybympveetvt9iEwLIHb3ez6w/au8fkJvPzlL3+HX2M3P+epJxDoqQQH23/Nm976qvMe9OTButbuLhK47Ie+vNzyMS/YxUfzTAicSuCyJ3wZQgickcBZZ5318lLKHSDqmwCB7juf9O4IdDpSBbckQKC3BGfb8AQI9PARzvoABHpWvGnFCXQayjEKEegxctqHLgn0PqTsGU8iQKDNxWkECPQY80Ggx8gprUsCnYZSoYkECPREgLYPS4BADxtdk8YJdBPMkw8h0JMRjlWAQI+V1y53S6B3OV3PdhoBAm0+3ECPPwMEevwMN3oCAr0RLotnJECgZ4SrdNcECHTX8SzenBvoxSNYqwECvRam3VlEoHcny9GfhECPnqD+tyVAoLcltx/7CPQYORPoMXJK65JAp6FUaCIBAj0RoO3DEiDQw0bXpHEC3QTz5EMI9GSEYxUg0GPltcvdEuhdTteznUaAQJuP0wgQ6DHmg0CPkVNalwQ6DaVCEwkQ6IkAbR+WAIEeNromjRPoJpgnH0KgJyMcqwCBHiuvXe6WQO9yup7NDbQZ2JYAgd6WXNt9BLot78VPI9CLR6CBFQECbRT2lYAb6H1Nfr3nJtDrcVp61T4L9OWllOseC+DhpZQrSin/fWIwTyqlvKWU8uRVnReVUt5aSnno6n//aCnlbaWU/1FKubCUct8TzvvtUsq3lVL+qJTyXaWUH1ytuWUp5QWllE/bpkcCvQ01e+YgQKDnoKrmCAQI9AgpLdfjBgJ97VLK/yqlXKuUco1Syv8spfynUsrvlFI+evUENyml/EEp5StOeKJ/LqW8ZvXX/6KU8q9X//1bSimPLKV8Qinl3FLKO5aj0e/JBHqebP5tKSX+736llI8opfxhKeWfSimfvzru91bD+funHH9UoI/KPoGeJzNVGxMg0I2BO64bAgS6myi6bGQDgQ6H+6hSSjjC2aWUl5ZSLiilvOzIgz27lPLLZ7gYPOkiMbbevpTyrlJKeMgdCPTJY0KgP5zLf14N4o+sBicE926llH9RSnnI6lPdR5ZSnlBKuevqU99PlFJ++hjef7n6xPexpZTbrW6SzyulfNXqhvuvSynxqTDWHd4mn1NK+ZlSyqeUUv60lBKi/M2r2+lvX31KfG0p5T+WUl64+hvlC1Y32f+mlPLedf5J4AZ6HUrWtCBAoFtQdkaPBAh0j6n009MGAn206eusvOAbSymHl3NxCx2x3saRAAAITklEQVQ3y7copfzDCU94JoE+XHoZgT7zXBDo0wX65aWUR5dS7llK+Q+llHuUUh62kt/HrQT6d1e3zW8+hjkG74tKKf+qlBKcb1ZKiZvnvy+lPH71Z0dvk6N+vJbx4FLKp5dS/riU8nmrVziO30C/YTXUryyl/FIp5fmllGes87c/gV6HkjUtCBDoFpSd0SMBAt1jKv30tKFAx6VeuMqtSylxofeYI0/y71avZZz0mmgse38pJTwi/jMuBp93gse4gT7DaBDo0wU6bntDkD9m9Z8xoPGOUQhuvCsdX9cvpfz7UsqvHWP886WUX1kJ9I+tBDpujEOgb1RKeezqlvnwBjoGN96H/s1VnRDokPV4B/q4QL+4lPKJq3XxN0t86yaEvvpFoKuILGhEgEA3Au2Y7ggQ6O4i6aqhDQX6sPf4TvlzSynfWkr5k9VfjO9WP72UEq9xnPQV3wX/36WUW63c44tLKW88stAN9CmTQaBPF+jDH+K78Upk48Y4BvG/llLiBwNP+4rXLz6plHLnUsrnrET7Watvo1y8kuujN9Ah0P93KeW31hDooz9EGD3GD0PG6yfVLwJdRWRBIwIEuhFox3RHgEB3F0lXDW0p0PEM8QOE7ymlxGuocVH3+tXl3fvWeMD/tnqlNC4JD78INIE+kcBJ7/4cfwf6JIGOW+F4pSN+SPDKUsptVu8hx9Ae/frMUspzSilvWr36EX8W32aJVzniVY34qdbjr3DE+8/xmzriz+PbKoevcMTL/PHOdJx3/IcICfQa/2SwpD8CBLq/THTUhgCBbsN51FM2EOj4DRnhBX9XSomfo4rvhP/QSoTjt4rFLy540Bk43GD1nfR/LKXEJWG8Yho/T3UpgV5vcvb5BvoDq29dHJKK1yyud+yHCE8S6PitGvG6xL1W7zb/zerXw8SrGUe/4r2kEN94LeO7V38Qn/BioONmOr6OyvDRHyIMeY7XRR6xuvmOvyHi18vEax3xWokb6PXm26qOCRDojsPR2qwECPSseIcvvoFAx+ukP1tKCd8IN4mfifovKwDxGzTiveZfPQIk3mcOsY6LunilNH4BQrhQ7I1fu3vRam24x3eUUm5aSnl7KeWSI7+Gd3i+WQ+wzwKdxXCoOl7hGCqunW6WQO90vB7uFAIE2nicRmADgQZyQQIEekH4SxxNoJeg7syTCBBoc7GvBAj0via/3nMT6PU4Lb2KQC+dQOPzCXRj4I47IwECbTj2lQCB3tfk13tuAr0ep6VXEeilE2h8PoFuDNxxBNoMIHCMAIE2EqcRINBjzAeBHiOntC4JdBpKhSYScAM9EaDtwxIg0MNG16RxAt0E8+RDCPRkhGMVINBj5bXL3RLoXU7Xs51GgECbDzfQ488AgR4/w42egEBvhMviGQkQ6BnhKt01AQLddTyLN+cGevEI1mqAQK+FaXcWEejdyXL0JyHQoyeo/20JEOhtye3HPgI9Rs4Eeoyc0rok0GkoFZpIgEBPBGj7sAQI9LDRNWmcQDfBPPkQAj0Z4VgFCPRYee1ytwR6l9P1bKcRINDm4zQCBHqM+SDQY+SU1iWBTkOp0EQCBHoiQNuHJUCgh42uSeMEugnmyYcQ6MkIxypAoMfKa5e7JdC7nK5ncwNtBrYlQKC3Jdd2H4Fuy3vx0wj04hFoYEWAQBuFfSXgBnpfk1/vuQn0epyWXkWgl06g8fkEujFwx52RAIE2HPtKgEDva/LrPTeBXo/T0qsI9NIJND6fQDcG7jgCbQYQOEaAQBuJ0wgQ6DHmg0CPkVNml39TSnlLZkG1EEAAAQQQQCCNwC1KKeemVVNoFgIEehasiiKAAAIIIIAAAgjsKgECvavJei4EEEAAAQQQQACBWQgQ6FmwKooAAggggAACCCCwqwQI9K4m67kQQAABBBBAAAEEZiFAoGfBqigCCCCAAAIIIIDArhIg0LuarOdCAAEEEEAAAQQQmIUAgZ4Fq6IIIIAAAggggAACu0qAQO9qsp4LAQQQQAABBBBAYBYCBHoWrIoigAACCCCAAAII7CoBAr2ryXouBBBAAAEEEEAAgVkIEOhZsCqKAAIIIIAAAgggsKsECPSuJuu5EEAAAQQQQAABBGYhQKBnwaooAggggAACCCCAwK4SINC7mqznQgABBBBAAAEEEJiFAIGeBauiCCCAAAIIIIAAArtKgEDvarKeCwEEEEAAAQQQQGAWAgR6FqyKIoAAAggggAACCOwqAQK9q8l6LgQQQAABBBBAAIFZCBDoWbAqigACCCCAAAIIILCrBAj0ribruRBAAAEEEEAAAQRmIUCgZ8GqKAIIIIAAAggggMCuEiDQu5qs50IAAQQQQAABBBCYhQCBngWroggggAACCCCAAAK7SoBA72qyngsBBBBAAAEEEEBgFgIEehasiiKAAAIIIIAAAgjsKgECvavJei4EEEAAAQQQQACBWQgQ6FmwKooAAggggAACCCCwqwQI9K4m67kQQAABBBBAAAEEZiFAoGfBqigCCCCAAAIIIIDArhIg0LuarOdCAAEEEEAAAQQQmIUAgZ4Fq6IIIIAAAggggAACu0qAQO9qsp4LAQQQQAABBBBAYBYCBHoWrIoigAACCCCAAAII7CoBAr2ryXouBBBAAAEEEEAAgVkIEOhZsCqKAAIIIIAAAgggsKsECPSuJuu5EEAAAQQQQAABBGYhQKBnwaooAggggAACCCCAwK4SINC7mqznQgABBBBAAAEEEJiFAIGeBauiCCCAAAIIIIAAArtKgEDvarKeCwEEEEAAAQQQQGAWAgR6FqyKIoAAAggggAACCOwqAQK9q8l6LgQQQAABBBBAAIFZCPwfKDcowP6ljSMAAAAASUVORK5CYII=\" width=\"720\">"
],
"text/plain": [
"<IPython.core.display.HTML object>"
]
},
"metadata": {},
"output_type": "display_data"
}
],
"source": [
"captured = CaptureHandwriting()"
]
},
{
"cell_type": "markdown",
"metadata": {},
"source": [
"My experience shows the network is very good at predicting digits, iff the line width above is set somewhere above 35."
]
},
{
"cell_type": "code",
"execution_count": 37,
"metadata": {},
"outputs": [
{
"data": {
"image/png": "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\n",
"text/plain": [
"<Figure size 720x720 with 2 Axes>"
]
},
"metadata": {
"needs_background": "light"
},
"output_type": "display_data"
}
],
"source": [
"predict_from_image(captured.img_array)"
]
},
{
"cell_type": "code",
"execution_count": null,
"metadata": {},
"outputs": [],
"source": []
}
],
"metadata": {
"kernelspec": {
"display_name": "Python 3",
"language": "python",
"name": "python3"
},
"language_info": {
"codemirror_mode": {
"name": "ipython",
"version": 3
},
"file_extension": ".py",
"mimetype": "text/x-python",
"name": "python",
"nbconvert_exporter": "python",
"pygments_lexer": "ipython3",
"version": "3.7.1"
}
},
"nbformat": 4,
"nbformat_minor": 2
}
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment