def single_calc_dvhs(self, rtss, rtdose, dict_roi, callback, dose_limit=None): dict_dvh = {} roi_list = [] for key in dict_roi: roi_list.append(key) self.copied += len(roi_list) callback(self.copied) # update the bar for roi in roi_list: dict_dvh[roi] = dvhcalc.get_dvh(rtss, rtdose, roi, dose_limit) self.copied += len(roi_list) callback(self.copied) # update the bar return dict_dvh
def test_FF_decubitus_right_structure_extents(self): """Test DVH for FF decubitus Rt orientation using structure extents.""" self.dose.ImageOrientationPatient = [0, -1, 0, -1, 0, 0] self.dose.PixelSpacing = [2.0, 1.0] # between Rows, Columns self.dose.ImagePositionPatient = [14, 19, 20] # X Y Z top left # see grid from test_FF_decubitus_right # 14 15 16 19 24 expected_counts = [0] * 14 + [ 1, 1, 0, 1, 2, 1, 0, 0, 0, 0, 1, 1, 0, 1, 2, 1, 0, 0, 0, 0, 1, 1, 0, 1, 2, 1 ] # 34 dvh = get_dvh(self.ss, self.dose, 1, use_structure_extents=True) diffl = dvh.differential # Counts are normalized to total, and to volume, # So undo that here for test dose grid. # 18=num dose voxels inside struct; 0.36=volume got_counts = diffl.counts * 18 / 0.36 assert_allclose(got_counts, expected_counts)
def calc_dvhs(dataset_rtss, dataset_rtdose, rois, interrupt_flag, dose_limit=None): """ :param dataset_rtss: RTSTRUCT DICOM dataset object. :param dataset_rtdose: RTDOSE DICOM dataset object. :param rois: Dictionary of ROI information. :param interrupt_flag: A threading.Event() object that tells the function to stop calculation. :param dose_limit: Limit of dose for DVH calculation. :return: Dictionary of all the DVHs of all the ROIs of the patient. """ dict_dvh = {} roi_list = [] for key in rois: roi_list.append(key) for roi in roi_list: dict_dvh[roi] = dvhcalc.get_dvh(dataset_rtss, dataset_rtdose, roi, dose_limit) if interrupt_flag.is_set(): # Stop calculating at the next DVH. return return dict_dvh
def calc_dvh(self, key, limit=None, calculate_full_volume=True, use_structure_extents=False, interpolation_resolution=None, interpolation_segments=0): """Calculate a DVH for testing.""" # Generate the calculated DVHs dvh = dvhcalc.get_dvh( self.rtss.ds, self.rtdose.ds, key, limit, calculate_full_volume=calculate_full_volume, use_structure_extents=use_structure_extents, interpolation_resolution=interpolation_resolution, interpolation_segments_between_planes=interpolation_segments) dvh.dose_units = 'Gy' return dvh
def calculate_review_dvh(self): global x, y patches = { 'x': [(0, [])], 'y': [(0, [])], 'roi_name': [(0, '')], 'volume': [(0, 1)], 'min_dose': [(0, '')], 'mean_dose': [(0, '')], 'max_dose': [(0, '')], 'mrn': [(0, '')], 'rx_dose': [(0, 1)] } try: if not self.sources.dvhs.data['x']: self.query.update_data() else: file_index = self.temp_dvh_info.mrn.index( self.select_reviewed_mrn.value) roi_index = self.dvh_review_rois.index( self.select_reviewed_dvh.value) structure_file = self.temp_dvh_info.structure[file_index] plan_file = self.temp_dvh_info.plan[file_index] dose_file = self.temp_dvh_info.dose[file_index] key = list( self.temp_dvh_info.get_roi_names( self.select_reviewed_mrn.value))[roi_index] rt_st = dicomparser.DicomParser(structure_file) rt_structures = rt_st.GetStructures() review_dvh = dvhcalc.get_dvh(structure_file, dose_file, key) dicompyler_plan = dicomparser.DicomParser(plan_file).GetPlan() roi_name = rt_structures[key]['name'] volume = review_dvh.volume min_dose = review_dvh.min mean_dose = review_dvh.mean max_dose = review_dvh.max if not self.review_rx.value: rx_dose = float(dicompyler_plan['rxdose']) / 100. self.review_rx.value = str(round(rx_dose, 2)) else: rx_dose = round(float(self.review_rx.value), 2) x = review_dvh.bincenters if max(review_dvh.counts): y = np.divide(review_dvh.counts, max(review_dvh.counts)) else: y = review_dvh.counts if self.radio_group_dose.active == 1: f = 5000 bin_count = len(x) new_bin_count = int(bin_count * f / (rx_dose * 100.)) x1 = np.linspace(0, bin_count, bin_count) x2 = np.multiply( np.linspace(0, new_bin_count, new_bin_count), rx_dose * 100. / f) y = np.interp(x2, x1, review_dvh.counts) y = np.divide(y, np.max(y)) x = np.divide(np.linspace(0, new_bin_count, new_bin_count), f) if self.radio_group_volume.active == 0: y = np.multiply(y, volume) patches = { 'x': [(0, x)], 'y': [(0, y)], 'roi_name': [(0, roi_name)], 'volume': [(0, volume)], 'min_dose': [(0, min_dose)], 'mean_dose': [(0, mean_dose)], 'max_dose': [(0, max_dose)], 'mrn': [(0, self.select_reviewed_mrn.value)], 'rx_dose': [(0, rx_dose)] } except: pass self.sources.dvhs.patch(patches) self.update_source_endpoint_calcs()
def parse_patient_data(self): """Thread to load the patient data.""" ptdata = self.patient patient = {} pbar = tqdm(total=100) if not 'images' in ptdata: # Look for DICOM data in the ptdata dictionary for rtdatatype in ptdata.keys(): if isinstance(ptdata[rtdatatype], pydicom.dataset.FileDataset): patient.update( dicomparser.DicomParser( ptdata[rtdatatype]).GetDemographics()) break if 'rtss' in ptdata: # pbar.update(20) pbar.n = 20 pbar.set_description('Processing RT Structure Set...') pbar.refresh() d = dicomparser.DicomParser(ptdata['rtss']) s = d.GetStructures() for k in s.keys(): s[k]['planes'] = d.GetStructureCoordinates(k) s[k]['thickness'] = d.CalculatePlaneThickness(s[k]['planes']) patient['structures'] = s if 'rtplan' in ptdata: pbar.n = 40 pbar.refresh() pbar.set_description('Processing RT Plan...') patient['plan'] = dicomparser.DicomParser( ptdata['rtplan']).GetPlan() if 'rtdose' in ptdata: pbar.n = 60 pbar.set_description('Processing RT Dose...') pbar.refresh() patient['dvhs'] = dicomparser.DicomParser( ptdata['rtdose']).GetDVHs() patient['dose'] = dicomparser.DicomParser(ptdata['rtdose']) if 'images' in ptdata: pbar.n = 80 pbar.set_description('Processing Images...') pbar.refresh() if not 'id' in patient: patient.update( dicomparser.DicomParser( ptdata['images'][0]).GetDemographics()) patient['images'] = [] for image in ptdata['images']: patient['images'].append(dicomparser.DicomParser(image)) if 'rxdose' in ptdata: if not 'plan' in patient: patient['plan'] = {} patient['plan']['rxdose'] = ptdata['rxdose'] # if the min/max/mean dose was not present, calculate it and save it for each structure pbar.n = 90 pbar.set_description('Processing DVH data...') pbar.refresh() if ('dvhs' in patient) and ('structures' in patient): # If the DVHs are not present, calculate them i = 0 for key, structure in patient['structures'].items(): # Only calculate DVHs if they are not present for the structure # or recalc all DVHs if the preference is set if ((not (key in patient['dvhs'].keys()))): # Only calculate DVHs for structures, not applicators # and only if the dose grid is present if ((structure['name'].startswith('Applicator')) or (not "PixelData" in patient['dose'].ds)): continue pbar.n = int(np.round( 10 * i / len(patient['structures']))) + 90 pbar.set_description('Calculating DVH for ' + structure['name'] + '...') pbar.refresh() # Limit DVH bins to 500 Gy due to high doses in brachy dvh = dvhcalc.get_dvh(ptdata['rtss'], patient['dose'].ds, key, 50000) if len(dvh.counts): patient['dvhs'][key] = dvh i += 1 for key, dvh in patient['dvhs'].items(): dvh.rx_dose = patient['plan']['rxdose'] / 100 pbar.n = 100 pbar.set_description('Done') pbar.close() self.parse_patient = patient
def plot(self): #retrieve plan data & design DVH window plan1files = float(self.box5.get()) plan2files = float(self.box6.get()) self.newWindow = Toplevel( self.window) #initiate new window for summed DVH self.newWindow.title("Recalculated DVH") #self.newWindow.iconbitmap("C:/Users/Owner/Documents/Rlogo2.ico") rtssfile1 = self.filename5 #structure file RTss1 = dicomparser.DicomParser( rtssfile1) #read through structure file RTstructures1 = RTss1.GetStructures( ) #get each individual structure information #at least one dose file required for plan 1 and plan 2 rtdosefile1 = self.filename1 rtdosefile5 = self.filename6 #conditional statements for multiple dose file input if plan1files >= 2: rtdosefile2 = self.filename2 if plan1files >= 3: rtdosefile3 = self.filename3 if plan1files >= 4: rtdosefile4 = self.filename4 if plan2files >= 2: rtdosefile6 = self.filename7 if plan2files >= 3: rtdosefile7 = self.filename8 if plan2files >= 4: rtdosefile8 = self.filename9 #structure to be analysed enteredtext = str(self.box4.get()) #EQD2 parameters dpf1 = float(self.box1.get()) #plan 1 dose per fraction dpf2 = float(self.box2.get()) #plan 2 dose per fraction abratio = float(self.box3.get()) # tissue-specific alpha/beta ratio #EQD2 equation x1 = np.array((dpf1 + abratio) / (float(2.0) + abratio)) x2 = np.array((dpf2 + abratio) / (float(2.0) + abratio)) # Generation of the calculated DVHs #initiation of empty arrays to fill with correct structure data calcdvhs1 = {} calcdvhs2 = {} calcdvhs3 = {} calcdvhs4 = {} calcdvhs5 = {} calcdvhs6 = {} calcdvhs7 = {} calcdvhs8 = {} print("RED is calculating your EQD2 DVH....") print("Please wait, this could take a few moments") #iterate through dose file 1 to find correct stucture data for key, structure in RTstructures1.items(): calcdvhs1[key] = dvhcalc.get_dvh(rtssfile1, rtdosefile1, key) if (key in calcdvhs1) and (structure['name'] == enteredtext) and ( len(calcdvhs1[key].counts) and calcdvhs1[key].counts[0] != 0): print('1st Plan 1 DVH found for ' + structure['name']) data1 = np.array(calcdvhs1[key].bins) * x1 lastdata1 = data1[-1] vola = calcdvhs1[key].counts * 100 / calcdvhs1[key].counts[0] # iterate through dose file 2 to find correct stucture data if plan1files >= 2: for key, structure in RTstructures1.items(): calcdvhs2[key] = dvhcalc.get_dvh(rtssfile1, rtdosefile2, key) if (key in calcdvhs2) and (structure['name'] == enteredtext) and ( len(calcdvhs2[key].counts) and calcdvhs2[key].counts[0] != 0): print('2nd Plan 1 DVH found for ' + structure['name']) data2 = np.array(calcdvhs2[key].bins) * x1 lastdata2 = data2[-1] volb = calcdvhs2[key].counts * 100 / calcdvhs2[key].counts[ 0] # iterate through dose file 3 to find correct stucture data if plan1files >= 3: for key, structure in RTstructures1.items(): calcdvhs3[key] = dvhcalc.get_dvh(rtssfile1, rtdosefile3, key) if (key in calcdvhs3) and (structure['name'] == enteredtext) and ( len(calcdvhs3[key].counts) and calcdvhs3[key].counts[0] != 0): print('3rd Plan 1 DVH found for ' + structure['name']) data3 = np.array(calcdvhs3[key].bins) * x1 lastdata3 = data3[-1] volc = calcdvhs3[key].counts * 100 / calcdvhs3[key].counts[ 0] # iterate through dose file 4 to find correct stucture data if plan1files >= 4: for key, structure in RTstructures1.items(): calcdvhs4[key] = dvhcalc.get_dvh(rtssfile1, rtdosefile4, key) if (key in calcdvhs4) and (structure['name'] == enteredtext) and ( len(calcdvhs4[key].counts) and calcdvhs4[key].counts[0] != 0): print('4th Plan 1 DVH found for ' + structure['name']) data4 = np.array(calcdvhs4[key].bins) * x1 lastdata4 = data4[-1] vold = calcdvhs4[key].counts * 100 / calcdvhs4[key].counts[ 0] # iterate through dose file 5 to find correct stucture data for key, structure in RTstructures1.items(): calcdvhs5[key] = dvhcalc.get_dvh(rtssfile1, rtdosefile5, key) if (key in calcdvhs5) and (structure['name'] == enteredtext) and ( len(calcdvhs5[key].counts) and calcdvhs5[key].counts[0] != 0): print('1st Plan 2 DVH found for ' + structure['name']) data5 = np.array(calcdvhs5[key].bins) * x2 lastdata5 = data5[-1] vole = calcdvhs5[key].counts * 100 / calcdvhs5[key].counts[0] # iterate through dose file 6 to find correct stucture data if plan2files >= 2: for key, structure in RTstructures1.items(): calcdvhs6[key] = dvhcalc.get_dvh(rtssfile1, rtdosefile6, key) if (key in calcdvhs6) and (structure['name'] == enteredtext) and ( len(calcdvhs6[key].counts) and calcdvhs6[key].counts[0] != 0): print('2nd Plan 2 DVH found for ' + structure['name']) data6 = np.array(calcdvhs6[key].bins) * x2 lastdata6 = data6[-1] volf = calcdvhs6[key].counts * 100 / calcdvhs6[key].counts[ 0] # iterate through dose file 7 to find correct stucture data if plan2files >= 3: for key, structure in RTstructures1.items(): calcdvhs7[key] = dvhcalc.get_dvh(rtssfile1, rtdosefile7, key) if (key in calcdvhs7) and (structure['name'] == enteredtext) and ( len(calcdvhs7[key].counts) and calcdvhs7[key].counts[0] != 0): print('3rd Plan 2 DVH found for ' + structure['name']) data7 = np.array(calcdvhs7[key].bins) * x2 lastdata7 = data7[-1] volg = calcdvhs7[key].counts * 100 / calcdvhs7[key].counts[ 0] # iterate through dose file 8 to find correct stucture data if plan2files >= 4: for key, structure in RTstructures1.items(): calcdvhs8[key] = dvhcalc.get_dvh(rtssfile1, rtdosefile8, key) if (key in calcdvhs8) and (structure['name'] == enteredtext) and ( len(calcdvhs8[key].counts) and calcdvhs8[key].counts[0] != 0): print('4th Plan 2 DVH found for ' + structure['name']) data8 = np.array(calcdvhs8[key].bins) * x2 lastdata8 = data8[-1] volh = calcdvhs8[key].counts * 100 / calcdvhs8[key].counts[ 0] #initiation of DVH window fig = Figure() ax = fig.add_subplot(111) #addition of plan 1 dose data if plan1files == 1: totaldose1 = lastdata1 if plan1files == 2: totaldose1 = lastdata1 + lastdata2 if plan1files == 3: totaldose1 = lastdata1 + lastdata2 + lastdata3 if plan1files == 4: totaldose1 = lastdata1 + lastdata2 + lastdata3 + lastdata4 #addition of plan 2 dose data if plan2files == 1: totaldose2 = lastdata5 if plan2files == 2: totaldose2 = lastdata5 + lastdata6 if plan2files == 3: totaldose2 = lastdata5 + lastdata6 + lastdata7 if plan2files == 4: totaldose2 = lastdata5 + lastdata6 + lastdata7 + lastdata8 #conditional statements for plan 1 based on number of dose files #definition of volume and dose data if plan1files == 1: y = vola #volume data y1len = len(y) x = np.linspace(0, totaldose1, num=y1len) #dose data elif plan1files == 2: interyvaluesplan1b = np.concatenate((vola, volb), axis=0) #summed volume data sorty21 = np.sort(interyvaluesplan1b ) #sorted volume data array in ascending order vol21 = sorty21[:: -1] #volume data array reversed to descending order y = vol21 #volume data y1len = len(y) x = np.linspace(0, totaldose1, num=y1len) #dose data elif plan1files == 3: interyvalues1plan1a = np.concatenate((vola, volb), axis=0) interyvaluesplan1c = np.concatenate((interyvalues1plan1a, volc), axis=0) #sumed volume data sorty31 = np.sort(interyvaluesplan1c ) #sorted volume data array in ascending order vol31 = sorty31[:: -1] #volume data array reversed to descending order y = vol31 #volume data y1len = len(y) x = np.linspace(0, totaldose1, num=y1len) #dose data elif plan1files == 4: interyvalues1plan1b = np.concatenate((vola, volb), axis=0) interyvalues2plan1a = np.concatenate((volc, vold), axis=0) interyvaluesplan1d = np.concatenate( (interyvalues1plan1b, interyvalues2plan1a), axis=0) #summed volume data sorty41 = np.sort(interyvaluesplan1d ) #sorted volume data array in ascending order vol41 = sorty41[:: -1] #volume data array reversed to descending order y = vol41 y1len = len(y) x = np.linspace(0, totaldose1, num=y1len) #dose data else: print("please check code starting at line 367") if plan2files == 1: b = vole #volume data blen = len(b) a = np.linspace(0, totaldose2, num=blen) #dose data elif plan2files == 2: interyvaluesplan2b = np.concatenate((vole, volf), axis=0) #summed volume data sorty22 = np.sort(interyvaluesplan2b ) # sorted volume data array in ascending order vol22 = sorty22[:: -1] #volume data array reversed to descending order b = vol22 blen = len(b) a = np.linspace(0, totaldose2, num=blen) #dose data elif plan2files == 3: interyvalues1plan2a = np.concatenate((vole, volf), axis=0) interyvaluesplan2c = np.concatenate((interyvalues1plan2a, volg), axis=0) #summed volume data sorty32 = np.sort(interyvaluesplan2c ) #sorted volume data array in ascending order vol32 = sorty32[:: -1] #volume data array reversed to descending order b = vol32 blen = len(b) a = np.linspace(0, totaldose2, num=blen) #dose data elif plan2files == 4: interyvalues1plan2b = np.concatenate((vole, volf), axis=0) interyvalues2plan2a = np.concatenate((volg, volh), axis=0) interyvaluesplan2d = np.concatenate( (interyvalues1plan2b, interyvalues2plan2a), axis=0) #summed volume data sorty42 = np.sort(interyvaluesplan2d ) #sorted volume data array in ascending order vol42 = sorty42[:: -1] # volume data array reversed to descending order b = vol42 blen = len(b) a = np.linspace(0, totaldose2, num=blen) #dose data else: print("please check code starting at line 398") #plot plan 1 and plan 2 re-calculated DVH ax.plot(x, y, c='b') ax.plot(a, b, c='g') array = np.linspace(0, 100, 9000, endpoint=False) # get values from plan1 graph line1 = ax.lines[0] line1.get_xydata() xdat1 = line1.get_xdata() #get x data fp1 = xdat1[:: -1] # reverses x values to match reversed y values in array# ydat1 = line1.get_ydata() #get y data xp1 = ydat1[:: -1] # reverses y values from decreasng to increasing so interpolation function can work # get values from plan2 graph line2 = ax.lines[1] line2.get_xydata() xdat2 = line2.get_xdata() #get xdata fp2 = xdat2[:: -1] # reverses x values to match reversed y values in array ydat2 = line2.get_ydata() #get y data xp2 = ydat2[:: -1] # reverses y values from decreasng to increasing so interpolation function can work # set volume array to use for dose interpolation inter1 = np.interp([array], xp1, fp1) # interpolation of plan1 dose reshapeinter1 = np.reshape(inter1, (9000, 1)) inter2 = np.interp([array], xp2, fp2) # interpolation of plan2 dose reshapeinter2 = np.reshape(inter2, (9000, 1)) xvalues = reshapeinter1 + reshapeinter2 # adding plan1 and plan2 dose reshapearray = np.reshape( array, (9000, 1)) # array of specified %volume intervals #define strings dpf12 = str(dpf1) dpf21 = str(dpf2) abdisplay = str(abratio) #plot summed re-calculated DVH in seperate window plt.plot(xvalues, reshapearray, c='r') plt.title("Recalculated Summed DVH") # plt.iconbitmap("C:/Users/Owner/Documents/Rlogo2.ico") plt.title("Summed EQD2 DVH for " + enteredtext + " a/b=" + abdisplay + " dpf Plan 1=" + dpf12 + " dpf Plan 2=" + dpf21) ax.legend(["Plan 1", "Plan 2"]) ax.set_title("EQD2 DVH for " + enteredtext + " a/b=" + abdisplay + " dpf Plan 1=" + dpf12 + " dpf Plan 2=" + dpf21) ax.set_ylabel("Volume %", fontsize=14) ax.set_xlabel("EQD2 Gy", fontsize=14) plt.legend(["Plan 1 + 2"]) plt.ylabel("Volume %", fontsize=14) plt.xlabel("EQD2 Gy", fontsize=14) ax.set_axisbelow(True) #design of summed dvh plt.grid(color='gray', linestyle='dashed') datacursor() ax.yaxis.grid(color='gray', linestyle='dashed') ax.xaxis.grid(color='gray', linestyle='dashed') #draw sumed DVH canvas = FigureCanvasTkAgg(fig, master=self.newWindow) canvas.get_tk_widget().grid(row=10) canvas.draw() plt.show() #initiate toolbar for analysis toolbarFrame = Frame(master=self.newWindow) toolbarFrame.grid(row=20) toolbar = NavigationToolbar2Tk(canvas, toolbarFrame) toolbar.draw()
def calculate_dvh(self, key): if key not in list(self.dvh): files = self.plans[self.select_plan.value] self.dvh[key] = dvhcalc.get_dvh(files['rtstruct'], files['rtdose'], key)
x, y, z, dx, dy, dz, volume = oncdose.process_file(dosename) print('dx=', dx, 'dy=', dy, 'dz=', dz, 'dose volume', np.shape(volume)) if args.structure: structname = args.structure structures = pydicom.dcmread(structname) plt.figure() #we need to get an accurate dose resolution to perform the calculations for k in range(0, structures[0x3006, 0x0039].VM): print(dx, dy) calcdvh = dvhcalc.get_dvh( structname, dosename, k, interpolation_resolution=(dy, dx), interpolation_segments_between_planes=10).relative_volume # calcdvh = dvhcalc.get_dvh(structname, dosename, k).relative_volume # calcdvh.volume_units = '%' # print(calcdvh.relative_volume) calcdvh.plot() plt.show() # calcdvh=dvhcalc.get_dvh(structname,dosename,0) # print(calcdvh.describe()) # print(calcdvh.cumulative) # plt.figure() # calcdvh.plot() # plt.show()
def calc_dvh_worker(rtss, dose, roi, queue, dose_limit=None): dvh = {} dvh[roi] = dvhcalc.get_dvh(rtss, dose, roi, dose_limit) queue.put(dvh)
def test_FF_decubitus_left(self): """Test DVH for feet-first decubitus left orientation.""" self.dose.ImageOrientationPatient = [0, 1, 0, 1, 0, 0] self.dose.PixelSpacing = [2.0, 1.0] # between Rows, Columns # original ipp = [2, 12, -20] # Then X = r * dr + ipp[0], X increases down the rows # and Y = c * dc + ipp[1], Y increases across cols # (https://nipy.org/nibabel/dicom/dicom_orientation.html # #dicom-affine-formula) # In this test, we also shift Z so three structure planes use the # first three dose planes rather than the middle three, # just to ensure asymmetry in z direction is checked. # Note, planes should really be reversed in pixel array, but doesn't # matter since contour is identical on each slice. self.dose.ImagePositionPatient = [2, 12, 10] # X Y Z top left # Below show contours box of (3, 14.5) - (7, 17.5) on dose grid # Y=12 13 14 15 16 17 19 # X=2 [ 0, 0, 0, 3, 4, 5, 6, 7], # |-----------| # 4 [ 0, 0, 0, 3, 4, 5, 6, 7] # 6 [ 0, 0, 0, 3, 4, 5, 6, 7] # |-----------| # 8 [ 3, 3, 3, 6, 7, 8, 9, 10] # 10 [ 4, 4, 4, 7, 8, 9, 10, 11] # 12 [ 5, 5, 5, 8, 9, 10, 11, 12] # 14 [ 6, 6, 6, 9, 10, 11, 12, 13]] # Y=12 13 14 19 # X=2 [10, 10, 10, 13, 14, 15, 16, 17], # |-----------| # 4 [10, 10, 10, 13, 14, 15, 16, 17] # 6 [10, 10, 10, 13, 14, 15, 16, 17] # |-----------| # 8 [13, 13, 13, 16, 17, 18, 19, 20] # 10 [14, 14, 14, 17, 18, 19, 20, 21] # 12 [15, 15, 15, 18, 19, 20, 21, 22] # 14 [16, 16, 16, 19, 20, 21, 22, 23]] # Y=12 13 14 19 # X=2 [20, 20, 20, 23, 24, 25, 26, 27] # |-----------| # 4 [20, 20, 20, 23, 24, 25, 26, 27] # 6 [20, 20, 20, 23, 24, 25, 26, 27] # |-----------| # 8 [23, 23, 23, 26, 27, 28, 29, 30] # 10 [24, 24, 24, 27, 28, 29, 30, 31] # 12 [25, 25, 25, 28, 29, 30, 31, 32] # 14 [...] # 3 expected_counts = [0] * 3 + [ 2, 2, 2, 0, 0, 0, 0, 0, 0, 0, 2, 2, 2, 0, 0, 0, 0, 0, 0, 0, 2, 2, 2 ] # 13 23 dvh = get_dvh(self.ss, self.dose, 1) diffl = dvh.differential # Counts are normalized to total, and to volume, # So undo that here for test dose grid. # 18=num dose voxels inside struct; 0.36=volume got_counts = diffl.counts * 18 / 0.36 assert_allclose(got_counts, expected_counts)
def test_not_implemented_orientations(self): """Test unhandled orientations raise NotImplementedError.""" self.dose.ImageOrientationPatient = [0.7071, 0.7071, 0, 1, 0, 0] with self.assertRaises(NotImplementedError): _ = get_dvh(self.ss, self.dose, 1)
def LoadPatientDataThread(self, parent, ptdata, progressFunc, updateFunc): """Thread to load the patient data.""" # Call the progress function to update the gui wx.CallAfter(progressFunc, 0, 0, 'Processing patient data...') patient = {} if not 'images' in ptdata: #Look for DICOM data in the ptdata dictionary for rtdatatype in ptdata.keys(): if isinstance(ptdata[rtdatatype], pydicom.dataset.FileDataset): patient.update(dp(ptdata[rtdatatype]).GetDemographics()) break if 'rtss' in ptdata: wx.CallAfter(progressFunc, 20, 100, 'Processing RT Structure Set...') d = dp(ptdata['rtss']) s = d.GetStructures() for k in s.keys(): s[k]['planes'] = d.GetStructureCoordinates(k) s[k]['thickness'] = d.CalculatePlaneThickness(s[k]['planes']) patient['structures'] = s if 'rtplan' in ptdata: wx.CallAfter(progressFunc, 40, 100, 'Processing RT Plan...') patient['plan'] = dp(ptdata['rtplan']).GetPlan() if 'rtdose' in ptdata: wx.CallAfter(progressFunc, 60, 100, 'Processing RT Dose...') patient['dvhs'] = dp(ptdata['rtdose']).GetDVHs() patient['dose'] = dp(ptdata['rtdose']) if 'images' in ptdata: wx.CallAfter(progressFunc, 80, 100, 'Processing Images...') if not 'id' in patient: patient.update(dp(ptdata['images'][0]).GetDemographics()) patient['images'] = [] for image in ptdata['images']: patient['images'].append(dp(image)) if 'rxdose' in ptdata: if not 'plan' in patient: patient['plan'] = {} patient['plan']['rxdose'] = ptdata['rxdose'] # if the min/max/mean dose was not present, calculate it and save it for each structure wx.CallAfter(progressFunc, 90, 100, 'Processing DVH data...') if ('dvhs' in patient) and ('structures' in patient): # If the DVHs are not present, calculate them i = 0 for key, structure in patient['structures'].items(): # Only calculate DVHs if they are not present for the structure # or recalc all DVHs if the preference is set if ((not (key in patient['dvhs'].keys())) or (self.dvhRecalc == 'Always Recalculate DVH')): # Only calculate DVHs for structures, not applicators # and only if the dose grid is present if ((structure['name'].startswith('Applicator')) or (not "PixelData" in patient['dose'].ds)): continue wx.CallAfter( progressFunc, 10 * i / len(patient['structures']) + 90, 100, 'Calculating DVH for ' + structure['name'] + '...') # Limit DVH bins to 500 Gy due to high doses in brachy dvh = dvhcalc.get_dvh(ptdata['rtss'], patient['dose'].ds, key, 50000) if len(dvh.counts): patient['dvhs'][key] = dvh i += 1 for key, dvh in patient['dvhs'].items(): dvh.rx_dose = patient['plan']['rxdose'] / 100 wx.CallAfter(progressFunc, 100, 100, 'Done') wx.CallAfter(updateFunc, patient)
def calc_dvh(self, key, limit=None, calculate_full_volume=True): """Calculate a DVH for testing.""" # Generate the calculated DVHs return dvhcalc.get_dvh( self.rtss.ds, self.rtdose.ds, key, limit, calculate_full_volume=calculate_full_volume)
def LoadPatientDataThread(self, parent, ptdata, progressFunc, updateFunc): """Thread to load the patient data.""" # Call the progress function to update the gui wx.CallAfter(progressFunc, 0, 0, 'Processing patient data...') patient = {} if not 'images' in ptdata: #Look for DICOM data in the ptdata dictionary for rtdatatype in ptdata.keys(): if isinstance(ptdata[rtdatatype], pydicom.dataset.FileDataset): patient.update(dp(ptdata[rtdatatype]).GetDemographics()) break if 'rtss' in ptdata: wx.CallAfter(progressFunc, 20, 100, 'Processing RT Structure Set...') d = dp(ptdata['rtss']) s = d.GetStructures() for k in s.keys(): s[k]['planes'] = d.GetStructureCoordinates(k) s[k]['thickness'] = d.CalculatePlaneThickness(s[k]['planes']) patient['structures'] = s if 'rtplan' in ptdata: wx.CallAfter(progressFunc, 40, 100, 'Processing RT Plan...') patient['plan'] = dp(ptdata['rtplan']).GetPlan() if 'rtdose' in ptdata: wx.CallAfter(progressFunc, 60, 100, 'Processing RT Dose...') patient['dvhs'] = dp(ptdata['rtdose']).GetDVHs() patient['dose'] = dp(ptdata['rtdose']) if 'images' in ptdata: wx.CallAfter(progressFunc, 80, 100, 'Processing Images...') if not 'id' in patient: patient.update(dp(ptdata['images'][0]).GetDemographics()) patient['images'] = [] for image in ptdata['images']: patient['images'].append(dp(image)) if 'rxdose' in ptdata: if not 'plan' in patient: patient['plan'] = {} patient['plan']['rxdose'] = ptdata['rxdose'] # if the min/max/mean dose was not present, calculate it and save it for each structure wx.CallAfter(progressFunc, 90, 100, 'Processing DVH data...') if ('dvhs' in patient) and ('structures' in patient): # If the DVHs are not present, calculate them i = 0 for key, structure in patient['structures'].items(): # Only calculate DVHs if they are not present for the structure # or recalc all DVHs if the preference is set if ((not (key in patient['dvhs'].keys())) or (self.dvhRecalc == 'Always Recalculate DVH')): # Only calculate DVHs for structures, not applicators # and only if the dose grid is present if ((structure['name'].startswith('Applicator')) or (not "PixelData" in patient['dose'].ds)): continue wx.CallAfter(progressFunc, 10*i/len(patient['structures'])+90, 100, 'Calculating DVH for ' + structure['name'] + '...') # Limit DVH bins to 500 Gy due to high doses in brachy dvh = dvhcalc.get_dvh(ptdata['rtss'], patient['dose'].ds, key, 50000) if len(dvh.counts): patient['dvhs'][key] = dvh i += 1 for key, dvh in patient['dvhs'].items(): dvh.rx_dose = patient['plan']['rxdose'] / 100 wx.CallAfter(progressFunc, 100, 100, 'Done') wx.CallAfter(updateFunc, patient)
def calc_dvh(self, key, limit=None): """Calculate a DVH for testing.""" # Generate the calculated DVHs return dvhcalc.get_dvh(self.rtss.ds, self.rtdose.ds, key, limit)
def calc_dvh_worker(rtss, dose, roi, queue, thickness, dose_limit=None): dvh = {} dvh[roi] = \ dvhcalc.get_dvh(rtss, dose, roi, dose_limit, thickness=thickness) queue.put(dvh)
def __init__(self, structure_file, dose_file): # Get ROI Category Map database_rois = DatabaseROIs() # Import RT Structure and RT Dose files using dicompyler rt_structure_dicom = dicom.read_file(structure_file) mrn = rt_structure_dicom.PatientID study_instance_uid = rt_structure_dicom.StudyInstanceUID rt_structure = dicomparser.DicomParser(structure_file) rt_structures = rt_structure.GetStructures() if hasattr(rt_structure_dicom, 'PhysiciansOfRecord'): physician = rt_structure_dicom.PhysiciansOfRecord.upper() elif hasattr(rt_structure_dicom, 'ReferringPhysicianName'): physician = rt_structure_dicom.ReferringPhysicianName.upper() else: physician = '(NULL)' values = {} row_counter = 0 self.dvhs = {} for key in rt_structures: # Import DVH from RT Structure and RT Dose files if rt_structures[key]['type'] != 'MARKER': current_dvh_calc = dvhcalc.get_dvh(structure_file, dose_file, key) self.dvhs[row_counter] = current_dvh_calc.counts if current_dvh_calc.volume > 0: print('Importing', current_dvh_calc.name, sep=' ') if rt_structures[key]['name'].lower().find('itv') == 0: roi_type = 'ITV' else: roi_type = rt_structures[key]['type'] current_roi_name = clean_name(rt_structures[key]['name']) if database_rois.is_roi(current_roi_name): if database_rois.is_physician(physician): physician_roi = database_rois.get_physician_roi(physician, current_roi_name) institutional_roi = database_rois.get_institutional_roi(physician, physician_roi) else: if current_roi_name in database_rois.institutional_rois: institutional_roi = current_roi_name else: institutional_roi = 'uncategorized' physician_roi = 'uncategorized' else: institutional_roi = 'uncategorized' physician_roi = 'uncategorized' coord = rt_structure.GetStructureCoordinates(key) roi_coord_str = dicompyler_roi_coord_to_db_string(rt_structure.GetStructureCoordinates(key)) try: surface_area = surface_area_of_roi(coord) except: print("Surface area calculation failed for key, name: %s, %s" % (key, current_dvh_calc.name)) surface_area = '(NULL)' # volume = calc_volume(get_planes_from_string(roi_coord_str)) current_dvh_row = DVHRow(mrn, study_instance_uid, institutional_roi, physician_roi, current_roi_name, roi_type, current_dvh_calc.volume, current_dvh_calc.min, current_dvh_calc.mean, current_dvh_calc.max, ','.join(['%.2f' % num for num in current_dvh_calc.counts]), roi_coord_str, surface_area) values[row_counter] = current_dvh_row row_counter += 1 self.count = row_counter dvh_range = range(self.count) for attr in dir(values[0]): if not attr.startswith('_'): setattr(self, attr, [getattr(values[x], attr) for x in dvh_range])
def LoadPatientDataThread(self, parent, ptdata, progressFunc, updateFunc): """Thread to load the patient data.""" # Call the progress function to update the gui wx.CallAfter(progressFunc, 0, 0, 'Processing patient data...') patient = {} if not 'images' in ptdata: #Look for DICOM data in the ptdata dictionary for rtdatatype in ptdata.keys(): if isinstance(ptdata[rtdatatype], pydicom.dataset.FileDataset): patient.update(dp(ptdata[rtdatatype]).GetDemographics()) break if 'rtss' in ptdata: wx.CallAfter(progressFunc, 20, 100, 'Processing RT Structure Set...') d = dp(ptdata['rtss']) s = d.GetStructures() for k in s.keys(): s[k]['planes'] = d.GetStructureCoordinates(k) s[k]['thickness'] = d.CalculatePlaneThickness(s[k]['planes']) patient['structures'] = s if 'rtplan' in ptdata: wx.CallAfter(progressFunc, 40, 100, 'Processing RT Plan...') patient['plan'] = dp(ptdata['rtplan']).GetPlan() if 'rtdose' in ptdata: wx.CallAfter(progressFunc, 60, 100, 'Processing RT Dose...') patient['dvhs'] = dp(ptdata['rtdose']).GetDVHs() patient['dose'] = dp(ptdata['rtdose']) if 'images' in ptdata: wx.CallAfter(progressFunc, 80, 100, 'Processing Images...') if not 'id' in patient: patient.update(dp(ptdata['images'][0]).GetDemographics()) patient['images'] = [] for image in ptdata['images']: patient['images'].append(dp(image)) if 'rxdose' in ptdata: if not 'plan' in patient: patient['plan'] = {} patient['plan']['rxdose'] = ptdata['rxdose'] # if the min/max/mean dose was not present, calculate it and save it for each structure wx.CallAfter(progressFunc, 90, 100, 'Processing DVH data...') if ('dvhs' in patient) and ('structures' in patient): # If the DVHs are not present, calculate them i = 0 # Grabbing patient ID dvh_patientID = ptdata['rtdose'].PatientID csv_filename = str(dvh_patientID) + '.csv' dvh_csv_list = [] # maximum dose of all ROIs max_roi_dose = 0 # csv Header csv_header = [] for key, structure in patient['structures'].items(): # Initialize a list for dvh exporting to csv dvh_roi_list = [] # Only calculate DVHs if they are not present for the structure # or recalc all DVHs if the preference is set if ((not (key in patient['dvhs'].keys())) or (self.dvhRecalc == 'Always Recalculate DVH')): # Only calculate DVHs for structures, not applicators # and only if the dose grid is present if ((structure['name'].startswith('Applicator')) or (not "PixelData" in patient['dose'].ds)): continue wx.CallAfter( progressFunc, 10 * i / len(patient['structures']) + 90, 100, 'Calculating DVH for ' + structure['name'] + '...') # Limit DVH bins to 500 Gy due to high doses in brachy dvh = dvhcalc.get_dvh(ptdata['rtss'], patient['dose'].ds, key, 50000) # Retrieve roi name dvh_roiName = dvh.name # Retrieve roi dvh volume dvh_roiVolume = dvh.volume # Append info dvh_roi_list.append(dvh_patientID) dvh_roi_list.append(dvh_roiName) dvh_roi_list.append(dvh_roiVolume) # dvh_roiDose = dvh.relative_volume.counts for i in range(0, len(dvh_roiDose), 10): dvh_roi_list.append(dvh_roiDose[i]) if i > max_roi_dose: max_roi_dose = i dvh_csv_list.append(dvh_roi_list) if len(dvh.counts): patient['dvhs'][key] = dvh i += 1 csv_header.append('Hash ID') csv_header.append('ROI') csv_header.append('Volume (mL)') for i in range(0, max_roi_dose + 1, 10): csv_header.append(str(i) + 'cGy') pddf_csv = pd.DataFrame(dvh_csv_list, columns=csv_header).round(2) pddf_csv.fillna(0.0, inplace=True) pddf_csv.set_index('Hash ID', inplace=True) if not os.path.exists(self.path + '/CSV'): os.makedirs(self.path + '/CSV') pddf_csv.to_csv(self.path + '/CSV/' + 'DVH_' + csv_filename) for key, dvh in patient['dvhs'].items(): dvh.rx_dose = patient['plan']['rxdose'] / 100 wx.CallAfter(progressFunc, 100, 100, 'Done') wx.CallAfter(updateFunc, patient)
def calc_dvh(rtss,rtdose,structure_id,px): """Calculate a dvh object from input files and structure id, and assign the prescription to the dvh object. All 3 files must be provided.""" dvh = dvhcalc.get_dvh(rtss,rtdose,structure_id) dvh.rx_dose = px return dvh
dp = dicomparser.DicomParser(Fichier_RS) # i.e. Get a dict of structure information structures = dp.GetStructures() New_structures = [] for j in List_ROI: #print(j) try: for i in list(structures): if j == structures[i]["name"]: indice = i calcdvh = dvhcalc.get_dvh(str(Fichier_RS), str(Fichier_RD), indice) print('\n pour le ' + str(structures[i]["name"]) + ' : ', '\n \t Dose max : ', calcdvh.max, " \n \t Dose min : ", calcdvh.min, '\n \t D1cc : ', calcdvh.D1cc, '\n \t D50 ', calcdvh.D50) except: pass # print(indice_Sein_G,indice_Coeur) #print(List) # # Calculate a DVH from DICOM RT data # calcdvh = dvhcalc.get_dvh(str(Fichier_RS), str(Fichier_RD), indice_Coeur) # print('pour le coeur' ,calcdvh.max,calcdvh.min,calcdvh.D50)