def post_process(self): phys_dose = None bio_dose = None dose_mean_let = None temp_dos = None for projectile in self.projectiles: name = os.path.join( self.path, self.plan_name) + "_" + self.projectiles[projectile]["name"] factor = float(self.projectile_dose_level[projectile]) / 1000 factor = 1 if os.path.exists(name + ".bio.dos") and self.plan.get_out_bio_dose(): path = os.path.join(name + ".bio.dos") temp = DosCube() temp.read(path) temp *= factor if self.mult_proj: self.plan.add_dose(temp, "bio_%s" % projectile) if bio_dose is None: bio_dose = temp else: bio_dose += temp if os.path.exists(name + ".phys.dos") and self.plan.get_out_phys_dose(): path = os.path.join(name + ".phys.dos") temp_dos = DosCube() temp_dos.read(path) temp_dos *= factor if self.mult_proj: self.plan.add_dose(temp_dos, "phys_%s" % projectile) if phys_dose is None: phys_dose = temp_dos else: phys_dose += temp_dos if os.path.exists(name + ".dosemlet.dos" ) and self.plan.get_out_dose_mean_let(): path = os.path.join(name + ".dosemlet.dos") temp = LETCube() temp.read(path) if not self.mult_proj: dose_mean_let = temp else: if dose_mean_let is None: dose_mean_let = temp * temp_dos else: dose_mean_let = dose_mean_let + temp * temp_dos out_path = os.path.join(self.path, self.plan_name) if bio_dose is not None: bio_dose.write(out_path + ".bio.dos") if phys_dose is not None: phys_dose.write(out_path + ".phys.dos") if dose_mean_let is not None: if self.mult_proj: dose_mean_let /= phys_dose dose_mean_let.write(out_path + ".hed")
def create_trip_exec_simple(self, no_output=False): """ Generates the .exec script and stores it into self.trip_exec. """ logger.info("Generating the trip_exec script...") oar_list = self.oar_list targets = self.targets fields = self.plan.get_fields() projectile = fields[0].get_projectile() output = [] output.extend(self.create_exec_header()) output.extend(self.create_exec_load_data_files(projectile)) output.extend(self.create_exec_field(fields)) output.extend(self.create_exec_oar(oar_list)) dosecube = None if len(targets) > 1: dosecube = DosCube(self.images) for i, voi in enumerate(targets): temp = DosCube(self.images) dose_level = int(voi.get_dose() / self.target_dose * 1000) if dose_level == 0: dose_level = -1 temp.load_from_structure(voi.get_voi().get_voi_data(), dose_level) if i == 0: dosecube = temp * 1 else: dosecube.merge_zero(temp) dosecube.cube[dosecube.cube == -1] = int(0) if not self.plan.get_target_dose_cube() is None: dosecube = self.plan.get_target_dose_cube() if dosecube is not None: if not no_output: dosecube.write(os.path.join(self.path, "target_dose.dos")) output.extend(self.create_exec_plan(incube="target_dose")) else: output.extend(self.create_exec_plan()) if self.plan.get_optimize(): output.extend(self.create_exec_opt()) name = self.plan_name output.extend(self.create_exec_output(name, fields)) out = "\n".join(output) + "\nexit\n" self.trip_exec = out
def load_dose(self, path, _type, target_dose=0.0): """ Load and append a new DosCube from path to self.doscubes. """ dos = DosCube() dos.read(os.path.splitext(path)[0] + DosCube.data_file_extension) dos._type = _type dos.target_dose = target_dose self.dosecubes.append(dos)
def test_dicom_plan(self): c = DosCube() c.read(self.cube000) dp = c.create_dicom_plan() self.assertIsNotNone(dp) d = c.create_dicom() self.assertIsNotNone(d)
def analyse_cube(self): keys = self.projectiles.keys() p1 = self.projectiles[keys[0]] p2 = self.projectiles[keys[1]] cube1 = p1["target_dos"] cube2 = p2["target_dos"] if not self.cube_in_other_cube(cube1, cube2): temp = p1 p1 = p2 p2 = temp cube1 = p1["target_dos"] cube2 = p2["target_dos"] self.execute_order = [keys[0], keys[1]] self.split_proj_key = keys[1] else: self.execute_order = [keys[1], keys[0]] self.split_proj_key = keys[0] target_cube = copy.deepcopy(cube1) shadow_cubes = [] for i, field in enumerate(p1["fields"]): d = DosCube(cube1) basis = get_basis_from_angles(field.get_gantry(), field.get_couch()) basis = basis[0] basis = np.array([ basis[0] / cube1.pixel_size, basis[1] / cube1.pixel_size, basis[2] / cube1.slice_distance ]) basis /= np.max(np.abs(basis)) d.cube = pytriplib.create_field_shadow( cube1.cube, cube2.cube, np.array(basis, dtype=np.double)) target_cube -= d shadow_cubes.append(d) target_cube.cube[target_cube.cube < 0] = 0 cube2.cube = cube2.cube + target_cube.cube # ~ cube2.cube = pytriplib.extend_cube(cube2.cube) cube1.cube = cube1.cube - target_cube.cube if len(p1["fields"]) == 2: a = self.execute_order.pop(1) b = self.projectile_dose_level[a] self.execute_order.append(a + str(1)) self.execute_order.append(a + str(2)) self.projectile_dose_level[a + str(1)] = b self.projectile_dose_level[a + str(2)] = b
def test_write_dicom(self): c = DosCube() c.read(self.cube000) outdir = tempfile.mkdtemp() c.write_dicom(outdir) self.assertTrue(os.path.exists(os.path.join(outdir, "rtdose.dcm"))) self.assertTrue(os.path.exists(os.path.join(outdir, "rtplan.dcm"))) self.assertGreater(os.path.getsize(os.path.join(outdir, "rtdose.dcm")), 0) self.assertGreater(os.path.getsize(os.path.join(outdir, "rtplan.dcm")), 0) shutil.rmtree(outdir)
def calculate_rest_dose(self, proj): self.rest_dose = copy.deepcopy(self.target_dos) for k, projectile in self.projectiles.items(): if k == proj: continue name = os.path.join(self.path, self.plan_name) + "_" + projectile["name"] path = os.path.join(name + ".phys.dos") if os.path.exists(path): temp = DosCube() temp.read(path) temp = temp self.rest_dose.cube = self.rest_dose.cube - temp.cube
def test_read(self): c = DosCube() c.read(self.cube000) self.assertEqual(c.cube.shape[0], 300) self.assertEqual(c.cube.shape[1], 512) self.assertEqual(c.cube.shape[2], 512) # test method from C extension dose_center = pytriplib.calculate_dose_center(np.array(c.cube)) self.assertEqual(dose_center.shape[0], 3) self.assertGreater(dose_center[0], 0.0) self.assertGreater(dose_center[1], 0.0) self.assertGreater(dose_center[2], 0.0)
def test_dvh_simple(self): c = DosCube() c.read(self.cube000) v = create_sphere(c, name="sph", center=[10, 10, 10], radius=8) self.assertIsNotNone(v) logger.info("Calculating DVH simple") x, y = volume_histogram(c.cube, v) self.assertIsNotNone(x) self.assertIsNotNone(y) logger.info("Calculating DVH simple for entire cube") x, y = volume_histogram(c.cube) self.assertIsNotNone(x) self.assertIsNotNone(y)
def test_dvh(self): c = DosCube() c.read(self.cube000) v = create_sphere(c, name="sph", center=[10, 10, 10], radius=8) self.assertIsNotNone(v) logger.info("Calculating DVH") result = c.calculate_dvh(v) self.assertIsNotNone(result) dvh, min_dose, max_dose, mean, area = result self.assertGreater(area, 2.0) self.assertEqual(len(dvh.shape), 2) self.assertEqual(dvh.shape[1], 2) self.assertEqual(dvh.shape[0], 1500) self.assertEqual(min_dose, 0.0) self.assertEqual(max_dose, 0.001)
def get_voi_cube(self): """ This method returns a DosCube object with value 1000 in each voxel within the Voi and zeros elsewhere. It can be used as a mask, for selecting certain voxels. The function may take some time to execute the first invocation, but is faster for subsequent calls, due to caching. :returns: a DosCube object which holds the value 1000 in those voxels which are inside the Voi. """ if hasattr(self, "voi_cube"): # caching: checks if class has voi_cube attribute # TODO: add parameter as argument to this function. Note, this needs to be compatible with # caching the cube. E.g. the method might be called twice with different arguments. return self.voi_cube self.voi_cube = DosCube(self.cube) self.voi_cube.load_from_structure(self, 1000) return self.voi_cube
def _finish(self, plan): """ return requested results, copy them back in to plan._working_dir """ for _file_name in plan._out_files: _path = os.path.join(plan._temp_dir, _file_name) # only copy files back, if we actually have been running TRiP if self._norun: logger.info("dummy run: would now copy {:s} to {:s}".format( _path, plan._working_dir)) else: logger.info("copy {:s} to {:s}".format(_path, plan._working_dir)) shutil.copy(_path, plan._working_dir) for _file_name in plan._out_files: _path = os.path.join(plan._temp_dir, _file_name) if ".phys.dos" in _file_name: _ctx_cube = DosCube() if not self._norun: _ctx_cube.read(_path) plan.dosecubes.append(_ctx_cube) if ".bio.dos" in _file_name: _ctx_cube = CtxCube() if not self._norun: _ctx_cube.read(_path) plan.dosecubes.append(_ctx_cube) if ".dosemlet.dos" in _file_name: _let_cube = LETCube() if not self._norun: _let_cube.read(_path) plan.letcubes.append(_let_cube) if ".rst" in _file_name: logger.warning( "attaching fields to class not implemented yet {:s}". format(_path)) # TODO # need to access the RstClass here for each rst file. This will then need to be attached # to the proper field in the list of fields. if self._cleanup: logger.debug("Delete {:s}".format(plan._temp_dir)) shutil.rmtree(plan._temp_dir)
def test_write(self): c = DosCube() c.read(self.cube000) fd, outfile = tempfile.mkstemp() os.close(fd) # Windows needs it os.remove(outfile) # we need only temp filename, not the file c.write(outfile) hed_file = outfile + ".hed" dos_file = outfile + ".dos" self.assertTrue(os.path.exists(hed_file)) self.assertTrue(os.path.exists(dos_file)) logger.info("Checking if output file " + hed_file + " is not empty") self.assertGreater(os.path.getsize(hed_file), 1) logger.info("Checking if output file " + dos_file + " is not empty") self.assertGreater(os.path.getsize(dos_file), 1) os.remove(hed_file) os.remove(dos_file)
def test_dvh_simple(self): c = DosCube() c.read(self.cube000) v = create_sphere(c, name="sph", center=[10, 10, 10], radius=8) self.assertIsNotNone(v) logger.info("Calculating DVH simple") vh = VolHist(c, v) self.assertIsNotNone(vh.x) self.assertIsNotNone(vh.y) outdir = tempfile.mkdtemp() c.write_dicom(outdir) f1 = os.path.join(outdir, "foobar_1.dvh") vh.write(f1, header=True) self.assertTrue(os.path.exists(f1)) self.assertGreater(os.path.getsize(f1), 0) f2 = os.path.join(outdir, "foobar_2.dvh") vh.write(f2, header=False) self.assertTrue(os.path.exists(f2)) self.assertGreater(os.path.getsize(f2), 0) logger.info("Calculating DVH simple for entire cube") vh = VolHist(c) self.assertIsNotNone(vh.x) self.assertIsNotNone(vh.y) f3 = os.path.join(outdir, "foobar_3.dvh") vh.write(f3, header=True) self.assertTrue(os.path.exists(f3)) self.assertGreater(os.path.getsize(f3), 0) f4 = os.path.join(outdir, "foobar_4.dvh") vh.write(f4, header=False) self.assertTrue(os.path.exists(f4)) self.assertGreater(os.path.getsize(f4), 0) shutil.rmtree(outdir)
def split_fields(self, proj): if self.split_proj_key not in self.projectiles.keys(): return name = os.path.join( self.path, self.plan_name) + "_" + self.projectiles[proj]["name"] path = os.path.join(name + ".phys.dos") temp = DosCube() temp.read(path) temp.version = "2.0" self.rest_dose = self.target_dos - temp p = self.projectiles[self.split_proj_key] p["target_dos"].cube = pytriplib.extend_cube(p["target_dos"].cube) if len(p["fields"]) == 2: temp.cube = (temp.cube < self.projectiles[proj]["target_dos"].cube) * \ self.projectiles[proj]["target_dos"].cube + \ (temp.cube > self.projectiles[proj]["target_dos"].cube) * temp.cube dose = self.target_dos - temp field1 = p["fields"][0] field2 = p["fields"][1] d1 = DosCube(temp) d2 = DosCube(temp) center = pytriplib.calculate_dose_center(p["target_dos"].cube) dose.cube[dose.cube < 0] = 0 temp.cube *= self.target_dos.cube > 0 basis = get_basis_from_angles(field1.get_gantry(), field1.get_couch())[0] basis = np.array([ basis[0] / dose.pixel_size, basis[1] / dose.pixel_size, basis[2] / dose.slice_distance ]) basis = basis / np.max(np.abs(basis)) * .5 d1.cube = pytriplib.create_field_shadow( dose.cube, temp.cube, np.array(basis, dtype=np.double)) basis = get_basis_from_angles(field2.get_gantry(), field2.get_couch())[0] basis = np.array([ basis[0] / dose.pixel_size, basis[1] / dose.pixel_size, basis[2] / dose.slice_distance ]) basis /= np.max(np.abs(basis)) d2.cube = pytriplib.create_field_shadow( dose.cube, temp.cube, np.array(basis, dtype=np.double)) a = d2.cube > d1.cube b = d2.cube < d1.cube d2.cube = p["target_dos"].cube * a d1.cube = p["target_dos"].cube * b rest = p["target_dos"].cube * ((a + b) < 1) b = pytriplib.split_by_plane(rest, center, basis) d1.cube += b d2.cube += rest - b self.plan.add_dose(d1, "H1") self.plan.add_dose(d2, "H2") self.projectiles[field1.get_projectile() + str(1)] = { "target_dos": d1, "fields": [field1], "name": field1.get_projectile() + str(1), "projectile": field1.get_projectile() } self.projectiles[field2.get_projectile() + str(2)] = { "target_dos": d2, "fields": [field2], "name": field2.get_projectile() + str(2), "projectile": field2.get_projectile() } del self.projectiles[self.split_proj_key]
def load_dose(self, path, t, target_dose=0.0): dos = DosCube() dos.read(os.path.splitext(path)[0] + ".dos") d = DoseCube(dos, t) d.set_dose(target_dose) self.add_dose(d)
def split_plan(self, plan=None): self.targets = [] self.oar_list = [] dose = 0 for voi in self.plan.get_vois(): if voi.is_oar(): self.oar_list.append(voi) if voi.is_target(): self.targets.append(voi) if voi.get_dose() > dose: dose = voi.get_dose() if not len(self.targets): raise InputError("No targets") if not len(self.plan.get_fields()): raise InputError("No fields") self.target_dose = dose if plan is None: plan = self.plan proj = [] self.projectile_dose_level = {} for field in plan.fields: if field.get_projectile() not in proj: proj.append(field.get_projectile()) self.projectile_dose_level[field.get_projectile()] = 0 if len(proj) > 1: self.mult_proj = True else: self.mult_proj = False if self.mult_proj: self.projectiles = {} for field in plan.fields: if field.get_projectile() not in self.projectiles.keys(): self.projectiles[field.get_projectile()] = { "target_dos": DosCube(self.images), "fields": [field], "name": field.get_projectile(), "projectile": field.get_projectile() } else: self.projectiles[field.get_projectile()]["fields"].append( field) self.target_dos = DosCube(self.images) for i, voi in enumerate(self.targets): temp = DosCube(self.images) voi_dose_level = int(voi.get_dose() / dose * 1000) temp.load_from_structure(voi.get_voi().get_voi_data(), 1) for projectile, data in self.projectiles.items(): dose_percent = self.plan.get_dose_percent(projectile) if not voi.get_dose_percent(projectile) is None: dose_percent = voi.get_dose_percent(projectile) proj_dose_lvl = int(voi.get_dose() / self.target_dose * dose_percent * 10) if self.projectile_dose_level[projectile] < proj_dose_lvl: self.projectile_dose_level[projectile] = proj_dose_lvl if proj_dose_lvl == 0: proj_dose_lvl = -1 if i == 0: data["target_dos"] = temp * proj_dose_lvl else: data["target_dos"].merge_zero(temp * proj_dose_lvl) if i == 0: self.target_dos = temp * voi_dose_level else: self.target_dos.merge_zero(temp * voi_dose_level) for projectile, data in self.projectiles.items(): data["target_dos"].cube[data["target_dos"].cube == -1] = int(0) self.plan.add_dose(data["target_dos"], "target_%s" % projectile) self.rest_dose = copy.deepcopy(self.target_dos)