{
"cells": [
{
"cell_type": "markdown",
"metadata": {},
"source": [
"## Plotly visualization of DICOM images ##"
]
},
{
"cell_type": "markdown",
"metadata": {},
"source": [
"This notebook is an update of one based on an older version (0.99) of pydicom for Python 2.7.\n",
"Here we use Python 3.6 and pydicom 1.0.2."
]
},
{
"cell_type": "markdown",
"metadata": {},
"source": [
"[DICOM](https://en.wikipedia.org/wiki/DICOM) (Digital Imaging and Communications in Medicine) is a standard for handling, storing, printing, and transmitting information in medical imaging.\n",
"\n",
"DICOM images have the extension `dcm`. A DICOM file has two parts: the *header*\n",
"and the *dataset*. \n",
"The header contains information on the encapsulated dataset. It consists of a File\n",
"Preamble, a DICOM prefix, and the File Meta Elements. \n",
"\n",
"The Python library, `pydicom, can read and write `dcm` files: [https://pydicom.github.io/pydicom/stable/](https://pydicom.github.io/pydicom/stable/)."
]
},
{
"cell_type": "markdown",
"metadata": {},
"source": [
"For `pydicom` installation see [https://pydicom.github.io/pydicom/stable/getting_started.html](https://pydicom.github.io/pydicom/stable/getting_started.html)."
]
},
{
"cell_type": "code",
"execution_count": 1,
"metadata": {},
"outputs": [],
"source": [
"import numpy as np\n",
"import pydicom"
]
},
{
"cell_type": "code",
"execution_count": 2,
"metadata": {},
"outputs": [
{
"name": "stdout",
"output_type": "stream",
"text": [
"1.0.2\n"
]
}
],
"source": [
"print (pydicom.__version__)"
]
},
{
"cell_type": "markdown",
"metadata": {},
"source": [
"In this notebook we define functions to read DICOM files, transform the corresponding image to get the z-value for a heatmap and plot the heatmap, using the mpl bone colormap/colorscale.\n",
"If necessary we increase the image contrast applying the histogram equalization, defined by the following function:"
]
},
{
"cell_type": "code",
"execution_count": 3,
"metadata": {},
"outputs": [],
"source": [
"def histogram_equalization(img, no_bins):\n",
" \n",
" #img- the image as a numpy.array\n",
" #the appropriate number of bins, `no_bins` in the histogram is chosen by experiments, \n",
" #until the contrast is convenient\n",
" \n",
" image_hist, bins = np.histogram(img.flatten(), no_bins, normed=True)\n",
" csum = image_hist.cumsum() \n",
" cdf_mult = np.max(img) * csum / csum[-1] # cdf multiplied by a factor\n",
"\n",
" # linear interpolation of cdf_mult to get new pixel values\n",
" im_new = np.interp(img.flatten(), bins[:-1], cdf_mult)\n",
"\n",
" return im_new.reshape(img.shape), cdf_mult"
]
},
{
"cell_type": "markdown",
"metadata": {},
"source": [
"Define the Plotly colorscale `pl_bone`:"
]
},
{
"cell_type": "code",
"execution_count": 4,
"metadata": {},
"outputs": [],
"source": [
"pl_bone=[[0.0, 'rgb(0, 0, 0)'],\n",
" [0.05, 'rgb(10, 10, 14)'],\n",
" [0.1, 'rgb(21, 21, 30)'],\n",
" [0.15, 'rgb(33, 33, 46)'],\n",
" [0.2, 'rgb(44, 44, 62)'],\n",
" [0.25, 'rgb(56, 55, 77)'],\n",
" [0.3, 'rgb(66, 66, 92)'],\n",
" [0.35, 'rgb(77, 77, 108)'],\n",
" [0.4, 'rgb(89, 92, 121)'],\n",
" [0.45, 'rgb(100, 107, 132)'],\n",
" [0.5, 'rgb(112, 123, 143)'],\n",
" [0.55, 'rgb(122, 137, 154)'],\n",
" [0.6, 'rgb(133, 153, 165)'],\n",
" [0.65, 'rgb(145, 169, 177)'],\n",
" [0.7, 'rgb(156, 184, 188)'],\n",
" [0.75, 'rgb(168, 199, 199)'],\n",
" [0.8, 'rgb(185, 210, 210)'],\n",
" [0.85, 'rgb(203, 221, 221)'],\n",
" [0.9, 'rgb(220, 233, 233)'],\n",
" [0.95, 'rgb(238, 244, 244)'],\n",
" [1.0, 'rgb(255, 255, 255)']]"
]
},