Orientation Annotation for volume rendering

This commit is contained in:
Krad
2022-10-21 14:16:26 +08:00
parent 7c8d97c73d
commit 2f5ad333da
4 changed files with 300 additions and 11 deletions

View File

@@ -10,6 +10,11 @@
#include <vtkRenderWindow.h>
#include <vtkRenderWindowInteractor.h>
#include <vtkRenderer.h>
#include <vtkVolume.h>
#include <vtkPropCollection.h>
#include <vtkAssemblyPath.h>
#include <vtkVolumeProperty.h>
#include <vtkPiecewiseFunction.h>
vtkStandardNewMacro(VolumeInteractorStyle)
@@ -22,8 +27,9 @@ VolumeInteractorStyle::~VolumeInteractorStyle() {
}
void VolumeInteractorStyle::OnLeftButtonDown() {
this->FindPokedRenderer(this->Interactor->GetEventPosition()[0],
this->Interactor->GetEventPosition()[1]);
int x = this->Interactor->GetEventPosition()[0];
int y = this->Interactor->GetEventPosition()[1];
this->FindPokedRenderer(x,y);
if (this->CurrentRenderer == nullptr)
{
return;
@@ -47,6 +53,201 @@ void VolumeInteractorStyle::OnLeftButtonDown() {
this->StartDolly();
break;
}
case VOLUME_WINDOW: {
this->WindowLevelStartPosition[0] = x;
this->WindowLevelStartPosition[1] = y;
this->StartWindowLevel();
break;
}
}
}
void VolumeInteractorStyle::OnLeftButtonUp() {
switch (this->State) {
case VTKIS_WINDOW_LEVEL:
this->EndWindowLevel();
if (this->Interactor) {
this->ReleaseFocus();
}
break;
}
this->Superclass::OnLeftButtonUp();
}
void VolumeInteractorStyle::StartWindowLevel() {
if (this->State != VTKIS_NONE)
{
return;
}
this->StartState(VTKIS_WINDOW_LEVEL);
if (this->HandleObservers &&
this->HasObserver(vtkCommand::StartWindowLevelEvent))
{
this->InvokeEvent(vtkCommand::StartWindowLevelEvent, this);
}
GetCurrentVolumeProperty();
if(VolumeProperty){
auto opacity = VolumeProperty->GetScalarOpacity();
if (opacity) {
double range[2] = {0.0, 0.0};
opacity->GetRange(range);
this->WindowLevelInitial[0] = range[1] - range[0];
this->WindowLevelInitial[1] = (range[1] + range[0]) / 2;
}
}
}
void VolumeInteractorStyle::EndWindowLevel() {
if (this->State != VTKIS_WINDOW_LEVEL)
{
return;
}
if (this->HandleObservers)
{
this->InvokeEvent(vtkCommand::EndWindowLevelEvent, this);
}
this->StopState();
}
void VolumeInteractorStyle::OnMouseMove() {
int x = this->Interactor->GetEventPosition()[0];
int y = this->Interactor->GetEventPosition()[1];
switch (this->State)
{
case VTKIS_WINDOW_LEVEL:
this->FindPokedRenderer(x, y);
this->WindowLevel();
this->InvokeEvent(vtkCommand::InteractionEvent, nullptr);
break;
}
this->Superclass::OnMouseMove();
}
void VolumeInteractorStyle::WindowLevel() {
vtkRenderWindowInteractor *rwi = this->Interactor;
this->WindowLevelCurrentPosition[0] = rwi->GetEventPosition()[0];
this->WindowLevelCurrentPosition[1] = rwi->GetEventPosition()[1];
if (this->HandleObservers &&
this->HasObserver(vtkCommand::WindowLevelEvent))
{
this->InvokeEvent(vtkCommand::WindowLevelEvent, this);
}
else if(VolumeProperty){
auto opacity = VolumeProperty->GetScalarOpacity();
if (opacity) {
double range[2] = {0.0, 0.0};
opacity->GetRange(range);
int *size = this->CurrentRenderer->GetSize();
double window = this->WindowLevelInitial[0];
double level = this->WindowLevelInitial[1];
// Compute normalized delta
double dx = (this->WindowLevelCurrentPosition[0] -
this->WindowLevelStartPosition[0]) ;
double dy = (this->WindowLevelStartPosition[1] -
this->WindowLevelCurrentPosition[1]);
// Scale by current values
if ( fabs( window ) > 300.0)
{
dx = dx/size[0] * window;
}
// if ( fabs( level ) > 1200.0 )
// {
// dy = dy/size[1] * level;
// }
// Abs so that direction does not flip
if ( window < 0.0 )
{
dx = -1 * dx;
}
if ( level < 0.0 )
{
dy = -1 * dy;
}
// Compute new window level
double newWindow = dx + window;
double newLevel = level - dy;
if ( newWindow < 0.01 )
{
newWindow = 0.01;
}
//apply new window
double newMin = newLevel - newWindow/2.0;
double newMax = newLevel + newWindow/2.0;
opacity->RemoveAllPoints();
opacity->AddPoint(newMin,0.0);
opacity->AddPoint(newMax,1.0);
this->Interactor->Render();
}
}
}
void VolumeInteractorStyle::GetCurrentVolumeProperty() {
if (!this->CurrentRenderer)
{
return;
}
vtkPropCollection *props = this->CurrentRenderer->GetViewProps();
vtkProp *prop = nullptr;
vtkAssemblyPath *path;
vtkVolume *volumeProp = nullptr;
vtkCollectionSimpleIterator pit;
for (int k = 0; k < 2; k++)
{
int j = 0;
for (props->InitTraversal(pit); (prop = props->GetNextProp(pit)); )
{
bool foundImageProp = false;
for (prop->InitPathTraversal(); (path = prop->GetNextPath()); )
{
vtkProp *tryProp = path->GetLastNode()->GetViewProp();
volumeProp = vtkVolume::SafeDownCast(tryProp);
if (volumeProp)
{
if (volumeProp->GetPickable())
{
foundImageProp = true;
break;
}
volumeProp = nullptr;
j++;
}
}
if (foundImageProp)
{
break;
}
}
}
vtkVolumeProperty* property = nullptr;
if (volumeProp)
{
property = volumeProp->GetProperty();
}
if (property != this->VolumeProperty){
if (this->VolumeProperty){
this->VolumeProperty->Delete();
}
this->VolumeProperty = property;
if (VolumeProperty){
this->VolumeProperty->Register(this);
}
}
}

