diff --git a/CMakeLists.txt b/CMakeLists.txt
index f705672..f743c9d 100644
--- a/CMakeLists.txt
+++ b/CMakeLists.txt
@@ -67,8 +67,8 @@ set(UI_RESOURCES
# Create executable
qt5_wrap_ui(UISrcs ${UI_FILES} )
# CMAKE_AUTOMOC is ON so the MOC headers will be automatically wrapped.
-add_executable(Segmentor MACOSX_BUNDLE WIN32 ${CXX_FILES} ${UISrcs} ${QT_WRAP} ${UI_RESOURCES})
-#add_executable(Segmentor MACOSX_BUNDLE ${CXX_FILES} ${UISrcs} ${QT_WRAP} ${UI_RESOURCES})
+#add_executable(Segmentor MACOSX_BUNDLE WIN32 ${CXX_FILES} ${UISrcs} ${QT_WRAP} ${UI_RESOURCES})
+add_executable(Segmentor MACOSX_BUNDLE ${CXX_FILES} ${UISrcs} ${QT_WRAP} ${UI_RESOURCES})
qt5_use_modules(Segmentor Core Gui)
target_link_libraries(Segmentor ${VTK_LIBRARIES})
install(TARGETS Segmentor
diff --git a/Segmentor.qrc b/Segmentor.qrc
index 0fda75f..d00de0f 100644
--- a/Segmentor.qrc
+++ b/Segmentor.qrc
@@ -25,5 +25,6 @@
icons/icon_show_neighbor_regions.png
icons/icon_filter_regions.png
icons/icon_reset_view.png
+ icons/icon_volume_render.png
\ No newline at end of file
diff --git a/icons/icon_volume_render.png b/icons/icon_volume_render.png
new file mode 100644
index 0000000..9a63b39
Binary files /dev/null and b/icons/icon_volume_render.png differ
diff --git a/icons/icon_volume_render.svg b/icons/icon_volume_render.svg
new file mode 100644
index 0000000..707ab09
--- /dev/null
+++ b/icons/icon_volume_render.svg
@@ -0,0 +1,171 @@
+
+
+
+
diff --git a/interaction/InteractionCallbacks.cxx b/interaction/InteractionCallbacks.cxx
index 6f32777..42a7a8c 100644
--- a/interaction/InteractionCallbacks.cxx
+++ b/interaction/InteractionCallbacks.cxx
@@ -13,6 +13,7 @@
#include "VolumeView.h"
#include "SliceView.h"
#include "vtkInteractorStyleSlice.h"
+#include "vtkInteractorStyleVolume.h"
bool InteractionCallbacks::firstCameraCallback = true;
@@ -293,4 +294,11 @@ void InteractionCallbacks::WindowLevel(vtkObject* caller, unsigned long eventId,
VisualizationContainer* vis = static_cast(clientData);
vis->SetWindowLevel(style->GetWindow(), style->GetLevel());
+}
+
+void InteractionCallbacks::VolumeWindowLevel(vtkObject* caller, unsigned long eventId, void* clientData, void *callData) {
+ vtkInteractorStyleVolume* style = static_cast(caller);
+ VisualizationContainer* vis = static_cast(clientData);
+
+ vis->SetVolumeWindowLevel(style->GetWindow(), style->GetLevel());
}
\ No newline at end of file
diff --git a/interaction/InteractionCallbacks.h b/interaction/InteractionCallbacks.h
index 2051114..9f0dc5e 100644
--- a/interaction/InteractionCallbacks.h
+++ b/interaction/InteractionCallbacks.h
@@ -34,6 +34,7 @@ class InteractionCallbacks {
static void MouseMove(vtkObject* caller, unsigned long eventId, void* clientData, void *callData);
static void WindowLevel(vtkObject* caller, unsigned long eventId, void* clientData, void *callData);
+ static void VolumeWindowLevel(vtkObject* caller, unsigned long eventId, void* clientData, void *callData);
private:
static bool firstCameraCallback;
diff --git a/interaction/vtkInteractorStyleVolume.cxx b/interaction/vtkInteractorStyleVolume.cxx
index 5b197ea..40a63b6 100644
--- a/interaction/vtkInteractorStyleVolume.cxx
+++ b/interaction/vtkInteractorStyleVolume.cxx
@@ -17,7 +17,18 @@ vtkStandardNewMacro(vtkInteractorStyleVolume);
vtkInteractorStyleVolume::vtkInteractorStyleVolume()
{
this->MouseMoved = false;
+
this->Mode = NavigationMode;
+
+ this->WindowLevelStartPosition[0] = 0;
+ this->WindowLevelStartPosition[1] = 0;
+
+ this->WindowLevelCurrentPosition[0] = 0;
+ this->WindowLevelCurrentPosition[1] = 0;
+
+ this->WindowLevelCurrent[0] = this->WindowLevelInitial[0] = 1.0;
+ this->WindowLevelCurrent[1] = this->WindowLevelInitial[1] = 0.5;
+
this->Picker = vtkSmartPointer::New();
}
@@ -183,6 +194,127 @@ void vtkInteractorStyleVolume::EndVisible()
this->StopState();
}
+//----------------------------------------------------------------------------
+void vtkInteractorStyleVolume::StartWindowLevel()
+{
+ if (this->State != VTKIS_NONE)
+ {
+ return;
+ }
+ this->StartState(VTKIS_WINDOW_LEVEL_VOLUME);
+
+ if (this->HandleObservers)
+ {
+ this->InvokeEvent(StartWindowLevelEvent, nullptr);
+ }
+
+ this->WindowLevelInitial[0] = this->WindowLevelCurrent[0];
+ this->WindowLevelInitial[1] = this->WindowLevelCurrent[1];
+}
+
+//----------------------------------------------------------------------------
+void vtkInteractorStyleVolume::EndWindowLevel()
+{
+ if (this->State != VTKIS_WINDOW_LEVEL_VOLUME)
+ {
+ return;
+ }
+ if (this->HandleObservers)
+ {
+ this->InvokeEvent(EndWindowLevelEvent, nullptr);
+ }
+ this->StopState();
+}
+
+//----------------------------------------------------------------------------
+void vtkInteractorStyleVolume::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);
+ }
+
+ 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]) * 4.0 / size[0];
+ double dy = (this->WindowLevelStartPosition[1] -
+ this->WindowLevelCurrentPosition[1]) * 4.0 / size[1];
+
+ // Scale by current values
+
+ if (fabs(window) > 0.01)
+ {
+ dx = dx * window;
+ }
+ else
+ {
+ dx = dx * (window < 0 ? -0.01 : 0.01);
+ }
+ if (fabs(level) > 0.01)
+ {
+ dy = dy * level;
+ }
+ else
+ {
+ dy = dy * (level < 0 ? -0.01 : 0.01);
+ }
+
+ // 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;
+ }
+
+ this->WindowLevelCurrent[0] = newWindow;
+ this->WindowLevelCurrent[1] = newLevel;
+
+ this->Interactor->Render();
+}
+
+//----------------------------------------------------------------------------
+void vtkInteractorStyleVolume::SetWindowLevel(double window, double level) {
+ this->WindowLevelCurrent[0] = this->WindowLevelInitial[0] = window;
+ this->WindowLevelCurrent[1] = this->WindowLevelInitial[1] = level;
+}
+
+
+//----------------------------------------------------------------------------
+double vtkInteractorStyleVolume::GetWindow() {
+ return this->WindowLevelCurrent[0];
+}
+
+//----------------------------------------------------------------------------
+double vtkInteractorStyleVolume::GetLevel() {
+ return this->WindowLevelCurrent[1];
+}
+
//----------------------------------------------------------------------------
void vtkInteractorStyleVolume::OnMouseMove()
{
@@ -205,6 +337,11 @@ void vtkInteractorStyleVolume::OnMouseMove()
this->FindPokedRenderer(x, y);
this->Slice();
break;
+
+ case VTKIS_WINDOW_LEVEL_VOLUME:
+ this->WindowLevel();
+ this->InvokeEvent(WindowLevelEvent, nullptr);
+ break;
}
// Call parent to handle all other states and perform additional work
@@ -246,11 +383,21 @@ void vtkInteractorStyleVolume::OnLeftButtonDown()
else if (this->Mode == VisibleMode)
{
this->StartVisible();
- }
+ }
else
{
- // Rotate
- this->StartRotate();
+ // If shift is held down, start window level
+ if (this->Interactor->GetShiftKey()) {
+ this->WindowLevelStartPosition[0] = x;
+ this->WindowLevelStartPosition[1] = y;
+ this->StartWindowLevel();
+ }
+
+ // Otherwise rotate
+ else
+ {
+ this->StartRotate();
+ }
}
}
@@ -291,6 +438,10 @@ void vtkInteractorStyleVolume::OnLeftButtonUp()
case VTKIS_VISIBLE_VOLUME:
this->EndVisible();
break;
+
+ case VTKIS_WINDOW_LEVEL_VOLUME:
+ this->EndWindowLevel();
+ break;
}
// Call parent to handle all other states and perform additional work
@@ -365,6 +516,11 @@ void vtkInteractorStyleVolume::OnRightButtonDown()
{
this->StartSlice();
}
+ // Transfer function if shift is held down
+ else if (this->Interactor->GetShiftKey())
+ {
+ this->StartWindowLevel();
+ }
else
{
if (this->Mode == EditMode)
diff --git a/interaction/vtkInteractorStyleVolume.h b/interaction/vtkInteractorStyleVolume.h
index b0f07db..8144ff9 100644
--- a/interaction/vtkInteractorStyleVolume.h
+++ b/interaction/vtkInteractorStyleVolume.h
@@ -18,6 +18,7 @@ class vtkCellPicker;
#define VTKIS_SLICE_VOLUME 2051
#define VTKIS_MERGE_VOLUME 2052
#define VTKIS_VISIBLE_VOLUME 2053
+#define VTKIS_WINDOW_LEVEL_VOLUME 2054
class vtkInteractorStyleVolume : public vtkInteractorStyleTrackballCamera {
public:
@@ -53,17 +54,27 @@ class vtkInteractorStyleVolume : public vtkInteractorStyleTrackballCamera {
virtual void EndMerge();
virtual void StartVisible();
virtual void EndVisible();
+ virtual void StartWindowLevel();
+ virtual void EndWindowLevel();
+
+ virtual void WindowLevel();
+ virtual void SetWindowLevel(double window, double level);
+ virtual double GetWindow();
+ virtual double GetLevel();
enum VolumeEventIds {
SelectLabelEvent = vtkCommand::UserEvent + 1,
StartPaintEvent,
PaintEvent,
- EndPaintEvent,
+ EndPaintEvent,
StartEraseEvent,
EraseEvent,
EndEraseEvent,
MergeEvent,
- VisibleEvent
+ VisibleEvent,
+ StartWindowLevelEvent,
+ WindowLevelEvent,
+ EndWindowLevelEvent
};
protected:
@@ -76,6 +87,11 @@ class vtkInteractorStyleVolume : public vtkInteractorStyleTrackballCamera {
vtkSmartPointer Picker;
+ int WindowLevelStartPosition[2];
+ int WindowLevelCurrentPosition[2];
+ double WindowLevelInitial[2];
+ double WindowLevelCurrent[2];
+
void SetOrientation(const double leftToRight[3], const double viewUp[3]);
private:
diff --git a/qt/MainWindow.cxx b/qt/MainWindow.cxx
index 7b31f45..c5d898f 100644
--- a/qt/MainWindow.cxx
+++ b/qt/MainWindow.cxx
@@ -746,6 +746,10 @@ void MainWindow::on_actionSmoothSurfaces(bool checked) {
visualizationContainer->GetVolumeView()->SetSmoothSurfaces(checked);
}
+void MainWindow::on_actionVolumeRendering(bool checked) {
+ visualizationContainer->GetVolumeView()->SetVolumeRendering(checked);
+}
+
void MainWindow::on_actionShowPlane(bool checked) {
visualizationContainer->GetVolumeView()->SetShowPlane(checked);
}
@@ -1079,6 +1083,7 @@ void MainWindow::createToolBar() {
toolBar->addWidget(createLabel("3D"));
toolBar->addAction(createActionIcon(":/icons/icon_smooth_normals.png", "Smooth normals (n)", "n", visualizationContainer->GetVolumeView()->GetSmoothShading(), &MainWindow::on_actionSmoothNormals));
toolBar->addAction(createActionIcon(":/icons/icon_smooth_surface.png", "Smooth surfaces (s)", "s", visualizationContainer->GetVolumeView()->GetSmoothSurfaces(), &MainWindow::on_actionSmoothSurfaces));
+ toolBar->addAction(createActionIcon(":/icons/icon_volume_render.png", "Volume rendering (])", "]", visualizationContainer->GetVolumeView()->GetVolumeRendering(), &MainWindow::on_actionVolumeRendering));
toolBar->addAction(createActionIcon(":/icons/icon_plane.png", "Show plane (o)", "o", visualizationContainer->GetVolumeView()->GetShowPlane(), &MainWindow::on_actionShowPlane));
toolBar->addSeparator();
toolBar->addWidget(createLabel("Filter"));
diff --git a/qt/MainWindow.h b/qt/MainWindow.h
index b79010a..29b8cd1 100644
--- a/qt/MainWindow.h
+++ b/qt/MainWindow.h
@@ -103,6 +103,7 @@ public slots:
virtual void on_actionToggleAutoRescale(bool checked);
virtual void on_actionSmoothNormals(bool checked);
virtual void on_actionSmoothSurfaces(bool checked);
+ virtual void on_actionVolumeRendering(bool checked);
virtual void on_actionShowPlane(bool checked);
virtual void on_actionClearRegionVisibilities();
virtual void on_actionShowPlaneRegions();
diff --git a/region/RegionSurface.cxx b/region/RegionSurface.cxx
index 19bf45b..9b803f2 100644
--- a/region/RegionSurface.cxx
+++ b/region/RegionSurface.cxx
@@ -45,14 +45,17 @@ RegionSurface::RegionSurface(Region* inputRegion, double color[3]) {
mapper = vtkSmartPointer::New();
mapper->ScalarVisibilityOff();
-
+
actor = vtkSmartPointer::New();
actor->SetMapper(mapper);
+
actor->GetProperty()->SetColor(color);
actor->GetProperty()->SetDiffuse(1.0);
actor->GetProperty()->SetAmbient(0.1);
actor->GetProperty()->SetSpecular(0.0);
+ SetRenderMode(Normal);
+
UpdatePipeline();
}
@@ -78,6 +81,31 @@ void RegionSurface::SetSmoothShading(bool smooth) {
UpdatePipeline();
}
+void RegionSurface::SetRenderMode(RenderMode mode) {
+ switch (mode) {
+ case Normal:
+ actor->GetProperty()->SetRepresentationToSurface();
+ actor->GetProperty()->FrontfaceCullingOff();
+ actor->GetProperty()->LightingOn();
+
+ break;
+
+ case Wireframe:
+ actor->GetProperty()->SetRepresentationToWireframe();
+ actor->GetProperty()->FrontfaceCullingOff();
+ actor->GetProperty()->LightingOn();
+
+ break;
+
+ case CullFrontFace:
+ actor->GetProperty()->SetRepresentationToSurface();
+ actor->GetProperty()->FrontfaceCullingOn();
+ actor->GetProperty()->LightingOff();
+
+ break;
+ }
+}
+
bool RegionSurface::IntersectsPlane(double p[3], double n[3]) {
if (!IntersectsBoundingBox(p, n)) return false;
diff --git a/region/RegionSurface.h b/region/RegionSurface.h
index a04ca1b..730b294 100644
--- a/region/RegionSurface.h
+++ b/region/RegionSurface.h
@@ -21,6 +21,14 @@ class RegionSurface {
void SetSmoothSurface(bool smooth);
void SetSmoothShading(bool smooth);
+ enum RenderMode {
+ Normal,
+ Wireframe,
+ CullFrontFace
+ };
+
+ void SetRenderMode(RenderMode mode);
+
bool IntersectsPlane(double p[3], double n[3]);
protected:
diff --git a/visualization/VisualizationContainer.cxx b/visualization/VisualizationContainer.cxx
index c232b61..170e94e 100644
--- a/visualization/VisualizationContainer.cxx
+++ b/visualization/VisualizationContainer.cxx
@@ -191,6 +191,11 @@ VisualizationContainer::VisualizationContainer(vtkRenderWindowInteractor* volume
windowLevelCallback->SetCallback(InteractionCallbacks::WindowLevel);
windowLevelCallback->SetClientData(this);
sliceView->GetInteractorStyle()->AddObserver(vtkCommand::WindowLevelEvent, windowLevelCallback);
+
+ vtkSmartPointer volumeWindowLevelCallback = vtkSmartPointer::New();
+ volumeWindowLevelCallback->SetCallback(InteractionCallbacks::VolumeWindowLevel);
+ volumeWindowLevelCallback->SetClientData(this);
+ volumeView->GetInteractorStyle()->AddObserver(vtkCommand::WindowLevelEvent, volumeWindowLevelCallback);
}
VisualizationContainer::~VisualizationContainer() {
@@ -1975,6 +1980,10 @@ void VisualizationContainer::SetWindowLevel(double window, double level) {
qtWindow->setWindowLevel(window, level);
}
+void VisualizationContainer::SetVolumeWindowLevel(double window, double level) {
+ volumeView->SetWindowLevel(window, level);
+}
+
void VisualizationContainer::SetVisibleOpacity(double opacity) {
volumeView->SetVisibleOpacity(opacity, filterRegions);
}
@@ -2089,7 +2098,9 @@ void VisualizationContainer::PopTempHistory() {
volumeView->SetRegions(labels, regions);
sliceView->SetSegmentationData(labels, regions);
+ SetCurrentRegion(currentRegion);
qtWindow->updateRegions(regions);
+
Render();
}
diff --git a/visualization/VisualizationContainer.h b/visualization/VisualizationContainer.h
index a9375a3..d70e8cc 100644
--- a/visualization/VisualizationContainer.h
+++ b/visualization/VisualizationContainer.h
@@ -114,6 +114,7 @@ class VisualizationContainer {
void ToggleCurrentRegionDone();
void SetWindowLevel(double window, double level);
+ void SetVolumeWindowLevel(double window, double level);
void SliceUp();
void SliceDown();
diff --git a/visualization/VolumeView.cxx b/visualization/VolumeView.cxx
index fe77648..bc79399 100644
--- a/visualization/VolumeView.cxx
+++ b/visualization/VolumeView.cxx
@@ -3,12 +3,19 @@
#include
#include
#include
+#include
#include
#include
+#include
+#include
+#include
#include
+#include
#include
#include
#include
+#include
+#include
#include
#include
#include
@@ -19,6 +26,8 @@
#include
#include
#include
+#include
+#include
#include "vtkInteractorStyleVolume.h"
@@ -29,6 +38,9 @@
#include "RegionSurface.h"
#include "RegionHighlight3D.h"
#include "RegionCollection.h"
+#include "SegmentorMath.h"
+
+#include
double rescale(double value, double min, double max) {
return min + (max - min) * value;
@@ -41,8 +53,12 @@ void VolumeView::cameraChange(vtkObject* caller, unsigned long eventId, void* cl
}
VolumeView::VolumeView(vtkRenderWindowInteractor* interactor) {
+ data = nullptr;
+ labels = nullptr;
+
smoothSurfaces = false;
smoothShading = false;
+ volumeRendering = false;
regions = nullptr;
currentRegion = nullptr;
@@ -87,6 +103,9 @@ VolumeView::VolumeView(vtkRenderWindowInteractor* interactor) {
// Interaction mode label
CreateInteractionModeLabel();
+ // Volume rendering
+ CreateVolumeRenderer();
+
// Lighting
double lightPosition[3] = { 0, 0.5, 1 };
double lightFocalPoint[3] = { 0, 0, 0 };
@@ -103,6 +122,9 @@ VolumeView::~VolumeView() {
}
void VolumeView::Reset() {
+ data = nullptr;
+ labels = nullptr;
+
SetCurrentRegion(nullptr);
HighlightRegion(nullptr);
@@ -111,10 +133,16 @@ void VolumeView::Reset() {
plane->VisibilityOff();
corners->VisibilityOff();
interactionModeLabel->VisibilityOff();
+
+ volume->VisibilityOff();
}
-void VolumeView::SetImageData(vtkImageData* data) {
+void VolumeView::SetImageData(vtkImageData* imageData) {
+ data = imageData;
+
brush->UpdateData(data);
+
+ UpdateVolumeRenderer();
}
void VolumeView::Enable(bool enable) {
@@ -147,7 +175,8 @@ void VolumeView::UpdateVoxelSize(vtkImageData* data) {
UpdateAxes(data);
}
-void VolumeView::SetRegions(vtkImageData* data, RegionCollection* newRegions) {
+void VolumeView::SetRegions(vtkImageData* imageLabels, RegionCollection* newRegions) {
+ labels = imageLabels;
regions = newRegions;
// Reset
@@ -187,6 +216,7 @@ void VolumeView::AddRegion(Region* region) {
surface->SetSmoothSurface(smoothSurfaces);
surface->SetSmoothShading(smoothShading);
+ surface->SetRenderMode(volumeRendering ? RegionSurface::CullFrontFace : RegionSurface::Normal);
highlight->GetActor()->VisibilityOff();
highlight->SetCamera(renderer->GetActiveCamera());
@@ -297,6 +327,26 @@ void VolumeView::ToggleSmoothShading() {
SetSmoothShading(!smoothShading);
}
+bool VolumeView::GetVolumeRendering() {
+ return volumeRendering;
+}
+
+void VolumeView::SetVolumeRendering(bool useVolumeRendering) {
+ volumeRendering = useVolumeRendering;
+
+ volume->SetVisibility(volumeRendering);
+
+ for (RegionCollection::Iterator it = regions->Begin(); it != regions->End(); it++) {
+ regions->Get(it)->GetSurface()->SetRenderMode(volumeRendering ? RegionSurface::CullFrontFace : RegionSurface::Normal);
+ }
+
+ Render();
+}
+
+void VolumeView::ToggleVolumeRendering() {
+ SetVolumeRendering(!volumeRendering);
+}
+
void VolumeView::UpdatePlane() {
vtkCamera* cam = renderer->GetActiveCamera();
@@ -331,16 +381,18 @@ void VolumeView::SetVisibleOpacity(double opacity, bool apply) {
}
void VolumeView::UpdateVisibleOpacity(bool apply) {
- if (!regions) return;
-
- for (RegionCollection::Iterator it = regions->Begin(); it != regions->End(); it++) {
- Region* region = regions->Get(it);
- RegionSurface* surface = region->GetSurface();
+ if (regions) {
+ for (RegionCollection::Iterator it = regions->Begin(); it != regions->End(); it++) {
+ Region* region = regions->Get(it);
+ RegionSurface* surface = region->GetSurface();
- double o = !apply || region == currentRegion ? 1 : visibleOpacity;
+ double o = !apply || region == currentRegion ? 1 : visibleOpacity;
- surface->GetActor()->GetProperty()->SetOpacity(o);
+ surface->GetActor()->GetProperty()->SetOpacity(o);
+ }
}
+
+ UpdateVolumeMask(apply);
}
void VolumeView::SetBrushRadius(int radius) {
@@ -348,6 +400,81 @@ void VolumeView::SetBrushRadius(int radius) {
brush->GetActor()->SetVisibility(radius > 1);
}
+void VolumeView::SetWindowLevel(double window, double level) {
+ UpdateVolumeRenderingTransferFunctions(level - window * 0.5, level + window * 0.5);
+}
+
+void VolumeView::UpdateVolumeMask(bool filter) {
+ if (regions && currentRegion && filter) {
+ vtkSmartPointer mask = volumeCopy->GetOutput();
+
+ const int* extent = mask->GetExtent();
+
+ for (int i = extent[0]; i <= extent[1]; i++) {
+ for (int j = extent[2]; j <= extent[3]; j++) {
+ for (int k = extent[4]; k <= extent[5]; k++) {
+ unsigned char* p = static_cast(mask->GetScalarPointer(i, j, k));
+ *p = 0;
+ }
+ }
+ }
+
+ for (RegionCollection::Iterator it = regions->Begin(); it != regions->End(); it++) {
+ Region* region = regions->Get(it);
+ const int* extent = region->GetExtent();
+
+ if (region == currentRegion || region->GetVisible()) {
+ for (int i = extent[0]; i <= extent[1]; i++) {
+ for (int j = extent[2]; j <= extent[3]; j++) {
+ for (int k = extent[4]; k <= extent[5]; k++) {
+ // Current label
+ unsigned char* p = static_cast(mask->GetScalarPointer(i, j, k));
+ *p = 1;
+ }
+ }
+ }
+ }
+ }
+
+ mask->Modified();
+
+ volumeMask->SetMaskInputData(mask);
+
+ volumeMapper->SetInputConnection(volumeMask->GetOutputPort());
+ }
+ else {
+ volumeMapper->SetInputDataObject(data);
+ }
+}
+
+void VolumeView::UpdateVolumeRenderingTransferFunctions(double x1, double x2) {
+ // Opacity
+ volumeOpacity->RemoveAllPoints();
+ volumeOpacity->AddPoint(x1, 0.0);
+ volumeOpacity->AddPoint(x2, 0.5);
+
+ // Colors
+ // Paraview diverging
+ const int numColors = 3;
+ double colors[numColors][3] = {
+ { 59, 76, 192 },
+ { 221, 221, 221 },
+ { 180, 4, 38 }
+ };
+
+ for (int i = 0; i < numColors; i++) {
+ for (int j = 0; j < 3; j++) {
+ colors[i][j] /= 255.0;
+ }
+ }
+
+ volumeColor->RemoveAllPoints();
+ for (int i = 0; i < numColors; i++) {
+ double x = x1 + (double)i / (numColors - 1) * (x2 - x1);
+ volumeColor->AddRGBPoint(x, colors[i][0], colors[i][1], colors[i][2]);
+ }
+}
+
void VolumeView::Render() {
renderer->GetRenderWindow()->Render();
}
@@ -371,6 +498,67 @@ void VolumeView::CreateInteractionModeLabel() {
renderer->AddActor2D(interactionModeLabel);
}
+void VolumeView::CreateVolumeRenderer() {
+ volumeCopy = vtkSmartPointer::New();
+ volumeCopy->SetOutputScalarTypeToUnsignedChar();
+
+ volumeMask = vtkSmartPointer::New();
+ volumeMask->SetMaskInputData(volumeCopy->GetOutput());
+
+ volumeMapper = vtkSmartPointer::New();
+ volumeMapper->SetBlendModeToComposite();
+ volumeMapper->AutoAdjustSampleDistancesOn();
+ volumeMapper->SetSampleDistance(0.1);
+ volumeMapper->SetInteractiveSampleDistance(0.1);
+ volumeMapper->SetImageSampleDistance(0.5);
+ volumeMapper->SetMaximumImageSampleDistance(2);
+
+ volumeOpacity = vtkSmartPointer::New();
+
+ volumeColor = vtkSmartPointer::New();
+
+ vtkSmartPointer volumeProperty = vtkSmartPointer::New();
+ volumeProperty->ShadeOff();
+ volumeProperty->SetInterpolationTypeToLinear();
+ volumeProperty->SetScalarOpacity(volumeOpacity);
+ volumeProperty->SetColor(volumeColor);
+
+ volume = vtkSmartPointer::New();
+ volume->SetMapper(volumeMapper);
+ volume->SetProperty(volumeProperty);
+ volume->VisibilityOff();
+ volume->PickableOff();
+
+ renderer->AddVolume(volume);
+}
+
+void VolumeView::UpdateVolumeRenderer() {
+ if (!data) return;
+
+ // Start with full volume
+ volumeCopy->SetInputDataObject(data);
+ volumeCopy->Update();
+
+ volumeMask->SetInputDataObject(data);
+
+ volumeMapper->SetInputDataObject(data);
+ volume->SetVisibility(volumeRendering);
+
+ // Initialize window level
+ SegmentorMath::OtsuValues otsu = SegmentorMath::OtsuThreshold(data);
+
+ double minValue = otsu.backgroundMean;
+ double maxValue = otsu.foregroundMean;
+
+ double range = maxValue - minValue;;
+
+ double window = range;
+ double level = minValue + range / 2;
+
+ SetWindowLevel(window, level);
+ style->SetWindowLevel(window, level);
+}
+
void VolumeView::CreatePlane() {
planeSource = vtkSmartPointer::New();
planeSource->SetXResolution(100);
diff --git a/visualization/VolumeView.h b/visualization/VolumeView.h
index 7f63921..c6ce198 100644
--- a/visualization/VolumeView.h
+++ b/visualization/VolumeView.h
@@ -9,14 +9,23 @@
class vtkActor;
class vtkBox;
+class vtkColorTransferFunction;
class vtkCubeAxesActor;
+class vtkImageCast;
class vtkImageData;
+class vtkLookupTable;
+class vtkImageMapToColors;
+class vtkImageMask;
+class vtkFixedPointVolumeRayCastMapper;
+class vtkPiecewiseFunction;
class vtkPlaneSource;
class vtkObject;
class vtkOutlineCornerFilter;
+class vtkPassThrough;
class vtkRenderer;
class vtkRenderWindowInteractor;
class vtkTextActor;
+class vtkVolume;
class vtkInteractorStyleVolume;
@@ -31,8 +40,8 @@ class VolumeView {
VolumeView(vtkRenderWindowInteractor* interactor);
~VolumeView();
- void SetImageData(vtkImageData* data);
- void SetRegions(vtkImageData* data, RegionCollection* newRegions);
+ void SetImageData(vtkImageData* imageData);
+ void SetRegions(vtkImageData* imageLabels, RegionCollection* newRegions);
void AddRegion(Region* region);
void Reset();
@@ -59,6 +68,10 @@ class VolumeView {
void SetSmoothShading(bool smooth);
void ToggleSmoothShading();
+ bool GetVolumeRendering();
+ void SetVolumeRendering(bool useVolumeRendering);
+ void ToggleVolumeRendering();
+
bool GetShowPlane();
void SetShowPlane(bool show);
void ToggleShowPlane();
@@ -70,14 +83,23 @@ class VolumeView {
void SetBrushRadius(int radius);
+ void SetWindowLevel(double window, double level);
+
+ void UpdateVolumeMask(bool filter);
+
void Render();
vtkRenderer* GetRenderer();
vtkInteractorStyleVolume* GetInteractorStyle();
protected:
+ // Data
+ vtkSmartPointer data;
+ vtkSmartPointer labels;
+
bool smoothSurfaces;
bool smoothShading;
+ bool volumeRendering;
Region* currentRegion;
Region* highlightRegion;
@@ -116,6 +138,17 @@ class VolumeView {
// Interaction mode label
vtkSmartPointer interactionModeLabel;
void CreateInteractionModeLabel();
+
+ // Volume rendering
+ vtkSmartPointer volumeCopy;
+ vtkSmartPointer volumeMask;
+ vtkSmartPointer volumeMapper;
+ vtkSmartPointer volume;
+ vtkSmartPointer volumeOpacity;
+ vtkSmartPointer volumeColor;
+ void CreateVolumeRenderer();
+ void UpdateVolumeRenderer();
+ void UpdateVolumeRenderingTransferFunctions(double x1, double x2);
double visibleOpacity;