diff --git a/CMakeLists.txt b/CMakeLists.txt
index 188d751..ad5a38a 100644
--- a/CMakeLists.txt
+++ b/CMakeLists.txt
@@ -4,7 +4,7 @@ if(POLICY CMP0020)
cmake_policy(SET CMP0020 NEW)
endif()
-set(SEGMENTOR_VERSION 0.4.3)
+set(SEGMENTOR_VERSION 0.4.4)
project(Segmentor VERSION ${SEGMENTOR_VERSION})
diff --git a/Segmentor.qrc b/Segmentor.qrc
index d00de0f..a9ebe0e 100644
--- a/Segmentor.qrc
+++ b/Segmentor.qrc
@@ -26,5 +26,6 @@
icons/icon_filter_regions.png
icons/icon_reset_view.png
icons/icon_volume_render.png
+ icons/icon_dot.png
\ No newline at end of file
diff --git a/icons/icon_dot.png b/icons/icon_dot.png
new file mode 100644
index 0000000..1336c9a
Binary files /dev/null and b/icons/icon_dot.png differ
diff --git a/icons/icon_dot.svg b/icons/icon_dot.svg
new file mode 100644
index 0000000..43a1150
--- /dev/null
+++ b/icons/icon_dot.svg
@@ -0,0 +1,4 @@
+
\ No newline at end of file
diff --git a/interaction/InteractionCallbacks.cxx b/interaction/InteractionCallbacks.cxx
index 42a7a8c..6e969f7 100644
--- a/interaction/InteractionCallbacks.cxx
+++ b/interaction/InteractionCallbacks.cxx
@@ -246,6 +246,21 @@ void InteractionCallbacks::Visible(vtkObject* caller, unsigned long eventId, voi
}
}
+void InteractionCallbacks::Dot(vtkObject* caller, unsigned long eventId, void* clientData, void *callData) {
+ vtkRenderWindowInteractor* rwi = static_cast(caller)->GetInteractor();
+ VisualizationContainer* vis = static_cast(clientData);
+
+ // Pick at the mouse location provided by the interactor
+ int pick = Pick(rwi);
+
+ if (pick) {
+ // Get the position for the pick event
+ double p[3];
+ PickPosition(p);
+ vis->SetDotAnnotation(p);
+ }
+}
+
void InteractionCallbacks::MouseMove(vtkObject* caller, unsigned long eventId, void* clientData, void *callData) {
vtkRenderWindowInteractor* rwi = static_cast(caller);
VisualizationContainer* vis = static_cast(clientData);
diff --git a/interaction/InteractionCallbacks.h b/interaction/InteractionCallbacks.h
index 9f0dc5e..894b9d0 100644
--- a/interaction/InteractionCallbacks.h
+++ b/interaction/InteractionCallbacks.h
@@ -30,6 +30,7 @@ class InteractionCallbacks {
static void Grow(vtkObject* caller, unsigned long eventId, void* clientData, void *callData);
static void Done(vtkObject* caller, unsigned long eventId, void* clientData, void *callData);
static void Visible(vtkObject* caller, unsigned long eventId, void* clientData, void *callData);
+ static void Dot(vtkObject* caller, unsigned long eventId, void* clientData, void *callData);
static void MouseMove(vtkObject* caller, unsigned long eventId, void* clientData, void *callData);
diff --git a/interaction/InteractionEnums.h b/interaction/InteractionEnums.h
index 70e0d03..ebbcafa 100644
--- a/interaction/InteractionEnums.h
+++ b/interaction/InteractionEnums.h
@@ -9,7 +9,8 @@ enum InteractionMode {
MergeMode,
SplitMode,
GrowMode,
- VisibleMode
+ VisibleMode,
+ DotMode
};
enum FilterMode {
diff --git a/interaction/vtkInteractorStyleSlice.cxx b/interaction/vtkInteractorStyleSlice.cxx
index ff035f3..8977a59 100644
--- a/interaction/vtkInteractorStyleSlice.cxx
+++ b/interaction/vtkInteractorStyleSlice.cxx
@@ -242,6 +242,30 @@ void vtkInteractorStyleSlice::EndVisible()
this->StopState();
}
+//----------------------------------------------------------------------------
+void vtkInteractorStyleSlice::StartDot()
+{
+ if (this->State != VTKIS_NONE)
+ {
+ return;
+ }
+ this->StartState(VTKIS_DOT_SLICE);
+}
+
+//----------------------------------------------------------------------------
+void vtkInteractorStyleSlice::EndDot()
+{
+ if (this->State != VTKIS_DOT_SLICE)
+ {
+ return;
+ }
+ if (this->HandleObservers)
+ {
+ this->InvokeEvent(DotEvent, nullptr);
+ }
+ this->StopState();
+}
+
//----------------------------------------------------------------------------
void vtkInteractorStyleSlice::WindowLevel()
{
@@ -414,6 +438,10 @@ void vtkInteractorStyleSlice::OnLeftButtonDown()
{
this->StartVisible();
}
+ else if (this->Mode == DotMode)
+ {
+ this->StartDot();
+ }
else
{
// Rotate
@@ -474,6 +502,10 @@ void vtkInteractorStyleSlice::OnLeftButtonUp() {
case VTKIS_VISIBLE_SLICE:
this->EndVisible();
break;
+
+ case VTKIS_DOT_SLICE:
+ this->EndDot();
+ break;
}
// Call parent to handle all other states and perform additional work
diff --git a/interaction/vtkInteractorStyleSlice.h b/interaction/vtkInteractorStyleSlice.h
index afbc91f..46ef593 100644
--- a/interaction/vtkInteractorStyleSlice.h
+++ b/interaction/vtkInteractorStyleSlice.h
@@ -20,6 +20,7 @@ class vtkCellPicker;
#define VTKIS_MERGE_SLICE 2053
#define VTKIS_GROW_SLICE 2054
#define VTKIS_VISIBLE_SLICE 2055
+#define VTKIS_DOT_SLICE 2056
class vtkInteractorStyleSlice : public vtkInteractorStyleImage {
public:
@@ -56,6 +57,8 @@ class vtkInteractorStyleSlice : public vtkInteractorStyleImage {
virtual void EndGrow();
virtual void StartVisible();
virtual void EndVisible();
+ virtual void StartDot();
+ virtual void EndDot();
virtual void WindowLevel() override;
double GetWindow();
@@ -79,7 +82,8 @@ class vtkInteractorStyleSlice : public vtkInteractorStyleImage {
AddEvent,
MergeEvent,
GrowEvent,
- VisibleEvent
+ VisibleEvent,
+ DotEvent
};
protected:
diff --git a/interaction/vtkInteractorStyleVolume.cxx b/interaction/vtkInteractorStyleVolume.cxx
index 40a63b6..93ae071 100644
--- a/interaction/vtkInteractorStyleVolume.cxx
+++ b/interaction/vtkInteractorStyleVolume.cxx
@@ -383,7 +383,7 @@ void vtkInteractorStyleVolume::OnLeftButtonDown()
else if (this->Mode == VisibleMode)
{
this->StartVisible();
- }
+ }
else
{
// If shift is held down, start window level
diff --git a/qt/FeedbackDialog.cxx b/qt/FeedbackDialog.cxx
index 0c1f4a6..58c8789 100644
--- a/qt/FeedbackDialog.cxx
+++ b/qt/FeedbackDialog.cxx
@@ -39,10 +39,13 @@ FeedbackDialog::FeedbackDialog(QWidget* parent, VisualizationContainer* visualiz
QObject::connect(table, &FeedbackTable::highlightRegion, this, &FeedbackDialog::on_highlightRegion);
QObject::connect(table, &FeedbackTable::countChanged, this, &FeedbackDialog::on_countChanged);
- // Shortcut
- QShortcut* shortcut = new QShortcut(QKeySequence(Qt::ALT + Qt::Key_V), this);
+ // Shortcuts
+ QShortcut* shortcut = new QShortcut(QKeySequence(Qt::Key_Period), this);
QObject::connect(shortcut, &QShortcut::activated, this, &FeedbackDialog::on_verifiedShortcut);
+ QShortcut* closeShortcut = new QShortcut(QKeySequence(Qt::CTRL + Qt::Key_F), this);
+ QObject::connect(closeShortcut, &QShortcut::activated, this, &FeedbackDialog::close);
+
updateRegions();
}
@@ -110,5 +113,5 @@ void FeedbackDialog::on_countChanged(int count) {
}
void FeedbackDialog::on_verifiedShortcut() {
- //printf("DLKJF");
+ visualizationContainer->ToggleCurrentRegionVerified();
}
\ No newline at end of file
diff --git a/qt/MainWindow.cxx b/qt/MainWindow.cxx
index d13eeea..b2b1bca 100644
--- a/qt/MainWindow.cxx
+++ b/qt/MainWindow.cxx
@@ -88,6 +88,7 @@ MainWindow::MainWindow() {
// Settings dialog
settingsDialog = new SettingsDialog(this, visualizationContainer);
+ QObject::connect(settingsDialog, &SettingsDialog::enableDotAnnotationChanged, this, &MainWindow::on_enableDotAnnotationChanged);
// Feedback dialog
feedbackDialog = new FeedbackDialog(this, visualizationContainer);
@@ -588,6 +589,32 @@ void MainWindow::on_actionSegment_Volume_triggered() {
}
}
+void MainWindow::on_actionApply_Dot_Annotation_triggered() {
+ QMessageBox message;
+ message.setIcon(QMessageBox::Warning);
+ message.setText("Apply dot annotation.");
+ message.setInformativeText(
+ "This operation will replace each region with a single voxel at the region center.\n\n"
+ "Do you wish to continue?"
+ );
+ message.setStandardButtons(QMessageBox::Yes | QMessageBox::No);
+ message.setDefaultButton(QMessageBox::Save);
+ int ret = message.exec();
+
+ switch (ret) {
+ case QMessageBox::Yes:
+ visualizationContainer->ApplyDotAnnotation();
+ break;
+
+ case QMessageBox::No:
+ break;
+
+ default:
+ // Should never be reached
+ break;
+ }
+}
+
void MainWindow::on_actionExit_triggered() {
close();
}
@@ -691,6 +718,10 @@ void MainWindow::on_actionVisible() {
visualizationContainer->SetInteractionMode(VisibleMode);
}
+void MainWindow::on_actionDot() {
+ visualizationContainer->SetInteractionMode(DotMode);
+}
+
void MainWindow::on_actionUpdate() {
visualizationContainer->RelabelCurrentRegion();
}
@@ -807,6 +838,22 @@ void MainWindow::on_brushRadiusSpinBox_valueChanged(int value) {
visualizationContainer->SetBrushRadius(value);
}
+void MainWindow::on_enableDotAnnotationChanged(bool enable) {
+ // Enable apply dot annotation
+ actionApply_Dot_Annotation->setEnabled(enable);
+
+ // Enable dot mode
+ QList actions = modeBarWidget->findChild()->actions();
+
+ for (int i = 0; i < actions.length(); i++) {
+ QAction* action = actions[i];
+ if (action->objectName() == "dotModeAction") {
+ action->setEnabled(enable);
+ break;
+ }
+ }
+}
+
void MainWindow::on_toggleView() {
if (qvtkWidgetLeft->isVisible()) {
qvtkWidgetLeft->setVisible(false);
@@ -1059,6 +1106,11 @@ void MainWindow::createModeBar() {
toolBar->addAction(createActionIcon(":/icons/icon_merge.png", "Merge with current region (m)", "m", interactionModeGroup, currentMode == MergeMode, &MainWindow::on_actionMerge));
toolBar->addAction(createActionIcon(":/icons/icon_grow.png", "Grow / shrink region (g)", "g", interactionModeGroup, currentMode == GrowMode, &MainWindow::on_actionGrow));
toolBar->addAction(createActionIcon(":/icons/icon_visible.png", "Toggle region visibility (v)", "v", interactionModeGroup, currentMode == VisibleMode, &MainWindow::on_actionVisible));
+
+ QAction* dotAction = createActionIcon(":/icons/icon_dot.png", "Dot annotation mode (Ctrl + d)", QKeySequence(Qt::CTRL + Qt::Key_D), interactionModeGroup, currentMode == DotMode, &MainWindow::on_actionDot);
+ dotAction->setObjectName("dotModeAction");
+ dotAction->setEnabled(false);
+ toolBar->addAction(dotAction);
toolBar->addSeparator();
toolBar->addWidget(createLabel("Actions", 0, 0, 5, 5));
@@ -1163,6 +1215,17 @@ QAction* MainWindow::createActionIcon(const QString& fileName, const QString& te
return action;
}
+QAction* MainWindow::createActionIcon(const QString& fileName, const QString& text, const QKeySequence& shortcut, QActionGroup* group, bool checked, void (MainWindow::*slot)()) {
+ QAction* action = new QAction(QIcon(fileName), text, group);
+ action->setShortcut(QKeySequence(shortcut));
+ action->setCheckable(true);
+ action->setChecked(checked);
+
+ QObject::connect(action, &QAction::triggered, this, slot);
+
+ return action;
+}
+
QLabel* MainWindow::createLabel(const QString& text, int topMargin, int bottomMargin, int leftMargin, int rightMargin) {
QString style = QStringLiteral("color:#999;margin-top:%1px;margin-bottom:%2px;margin-left:%3px;margin-right:%4px")
.arg(topMargin).arg(bottomMargin).arg(leftMargin).arg(rightMargin);
diff --git a/qt/MainWindow.h b/qt/MainWindow.h
index eea47cb..58ef7c2 100644
--- a/qt/MainWindow.h
+++ b/qt/MainWindow.h
@@ -62,6 +62,7 @@ public slots:
virtual void on_actionRedo_triggered();
virtual void on_actionSegment_Volume_triggered();
+ virtual void on_actionApply_Dot_Annotation_triggered();
virtual void on_actionExit_triggered();
@@ -90,6 +91,7 @@ public slots:
virtual void on_actionGrow();
virtual void on_actionDone();
virtual void on_actionVisible();
+ virtual void on_actionDot();
// Action events
virtual void on_actionUpdate();
@@ -142,6 +144,8 @@ public slots:
virtual void on_brushRadiusDown();
virtual void on_brushRadiusUp();
+ virtual void on_enableDotAnnotationChanged(bool enable);
+
signals:
void windowLevelChanged(double window, double value);
@@ -184,6 +188,7 @@ public slots:
QAction* createActionIcon(const QString& fileName, const QString& text, const QKeySequence& shortcut, void (MainWindow::*slot)());
QAction* createActionIcon(const QString& fileName, const QString& text, const QString& shortcut, bool checked, void (MainWindow::*slot)(bool));
QAction* createActionIcon(const QString& fileName, const QString& text, const QString& shortcut, QActionGroup* group, bool checked, void (MainWindow::*slot)());
+ QAction* createActionIcon(const QString& fileName, const QString& text, const QKeySequence& shortcut, QActionGroup* group, bool checked, void (MainWindow::*slot)());
QLabel* createLabel(const QString& text, int topMargin = 10, int bottomMargin = 5, int leftMargin = 0, int rightMargin = 0);
InteractionMode navEditMode;
diff --git a/qt/MainWindow.ui b/qt/MainWindow.ui
index 99d3cca..d2445f8 100644
--- a/qt/MainWindow.ui
+++ b/qt/MainWindow.ui
@@ -398,6 +398,7 @@
Analyze
+
diff --git a/qt/SettingsDialog.cxx b/qt/SettingsDialog.cxx
index 1b2aee8..2cdb72c 100644
--- a/qt/SettingsDialog.cxx
+++ b/qt/SettingsDialog.cxx
@@ -114,4 +114,9 @@ void SettingsDialog::on_gradientOpacityCheckBox_stateChanged(int state) {
void SettingsDialog::on_autoAdjustSamplingCheckBox_stateChanged(int state) {
visualizationContainer->GetVolumeView()->SetVolumeRenderingAutoAdjustSampling(state != 0);
+}
+
+
+void SettingsDialog::on_enableDotAnnotationCheckBox_stateChanged(int state) {
+ emit enableDotAnnotationChanged(state != 0);
}
\ No newline at end of file
diff --git a/qt/SettingsDialog.h b/qt/SettingsDialog.h
index 396fa18..93aba9d 100644
--- a/qt/SettingsDialog.h
+++ b/qt/SettingsDialog.h
@@ -40,6 +40,11 @@ public slots:
virtual void on_gradientOpacityCheckBox_stateChanged(int state);
virtual void on_autoAdjustSamplingCheckBox_stateChanged(int state);
+ virtual void on_enableDotAnnotationCheckBox_stateChanged(int state);
+
+signals:
+ void enableDotAnnotationChanged(bool enable);
+
protected:
VisualizationContainer* visualizationContainer;
};
diff --git a/qt/SettingsDialog.ui b/qt/SettingsDialog.ui
index 3fa258e..3971fe4 100644
--- a/qt/SettingsDialog.ui
+++ b/qt/SettingsDialog.ui
@@ -7,7 +7,7 @@
0
0
314
- 367
+ 425
@@ -330,6 +330,26 @@
+ -
+
+
+ Dot annotation
+
+
+
-
+
+
-
+
+
+ Enable dot annotation
+
+
+
+
+
+
+
+
-
diff --git a/region/Region.cxx b/region/Region.cxx
index 1529a1b..60dd8a7 100644
--- a/region/Region.cxx
+++ b/region/Region.cxx
@@ -25,6 +25,8 @@
#include "RegionOutline.h"
#include "RegionVoxelOutlines.h"
#include "RegionHighlight3D.h"
+#include "RegionCenter3D.h"
+#include "RegionCenter2D.h"
Region::Region(unsigned short regionLabel, double regionColor[3], vtkImageData* inputData, const int* regionExtent) {
visible = false;
@@ -72,6 +74,8 @@ Region::Region(unsigned short regionLabel, double regionColor[3], vtkImageData*
outline = new RegionOutline(this, color);
voxelOutlines = new RegionVoxelOutlines(this, color);
highlight3D = new RegionHighlight3D(this, color);
+ center3D = new RegionCenter3D(this, color);
+ center2D = new RegionCenter2D(this, color);
#ifdef SHOW_REGION_BOX
// Outline for testing bounding box
@@ -118,6 +122,8 @@ Region::Region(const RegionInfo& info, vtkImageData* inputData) {
outline = new RegionOutline(this, color);
voxelOutlines = new RegionVoxelOutlines(this, color);
highlight3D = new RegionHighlight3D(this, color);
+ center3D = new RegionCenter3D(this, color);
+ center2D = new RegionCenter2D(this, color);
}
Region::~Region() {
@@ -137,6 +143,8 @@ Region::~Region() {
delete outline;
delete voxelOutlines;
delete highlight3D;
+ delete center3D;
+ delete center2D;
}
vtkAlgorithmOutput* Region::GetOutput() {
@@ -198,6 +206,14 @@ RegionHighlight3D* Region::GetHighlight3D() {
return highlight3D;
}
+RegionCenter3D* Region::GetCenter3D() {
+ return center3D;
+}
+
+RegionCenter2D* Region::GetCenter2D() {
+ return center2D;
+}
+
vtkSmartPointer Region::GetText() {
return text;
}
@@ -425,6 +441,8 @@ void Region::UpdateColor() {
outline->GetActor()->GetProperty()->SetColor(currentColor);
voxelOutlines->GetActor()->GetProperty()->SetColor(currentColor);
highlight3D->GetActor()->GetProperty()->SetColor(currentColor);
+ center3D->GetActor()->GetProperty()->SetColor(currentColor);
+ center2D->GetActor()->GetProperty()->SetColor(currentColor);
}
void Region::ShowText(bool show) {
@@ -456,6 +474,11 @@ void Region::ShowText(bool show) {
}
}
+void Region::ShowCenter(bool show) {
+ center2D->GetActor()->SetVisibility(show);
+ center3D->GetActor()->SetVisibility(show);
+}
+
unsigned short Region::GetLabel() {
return label;
}
@@ -507,7 +530,10 @@ void Region::GetExtent(int outExtent[6]) {
}
double* Region::GetCenter() {
- return voi->GetOutput()->GetCenter();
+ center[0] = (extent[0] + extent[1]) / 2.0;
+ center[1] = (extent[2] + extent[3]) / 2.0;
+ center[2] = (extent[4] + extent[5]) / 2.0;
+ return center;
}
double Region::GetLength() {
@@ -612,6 +638,28 @@ void Region::SetComment(const std::string& commentString) {
text->SetInput(LabelString().c_str());
}
+void Region::ApplyDot() {
+ double* c = GetCenter();
+ int x = (int)c[0];
+ int y = (int)c[1];
+ int z = (int)c[2];
+
+ ClearLabels();
+
+ unsigned short* p = static_cast(data->GetScalarPointer(x, y, z));
+
+ *p = label;
+
+ data->Modified();
+
+ int ext[6] = { x, x, y, y, z, z, };
+ SetExtent(ext);
+ voi->Update();
+
+ center3D->Update();
+ center2D->Update(center2D->GetActor()->GetPosition()[2]);
+}
+
void Region::ClearLabels() {
for (int i = extent[0]; i <= extent[1]; i++) {
for (int j = extent[2]; j <= extent[3]; j++) {
diff --git a/region/Region.h b/region/Region.h
index 198d151..910014f 100644
--- a/region/Region.h
+++ b/region/Region.h
@@ -23,6 +23,8 @@ class RegionSurface;
class RegionOutline;
class RegionVoxelOutlines;
class RegionHighlight3D;
+class RegionCenter3D;
+class RegionCenter2D;
class Feedback;
class Region {
@@ -40,6 +42,8 @@ class Region {
RegionOutline* GetOutline();
RegionVoxelOutlines* GetVoxelOutlines();
RegionHighlight3D* GetHighlight3D();
+ RegionCenter3D* GetCenter3D();
+ RegionCenter2D* GetCenter2D();
vtkSmartPointer GetText();
vtkSmartPointer GetZSlice(int z);
@@ -70,6 +74,8 @@ class Region {
void ShowText(bool show);
+ void ShowCenter(bool show);
+
unsigned short GetLabel();
int GetNumVoxels();
int GetNumVoxels(int slice);
@@ -89,10 +95,13 @@ class Region {
const std::string& GetComment();
void SetComment(const std::string& commentString);
+ void ApplyDot();
+
protected:
unsigned short label;
double color[3];
int extent[6];
+ double center[3];
bool visible;
bool modified;
@@ -108,6 +117,8 @@ class Region {
RegionOutline* outline;
RegionVoxelOutlines* voxelOutlines;
RegionHighlight3D* highlight3D;
+ RegionCenter3D* center3D;
+ RegionCenter2D* center2D;
vtkSmartPointer text;
#ifdef SHOW_REGION_BOX
diff --git a/region/RegionCenter2D.cxx b/region/RegionCenter2D.cxx
new file mode 100644
index 0000000..9b09133
--- /dev/null
+++ b/region/RegionCenter2D.cxx
@@ -0,0 +1,77 @@
+#include "RegionCenter2D.h"
+
+#include
+#include
+#include
+#include
+#include
+#include
+#include
+
+#include "Region.h"
+
+RegionCenter2D::RegionCenter2D(Region* inputRegion, double color[3]) {
+ region = inputRegion;
+
+ vtkSmartPointer cone = vtkSmartPointer::New();
+ cone->SetResolution(16);
+
+ vtkSmartPointer sphere = vtkSmartPointer::New();
+ sphere->SetThetaResolution(16);
+ sphere->SetPhiResolution(16);
+
+ coneMapper = vtkSmartPointer::New();
+ coneMapper->ScalarVisibilityOff();
+ coneMapper->SetInputConnection(cone->GetOutputPort());
+
+ sphereMapper = vtkSmartPointer::New();
+ sphereMapper->ScalarVisibilityOff();
+ sphereMapper->SetInputConnection(sphere->GetOutputPort());
+
+ actor = vtkSmartPointer::New();
+ actor->GetProperty()->SetColor(color);
+ actor->GetProperty()->SetDiffuse(0.0);
+ actor->GetProperty()->SetAmbient(1.0);
+ actor->GetProperty()->SetSpecular(0.0);
+ actor->PickableOff();
+ actor->VisibilityOff();
+
+ Update(region->GetCenter()[2]);
+}
+
+RegionCenter2D::~RegionCenter2D() {
+ while (actor->GetNumberOfConsumers() > 0) {
+ vtkRenderer::SafeDownCast(actor->GetConsumer(0))->RemoveActor(actor);
+ }
+}
+
+vtkSmartPointer RegionCenter2D::GetActor() {
+ return actor;
+}
+
+void RegionCenter2D::Update(double z) {
+ double r = 1.25;
+
+ double numSlices = 4.0;
+
+ double* c = region->GetCenter();
+
+ double d = fabs(z - c[2]);
+
+ double s = (numSlices - d) / numSlices;
+ if (s < 0) s = 0;
+
+ actor->SetPosition(c[0], c[1], z);
+ actor->SetScale(r * s);
+
+ double epsilon = 0.9;
+ if (d < epsilon) {
+ // Sphere
+ actor->SetMapper(sphereMapper);
+ }
+ else {
+ // Cone
+ actor->SetMapper(coneMapper);
+ actor->SetOrientation(0, 0, z > c[2] ? 90 : -90);
+ }
+}
\ No newline at end of file
diff --git a/region/RegionCenter2D.h b/region/RegionCenter2D.h
new file mode 100644
index 0000000..b2f2fa4
--- /dev/null
+++ b/region/RegionCenter2D.h
@@ -0,0 +1,29 @@
+#ifndef RegionCenter2D_H
+#define RegionCenter2D_H
+
+#include "vtkSmartPointer.h"
+
+class vtkActor;
+class vtkCamera;
+class vtkMapper;
+
+class Region;
+
+class RegionCenter2D {
+public:
+ RegionCenter2D(Region* inputRegion, double color[3]);
+ ~RegionCenter2D();
+
+ vtkSmartPointer GetActor();
+
+ void Update(double z);
+
+protected:
+ Region* region;
+
+ vtkSmartPointer coneMapper;
+ vtkSmartPointer sphereMapper;
+ vtkSmartPointer actor;
+};
+
+#endif
diff --git a/region/RegionCenter3D.cxx b/region/RegionCenter3D.cxx
new file mode 100644
index 0000000..43faf61
--- /dev/null
+++ b/region/RegionCenter3D.cxx
@@ -0,0 +1,47 @@
+#include "RegionCenter3D.h"
+
+#include
+#include
+#include
+#include
+#include
+#include
+
+#include "Region.h"
+
+RegionCenter3D::RegionCenter3D(Region* inputRegion, double color[3]) {
+ region = inputRegion;
+
+ sphere = vtkSmartPointer::New();
+ sphere->SetThetaResolution(16);
+ sphere->SetPhiResolution(16);
+
+ vtkSmartPointer mapper = vtkSmartPointer::New();
+ mapper->ScalarVisibilityOff();
+ mapper->SetInputConnection(sphere->GetOutputPort());
+
+ actor = vtkSmartPointer::New();
+ actor->SetMapper(mapper);
+ actor->SetScale(1.25);
+ actor->GetProperty()->SetColor(color);
+ actor->GetProperty()->SetDiffuse(1.0);
+ actor->GetProperty()->SetAmbient(0.0);
+ actor->GetProperty()->SetSpecular(0.0);
+ actor->VisibilityOff();
+
+ Update();
+}
+
+RegionCenter3D::~RegionCenter3D() {
+ while (actor->GetNumberOfConsumers() > 0) {
+ vtkRenderer::SafeDownCast(actor->GetConsumer(0))->RemoveActor(actor);
+ }
+}
+
+vtkSmartPointer RegionCenter3D::GetActor() {
+ return actor;
+}
+
+void RegionCenter3D::Update() {
+ actor->SetPosition(region->GetCenter());
+}
\ No newline at end of file
diff --git a/region/RegionCenter3D.h b/region/RegionCenter3D.h
new file mode 100644
index 0000000..3a3c292
--- /dev/null
+++ b/region/RegionCenter3D.h
@@ -0,0 +1,28 @@
+#ifndef RegionCenter3D_H
+#define RegionCenter3D_H
+
+#include "vtkSmartPointer.h"
+
+class vtkActor;
+class vtkCamera;
+class vtkSphereSource;
+
+class Region;
+
+class RegionCenter3D {
+public:
+ RegionCenter3D(Region* inputRegion, double color[3]);
+ ~RegionCenter3D();
+
+ vtkSmartPointer GetActor();
+
+ void Update();
+
+protected:
+ Region* region;
+
+ vtkSmartPointer sphere;
+ vtkSmartPointer actor;
+};
+
+#endif
diff --git a/region/RegionInfo.cxx b/region/RegionInfo.cxx
index 7f923ad..a6acf38 100644
--- a/region/RegionInfo.cxx
+++ b/region/RegionInfo.cxx
@@ -2,7 +2,7 @@
#include "Region.h"
-RegionInfo::RegionInfo() : label(-1), visible(true), modified(false), done(false) {
+RegionInfo::RegionInfo() : label(-1), visible(true), modified(false), done(false), verified(false), comment("") {
color[0] = color[1] = color[2] = -1.0;
extent[0] = extent[1] = extent[2] = extent[3] = extent[4] = extent[5] = -1;
}
diff --git a/visualization/SliceView.cxx b/visualization/SliceView.cxx
index 66a4be2..1cb4bf8 100644
--- a/visualization/SliceView.cxx
+++ b/visualization/SliceView.cxx
@@ -35,6 +35,7 @@
#include "Region.h"
#include "RegionOutline.h"
#include "RegionSurface.h"
+#include "RegionCenter2D.h"
#include "RegionCollection.h"
#include "SegmentorMath.h"
#include "SliceLocation.h"
@@ -200,6 +201,9 @@ void SliceView::AddRegionActors(Region* region) {
//renderer->AddActor(region->GetText());
regionOutlinesRenderer->AddActor(region->GetText());
+ regionOutlinesRenderer->AddActor(region->GetCenter2D()->GetActor());
+ region->GetCenter2D()->Update(plane->GetOrigin()[2]);
+
#ifdef SHOW_REGION_BOX
//regionOutlinesRenderer->AddActor(region->GetBox());
#endif
@@ -268,6 +272,7 @@ void SliceView::SetInteractionMode(enum InteractionMode mode) {
mode == MergeMode ? "Merge region" :
mode == GrowMode ? "Grow/shrink region" :
mode == VisibleMode ? "Toggle region visibility" :
+ mode == DotMode ? "Dot annotation mode" :
"";
interactionModeLabel->SetInput(s.c_str());
@@ -411,9 +416,18 @@ void SliceView::UpdatePlane() {
sliceLocation->UpdateView(cam, plane);
- if (plane->GetOrigin()[2] != oldZ) {
+ double z = plane->GetOrigin()[2];
+
+ if (z != oldZ) {
DoAutoRescale();
}
+
+ if (regions) {
+ for (RegionCollection::Iterator it = regions->Begin(); it != regions->End(); it++) {
+ Region* region = regions->Get(it);
+ region->GetCenter2D()->Update(z);
+ }
+ }
}
void SliceView::SetBrushRadius(int radius) {
diff --git a/visualization/VisualizationContainer.cxx b/visualization/VisualizationContainer.cxx
index b5ece82..06e71fc 100644
--- a/visualization/VisualizationContainer.cxx
+++ b/visualization/VisualizationContainer.cxx
@@ -179,6 +179,12 @@ VisualizationContainer::VisualizationContainer(vtkRenderWindowInteractor* volume
volumeView->GetInteractorStyle()->AddObserver(vtkInteractorStyleVolume::VisibleEvent, visibleCallback);
sliceView->GetInteractorStyle()->AddObserver(vtkInteractorStyleSlice::VisibleEvent, visibleCallback);
+ // Dot annotation
+ vtkSmartPointer dotCallback = vtkSmartPointer::New();
+ dotCallback->SetCallback(InteractionCallbacks::Dot);
+ dotCallback->SetClientData(this);
+ sliceView->GetInteractorStyle()->AddObserver(vtkInteractorStyleSlice::DotEvent, dotCallback);
+
// Mouse move
vtkSmartPointer mouseMoveCallback = vtkSmartPointer::New();
mouseMoveCallback->SetCallback(InteractionCallbacks::MouseMove);
@@ -637,6 +643,13 @@ void VisualizationContainer::SetInteractionMode(InteractionMode mode) {
sliceView->SetInteractionMode(interactionMode);
volumeView->SetInteractionMode(interactionMode);
+ for (RegionCollection::Iterator it = regions->Begin(); it != regions->End(); it++) {
+ Region* region = regions->Get(it);
+ region->ShowCenter(interactionMode == DotMode);
+ }
+
+ UpdateVisibility();
+
Render();
}
@@ -1904,6 +1917,82 @@ void VisualizationContainer::ToggleCurrentRegionDone() {
SetRegionDone(currentRegion->GetLabel(), !currentRegion->GetDone());
}
+void VisualizationContainer::ToggleCurrentRegionVerified() {
+ if (!currentRegion || !currentRegion->GetDone()) return;
+
+ SetRegionVerified(currentRegion->GetLabel(), !currentRegion->GetVerified());
+}
+
+void VisualizationContainer::SetDotAnnotation(double point[3]) {
+ int ijk[3];
+ PointToIndex(point, ijk);
+ int x = ijk[0];
+ int y = ijk[1];
+ int z = ijk[2];
+
+ // Get data at point
+ unsigned short* labelData = static_cast(labels->GetScalarPointer(x, y, z));
+
+ if (*labelData > 0) {
+ regions->Remove(*labelData);
+
+ }
+ else {
+ // Create new label
+ unsigned short newLabel = regions->GetNewLabel();
+
+ UpdateColors(newLabel);
+
+ // Set dot label
+ *labelData = newLabel;
+
+ // Create new region
+ int extent[6] = { x, x, y, y, z, z };
+ Region* newRegion = new Region(newLabel, labelColors->GetTableValue(newLabel), labels, extent);
+ newRegion->ShowCenter(true);
+
+ regions->Add(newRegion);
+ volumeView->AddRegion(newRegion);
+ sliceView->AddRegion(newRegion);
+
+ newRegion->SetModified(true);
+ }
+
+ qtWindow->updateRegions(regions);
+
+ SetCurrentRegion(nullptr);
+
+ labels->Modified();
+ Render();
+}
+
+bool VisualizationContainer::CheckDots() {
+ for (RegionCollection::Iterator it = regions->Begin(); it != regions->End(); it++) {
+ Region* region = regions->Get(it);
+
+ if (region->GetNumVoxels() > 1) {
+ return false;
+ }
+ }
+
+ return true;
+}
+
+void VisualizationContainer::ApplyDotAnnotation() {
+ for (RegionCollection::Iterator it = regions->Begin(); it != regions->End(); it++) {
+ Region* region = regions->Get(it);
+ region->ApplyDot();
+ }
+
+ labels->Modified();
+
+ qtWindow->updateRegions(regions);
+
+ Render();
+
+ PushHistory();
+}
+
Region* VisualizationContainer::SetRegionDone(unsigned short label, bool done) {
Region* region = regions->Get(label);
@@ -2348,6 +2437,11 @@ void VisualizationContainer::ExtractRegions(vtkIntArray* extents) {
delete region;
}
+ if (interactionMode == DotMode) {
+ region->ApplyDot();
+ region->ShowCenter(true);
+ }
+
qtWindow->updateProgress((double)label / maxLabel);
}
@@ -2394,6 +2488,11 @@ void VisualizationContainer::ExtractRegions(const std::vector& metad
region->SetVerified(true);
labelColors->SetTableValue(region->GetLabel(), LabelColors::verifiedColor);
}
+
+ if (interactionMode == DotMode) {
+ region->ApplyDot();
+ region->ShowCenter(true);
+ }
}
else {
delete region;
@@ -2511,7 +2610,7 @@ void VisualizationContainer::UpdateVisibility(Region* highlightRegion) {
for (RegionCollection::Iterator it = regions->Begin(); it != regions->End(); it++) {
Region* region = regions->Get(it);
- bool show = !filterRegions || region->GetVisible() || region == currentRegion || region == highlightRegion;
+ bool show = interactionMode != DotMode && (!filterRegions || region->GetVisible() || region == currentRegion || region == highlightRegion);
volumeView->ShowRegion(region, show);
sliceView->ShowRegion(region, show);
diff --git a/visualization/VisualizationContainer.h b/visualization/VisualizationContainer.h
index c02dd1a..5559281 100644
--- a/visualization/VisualizationContainer.h
+++ b/visualization/VisualizationContainer.h
@@ -114,6 +114,10 @@ class VisualizationContainer {
void FillCurrentRegionSlice();
void GrowCurrentRegion(double point[3]);
void ToggleCurrentRegionDone();
+ void ToggleCurrentRegionVerified();
+ void SetDotAnnotation(double point[3]);
+ bool CheckDots();
+ void ApplyDotAnnotation();
void SetWindowLevel(double window, double level);
void SetVolumeWindowLevel(double window, double level);
diff --git a/visualization/VolumeView.cxx b/visualization/VolumeView.cxx
index b7d85f0..190e933 100644
--- a/visualization/VolumeView.cxx
+++ b/visualization/VolumeView.cxx
@@ -37,6 +37,7 @@
#include "Region.h"
#include "RegionSurface.h"
#include "RegionHighlight3D.h"
+#include "RegionCenter3D.h"
#include "RegionCollection.h"
#include "SegmentorMath.h"
@@ -213,6 +214,7 @@ void VolumeView::SetRegions(vtkImageData* imageLabels, RegionCollection* newRegi
void VolumeView::AddRegion(Region* region) {
RegionSurface* surface = region->GetSurface();
RegionHighlight3D* highlight = region->GetHighlight3D();
+ RegionCenter3D* center = region->GetCenter3D();
surface->SetSmoothSurface(smoothSurfaces);
surface->SetSmoothShading(smoothShading);
@@ -223,6 +225,7 @@ void VolumeView::AddRegion(Region* region) {
renderer->AddActor(surface->GetActor());
renderer->AddActor(highlight->GetActor());
+ renderer->AddActor(center->GetActor());
#ifdef SHOW_REGION_BOX
renderer->AddActor(region->GetBox());