{ "cells": [ { "cell_type": "code", "execution_count": null, "metadata": {}, "outputs": [], "source": [ "# Run this cell in Google Colab to install the required packages and retrieve the data\n", "# You will need to restart the runtime!\n", "!pip install jupyter-dash\n", "!pip install plotly==4.14.3\n", "!wget https://github.com/matteomancini/neurosnippets/raw/master/brainviz/mri-histogram/T1map_cropped_reoriented.nii.gz" ] }, { "cell_type": "code", "execution_count": 1, "metadata": {}, "outputs": [], "source": [ "from jupyter_dash import JupyterDash\n", "\n", "import dash\n", "import dash_core_components as dcc\n", "import dash_html_components as html\n", "import nibabel as nib\n", "import numpy as np\n", "import plotly.express as px\n", "from dash.dependencies import Input, Output, State\n", "from scipy import ndimage\n", "from skimage import draw\n", "\n", "\n", "# Uncomment the next line to detect the proxy configuration on Binder\n", "#JupyterDash.infer_jupyter_proxy_config()\n", "\n", "\n", "def path_to_indices(path):\n", " \"\"\"From SVG path to numpy array of coordinates, each row being a (row, col) point\n", " \"\"\"\n", " indices_str = [\n", " el.replace('M', '').replace('Z', '').split(',') for el in path.split('L')\n", " ]\n", " return np.floor(np.array(indices_str, dtype=float)).astype(np.int)\n", "\n", "\n", "def path_to_mask(path, shape):\n", " \"\"\"From SVG path to a boolean array where all pixels enclosed by the path\n", " are True, and the other pixels are False.\n", " \"\"\"\n", " cols, rows = path_to_indices(path).T\n", " rr, cc = draw.polygon(rows, cols)\n", " mask = np.zeros(shape, dtype=np.bool)\n", " mask[rr, cc] = True\n", " mask = ndimage.binary_fill_holes(mask)\n", " return mask\n", "\n", "\n", "# Loading the data and creating the figures\n", "img = nib.load('T1map_cropped_reoriented.nii.gz')\n", "data = img.dataobj\n", "default_view = 2\n", "default_slice = int(data.shape[default_view]/2)\n", "view = np.take(data, default_slice, axis=default_view)\n", "view = view.T\n", "fig = px.imshow(view, binary_string=True, origin='lower')\n", "fig.update_layout(dragmode='drawopenpath',\n", " newshape=dict(opacity=0.8, line=dict(color='red', width=2)))\n", "config = {\n", " 'modeBarButtonsToAdd': [\n", " 'drawopenpath',\n", " 'drawclosedpath',\n", " 'eraseshape'\n", " ]\n", "}\n", "fig_hist = px.histogram(data.get_unscaled().ravel())\n", "fig_hist.update_layout(showlegend=False)\n", "\n", "app = JupyterDash(__name__)\n", "server = app.server\n", "\n", "# Defining the layout of the dashboard\n", "app.layout = html.Div([html.Div([\n", " html.Div(\n", " [dcc.Dropdown(id=\"plane-dropdown\", options=[\n", " {'label': 'Axial', 'value': 2},\n", " {'label': 'Coronal', 'value': 1},\n", " {'label': 'Sagittal', 'value': 0}],\n", " value=2),\n", " dcc.Graph(id='graph-mri', figure=fig, config=config),\n", " dcc.Slider(\n", " id='slice-slider',\n", " min=0,\n", " max=data.shape[default_view] - 1,\n", " value=int(data.shape[default_view]/2),\n", " step=1)],\n", " style={'width': '60%', 'display': 'inline-block', 'padding': '0 0'},\n", " ),\n", " html.Div(\n", " [dcc.Graph(id='graph-histogram', figure=fig_hist)],\n", " style={'width': '40%', 'display': 'inline-block', 'padding': '0 0'},\n", " ), html.Div(id='test')\n", " ])\n", "])" ] }, { "cell_type": "code", "execution_count": 2, "metadata": {}, "outputs": [], "source": [ "@app.callback(\n", " [Output('graph-mri', 'figure'),\n", " Output('slice-slider', 'max'),\n", " Output('slice-slider', 'value')],\n", " [Input('plane-dropdown', 'value'),\n", " Input('slice-slider', 'value')],\n", " prevent_initial_call=True)\n", "def update_plane(plane, vol_slice):\n", " ctx = dash.callback_context\n", " if ctx.triggered[0]['prop_id'].split('.')[0]=='plane-dropdown':\n", " vol_slice = int(data.shape[plane]/2)\n", " view = np.take(data, vol_slice, axis=plane)\n", " view = view.T\n", " fig = px.imshow(view, binary_string=True, origin='lower')\n", " fig.update_layout(dragmode='drawopenpath',\n", " newshape=dict(opacity=0.8, line=dict(color='red', width=2)))\n", " return [fig, data.shape[plane] - 1, vol_slice]\n", "\n", "\n", "@app.callback(\n", " Output('graph-histogram', 'figure'),\n", " Input('graph-mri', 'relayoutData'),\n", " [State('plane-dropdown', 'value'),\n", " State('slice-slider', 'value')],\n", " prevent_initial_call=True)\n", "def histo_from_annotation(relayout_data, current_plane, current_slice):\n", " if 'shapes' in relayout_data:\n", " last_shape = relayout_data['shapes'][-1]\n", " view = np.take(data, current_slice, axis=current_plane)\n", " view = view.T\n", " mask = path_to_mask(last_shape['path'], view.shape)\n", " fig = px.histogram(view[mask])\n", " fig.update_layout(showlegend=False)\n", " return(fig)\n", " else:\n", " return dash.no_update" ] }, { "cell_type": "code", "execution_count": 3, "metadata": {}, "outputs": [ { "data": { "text/html": [ "\n", " \n", " " ], "text/plain": [ "" ] }, "metadata": {}, "output_type": "display_data" } ], "source": [ "app.run_server(mode=\"inline\")" ] }, { "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.3" } }, "nbformat": 4, "nbformat_minor": 4 }