示例#1
0
class CIP_ParenchymaAnalysisWidget(ScriptedLoadableModuleWidget):
    @property
    def moduleName(self):
        return os.path.basename(__file__).replace(".py", "")

    def __init__(self, parent=None):
        ScriptedLoadableModuleWidget.__init__(self, parent)

        self.chartOptions = ("LAA%-950", "LAA%-925", "LAA%-910", "LAA%-856",
                             "HAA%-700", "HAA%-600", "HAA%-500", "HAA%-250",
                             "Perc10", "Perc15", "Mean", "Std", "Kurtosis",
                             "Skewness", "Ventilation Heterogeneity", "Mass",
                             "Volume")

        # Build the column keys. Here all the columns are declared, but an alternative could be just:
        #self.columnsDict = CaseReportsWidget.getColumnKeysNormalizedDictionary(["Volume Name", "Region", "LAA%-950", ...])
        self.columnsDict = OrderedDict()
        self.columnsDict["VolumeName"] = "Volume Name"
        self.columnsDict["Region"] = "Region"
        self.columnsDict["LAA950"] = "LAA%-950"
        self.columnsDict["LAA925"] = "LAA%-925"
        self.columnsDict["LAA910"] = "LAA%-910"
        self.columnsDict["LAA856"] = "LAA%-856"
        self.columnsDict["HAA700"] = "HAA%-700"
        self.columnsDict["HAA600"] = "HAA%-600"
        self.columnsDict["HAA500"] = "HAA%-500"
        self.columnsDict["HAA250"] = "HAA%-250"
        self.columnsDict["Perc10"] = "Perc10"
        self.columnsDict["Perc15"] = "Perc15"
        self.columnsDict["Mean"] = "Mean"
        self.columnsDict["Std"] = "Std"
        self.columnsDict["Kurtosis"] = "Kurtosis"
        self.columnsDict["Skewness"] = "Skewness"
        self.columnsDict[
            "VentilationHeterogeneity"] = "Ventilation Heterogeneity"
        self.columnsDict["Mass"] = "Mass"
        self.columnsDict["Volume"] = "Volume"

        self.rTags = ("WholeLung", "RightLung", "LeftLung", "RUL", "RLL",
                      "RML", "LUL", "LLL", "LUT", "LMT", "LLT", "RUT", "RMT",
                      "RLT")
        if not parent:
            self.parent = slicer.qMRMLWidget()
            self.parent.setLayout(qt.QVBoxLayout())
            self.parent.setMRMLScene(slicer.mrmlScene)
        else:
            self.parent = parent
        self.logic = None
        self.CTNode = None
        self.labelNode = None
        # self.expNode = None
        # self.explabelNode = None
        self.fileName = None
        self.fileDialog = None

        if not parent:
            self.setup()
            self.CTSelector.setMRMLScene(slicer.mrmlScene)
            # self.expSelector.setMRMLScene(slicer.mrmlScene)
            self.labelSelector.setMRMLScene(slicer.mrmlScene)
            # self.explabelSelector.setMRMLScene(slicer.mrmlScene)
            self.parent.show()

    def enter(self):
        if self.labelSelector.currentNode():
            for color in ['Red', 'Yellow', 'Green']:
                slicer.app.layoutManager().sliceWidget(color).sliceLogic(
                ).GetSliceCompositeNode().SetLabelVolumeID(
                    self.labelSelector.currentNode().GetID())

    def exit(self):
        for color in ['Red', 'Yellow', 'Green']:
            slicer.app.layoutManager().sliceWidget(color).sliceLogic(
            ).GetSliceCompositeNode().SetLabelVolumeID('None')

    def setup(self):
        ScriptedLoadableModuleWidget.setup(self)

        #
        # the inps volume selector
        #
        parametersCollapsibleButton = ctk.ctkCollapsibleButton()
        parametersCollapsibleButton.text = "IO Volumes"
        self.parent.layout().addWidget(parametersCollapsibleButton)

        # Layout within the dummy collapsible button
        parametersFormLayout = qt.QFormLayout(parametersCollapsibleButton)
        parametersFormLayout.setVerticalSpacing(5)

        self.CTSelector = slicer.qMRMLNodeComboBox()
        self.CTSelector.nodeTypes = (("vtkMRMLScalarVolumeNode"), "")
        self.CTSelector.addAttribute("vtkMRMLScalarVolumeNode", "LabelMap", 0)
        self.CTSelector.selectNodeUponCreation = False
        self.CTSelector.addEnabled = False
        self.CTSelector.removeEnabled = False
        self.CTSelector.noneEnabled = True
        self.CTSelector.showHidden = False
        self.CTSelector.showChildNodeTypes = False
        self.CTSelector.setMRMLScene(slicer.mrmlScene)
        self.CTSelector.setToolTip("Pick the CT image to work on.")
        parametersFormLayout.addRow("Input CT Volume: ", self.CTSelector)

        #
        # the label map volume selector
        #
        self.labelSelector = slicer.qMRMLNodeComboBox()
        # self.labelSelector.nodeTypes = ( ("vtkMRMLScalarVolumeNode"), "" )
        # self.labelSelector.addAttribute( "vtkMRMLScalarVolumeNode", "LabelMap", 1 )
        self.labelSelector.nodeTypes = (("vtkMRMLLabelMapVolumeNode"), "")
        self.labelSelector.selectNodeUponCreation = True
        self.labelSelector.addEnabled = False
        self.labelSelector.removeEnabled = False
        self.labelSelector.noneEnabled = True
        self.labelSelector.showHidden = False
        self.labelSelector.showChildNodeTypes = False
        self.labelSelector.setMRMLScene(slicer.mrmlScene)
        self.labelSelector.setToolTip("Pick the label map to the algorithm.")
        parametersFormLayout.addRow("Label Map Volume: ", self.labelSelector)

        # Image filtering section
        self.preProcessingWidget = PreProcessingWidget(
            self.moduleName, parentWidget=self.parent)
        self.preProcessingWidget.setup()
        #
        # self.splitRadioButton = qt.QRadioButton()
        # self.splitRadioButton.setText('Split Label Map')
        # self.splitRadioButton.setChecked(0)
        # self.parent.layout().addWidget(self.splitRadioButton, 0, 3)

        # Apply button
        self.applyButton = qt.QPushButton("Apply")
        self.applyButton.toolTip = "Calculate Parenchyma Phenotypes."
        self.applyButton.enabled = False
        self.applyButton.setFixedSize(300, 30)
        self.parent.layout().addWidget(self.applyButton, 0, 4)

        # model and view for stats table
        self.view = qt.QTableView()
        self.view.sortingEnabled = True
        self.parent.layout().addWidget(self.view)

        # model and view for EXP stats table
        """self.viewexp = qt.QTableView()
        self.viewexp.sortingEnabled = True
        self.parent.layout().addWidget(self.viewexp)"""

        # Histogram Selection
        self.HistSection = qt.QFrame()
        self.HistSection.setLayout(qt.QVBoxLayout())
        self.parent.layout().addWidget(self.HistSection)
        self.HistSection.setObjectName('HistSection')
        self.HistSection.setStyleSheet(
            '#HistSection {border: 0.5px solid lightGray; }')
        HistSectionTitle = qt.QLabel()
        HistSectionTitle.setText('Histogram Section')
        # HistSectionTitle.setStyleSheet('border: 1px solid white; color: black')
        self.HistSection.layout().addWidget(HistSectionTitle)

        self.histogramCheckBoxes = []
        self.histFrame = qt.QFrame()
        # self.histFrame.setStyleSheet('border: 1px solid white')
        self.histFrame.setLayout(qt.QHBoxLayout())

        self.GlobalHistCheckBox = qt.QCheckBox()
        self.histogramCheckBoxes.append(self.GlobalHistCheckBox)
        self.histFrame.layout().addWidget(self.GlobalHistCheckBox)

        self.RightHistCheckBox = qt.QCheckBox()
        self.histogramCheckBoxes.append(self.RightHistCheckBox)
        self.histFrame.layout().addWidget(self.RightHistCheckBox)

        self.LeftHistCheckBox = qt.QCheckBox()
        self.histogramCheckBoxes.append(self.LeftHistCheckBox)
        self.histFrame.layout().addWidget(self.LeftHistCheckBox)

        self.RULHistCheckBox = qt.QCheckBox()
        self.histogramCheckBoxes.append(self.RULHistCheckBox)
        self.histFrame.layout().addWidget(self.RULHistCheckBox)

        self.RLLHistCheckBox = qt.QCheckBox()
        self.histogramCheckBoxes.append(self.RLLHistCheckBox)
        self.histFrame.layout().addWidget(self.RLLHistCheckBox)

        self.RMLHistCheckBox = qt.QCheckBox()
        self.histogramCheckBoxes.append(self.RMLHistCheckBox)
        self.histFrame.layout().addWidget(self.RMLHistCheckBox)

        self.LULHistCheckBox = qt.QCheckBox()
        self.histogramCheckBoxes.append(self.LULHistCheckBox)
        self.histFrame.layout().addWidget(self.LULHistCheckBox)

        self.LLLHistCheckBox = qt.QCheckBox()
        self.histogramCheckBoxes.append(self.LLLHistCheckBox)
        self.histFrame.layout().addWidget(self.LLLHistCheckBox)

        self.LUTHistCheckBox = qt.QCheckBox()
        self.histogramCheckBoxes.append(self.LUTHistCheckBox)
        self.histFrame.layout().addWidget(self.LUTHistCheckBox)

        self.LMTHistCheckBox = qt.QCheckBox()
        self.histogramCheckBoxes.append(self.LMTHistCheckBox)
        self.histFrame.layout().addWidget(self.LMTHistCheckBox)

        self.LLTHistCheckBox = qt.QCheckBox()
        self.histogramCheckBoxes.append(self.LLTHistCheckBox)
        self.histFrame.layout().addWidget(self.LLTHistCheckBox)

        self.RUTHistCheckBox = qt.QCheckBox()
        self.histogramCheckBoxes.append(self.RUTHistCheckBox)
        self.histFrame.layout().addWidget(self.RUTHistCheckBox)

        self.RMTHistCheckBox = qt.QCheckBox()
        self.histogramCheckBoxes.append(self.RMTHistCheckBox)
        self.histFrame.layout().addWidget(self.RMTHistCheckBox)

        self.RLTHistCheckBox = qt.QCheckBox()
        self.histogramCheckBoxes.append(self.RLTHistCheckBox)
        self.histFrame.layout().addWidget(self.RLTHistCheckBox)

        for i in xrange(len(self.histogramCheckBoxes)):
            self.histogramCheckBoxes[i].setText(self.rTags[i])
            self.histogramCheckBoxes[i].hide()

        self.HistSection.layout().addWidget(self.histFrame)
        self.HistSection.enabled = False

        # Chart button
        self.chartBox = qt.QFrame()
        self.chartBox.setObjectName("chartBox")
        self.chartBox.setStyleSheet(
            '#chartBox {border: 0.5px solid lightGray;}')
        self.chartBox.setLayout(qt.QVBoxLayout())
        self.parent.layout().addWidget(self.chartBox)
        chartSectionTitle = qt.QLabel()
        chartSectionTitle.setText('Chart Section')
        self.chartBox.layout().addWidget(chartSectionTitle)
        chartFrame = qt.QFrame()
        chartFrame.setLayout(qt.QHBoxLayout())
        self.chartBox.layout().addWidget(chartFrame)
        self.chartButton = qt.QPushButton("Chart")
        self.chartButton.toolTip = "Make a chart from the current statistics."
        chartFrame.layout().addWidget(self.chartButton)
        self.chartOption = qt.QComboBox()
        self.chartOption.addItems(self.chartOptions)
        chartFrame.layout().addWidget(self.chartOption)
        self.chartBox.enabled = False

        self.reportsWidget = CaseReportsWidget(self.moduleName,
                                               self.columnsDict,
                                               parentWidget=self.parent)
        self.reportsWidget.setup()
        self.reportsWidget.showPrintButton(True)
        # self.reportsWidget.saveButton.enabled = False
        # self.reportsWidget.openButton.enabled = False
        # self.reportsWidget.exportButton.enabled = False
        # self.reportsWidget.removeButton.enabled = False
        # By default, the Print button is hidden
        # self.reportsWidget.showPrintButton.enabled = False

        # Add vertical spacer
        self.parent.layout().addStretch(1)

        # connections
        self.applyButton.connect('clicked()', self.onApply)

        self.chartButton.connect('clicked()', self.onChart)

        self.reportsWidget.addObservable(
            self.reportsWidget.EVENT_SAVE_BUTTON_CLICKED, self.onSaveReport)
        self.reportsWidget.addObservable(
            self.reportsWidget.EVENT_PRINT_BUTTON_CLICKED, self.onPrintReport)
        self.CTSelector.connect('currentNodeChanged(vtkMRMLNode*)',
                                self.onCTSelect)
        self.labelSelector.connect('currentNodeChanged(vtkMRMLNode*)',
                                   self.onLabelSelect)

        self.GlobalHistCheckBox.connect('clicked()', self.onHistogram)
        self.RightHistCheckBox.connect('clicked()', self.onHistogram)
        self.LeftHistCheckBox.connect('clicked()', self.onHistogram)
        self.RULHistCheckBox.connect('clicked()', self.onHistogram)
        self.RLLHistCheckBox.connect('clicked()', self.onHistogram)
        self.RMLHistCheckBox.connect('clicked()', self.onHistogram)
        self.LULHistCheckBox.connect('clicked()', self.onHistogram)
        self.LLLHistCheckBox.connect('clicked()', self.onHistogram)
        self.LUTHistCheckBox.connect('clicked()', self.onHistogram)
        self.LMTHistCheckBox.connect('clicked()', self.onHistogram)
        self.LLTHistCheckBox.connect('clicked()', self.onHistogram)
        self.RUTHistCheckBox.connect('clicked()', self.onHistogram)
        self.RMTHistCheckBox.connect('clicked()', self.onHistogram)
        self.RLTHistCheckBox.connect('clicked()', self.onHistogram)

    def cleanup(self):
        self.reportsWidget.cleanup()
        self.reportsWidget = None

    def onCTSelect(self, node):
        self.CTNode = node
        self.applyButton.enabled = bool(
            self.CTNode)  # and bool(self.labelNode)
        self.preProcessingWidget.enableFilteringFrame(bool(self.CTNode))
        self.preProcessingWidget.enableLMFrame(bool(not self.labelNode))
        if self.CTNode:
            for color in ['Red', 'Yellow', 'Green']:
                slicer.app.layoutManager().sliceWidget(color).sliceLogic(
                ).GetSliceCompositeNode().SetBackgroundVolumeID(
                    self.CTNode.GetID())
        else:
            for color in ['Red', 'Yellow', 'Green']:
                slicer.app.layoutManager().sliceWidget(color).sliceLogic(
                ).GetSliceCompositeNode().SetBackgroundVolumeID('None')

    def onLabelSelect(self, node):
        self.labelNode = node
        self.applyButton.enabled = bool(
            self.CTNode)  # and bool(self.labelNode)
        self.preProcessingWidget.enableFilteringFrame(bool(self.CTNode))
        self.preProcessingWidget.enableLMFrame(bool(not self.labelNode))
        SlicerUtil.changeLabelmapOpacity(0.5)
        if self.labelNode:
            self.preProcessingWidget.filterApplication.setChecked(1)
            self.preProcessingWidget.filterApplication.setEnabled(0)
            for color in ['Red', 'Yellow', 'Green']:
                slicer.app.layoutManager().sliceWidget(color).sliceLogic(
                ).GetSliceCompositeNode().SetLabelVolumeID(
                    self.labelNode.GetID())
        else:
            self.preProcessingWidget.filterApplication.setChecked(0)
            self.preProcessingWidget.filterApplication.setEnabled(1)
            for color in ['Red', 'Yellow', 'Green']:
                slicer.app.layoutManager().sliceWidget(color).sliceLogic(
                ).GetSliceCompositeNode().SetLabelVolumeID('None')

    def inputVolumesAreValid(self):
        """Verify that volumes are compatible with label calculation
        algorithm assumptions"""
        if not self.CTNode:  # or not self.labelNode:
            qt.QMessageBox.warning(slicer.util.mainWindow(),
                                   "Parenchyma Analysis",
                                   "Please select a CT Input Volume.")
            return False
        if not self.CTNode.GetImageData(
        ):  # or not self.labelNode.GetImageData():
            qt.QMessageBox.warning(slicer.util.mainWindow(),
                                   "Parenchyma Analysis",
                                   "Please select a CT Input Volume.")
            return False
        if not self.labelNode or not self.labelNode.GetImageData():
            warning = self.preProcessingWidget.warningMessageForLM()
            if warning == 16384:
                self.createLungLabelMap()
            else:
                qt.QMessageBox.warning(slicer.util.mainWindow(),
                                       "Parenchyma Analysis",
                                       "Please select a Lung Label Map.")
                return False
            return True
        if self.CTNode.GetImageData().GetDimensions(
        ) != self.labelNode.GetImageData().GetDimensions():
            qt.QMessageBox.warning(
                slicer.util.mainWindow(), "Parenchyma Analysis",
                "Input Volumes do not have the same geometry.")
            return False


