Orientation Annotation for volume rendering
This commit is contained in:
@@ -10,6 +10,11 @@
|
|||||||
#include <vtkRenderWindow.h>
|
#include <vtkRenderWindow.h>
|
||||||
#include <vtkRenderWindowInteractor.h>
|
#include <vtkRenderWindowInteractor.h>
|
||||||
#include <vtkRenderer.h>
|
#include <vtkRenderer.h>
|
||||||
|
#include <vtkVolume.h>
|
||||||
|
#include <vtkPropCollection.h>
|
||||||
|
#include <vtkAssemblyPath.h>
|
||||||
|
#include <vtkVolumeProperty.h>
|
||||||
|
#include <vtkPiecewiseFunction.h>
|
||||||
|
|
||||||
vtkStandardNewMacro(VolumeInteractorStyle)
|
vtkStandardNewMacro(VolumeInteractorStyle)
|
||||||
|
|
||||||
@@ -22,8 +27,9 @@ VolumeInteractorStyle::~VolumeInteractorStyle() {
|
|||||||
}
|
}
|
||||||
|
|
||||||
void VolumeInteractorStyle::OnLeftButtonDown() {
|
void VolumeInteractorStyle::OnLeftButtonDown() {
|
||||||
this->FindPokedRenderer(this->Interactor->GetEventPosition()[0],
|
int x = this->Interactor->GetEventPosition()[0];
|
||||||
this->Interactor->GetEventPosition()[1]);
|
int y = this->Interactor->GetEventPosition()[1];
|
||||||
|
this->FindPokedRenderer(x,y);
|
||||||
if (this->CurrentRenderer == nullptr)
|
if (this->CurrentRenderer == nullptr)
|
||||||
{
|
{
|
||||||
return;
|
return;
|
||||||
@@ -47,6 +53,201 @@ void VolumeInteractorStyle::OnLeftButtonDown() {
|
|||||||
this->StartDolly();
|
this->StartDolly();
|
||||||
break;
|
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);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -11,6 +11,11 @@
|
|||||||
#define VOLUME_ROTATE2D 1
|
#define VOLUME_ROTATE2D 1
|
||||||
#define VOLUME_ZOOM 2
|
#define VOLUME_ZOOM 2
|
||||||
#define VOLUME_PAN 3
|
#define VOLUME_PAN 3
|
||||||
|
#define VOLUME_WINDOW 4
|
||||||
|
|
||||||
|
#define VTKIS_WINDOW_LEVEL 1024
|
||||||
|
|
||||||
|
class vtkVolumeProperty;
|
||||||
|
|
||||||
class VolumeInteractorStyle:public vtkInteractorStyleTrackballCamera {
|
class VolumeInteractorStyle:public vtkInteractorStyleTrackballCamera {
|
||||||
public:
|
public:
|
||||||
@@ -18,18 +23,22 @@ public:
|
|||||||
|
|
||||||
vtkTypeMacro(VolumeInteractorStyle, vtkInteractorStyleTrackballCamera);
|
vtkTypeMacro(VolumeInteractorStyle, vtkInteractorStyleTrackballCamera);
|
||||||
|
|
||||||
vtkSetClampMacro(InteractionMode, int, VOLUME_ROTATE3D, VOLUME_PAN);
|
vtkSetClampMacro(InteractionMode, int, VOLUME_ROTATE3D, VOLUME_WINDOW);
|
||||||
vtkGetMacro(InteractionMode, int);
|
vtkGetMacro(InteractionMode, int);
|
||||||
|
|
||||||
|
virtual void StartWindowLevel();
|
||||||
|
virtual void EndWindowLevel();
|
||||||
void OnLeftButtonDown() override;
|
void OnLeftButtonDown() override;
|
||||||
// void OnLeftButtonUp() override;
|
void OnLeftButtonUp() override;
|
||||||
// void OnRightButtonDown() override;
|
// void OnRightButtonDown() override;
|
||||||
// void OnRightButtonUp() override;
|
// void OnRightButtonUp() override;
|
||||||
// void OnMouseMove() override;
|
void OnMouseMove() override;
|
||||||
|
|
||||||
void OnMouseWheelForward() override{};
|
void OnMouseWheelForward() override{};
|
||||||
void OnMouseWheelBackward() override{};
|
void OnMouseWheelBackward() override{};
|
||||||
|
|
||||||
|
void WindowLevel();
|
||||||
|
|
||||||
protected:
|
protected:
|
||||||
VolumeInteractorStyle();
|
VolumeInteractorStyle();
|
||||||
|
|
||||||
@@ -40,7 +49,15 @@ private:
|
|||||||
|
|
||||||
void operator=(const VolumeInteractorStyle &) = delete;
|
void operator=(const VolumeInteractorStyle &) = delete;
|
||||||
|
|
||||||
|
void GetCurrentVolumeProperty();
|
||||||
|
|
||||||
int InteractionMode;
|
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};
|
||||||
};
|
};
|
||||||
|
|
||||||
|
|
||||||
|
|||||||
@@ -137,8 +137,12 @@ void VolumeRenderingViewer::InstallPipeline() {
|
|||||||
if (this->Interactor) {
|
if (this->Interactor) {
|
||||||
if (!this->InteractorStyle) {
|
if (!this->InteractorStyle) {
|
||||||
auto style = VolumeInteractorStyle::New();
|
auto style = VolumeInteractorStyle::New();
|
||||||
style->SetInteractionMode(0);
|
// style->SetInteractionMode(1024);
|
||||||
this->InteractorStyle = style;
|
this->InteractorStyle = style;
|
||||||
|
this->InteractorStyle->AddObserver(vtkCommand::StartWindowLevelEvent,this,
|
||||||
|
&VolumeRenderingViewer::DecreaseMaximumImageSampleDistance);
|
||||||
|
this->InteractorStyle->AddObserver(vtkCommand::EndWindowLevelEvent,this,
|
||||||
|
&VolumeRenderingViewer::ResetMaximumImageSampleDistance);
|
||||||
}
|
}
|
||||||
|
|
||||||
this->Interactor->SetInteractorStyle(this->InteractorStyle);
|
this->Interactor->SetInteractorStyle(this->InteractorStyle);
|
||||||
@@ -148,7 +152,7 @@ void VolumeRenderingViewer::InstallPipeline() {
|
|||||||
if (this->Renderer && this->VolumeActor) {
|
if (this->Renderer && this->VolumeActor) {
|
||||||
this->Renderer->AddVolume(this->VolumeActor);
|
this->Renderer->AddVolume(this->VolumeActor);
|
||||||
this->Renderer->AddViewProp(annotation);
|
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);
|
this->Renderer->SetBackground(0.0, 0.0, 0.0);
|
||||||
}
|
}
|
||||||
//TODO: annotation for orientation
|
//TODO: annotation for orientation
|
||||||
@@ -263,13 +267,61 @@ vtkImageData *VolumeRenderingViewer::GetInput() {
|
|||||||
return this->VolumeMapper->GetInput();
|
return this->VolumeMapper->GetInput();
|
||||||
}
|
}
|
||||||
|
|
||||||
void VolumeRenderingViewer::printFrameRate() {
|
void VolumeRenderingViewer::renderAnnotation() {
|
||||||
|
|
||||||
|
// set FPS
|
||||||
double timeInSeconds = Renderer->GetLastRenderTimeInSeconds();
|
double timeInSeconds = Renderer->GetLastRenderTimeInSeconds();
|
||||||
double fps = 1.0 / timeInSeconds;
|
double fps = 1.0 / timeInSeconds;
|
||||||
char buff[200]={0};
|
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 );
|
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) {
|
void VolumeRenderingViewer::SetInteractorStyleMode(int mode) {
|
||||||
@@ -371,9 +423,23 @@ void VolumeRenderingViewer::ResetZoomFitWindow() {
|
|||||||
camera->Dolly(0.99/halfDisX);
|
camera->Dolly(0.99/halfDisX);
|
||||||
}
|
}
|
||||||
else{
|
else{
|
||||||
//use 1.97 to avoid border out of screen
|
//use 0.99 to avoid border out of screen
|
||||||
camera->Dolly(0.99/halfDisY);
|
camera->Dolly(0.99/halfDisY);
|
||||||
}
|
}
|
||||||
|
|
||||||
Renderer->ResetCameraClippingRange();
|
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);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|||||||
@@ -99,12 +99,15 @@ protected:
|
|||||||
|
|
||||||
virtual void UnInstallPipeline();
|
virtual void UnInstallPipeline();
|
||||||
|
|
||||||
void printFrameRate();
|
void renderAnnotation();
|
||||||
private:
|
private:
|
||||||
VolumeRenderingViewer(const VolumeRenderingViewer &) = delete;
|
VolumeRenderingViewer(const VolumeRenderingViewer &) = delete;
|
||||||
|
|
||||||
void operator=(const VolumeRenderingViewer &) = delete;
|
void operator=(const VolumeRenderingViewer &) = delete;
|
||||||
|
|
||||||
|
void DecreaseMaximumImageSampleDistance();
|
||||||
|
void ResetMaximumImageSampleDistance();
|
||||||
|
|
||||||
vtkRenderWindow *RenderWindow;
|
vtkRenderWindow *RenderWindow;
|
||||||
vtkRenderer *Renderer;
|
vtkRenderer *Renderer;
|
||||||
vtkVolume * VolumeActor;
|
vtkVolume * VolumeActor;
|
||||||
@@ -115,6 +118,8 @@ private:
|
|||||||
vtkNew<vtkMatrix4x4> OrientationMatrix;
|
vtkNew<vtkMatrix4x4> OrientationMatrix;
|
||||||
bool gpuMode = false;
|
bool gpuMode = false;
|
||||||
bool firstRender = true;
|
bool firstRender = true;
|
||||||
|
|
||||||
|
void GetDirectionString(const double *directionVector, std::string &str) const;
|
||||||
};
|
};
|
||||||
|
|
||||||
|
|
||||||
|
|||||||
Reference in New Issue
Block a user