View File

@@ -11,6 +11,11 @@
#define VOLUME_ROTATE2D 1
#define VOLUME_ZOOM 2
#define VOLUME_PAN 3
#define VOLUME_WINDOW 4
#define VTKIS_WINDOW_LEVEL 1024
class vtkVolumeProperty;
class VolumeInteractorStyle:public vtkInteractorStyleTrackballCamera {
public:
@@ -18,18 +23,22 @@ public:
vtkTypeMacro(VolumeInteractorStyle, vtkInteractorStyleTrackballCamera);
vtkSetClampMacro(InteractionMode, int, VOLUME_ROTATE3D, VOLUME_PAN);
vtkSetClampMacro(InteractionMode, int, VOLUME_ROTATE3D, VOLUME_WINDOW);
vtkGetMacro(InteractionMode, int);
virtual void StartWindowLevel();
virtual void EndWindowLevel();
void OnLeftButtonDown() override;
// void OnLeftButtonUp() override;
void OnLeftButtonUp() override;
// void OnRightButtonDown() override;
// void OnRightButtonUp() override;
// void OnMouseMove() override;
void OnMouseMove() override;
void OnMouseWheelForward() override{};
void OnMouseWheelBackward() override{};
void WindowLevel();
protected:
VolumeInteractorStyle();
@@ -40,7 +49,15 @@ private:
void operator=(const VolumeInteractorStyle &) = delete;
void GetCurrentVolumeProperty();
int InteractionMode;
vtkVolumeProperty* VolumeProperty = nullptr;
double WindowLevelInitial[2] = {0.0, 0.0};
double WindowLevelStartPosition[2] = {0.0, 0.0};
double WindowLevelCurrentPosition[2] = {0.0, 0.0};
};

View File