#        if self.preProcessingWidget.filterOnRadioButton.checked:
#            self.preProcessingWidget.filterApplication.setChecked(1)
        return True

    def filterInputCT(self):
        self.applyButton.enabled = False
        self.applyButton.text = "Filtering..."
        # TODO: why doesn't processEvents alone make the label text change?
        self.applyButton.repaint()
        slicer.app.processEvents()

        self.preProcessingWidget.filterInputCT(self.CTNode)

    def createLungLabelMap(self):
        """Create the lung label map
        """
        self.applyButton.enabled = False
        if self.preProcessingWidget.filterOnRadioButton.checked:  # and not self.preProcessingWidget.filterApplication.checked:
            self.filterInputCT()

        inputNode = self.CTNode

        self.applyButton.text = "Creating Label Map..."
        # TODO: why doesn't processEvents alone make the label text change?
        self.applyButton.repaint()
        slicer.app.processEvents()

        self.labelNode = slicer.mrmlScene.AddNode(
            slicer.vtkMRMLLabelMapVolumeNode())
        name = inputNode.GetName() + '_partialLungLabelMap'
        self.labelNode.SetName(slicer.mrmlScene.GenerateUniqueName(name))

        self.preProcessingWidget.createPartialLM(inputNode, self.labelNode)

        label_image = self.labelNode.GetImageData()
        shape = list(label_image.GetDimensions())
        input_array = vtk.util.numpy_support.vtk_to_numpy(
            label_image.GetPointData().GetScalars())
        original_shape = input_array.shape
        input_array = input_array.reshape(
            shape[2], shape[1],
            shape[0])  # input_array.transpose([2, 1, 0]) would not work!

        input_image = sitk.GetImageFromArray(input_array)
        input_image.SetSpacing(self.labelNode.GetSpacing())
        input_image.SetOrigin(self.labelNode.GetOrigin())

        my_lung_splitter = lung_splitter(split_thirds=True)
        split_lm = my_lung_splitter.execute(input_image)

        split = sitk.GetArrayFromImage(split_lm)

        input_aa = vtk.util.numpy_support.vtk_to_numpy(
            label_image.GetPointData().GetScalars())

        input_aa[:] = split.reshape(original_shape)

        self.labelNode.StorableModified()
        self.labelNode.Modified()
        self.labelNode.InvokeEvent(
            slicer.vtkMRMLVolumeNode.ImageDataModifiedEvent, self.labelNode)

        SlicerUtil.changeLabelmapOpacity(0.5)

    def onApply(self):
        """Calculate the parenchyma analysis
        """
        if not self.inputVolumesAreValid():
            return

        self.applyButton.enabled = False

        if self.preProcessingWidget.filterOnRadioButton.checked and self.preProcessingWidget.filterApplication.checked:
            self.filterInputCT()

        self.applyButton.text = "Analysing..."
        # TODO: why doesn't processEvents alone make the label text change?
        self.applyButton.repaint()
        slicer.app.processEvents()

        self.logic = CIP_ParenchymaAnalysisLogic(self.CTNode, self.labelNode)
        self.populateStats()
        self.logic.createHistogram()
        for i in xrange(len(self.histogramCheckBoxes)):
            self.histogramCheckBoxes[i].setChecked(0)
            self.histogramCheckBoxes[i].hide()

        for tag in self.rTags:
            if tag in self.logic.regionTags:
                self.histogramCheckBoxes[self.rTags.index(tag)].show()

        self.HistSection.enabled = True
        self.chartBox.enabled = True
        # self.reportsWidget.saveButton.enabled = True
        # self.reportsWidget.openButton.enabled = True
        # self.reportsWidget.exportButton.enabled = True
        # self.reportsWidget.removeButton.enabled = True

        self.applyButton.enabled = True
        self.applyButton.text = "Apply"

        for color in ['Red', 'Yellow', 'Green']:
            slicer.app.layoutManager().sliceWidget(color).sliceLogic(
            ).GetSliceCompositeNode().SetBackgroundVolumeID(
                self.CTNode.GetID())

        self.labelSelector.setCurrentNode(self.labelNode)

    def onHistogram(self):
        """Histogram of the selected region
        """
        self.histList = []
        for i in xrange(len(self.histogramCheckBoxes)):
            if self.histogramCheckBoxes[i].checked == True:
                self.histList.append(self.rTags[i])

        self.logic.AddSelectedHistograms(self.histList)

    def onChart(self):
        """chart the parenchyma analysis
        """
        valueToPlot = self.chartOptions[self.chartOption.currentIndex]
        self.logic.createStatsChart(self.labelNode, valueToPlot)

    def onSaveReport(self):
        """ Save the current values in a persistent csv file
        """
        self.logic.statsAsCSV(self.reportsWidget, self.CTNode)

    def onPrintReport(self):
        """
        Print a pdf report
        """
        emphysema_image_path, ct_slice_path = self.logic.computeEmphysemaOnSlice(
            self.CTNode, self.labelNode, op=0.5)
        pdfReporter = PdfReporter()
        # Get the values that are going to be inserted in the html template
        caseName = self.CTNode.GetName()

        values = dict()
        values["@@PATH_TO_STATIC@@"] = os.path.join(
            os.path.dirname(os.path.realpath(__file__)), "Resources/")
        values["@@SUBJECT@@"] = "Subject: " + str(caseName)
        values["@@GLOBAL_LEVEL@@"] = "{:.2f}%".format(
            self.logic.labelStats['LAA%-950', 'WholeLung'])
        values["@@SUMMARY@@"] = "Emphysema per region: "

        pdfRows = """"""
        for tag in self.logic.regionTags:
            pdfRows += """<tr>
              <td align="center">{} </td>
              <td align="center">{:.2f} </td>
            </tr>""".format(tag, self.logic.labelStats['LAA%-950', tag])

        values["@@TABLE_ROWS@@"] = pdfRows

        # Get the path to the html template
        htmlTemplatePath = os.path.join(
            os.path.dirname(os.path.realpath(__file__)),
            "Resources/CIP_ParenchymaAnalysisReport.html")
        # Get a list of image absolute paths that may be needed for the report. In this case, we get the ACIL logo
        imagesFileList = [SlicerUtil.ACIL_LOGO_PATH]

        values["@@EMPHYSEMA_IMAGE@@"] = emphysema_image_path
        values["@@CT_IMAGE@@"] = ct_slice_path

        # Print the report. Remember that we can optionally specify the absolute path where the report is going to
        # be stored
        pdfReporter.printPdf(htmlTemplatePath,
                             values,
                             self.reportPrinted,
                             imagesFileList=imagesFileList)

    def reportPrinted(self, reportPath):
        Util.openFile(reportPath)

    def onFileSelected(self, fileName):
        self.logic.saveStats(fileName)

    def populateStats(self):
        if not self.logic:
            return
        displayNode = self.labelNode.GetDisplayNode()
        colorNode = displayNode.GetColorNode()
        lut = colorNode.GetLookupTable()
        self.items = []
        self.model = qt.QStandardItemModel()
        self.view.setModel(self.model)
        self.view.verticalHeader().visible = False
        row = 0

        for regionTag, regionValue in zip(self.logic.regionTags,
                                          self.logic.regionValues):
            color = qt.QColor()
            rgb = lut.GetTableValue(regionValue[0])
            color.setRgb(rgb[0] * 255, rgb[1] * 255, rgb[2] * 255)
            item = qt.QStandardItem()
            item.setData(color, 1)
            item.setText(str(regionTag))
            item.setData(regionTag, 1)
            item.setToolTip(regionTag)
            item.setTextAlignment(1)
            self.model.setItem(row, 0, item)
            self.items.append(item)
            col = 1
            for k in self.logic.keys:
                item = qt.QStandardItem()
                item.setText("%.3f" % self.logic.labelStats[k, regionTag])
                item.setTextAlignment(4)
                self.view.setColumnWidth(col, 15 * len(item.text()))
                self.model.setItem(row, col, item)
                self.items.append(item)
                col += 1
            row += 1

        self.view.setColumnWidth(0, 15 * len('Region'))
        self.model.setHeaderData(0, 1, "Region")
        col = 1
        for k in self.logic.keys:
            # self.view.setColumnWidth(col,15*len(k))
            self.model.setHeaderData(col, 1, k)
            col += 1
