def test_exec(self): """ Prepare and execute a dry-run test using the Executer. """ logger.info("Test norun TRiP98 execution") logger.debug("Load CtxCube {:s}".format(self.ctx_path)) c = pt.CtxCube() c.read(self.ctx_path) logger.debug("Load VdxCube {:s}".format(self.vdx_path)) v = pt.VdxCube(c) v.read(self.vdx_path) print(v.get_voi_names()) plan = pte.Plan(basename=self.patient_name) self.assertIsNotNone(plan) plan.ddd_dir = "~/TRiP98/base/DATA/DDD/12C/RF3MM/*" plan.spc_dir = "~/TRiP98/base/DATA/SPC/12C/RF3MM/*" plan.sis_path = "~/TRiP98/base/DATA/SIS/12C.sis" plan.hlut_path = "~/TRiP98/base/DATA/HLUT/19990218.hlut" plan.dedx_path = "~/TRiP98/base/DATA/DEDX/20040607.dedx" plan.working_dir = "." # working dir must exist. # add the target voi to the plan plan.voi_target = v.get_voi_by_name('target') plan.rifi = 3.0 plan.bolus = 0.0 plan.offh2o = 1.873 # create a field and add it to the plan field = pte.Field() self.assertIsNotNone(field) field.basename = self.patient_name field.gantry = 10.0 field.couch = 90.0 # degrees field.fwhm = 4.0 # spot size in [mm] field.projectile = 'C' plan.fields.append(field) # flags for what output should be generated plan.want_phys_dose = True plan.want_bio_dose = False plan.want_dlet = True plan.want_rst = False t = pte.Execute(c, v) self.assertIsNotNone(t) t.trip_bin_path = self.trip_path print(self.trip_path) if os.name != 'nt': # skip running fake TRiP98 on Windows as it is not supported there t.execute( plan, False ) # setup and make a dry-run, since TRiP98 is not installed. executer_str = str(t) self.assertGreater(len(executer_str), 1)
def test_read_with_ct(self): logger.info("Creating CT cube from path " + self.cube000) c = pt.CtxCube() c.read(self.cube000) v = pt.VdxCube("", c) logger.info("Adding VDX from path " + self.vdx) v.read(self.vdx) logger.info("Checking len of get_voi_names") self.assertEqual(len(v.get_voi_names()), 2) logger.info("Checking get_voi_names") self.assertEqual(v.get_voi_names(), ['target', 'voi_empty']) logger.info("Checking number of vois") self.assertEqual(v.number_of_vois(), 2) logger.info("Checking Vdx str method") self.assertEqual(str(v), "target&voi_empty") logger.info("Checking Vdx write_to_voxel method") fd, outfile = tempfile.mkstemp() v.write_to_voxel(outfile) self.assertTrue(os.path.exists(outfile)) logger.info("Checking if output file " + outfile + " is not empty") self.assertGreater(os.path.getsize(outfile), 1) os.close(fd) # Windows needs it os.remove(outfile) logger.info("Checking Vdx write method") fd, outfile = tempfile.mkstemp() v.write(outfile) self.assertTrue(os.path.exists(outfile)) logger.info("Checking if output file " + outfile + " is not empty") self.assertGreater(os.path.getsize(outfile), 1) os.close(fd) # Windows needs it os.remove(outfile) logger.info("Checking if getting non-existend VOI throws an exception") self.assertRaises(InputError, v.get_voi_by_name, '') logger.info("Checking Vdx get_voi_by_name method") target_voi = v.get_voi_by_name('target') self.assertEqual(target_voi.get_name(), 'target') self.assertEqual(target_voi.get_thickness(), 3) self.assertEqual(target_voi.number_of_slices(), 18) logger.info("Checking Voi get_3d_polygon method") self.assertIsNotNone(target_voi.get_3d_polygon()) # TODO add some assertions target_voi.get_2d_projection_on_basis(basis=((1, 0, 0), (0, 2, 0))) # TODO add some assertions vc = target_voi.get_voi_cube() self.assertTrue(vc.is_compatible(c)) # TODO add some assertions target_voi.create_point_tree()
def main(args=sys.argv[1:]): """ Main function for trip2dicom.py """ # parse arguments parser = argparse.ArgumentParser() parser.add_argument( "ctx_data", help="location of CT file (header or data) in TRiP98 format", type=str) parser.add_argument("outputdir", help="write resulting DICOM files to this directory", type=str) parser.add_argument("-v", "--verbosity", action='count', help="increase output verbosity", default=0) parser.add_argument('-V', '--version', action='version', version=pt.__version__) parsed_args = parser.parse_args(args) if parsed_args.verbosity == 1: logging.basicConfig(level=logging.INFO) elif parsed_args.verbosity > 1: logging.basicConfig(level=logging.DEBUG) else: logging.basicConfig() output_folder = parsed_args.outputdir _, data_file_name = pt.CtxCube.parse_path(parsed_args.ctx_data) data_file_path = pt.CtxCube.discover_file(data_file_name) if not os.path.exists(data_file_path): logger.error("CTX file missing") return 1 logger.info("Convert CT images...") c = pt.CtxCube() c.read(data_file_name) if not os.path.exists(output_folder): os.makedirs(output_folder) c.write_dicom(output_folder) ctx_basename = os.path.splitext(data_file_path)[0] ctx_path = ctx_basename + ".vdx" if os.path.exists(ctx_path): logger.info("Convert VDX structures...") v = pt.VdxCube(cube=c) v.read(ctx_path) v.write_dicom(output_folder) else: logger.info("No VDX data found for conversion.") logger.info("Done") return 0
def main(args=sys.argv[1:]): """ Main function for dicom2trip.py """ # parse arguments parser = argparse.ArgumentParser() parser.add_argument("dicom_folder", help="location of folder with DICOM files", type=str) parser.add_argument("ctx_basename", help="basename of output file in TRiP98 format", type=str) parser.add_argument("-v", "--verbosity", action='count', help="increase output verbosity", default=0) parser.add_argument('-V', '--version', action='version', version=pt.__version__) parsed_args = parser.parse_args(args) if parsed_args.verbosity == 1: logging.basicConfig(level=logging.INFO) elif parsed_args.verbosity > 1: logging.basicConfig(level=logging.DEBUG) else: logging.basicConfig() basename = parsed_args.ctx_basename # import DICOM dcm = pt.dicomhelper.read_dicom_folder(parsed_args.dicom_folder) if 'images' in dcm: c = pt.CtxCube() c.read_dicom(dcm) logger.info("Write CtxCube header... {}".format( basename + pt.CtxCube.header_file_extension)) c.write_trip_header(basename + pt.CtxCube.header_file_extension) logger.info("Write CtxCube... {}".format( basename + pt.CtxCube.data_file_extension)) c.write_trip_data(basename + pt.CtxCube.data_file_extension) else: logger.warning("No CT data found in {}".format( parsed_args.dicom_folder)) c = None if 'rtss' in dcm: logger.info("Write VdxCube... {}".format(basename + ".vdx")) vdx_cube = pt.VdxCube(cube=c) vdx_cube.read_dicom(dcm) vdx_cube.write_trip(basename + ".vdx") else: logger.warning("No RTSTRUCT data found in {}".format( parsed_args.dicom_folder)) return 0
def open_voxelplan(self, ctx_path): """ Open a Voxelplan type CTX and possibly a VDX if one exists with the same basename. """ model = self.model # local object of plot_model pm = self.model.plot # local object of plot_model # Get the CTX cubes first logger.debug("Open CTX {:s}".format(ctx_path)) ctx = pt.CtxCube() ctx.read(ctx_path) # update model model.ctx = ctx pm.ctx = ctx # Point to center of slices for default plotting pm.xslice = int(ctx.dimx * 0.5) pm.yslice = int(ctx.dimy * 0.5) pm.zslice = int(ctx.dimz * 0.5) # TODO: we assume transversal view as start. fixme. pm.slice_pos_idx = int(ctx.dimz * 0.5) # show file basename in window title self.app.setWindowTitle("PyTRiPGUI - {}".format(ctx.basename)) # Check if there is a VDX file with the same basename logger.debug("Check for VDX") from pytrip.util import TRiP98FilePath _d = TRiP98FilePath( ctx_path, ctx).dir_basename # returns full path, but without suffix. vdx_path = _d + ".vdx" logger.debug("Check if '{:s}' exists...".format(vdx_path)) # If VDX is there, load it. if os.path.isfile(vdx_path): logger.debug(" Open '{:s}'".format(vdx_path)) vdx = pt.VdxCube(self.model.ctx) vdx.read(vdx_path) # update model model.vdx = vdx pm.vdx = vdx # enable all VOIs to be plotted for voi in vdx.vois: pm.vois.append(voi) # add cube to the treeviews self.tree.update_tree() # update the canvas self.plot.update_viewcanvas()
def open_dicom(self, path): self.dcm = pt.dicomhelper.read_dicom_dir(path) if 'images' in self.dcm: self.ctx = pt.CtxCube() self.ctx.read_dicom(self.dcm) self.name = self.ctx.basename if 'rtss' in self.dcm: self.vdx = pt.VdxCube(self.ctx) self.vdx.read_dicom(self.dcm) self.name = self.vdx.basename
def main(args=sys.argv[1:]): """ Main function for trip2dicom.py """ # parse arguments parser = argparse.ArgumentParser() parser.add_argument( "ctx_data", help="location of CT file (header or data) in TRiP98 format", type=str) parser.add_argument("outputdir", help="Write resulting DICOM files to this directory.", type=str) parser.add_argument("-v", "--verbosity", action='count', help="increase output verbosity", default=0) parser.add_argument('-V', '--version', action='version', version=pt.__version__) args = parser.parse_args(args) output_folder = args.outputdir _, data_file_name = pt.CtxCube.parse_path(args.ctx_data) data_file_path = pt.CtxCube.discover_file(data_file_name) if not os.path.exists(data_file_path): print("CTX file missing") return 1 print("Convert CT images") c = pt.CtxCube() c.read(data_file_name) if not os.path.exists(output_folder): os.makedirs(output_folder) c.write_dicom(output_folder) ctx_basename = os.path.splitext(data_file_path)[0] ctx_path = ctx_basename + ".vdx" if os.path.exists(ctx_path): print("Convert structures") v = pt.VdxCube(content="", cube=c) v.read(ctx_path) v.write_dicom(output_folder) print("Done") return 0
def test_mcnamara_cube(self): """ McNamara test on real cubes. """ dose = pt.DosCube() dose.read(self.cube001) let = pt.LETCube() let.read(self.cube001) v = pt.VdxCube(dose) logger.info("Adding VDX from path " + self.vdx) v.read(self.vdx) # increasing LET should increase RBE abx = 10.0 # alpha/beta ratio for x-rays [Gy] rbe1 = rbe_mcnamara(dose.cube, let.cube, abx) rbe2 = rbe_mcnamara(dose.cube, let.cube, 2.0) self.assertTrue(np.all(rbe2 >= rbe1)) # RBE goes up as abx goes down.
def open_dicom(self, ddir): """ Open a DICOM directory. Images must be present. RTSS is optional. """ model = self.model # local object of plot_model pm = self.model.plot # local object of plot_model logger.debug("open dicom '{}'".format(ddir)) dcm = pt.dicomhelper.read_dicom_dir(ddir) ctx = None vdx = None if 'images' in dcm: logger.debug("Found images in DICOM") ctx = pt.CtxCube() ctx.read_dicom(dcm) model.ctx = ctx pm.ctx = ctx else: from pytripgui.view.dialogs import MyDialogs MyDialogs.show_error( "No images found in selected DICOM directory.") return if 'rtss' in dcm: logger.debug("Found rtss in DICOM") vdx = pt.VdxCube(cube=ctx) vdx.read_dicom(dcm) for voi in vdx.vois: pm.vois.append(voi) # This is a workaround for pytrip issue #455 https://github.com/pytrip/pytrip/issues/455 vdx.basename = "basename" model.vdx = vdx pm.vdx = vdx # TODO: RTplan data # add cube to the treeviews self.tree.update_tree() # update the canvas self.plot.update_viewcanvas()
def open_voxelplan(self, ctx_path): """ Open a Voxelplan type CTX and possibly a VDX if one exists with the same basename. """ model = self.model # local object of plot_model # Get the CTX cubes first logger.debug("Open CTX {:s}".format(ctx_path)) ctx = pt.CtxCube() ctx.read(ctx_path) # update model model.ctx = ctx self.model.one_plot.set_ctx(ctx) # show file basename in window title self.app.setWindowTitle("PyTRiPGUI - {}".format(ctx.basename)) # Check if there is a VDX file with the same basename logger.debug("Check for VDX") from pytrip.util import TRiP98FilePath _d = TRiP98FilePath( ctx_path, ctx).dir_basename # returns full path, but without suffix. vdx_path = _d + ".vdx" logger.debug("Check if '{:s}' exists...".format(vdx_path)) # If VDX is there, load it. if os.path.isfile(vdx_path): logger.debug(" Open '{:s}'".format(vdx_path)) vdx = pt.VdxCube(self.model.ctx) vdx.read(vdx_path) # update model model.vdx = vdx # update the canvas self.plot.update_viewcanvas()
This example shows how to use contours to select volume of interests inside a CTX cube. The VOI is then manipulated. """ import pytrip as pt # first define some paths and other important parameters ctx_path = "/home/bassler/Projects/CTdata/TST000/TST000000.ctx" vdx_path = "/home/bassler/Projects/CTdata/TST000/TST000000.vdx" my_target_voi = "GTV" # load CT cube my_ctx = pt.CtxCube() my_ctx.read(ctx_path) # load VOIs my_vdx = pt.VdxCube( my_ctx ) # my_vdx is the object which will hold all volumes of interest and the meta information my_vdx.read(vdx_path) # load the .vdx file print(my_vdx.get_voi_names()) # show us all VOIs found in the .vdx file # Select the requested VOI from the VdxCube object target_voi = my_vdx.get_voi_by_name(my_target_voi) # get_voi_cube() returns a DosCube() object, where all voxels inside the VOI holds the value 1000, and 0 elsewhere. voi_cube = target_voi.get_voi_cube() # Based on the retrieved DosCube() we calculate a three dimensional mask object, # which assigns True to all voxels inside the Voi, and False elsewhere. mask = (voi_cube.cube == 1000) # "The mask object and the CTX cube have same dimensions (they are infact inherited from the same top level class).
def test_read_solo(self): logger.info("Checking reading VdxCube without CT cube loaded") v = pt.VdxCube() v.read(self.vdx)
def open_vdx(self, path): vdx = pt.VdxCube(self.ctx) vdx.read(path) self.vdx = vdx if self.name != vdx.basename: logger.error("CTX | VDX patient name not match")
def main(args=sys.argv[1:]): """ Main function for dvhplot.py """ # parse arguments parser = argparse.ArgumentParser() parser.add_argument( "cube", help="Path to input cube. May also be a .dos or dosemlet.dos cube", type=str) parser.add_argument("vdx", help="Path to .vdx file holding the structures", type=str) parser.add_argument( "rois", nargs="?", help= "Comma-seperated list for ROIs to be analyzed. If not set, print list.", type=str, default=None) parser.add_argument("-d", "--dose", type=float, dest='dose', metavar='dose', help="Taget dose in [Gy]", default=-1.0) parser.add_argument("-o", "--output", type=str, dest='output', metavar='filename', help="Don't open GUI, save to filename instead.", default=None) parser.add_argument("-l", "--legend", dest='legend', default=False, action='store_true', help="Print legend box") parser.add_argument("-v", "--verbosity", action='count', help="increase output verbosity", default=0) parser.add_argument('-V', '--version', action='version', version=pt.__version__) parsed_args = parser.parse_args(args) if parsed_args.verbosity == 1: logging.basicConfig(level=logging.INFO) elif parsed_args.verbosity > 1: logging.basicConfig(level=logging.DEBUG) else: logging.basicConfig() path_cube = parsed_args.cube path_vdx = parsed_args.vdx rois_arg = parsed_args.rois dose = parsed_args.dose legend = parsed_args.legend outfile = parsed_args.output # there are some cases when this script is run on systems without DISPLAY variable being set # in such case matplotlib backend has to be explicitly specified # despite the fact interleaving imports with code lines is discouraged, we do it here # as it depends on the users options if outfile: matplotlib.use('Agg') import matplotlib.pyplot as plt d = pt.DosCube() d.read(path_cube) v = pt.VdxCube(d) v.read(path_vdx) vois = v.get_voi_names() if not rois_arg: print("Available ROIs:") for voi in vois: print("'{:s}'".format(voi)) return rois = rois_arg.split(",") if d.type == 'DOS': if dose < 0.0: plt.xlabel("Dose [%]") else: plt.xlabel("Dose [Gy]") elif d.type == 'LET': plt.xlabel("LET [keV/um]") else: plt.xlabel("") # unknown data cube for roi in rois: logger.info("Processing ROI '{:s}'...".format(roi)) voi = v.get_voi_by_name(roi) x, y = volume_histogram(d.cube, voi) if d.type == 'DOS': if dose < 0.0: x = x * 0.1 else: x = x * 0.001 * dose plt.plot(x, y, label=roi) plt.ylabel("Volume [%]") plt.grid(True) if legend: plt.legend() if outfile: plt.savefig(outfile) else: plt.show()
def main(args=sys.argv[1:]): """ Main function for dvhplot.py """ # parse arguments parser = argparse.ArgumentParser() parser.add_argument("cube", help="path to input cube. May also be a .dos or dosemlet.dos cube", type=str) parser.add_argument("vdx", help="path to .vdx file holding the structures", type=str) parser.add_argument("rois", nargs="?", help="comma-seperated list for ROIs to be analyzed. If not set, print list.", type=str, default=None) parser.add_argument("-d", "--dose", type=float, dest='dose', metavar='dose', help="target dose in [Gy] (if target_dose is unavailable in cube)", default=None) parser.add_argument("-o", "--output", type=str, dest='output', metavar='filename', help="don't open GUI, save figure to <filename> instead.", default=None) parser.add_argument("-t", "--tofile", type=str, dest='tofile', metavar='filename', help="save histogram data to <filename>.", default=None) parser.add_argument("-l", "--legend", dest='legend', default=False, action='store_true', help="print legend box") parser.add_argument("-v", "--verbosity", action='count', help="increase output verbosity", default=0) parser.add_argument('-V', '--version', action='version', version=pt.__version__) parsed_args = parser.parse_args(args) if parsed_args.verbosity == 1: logging.basicConfig(level=logging.INFO) elif parsed_args.verbosity > 1: logging.basicConfig(level=logging.DEBUG) else: logging.basicConfig() path_cube = parsed_args.cube path_vdx = parsed_args.vdx rois_arg = parsed_args.rois dose = parsed_args.dose legend = parsed_args.legend outfile = parsed_args.output tofile = parsed_args.tofile # there are some cases when this script is run on systems without DISPLAY variable being set # in such case matplotlib backend has to be explicitly specified # despite the fact interleaving imports with code lines is discouraged, we do it here # as it depends on the users options if outfile: matplotlib.use('Agg') import matplotlib.pyplot as plt d = pt.DosCube() d.read(path_cube) v = pt.VdxCube(d) v.read(path_vdx) vois = v.voi_names() if not rois_arg: print("Available ROIs:") for voi in vois: print("'{:s}'".format(voi)) return rois = rois_arg.split(",") for roi in rois: voi = v.get_voi_by_name(roi) vh = VolHist(d, voi, target_dose=dose) plt.xlabel(vh.xlabel) plt.ylabel(vh.ylabel) plt.plot(vh.x, vh.y, label=vh.name) plt.ylabel("Volume [%]") plt.grid(True) if legend: plt.legend() if tofile: logger.info("Write {}".format(tofile)) f = open(tofile, "w+") for _x, _y in zip(vh.x, vh.y): f.write("{:.3f} {:.3f}\n".format(_x, _y)) f.close() if outfile: plt.savefig(outfile) else: plt.show()
# Fist we specify the directory where all our files are: wdir = "/home/bassler/test" # In TRiP, the patient "TST000" would typically carry the filename "TST000000" patient_name = "TST000000" # so we can construc the paths to the CTX and VDX files like this: ctx_path = os.path.join(wdir, patient_name + ".ctx") vdx_path = os.path.join(wdir, patient_name + ".vdx") # Next we load the CT cube: c = pt.CtxCube() c.read(ctx_path) # And load the contours v = pt.VdxCube(c) v.read(vdx_path) # we may print all contours found in the Vdx file, if we want to print(v.get_voi_names()) # Ok, we have the Contours and the CT cube ready. Next we must prepare a plan. # We may choose any basename for the patient. All output files will be named using # this basename. plan = pte.Plan(basename=patient_name) # We need to specify where the kernel files can be found. The location may depend on the ion we # wnat to treat with. This example is for carbon ions: plan.ddd_dir = "/home/bassler/TRiP98/base/DATA/DDD/12C/RF3MM/*" plan.spc_dir = "/home/bassler/TRiP98/base/DATA/SPC/12C/RF3MM/*" plan.sis_path = "/home/bassler/TRiP98/base/DATA/SIS/12C.sis"