Skip to content

Commit

Permalink
BUG: Make segmentation .seg.nrrd file reading more robust
Browse files Browse the repository at this point in the history
1. Fixed extent computation when reading from a segmentation that had shared labelmaps. Extent of the first segment in that layer was used. Instead, union of extents of all the segments in that layer should be used.
2. Fixed parsing error of Segmentation_ContainedRepresentationNames and SegmentN_Tags: previously, a "|" was required at the end of the list (e.g., "Binary labelmap|Closed surface|"). Now the closing separator is optional.
  • Loading branch information
lassoan committed May 10, 2024
1 parent bc275c4 commit f1ddca9
Show file tree
Hide file tree
Showing 2 changed files with 82 additions and 49 deletions.
129 changes: 80 additions & 49 deletions Libs/MRML/Core/vtkMRMLSegmentationStorageNode.cxx
Original file line number Diff line number Diff line change
Expand Up @@ -74,7 +74,7 @@
#include <sstream>

//----------------------------------------------------------------------------
static const std::string SERIALIZATION_SEPARATOR = "|";
static const char LIST_SEPARATOR = '|';
static const std::string KEY_SEGMENT_ID = "ID";
static const std::string KEY_SEGMENT_NAME = "Name";
static const std::string KEY_SEGMENT_COLOR = "Color";
Expand Down Expand Up @@ -862,35 +862,42 @@ int vtkMRMLSegmentationStorageNode::ReadBinaryLabelmapRepresentation(vtkMRMLSegm
{
currentBinaryLabelmap = vtkSmartPointer<vtkOrientedImageData>::New();

// Extent
int currentSegmentExtent[6] = { 0, -1, 0, -1, 0, -1 };
std::string currentExtentString;
if (this->GetSegmentMetaDataFromDicitionary(currentExtentString, dictionary, segmentIndex, KEY_SEGMENT_EXTENT))
// Get layer extent: union of all segment extents in this layer
int layerExtent[6] = { 0, -1, 0, -1, 0, -1 };
for (int segmentIndexForExtent : segmentIndexInLayer[frameIndex])
{
GetImageExtentFromString(currentSegmentExtent, currentExtentString);
}
else
{
vtkWarningToMessageCollectionMacro(this->GetUserMessages(), "vtkMRMLSegmentationStorageNode::ReadBinaryLabelmapRepresentation",
"Segment extent is missing for segment " << segmentIndex);
for (int i = 0; i < 6; i++)
int currentSegmentExtent[6] = { 0, -1, 0, -1, 0, -1 };
std::string currentExtentString;
if (this->GetSegmentMetaDataFromDicitionary(currentExtentString, dictionary, segmentIndexForExtent, KEY_SEGMENT_EXTENT))
{
currentSegmentExtent[i] = imageExtentInFile[i];
GetImageExtentFromString(currentSegmentExtent, currentExtentString);
vtkMRMLSegmentationStorageNode::AddToExtent(layerExtent, currentSegmentExtent);
}
else
{
// Extent is not specified, we don't know the extent of this layer, so use the entire region
vtkWarningToMessageCollectionMacro(this->GetUserMessages(), "vtkMRMLSegmentationStorageNode::ReadBinaryLabelmapRepresentation",
"Segment extent is missing for segment " << segmentIndexForExtent);
for (int i = 0; i < 6; i++)
{
layerExtent[i] = imageExtentInFile[i];
}
break;
}
}
for (int i = 0; i < 3; i++)
{
currentSegmentExtent[i * 2] += referenceImageExtentOffset[i];
currentSegmentExtent[i * 2 + 1] += referenceImageExtentOffset[i];
layerExtent[i * 2] += referenceImageExtentOffset[i];
layerExtent[i * 2 + 1] += referenceImageExtentOffset[i];
}
// Copy with clipping to specified extent
if (currentSegmentExtent[0] <= currentSegmentExtent[1]
&& currentSegmentExtent[2] <= currentSegmentExtent[3]
&& currentSegmentExtent[4] <= currentSegmentExtent[5])
if (layerExtent[0] <= layerExtent[1]
&& layerExtent[2] <= layerExtent[3]
&& layerExtent[4] <= layerExtent[5])
{
// non-empty segment
extractComponents->SetComponents(frameIndex);
padder->SetOutputWholeExtent(currentSegmentExtent);
padder->SetOutputWholeExtent(layerExtent);
padder->Update();
currentBinaryLabelmap->DeepCopy(padder->GetOutput());
}
Expand All @@ -899,10 +906,10 @@ int vtkMRMLSegmentationStorageNode::ReadBinaryLabelmapRepresentation(vtkMRMLSegm
// empty segment
for (int i = 0; i < 3; ++i)
{
currentSegmentExtent[2 * i] = 0;
currentSegmentExtent[2 * i + 1] = -1;
layerExtent[2 * i] = 0;
layerExtent[2 * i + 1] = -1;
}
currentBinaryLabelmap->SetExtent(currentSegmentExtent);
currentBinaryLabelmap->SetExtent(layerExtent);
currentBinaryLabelmap->AllocateScalars(VTK_UNSIGNED_CHAR, 1);
}
currentBinaryLabelmap->SetImageToWorldMatrix(imageToWorldMatrix.GetPointer());
Expand Down Expand Up @@ -1009,6 +1016,43 @@ int vtkMRMLSegmentationStorageNode::ReadBinaryLabelmapRepresentation(vtkMRMLSegm
return 1;
}

//----------------------------------------------------------------------------
void vtkMRMLSegmentationStorageNode::AddToExtent(int extent[6], int extentToAdd[6])
{
if (extentToAdd[0] > extentToAdd[1]
|| extentToAdd[2] > extentToAdd[3]
|| extentToAdd[4] > extentToAdd[5])
{
// extentToAdd is empty, no need to change the extent
return;
}
if (extent[0] > extent[1]
|| extent[2] > extent[3]
|| extent[4] > extent[5])
{
// initial extent is empty, use the added extent
for (int i = 0; i < 6; i++)
{
extent[i] = extentToAdd[i];
}
}
else
{
// add extentToAdd to extent
for (int i = 0; i < 3; i++)
{
if (extentToAdd[i * 2] < extent[i * 2])
{
extent[i * 2] = extentToAdd[i * 2];
}
if (extentToAdd[i * 2 + 1] > extent[i * 2 + 1])
{
extent[i * 2 + 1] = extentToAdd[i * 2 + 1];
}
}
}
}

//----------------------------------------------------------------------------
int vtkMRMLSegmentationStorageNode::ReadPolyDataRepresentation(vtkMRMLSegmentationNode* segmentationNode, std::string path)
{
Expand Down Expand Up @@ -1169,20 +1213,16 @@ int vtkMRMLSegmentationStorageNode::ReadPolyDataRepresentation(vtkMRMLSegmentati
currentPolyData->GetFieldData()->GetAbstractArray(GetSegmentMetaDataKey(SINGLE_SEGMENT_INDEX, KEY_SEGMENT_TAGS).c_str()) );
if (tagsArray)
{
std::string tags(tagsArray->GetValue(0).c_str());
std::string separatorCharacter("|");
size_t separatorPosition = tags.find(separatorCharacter);
while (separatorPosition != std::string::npos)
std::stringstream tagsValueStr(tagsArray->GetValue(0));
std::string tagValue;
while (getline(tagsValueStr, tagValue, LIST_SEPARATOR))
{
std::string mapPairStr = tags.substr(0, separatorPosition);
size_t colonPosition = mapPairStr.find(":");
size_t colonPosition = tagValue.find(":");
if (colonPosition == std::string::npos)
{
continue;
}
currentSegment->SetTag(mapPairStr.substr(0, colonPosition), mapPairStr.substr(colonPosition+1));
tags = tags.substr(separatorPosition+1);
separatorPosition = tags.find(separatorCharacter);
currentSegment->SetTag(tagValue.substr(0, colonPosition), tagValue.substr(colonPosition + 1));
}
}

Expand Down Expand Up @@ -1679,7 +1719,7 @@ std::string vtkMRMLSegmentationStorageNode::SerializeContainedRepresentationName
for (std::vector<std::string>::iterator reprIt = containedRepresentationNames.begin();
reprIt != containedRepresentationNames.end(); ++reprIt)
{
ssRepresentationNames << (*reprIt) << SERIALIZATION_SEPARATOR;
ssRepresentationNames << (*reprIt) << LIST_SEPARATOR;
}

return ssRepresentationNames.str();
Expand All @@ -1705,19 +1745,15 @@ void vtkMRMLSegmentationStorageNode::CreateRepresentationsBySerializedNames(vtkS
}

std::string sourceRepresentation(segmentation->GetSourceRepresentationName());
size_t separatorPosition = representationNames.find(SERIALIZATION_SEPARATOR);
while (separatorPosition != std::string::npos)
std::string representationName;
std::stringstream representationNamesStr(representationNames);
while (getline(representationNamesStr, representationName, LIST_SEPARATOR))
{
std::string representationName = representationNames.substr(0, separatorPosition);

// Only create non-source representations
if (representationName.compare(sourceRepresentation))
{
segmentation->CreateRepresentation(representationName);
}

representationNames = representationNames.substr(separatorPosition+1);
separatorPosition = representationNames.find(SERIALIZATION_SEPARATOR);
}
}

Expand Down Expand Up @@ -1787,21 +1823,16 @@ std::string vtkMRMLSegmentationStorageNode::GetSegmentTagsAsString(vtkSegment* s
//----------------------------------------------------------------------------
void vtkMRMLSegmentationStorageNode::SetSegmentTagsFromString(vtkSegment* segment, std::string tagsValue)
{
std::string separatorCharacter("|");
size_t separatorPosition = tagsValue.find(separatorCharacter);
while (separatorPosition != std::string::npos)
std::stringstream tagsValueStr(tagsValue);
std::string tagValue;
while (getline(tagsValueStr, tagValue, LIST_SEPARATOR))
{
std::string mapPairStr = tagsValue.substr(0, separatorPosition);
size_t colonPosition = mapPairStr.find(":");
size_t colonPosition = tagValue.find(":");
if (colonPosition == std::string::npos)
{
tagsValue = tagsValue.substr(separatorPosition + 1);
separatorPosition = tagsValue.find(separatorCharacter);
continue;
}
segment->SetTag(mapPairStr.substr(0, colonPosition), mapPairStr.substr(colonPosition + 1));
tagsValue = tagsValue.substr(separatorPosition + 1);
separatorPosition = tagsValue.find(separatorCharacter);
segment->SetTag(tagValue.substr(0, colonPosition), tagValue.substr(colonPosition + 1));
}
}

Expand Down
2 changes: 2 additions & 0 deletions Libs/MRML/Core/vtkMRMLSegmentationStorageNode.h
Original file line number Diff line number Diff line change
Expand Up @@ -174,6 +174,8 @@ class VTK_MRML_EXPORT vtkMRMLSegmentationStorageNode : public vtkMRMLStorageNode
static std::string GetSegmentColorAsString(vtkMRMLSegmentationNode* segmentationNode, const std::string& segmentId);
static void GetSegmentColorFromString(double color[3], std::string colorString);

static void AddToExtent(int extent[6], int extentToAdd[6]);

protected:
bool CropToMinimumExtent{false};

Expand Down

0 comments on commit f1ddca9

Please sign in to comment.