示例#2
0
class CIP_InteractiveLobeSegmentationWidget(ScriptedLoadableModuleWidget):
    def __init__(self, parent=None):
        ScriptedLoadableModuleWidget.__init__(self, parent)
        self.logic = CIP_InteractiveLobeSegmentationLogic()
        self.observerTags = []
        if not parent:
            self.parent = slicer.qMRMLWidget()
            self.parent.setLayout(qt.QVBoxLayout())
            self.parent.setMRMLScene(slicer.mrmlScene)
        else:
            self.parent = parent
        self.layout = self.parent.layout()
        if not parent:
            self.setup()
            self.parent.show()

#    def enter(self):
#        """Method that is invoked when we switch to the module in slicer user interface"""
#        if self.nodeObserver is None:
#            self.nodeObserver = slicer.mrmlScene.AddObserver(slicer.vtkMRMLScene.NodeAddedEvent, self.onNodeAdded)
#        self.checkMasterAndLabelMapNodes()

    def enter(self):
        if self.labelSelector.currentNode():
            for color in ['Red', 'Yellow', 'Green']:
                slicer.app.layoutManager().sliceWidget(color).sliceLogic(
                ).GetSliceCompositeNode().SetLabelVolumeID(
                    self.labelSelector.currentNode().GetID())

    def exit(self):
        for color in ['Red', 'Yellow', 'Green']:
            slicer.app.layoutManager().sliceWidget(color).sliceLogic(
            ).GetSliceCompositeNode().SetLabelVolumeID('None')


