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_create_voi_cyl(self): logger.info("Creating CT cube from path " + self.cube000) c = pt.CtxCube() c.read(self.cube000) logger.info("Generating and adding cylinder VOI") v = create_cylinder(c, name="cube2", center=[10, 10, 10], radius=4, depth=8) self.assertEqual(v.number_of_slices(), 3)
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__) args = parser.parse_args(args) basename = args.ctx_basename # import DICOM dcm = pt.dicomhelper.read_dicom_folder(args.dicom_folder) c = pt.CtxCube() c.read_dicom(dcm) c.write_trip_header(basename + ".hed") c.write_trip_data(basename + ".ctx") return 0
def test_create_voi_cube(self): logger.info("Creating CT cube from path " + self.cube000) c = pt.CtxCube() c.read(self.cube000) logger.info("Generating and adding cube VOI") v = create_cube(c, name="cube1", center=[10, 10, 10], width=4, height=4, depth=8) self.assertEqual(v.number_of_slices(), 3)
def load_ct_cube(filename): """ loads the CT cube :params str filename: path to filename which must be loaded :returns: a CtxCube object and a str containing the path to the basename. """ if not filename: logger.warn("Empty CT cube filename") return None, None logger.info("Reading " + filename) ctx_header, _ = pt.CtxCube.parse_path(filename) if ctx_header is None: logger.warn("Path " + filename + " doesn't seem to point to proper CT cube") return None, None basename_cube = os.path.splitext(ctx_header)[0] c = pt.CtxCube() c.read(filename) logger.info("CT cube shape" + str(c.cube.shape)) cmax = c.cube.max() cmin = c.cube.min() logger.info("CT min, max values: {:d} {:d}".format(cmin, cmax)) return c, basename_cube
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 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 test_create_voi_2(self): logger.info("Testing cube from path " + self.cube000) c = pt.CtxCube() c.read(self.cube000) logger.info("Generating VOI from cube") v = create_voi_from_cube(c, name="cube4") # TODO check if v was created correctly self.assertEqual(v.number_of_slices(), 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 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 load_ct_cube(filename): """ loads the CT cube :params str filename: path to filename which must be loaded :returns: a CtxCube object and a str containing the path to the basename. """ if not filename: logger.warning("Empty CT cube filename") return None, None logger.info("Reading " + filename) c = pt.CtxCube() c.read(filename) logger.info("CT cube shape" + str(c.cube.shape)) cmax = c.cube.max() cmin = c.cube.min() logger.info("CT min, max values: {:d} {:d}".format(cmin, cmax)) return c, c.basename
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()
# # You should have received a copy of the GNU General Public License # along with PyTRiP98. If not, see <http://www.gnu.org/licenses/>. # """ 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()
def test_create_voi_sphere(self): logger.info("Creating CT cube from path " + self.cube000) c = pt.CtxCube() c.read(self.cube000) logger.info("Generating and adding sphere VOI") v = create_sphere(c, name="cube3", center=[10, 10, 10], radius=8) self.assertEqual(v.number_of_slices(), 6) logger.info("Checking Voi vdx_string method") self.assertGreater(len(v.vdx_string()), 1) logger.info("Checking Voi get_slice_at_pos method, non-existent slice") self.assertIsNone(v.get_slice_at_pos(137)) logger.info("Checking Voi get_slice_at_pos method, good slice") s = v.get_slice_at_pos(11) self.assertIsNotNone(s) logger.info("Checking Slice vdx_string method") self.assertGreater(len(s.vdx_string()), 1) logger.info("Checking Slice number_of_contours method") self.assertEqual(s.number_of_contours(), 1) logger.info("Checking Contour create_dicom_contours method") dicom_cont = s.create_dicom_contours(v.cube.create_dicom()) self.assertEqual(len(dicom_cont), 1) contour = s.contours[0] logger.info("Checking Contour calculate_center method") center, area = contour.calculate_center() self.assertAlmostEqual(center[0], 10.0) self.assertAlmostEqual(center[1], 10.0) self.assertAlmostEqual(center[2], 12.0) # TODO why 12 ? self.assertGreater(area, 100.0) logger.info("Checking Contour vdx_string method") self.assertGreater(len(contour.vdx_string()), 1) logger.info("Checking Contour number_of_points method") self.assertEqual(contour.number_of_points(), 99) logger.info("Checking Contour has_childs method") self.assertFalse( contour.has_childs()) # TODO why doesn't have children ? contour.print_child(level=0) logger.info("Test of Voi get_min_max method") s_min, s_max = v.get_min_max() self.assertIsNotNone(s_min) self.assertIsNotNone(s_max) # TODO check why get_min_max returns tuple, not a number self.assertEqual(s_max[2], 18.0) self.assertEqual(s_min[2], 3.0) logger.info( "Subsequent test of Voi get_min_max method, as it modifies the object" ) s_min, s_max = v.get_min_max() self.assertIsNotNone(s_min) self.assertIsNotNone(s_max) # TODO check why get_min_max returns tuple, not a number self.assertEqual(s_max[2], 18.0) self.assertEqual(s_min[2], 3.0) logger.info("Test of Voi get_2d_slice method (sagittal)") s2 = v.get_2d_slice(plane=pt.Voi.sagittal, depth=10.0) self.assertIsNotNone(s2) logger.info("Test of Voi get_2d_slice method (coronal)") s3 = v.get_2d_slice(plane=pt.Voi.coronal, depth=5.0) self.assertIsNotNone(s3) logger.info("Test of Voi get_2d_projection_on_basis method") v.get_2d_projection_on_basis(basis=((1, 0, 0), (0, 2, 0))) logger.info("Test of Voi create_point_tree method") v.create_point_tree() self.assertIsNotNone(v.points) self.assertEqual(len(v.points), 496) logger.info("Test of Voi get_row_intersections method") isec = v.get_row_intersections(pos=(10, 10, 9)) self.assertIsNotNone(isec) self.assertEqual(len(isec), 2) logger.info("Test of Voi get_voi_cube method") vc = v.get_voi_cube() self.assertIsNotNone(vc) center_pos = v.calculate_center() self.assertAlmostEqual(center_pos[0], 10.0)
logger = logging.getLogger(__name__) logging.basicConfig( level=logging.INFO) # give some output on what is going on. # 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
def open_ctx(self, path): ctx = pt.CtxCube() ctx.read(path) self.ctx = ctx self.name = ctx.basename