Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

BUG: Fix color synchronization inaccuarcy in volume rendering module #7042

Merged
merged 1 commit into from Jun 27, 2023
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Jump to
Jump to file
Failed to load files.
Diff view
Diff view
Expand Up @@ -488,80 +488,108 @@ void vtkSlicerVolumeRenderingLogic::SetThresholdToVolumeProp(

//----------------------------------------------------------------------------
void vtkSlicerVolumeRenderingLogic::SetWindowLevelToVolumeProp(
double scalarRange[2], double windowLevel[2], vtkLookupTable* lut, vtkVolumeProperty* volumeProp)
double scalarRange[2], double windowLevel[2], vtkScalarsToColors* colors, vtkVolumeProperty* volumeProp)
{
if (!volumeProp || !scalarRange || !windowLevel)
{
{
vtkWarningMacro("SetWindowLevelToVolumeProp: Inputs do not exist.");
return;
}

double windowLevelMinMax[2];
windowLevelMinMax[0] = windowLevel[1] - 0.5 * windowLevel[0];
windowLevelMinMax[1] = windowLevel[1] + 0.5 * windowLevel[0];
}

double previous = VTK_DOUBLE_MIN;
double windowLevelMinMax[2] =
{
windowLevel[1] - 0.5 * windowLevel[0],
windowLevel[1] + 0.5 * windowLevel[0],
};

vtkNew<vtkColorTransferFunction> colorTransfer;

const int size = lut ? lut->GetNumberOfTableValues() : 0;
if (size == 0)
{
const double black[3] = {0., 0., 0.};
const double white[3] = {1., 1., 1.};
colorTransfer->AddRGBPoint(scalarRange[0], black[0], black[1], black[2]);
colorTransfer->AddRGBPoint(windowLevelMinMax[0], black[0], black[1], black[2]);
colorTransfer->AddRGBPoint(windowLevelMinMax[1], white[0], white[1], white[2]);
colorTransfer->AddRGBPoint(scalarRange[1], white[0], white[1], white[2]);
}
else if (size == 1)
{
double color[4];
lut->GetTableValue(0, color);

colorTransfer->AddRGBPoint(vtkMRMLVolumePropertyNode::HigherAndUnique(scalarRange[0], previous),
color[0], color[1], color[2]);
colorTransfer->AddRGBPoint(vtkMRMLVolumePropertyNode::HigherAndUnique(windowLevelMinMax[0], previous),
color[0], color[1], color[2]);
colorTransfer->AddRGBPoint(vtkMRMLVolumePropertyNode::HigherAndUnique(windowLevelMinMax[1], previous),
color[0], color[1], color[2]);
colorTransfer->AddRGBPoint(vtkMRMLVolumePropertyNode::HigherAndUnique(scalarRange[1], previous),
color[0], color[1], color[2]);
}
else // if (size > 1)
{
previous = VTK_DOUBLE_MIN;

double color[4];
lut->GetTableValue(0, color);
colorTransfer->AddRGBPoint(vtkMRMLVolumePropertyNode::HigherAndUnique(scalarRange[0], previous),
color[0], color[1], color[2]);
vtkColorTransferFunction* inputColorTransfer = vtkColorTransferFunction::SafeDownCast(colors);
if (inputColorTransfer)
{
// Colors are defined by transfer function
// We cannot simply copy but we need to scale and offset as specified by window/level
double inputColorTransferRange[2] = { 0.0, 1.0 };
inputColorTransfer->GetRange(inputColorTransferRange);
const double scale = (windowLevelMinMax[1] - windowLevelMinMax[0]) / (inputColorTransferRange[1] - inputColorTransferRange[0]);
const double offset = windowLevelMinMax[0] - scale * inputColorTransferRange[0];
const vtkIdType colorCount = inputColorTransfer->GetSize();
double color_X_RGB_MS[6] = { 0., 0., 0., 0., 0.5, 1.0 }; // x, RGB, midpoint, sharpness
for (vtkIdType i = 0; i < colorCount; ++i)
{
inputColorTransfer->GetNodeValue(i, color_X_RGB_MS);
colorTransfer->AddRGBPoint(offset + color_X_RGB_MS[0] * scale,
color_X_RGB_MS[1], color_X_RGB_MS[2], color_X_RGB_MS[3], color_X_RGB_MS[4], color_X_RGB_MS[5]);
}
}
else
{
// Colors are defined by lookup table
vtkLookupTable* lut = vtkLookupTable::SafeDownCast(colors);
double previous = VTK_DOUBLE_MIN;
const vtkIdType numberOfColors = lut ? lut->GetNumberOfTableValues() : 0;
if (numberOfColors == 0)
{
const double black[3] = { 0., 0., 0. };
const double white[3] = { 1., 1., 1. };
colorTransfer->AddRGBPoint(scalarRange[0], black[0], black[1], black[2]);
colorTransfer->AddRGBPoint(windowLevelMinMax[0], black[0], black[1], black[2]);
colorTransfer->AddRGBPoint(windowLevelMinMax[1], white[0], white[1], white[2]);
colorTransfer->AddRGBPoint(scalarRange[1], white[0], white[1], white[2]);
}
else if (numberOfColors == 1)
{
double color[4];
lut->GetTableValue(0, color);

double value = windowLevelMinMax[0];
colorTransfer->AddRGBPoint(vtkMRMLVolumePropertyNode::HigherAndUnique(scalarRange[0], previous),
color[0], color[1], color[2]);
colorTransfer->AddRGBPoint(vtkMRMLVolumePropertyNode::HigherAndUnique(windowLevelMinMax[0], previous),
color[0], color[1], color[2]);
colorTransfer->AddRGBPoint(vtkMRMLVolumePropertyNode::HigherAndUnique(windowLevelMinMax[1], previous),
color[0], color[1], color[2]);
colorTransfer->AddRGBPoint(vtkMRMLVolumePropertyNode::HigherAndUnique(scalarRange[1], previous),
color[0], color[1], color[2]);
}
else // if (numberOfColors > 1)
{
double color[4] = { 0.0, 0.0, 0.0, 1.0 };
lut->GetTableValue(0, color);
colorTransfer->AddRGBPoint(vtkMRMLVolumePropertyNode::HigherAndUnique(scalarRange[0], previous),
color[0], color[1], color[2]);

double step = windowLevel[0] / (size - 1);
// We place up to maxNumberOfPoints points in the color transfer function.
// The number is high enough to accurately describe most color tables,
// but not too high so that the user can still edit the function manually.
const vtkIdType maxNumberOfPoints = 24;

const vtkIdType numberOfPoints = std::min(numberOfColors, maxNumberOfPoints);
// convert from point index to color index
double pointIndexScale = static_cast<double>(numberOfColors - 1) / (numberOfPoints - 1);
double offset = windowLevelMinMax[0];
double scale = windowLevel[0] / (numberOfColors - 1);
for (vtkIdType pointIndex = 0; pointIndex < numberOfPoints; ++pointIndex)
{
vtkIdType colorIndex = pointIndex * pointIndexScale;
lut->GetTableValue(colorIndex, color);
const double value = offset + colorIndex * scale;
colorTransfer->AddRGBPoint(vtkMRMLVolumePropertyNode::HigherAndUnique(value, previous),
color[0], color[1], color[2]);
}

int downSamplingFactor = 64;
for (int i = 0; i < size; i += downSamplingFactor,
value += downSamplingFactor*step)
{
lut->GetTableValue(i, color);
colorTransfer->AddRGBPoint(vtkMRMLVolumePropertyNode::HigherAndUnique(value, previous),
lut->GetTableValue(numberOfColors - 1, color);
colorTransfer->AddRGBPoint(vtkMRMLVolumePropertyNode::HigherAndUnique(windowLevelMinMax[1], previous),
color[0], color[1], color[2]);
colorTransfer->AddRGBPoint(vtkMRMLVolumePropertyNode::HigherAndUnique(scalarRange[1], previous),
color[0], color[1], color[2]);
}
}

lut->GetTableValue(size - 1, color);
colorTransfer->AddRGBPoint(vtkMRMLVolumePropertyNode::HigherAndUnique(windowLevelMinMax[1], previous),
color[0], color[1], color[2]);
colorTransfer->AddRGBPoint(vtkMRMLVolumePropertyNode::HigherAndUnique(scalarRange[1], previous),
color[0], color[1], color[2]);
}

vtkColorTransferFunction *volumePropColorTransfer = volumeProp->GetRGBTransferFunction();
if (this->IsDifferentFunction(colorTransfer.GetPointer(), volumePropColorTransfer))
{
{
volumePropColorTransfer->DeepCopy(colorTransfer.GetPointer());
}
}

volumeProp->SetInterpolationTypeToLinear();
volumeProp->ShadeOn();
Expand Down Expand Up @@ -727,7 +755,7 @@ void vtkSlicerVolumeRenderingLogic::CopyScalarDisplayToVolumeRenderingDisplayNod
threshold[1] = vpNode->GetWindowLevelMax();
}

vtkLookupTable* lut = vpNode->GetColorNode() ? vpNode->GetColorNode()->GetLookupTable() : nullptr;
vtkScalarsToColors* lut = vpNode->GetColorNode() ? vpNode->GetColorNode()->GetScalarsToColors() : nullptr;
vtkVolumeProperty *prop = vspNode->GetVolumePropertyNode()->GetVolumeProperty();

int disabledModify = vspNode->StartModify();
Expand Down
Expand Up @@ -36,7 +36,6 @@ class vtkMRMLVolumePropertyNode;

// VTK includes
class vtkColorTransferFunction;
class vtkLookupTable;
class vtkPiecewiseFunction;
class vtkScalarsToColors;
class vtkVolumeProperty;
Expand Down Expand Up @@ -217,7 +216,7 @@ class VTK_SLICER_VOLUMERENDERING_MODULE_LOGIC_EXPORT vtkSlicerVolumeRenderingLog
/// \sa SetThresholdToVolumeProp
void SetWindowLevelToVolumeProp(
double scalarRange[2], double windowLevel[2],
vtkLookupTable* lut, vtkVolumeProperty* node);
vtkScalarsToColors* lut, vtkVolumeProperty* node);

/// Create an opacity transfer function for gradient opacity.
/// It ranges from 0 to scalarRange[1] - scalarRange[0].
Expand All @@ -229,7 +228,7 @@ class VTK_SLICER_VOLUMERENDERING_MODULE_LOGIC_EXPORT vtkSlicerVolumeRenderingLog
/// transfer function from the labelmap LUT \a colors.
/// \sa SetWindowLevelToVolumeProp, SetThresholdToVolumeProp
void SetLabelMapToVolumeProp(
vtkScalarsToColors* lut, vtkVolumeProperty* node);
vtkScalarsToColors* colors, vtkVolumeProperty* node);

/// Update DisplayNode from VolumeNode,
/// Can pass a VolumePropertyNode and an ROI node to be the display node.
Expand Down