Skip to content

Commit

Permalink
ENH: add an option to export selected segments (with or without the c…
Browse files Browse the repository at this point in the history
…onsideration of resections) into new segmentation node
  • Loading branch information
RuoyanMeng committed Oct 14, 2024
1 parent 49ca1ec commit 3f65c92
Show file tree
Hide file tree
Showing 7 changed files with 284 additions and 151 deletions.
4 changes: 4 additions & 0 deletions Liver/Liver.py
Original file line number Diff line number Diff line change
Expand Up @@ -737,6 +737,10 @@ def onComputeDistanceMapButtonClicked(self):
downSamplingRate = self.distanceMapsWidget.DownsamplingRateSpinBox.value
self.logic.computeDistanceMaps(tumorLabelmapVolumeNode, parenchymaLabelmapVolumeNode, hepaticLabelmapVolumeNode, portalLabelmapVolumeNode, outputVolumeNode, downSamplingRate)

slicer.mrmlScene.RemoveNode(tumorLabelmapVolumeNode)
slicer.mrmlScene.RemoveNode(parenchymaLabelmapVolumeNode)
slicer.mrmlScene.RemoveNode(hepaticLabelmapVolumeNode)
slicer.mrmlScene.RemoveNode(portalLabelmapVolumeNode)

slicer.app.resumeRender()
qt.QApplication.restoreOverrideCursor()
Expand Down
51 changes: 45 additions & 6 deletions LiverVolumetry/LiverVolumetry.py
Original file line number Diff line number Diff line change
Expand Up @@ -136,24 +136,47 @@ def setup(self):
self.ui.InputSegmentSelectorWidget.connect('segmentSelectionChanged(QStringList)', self.onVolumetryParameterChanged)
self.ui.TargetSegmentationSelectorWidget.connect('segmentSelectionChanged(QStringList)', self.updateParameterNodeFromGUI)
self.ui.ComputeVolumePushButton.connect('clicked(bool)', self.onComputeAdvancedVolumeButtonClicked)
self.ui.GenerateSegmentsPushButton.connect('clicked(bool)', self.onGenerateSegmentsButtonClicked)
self.ui.ResectionTargetNodeComboBox.connect('currentNodeChanged(vtkMRMLNode*)', self.onGenerateSegmentsParameterChanged)
self.ui.ROIMarkersListSelector.connect('currentNodeChanged(vtkMRMLNode*)', self.onGenerateSegmentsParameterChanged)

self.addObserver(slicer.mrmlScene, slicer.mrmlScene.StartCloseEvent, self.onSceneStartClose)
self.addObserver(slicer.mrmlScene, slicer.mrmlScene.EndCloseEvent, self.onSceneEndClose)

self.initializeParameterNode()


def onGenerateSegmentsParameterChanged(self):
node2 = self.ui.ROIMarkersListSelector.currentNode()
node3 = self.ui.ReferenceVolumeSelector.currentNode()
node4 = self.ui.InputSegmentationSelector.currentNode()
node5 = self.ui.InputSegmentSelectorWidget.selectedSegmentIDs()
if len(self.ui.InputSegmentSelectorWidget.selectedSegmentIDs()) == 0: node5 = None
self.ui.GenerateSegmentsPushButton.setEnabled(None not in [ node2, node3, node4, node5])

def onGenerateSegmentsButtonClicked(self):
resectionNodes = self.getResectionNodes()
ROIMarkersList = self.ui.ROIMarkersListSelector.currentNode()
segmentsVolumeNode = slicer.mrmlScene.GetFirstNodeByName("segmentVolumeNode")
if not segmentsVolumeNode:
segmentsVolumeNode = slicer.mrmlScene.AddNewNodeByClass("vtkMRMLLabelMapVolumeNode", "segmentVolumeNode")
segmentationNode = self.ui.InputSegmentationSelector.currentNode()
refVolumeNode = self.ui.ReferenceVolumeSelector.currentNode()
segmentationIds = self.ui.InputSegmentSelectorWidget.selectedSegmentIDs()
slicer.modules.segmentations.logic().ExportSegmentsToLabelmapNode(segmentationNode, segmentationIds,
segmentsVolumeNode, refVolumeNode)

self.logic.generateSegments(resectionNodes, ROIMarkersList, segmentsVolumeNode)
slicer.mrmlScene.RemoveNode(segmentsVolumeNode)

def onVolumetryParameterChanged(self):
node1 = self.ui.VolumeTableSelectorWidget.currentNode()
node2 = self.ui.ReferenceVolumeSelector.currentNode()
node3 = self.ui.InputSegmentationSelector.currentNode()
node4 = self.ui.InputSegmentSelectorWidget.selectedSegmentIDs()
if len(self.ui.InputSegmentSelectorWidget.selectedSegmentIDs()) == 0: node4 = None
self.ui.ComputeVolumePushButton.setEnabled(None not in [node1, node2, node3, node4])
self.ui.ComputeVolumePushButton.setEnabled(None not in [ node2, node3, node4])

