ENH: first working version of slicerSMTK plugin (export only)
This MR is for merging with master the branch containing the first version of a working slicerSMTK
plugin. Currently it provides functionality for exporting MRML scene to the smtk
(json) file format used by aevaCMB
and aeva-session
. The exported file is saved with extension .aeva.smtk
which can then be loaded into aevaCMB
application.
Features included:
- Export surface models as vtkPolyData (.vtp)
- Export solid models as vtkUnstructuredGrid (.vtu)
- Export Images/Volumes as vtkImageData (.vti)
- Export scene as smtk json format (.aeva.smtk)
- Individual component files (vtp, vtu, vti) are saved under a subfolder with the same name as the parent filename.
- Components undergo transformation hardening before saving, thereby carrying forward any registrations/transformations applied within Slicer.
- File names of component files are made more readable by using the user-specified MRML node name as a prefix to the file followed by the respective uuid. The prefix undergoes string sanitization to ensure that it is compatible as a file name.
Merge request reports
Activity
481 using smtk::session::aeva::Write; 482 auto writeOp = smtk::session::aeva::Write::create(); 483 writeOp->parameters()->associate(resource); 484 writeOp->parameters()->findVoid("copy all data with resource")->setIsEnabled(false); 485 auto voidItem = writeOp->parameters()->findVoid("copy all data with resource"); 486 if (voidItem) 487 { 488 voidItem->setIsEnabled(false); 489 } 490 else 491 { 492 qWarning() << "Unable to switch-off \"copy all data with resource\" flag."; 493 } 494 495 Write::Result result = writeOp->operate(); 496 bool outcome = result->findInt("outcome")->value() == static_cast<int>(Write::Outcome::SUCCEEDED); 487 { 488 voidItem->setIsEnabled(false); 489 } 490 else 491 { 492 qWarning() << "Unable to switch-off \"copy all data with resource\" flag."; 493 } 494 495 Write::Result result = writeOp->operate(); 496 bool outcome = result->findInt("outcome")->value() == static_cast<int>(Write::Outcome::SUCCEEDED); 497 498 // Forward smtk log records to Slicer's error log. 499 auto logger = writeOp->log(); 500 qInfo() << logger.convertToString().data(); 501 409 502 if (outcome) 490 else 491 { 492 qWarning() << "Unable to switch-off \"copy all data with resource\" flag."; 493 } 494 495 Write::Result result = writeOp->operate(); 496 bool outcome = result->findInt("outcome")->value() == static_cast<int>(Write::Outcome::SUCCEEDED); 497 498 // Forward smtk log records to Slicer's error log. 499 auto logger = writeOp->log(); 500 qInfo() << logger.convertToString().data(); 501 409 502 if (outcome) 410 503 { 411 qInfo() << "SMTK export successful"; 504 qInfo() << "SMTK export successful."; I suggest to be more explicit:
qInfo() << "MRML scene successfully exported to aeva SMTK file:" << fullPath; qInfo() << "Model, Segmentation and Volume nodes successfully exported to aeva SMTK resources into directory:" << modelPath;
Edited by Jean-Christophe Fillion-Robin
Additional suggestions:
-
Introduce a function called
exportMRMLSceneToAevaSMTKResources(vtkMRMLScene* scene, const std::string& smtkFileName, const std::string& smtkExportDir)
that would be called bywriteToSMTK
.- Internally
smtkFilePath
would be set doingsmtkExportDir + "/" + smtkFileName
- Consider moving
exportMRMLSceneToSMTKResources
(and associated functions) intovtkSlicerSMTKFileWriterLogic
) currently the only reason it depends onqSlicerBaseQTCore
it because of use ofqSlicerCoreIOManager::forceFileNameValidCharacters
- Internally
-
Change
importSeparatedSegmentations
toexportSegmentationNodeToAevaSMTKResource(vtkMRMLSegmentationNode* segmentationNode, const smtk::session::aeva::Resource::Ptr& resource, const std::string& smtkExportDir)
-
Change
importVTKImage
toexportVolumeNodeToAevaSMTKResource(vtkMRMLVolumeNode* volumeNode, const smtk::session::aeva::Resource::Ptr& resource, const std::string& smtkExportDir)
-
Change
addImageToResource
toaddVTKImageDataToAevaSMTKModelResource(vtkSmartPointer<vtkImageData> imageData, const smtk::session::aeva::Resource::Ptr& resource, const std::string& modelName, const std::string& smtkExportDir)
-
Change
importVTKMesh
toexportModelNodeToAevaSMTKResource(vtkMRMLModelNode* modelNode, const smtk::session::aeva::Resource::Ptr& resource, const std::string& smtkExportDir)
-
Change
setFileNameProperty
tosetSMTKCellFilenameProperty
-
Remove unused
vtkSlicerSMTKFileWriterLogic::LoadSMTKFile()
Edited by Jean-Christophe Fillion-Robin-
-
Rename
writeToSMTK
towriteToAevaSMTK
-
Instead of exporting to a file with only
.smtk
, I suggest to export as.aeva.smtk
. That way it would be clear that this SMTK file requires the aeva-session plugin to be interpreted. @dcthomp What do you think ?
-
Considering that segmentation, volume and model nodes can all be part of a transform hierarchy, and assuming that:
- aeva smtk doesn't have a concept of transform hierarchy
- relative location of "object" is important
the corresponding nodes should be first exported to a internal scene and the corresponding transform hardened.
The good news is that the underlying logic (hardening) has been implemented by Ebrahim while adding the node export functionality (see https://github.com/Slicer/Slicer/pull/5876 and https://discourse.slicer.org/t/new-export-functionality/20950.
Code available in https://github.com/Slicer/Slicer/blob/3de75b9c4c6f3ee9a1e5da17059b9f11517e16ab/Base/QTCore/qSlicerCoreIOManager.cxx#L785 should be factored out to be reusable.
.aeva.stmk
aeva-session's write operator actually uses SMTK's functionality to write the json as .smtk. So I think the file should be readable independent of aeva-session too? Nonetheless, making changes will also be needed on aevaCMB side to use
.aeva.smtk
extension.transform hardening
Retaining transformations is definitely needed, and as I understand smtk supports a transform property, so we may not need to apply the transform directly to the dataset geometry. There may be many other attributes we want to include in future iterations. We will also have to consider loading data back-and-forth between aevaSlicer and aevaCMB. Since this would need some work, I suggest we do this in future releases.
SMTK will (but does not currently) support storing transforms as property-data on components. The change required is small and will definitely be part of the next milestone as part of the annotation transfer work (smtk::extension::vtk::Geometry will need to check for the property on each component and apply it to the VTK data it produces).
I'm not sure about hierarchical transforms. We would need to agree on how the hierarchy is represented in SMTK. I can certainly see checking whether a volume/face/edge/vertex cell's owning model has a transform, but what other cases are there that we need to handle?
All review comments (@jcfr) addressed in the following commits:
896c4b49 and 0a485a31Output extension changed from
.smtk
to.aeva.smtk
, and added documentation comments to functions in the Logic class:
f6204557Transform hardening implemented through the following commits:
SlicerSMTK: 4b0d142d
KitwareMedical/Slicer refactor: https://github.com/KitwareMedical/Slicer/commit/0eaf803012bbecdb7735a9c4b4b934754fe3c148
- Resolved by Shreeraj Jadhav
- Resolved by Shreeraj Jadhav
+1 from me for the SMTK part.
- Resolved by Shreeraj Jadhav