
{
"cell_type": "code",
"execution_count": 5,
"metadata": {},
"outputs": [],
"source": [
"def get_pl_image(dicom_filename, hist_equal=False, no_bins=None):\n",
" #dicom_filename- a string 'filename.dcm'\n",
" #no_bins is the number of bins for histogram when hist_equal=False, else it is None\n",
" #returns the np.array that defines the z-value for the heatmap representing the dicom image\n",
" \n",
" dic_file=pydicom.read_file(dicom_filename)\n",
" img=dic_file.pixel_array#get the image as a numpy.array\n",
" if hist_equal and isinstance(no_bins, int):\n",
" img_new=histogram_equalization(img, no_bins)[0]\n",
" img_new=np.array(img_new, dtype=np.int16)\n",
" return np.flipud(img_new)\n",
" else:\n",
" return np.flipud(img)"
]
},
{
"cell_type": "markdown",
"metadata": {},
"source": [
"The following function returns the Plotly figure of the heatmap representing the DICOM image:"
]
},
{
"cell_type": "code",
"execution_count": 6,
"metadata": {},
"outputs": [],
"source": [
"def DICOM_heatmap(z, title, width=600, height=600, colorscale=pl_bone):\n",
"\n",
" data=[dict(type='heatmap', \n",
" z=z, \n",
" colorscale=colorscale, \n",
" zsmooth='best',\n",
" colorbar=dict(thickness=20, ticklen=4), \n",
" )\n",
" ]\n",
"\n",
" axis=dict(zeroline=False, showgrid=False, ticklen=4)\n",
" layout=dict(width=600, height=600,\n",
" font=dict(family='Balto', size=12),\n",
" xaxis= dict(axis),\n",
" yaxis= dict(axis),\n",
" title= title\n",
" )\n",
" return dict(data=data, layout=layout)"
]
},
{
"cell_type": "markdown",
"metadata": {},
"source": [
"Here we plot two DICOM images. Since the second one has a poor contrast we process it through histogram equalization:"
]
},
{
"cell_type": "code",
"execution_count": 7,
"metadata": {},
"outputs": [],
"source": [
"import plotly.plotly as py\n",
"py.sign_in('empet', '')\n"
]
},
{
"cell_type": "code",
"execution_count": 8,
"metadata": {},
"outputs": [
{
"data": {
"text/html": [
""
],
"text/vnd.plotly.v1+html": [
""
]
},
"metadata": {},
"output_type": "display_data"
}
],
"source": [
"from plotly.offline import download_plotlyjs, init_notebook_mode, iplot, plot\n",
"init_notebook_mode(connected=True)"
]
},
{
"cell_type": "code",
"execution_count": 9,
"metadata": {},
"outputs": [
{
"data": {
"text/html": [
""
],
"text/plain": [
""
]
},
"execution_count": 9,
"metadata": {},
"output_type": "execute_result"
}
],
"source": [
"title1=\"Plotly viz of a DICOM medical image
\"+\\\n",
"\"Data set: [1]\"\n",
"pl_img1=get_pl_image('MR-MONO2-16-head')#this DICOM file has no extension\n",
"fig1=DICOM_heatmap(pl_img1, title1)\n",
"py.iplot(fig1, filename='DICOM-img-MRIs')"
]
},
{
"cell_type": "code",
"execution_count": 10,
"metadata": {},
"outputs": [],
"source": [
"title2=\"Plotly viz of a DICOM medical image\"+\\\n",
"\"
Data set: [1]\"\n",
"pl_img2=get_pl_image('MRBRAIN.DCM', hist_equal=True, no_bins=36)\n",
"fig2=DICOM_heatmap(pl_img2, title2)"
]
},
{
"cell_type": "code",
"execution_count": 11,
"metadata": {},
"outputs": [
{
"data": {
"text/html": [
""
],
"text/plain": [
""
]
},
"execution_count": 11,
"metadata": {},
"output_type": "execute_result"
}
],
"source": [
"py.iplot(fig2, filename='DICOM-MRBRAIN')"
]
},
{
"cell_type": "code",
"execution_count": 12,
"metadata": {},
"outputs": [
{
"data": {
"text/html": [
"\n",
"\n"
],
"text/plain": [
""
]
},
"execution_count": 12,
"metadata": {},
"output_type": "execute_result"
}
],
"source": [
"from IPython.core.display import HTML\n",
"def css_styling():\n",
" styles = open(\"./custom.css\", \"r\").read()\n",
" return HTML(styles)\n",
"css_styling()"
]
}
],
"metadata": {
"kernelspec": {
"display_name": "Python [conda env:python36]",
"language": "python",
"name": "conda-env-python36-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.4"
}
},
"nbformat": 4,
"nbformat_minor": 1
}