def onComputeAdvancedVolumeButtonClicked(self):
"""
This function is for compute volume
"""
def getResectionNodes(self):
resectionNodes = vtk.vtkCollection()
if not self.ui.ResectionTargetNodeComboBox.noneChecked():
lvLogic = slicer.modules.liverresections.logic()
Expand All @@ -163,6 +186,13 @@ def onComputeAdvancedVolumeButtonClicked(self):
resectionNodes.AddItem(bs)
else:
resectionNodes = None
return resectionNodes

def onComputeAdvancedVolumeButtonClicked(self):
"""
This function is for compute volume
"""
resectionNodes = self.getResectionNodes()

segmentsVolumeNode = slicer.mrmlScene.GetFirstNodeByName("segmentVolumeNode")
if not segmentsVolumeNode:
Expand Down Expand Up @@ -414,6 +444,15 @@ def computeVolume(self, segmentsVolumeNode, targetSegmentVolumeNode, segmentatio



def generateSegments(self, resectionNodes, ROIMarkersList, segmentsVolumeNode):
generatedSegmentsNode = slicer.mrmlScene.AddNewNodeByClass("vtkMRMLLabelMapVolumeNode")
generatedSegmentsNode.CreateDefaultDisplayNodes()

self.scl.GenerateSegmentsLabelMap(segmentsVolumeNode, generatedSegmentsNode, resectionNodes, ROIMarkersList)

seg = slicer.mrmlScene.AddNewNodeByClass("vtkMRMLSegmentationNode")
slicer.modules.segmentations.logic().ImportLabelmapToSegmentationNode(generatedSegmentsNode, seg)
slicer.mrmlScene.RemoveNode(generatedSegmentsNode)



Expand Down
135 changes: 107 additions & 28 deletions LiverVolumetry/Logic/vtkLiverVolumetryLogic.cxx
Original file line number Diff line number Diff line change
Expand Up @@ -58,6 +58,7 @@
#include <vtkPointData.h>
#include <vtkStringArray.h>
#include <vtkMatrix4x4.h>
#include <vtkIntArray.h>
#include <vtkImageThreshold.h>
#include <vtkImageAccumulate.h>
#include <vtkOrientedImageData.h>
Expand All @@ -82,7 +83,7 @@ void vtkLiverVolumetryLogic::PrintSelf(ostream &os, vtkIndent indent)
}