#        slicer.mrmlScene.RemoveObserver(self.nodeObserver)

    def setup(self):
        # Instantiate and connect widgets ...
        ScriptedLoadableModuleWidget.setup(self)

        if SlicerUtil.IsDevelopment:
            self.reloadAndTestButton.visible = False  # No valid tests at the moment
        #
        # Parameters Area
        #
        parametersCollapsibleButton = ctk.ctkCollapsibleButton()
        parametersCollapsibleButton.text = "Parameters"
        self.layout.addWidget(parametersCollapsibleButton)

        # Layout within the dummy collapsible button
        parametersFormLayout = qt.QFormLayout(parametersCollapsibleButton)
        parametersFormLayout.setVerticalSpacing(5)

        #
        # Input volume selector
        #
        self.CTSelector = slicer.qMRMLNodeComboBox()
        self.CTSelector.nodeTypes = (("vtkMRMLScalarVolumeNode"), "")
        self.CTSelector.addAttribute("vtkMRMLScalarVolumeNode", "LabelMap", 0)
        self.CTSelector.selectNodeUponCreation = False
        self.CTSelector.addEnabled = False
        self.CTSelector.removeEnabled = False
        self.CTSelector.noneEnabled = True
        self.CTSelector.showHidden = False
        self.CTSelector.showChildNodeTypes = False
        self.CTSelector.setMRMLScene(slicer.mrmlScene)
        self.CTSelector.setToolTip("Pick the CT image to work on.")
        parametersFormLayout.addRow("Input CT Volume: ", self.CTSelector)

        #
        # First input volume selector
        #
        self.labelSelector = slicer.qMRMLNodeComboBox()
        # self.labelSelector.nodeTypes = ( ("vtkMRMLScalarVolumeNode"), "" )
        # self.labelSelector.addAttribute( "vtkMRMLScalarVolumeNode", "LabelMap", 1 )
        self.labelSelector.nodeTypes = (("vtkMRMLLabelMapVolumeNode"), "")
        self.labelSelector.selectNodeUponCreation = True
        self.labelSelector.addEnabled = False
        self.labelSelector.removeEnabled = False
        self.labelSelector.noneEnabled = True
        self.labelSelector.showHidden = False
        self.labelSelector.showChildNodeTypes = False
        self.labelSelector.setMRMLScene(slicer.mrmlScene)
        self.labelSelector.setToolTip("Pick the label map to the algorithm.")
        parametersFormLayout.addRow("Label Map Volume: ", self.labelSelector)
        #
        # output volume selector
        #

        self.outputSelector = slicer.qMRMLNodeComboBox()
        # self.outputSelector.nodeTypes = ( ("vtkMRMLScalarVolumeNode"), "" )
        # self.outputSelector.addAttribute( "vtkMRMLScalarVolumeNode", "LabelMap", 1 )
        self.outputSelector.nodeTypes = (("vtkMRMLLabelMapVolumeNode"), "")
        self.outputSelector.selectNodeUponCreation = True
        self.outputSelector.addEnabled = True
        self.outputSelector.removeEnabled = True
        self.outputSelector.noneEnabled = True
        self.outputSelector.showHidden = False
        self.outputSelector.showChildNodeTypes = True
        self.outputSelector.setMRMLScene(slicer.mrmlScene)
        self.outputSelector.setToolTip("Pick the output to the algorithm.")
        self.outputSelector.baseName = 'Fissures Segmentation Volume'
        parametersFormLayout.addRow("Fissures Volume: ", self.outputSelector)

        self.preProcessingWidget = PreProcessingWidget(
            self.moduleName, parentWidget=self.parent)
        self.preProcessingWidget.setup()
        self.preProcessingWidget.filterApplication.hide()
        self.preProcessingWidget.enableFilteringFrame(True)
        self.preProcessingWidget.enableFilterOptions(True)
        self.preProcessingWidget.enableLMFrame(True)

        self.layoutCollapsibleButton = ctk.ctkCollapsibleButton()
        self.layoutCollapsibleButton.text = "Layout Selection"
        self.layoutCollapsibleButton.setChecked(False)
        self.layoutCollapsibleButton.setFixedSize(600, 40)
        self.layout.addWidget(self.layoutCollapsibleButton, 0, 4)
        self.layoutFormLayout = qt.QFormLayout(self.layoutCollapsibleButton)
        """spacer = ""
        for s in range( 20 ):
          spacer += " """
        # self.fiducialsFormLayout.setFormAlignment(4)

        self.layoutGroupBox = qt.QFrame()
        self.layoutGroupBox.setLayout(qt.QVBoxLayout())
        self.layoutGroupBox.setFixedHeight(86)
        self.layoutFormLayout.addRow(self.layoutGroupBox)

        self.buttonGroupBox = qt.QFrame()
        self.buttonGroupBox.setLayout(qt.QHBoxLayout())
        self.layoutGroupBox.layout().addWidget(self.buttonGroupBox)
        # self.layoutFormLayout.addRow(self.buttonGroupBox)

        #
        # Four-Up Button
        #
        self.fourUpButton = qt.QPushButton()
        self.fourUpButton.toolTip = "Four-up view."
        self.fourUpButton.enabled = True
        self.fourUpButton.setFixedSize(40, 40)
        fourUpIcon = qt.QIcon(":/Icons/LayoutFourUpView.png")
        self.fourUpButton.setIcon(fourUpIcon)
        self.buttonGroupBox.layout().addWidget(self.fourUpButton)

        #
        # Red Slice Button
        #
        self.redViewButton = qt.QPushButton()
        self.redViewButton.toolTip = "Red slice only."
        self.redViewButton.enabled = True
        self.redViewButton.setFixedSize(40, 40)
        redIcon = qt.QIcon(":/Icons/LayoutOneUpRedSliceView.png")
        self.redViewButton.setIcon(redIcon)
        self.buttonGroupBox.layout().addWidget(self.redViewButton)

        #
        # Yellow Slice Button
        #
        self.yellowViewButton = qt.QPushButton()
        self.yellowViewButton.toolTip = "Yellow slice only."
        self.yellowViewButton.enabled = True
        self.yellowViewButton.setFixedSize(40, 40)
        yellowIcon = qt.QIcon(":/Icons/LayoutOneUpYellowSliceView.png")
        self.yellowViewButton.setIcon(yellowIcon)
        self.buttonGroupBox.layout().addWidget(self.yellowViewButton)

        #
        # Green Slice Button
        #
        self.greenViewButton = qt.QPushButton()
        self.greenViewButton.toolTip = "Yellow slice only."
        self.greenViewButton.enabled = True
        self.greenViewButton.setFixedSize(40, 40)
        greenIcon = qt.QIcon(":/Icons/LayoutOneUpGreenSliceView.png")
        self.greenViewButton.setIcon(greenIcon)
        self.buttonGroupBox.layout().addWidget(self.greenViewButton)

        #
        # Buttons labels
        #
        self.labelsGroupBox = qt.QFrame()
        hBox = qt.QHBoxLayout()
        hBox.setSpacing(10)
        self.labelsGroupBox.setLayout(hBox)
        self.labelsGroupBox.setFixedSize(450, 26)
        self.layoutGroupBox.layout().addWidget(self.labelsGroupBox, 0, 4)

        fourUpLabel = qt.QLabel("   Four-up")
        # fourUpLabel.setFixedHeight(10)
        self.labelsGroupBox.layout().addWidget(fourUpLabel)

        redLabel = qt.QLabel("       Axial")
        self.labelsGroupBox.layout().addWidget(redLabel)

        yellowLabel = qt.QLabel("       Saggital")
        self.labelsGroupBox.layout().addWidget(yellowLabel)

        greenLabel = qt.QLabel("          Coronal")
        self.labelsGroupBox.layout().addWidget(greenLabel)

        # Layout connections
        self.fourUpButton.connect('clicked()', self.onFourUpButton)
        self.redViewButton.connect('clicked()', self.onRedViewButton)
        self.yellowViewButton.connect('clicked()', self.onYellowViewButton)
        self.greenViewButton.connect('clicked()', self.onGreenViewButton)

        #
        # Fiducials Area        #

        self.groupBox = qt.QFrame()
        self.groupBox.setLayout(qt.QHBoxLayout())

        fiducialsCollapsibleButton = ctk.ctkCollapsibleButton()
        fiducialsCollapsibleButton.text = "Fiducials Selection"
        self.layout.addWidget(fiducialsCollapsibleButton)
        self.fiducialsFormLayout = qt.QFormLayout(fiducialsCollapsibleButton)
        self.fiducialsFormLayout.setVerticalSpacing(5)
        self.fiducialsFormLayout.addRow(self.groupBox)

        # Add fiducial lists button
        self.AddLeftListButton = qt.QPushButton("Left oblique fissure")
        self.AddLeftListButton.toolTip = "Create a new fiducial list for the left lung oblique fissure."
        self.AddLeftListButton.setFixedHeight(40)
        self.groupBox.layout().addWidget(self.AddLeftListButton)

        # Add fiducial lists button
        self.AddRight1ListButton = qt.QPushButton("Right oblique fissure")
        self.AddRight1ListButton.toolTip = "Create a new fiducial list for the right lung oblique fissure."
        self.AddRight1ListButton.setFixedHeight(40)
        self.groupBox.layout().addWidget(self.AddRight1ListButton)

        # Add fiducial lists button
        self.AddRight2ListButton = qt.QPushButton("Right horizontal fissure")
        self.AddRight2ListButton.toolTip = "Create a new fiducial list for the right lung horizontal fissure."
        self.AddRight2ListButton.setFixedHeight(40)
        self.groupBox.layout().addWidget(self.AddRight2ListButton)

        #
        # Apply Button
        #
        self.applyButton = qt.QPushButton("Apply")
        self.applyButton.toolTip = "Run the algorithm."
        self.applyButton.enabled = False
        self.applyButton.setFixedSize(150, 45)
        self.layout.addWidget(self.applyButton, 0, 4)
        # self.layout.setAlignment(2)

        #
        # Show Fiducials
        #
        fiducialButtonsList = []
        fiducialButtonsList.append(self.AddLeftListButton)
        fiducialButtonsList.append(self.AddRight1ListButton)
        fiducialButtonsList.append(self.AddRight2ListButton)

        self.visualizationWidget = ILSVisualizationWidget(
            self.logic, self.applyButton, fiducialButtonsList)
        self.fiducialsFormLayout.addRow(self.visualizationWidget.widget)

        # connections
        self.applyButton.connect('clicked(bool)', self.onApplyButton)
        self.CTSelector.connect("currentNodeChanged(vtkMRMLNode*)",
                                self.onCTSelect)
        self.labelSelector.connect("currentNodeChanged(vtkMRMLNode*)",
                                   self.onSelect)
        self.outputSelector.connect("currentNodeChanged(vtkMRMLNode*)",
                                    self.onSelect)
        self.AddLeftListButton.connect('clicked()', self.onAddLeftListButton)
        self.AddRight1ListButton.connect('clicked()',
                                         self.onAddRight1ListButton)
        self.AddRight2ListButton.connect('clicked()',
                                         self.onAddRight2ListButton)

        self.updateList()

        # Add vertical spacer
        self.layout.addStretch(1)

    def cleanup(self):
        pass

    def onCTSelect(self, CTNode):
        if CTNode:
            for color in ['Red', 'Yellow', 'Green']:
                slicer.app.layoutManager().sliceWidget(color).sliceLogic(
                ).GetSliceCompositeNode().SetBackgroundVolumeID(CTNode.GetID())
        else:
            for color in ['Red', 'Yellow', 'Green']:
                slicer.app.layoutManager().sliceWidget(color).sliceLogic(
                ).GetSliceCompositeNode().SetBackgroundVolumeID('None')

    def onSelect(self):
        self.layoutCollapsibleButton.setChecked(True)
        if self.labelSelector.currentNode():
            self.preProcessingWidget.enableFilteringFrame(False)
            self.preProcessingWidget.enableLMFrame(False)
            for color in ['Red', 'Yellow', 'Green']:
                slicer.app.layoutManager().sliceWidget(color).sliceLogic(
                ).GetSliceCompositeNode().SetLabelVolumeID(
                    self.labelSelector.currentNode().GetID())
            SlicerUtil.changeLabelmapOpacity(0.5)
        else:
            self.preProcessingWidget.enableFilteringFrame(True)
            self.preProcessingWidget.enableLMFrame(True)
            for color in ['Red', 'Yellow', 'Green']:
                slicer.app.layoutManager().sliceWidget(color).sliceLogic(
                ).GetSliceCompositeNode().SetLabelVolumeID('None')

    def onFourUpButton(self):
        applicationLogic = slicer.app.applicationLogic()
        interactionNode = applicationLogic.GetInteractionNode()
        interactionNode.Reset(None)
        interactionNode.SwitchToPersistentPlaceMode()
        layoutManager = slicer.app.layoutManager()
        layoutManager.setLayout(3)

    def onRedViewButton(self):
        applicationLogic = slicer.app.applicationLogic()
        interactionNode = applicationLogic.GetInteractionNode()
        interactionNode.Reset(None)
        interactionNode.SwitchToPersistentPlaceMode()
        layoutManager = slicer.app.layoutManager()
        layoutManager.setLayout(6)

    def onYellowViewButton(self):
        applicationLogic = slicer.app.applicationLogic()
        interactionNode = applicationLogic.GetInteractionNode()
        interactionNode.Reset(None)
        interactionNode.SwitchToPersistentPlaceMode()
        layoutManager = slicer.app.layoutManager()
        layoutManager.setLayout(7)

    def onGreenViewButton(self):
        applicationLogic = slicer.app.applicationLogic()
        interactionNode = applicationLogic.GetInteractionNode()
        interactionNode.Reset(None)
        interactionNode.SwitchToPersistentPlaceMode()
        layoutManager = slicer.app.layoutManager()
        layoutManager.setLayout(8)

    def onAddLeftListButton(self):
        # self.applyButton.enabled = self.inputSelector.currentNode() and self.labelSelector.currentNode() and self.outputSelector.currentNode()
        self.AddLeftListButton.setStyleSheet(
            "background-color: rgb(255,99,71)")
        self.AddRight1ListButton.setStyleSheet(
            "background-color: rgb(255,255,255)")
        self.AddRight2ListButton.setStyleSheet(
            "background-color: rgb(255,255,255)")
        logic = CIP_InteractiveLobeSegmentationLogic()
        self.logic.name = 'LO'
        logic.createList('LO')

    def onAddRight1ListButton(self):
        # self.applyButton.enabled = self.inputSelector.currentNode() and self.labelSelector.currentNode() and self.outputSelector.currentNode()
        self.AddRight1ListButton.setStyleSheet(
            "background-color: rgb(255,99,71)")
        self.AddLeftListButton.setStyleSheet(
            "background-color: rgb(255,255,255)")
        self.AddRight2ListButton.setStyleSheet(
            "background-color: rgb(255,255,255)")
        logic = CIP_InteractiveLobeSegmentationLogic()
        self.logic.name = 'RO'
        logic.createList('RO')

    def onAddRight2ListButton(self):
        # self.applyButton.enabled = self.inputSelector.currentNode() and self.labelSelector.currentNode() and self.outputSelector.currentNode()
        self.AddRight2ListButton.setStyleSheet(
            "background-color: rgb(255,99,71)")
        self.AddLeftListButton.setStyleSheet(
            "background-color: rgb(255,255,255)")
        self.AddRight1ListButton.setStyleSheet(
            "background-color: rgb(255,255,255)")
        logic = CIP_InteractiveLobeSegmentationLogic()
        self.logic.name = 'RH'
        logic.createList('RH')

    def onApplyButton(self):
        red_logic = slicer.app.layoutManager().sliceWidget("Red").sliceLogic()
        red_cn = red_logic.GetSliceCompositeNode()
        volumeID = red_cn.GetBackgroundVolumeID()
        CTNode = SlicerUtil.getNode(volumeID)
        if self.labelSelector.currentNode() == None:
            warning = self.preProcessingWidget.warningMessageForLM()
            if warning == 16384:
                labelNode = slicer.mrmlScene.AddNode(
                    slicer.vtkMRMLLabelMapVolumeNode())
                labelNode.SetName(CTNode.GetName() + '_partialLungLabelMap')

                if not CTNode:
                    self.applyButton.enabled = True
                    return False
                if self.preProcessingWidget.filterOnRadioButton.checked:
                    volumesLogic = slicer.modules.volumes.logic()
                    clonedCTNode = volumesLogic.CloneVolume(
                        slicer.mrmlScene, CTNode, 'Cloned Volume')
                    self.filterInputCT(clonedCTNode)
                    self.createLungLabelMap(clonedCTNode, labelNode)
                    slicer.mrmlScene.RemoveNode(clonedCTNode)
                    for color in ['Red', 'Yellow', 'Green']:
                        slicer.app.layoutManager().sliceWidget(
                            color).sliceLogic().GetSliceCompositeNode(
                            ).SetBackgroundVolumeID(CTNode.GetID())
                else:
                    self.createLungLabelMap(CTNode, labelNode)

            else:
                qt.QMessageBox.warning(slicer.util.mainWindow(),
                                       "Parenchyma Analysis",
                                       "Please select a Lung Label Map.")
                self.applyButton.enabled = True
                return False

        self.visualizationWidget.updateScene()

        self.applyButton.text = "Segmenting Lobes..."
        self.applyButton.repaint()
        slicer.app.processEvents()

        logic = CIP_InteractiveLobeSegmentationLogic()

        self.visualizationWidget.pendingUpdate = True
        outputNode = self.outputSelector.currentNode()
        if not outputNode:
            outputNode = slicer.vtkMRMLLabelMapVolumeNode()
            slicer.mrmlScene.AddNode(outputNode)

        fissureVolume = None
        try:
            fissureVolume = logic.run(self.labelSelector.currentNode(),
                                      outputNode)
        except Exception as e:
            import traceback
            traceback.print_exc()
            qt.QMessageBox.warning(
                slicer.util.mainWindow(), "Running", 'Exception!\n\n' +
                str(e) + "\n\nSee Python Console for Stack Trace")

        # if fissureVolume is not None:
        self.outputSelector.setCurrentNode(fissureVolume)
        SlicerUtil.changeLabelmapOpacity(0.5)

        self.onFourUpButton()
        self.applyButton.text = "Apply"
        self.applyButton.repaint()
        slicer.app.processEvents()
        self.applyButton.enabled = True
        applicationLogic = slicer.app.applicationLogic()
        interactionNode = applicationLogic.GetInteractionNode()
        interactionNode.Reset(None)
        self.visualizationWidget.pendingUpdate = False

        for color in ['Red', 'Yellow', 'Green']:
            slicer.app.layoutManager().sliceWidget(color).sliceLogic(
            ).GetSliceCompositeNode().SetBackgroundVolumeID(CTNode.GetID())
            slicer.app.layoutManager().sliceWidget(
                color).sliceLogic().GetSliceCompositeNode().SetLabelVolumeID(
                    self.outputSelector.currentNode().GetID())

    def filterInputCT(self, input_node):
        # self.applyButton.enabled = False
        self.applyButton.text = "Filtering..."
        self.applyButton.repaint()
        slicer.app.processEvents()

        self.preProcessingWidget.filterInputCT(input_node)

    def createLungLabelMap(self, input_node, label_node):
        """Create the lung label map
        """
        self.applyButton.text = "Creating Label Map..."
        self.applyButton.repaint()
        slicer.app.processEvents()

        self.preProcessingWidget.createPartialLM(input_node, label_node)

        SlicerUtil.changeLabelmapOpacity(0.5)
        self.labelSelector.setCurrentNode(label_node)

    def updateList(self):
        """Observe the mrml scene for changes that we wish to respond to."""

        tag = slicer.mrmlScene.AddObserver(slicer.mrmlScene.EndCloseEvent,
                                           self.clearTable)
        tag = slicer.mrmlScene.AddObserver(
            slicer.mrmlScene.NodeAddedEvent,
            self.visualizationWidget.requestNodeAddedUpdate)
        self.observerTags.append((slicer.mrmlScene, tag))

    def clearTable(self, caller, event):
        self.onFourUpButton()
        self.visualizationWidget.tableWidget.clearContents()
        self.visualizationWidget.tableWidget.setRowCount(0)
        self.visualizationWidget.leftRow = 0
        self.visualizationWidget.rightObliqueRow = 0
        self.visualizationWidget.rightHorizontalRow = 0
        self.visualizationWidget.updateScene()
        self.visualizationWidget.fiducialsCollapsibleButton.hide()
        self.visualizationWidget.removeFiducialObservers()