refactor: enhance vtkVoxImage volume rendering with dynamic shader range scaling, improved transfer function management, and synchronized VTK property updates.
This commit is contained in:
@@ -41,6 +41,7 @@
|
||||
#include <vtkPiecewiseFunction.h>
|
||||
#include <vtkSmartVolumeMapper.h>
|
||||
#include <vtkVolumeProperty.h>
|
||||
#include <vtkMatrix4x4.h>
|
||||
|
||||
#include <vtkActor.h>
|
||||
#include <vtkPolyDataMapper.h>
|
||||
@@ -49,6 +50,7 @@
|
||||
#include <Math/VoxImage.h>
|
||||
|
||||
#include "Vtk/Math/vtkVoxImage.h"
|
||||
#include "Vtk/Math/vtkDense.h"
|
||||
|
||||
#include <vtkAutoInit.h>
|
||||
VTK_MODULE_INIT(vtkRenderingVolumeOpenGL2);
|
||||
@@ -128,10 +130,15 @@ vtkVoxImage::vtkVoxImage(Content &content)
|
||||
m_Image(vtkImageData::New()), m_Outline(vtkCubeSource::New()),
|
||||
m_OutlineActor(vtkActor::New()),
|
||||
m_Reader(NULL), m_Writer(NULL), writer_factor(1.E6),
|
||||
m_Window(40/1.E6), m_Level(20/1.E6), m_ShadingPreset(0) {
|
||||
m_Window(1.0), m_Level(0.5), m_ShadingPreset(0) {
|
||||
// Transfer functions
|
||||
m_ColorFun = vtkColorTransferFunction::New();
|
||||
m_OpacityFun = vtkPiecewiseFunction::New();
|
||||
m_UpdateConnection = Object::connect(&m_Content, &uLib::Object::Updated, this, &vtkVoxImage::Update);
|
||||
|
||||
GetContent();
|
||||
InstallPipe();
|
||||
ULIB_ACTIVATE_DISPLAY_PROPERTIES;
|
||||
RescaleShaderRange();
|
||||
}
|
||||
|
||||
vtkVoxImage::~vtkVoxImage() {
|
||||
@@ -140,6 +147,8 @@ vtkVoxImage::~vtkVoxImage() {
|
||||
m_Asm->Delete();
|
||||
m_Outline->Delete();
|
||||
m_OutlineActor->Delete();
|
||||
m_ColorFun->Delete();
|
||||
m_OpacityFun->Delete();
|
||||
}
|
||||
|
||||
vtkImageData *vtkVoxImage::GetImageData() {
|
||||
@@ -181,6 +190,7 @@ void vtkVoxImage::ReadFromVKTFile(const char *fname) {
|
||||
|
||||
m_Image->DeepCopy(vtkscale->GetOutput());
|
||||
SetContent();
|
||||
RescaleShaderRange();
|
||||
} else {
|
||||
std::cerr << "Error: file does not contain structured points\n";
|
||||
}
|
||||
@@ -200,115 +210,134 @@ void vtkVoxImage::ReadFromXMLFile(const char *fname) {
|
||||
|
||||
m_Image->DeepCopy(vtkscale->GetOutput());
|
||||
SetContent();
|
||||
RescaleShaderRange();
|
||||
}
|
||||
|
||||
void vtkVoxImage::setShadingPreset(int blendType) {
|
||||
m_ShadingPreset = blendType;
|
||||
vtkSmartVolumeMapper *mapper = (vtkSmartVolumeMapper *)m_Actor->GetMapper();
|
||||
if (!mapper) return;
|
||||
vtkVolumeProperty *property = m_Actor->GetProperty();
|
||||
|
||||
static vtkColorTransferFunction *colorFun = vtkColorTransferFunction::New();
|
||||
static vtkPiecewiseFunction *opacityFun = vtkPiecewiseFunction::New();
|
||||
|
||||
float window = m_Window;
|
||||
float level = m_Level;
|
||||
|
||||
property->SetColor(colorFun);
|
||||
property->SetScalarOpacity(opacityFun);
|
||||
property->SetColor(m_ColorFun);
|
||||
property->SetScalarOpacity(m_OpacityFun);
|
||||
property->SetInterpolationTypeToLinear();
|
||||
|
||||
if (blendType != 6) {
|
||||
colorFun->RemoveAllPoints();
|
||||
opacityFun->RemoveAllPoints();
|
||||
}
|
||||
m_ColorFun->RemoveAllPoints();
|
||||
m_OpacityFun->RemoveAllPoints();
|
||||
|
||||
switch (blendType) {
|
||||
case 0:
|
||||
colorFun->AddRGBSegment(0.0, 1.0, 1.0, 1.0, 255.0, 1.0, 1.0, 1.0);
|
||||
opacityFun->AddSegment(level - 0.5 * window, 0.0, level + 0.5 * window,
|
||||
1.0);
|
||||
case 0: // MIP
|
||||
m_ColorFun->AddRGBPoint(level - 0.5 * window, 0, 0, 0);
|
||||
m_ColorFun->AddRGBPoint(level + 0.5 * window, 1, 1, 1);
|
||||
m_OpacityFun->AddSegment(level - 0.5 * window, 0.0, level + 0.5 * window, 1.0);
|
||||
mapper->SetBlendModeToMaximumIntensity();
|
||||
break;
|
||||
case 1:
|
||||
colorFun->AddRGBSegment(level - 0.5 * window, 0.0, 0.0, 0.0,
|
||||
level + 0.5 * window, 1.0, 1.0, 1.0);
|
||||
opacityFun->AddSegment(level - 0.5 * window, 0.0, level + 0.5 * window,
|
||||
1.0);
|
||||
case 1: // Composite
|
||||
m_ColorFun->AddRGBPoint(level - 0.5 * window, 0, 0, 0);
|
||||
m_ColorFun->AddRGBPoint(level + 0.5 * window, 1, 1, 1);
|
||||
m_OpacityFun->AddSegment(level - 0.5 * window, 0.0, level + 0.5 * window, 1.0);
|
||||
mapper->SetBlendModeToComposite();
|
||||
property->ShadeOff();
|
||||
break;
|
||||
case 2:
|
||||
colorFun->AddRGBSegment(0.0, 1.0, 1.0, 1.0, 255.0, 1.0, 1.0, 1.0);
|
||||
opacityFun->AddSegment(level - 0.5 * window, 0.0, level + 0.5 * window,
|
||||
1.0);
|
||||
case 2: // Composite Shaded
|
||||
m_ColorFun->AddRGBPoint(level - 0.5 * window, 0, 0, 0);
|
||||
m_ColorFun->AddRGBPoint(level + 0.5 * window, 1, 1, 1);
|
||||
m_OpacityFun->AddSegment(level - 0.5 * window, 0.0, level + 0.5 * window, 1.0);
|
||||
mapper->SetBlendModeToComposite();
|
||||
property->ShadeOn();
|
||||
break;
|
||||
case 3:
|
||||
colorFun->AddRGBPoint(-3024, 0, 0, 0, 0.5, 0.0);
|
||||
colorFun->AddRGBPoint(-1000, .62, .36, .18, 0.5, 0.0);
|
||||
colorFun->AddRGBPoint(-500, .88, .60, .29, 0.33, 0.45);
|
||||
colorFun->AddRGBPoint(3071, .83, .66, 1, 0.5, 0.0);
|
||||
opacityFun->AddPoint(-3024, 0, 0.5, 0.0);
|
||||
opacityFun->AddPoint(-1000, 0, 0.5, 0.0);
|
||||
opacityFun->AddPoint(-500, 1.0, 0.33, 0.45);
|
||||
opacityFun->AddPoint(3071, 1.0, 0.5, 0.0);
|
||||
mapper->SetBlendModeToComposite();
|
||||
property->ShadeOn();
|
||||
property->SetAmbient(0.1);
|
||||
property->SetDiffuse(0.9);
|
||||
property->SetSpecular(0.2);
|
||||
property->SetSpecularPower(10.0);
|
||||
property->SetScalarOpacityUnitDistance(0.8919);
|
||||
break;
|
||||
case 4:
|
||||
colorFun->AddRGBPoint(0.0, 0, 0, 1); // Blue
|
||||
colorFun->AddRGBPoint(level, 0, 1, 0); // Green
|
||||
colorFun->AddRGBPoint(level + 0.5*window, 1, 1, 0); // Yellow
|
||||
colorFun->AddRGBPoint(level + window, 1, 0, 0); // Red
|
||||
opacityFun->AddSegment(level - 0.5 * window, 0.0, level + 0.5 * window, 1.0);
|
||||
case 3: // Rainbow MIP
|
||||
m_ColorFun->AddRGBPoint(level - 0.5 * window, 0, 0, 1);
|
||||
m_ColorFun->AddRGBPoint(level, 0, 1, 0);
|
||||
m_ColorFun->AddRGBPoint(level + 0.5 * window, 1, 1, 0);
|
||||
m_ColorFun->AddRGBPoint(level + window, 1, 0, 0);
|
||||
m_OpacityFun->AddSegment(level - 0.5 * window, 0.0, level + 0.5 * window, 1.0);
|
||||
mapper->SetBlendModeToMaximumIntensity();
|
||||
break;
|
||||
case 5:
|
||||
colorFun->AddRGBSegment(0.0, 1.0, 1.0, 1.0, 255.0, 1.0, 1.0, 1.0);
|
||||
opacityFun->AddSegment(level - 0.5 * window, 0.0, level + 0.5 * window, 1.0);
|
||||
case 4: // Additive
|
||||
m_ColorFun->AddRGBPoint(level - 0.5 * window, 0, 0, 0);
|
||||
m_ColorFun->AddRGBPoint(level + 0.5 * window, 1, 1, 1);
|
||||
m_OpacityFun->AddSegment(level - 0.5 * window, 0.0, level + 0.5 * window, 1.0);
|
||||
mapper->SetBlendModeToAdditive();
|
||||
break;
|
||||
default:
|
||||
vtkGenericWarningMacro("Unknown blend type.");
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
void vtkVoxImage::RescaleShaderRange() {
|
||||
double range[2];
|
||||
m_Image->GetScalarRange(range);
|
||||
m_Level = (range[0] + range[1]) / 2.0;
|
||||
m_Window = range[1] - range[0];
|
||||
if (m_Window <= 1e-9)
|
||||
m_Window = 1.0;
|
||||
setShadingPreset(m_ShadingPreset);
|
||||
}
|
||||
|
||||
void vtkVoxImage::SetRepresentation(Representation mode) {
|
||||
Puppet::SetRepresentation(mode); // Ensure base class data state is updated
|
||||
|
||||
if (mode == Wireframe) {
|
||||
m_Actor->SetVisibility(0);
|
||||
m_OutlineActor->SetVisibility(1);
|
||||
} else if (mode == Surface) {
|
||||
m_OutlineActor->GetProperty()->SetRepresentationToWireframe();
|
||||
} else if (mode == Volume) {
|
||||
m_Actor->SetVisibility(1);
|
||||
m_OutlineActor->SetVisibility(1); // Keep outline visible as boundary
|
||||
m_OutlineActor->SetVisibility(1);
|
||||
m_OutlineActor->GetProperty()->SetRepresentationToWireframe();
|
||||
} else {
|
||||
Puppet::SetRepresentation(mode);
|
||||
// Other representations (Points, Surface, etc) are handled by basic Puppet
|
||||
// behavior which affects the m_Asm parts.
|
||||
}
|
||||
}
|
||||
|
||||
void vtkVoxImage::serialize_display(uLib::Archive::display_properties_archive & ar, const unsigned int version) {
|
||||
// Call base class if it has display properties
|
||||
// Call base class to show Transform and Appearance properties
|
||||
Puppet::serialize_display(ar, version);
|
||||
|
||||
// Use the member variables if they are available
|
||||
// Use the member variables for volume rendering parameters
|
||||
ar & boost::serialization::make_hrp("Window", m_Window);
|
||||
ar & boost::serialization::make_hrp("Level", m_Level);
|
||||
ar & boost::serialization::make_hrp_enum("Shading", m_ShadingPreset, {"MIP", "Composite", "Composite Shaded", "MIP Bone", "MIP Hot", "Additive"});
|
||||
ar & boost::serialization::make_hrp_enum("Shading", m_ShadingPreset,
|
||||
{"MIP", "Composite", "Composite Shaded", "MIP Bone", "MIP Hot", "Additive"});
|
||||
}
|
||||
|
||||
void vtkVoxImage::SyncFromVtk() {
|
||||
if (auto *root = this->GetProxyProp()) {
|
||||
vtkMatrix4x4 *rootMat = root->GetUserMatrix();
|
||||
if (rootMat) {
|
||||
Matrix4f vtkLocal = VtkToMatrix4f(rootMat);
|
||||
// Synchronize TRS from VTK, compensating for local volume offset
|
||||
m_Content.FromMatrix(vtkLocal); // * m_Content.GetLocalMatrix().inverse());
|
||||
m_Content.Updated();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void vtkVoxImage::Update() {
|
||||
if (auto *root = vtkProp3D::SafeDownCast(this->GetProp())) {
|
||||
vtkNew<vtkMatrix4x4> m;
|
||||
Matrix4fToVtk(m_Content.GetMatrix(), m); // * m_Content.GetLocalMatrix(), m);
|
||||
root->SetUserMatrix(m);
|
||||
root->Modified();
|
||||
// std::cout << "[vtkVoxImage::Update] Set Proxy UserMatrix:" << std::endl;
|
||||
// std::cout << m_Content.GetMatrix() << std::endl;
|
||||
}
|
||||
setShadingPreset(m_ShadingPreset);
|
||||
m_Actor->Update();
|
||||
m_Outline->SetBounds(m_Image->GetBounds());
|
||||
m_Outline->Update();
|
||||
|
||||
ConnectionBlock blocker(m_UpdateConnection);
|
||||
this->Puppet::Update();
|
||||
}
|
||||
|
||||
|
||||
void vtkVoxImage::InstallPipe() {
|
||||
vtkSmartPointer<vtkSmartVolumeMapper> mapper =
|
||||
vtkSmartPointer<vtkSmartVolumeMapper>::New();
|
||||
@@ -320,7 +349,7 @@ void vtkVoxImage::InstallPipe() {
|
||||
mapper->Update();
|
||||
|
||||
m_Actor->SetMapper(mapper);
|
||||
this->setShadingPreset(0);
|
||||
this->setShadingPreset(m_ShadingPreset);
|
||||
mapper->Update();
|
||||
|
||||
m_Outline->SetBounds(m_Image->GetBounds());
|
||||
@@ -331,13 +360,13 @@ void vtkVoxImage::InstallPipe() {
|
||||
m_OutlineActor->SetMapper(mmapper);
|
||||
m_OutlineActor->GetProperty()->SetRepresentationToWireframe();
|
||||
m_OutlineActor->GetProperty()->SetAmbient(0.7);
|
||||
|
||||
|
||||
m_Asm->AddPart(m_Actor);
|
||||
m_Asm->AddPart(m_OutlineActor);
|
||||
this->SetProp(m_Asm);
|
||||
|
||||
// Default look
|
||||
this->SetRepresentation(Surface);
|
||||
this->SetRepresentation(Volume);
|
||||
}
|
||||
|
||||
} // namespace Vtk
|
||||
|
||||
Reference in New Issue
Block a user