void vtkLiverVolumetryLogic::ComputeAdvancedPlanningVolumetry(vtkMRMLLabelMapVolumeNode* SelectedSegmentsLabelMap, vtkMRMLTableNode* OutputTableNode, vtkMRMLMarkupsFiducialNode* ROIMarkersList, vtkCollection* ResectionNodes, double TargetSegmentationVolume){
vtkLabelMapHelper::LabelMapType::RegionType TargetSegmentsBoundingBox;

vtkLabelMapHelper::LabelMapType::Pointer TargetSegmentsITKImage;
int baseValue = 100;
double spacing[3];
Expand All @@ -95,39 +96,19 @@ void vtkLiverVolumetryLogic::ComputeAdvancedPlanningVolumetry(vtkMRMLLabelMapVol
return;
}


if (ResectionNodes){
// Project SelectedSegmentsLabelMap from vtkImage to itkImage
// need deep copy the label map

auto LabelRetrievingOnly = vtkLabelMapHelper::VolumeNodeToItkImage(SelectedSegmentsLabelMap, true, false);

auto TargetSegmentImageDataCopy = vtkSmartPointer<vtkImageData>::New();
TargetSegmentImageDataCopy->DeepCopy(SelectedSegmentsLabelMap->GetImageData());
auto TargetSegmentLabelMapCopy = vtkSmartPointer<vtkMRMLLabelMapVolumeNode>::New();
TargetSegmentLabelMapCopy->CopyOrientation(SelectedSegmentsLabelMap);
TargetSegmentLabelMapCopy->SetAndObserveImageData(TargetSegmentImageDataCopy);

auto LabelRetrievingOnly = vtkLabelMapHelper::VolumeNodeToItkImage(SelectedSegmentsLabelMap, true, false);
TargetSegmentsBoundingBox = vtkLabelMapHelper::GetBoundingBox(LabelRetrievingOnly);

auto BezierHR = vtkSmartPointer<vtkBezierSurfaceSource>::New();
if (this->resectionNodes != ResectionNodes && ResectionNodes != nullptr)
{
this->resectionNodes = ResectionNodes;
for (int i = 0; i < this->resectionNodes->GetNumberOfItems(); i++)
{
auto bezierSurfaceNode = vtkMRMLMarkupsBezierSurfaceNode::SafeDownCast(this->resectionNodes->GetItemAsObject(i));
auto Res = GetRes(bezierSurfaceNode, spacing, 300);
if(Res < 500){
Res = 500;
}
BezierHR = GenerateBezierSurface(Res, bezierSurfaceNode);
if(i == 0){
this->ProjectedTargetSegmentImage = vtkLabelMapHelper::VolumeNodeToItkImage(TargetSegmentLabelMapCopy, true, false);
}
vtkLabelMapHelper::ProjectPointsOntoItkImage(this->ProjectedTargetSegmentImage,
BezierHR->GetOutput()->GetPoints(),
baseValue);
}
}
GetResectionsProjectionITKImage(TargetSegmentLabelMapCopy, ResectionNodes, baseValue);

// region growing for every point in the list
if (ROIMarkersList){
Expand Down Expand Up @@ -170,7 +151,6 @@ void vtkLiverVolumetryLogic::ComputeAdvancedPlanningVolumetry(vtkMRMLLabelMapVol
}
}


vtkSmartPointer<vtkBezierSurfaceSource> vtkLiverVolumetryLogic::GenerateBezierSurface(int Res, vtkMRMLMarkupsBezierSurfaceNode* bezierSurfaceNode){
if (!bezierSurfaceNode)
{
Expand Down Expand Up @@ -330,8 +310,6 @@ int vtkLiverVolumetryLogic::GetSegmentVoxels(vtkOrientedImageData *TargetSegment
}
}



std::vector<int> vtkLiverVolumetryLogic::GetROIPointsLabelValue(vtkMRMLLabelMapVolumeNode* SelectedSegmentsLabelMap, vtkMRMLMarkupsFiducialNode* ROIMarkersList){
std::vector<int> re;
vtkLabelMapHelper::LabelMapType::Pointer TargetSegmentsITKImage;
Expand All @@ -358,3 +336,104 @@ std::vector<int> vtkLiverVolumetryLogic::GetROIPointsLabelValue(vtkMRMLLabelMapV
return re;
}

void vtkLiverVolumetryLogic::GetResectionsProjectionITKImage(vtkMRMLLabelMapVolumeNode* TargetSegmentLabelMapCopy,vtkCollection* ResectionNodes, int baseValue){
double spacing[3];
TargetSegmentLabelMapCopy->GetSpacing(spacing);

auto BezierHR = vtkSmartPointer<vtkBezierSurfaceSource>::New();
if (this->resectionNodes != ResectionNodes && ResectionNodes != nullptr)
{
this->resectionNodes = ResectionNodes;
for (int i = 0; i < this->resectionNodes->GetNumberOfItems(); i++)
{
auto bezierSurfaceNode = vtkMRMLMarkupsBezierSurfaceNode::SafeDownCast(this->resectionNodes->GetItemAsObject(i));
auto Res = GetRes(bezierSurfaceNode, spacing, 300);
if(Res < 500){
Res = 500;
}
BezierHR = GenerateBezierSurface(700, bezierSurfaceNode);
if(i == 0){
this->ProjectedTargetSegmentImage = vtkLabelMapHelper::VolumeNodeToItkImage(TargetSegmentLabelMapCopy, true, false);
}
vtkLabelMapHelper::ProjectPointsOntoItkImage(this->ProjectedTargetSegmentImage,
BezierHR->GetOutput()->GetPoints(),
baseValue);
}
}
}

void vtkLiverVolumetryLogic::GenerateSegmentsLabelMap(vtkMRMLLabelMapVolumeNode* SelectedSegmentsLabelMap, vtkMRMLLabelMapVolumeNode* GeneratedSegmentsNode,vtkCollection* ResectionNodes, vtkMRMLMarkupsFiducialNode* ROIMarkersList){
auto ijkras = vtkSmartPointer<vtkMatrix4x4>::New();
auto ImageOrigin = SelectedSegmentsLabelMap->GetOrigin();
auto ImageSpacing = SelectedSegmentsLabelMap->GetSpacing();
SelectedSegmentsLabelMap->GetIJKToRASDirectionMatrix(ijkras);
GeneratedSegmentsNode->SetOrigin(ImageOrigin);
GeneratedSegmentsNode->SetSpacing(ImageSpacing);
GeneratedSegmentsNode->SetIJKToRASDirectionMatrix(ijkras);

auto ROIlabelvalues = GetROIPointsLabelValue(SelectedSegmentsLabelMap, ROIMarkersList);

auto SelectedImage = SelectedSegmentsLabelMap->GetImageData();
auto Newlabelvalue = vtkSmartPointer<vtkIntArray>::New();
Newlabelvalue->DeepCopy(SelectedImage->GetPointData()->GetArray(0));
auto NewImage = vtkSmartPointer<vtkImageData>::New();
NewImage->DeepCopy(SelectedImage);

if (ResectionNodes){

int baseValue = 100;
auto LabelRetrievingOnly = vtkLabelMapHelper::VolumeNodeToItkImage(SelectedSegmentsLabelMap, true, false);

auto TargetSegmentImageDataCopy = vtkSmartPointer<vtkImageData>::New();
TargetSegmentImageDataCopy->DeepCopy(SelectedSegmentsLabelMap->GetImageData());
auto TargetSegmentLabelMapCopy = vtkSmartPointer<vtkMRMLLabelMapVolumeNode>::New();
TargetSegmentLabelMapCopy->CopyOrientation(SelectedSegmentsLabelMap);
TargetSegmentLabelMapCopy->SetAndObserveImageData(TargetSegmentImageDataCopy);

GetResectionsProjectionITKImage(TargetSegmentLabelMapCopy, ResectionNodes, baseValue);

vtkSmartPointer<vtkLabelMapHelper> labelMapHelper = vtkSmartPointer<vtkLabelMapHelper>::New();
for(int i = 0; i<ROIMarkersList->GetNumberOfControlPoints();i++){
double point[3];
ROIMarkersList->GetNthControlPointPosition(i, point);
auto pointLabel = ROIMarkersList->GetNthControlPointLabel(i);
this->connectedThreshold = nullptr;
if(this->resectionNodes != nullptr)
{
auto seedIndex = GetITKRGSeedIndex(point, LabelRetrievingOnly);
int LabelValue = LabelRetrievingOnly->GetPixel(seedIndex);
this->connectedThreshold = labelMapHelper->ConnectedThreshold(this->ProjectedTargetSegmentImage, 1, baseValue-1, baseValue+i, seedIndex);

int Count = 0;
typedef itk::ImageRegionConstIterator<itk::Image<short, 3> > IteratorType;
IteratorType iterator(LabelRetrievingOnly, LabelRetrievingOnly->GetRequestedRegion());
while (!iterator.IsAtEnd())
{
auto index = iterator.GetIndex();
if (iterator.Get() != 0)
{
if (this->connectedThreshold->GetPixel(index) == baseValue+i && LabelRetrievingOnly->GetPixel(index) == LabelValue){
Newlabelvalue->SetTuple1(Count,baseValue+i);
} else if (Newlabelvalue->GetTuple1(Count) < baseValue) {
Newlabelvalue->SetTuple1(Count,99);
}
}
++iterator;
Count++;
}
}
}
} else {
auto LabelValue = SelectedImage->GetPointData()->GetArray(0);
for (int i = 0; i< LabelValue->GetNumberOfValues(); i ++){
int v = static_cast<int>(LabelValue->GetTuple(i)[0]);
if (std::find(ROIlabelvalues.begin(), ROIlabelvalues.end(), v) != ROIlabelvalues.end() || v == 0) {
Newlabelvalue->SetTuple1(i,v);
} else {
Newlabelvalue->SetTuple1(i,99);
}
}
}
NewImage->GetPointData()->SetScalars(Newlabelvalue);
GeneratedSegmentsNode->SetAndObserveImageData(NewImage);
}
2 changes: 2 additions & 0 deletions LiverVolumetry/Logic/vtkLiverVolumetryLogic.h
Original file line number Diff line number Diff line change
Expand Up @@ -79,6 +79,8 @@ vtkLiverVolumetryLogic : public vtkObject {
itk::Index<3> GetITKRGSeedIndex(double *ROISeedPoint, itk::SmartPointer<itk::Image<short, 3>> SourceImage);
void VolumetryTable(std::string Properties, double TargetSegmentationVolume, int ROIVoxels, double ROIVolume, vtkMRMLTableNode *OutputTableNode);
int GetRes(vtkMRMLMarkupsBezierSurfaceNode *bezierSurfaceNode, double space[3], int Steps);
void GetResectionsProjectionITKImage(vtkMRMLLabelMapVolumeNode* SelectedSegmentsLabelMap,vtkCollection* ResectionNodes, int baseValue);
void GenerateSegmentsLabelMap(vtkMRMLLabelMapVolumeNode* TargetSegmentLabelMapCopy, vtkMRMLLabelMapVolumeNode* newLabelMap,vtkCollection* ResectionNodes, vtkMRMLMarkupsFiducialNode* ROIMarkersList);

protected:
itk::SmartPointer<itk::Image<short, 3>> ProjectedTargetSegmentImage;
Expand Down
Loading

0 comments on commit 3f65c92

Please sign in to comment.