Skip to content

Commit

Permalink
ENH: Expose segment conversion paths in Python
Browse files Browse the repository at this point in the history
Previously, complex STL containers were used that were not Python-wrappable. Now conversion paths and parameters are all VTK objects.
This allows converting segmentations using a custom path, when there are multiple conversion paths are available.
For example, multiple conversion paths are available after installing SlicerRT extension, which provides additional segment representations and conversion rules.
  • Loading branch information
lassoan committed Aug 22, 2022
1 parent a3b7544 commit 693c819
Show file tree
Hide file tree
Showing 20 changed files with 1,061 additions and 318 deletions.
4 changes: 4 additions & 0 deletions Libs/vtkSegmentationCore/CMakeLists.txt
Original file line number Diff line number Diff line change
Expand Up @@ -39,6 +39,10 @@ set(vtkSegmentationCore_SRCS
vtkSegment.h
vtkSegmentation.cxx
vtkSegmentation.h
vtkSegmentationConversionParameters.cxx
vtkSegmentationConversionParameters.h
vtkSegmentationConversionPath.cxx
vtkSegmentationConversionPath.h
vtkSegmentationConverter.cxx
vtkSegmentationConverter.h
vtkSegmentationConverterFactory.cxx
Expand Down
68 changes: 33 additions & 35 deletions Libs/vtkSegmentationCore/Testing/vtkSegmentationConverterTest1.cxx
Original file line number Diff line number Diff line change
Expand Up @@ -97,11 +97,23 @@ RULE(C, E, 4);
RULE(D, E, 2);
RULE(E, D, 1);

void PrintPath(const vtkSegmentationConverter::ConversionPathType& path)
void PrintPath(vtkSegmentationConversionPath* path)
{
for (vtkSegmentationConverter::ConversionPathType::const_iterator ruleIt = path.begin(); ruleIt != path.end(); ++ruleIt)
for (int i=0; i < path->GetNumberOfRules(); i++)
{
std::cout << " " << (*ruleIt)->GetName() << "(" << (*ruleIt)->GetConversionCost() << ")" << std::endl;
vtkSegmentationConverterRule* rule = path->GetRule(i);
std::cout << " " << rule->GetName() << "(" << rule->GetConversionCost() << ")" << std::endl;
}
}

void PrintPaths(vtkSegmentationConversionPaths* paths)
{
vtkSegmentationConversionPath* path = nullptr;
vtkCollectionSimpleIterator it;
for (paths->InitTraversal(it); (path = paths->GetNextPath(it));)
{
std::cout << " Path: (total cost = " << path->GetCost() << ")" << std::endl;
PrintPath(path);
}
}

Expand Down Expand Up @@ -145,58 +157,44 @@ int vtkSegmentationConverterTest1(int vtkNotUsed(argc), char* vtkNotUsed(argv)[]

vtkSmartPointer<vtkSegmentationConverter> converter = vtkSmartPointer<vtkSegmentationConverter>::New();

vtkSegmentationConverter::ConversionPathAndCostListType pathsCosts;
vtkSegmentationConverter::ConversionPathType shortestPath;

// A->E paths: ABCE, ABCDE, ADE
std::cout << "Conversion from RepA to RepE" << std::endl;
std::cout << " All paths:" << std::endl;
converter->GetPossibleConversions("RepA", "RepE", pathsCosts);
for (vtkSegmentationConverter::ConversionPathAndCostListType::iterator pathsCostsIt = pathsCosts.begin(); pathsCostsIt != pathsCosts.end(); ++pathsCostsIt)
{
std::cout << " Path: (total cost = " << pathsCostsIt->second << ")" << std::endl;
PrintPath(pathsCostsIt->first);
}
VERIFY_EQUAL("number of paths from representation A to E", pathsCosts.size(), 3);
vtkNew<vtkSegmentationConversionPaths> paths;
converter->GetPossibleConversions("RepA", "RepE", paths);
PrintPaths(paths);
VERIFY_EQUAL("number of paths from representation A to E", paths->GetNumberOfPaths(), 3);
std::cout << " Cheapest path:" << std::endl;
shortestPath = vtkSegmentationConverter::GetCheapestPath(pathsCosts);
vtkSegmentationConversionPath* shortestPath = vtkSegmentationConverter::GetCheapestPath(paths);
PrintPath(shortestPath);
VERIFY_EQUAL("number of paths from representation A to E", shortestPath.size(), 3);
VERIFY_EQUAL("minimum number of rules from representation A to E", shortestPath->GetNumberOfRules(), 3);

// E->A paths: none
std::cout << "Conversion from RepE to RepA" << std::endl;
converter->GetPossibleConversions("RepE", "RepA", pathsCosts);
VERIFY_EQUAL("number of paths from representation E to A", pathsCosts.size(), 0);
converter->GetPossibleConversions("RepE", "RepA", paths);
VERIFY_EQUAL("number of paths from representation E to A", paths->GetNumberOfPaths(), 0);

// B->D paths: BAD, BCD, BCED
std::cout << "Conversion from RepB to RepD" << std::endl;
std::cout << " All paths:" << std::endl;
converter->GetPossibleConversions("RepB", "RepD", pathsCosts);
for (vtkSegmentationConverter::ConversionPathAndCostListType::iterator pathsCostsIt = pathsCosts.begin(); pathsCostsIt != pathsCosts.end(); ++pathsCostsIt)
{
std::cout << " Path: (total cost = " << pathsCostsIt->second << ")" << std::endl;
PrintPath(pathsCostsIt->first);
}
VERIFY_EQUAL("number of paths from representation B to D", pathsCosts.size(), 3);
converter->GetPossibleConversions("RepB", "RepD", paths);
PrintPaths(paths);
VERIFY_EQUAL("number of paths from representation B to D", paths->GetNumberOfPaths(), 3);
std::cout << " Cheapest path:" << std::endl;
shortestPath = vtkSegmentationConverter::GetCheapestPath(pathsCosts);
shortestPath = vtkSegmentationConverter::GetCheapestPath(paths);
PrintPath(shortestPath);
VERIFY_EQUAL("number of paths from representation B to D", shortestPath.size(), 2);
VERIFY_EQUAL("number of paths from representation B to D", shortestPath->GetNumberOfRules(), 2);

// C->D paths: CD, CED
std::cout << "Conversion from RepC to RepD" << std::endl;
std::cout << " All paths:" << std::endl;
converter->GetPossibleConversions("RepC", "RepD", pathsCosts);
for (vtkSegmentationConverter::ConversionPathAndCostListType::iterator pathsCostsIt = pathsCosts.begin(); pathsCostsIt != pathsCosts.end(); ++pathsCostsIt)
{
std::cout << " Path: (total cost = " << pathsCostsIt->second << ")" << std::endl;
PrintPath(pathsCostsIt->first);
}
VERIFY_EQUAL("number of paths from representation C to D", pathsCosts.size(), 2);
converter->GetPossibleConversions("RepC", "RepD", paths);
PrintPaths(paths);
VERIFY_EQUAL("number of paths from representation C to D", paths->GetNumberOfPaths(), 2);
std::cout << " Cheapest path:" << std::endl;
shortestPath = vtkSegmentationConverter::GetCheapestPath(pathsCosts);
shortestPath = vtkSegmentationConverter::GetCheapestPath(paths);
PrintPath(shortestPath);
VERIFY_EQUAL("number of paths from representation C to D", shortestPath.size(), 1);
VERIFY_EQUAL("number of paths from representation C to D", shortestPath->GetNumberOfRules(), 1);

std::cout << "Test passed." << std::endl;
return EXIT_SUCCESS;
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -69,15 +69,15 @@ vtkSegmentationConverterRuleNewMacro(vtkBinaryLabelmapToClosedSurfaceConversionR
//----------------------------------------------------------------------------
vtkBinaryLabelmapToClosedSurfaceConversionRule::vtkBinaryLabelmapToClosedSurfaceConversionRule()
{
this->ConversionParameters[GetDecimationFactorParameterName()] = std::make_pair("0.0",
this->ConversionParameters->SetParameter(GetDecimationFactorParameterName(), "0.0",
"Desired reduction in the total number of polygons. Range: 0.0 (no decimation) to 1.0 (as much simplification as possible)."
" Value of 0.8 typically reduces data set size by 80% without losing too much details.");
this->ConversionParameters[GetSmoothingFactorParameterName()] = std::make_pair("0.5",
this->ConversionParameters->SetParameter(GetSmoothingFactorParameterName(), "0.5",
"Smoothing factor. Range: 0.0 (no smoothing) to 1.0 (strong smoothing).");
this->ConversionParameters[GetComputeSurfaceNormalsParameterName()] = std::make_pair("1",
this->ConversionParameters->SetParameter(GetComputeSurfaceNormalsParameterName(), "1",
"Compute surface normals. 1 (default) = surface normals are computed. "
"0 = surface normals are not computed (slightly faster but produces less smooth surface display).");
this->ConversionParameters[GetJointSmoothingParameterName()] = std::make_pair("0",
this->ConversionParameters->SetParameter(GetJointSmoothingParameterName(), "0",
"Perform joint smoothing.");
}

Expand Down Expand Up @@ -150,8 +150,8 @@ bool vtkBinaryLabelmapToClosedSurfaceConversionRule::Convert(vtkSegment* segment
return false;
}

double smoothingFactor = vtkVariant(this->ConversionParameters[GetSmoothingFactorParameterName()].first).ToDouble();
int jointSmoothing = vtkVariant(this->ConversionParameters[GetJointSmoothingParameterName()].first).ToInt();
double smoothingFactor = this->ConversionParameters->GetValueAsDouble(GetSmoothingFactorParameterName());
int jointSmoothing = this->ConversionParameters->GetValueAsInt(GetJointSmoothingParameterName());

if (jointSmoothing > 0 && smoothingFactor > 0)
{
Expand Down Expand Up @@ -285,9 +285,9 @@ bool vtkBinaryLabelmapToClosedSurfaceConversionRule::CreateClosedSurface(vtkOrie
binaryLabelmapWithIdentityGeometry->SetSpacing(1.0, 1.0, 1.0);

// Get conversion parameters
double decimationFactor = vtkVariant(this->ConversionParameters[GetDecimationFactorParameterName()].first).ToDouble();
double smoothingFactor = vtkVariant(this->ConversionParameters[GetSmoothingFactorParameterName()].first).ToDouble();
int computeSurfaceNormals = vtkVariant(this->ConversionParameters[GetComputeSurfaceNormalsParameterName()].first).ToInt();
double decimationFactor = this->ConversionParameters->GetValueAsDouble(GetDecimationFactorParameterName());
double smoothingFactor = this->ConversionParameters->GetValueAsDouble(GetSmoothingFactorParameterName());
int computeSurfaceNormals = this->ConversionParameters->GetValueAsInt(GetComputeSurfaceNormalsParameterName());

vtkNew<vtkDiscreteFlyingEdges3D> marchingCubes;
marchingCubes->SetInputData(binaryLabelmapWithIdentityGeometry);
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -57,19 +57,19 @@ vtkClosedSurfaceToBinaryLabelmapConversionRule::vtkClosedSurfaceToBinaryLabelmap
this->ReplaceTargetRepresentation = true;

// Reference image geometry parameter
this->ConversionParameters[vtkSegmentationConverter::GetReferenceImageGeometryParameterName()] = std::make_pair("",
this->ConversionParameters->SetParameter(vtkSegmentationConverter::GetReferenceImageGeometryParameterName(), "",
"Image geometry description string determining the geometry of the labelmap that is created in course of conversion."
" Can be copied from a volume, using the button.");
// Oversampling factor parameter
this->ConversionParameters[GetOversamplingFactorParameterName()] = std::make_pair("1",
this->ConversionParameters->SetParameter(GetOversamplingFactorParameterName(), "1",
"Determines the oversampling of the reference image geometry. If it's a number, then all segments are oversampled"
" with the same value (value of 1 means no oversampling). If it has the value \"A\", then automatic oversampling is calculated.");
// Crop to reference geometry parameter
this->ConversionParameters[GetCropToReferenceImageGeometryParameterName()] = std::make_pair("0",
this->ConversionParameters->SetParameter(GetCropToReferenceImageGeometryParameterName(), "0",
"Crop the model to the extent of reference geometry. 0 (default) = created labelmap will contain the entire model."
" 1 = created labelmap extent will be within reference image extent.");
// Collapse labelmaps parameter
this->ConversionParameters[GetCollapseLabelmapsParameterName()] = std::make_pair("1",
this->ConversionParameters->SetParameter(GetCollapseLabelmapsParameterName(), "1",
"Merge the labelmaps into as few shared labelmaps as possible"
" 1 = created labelmaps will be shared if possible without overwriting each other.");
}
Expand Down Expand Up @@ -255,7 +255,7 @@ bool vtkClosedSurfaceToBinaryLabelmapConversionRule::Convert(vtkSegment* segment
//----------------------------------------------------------------------------
bool vtkClosedSurfaceToBinaryLabelmapConversionRule::PostConvert(vtkSegmentation* segmentation)
{
int collapseLabelmaps = vtkVariant(this->ConversionParameters[GetCollapseLabelmapsParameterName()].first).ToInt();
int collapseLabelmaps = this->ConversionParameters->GetValueAsInt(GetCollapseLabelmapsParameterName());
if (collapseLabelmaps > 0)
{
segmentation->CollapseBinaryLabelmaps(false);
Expand All @@ -278,7 +278,7 @@ bool vtkClosedSurfaceToBinaryLabelmapConversionRule::CalculateOutputGeometry(vtk
}

// Get reference image geometry from parameters
std::string geometryString = this->ConversionParameters[vtkSegmentationConverter::GetReferenceImageGeometryParameterName()].first;
std::string geometryString = this->ConversionParameters->GetValue(vtkSegmentationConverter::GetReferenceImageGeometryParameterName());
if (geometryString.empty() || !vtkSegmentationConverter::DeserializeImageGeometry(geometryString, geometryImageData))
{
geometryString = this->GetDefaultImageGeometryStringForPolyData(closedSurfacePolyData);
Expand All @@ -299,7 +299,7 @@ bool vtkClosedSurfaceToBinaryLabelmapConversionRule::CalculateOutputGeometry(vtk
}

// Get oversampling factor
std::string oversamplingString = this->ConversionParameters[GetOversamplingFactorParameterName()].first;
std::string oversamplingString = this->ConversionParameters->GetValue(GetOversamplingFactorParameterName());
double oversamplingFactor = 1.0;
if (!oversamplingString.compare("A"))
{
Expand Down Expand Up @@ -334,7 +334,7 @@ bool vtkClosedSurfaceToBinaryLabelmapConversionRule::CalculateOutputGeometry(vtk

int cropToReferenceImageGeometry = 0;
{
std::string cropToReferenceImageGeometryString = this->ConversionParameters[GetCropToReferenceImageGeometryParameterName()].first;
std::string cropToReferenceImageGeometryString = this->ConversionParameters->GetValue(GetCropToReferenceImageGeometryParameterName());
std::stringstream ss;
ss << cropToReferenceImageGeometryString;
ss >> cropToReferenceImageGeometry;
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -47,8 +47,10 @@ vtkSegmentationConverterRuleNewMacro(vtkFractionalLabelmapToClosedSurfaceConvers
vtkFractionalLabelmapToClosedSurfaceConversionRule::vtkFractionalLabelmapToClosedSurfaceConversionRule()
: vtkBinaryLabelmapToClosedSurfaceConversionRule()
{
this->ConversionParameters[this->GetFractionalLabelMapOversamplingFactorParameterName()] = std::make_pair("1", "Determines the oversampling of the reference image geometry. All segments are oversampled with the same value (value of 1 means no oversampling).");
this->ConversionParameters[this->GetThresholdFractionParameterName()] = std::make_pair("0.5", "Determines the threshold that the closed surface is created at as a fractional value between 0 and 1.");
this->ConversionParameters->SetParameter(this->GetFractionalLabelMapOversamplingFactorParameterName(), "1",
"Determines the oversampling of the reference image geometry. All segments are oversampled with the same value (value of 1 means no oversampling).");
this->ConversionParameters->SetParameter(this->GetThresholdFractionParameterName(), "0.5",
"Determines the threshold that the closed surface is created at as a fractional value between 0 and 1.");
}

//----------------------------------------------------------------------------
Expand Down Expand Up @@ -144,11 +146,11 @@ bool vtkFractionalLabelmapToClosedSurfaceConversionRule::Convert(vtkSegment* seg
}

// Get conversion parameters
double decimationFactor = vtkVariant(this->ConversionParameters[this->GetDecimationFactorParameterName()].first).ToDouble();
double smoothingFactor = vtkVariant(this->ConversionParameters[this->GetSmoothingFactorParameterName()].first).ToDouble();
int computeSurfaceNormals = vtkVariant(this->ConversionParameters[GetComputeSurfaceNormalsParameterName()].first).ToInt();
double fractionalOversamplingFactor = vtkVariant(this->ConversionParameters[this->GetFractionalLabelMapOversamplingFactorParameterName()].first).ToDouble();
double fractionalThreshold = vtkVariant(this->ConversionParameters[this->GetThresholdFractionParameterName()].first).ToDouble();
double decimationFactor = this->ConversionParameters->GetValueAsDouble(this->GetDecimationFactorParameterName());
double smoothingFactor = this->ConversionParameters->GetValueAsDouble(this->GetSmoothingFactorParameterName());
int computeSurfaceNormals = this->ConversionParameters->GetValueAsInt(GetComputeSurfaceNormalsParameterName());
double fractionalOversamplingFactor = this->ConversionParameters->GetValueAsDouble(this->GetFractionalLabelMapOversamplingFactorParameterName());
double fractionalThreshold = this->ConversionParameters->GetValueAsDouble(this->GetThresholdFractionParameterName());

if (fractionalThreshold < 0 || fractionalThreshold > 1)
{
Expand Down
Loading

0 comments on commit 693c819

Please sign in to comment.