def OpenFile(self): options = QFileDialog.Options() options |= QFileDialog.DontUseNativeDialog fileName, _ = QFileDialog.getOpenFileName( self, "QFileDialog.getOpenFileName()", "", "All Files (*);;DICOM Files (*.dcm)", options=options) if fileName: leeds = PicketFence(fileName) # leeds = PicketFence(fileName, filter=1) # leeds.analyze(tolerance=0.2, action_tolerance=0.03, hdmlc=True, invert=False) leeds.analyze(orientation='Up-Down', tolerance=0.2, action_tolerance=0.1, hdmlc=True) d = picketfence.Settings(orientation='Up-Down', tolerance=0.2, action_tolerance=0.1, hdmlc=True, image=leeds.image, log_fits=None) d1 = picketfence.Settings(orientation='Left-Right', tolerance=0.2, action_tolerance=0.1, hdmlc=True, image=leeds.image, log_fits=None) m = picketfence.PicketManager(image=leeds.image, settings=d1, num_pickets=leeds.num_pickets) plt.hist(m.error_hist()[2]) #oo = picketfence.Picket(image=leeds.image, settings=d1, approximate_idx=m.num_pickets, # spacing=m.mean_spacing) oo = picketfence.Picket(image=leeds.image, settings=d1, approximate_idx=m.num_pickets, spacing=m.mean_spacing) print(len(m.error_hist()[2])) plt.plot(m.passed) print(len(oo.mlc_meas)) plt.show( ) # y = picketfence.MLCMeas(point1=geometry.Point(), point2=geometry.Point(), settings=d) rr = picketfence.MLCMeas(point1=oo.mlc_meas[1], point2=oo.mlc_meas[9], settings=d1) print(oo.mlc_meas[1]) print(rr.passed) print(oo.abs_median_error)
def OpenFile(self, textparam): options=QFileDialog.Options() options|=QFileDialog.DontUseNativeDialog fileName, _=QFileDialog.getOpenFileName(self, "QFileDialog.getOpenFileName()", "", "All Files (*);;DICOM Files (*.dcm)", options=options) if fileName: MainWindow.pathDir=fileName; leeds=PicketFence(fileName) leeds.analyze(tolerance=0.15, action_tolerance=0.03,hdmlc=True) leeds.plot_analyzed_image() leeds.publish_pdf(MainWindow.pathDir + '.pdf', metadata={"name": textparam, "unit": "TrueBeam STX"})
def run_pf(path): """Function to pass to the process pool executor to process picket fence images.""" try: mypf = PicketFence(path) mypf.analyze() if mypf.max_error > 1.2: raise Exception("Max MLC peak error > 1.2mm") return 'Success' except ValueError: try: mypf = PicketFence(path, filter=3) mypf.analyze() if mypf.max_error > 1.2: raise Exception("Max MLC peak error > 1.2mm") return 'Success' except (ValueError, ) as e: return 'Failure: {} @ {}'.format(e, path) except Exception as e: return 'Failure: {} @ {}'.format(e, path)
def run_pf(path): """Function to pass to the process pool executor to process picket fence images.""" try: mypf = PicketFence(path) mypf.analyze() if mypf.max_error > 1.2: raise Exception("Max MLC peak error > 1.2mm") return 'Success' except ValueError: try: mypf = PicketFence(path, filter=3) mypf.analyze() if mypf.max_error > 1.2: raise Exception("Max MLC peak error > 1.2mm") return 'Success' except (ValueError,) as e: return 'Failure: {} @ {}'.format(e, path) except Exception as e: return 'Failure: {} @ {}'.format(e, path)
########################################### # William A. P. dos Santos # # [email protected] # # Testanto Pylinac # # 02 de julho de 2020 # ########################################### from pylinac import PicketFence pf_img = ['G0C0Y1', 'G0C0Y2'] #pf_img = r"EPID-PF-LR.dcm" for nome in pf_img: pf = PicketFence(nome + '.dcm') pf.analyze(tolerance=0.05, action_tolerance=0.03, hdmlc=False) pf.plot_analyzed_image(leaf_error_subplot=True) print(pf.results()) pf.publish_pdf(filename=nome + '.pdf') ''' #PicketFence.run_demo() pf = PicketFence.from_demo_image() pf.analyze(tolerance=0.05, action_tolerance=0.03) # print results to the console print(pf.results()) # view analyzed image pf.plot_analyzed_image() # save PDF report #pf.publish_pdf(filename='Demo Picket Fence.pdf') #pf = PicketFence.from_demo_image() #print(type(pf))'''
from pylinac import PicketFence #importa modulo pylinac img_dcm = './Arquivos_de_teste/dcm_Varian.dcm' # local + nome do arquivo a ser analisado pf = PicketFence(img_dcm) # inicializa o modulo pf.analyze(tolerance=0.2, action_tolerance=0.1, orientation='l') pf.plot_analyzed_image() print(pf.results()) pf.publish_pdf(filename='./Arquivos_de_teste/teste_varian.pdf')
def picket_fence_helperf(args): '''This function is used in order to prevent memory problems''' temp_folder = args["temp_folder"] file_path = args["file_path"] clip_box = args["clip_box"] py_filter = args["py_filter"] num_pickets = args["num_pickets"] sag = args["sag"] mlc = args["mlc"] invert = args["invert"] orientation = args["orientation"] w = args["w"] imgdescription = args["imgdescription"] station = args["station"] displayname = args["displayname"] acquisition_datetime = args["acquisition_datetime"] general_functions.set_configuration( args["config"]) # Transfer to this process # Chose module: if mlc in ["Varian_80", "Elekta_80", "Elekta_160"]: use_original_pylinac = "False" else: use_original_pylinac = "True" # Collect data for "save results" dicomenergy = general_functions.get_energy_from_imgdescription( imgdescription) user_machine, user_energy = general_functions.get_user_machine_and_energy( station, dicomenergy) machines_and_energies = general_functions.get_machines_and_energies( general_functions.get_treatmentunits_picketfence()) tolerances = general_functions.get_tolerance_user_machine_picketfence( user_machine) # If user_machne has specific tolerance if not tolerances: action_tolerance, tolerance, generate_pdf_report = general_functions.get_settings_picketfence( ) else: action_tolerance, tolerance, generate_pdf_report = tolerances[0] tolerance = float(tolerance) action_tol = float(action_tolerance) save_results = { "user_machine": user_machine, "user_energy": user_energy, "machines_and_energies": machines_and_energies, "displayname": displayname } # Import either original pylinac module or the modified module if use_original_pylinac == "True": from pylinac import PicketFence as PicketFence # Original pylinac analysis else: if __name__ == '__main__' or parent_module.__name__ == '__main__': from python_packages.pylinac.picketfence_modified import PicketFence as PicketFence else: from .python_packages.pylinac.picketfence_modified import PicketFence as PicketFence try: pf = PicketFence(file_path, filter=py_filter) except Exception as e: return template("error_template", { "error_message": "Module PicketFence cannot calculate. " + str(e) }) # Here we force pixels to background outside of box: if clip_box != 0: try: pf.image.check_inversion_by_histogram(percentiles=[ 4, 50, 96 ]) # Check inversion otherwise this might not work general_functions.clip_around_image(pf.image, clip_box) except Exception as e: return template( "error_template", {"error_message": "Unable to apply clipbox. " + str(e)}) # Now invert if needed if invert: try: pf.image.invert() except Exception as e: return template( "error_template", {"error_message": "Unable to invert the image. " + str(e)}) # Now analyze try: if use_original_pylinac == "True": hdmlc = True if mlc == "Varian_120HD" else False pf.analyze(tolerance=tolerance, action_tolerance=action_tol, hdmlc=hdmlc, sag_adjustment=float(sag), num_pickets=num_pickets, orientation=orientation) else: pf.analyze(tolerance=tolerance, action_tolerance=action_tol, mlc_type=mlc, sag_adjustment=float(sag), num_pickets=num_pickets, orientation=orientation) except Exception as e: return template( "error_template", {"error_message": "Picket fence module cannot analyze. " + str(e)}) # Added an if clause to tell if num of mlc's are not the same on all pickets: num_mlcs = len(pf.pickets[0].mlc_meas) for p in pf.pickets: if len(p.mlc_meas) != num_mlcs: return template( "error_template", { "error_message": "Not all pickets have the same number of leaves. " + "Probably your image si too skewed. Rotate your collimator a bit " + "and try again. Use the jaws perpendicular to MLCs to set the right " + "collimator angle." }) error_array = np.array([]) max_error = [] max_error_leaf = [] passed_tol = [] picket_offsets = [] picket_nr = pf.num_pickets for k in pf.pickets.pickets: error_array = np.concatenate((error_array, k.error_array)) max_error.append(k.max_error) max_err_leaf_ind = np.argmax(k.error_array) max_error_leaf.append(max_err_leaf_ind) passed_tol.append("Passed" if k.passed else "Failed") picket_offsets.append(k.dist2cax) # Plot images if pf.settings.orientation == "Left-Right": fig_pf = Figure(figsize=(9, 10), tight_layout={"w_pad": 0}) else: fig_pf = Figure(figsize=(9.5, 7), tight_layout={"w_pad": 0}) img_ax = fig_pf.add_subplot(1, 1, 1) img_ax.imshow(pf.image.array, cmap=matplotlib.cm.gray, interpolation="none", aspect="equal", origin='upper') # Taken from pylinac: leaf_error_subplot: tol_line_height = [pf.settings.tolerance, pf.settings.tolerance] tol_line_width = [0, max(pf.image.shape)] # make the new axis divider = make_axes_locatable(img_ax) if pf.settings.orientation == 'Up-Down': axtop = divider.append_axes('right', 1.75, pad=0.2, sharey=img_ax) else: axtop = divider.append_axes('bottom', 1.75, pad=0.5, sharex=img_ax) # get leaf positions, errors, standard deviation, and leaf numbers pos, vals, err, leaf_nums = pf.pickets.error_hist() # Changed leaf_nums to sequential numbers: leaf_nums = list(np.arange(0, len(leaf_nums), 1)) # plot the leaf errors as a bar plot if pf.settings.orientation == 'Up-Down': axtop.barh(pos, vals, xerr=err, height=pf.pickets[0].sample_width * 2, alpha=0.4, align='center') # plot the tolerance line(s) axtop.plot(tol_line_height, tol_line_width, 'r-', linewidth=3) if pf.settings.action_tolerance is not None: tol_line_height_action = [ pf.settings.action_tolerance, pf.settings.action_tolerance ] tol_line_width_action = [0, max(pf.image.shape)] axtop.plot(tol_line_height_action, tol_line_width_action, 'y-', linewidth=3) # reset xlims to comfortably include the max error or tolerance value axtop.set_xlim([0, max(max(vals), pf.settings.tolerance) + 0.1]) else: axtop.bar(pos, vals, yerr=err, width=pf.pickets[0].sample_width * 2, alpha=0.4, align='center') axtop.plot(tol_line_width, tol_line_height, 'r-', linewidth=3) if pf.settings.action_tolerance is not None: tol_line_height_action = [ pf.settings.action_tolerance, pf.settings.action_tolerance ] tol_line_width_action = [0, max(pf.image.shape)] axtop.plot(tol_line_width_action, tol_line_height_action, 'y-', linewidth=3) axtop.set_ylim([0, max(max(vals), pf.settings.tolerance) + 0.1]) # add formatting to axis axtop.grid(True) axtop.set_title("Average Error (mm)") # add tooltips if interactive # Copied this from previous version of pylinac interactive = True if interactive: if pf.settings.orientation == 'Up-Down': labels = [[ 'Leaf pair: {0} <br> Avg Error: {1:3.3f} mm <br> Stdev: {2:3.3f} mm' .format(leaf_num, err, std) ] for leaf_num, err, std in zip(leaf_nums, vals, err)] voffset = 0 hoffset = 20 else: labels = [[ 'Leaf pair: {0}, Avg Error: {1:3.3f} mm, Stdev: {2:3.3f} mm'. format(leaf_num, err, std) ] for leaf_num, err, std in zip(leaf_nums, vals, err)] if pf.settings.orientation == 'Up-Down': for num, patch in enumerate(axtop.axes.patches): ttip = mpld3.plugins.PointHTMLTooltip(patch, labels[num], voffset=voffset, hoffset=hoffset) mpld3.plugins.connect(fig_pf, ttip) mpld3.plugins.connect(fig_pf, mpld3.plugins.MousePosition(fontsize=14)) else: for num, patch in enumerate(axtop.axes.patches): ttip = mpld3.plugins.PointLabelTooltip(patch, labels[num], location='top left') mpld3.plugins.connect(fig_pf, ttip) mpld3.plugins.connect(fig_pf, mpld3.plugins.MousePosition(fontsize=14)) for p_num, picket in enumerate(pf.pickets): picket.add_guards_to_axes(img_ax.axes) for idx, mlc_meas in enumerate(picket.mlc_meas): mlc_meas.plot2axes(img_ax.axes, width=1.5) # plot CAX img_ax.plot(pf.settings.image_center.x, pf.settings.image_center.y, 'r+', ms=12, markeredgewidth=3) # tighten up the plot view img_ax.set_xlim([0, pf.image.shape[1]]) img_ax.set_ylim([pf.image.shape[0], 0]) img_ax.axis('off') img_ax.set_xticks([]) img_ax.set_yticks([]) # Histogram of all errors and average profile plot upper_bound = pf.settings.tolerance upper_outliers = np.sum(error_array.flatten() >= upper_bound) fig_pf2 = Figure(figsize=(10, 4), tight_layout={"w_pad": 2}) ax2 = fig_pf2.add_subplot(1, 2, 1) ax3 = fig_pf2.add_subplot(1, 2, 2) n, bins = np.histogram(error_array.flatten(), density=False, bins=10, range=(0, upper_bound)) ax2.bar(bins[0:-1], n, width=np.diff(bins)[0], facecolor='green', alpha=0.75) ax2.bar([upper_bound, upper_bound * 1.1], upper_outliers, width=0.1 * upper_bound, facecolor='red', alpha=0.75) ax2.plot([pf.settings.action_tolerance, pf.settings.action_tolerance], [0, max(n) / 2], color="orange") ax2.annotate("Action Tol.", (pf.settings.action_tolerance, 1.05 * max(n) / 2), color='black', fontsize=6, ha='center', va='bottom') ax2.plot([pf.settings.tolerance, pf.settings.tolerance], [0, max(n) / 2], color="darkred") ax2.annotate("Tol.", (pf.settings.tolerance, 1.05 * max(n) / 2), color='black', fontsize=6, ha='center', va='bottom') # Plot mean inplane profile and calculate FWHM: mlc_mean_profile = pf.pickets.image_mlc_inplane_mean_profile ax3.plot(mlc_mean_profile.values, "b-") picket_fwhm = [] fwhm_mean = 0 try: peaks = mlc_mean_profile.find_peaks(max_number=picket_nr, min_distance=0.02, threshold=0.5) peaks = np.sort(peaks) ax3.plot(peaks, mlc_mean_profile[peaks], "ro") separation = int(np.mean(np.diff(peaks)) / 3) mmpd = 1 / pf.image.dpmm # Get valleys valleys = [] for p in np.arange(0, len(peaks) - 1, 1): prof_partial = mlc_mean_profile[peaks[p]:peaks[p + 1]] valleys.append(peaks[p] + np.argmin(prof_partial)) edge_points = [peaks[0] - separation ] + valleys + [peaks[-1] + separation] ax3.plot(edge_points, mlc_mean_profile[edge_points], "yo") for k in np.arange(0, len(edge_points) - 1, 1): pr = PylinacSingleProfile( mlc_mean_profile[edge_points[k]:edge_points[k + 1]]) left = pr[0] right = pr[-1] amplitude = mlc_mean_profile[peaks[k]] if left < right: x = 100 * ((amplitude - left) * 0.5 + left - right) / (amplitude - right) a = pr._penumbra_point(x=50, side="left", interpolate=True) b = pr._penumbra_point(x=x, side="right", interpolate=True) else: x = 100 * ((amplitude - right) * 0.5 + right - left) / (amplitude - left) a = pr._penumbra_point(x=x, side="left", interpolate=True) b = pr._penumbra_point(x=50, side="right", interpolate=True) left_point = edge_points[k] + a right_point = edge_points[k] + b ax3.plot([left_point, right_point], [ np.interp(left_point, np.arange(0, len(mlc_mean_profile.values), 1), mlc_mean_profile.values), np.interp(right_point, np.arange(0, len(mlc_mean_profile.values), 1), mlc_mean_profile.values) ], "-k", alpha=0.5) picket_fwhm.append(np.abs(a - b) * mmpd) fwhm_mean = np.mean(picket_fwhm) except: picket_fwhm = [np.nan] * picket_nr fwhm_mean = np.nan if len(picket_fwhm) != picket_nr: fwhm_mean = np.mean(picket_fwhm) picket_fwhm = [np.nan] * picket_nr ax2.set_xlim([-0.025, pf.settings.tolerance * 1.15]) ax3.set_xlim([0, pf.image.shape[1]]) ax2.set_title("Leaf error") ax3.set_title("MLC mean profile") ax2.set_xlabel("Error [mm]") ax2.set_ylabel("Counts") ax3.set_xlabel("Pixel") ax3.set_ylabel("Grey value") passed = "Passed" if pf.passed else "Failed" script = mpld3.fig_to_html(fig_pf, d3_url=D3_URL, mpld3_url=MPLD3_URL) script2 = mpld3.fig_to_html(fig_pf2, d3_url=D3_URL, mpld3_url=MPLD3_URL) variables = { "script": script, "script2": script2, "passed": passed, "max_error": max_error, "max_error_leaf": max_error_leaf, "passed_tol": passed_tol, "picket_nr": picket_nr, "tolerance": pf.settings.tolerance, "perc_passing": pf.percent_passing, "max_error_all": pf.max_error, "max_error_picket_all": pf.max_error_picket, "max_error_leaf_all": pf.max_error_leaf, "median_error": pf.abs_median_error, "spacing": pf.pickets.mean_spacing, "picket_offsets": picket_offsets, "fwhm_mean": fwhm_mean, "picket_fwhm": picket_fwhm, "pdf_report_enable": generate_pdf_report, "save_results": save_results, "acquisition_datetime": acquisition_datetime } # Generate pylinac report: if generate_pdf_report == "True": pdf_file = tempfile.NamedTemporaryFile(delete=False, prefix="PicketFence_", suffix=".pdf", dir=config.PDF_REPORT_FOLDER) metadata = RestToolbox.GetInstances(config.ORTHANC_URL, [w]) try: patient = metadata[0]["PatientName"] except: patient = "" try: stationname = metadata[0]["StationName"] except: stationname = "" try: date_time = RestToolbox.get_datetime(metadata[0]) date_var = datetime.datetime.strptime( date_time[0], "%Y%m%d").strftime("%d/%m/%Y") except: date_var = "" pf.publish_pdf(pdf_file, notes=[ "Date = " + date_var, "Patient = " + patient, "Station = " + stationname ]) variables["pdf_report_filename"] = os.path.basename(pdf_file.name) #gc.collect() general_functions.delete_files_in_subfolders([temp_folder]) # Delete image return template("picket_fence_results", variables)