|
53 | 53 | "* https://github.com/ababier/open-kbp" |
54 | 54 | ] |
55 | 55 | }, |
56 | | - { |
57 | | - "cell_type": "markdown", |
58 | | - "metadata": {}, |
59 | | - "source": [ |
60 | | - "Link to data\n", |
61 | | - "* https://github.com/ababier/open-kbp" |
62 | | - ] |
63 | | - }, |
64 | 56 | { |
65 | 57 | "cell_type": "markdown", |
66 | 58 | "metadata": {}, |
|
94 | 86 | "\\end{aligned}\n", |
95 | 87 | "$$\n", |
96 | 88 | "\n", |
97 | | - "What physical phenomena govern this loss of intensity (state at least 3 specific processes)? Which one dominates radiotherapy (6-15 MV energies)? It's easy to conclude that the highest dose of radiation is delivered at the skin, why is this untrue?" |
| 89 | + "What physical phenomena govern this loss of intensity (state at least 3 specific processes)? Which one dominates radiotherapy (which uses ~6 MeV photons)? It's easy to conclude that the highest dose of radiation is delivered at the skin, why is this untrue?" |
98 | 90 | ] |
99 | 91 | }, |
100 | 92 | { |
|
147 | 139 | "cell_type": "markdown", |
148 | 140 | "metadata": {}, |
149 | 141 | "source": [ |
150 | | - "Run the cell below and unzip the contents of `openkbp_patient_data` into a folder. It should be present in Colab's (`/content/..`) or local directory. Note that you will need to adjust `base_path` to lead to this folder.\n", |
| 142 | + "Run the cell below and unzip the contents of `openkbp_patient_data` into a folder. It should be present in Colab's (`/content/..`) or local directory. Note that you will need to adjust `base_path` to lead to this folder. \n", |
151 | 143 | "\n", |
152 | 144 | "Each patient hosts the following three files: \n", |
153 | 145 | "`ct.csv` - 3D grayscale CT scan of patient anatomy composed of 2D slices. Each voxel represents how much X-ray is absorbed by the tissue in Hounsfield Units (HU) \n", |
154 | | - "`PTV63.csv` - Planning Target Volume (PTV) is a binary mask that maps the location of the tumor (tumor is 1, everything else is 0) \n", |
155 | | - "`SpinalCord.csv` - This is a similar binary mask of our Organ at Risk (OAR), representing what we would like our beam to avoid. \n", |
| 146 | + "`PTV63.csv` - Planning Target Volume (PTV) is a sparse mask that maps the location of the tumor. We'd like binary mask so that 1 is tumor, 0 is no-tumor.\n", |
| 147 | + "\n", |
| 148 | + "The following are sparse masks of Organs at Risk (OAR), representing what we'd like our beams to avoid\n", |
| 149 | + "- `SpinalCord.csv` - Irradiating the spine can cause a serious condition known as radiation myelopathy.\n", |
| 150 | + "- `Brainstem.csv` - Radiation causes a vast myriad of devastating effects like RIBN, Ataxia, or even comas.\n", |
| 151 | + "- `LeftParotid.csv`, `RightParotid.csv` - Irradiating salivary glands can cause xerostomia (dry mouth) or worse\n", |
| 152 | + "- `Mandible.csv` - The mandible has a limited blood supply, and osteoradionecrosis can occur YEARS after treatment.\n", |
| 153 | + "\n", |
| 154 | + "These are flattened volumes with voxel indices. The cell below converts the csv files into binary 3D volumes [128, 128, 128] where each point represents an intensity (for the ct scan) or binary value (for ptv & spinal cord). You will have an optional dictionary of `patient_data` to access for your convenience.\n", |
| 155 | + "\n", |
| 156 | + "Your tasks:\n", |
| 157 | + "1) Add a line to the for loop that stacks `ct`, `ptv`, and `oars` (Organs at Risk) into a single tensor. Note that oars is a dictionary you may need to unpack. What's the final shape of this tensor? Draw an analogy to an RGB image. \n", |
156 | 158 | "\n", |
157 | | - "These are currently flattened 1D rows. The cell below converts the csv files into 3D volumes [64, 128, 128] where each point represents an intensity (for the ct scan) or binary value (for ptv & spinal cord). Add a line to the for loop that stacks `ct`, `ptv`, and `spine` into a single tensor. What's the final shape of this tensor? Draw an analogy to an RGB image." |
| 159 | + "2) Visualize a single 2D axial slice (z-axis slice) of a patient's CT scan with the tumor(ptv) overlaid...as a sanity check." |
158 | 160 | ] |
159 | 161 | }, |
160 | 162 | { |
|
163 | 165 | "metadata": {}, |
164 | 166 | "outputs": [], |
165 | 167 | "source": [ |
166 | | - "!wget https://github.com/florilegium7/Physics-informed-DQN-Radiotherapy/releases/download/v1.0/openkbp_patient_data.zip\n", |
167 | | - "!unzip patient_data.zip" |
| 168 | + "!wget https://github.com/florilegium7/Physics-informed-DQN-Radiotherapy/releases/download/v1/openkbp_patient_data.zip\n", |
| 169 | + "!unzip openkbp_patient_data.zip -d openkbp_patient_data" |
168 | 170 | ] |
169 | 171 | }, |
170 | 172 | { |
|
179 | 181 | "\n", |
180 | 182 | "base_path = 'openkbp_patient_data' #you may adjust this (e.g. /content/openkbp_patient_data)\n", |
181 | 183 | "\n", |
182 | | - "patient_ids = [\"patient_1\", \"patient_2\", \"patient_3\",\"patient_5\",\"patient_7\",\"patient_9\", \"patient_10\", \"patient_12\" , \"patient_14\", \"patient_16\"]\n", |
| 184 | + "patient_ids = [\"pt_1\", \"pt_2\", \"pt_9\",\"pt_40\",\"pt_68\",\"pt_70\", \"pt_90\", \"pt_91\" , \"pt_99\", \"pt_187\"]\n", |
183 | 185 | "patient_tensors = []\n", |
184 | 186 | "\n", |
185 | | - "#64 slices of 128 x 128 pixels : (depth, height, width)\n", |
| 187 | + "def mask_from_sparse(path,shape=(128, 128,128)): #converts voxel indices to 3D binary masks!\n", |
| 188 | + " indices = pd.read_csv(path, header=None)[0].dropna().astype(int).values\n", |
| 189 | + " mask_flat = np.zeros(np.prod(shape), dtype=np.uint8)\n", |
| 190 | + " mask_flat[indices] = 1\n", |
| 191 | + " return mask_flat.reshape(shape)\n", |
| 192 | + "\n", |
| 193 | + "def sparse_to_ct_volume(path, shape=(128, 128, 128)): #turns the csv into full 3D volume CT scans\n", |
| 194 | + " df = pd.read_csv(path, header=None).dropna()\n", |
| 195 | + " indices = df[0].astype(int).values\n", |
| 196 | + " values = df[1].astype(float).values\n", |
| 197 | + " ct_flat = np.zeros(np.prod(shape), dtype=np.float32)\n", |
| 198 | + " ct_flat[indices] = values\n", |
| 199 | + " return ct_flat.reshape(shape)\n", |
| 200 | + "\n", |
| 201 | + "\n", |
| 202 | + "#128 x 128 x 128 pixels : (depth, height, width)\n", |
186 | 203 | "for pt_id in patient_ids:\n", |
187 | | - " ct = pd.read_csv(os.path.join(base_path, pt_id, \"ct.csv\"), header=None).values.reshape((64, 128, 128))\n", |
188 | | - " ptv = pd.read_csv(os.path.join(base_path, pt_id, \"PTV63.csv\"), header=None).values.reshape((64, 128, 128))\n", |
189 | | - " spine = pd.read_csv(os.path.join(base_path, pt_id, \"SpinalCord.csv\"), header=None).values.reshape((64, 128, 128))\n", |
190 | 204 | "\n", |
191 | | - " pt_tensor = #Your Code Here \n", |
192 | | - " patient_tensors.append(pt_tensor)\n", |
| 205 | + " pt_dir = os.path.join(base_path, pt_id)\n", |
| 206 | + "\n", |
| 207 | + " ct = sparse_to_ct_volume(os.path.join(pt_dir, \"ct.csv\"))\n", |
| 208 | + " ptv = mask_from_sparse(os.path.join(pt_dir, \"PTV63.csv\"))\n", |
| 209 | + "\n", |
| 210 | + " oars = {} #dictionary\n", |
| 211 | + " organs = [\"SpinalCord\", \"Brainstem\", \"LeftParotid\", \"RightParotid\", \"Mandible\"]\n", |
| 212 | + " for organ in organs:\n", |
| 213 | + " organ_path = os.path.join(pt_dir, f\"{organ}.csv\")\n", |
| 214 | + " if os.path.exists(organ_path):\n", |
| 215 | + " oars[organ] = mask_from_sparse(organ_path)\n", |
| 216 | + " \n", |
| 217 | + " patient_data = {\n", |
| 218 | + " \"id\": pt_id,\n", |
| 219 | + " \"ct\": ct, #ct scan -> 3D volume with intensities\n", |
| 220 | + " \"ptv\": ptv, #tumor -> 3D binary mask (1 means tumor!)\n", |
| 221 | + " \"oars\": oars #dictionary of organs at risk -> each is a 3D binary mask\n", |
| 222 | + " }\n", |
193 | 223 | " \n", |
194 | 224 | "\n", |
195 | | - "#patient_tensors[0] --> patient 1\n" |
| 225 | + " pt_tensor = #Your Code Here \n", |
| 226 | + " patient_tensors.append(pt_tensor)\n", |
| 227 | + "\n" |
| 228 | + ] |
| 229 | + }, |
| 230 | + { |
| 231 | + "cell_type": "code", |
| 232 | + "execution_count": null, |
| 233 | + "metadata": {}, |
| 234 | + "outputs": [], |
| 235 | + "source": [ |
| 236 | + "#Your slice visualization here\n", |
| 237 | + "import matplotlib.pyplot as plt" |
196 | 238 | ] |
197 | 239 | }, |
198 | 240 | { |
|
225 | 267 | "metadata": {}, |
226 | 268 | "outputs": [], |
227 | 269 | "source": [ |
| 270 | + "import random\n", |
| 271 | + "\n", |
228 | 272 | "beam_angles = np.arange(0, 360, 10) #[0, 10, 20, ..., 350]\n", |
229 | 273 | "max_beams = 5 #ensures beams are chosen strategically\n", |
230 | 274 | "episodes = 500\n", |
|
259 | 303 | " mid = ct.shape[2] // 2 \n", |
260 | 304 | " ct_slice = ct[:, :, mid]\n", |
261 | 305 | " ptv_slice = ptv[:, :, mid]\n", |
262 | | - " spine_slice = spine[:, :, mid]\n", |
| 306 | + " #extract organ slices HERE\n", |
263 | 307 | "\n", |
264 | 308 | " dose = np.zeros(slice_shape, dtype=np.float32)\n", |
265 | 309 | " selected_angles = []\n", |
|
269 | 313 | "\n", |
270 | 314 | " beam = generate_beam(angle, slice_shape)\n", |
271 | 315 | " dose += beam.astype(np.float32) #adds 'radiation' to the pixels \n", |
272 | | - " chosen_angles.append(angle)\n", |
| 316 | + " selected_angles.append(angle)\n", |
| 317 | + "\n", |
273 | 318 | " \n", |
274 | | - " reward_score = reward(dose, ptv, spine)\n", |
| 319 | + " reward_score = reward(dose, ptv, organs)\n", |
275 | 320 | "\n", |
276 | | - " for angle in chosen_angles:\n", |
| 321 | + " for angle in selected_angles:\n", |
277 | 322 | " #Q-learning update here\n", |
278 | 323 | "\n", |
279 | 324 | "\n", |
280 | 325 | "\n", |
281 | | - "def reward(dose, ptv, spine): #your reward function HERE\n", |
| 326 | + "def reward(dose, ptv, organs): #your reward function HERE\n", |
282 | 327 | " raise NotImplementedError()\n", |
283 | | - "\n", |
| 328 | + " \n", |
284 | 329 | "\n", |
285 | 330 | "Q = np.zeros(len(beam_angles)) #Q-table\n" |
286 | 331 | ] |
|
300 | 345 | "source": [ |
301 | 346 | "import matplotlib as plt\n", |
302 | 347 | "\n", |
| 348 | + "\n", |
303 | 349 | "patient = random.choice(patient_tensors)\n", |
304 | 350 | "mid = patient.shape[3] // 2\n", |
305 | 351 | "ct, ptv, cord = patient[0, :, :, mid], patient[1, :, :, mid], patient[2, :, :, mid]\n", |
|
375 | 421 | " beam_mask[r_spread, c_spread] += decay * spread_value \n", |
376 | 422 | "\n", |
377 | 423 | " beam_mask = np.clip(beam_mask, 0, 1.0) \n", |
378 | | - " return beam_mask\n", |
379 | | - "\n" |
| 424 | + " return beam_mask" |
380 | 425 | ] |
381 | 426 | }, |
382 | 427 | { |
|
437 | 482 | "metadata": {}, |
438 | 483 | "outputs": [], |
439 | 484 | "source": [ |
440 | | - "import numpy as np\n", |
441 | | - "import matplotlib.pyplot as plt\n", |
442 | | - "from skimage.draw import line_nd\n", |
443 | | - "\n", |
444 | 485 | "#your conclusions\n", |
445 | 486 | "selected_angles = \n", |
446 | 487 | "\n", |
|
501 | 542 | ], |
502 | 543 | "metadata": { |
503 | 544 | "kernelspec": { |
504 | | - "display_name": "Python 3 (ipykernel)", |
| 545 | + "display_name": "Python 3", |
505 | 546 | "language": "python", |
506 | 547 | "name": "python3" |
507 | 548 | }, |
|
515 | 556 | "name": "python", |
516 | 557 | "nbconvert_exporter": "python", |
517 | 558 | "pygments_lexer": "ipython3", |
518 | | - "version": "3.9.7" |
| 559 | + "version": "3.9.6" |
519 | 560 | } |
520 | 561 | }, |
521 | 562 | "nbformat": 4, |
|
0 commit comments