Beispiel #1
0
def test_parse_dicom_folder(dicom_folder):
    # given
    p_kernel = PyPlanScoringKernel()

    # when having a folder containing RS/RD/RP dicom
    # then
    p_kernel.parse_dicom_folder(dicom_folder)
    assert p_kernel.dcm_files
Beispiel #2
0
def test_parse_empty_dicom_folder(tmpdir):
    # given
    p_kernel = PyPlanScoringKernel()

    # when having a folder containing RS/RD/RP dicom
    # then
    with pytest.raises(FileNotFoundError):
        p_kernel.parse_dicom_folder(tmpdir)
Beispiel #3
0
def test_setup_case(dicom_folder):
    # given case files
    rs_dvh = os.path.join(dicom_folder, 'RS.dcm')
    file_path = os.path.join(dicom_folder, 'Scoring_criteria_2018.xlsx')
    case_name = 'BiLateralLungSBRTCase'

    # when instantiate
    p_kernel = PyPlanScoringKernel()
    p_kernel.setup_case(rs_dvh, file_path, case_name)
    assert p_kernel.case is not None
Beispiel #4
0
    def __init__(self, parent=None):
        super(MainDialog, self).__init__(parent=parent)
        self.setupUi(self)
        self.folder_root = None
        self.result = None
        # calculation kernel
        self.calc_kernel = PyPlanScoringKernel()
        # Redirect STD out to
        stdout = OutputWrapper(self, True)
        stdout.outputWritten.connect(self.handle_output)
        stderr = OutputWrapper(self, False)
        stderr.outputWritten.connect(self.handle_output)

        self.worker = Worker()
        # connect Signal and Slots
        self.set_conections()
        self.save_reports_button.setEnabled(False)
        self.textBrowser.setOpenExternalLinks(True)
Beispiel #5
0
def test_setup_dvh_calculation(dicom_folder, ini_file_path):
    # given case files
    rs_dvh = os.path.join(dicom_folder, 'RS.dcm')
    file_path = os.path.join(dicom_folder, 'Scoring_criteria_2018.xlsx')
    case_name = 'BiLateralLungSBRTCase'

    # when instantiate
    p_kernel = PyPlanScoringKernel()

    # if not setup case before setup dvh calculator
    p_kernel.setup_dvh_calculation(ini_file_path)

    # then
    assert p_kernel.dvh_calculator is None

    # when setup case
    p_kernel = PyPlanScoringKernel()
    p_kernel.setup_case(rs_dvh, file_path, case_name)

    p_kernel.setup_dvh_calculation(ini_file_path)

    # then
    assert p_kernel.dvh_calculator is not None
Beispiel #6
0
def test_calculate_dvh(dicom_folder, ini_file_path):
    # given case files
    rs_dvh = os.path.join(dicom_folder, 'RS.dcm')
    file_path = os.path.join(dicom_folder, 'Scoring_criteria_2018.xlsx')
    case_name = 'BiLateralLungSBRTCase'

    # when instantiate
    p_kernel = PyPlanScoringKernel()
    p_kernel.parse_dicom_folder(dicom_folder)
    p_kernel.setup_case(rs_dvh, file_path, case_name)
    p_kernel.setup_dvh_calculation(ini_file_path)
    p_kernel.setup_planing_item()

    p_kernel.calculate_dvh()

    assert p_kernel.dvh_data
Beispiel #7
0
def test_calc_plan_complexity(test_case, dicom_folder, ini_file_path):
    # given case files
    rs_dvh = os.path.join(dicom_folder, 'RS.dcm')
    file_path = os.path.join(dicom_folder, 'Scoring_criteria_2018.xlsx')
    case_name = 'BiLateralLungSBRTCase'

    # when instantiate
    p_kernel = PyPlanScoringKernel()
    p_kernel.parse_dicom_folder(dicom_folder)
    p_kernel.setup_case(rs_dvh, file_path, case_name)
    p_kernel.setup_dvh_calculation(ini_file_path)
    p_kernel.setup_planing_item()

    # calculate plan complexity
    p_kernel.calc_plan_complexity()
    test_case.assertAlmostEqual(p_kernel.plan_complexity,
                                0.166503597706,
                                places=3)
Beispiel #8
0
def test_calc_plan_score(dicom_folder, ini_file_path):
    # given case files
    rs_dvh = os.path.join(dicom_folder, 'RS.dcm')
    file_path = os.path.join(dicom_folder, 'Scoring_criteria_2018.xlsx')
    case_name = 'BiLateralLungSBRTCase'

    # when instantiate
    p_kernel = PyPlanScoringKernel()
    p_kernel.parse_dicom_folder(dicom_folder)
    p_kernel.setup_case(rs_dvh, file_path, case_name)
    p_kernel.setup_dvh_calculation(ini_file_path)
    p_kernel.setup_planing_item()

    p_kernel.calculate_dvh()
    p_kernel.calc_plan_score()

    assert not p_kernel._report_data_frame.empty
    assert round(p_kernel._total_score) == round(90.01)

    # save report data
    p_kernel.save_report_data()
