Dear 3D Slicer Community,
I am a learner of Slicer module development. I am writing to inquire about the underlying principle/algorithm behind the “Auto” option in the Control Mode of the qMRMLVolumeThresholdWidget.
Through tracing the source code in qMRMLVolumeThresholdWidget.cxx and vtkMRMLScalarVolumeDisplayNode.cxx on GitHub, it appears that the following code snippet in the vtkMRMLScalarVolumeDisplayNode.cxx explains the basis of this “Auto” option:
void vtkMRMLScalarVolumeDisplayNode::CalculateAutoLevels()
{
if (!this->GetAutoWindowLevel() && !this->GetAutoThreshold())
{
vtkDebugMacro("CalculateScalarAutoLevels: " << (this->GetID() == nullptr ? "nullid" : this->GetID())
<< ": Auto window level not turned on, returning.");
return;
}
vtkImageData *imageDataScalar = this->GetScalarImageData();
if (!imageDataScalar)
{
vtkDebugMacro("CalculateScalarAutoLevels: input image data is null");
return;
}
// Make sure the point data is up to date.
// Remember, the display node pipeline is not connected to a consumer (volume
// display nodes are cloned by the slice logic) therefore no-one has run the
// filters.
if (this->GetInputImageData())
{
this->GetScalarImageDataConnection()->GetProducer()->Update();
}
if (!(imageDataScalar->GetPointData()) ||
!(imageDataScalar->GetPointData()->GetScalars()))
{
vtkDebugMacro("CalculateScalarAutoLevels: input image data is null");
return;
}
if (this->HistogramStatistics == nullptr)
{
this->HistogramStatistics = vtkImageHistogramStatistics::New();
// Set automatic window/level to include the entire intensity range
// (except top/bottom 0.1%, to not let a very thin tail of the intensity
// distribution to decrease the image contrast too much).
// While in CT and sometimes in MRI, there may be a large empty area
// outside the reconstructed image, which could be suppressed
// by a larger lower percentile value, it would make the method
// too specific to particular imaging modalities and could lead to
// suboptimal results for other types of images.
// Therefore, we choose small, symmetric percentile values here
// and maybe add modality-specific methods later (e.g., for CT
// images we could set lower value to -1000HU).
this->HistogramStatistics->SetAutoRangePercentiles(0.1, 99.9);
// Percentiles are very low (0.1%), so there is no need for
// range expansion.
this->HistogramStatistics->SetAutoRangeExpansionFactors(0.0, 0.0);
}
this->IsInCalculateAutoLevels = true;
this->HistogramStatistics->SetInputData(imageDataScalar);
this->HistogramStatistics->Update();
double* intensityRange = this->HistogramStatistics->GetAutoRange();
vtkDebugMacro("CalculateScalarAutoLevels:"
<< " lower: " << intensityRange[0] << " upper: " << intensityRange[1]);
int disabledModify = this->StartModify();
if (this->GetAutoWindowLevel())
{
this->SetWindowLevelMinMax(intensityRange[0], intensityRange[1]);
}
if (this->GetAutoThreshold())
{
this->SetThreshold(intensityRange[0], intensityRange[1]);
}
this->EndModify(disabledModify);
this->IsInCalculateAutoLevels = false;
}
If my tracking and understanding are correct, the above code snippet appears to primarily perform the following tasks::
- After loading the image data, ensure that the point data (scalar values) of the image are up to date.
- Utilize VTK’s vtkImageHistogramStatistics class to compute the grayscale histogram statistics of the image data, setting the percentiles for the automatic range computation to (0.1, 99.9). This implies that the histogram statistics will includes all values of image intensity except the top/bottom 0.1%.
- Update the histogram statistics and invoke the ‘GetAutoRange’ method of the vtkImageHistogramStatistics class to obtain the resulting threshold range.
So, it seems that the algorithm behind this ‘Auto’ option is based on custom percentile ranges for the image’s histogram statistics rather than methods like Otsu’s method?
Could you please confirm if my understanding above is correct? Additionally, I would greatly appreciate any further explanation or supplementary details regarding the underlying principle/algorithm of this “Auto” option.
Thank you very much!