@@ -137,8 +137,12 @@ void VolumeRenderingViewer::InstallPipeline() {
if (this->Interactor) {
if (!this->InteractorStyle) {
auto style = VolumeInteractorStyle::New();
style->SetInteractionMode(0);
// style->SetInteractionMode(1024);
this->InteractorStyle = style;
this->InteractorStyle->AddObserver(vtkCommand::StartWindowLevelEvent,this,
&VolumeRenderingViewer::DecreaseMaximumImageSampleDistance);
this->InteractorStyle->AddObserver(vtkCommand::EndWindowLevelEvent,this,
&VolumeRenderingViewer::ResetMaximumImageSampleDistance);
}
this->Interactor->SetInteractorStyle(this->InteractorStyle);
@@ -148,7 +152,7 @@ void VolumeRenderingViewer::InstallPipeline() {
if (this->Renderer && this->VolumeActor) {
this->Renderer->AddVolume(this->VolumeActor);
this->Renderer->AddViewProp(annotation);
Renderer->AddObserver(vtkCommand::EndEvent,this,&VolumeRenderingViewer::printFrameRate);
Renderer->AddObserver(vtkCommand::EndEvent,this, &VolumeRenderingViewer::renderAnnotation);
this->Renderer->SetBackground(0.0, 0.0, 0.0);
}
//TODO: annotation for orientation
@@ -263,13 +267,61 @@ vtkImageData *VolumeRenderingViewer::GetInput() {
return this->VolumeMapper->GetInput();
}
void VolumeRenderingViewer::printFrameRate() {
void VolumeRenderingViewer::renderAnnotation() {
// set FPS
double timeInSeconds = Renderer->GetLastRenderTimeInSeconds();
double fps = 1.0 / timeInSeconds;
char buff[200]={0};
sprintf(buff,"FPS:%3.0f", fps);
double *range = VolumeActor->GetProperty()->GetScalarOpacity()->GetRange();
double wl = (range[0]+range[1])/2.0;
double ww = (range[1] - range[0]);
sprintf(buff,"FPS:%3.0f\r\nWW:%.0f WL:%.0f", fps,ww,wl);
annotation->SetText(0,buff );
auto camera = Renderer->GetActiveCamera();
double dop[4] = {.0, .0, .0, 1.};
camera->GetDirectionOfProjection(dop);
double viewUp[4] = {.0, .0, .0, 1.};
camera->GetViewUp(viewUp);
double viewRight[4] = {.0, .0, .0, 1.};
vtkMath::Cross(dop,viewUp,viewRight);
OrientationMatrix->Invert();
OrientationMatrix->MultiplyPoint(viewUp,viewUp);
OrientationMatrix->MultiplyPoint(viewRight,viewRight);
OrientationMatrix->Invert();
double viewDown[4] = {-viewUp[0], -viewUp[1], -viewUp[2], 1.};
double viewLeft[4] = {-viewRight[0], -viewRight[1], -viewRight[2], 1.};
std::string str;
GetDirectionString(viewUp, str);
annotation->SetText(7,str.c_str() );
GetDirectionString(viewRight, str);
annotation->SetText(5,str.c_str() );
GetDirectionString(viewDown, str);
annotation->SetText(4,str.c_str() );
GetDirectionString(viewLeft, str);
annotation->SetText(6,str.c_str() );
}
void VolumeRenderingViewer::GetDirectionString(const double *directionVector, std::string &str) const {
str.clear();
char dV [3] = {' ', ' ', ' '};
if (fabs(directionVector[0]) > 0.5){
dV[0] = directionVector[0] > 0 ? 'L' : 'R';
}
if (fabs(directionVector[1])>0.5){
dV[1] = directionVector[1]>0?'P':'A';
}
if (fabs(directionVector[2])>0.5){
dV[2] =directionVector[2]>0?'H':'F';
}
if (fabs(directionVector[2]) > fabs(directionVector[1])) std::swap(dV[1], dV[2]);
if (fabs(directionVector[1]) > fabs(directionVector[0])) std::swap(dV[0], dV[1]);
for (int i = 0; i < 3; ++i) {
if(dV[i]!=' '){
str.append(1, dV[i]);
}
}
}
void VolumeRenderingViewer::SetInteractorStyleMode(int mode) {
@@ -371,9 +423,23 @@ void VolumeRenderingViewer::ResetZoomFitWindow() {
camera->Dolly(0.99/halfDisX);
}
else{
//use 1.97 to avoid border out of screen
//use 0.99 to avoid border out of screen
camera->Dolly(0.99/halfDisY);
}
Renderer->ResetCameraClippingRange();
}
void VolumeRenderingViewer::DecreaseMaximumImageSampleDistance() {
auto mapper = vtkFixedPointVolumeRayCastMapper::SafeDownCast(VolumeMapper);
if (mapper){
mapper->SetMaximumImageSampleDistance(1.0);
}
}
void VolumeRenderingViewer::ResetMaximumImageSampleDistance() {
auto mapper = vtkFixedPointVolumeRayCastMapper::SafeDownCast(VolumeMapper);
if (mapper){
mapper->SetMaximumImageSampleDistance(4.0);
}
}

View File

@@ -99,12 +99,15 @@ protected:
virtual void UnInstallPipeline();
void printFrameRate();
void renderAnnotation();
private:
VolumeRenderingViewer(const VolumeRenderingViewer &) = delete;
void operator=(const VolumeRenderingViewer &) = delete;
void DecreaseMaximumImageSampleDistance();
void ResetMaximumImageSampleDistance();
vtkRenderWindow *RenderWindow;
vtkRenderer *Renderer;
vtkVolume * VolumeActor;
@@ -115,6 +118,8 @@ private:
vtkNew<vtkMatrix4x4> OrientationMatrix;
bool gpuMode = false;
bool firstRender = true;
void GetDirectionString(const double *directionVector, std::string &str) const;
};