diff --git a/OpenAnatomyExport/OpenAnatomyExport.py b/OpenAnatomyExport/OpenAnatomyExport.py index 364dcd6..a8e37a3 100644 --- a/OpenAnatomyExport/OpenAnatomyExport.py +++ b/OpenAnatomyExport/OpenAnatomyExport.py @@ -5,6 +5,9 @@ import vtk, qt, ctk, slicer from slicer.ScriptedLoadableModule import * import logging +import sys +import subprocess + # # OpenAnatomyExport @@ -24,6 +27,7 @@ def __init__(self, parent): self.parent.helpText = """ Export model hierarchy or segmentation to OpenAnatomy-compatible glTF file. """ + self.logic = None self.parent.helpText += self.getDefaultModuleDocumentationLink() self.parent.acknowledgementText = """ """ # replace with organization, grant and thanks. @@ -38,6 +42,8 @@ class OpenAnatomyExportWidget(ScriptedLoadableModuleWidget): """ def setup(self): + self.logic = OpenAnatomyExportLogic() + ScriptedLoadableModuleWidget.setup(self) self.logic = OpenAnatomyExportLogic() @@ -71,7 +77,22 @@ def cleanup(self): pass def onSelect(self): + currentItemId = self.ui.inputSelector.currentItem() + + currentItemId = self.ui.inputSelector.currentItem() + subjectHierarchy = slicer.vtkMRMLSubjectHierarchyNode.GetSubjectHierarchyNode(slicer.mrmlScene) + + # Get the MRML node associated with the current item + inputNode = subjectHierarchy.GetItemDataNode(currentItemId) + + if inputNode is not None: + # Do something with the node + print("Node name:", inputNode.GetName()) + else: + print("No associated node found for the current item") + + self.logic.inputNode = inputNode shNode = slicer.vtkMRMLSubjectHierarchyNode.GetSubjectHierarchyNode(slicer.mrmlScene) owner = shNode.GetItemOwnerPluginName(currentItemId) if currentItemId else "" self.ui.exportButton.enabled = (owner == "Folder" or owner == "Segmentations") @@ -80,7 +101,7 @@ def onSelect(self): self.ui.outputModelHierarchyLabel.visible = (currentFormat == "scene") self.ui.outputFileFolderSelector.visible = (currentFormat != "scene") - self.ui.imageExportButton.enabled = self.ui.imageInputSelector.currentNode() + self.ui.imageExportButton.enabled = True def onExportButton(self): slicer.app.setOverrideCursor(qt.Qt.WaitCursor) @@ -134,6 +155,8 @@ class OpenAnatomyExportLogic(ScriptedLoadableModuleLogic): Uses ScriptedLoadableModuleLogic base class, available at: https://github.com/Slicer/Slicer/blob/master/Base/Python/slicer/ScriptedLoadableModule.py """ + + def __init__(self): ScriptedLoadableModuleLogic.__init__(self) @@ -148,7 +171,7 @@ def __init__(self): # change seems to be working well. self.saturationBoost = 1.5 self.brightnessBoost = 1.0 - + self.inputNode = None self._outputShFolderItemId = None self._numberOfExpectedModels = 0 self._numberOfProcessedModels = 0 @@ -175,15 +198,85 @@ def isValidInputOutputData(self, inputNode): return True + def exportAllSegmentsToUSD(self, segmentationNode, fileName, outputFolder): + + import slicer + slicer.util.pip_install("usd-core") + from pxr import Usd, UsdGeom + import vtk + + if not segmentationNode: + logging.debug('exportAllSegmentsToUSD failed: no input node defined') + return False + + # Create an append filter to combine all meshes + append_filter = vtk.vtkAppendPolyData() + + # Iterate through each segment and append its polyData + segment_ids = vtk.vtkStringArray() + segmentationNode.GetSegmentation().GetSegmentIDs(segment_ids) + for i in range(segment_ids.GetNumberOfValues()): + segment_id = segment_ids.GetValue(i) + polyData = vtk.vtkPolyData() + segmentationNode.GetClosedSurfaceRepresentation(segment_id, polyData) + append_filter.AddInputData(polyData) + + # Combine all segments + append_filter.Update() + + # Get the combined mesh + combined_polyData = append_filter.GetOutput() + points = combined_polyData.GetPoints() + triangles = combined_polyData.GetPolys() + + output_file = outputFolder + "/" + fileName + print(output_file) + + # Convert to USD format + stage = Usd.Stage.CreateNew(output_file) + mesh = UsdGeom.Mesh.Define(stage, '/CombinedMesh') + + # Set vertices (points) + usd_points = [] + for i in range(points.GetNumberOfPoints()): + p = points.GetPoint(i) + usd_points.append((p[0], p[1], p[2])) + mesh.CreatePointsAttr(usd_points) + + # Set faces (triangles) + usd_faces = [] + # Logic to convert slicer triangles to USD face format: + # This part needs to be implemented according to how your specific data is structured. + # ... + + mesh.CreateFaceVertexCountsAttr(usd_faces) + + # Save the USD file + stage.Save() + def exportModel(self, inputItem, outputFolder=None, reductionFactor=None, outputFormat=None): + + if outputFolder is None: + if self._exportToFile: + raise ValueError("Output folder must be specified if output format is not 'scene'") if outputFormat is None: outputFormat = "glTF" + if outputFormat == "OpenUSD": + outputFormat = "USD" + # Example usage - Export all segments to a single USD file + nodename = self.inputNode.GetName() + print(nodename) + segmentationNode = self.inputNode + if segmentationNode: + self.exportAllSegmentsToUSD(self.inputNode, 'combined_output.usd', outputFolder) + print("USD export successful.") + else: + print("Segmentation node not found.") + return + if reductionFactor is not None: self.reductionFactor = reductionFactor self._exportToFile = (outputFormat != "scene") - if outputFolder is None: - if self._exportToFile: - raise ValueError("Output folder must be specified if output format is not 'scene'") shNode = slicer.vtkMRMLSubjectHierarchyNode.GetSubjectHierarchyNode(slicer.mrmlScene) inputName = shNode.GetItemName(inputItem) @@ -236,11 +329,17 @@ def exportModel(self, inputItem, outputFolder=None, reductionFactor=None, output outputFilePath = outputFilePathBase + '.obj' exporter.SetFilePrefix(outputFilePathBase) else: - raise ValueError("Output format must be scene, glTF, or OBJ") + raise ValueError("Output format must be scene, glTF, OBJ or OpenUSD") self.addLog(f"Writing file {outputFilePath}...") - exporter.SetRenderWindow(self._renderWindow) - exporter.Write() + if not outputFormat == "OpenUSD": + exporter.SetRenderWindow(self._renderWindow) + exporter.Write() + else: + import sys + sys.path.append("{}/utils".format(slicer.mrmlScene.GetRootDirectory().strip("/"))) + from export_usd import export_usd + export_usd() if outputFormat == "glTF": diff --git a/OpenAnatomyExport/Resources/UI/OpenAnatomyExport.ui b/OpenAnatomyExport/Resources/UI/OpenAnatomyExport.ui index e896057..6ba60a7 100644 --- a/OpenAnatomyExport/Resources/UI/OpenAnatomyExport.ui +++ b/OpenAnatomyExport/Resources/UI/OpenAnatomyExport.ui @@ -24,18 +24,6 @@ - - - - Select segmentation node or model folder - - - - vtkMRMLSegmentationNode - - - - @@ -92,6 +80,11 @@ scene + + + OpenUSD + + @@ -145,6 +138,22 @@ + + + + + + + + + + + + + + + +