Beispiel #9
0
class MainDialog(QtGui.QMainWindow, PyPlanScoringBrainPTV.Ui_MainWindow):
    def __init__(self, parent=None):
        super(MainDialog, self).__init__(parent=parent)
        self.setupUi(self)
        self.folder_root = None
        self.result = None
        # calculation kernel
        self.calc_kernel = PyPlanScoringKernel()
        # Redirect STD out to
        stdout = OutputWrapper(self, True)
        stdout.outputWritten.connect(self.handle_output)
        stderr = OutputWrapper(self, False)
        stderr.outputWritten.connect(self.handle_output)

        self.worker = Worker()
        # connect Signal and Slots
        self.set_conections()
        self.save_reports_button.setEnabled(False)
        self.textBrowser.setOpenExternalLinks(True)

    def handle_output(self, text, stdout):
        # self.listWidget.addItem(text)
        self.textBrowser.insertPlainText(str(text))

    def set_conections(self):
        self.action_developer.triggered.connect(self.about)
        self.import_button.clicked.connect(self.on_import)
        self.save_reports_button.clicked.connect(self.on_save)
        self.worker.worker_finished.connect(self.worker_done)

    def worker_done(self, obj):
        self.calc_kernel = obj
        self.calc_kernel.save_dvh_data(self.name)
        self.calc_kernel.save_report_data(self.name)

        total_score = self.calc_kernel.total_score
        self.textBrowser.insertPlainText('Total score: %1.2f \n' % total_score)
        self.textBrowser.insertPlainText('---------- Metrics -------------\n')

        # Pretty print
        print(self.calc_kernel.report)
        print()
        if self.complexity_check_box.isChecked():
            self.textBrowser.insertPlainText(
                '---------- Complexity metric -------------\n')
            # try:
            self.calc_kernel.calc_plan_complexity()
            self.calc_kernel.save_complexity_figure_per_beam()
            self.textBrowser.insertPlainText(
                "Aperture complexity: %1.3f [mm-1]:\n" %
                self.calc_kernel.plan_complexity)

            txt = 'It is a Python 3.x port of the Eclipse ESAPI plug-in script.\n' \
                  'As such, it aims to contain the complete functionality of  the aperture complexity analysis\n'
            self.textBrowser.insertPlainText(txt)
            self.textBrowser.insertPlainText("Reference: ")
            self.textBrowser.insertHtml(
                "<a href=\"https://github.com/umro/Complexity\" > https://github.com/umro/Complexity</a>"
            )
            self.textBrowser.insertPlainText('\n')
            # except:
            #     print("Aperture complexity is valid only in linac-based dynamic treatments - (IMRT/VMAT)")

    def setup_case(self, file_path, case_name, ini_file_path, rs_dvh=''):
        if not rs_dvh:
            rs_dvh = self.calc_kernel.dcm_files[
                'rtss']  # TODO add folder DVH file
        self.calc_kernel.setup_case(rs_dvh, file_path, case_name)
        self.calc_kernel.setup_dvh_calculation(ini_file_path)
        self.calc_kernel.setup_planing_item()

    def on_import(self):

        self.textBrowser.clear()
        self.name = self.lineEdit.text()
        if self.name:
            self.folder_root = QtGui.QFileDialog.getExistingDirectory(
                self,
                "Select the directory containing only: RP and RD Dicom RT dose files from one plan",
                QtCore.QDir.currentPath())

            if self.folder_root:
                dcm_files, flag = self.calc_kernel.parse_dicom_folder(
                    self.folder_root)
                if flag:
                    # setup case using global variables
                    self.setup_case(criteria_file, case_name, ini_file_path,
                                    rs_dvh)
                    self.worker.set_calc_kernel(self.calc_kernel)

                    self.textBrowser.insertPlainText(
                        'Loaded - DICOM-RT Files: \n')
                    txt = [os.path.split(v)[1] for k, v in dcm_files.items()]
                    for t in txt:
                        self.textBrowser.insertPlainText(str(t) + '\n')
                    self.save_reports_button.setEnabled(True)
                else:
                    msg = "<p>missing Dicom Files: " + str(dcm_files)
                    QtGui.QMessageBox.critical(self, "Missing Data", msg,
                                               QtGui.QMessageBox.Abort)
        else:
            msg = "Please set the output file name"
            QtGui.QMessageBox.critical(self, "Missing Data", msg,
                                       QtGui.QMessageBox.Abort)

    def on_save(self):
        self.textBrowser.insertPlainText(
            '------------- Calculating DVH and score --------------\n')
        self.worker.start()

    def about(self):
        txt = "PyPlanScoring - 2018 - RT Plan Competition: %s \n" \
              "Be the strongest link in the radiotherapy chain\n" \
              "https://radiationknowledge.org \n" \
              "Author: %s\n" \
              "Copyright (C) 2017 - 2018 Victor Gabriel Leandro Alves, All rights reserved\n" \
              "Platform details: Python %s on %s\n" \
              "This program aims to calculate_integrate an approximate score.\n" \
              "your final score may be different due to structure boundaries and dose interpolation uncertainties\n" \
              "%s" \
              % (__version__, __author__, platform.python_version(), platform.system(), __license__)

        QtGui.QMessageBox.about(self, 'Information', txt)