{
 "cells": [
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "# Gamma from DICOM\n",
    "\n",
    "PyMedPhys has multiple ways to calculate Gamma. There are also a range of interfaces that can be used. Presented here is a simplified interface which receives as its input two DICOM file paths for the purpose of directly calculating Gamma from a pair of RT DICOM dose files."
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "metadata": {},
   "outputs": [],
   "source": [
    "import numpy as np\n",
    "import matplotlib.pyplot as plt"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "metadata": {},
   "outputs": [],
   "source": [
    "!pip install pydicom pymedphys\n",
    "\n",
    "import pydicom\n",
    "import pymedphys"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "## Getting the demo DICOM files\n",
    "\n",
    "Let's download some demo files for the purpose of demonstrating `gamma_dicom` usage."
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "metadata": {},
   "outputs": [],
   "source": [
    "reference_filepath = pymedphys.data_path(\"original_dose_beam_4.dcm\")\n",
    "reference_filepath"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "metadata": {},
   "outputs": [],
   "source": [
    "evaluation_filepath = pymedphys.data_path(\"logfile_dose_beam_4.dcm\")\n",
    "evaluation_filepath"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "metadata": {},
   "outputs": [],
   "source": [
    "reference = pydicom.read_file(str(reference_filepath), force=True)\n",
    "evaluation = pydicom.read_file(str(evaluation_filepath), force=True)"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "metadata": {},
   "outputs": [],
   "source": [
    "axes_reference, dose_reference = pymedphys.dicom.zyx_and_dose_from_dataset(reference)\n",
    "axes_evaluation, dose_evaluation = pymedphys.dicom.zyx_and_dose_from_dataset(evaluation)\n",
    "\n",
    "(z_ref, y_ref, x_ref) = axes_reference\n",
    "(z_eval, y_eval, x_eval) = axes_evaluation"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "Importantly the shape of the coordinates are in the same order as the dose axis order"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "metadata": {},
   "outputs": [],
   "source": [
    "np.shape(z_ref)"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "metadata": {},
   "outputs": [],
   "source": [
    "np.shape(y_ref)"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "metadata": {},
   "outputs": [],
   "source": [
    "np.shape(x_ref)"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "metadata": {},
   "outputs": [],
   "source": [
    "np.shape(dose_reference)"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "## Calculate and display Gamma"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "metadata": {},
   "outputs": [],
   "source": [
    "gamma_options = {\n",
    "    'dose_percent_threshold': 1,\n",
    "    'distance_mm_threshold': 1,\n",
    "    'lower_percent_dose_cutoff': 20,\n",
    "    'interp_fraction': 10,  # Should be 10 or more for more accurate results\n",
    "    'max_gamma': 2,\n",
    "    'random_subset': None,\n",
    "    'local_gamma': True,\n",
    "    'ram_available': 2**29  # 1/2 GB\n",
    "}\n",
    "    \n",
    "gamma = pymedphys.gamma(\n",
    "    axes_reference, dose_reference, \n",
    "    axes_evaluation, dose_evaluation, \n",
    "    **gamma_options)"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "metadata": {},
   "outputs": [],
   "source": [
    "valid_gamma = gamma[~np.isnan(gamma)]\n",
    "\n",
    "num_bins = (\n",
    "    gamma_options['interp_fraction'] * gamma_options['max_gamma'])\n",
    "bins = np.linspace(0, gamma_options['max_gamma'], num_bins + 1)\n",
    "\n",
    "plt.hist(valid_gamma, bins, density=True)\n",
    "#if density is True, y value is probability density; otherwise, it is count in a bin\n",
    "plt.xlim([0, gamma_options['max_gamma']])\n",
    "plt.xlabel('gamma index')\n",
    "plt.ylabel('probability density')\n",
    "    \n",
    "pass_ratio = np.sum(valid_gamma <= 1) / len(valid_gamma)\n",
    "\n",
    "if gamma_options['local_gamma']:\n",
    "    gamma_norm_condition = 'Local gamma'\n",
    "else:\n",
    "    gamma_norm_condition = 'Global gamma'\n",
    "\n",
    "plt.title(f\"Dose cut: {gamma_options['lower_percent_dose_cutoff']}% | {gamma_norm_condition} ({gamma_options['dose_percent_threshold']}%/{gamma_options['distance_mm_threshold']}mm) | Pass Rate(\\u03B3<=1): {pass_ratio*100:.2f}% \\n ref pts: {len(z_ref)*len(y_ref)*len(x_ref)} | valid \\u03B3 pts: {len(valid_gamma)}\")\n",
    "\n",
    "# plt.savefig('gamma_hist.png', dpi=300)"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "## Plotting the Dose and the Gamma"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "metadata": {},
   "outputs": [],
   "source": [
    "max_ref_dose = np.max(dose_reference)\n",
    "\n",
    "lower_dose_cutoff = gamma_options['lower_percent_dose_cutoff'] / 100 * max_ref_dose\n",
    "\n",
    "relevant_slice = (\n",
    "    np.max(dose_reference, axis=(1, 2)) > \n",
    "    lower_dose_cutoff)\n",
    "slice_start = np.max([\n",
    "        np.where(relevant_slice)[0][0], \n",
    "        0])\n",
    "slice_end = np.min([\n",
    "        np.where(relevant_slice)[0][-1], \n",
    "        len(z_ref)])"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "metadata": {},
   "outputs": [],
   "source": [
    "z_vals = z_ref[slice(slice_start, slice_end, 5)]\n",
    "\n",
    "eval_slices = [\n",
    "    dose_evaluation[np.where(z_i == z_eval)[0][0], :, :]\n",
    "    for z_i in z_vals\n",
    "]\n",
    "\n",
    "ref_slices = [\n",
    "    dose_reference[np.where(z_i == z_ref)[0][0], :, :]\n",
    "    for z_i in z_vals\n",
    "]\n",
    "\n",
    "gamma_slices = [\n",
    "    gamma[np.where(z_i == z_ref)[0][0], :, :]\n",
    "    for z_i in z_vals\n",
    "]\n",
    "\n",
    "diffs = [\n",
    "    eval_slice - ref_slice\n",
    "    for eval_slice, ref_slice \n",
    "    in zip(eval_slices, ref_slices)\n",
    "]\n",
    "\n",
    "max_diff = np.max(np.abs(diffs))\n",
    "\n",
    "\n",
    "\n",
    "for i, (eval_slice, ref_slice, diff, gamma_slice) in enumerate(zip(eval_slices, ref_slices, diffs, gamma_slices)):    \n",
    "    fig, ax = plt.subplots(figsize=(13,10), nrows=2, ncols=2)\n",
    "   \n",
    "    fig.suptitle('Slice Z_{0}'.format(slice_start+i*5), fontsize=12)\n",
    "    c00 = ax[0,0].contourf(\n",
    "        x_eval, y_eval, eval_slice, 100, \n",
    "        vmin=0, vmax=max_ref_dose)\n",
    "    ax[0,0].set_title(\"Evaluation\")\n",
    "    fig.colorbar(c00, ax=ax[0,0], label='Dose (Gy)')\n",
    "    ax[0,0].invert_yaxis()\n",
    "    ax[0,0].set_xlabel('x (mm)')\n",
    "    ax[0,0].set_ylabel('y (mm)')\n",
    "    \n",
    "    c01 = ax[0,1].contourf(\n",
    "        x_ref, y_ref, ref_slice, 100, \n",
    "        vmin=0, vmax=max_ref_dose)\n",
    "    ax[0,1].set_title(\"Reference\")  \n",
    "    fig.colorbar(c01, ax=ax[0,1], label='Dose (Gy)')\n",
    "    ax[0,1].invert_yaxis()\n",
    "    ax[0,1].set_xlabel('x (mm)')\n",
    "    ax[0,1].set_ylabel('y (mm)')\n",
    "\n",
    "    c10 = ax[1,0].contourf(\n",
    "        x_ref, y_ref, diff, 100, \n",
    "        vmin=-max_diff, vmax=max_diff, cmap=plt.get_cmap('seismic'))\n",
    "    ax[1,0].set_title(\"Dose difference\")    \n",
    "    cbar = fig.colorbar(c10, ax=ax[1,0], label='[Dose Eval] - [Dose Ref] (Gy)')\n",
    "    cbar.formatter.set_powerlimits((0, 0)) #use scientific notation\n",
    "    ax[1,0].invert_yaxis()\n",
    "    ax[1,0].set_xlabel('x (mm)')\n",
    "    ax[1,0].set_ylabel('y (mm)')\n",
    "    \n",
    "    c11 = ax[1,1].contourf(\n",
    "        x_ref, y_ref, gamma_slice, 100, \n",
    "        vmin=0, vmax=2, cmap=plt.get_cmap('coolwarm'))\n",
    "    ax[1,1].set_title(\n",
    "        f\"{gamma_norm_condition} ({gamma_options['dose_percent_threshold']} % / {gamma_options['distance_mm_threshold']} mm)\")    \n",
    "    fig.colorbar(c11, ax=ax[1,1], label='gamma index')\n",
    "    ax[1,1].invert_yaxis()\n",
    "    ax[1,1].set_xlabel('x (mm)')\n",
    "    ax[1,1].set_ylabel('y (mm)')\n",
    "    \n",
    "    plt.show()\n",
    "    print(\"\\n\")    "
   ]
  }
 ],
 "metadata": {
  "kernelspec": {
   "display_name": "Python 3 (ipykernel)",
   "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.5"
  },
  "nbsphinx": {
   "timeout": 600
  }
 },
 "nbformat": 4,
 "nbformat_minor": 4
}
