Skip to content

Commit 40876f1

Browse files
akenmorrisnawazishkhan1-nk
authored andcommitted
2356 - Segmentation Tool Updates (#2363)
* Fix bug where segmentation tool does not operate on the correct location. vtkImageActorPointPlacer does not take into account the UserTransform. I had to subclass it as StudioImageActorPointPlacer. * Cleanup * "included" part of segmentation should become part of the object * Hide include/exclude for 6.6 release (#2356)
1 parent 6df47f4 commit 40876f1

File tree

9 files changed

+261
-12
lines changed

9 files changed

+261
-12
lines changed

Libs/Analyze/Shape.cpp

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -118,7 +118,7 @@ void Shape::recompute_original_surface() {
118118
}
119119
auto meshes = original_meshes_.meshes();
120120
Image copy = *seg;
121-
copy.binarize(0, 1);
121+
copy.binarize(0, 2);
122122
Mesh mesh = copy.toMesh(0.001);
123123
MeshHandle mesh_handle = std::make_shared<StudioMesh>();
124124
mesh_handle->set_poly_data(mesh.getVTKMesh());

Studio/CMakeLists.txt

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -228,6 +228,7 @@ SET(STUDIO_VISUALIZATION_SRCS
228228
Visualization/ParticleColors.cpp
229229
Visualization/StudioInteractorStyle.cpp
230230
Visualization/StudioSliceInteractorStyle.cpp
231+
Visualization/StudioImageActorPointPlacer.cpp
231232
Visualization/SliceView.cpp
232233
Visualization/MeshSlice.cpp
233234
Visualization/Viewer.cpp

Studio/Data/SegmentationToolPanel.cpp

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -40,6 +40,10 @@ SegmentationToolPanel::SegmentationToolPanel(QWidget* parent) : QWidget(parent),
4040
connect(ui_->recompute_surface_, &QPushButton::clicked, this, &SegmentationToolPanel::recompute_surface);
4141
ui_->header_label->setAttribute(Qt::WA_TransparentForMouseEvents);
4242
ui_->open_button->setChecked(false);
43+
44+
// for 6.6 release we don't have include/exclude
45+
ui_->included_mode_->hide();
46+
ui_->excluded_mode_->hide();
4347
}
4448

4549
//---------------------------------------------------------------------------

Studio/Visualization/PaintWidget.cpp

Lines changed: 7 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -2,6 +2,7 @@
22
#include "PaintWidget.h"
33

44
#include <Logging.h>
5+
#include <Visualization/StudioImageActorPointPlacer.h>
56

67
#include <QCursor>
78

@@ -10,7 +11,6 @@
1011
#include "vtkCommand.h"
1112
#include "vtkEvent.h"
1213
#include "vtkImageActor.h"
13-
#include "vtkImageActorPointPlacer.h"
1414
#include "vtkObjectFactory.h"
1515
#include "vtkOrientedGlyphContourRepresentation.h"
1616
#include "vtkPointPlacer.h"
@@ -255,10 +255,13 @@ bool PaintWidget::use_point_placer(double displayPos[2], int newState) {
255255

256256
if (WidgetState == PaintWidget::Paint || WidgetState == PaintWidget::Erase) {
257257
if (WidgetState == PaintWidget::Paint) {
258+
if (circle_mode_) {
259+
// we have to transform the point back to the image space in case there is alignment
260+
auto transform = viewer_->get_inverse_image_transform();
261+
transform->TransformPoint(worldPos, worldPos);
262+
}
263+
258264
viewer_->handle_paint(displayPos, worldPos);
259-
////paint_position( this, worldPos );
260-
} else if (WidgetState == PaintWidget::Erase) {
261-
////erase_position( this, worldPos );
262265
}
263266

264267
EventCallbackCommand->SetAbortFlag(1);

Studio/Visualization/SliceView.cpp

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -3,7 +3,7 @@
33
#include <vtkCellData.h>
44
#include <vtkCutter.h>
55
#include <vtkImageActor.h>
6-
#include <vtkImageActorPointPlacer.h>
6+
#include <Visualization/StudioImageActorPointPlacer.h>
77
#include <vtkImageProperty.h>
88
#include <vtkImageSliceMapper.h>
99
#include <vtkLookupTable.h>
@@ -62,7 +62,7 @@ SliceView::SliceView(Viewer *viewer) : viewer_(viewer) {
6262
mask_slice_ = vtkSmartPointer<vtkImageActor>::New();
6363
slice_mapper_ = vtkSmartPointer<vtkImageSliceMapper>::New();
6464
mask_mapper_ = vtkSmartPointer<vtkImageSliceMapper>::New();
65-
placer_ = vtkSmartPointer<vtkImageActorPointPlacer>::New();
65+
placer_ = vtkSmartPointer<StudioImageActorPointPlacer>::New();
6666
placer_->SetImageActor(image_slice_);
6767
}
6868

Lines changed: 202 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,202 @@
1+
#include "StudioImageActorPointPlacer.h"
2+
3+
#include "vtkCamera.h"
4+
#include "vtkCellPicker.h"
5+
#include "vtkImageActor.h"
6+
#include "vtkImageData.h"
7+
#include "vtkImageMapper3D.h"
8+
#include "vtkMath.h"
9+
#include "vtkNew.h"
10+
#include "vtkObjectFactory.h"
11+
#include "vtkRenderer.h"
12+
#include "vtkTransform.h"
13+
14+
vtkStandardNewMacro(StudioImageActorPointPlacer);
15+
16+
//----------------------------------------------------------------------------
17+
StudioImageActorPointPlacer::StudioImageActorPointPlacer() {}
18+
19+
//----------------------------------------------------------------------------
20+
StudioImageActorPointPlacer::~StudioImageActorPointPlacer() {}
21+
22+
//----------------------------------------------------------------------------
23+
void StudioImageActorPointPlacer::PrintSelf(ostream& os, vtkIndent indent) { this->Superclass::PrintSelf(os, indent); }
24+
25+
//----------------------------------------------------------------------------
26+
int StudioImageActorPointPlacer::ComputeWorldPosition(vtkRenderer* renderer, double displayPos[2], double worldPos[3],
27+
double worldOrient[9]) {
28+
vtkImageActor* actor = this->GetImageActor();
29+
if (!actor) {
30+
return 0;
31+
}
32+
33+
// Use a cell picker for accurate picking on the transformed image
34+
vtkNew<vtkCellPicker> picker;
35+
picker->SetTolerance(0.005);
36+
37+
// Pick only on our specific actor
38+
picker->InitializePickList();
39+
picker->AddPickList(actor);
40+
picker->PickFromListOn();
41+
42+
// Perform the pick
43+
if (!picker->Pick(displayPos[0], displayPos[1], 0.0, renderer)) {
44+
return 0;
45+
}
46+
47+
// Check if we picked the correct actor
48+
vtkImageActor* pickedActor = vtkImageActor::SafeDownCast(picker->GetProp3D());
49+
if (pickedActor != actor) {
50+
return 0;
51+
}
52+
53+
// Get the picked position - this accounts for the transformation
54+
picker->GetPickPosition(worldPos);
55+
56+
// If orientation is requested, compute it using the transformed plane
57+
if (worldOrient) {
58+
// Get the normal from the picker - this will be in transformed space
59+
double* normal = picker->GetPickNormal();
60+
61+
// Z axis is the normal to the image plane
62+
worldOrient[6] = normal[0];
63+
worldOrient[7] = normal[1];
64+
worldOrient[8] = normal[2];
65+
66+
// Compute a suitable X axis that's perpendicular to Z
67+
double xAxis[3] = {1.0, 0.0, 0.0};
68+
if (fabs(normal[0]) > 0.99) {
69+
xAxis[0] = 0.0;
70+
xAxis[1] = 1.0;
71+
}
72+
73+
// Make X perpendicular to Z
74+
double dot = vtkMath::Dot(xAxis, normal);
75+
xAxis[0] -= dot * normal[0];
76+
xAxis[1] -= dot * normal[1];
77+
xAxis[2] -= dot * normal[2];
78+
vtkMath::Normalize(xAxis);
79+
80+
worldOrient[0] = xAxis[0];
81+
worldOrient[1] = xAxis[1];
82+
worldOrient[2] = xAxis[2];
83+
84+
// Y axis is Z cross X
85+
double yAxis[3];
86+
vtkMath::Cross(normal, xAxis, yAxis);
87+
worldOrient[3] = yAxis[0];
88+
worldOrient[4] = yAxis[1];
89+
worldOrient[5] = yAxis[2];
90+
}
91+
92+
return 1;
93+
}
94+
95+
//----------------------------------------------------------------------------
96+
int StudioImageActorPointPlacer::ValidateWorldPosition(double worldPos[3]) {
97+
vtkImageActor* actor = this->GetImageActor();
98+
if (!actor) {
99+
return 0;
100+
}
101+
102+
// Create a cell locator for the actor's mapper input
103+
vtkImageMapper3D* mapper = actor->GetMapper();
104+
if (!mapper) {
105+
return 0;
106+
}
107+
108+
vtkImageData* image = vtkImageData::SafeDownCast(mapper->GetInput());
109+
if (!image) {
110+
return 0;
111+
}
112+
113+
// Transform the world position back to image space
114+
double imagePos[3];
115+
if (actor->GetUserTransform()) {
116+
vtkNew<vtkTransform> inverseTransform;
117+
inverseTransform->DeepCopy(actor->GetUserTransform());
118+
inverseTransform->Inverse();
119+
inverseTransform->TransformPoint(worldPos, imagePos);
120+
} else {
121+
imagePos[0] = worldPos[0];
122+
imagePos[1] = worldPos[1];
123+
imagePos[2] = worldPos[2];
124+
}
125+
126+
// Check if the point is within the image bounds
127+
double bounds[6];
128+
image->GetBounds(bounds);
129+
130+
if (imagePos[0] < bounds[0] || imagePos[0] > bounds[1] || imagePos[1] < bounds[2] || imagePos[1] > bounds[3] ||
131+
imagePos[2] < bounds[4] || imagePos[2] > bounds[5]) {
132+
return 0;
133+
}
134+
135+
return 1;
136+
}
137+
138+
//----------------------------------------------------------------------------
139+
int StudioImageActorPointPlacer::UpdateWorldPosition(vtkRenderer* renderer, double worldPos[3], double worldOrient[9]) {
140+
// For a point already on the plane, we need to ensure it stays within bounds
141+
if (!this->ValidateWorldPosition(worldPos)) {
142+
return 0;
143+
}
144+
145+
// Orientation handling
146+
if (worldOrient) {
147+
vtkImageActor* actor = this->GetImageActor();
148+
if (actor && actor->GetUserTransform()) {
149+
// Use the normal from the transformed image plane for the Z axis
150+
vtkNew<vtkCellPicker> picker;
151+
picker->SetTolerance(0.005);
152+
153+
// Pick at the world position
154+
double displayPos[3];
155+
renderer->SetWorldPoint(worldPos[0], worldPos[1], worldPos[2], 1.0);
156+
renderer->WorldToDisplay();
157+
renderer->GetDisplayPoint(displayPos);
158+
159+
// Try to pick on the actor
160+
picker->InitializePickList();
161+
picker->AddPickList(actor);
162+
picker->PickFromListOn();
163+
164+
if (picker->Pick(displayPos[0], displayPos[1], 0.0, renderer)) {
165+
vtkImageActor* pickedActor = vtkImageActor::SafeDownCast(picker->GetProp3D());
166+
if (pickedActor == actor) {
167+
// Use the picked normal
168+
double* normal = picker->GetPickNormal();
169+
170+
worldOrient[6] = normal[0];
171+
worldOrient[7] = normal[1];
172+
worldOrient[8] = normal[2];
173+
174+
// Compute suitable X and Y axes as in ComputeWorldPosition
175+
double xAxis[3] = {1.0, 0.0, 0.0};
176+
if (fabs(normal[0]) > 0.99) {
177+
xAxis[0] = 0.0;
178+
xAxis[1] = 1.0;
179+
}
180+
181+
double dot = vtkMath::Dot(xAxis, normal);
182+
xAxis[0] -= dot * normal[0];
183+
xAxis[1] -= dot * normal[1];
184+
xAxis[2] -= dot * normal[2];
185+
vtkMath::Normalize(xAxis);
186+
187+
worldOrient[0] = xAxis[0];
188+
worldOrient[1] = xAxis[1];
189+
worldOrient[2] = xAxis[2];
190+
191+
double yAxis[3];
192+
vtkMath::Cross(normal, xAxis, yAxis);
193+
worldOrient[3] = yAxis[0];
194+
worldOrient[4] = yAxis[1];
195+
worldOrient[5] = yAxis[2];
196+
}
197+
}
198+
}
199+
}
200+
201+
return 1;
202+
}
Lines changed: 28 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,28 @@
1+
#pragma once
2+
3+
#include "vtkImageActorPointPlacer.h"
4+
5+
class StudioImageActorPointPlacer : public vtkImageActorPointPlacer {
6+
public:
7+
static StudioImageActorPointPlacer* New();
8+
vtkTypeMacro(StudioImageActorPointPlacer, vtkImageActorPointPlacer);
9+
void PrintSelf(ostream& os, vtkIndent indent) override;
10+
11+
// Override ComputeWorldPosition to take transform into account
12+
int ComputeWorldPosition(vtkRenderer* renderer, double displayPos[2], double worldPos[3],
13+
double worldOrient[9]) override;
14+
15+
// Override ValidateWorldPosition
16+
int ValidateWorldPosition(double worldPos[3]) override;
17+
18+
// Override UpdateWorldPosition
19+
int UpdateWorldPosition(vtkRenderer* renderer, double worldPos[3], double worldOrient[9]) override;
20+
21+
protected:
22+
StudioImageActorPointPlacer();
23+
~StudioImageActorPointPlacer() override;
24+
25+
private:
26+
StudioImageActorPointPlacer(const StudioImageActorPointPlacer&) = delete;
27+
void operator=(const StudioImageActorPointPlacer&) = delete;
28+
};

Studio/Visualization/Viewer.cpp

Lines changed: 14 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -8,7 +8,7 @@
88
#include <vtkGlyph3D.h>
99
#include <vtkHandleWidget.h>
1010
#include <vtkImageActor.h>
11-
#include <vtkImageActorPointPlacer.h>
11+
#include <Visualization/StudioImageActorPointPlacer.h>
1212
#include <vtkImageData.h>
1313
#include <vtkKdTreePointLocator.h>
1414
#include <vtkLookupTable.h>
@@ -58,7 +58,7 @@ Viewer::Viewer() {
5858
prop_picker_->SetPickFromList(1);
5959
point_placer_ = vtkSmartPointer<vtkPolygonalSurfacePointPlacer>::New();
6060
point_placer_->SetDistanceOffset(0);
61-
slice_point_placer_ = vtkSmartPointer<vtkImageActorPointPlacer>::New();
61+
slice_point_placer_ = vtkSmartPointer<StudioImageActorPointPlacer>::New();
6262

6363
landmark_widget_ = std::make_shared<LandmarkWidget>(this);
6464
plane_widget_ = std::make_shared<PlaneWidget>(this);
@@ -756,13 +756,13 @@ void Viewer::display_shape(std::shared_ptr<Shape> shape) {
756756
auto compare_poly_data = compare_meshes_.meshes()[i]->get_poly_data();
757757

758758
if (compare_settings.get_mean_shape_checked()) {
759-
auto transform = visualizer_->get_transform(shape_, compare_settings.get_display_mode(),
759+
auto mean_transform = visualizer_->get_transform(shape_, compare_settings.get_display_mode(),
760760
visualizer_->get_alignment_domain(), i);
761761

762-
transform->Inverse();
762+
mean_transform->Inverse();
763763
auto transform_filter = vtkSmartPointer<vtkTransformPolyDataFilter>::New();
764764
transform_filter->SetInputData(compare_poly_data);
765-
transform_filter->SetTransform(transform);
765+
transform_filter->SetTransform(mean_transform);
766766
transform_filter->Update();
767767

768768
compare_poly_data = transform_filter->GetOutput();
@@ -1439,6 +1439,15 @@ vtkSmartPointer<vtkTransform> Viewer::get_image_transform() {
14391439
return visualizer_->get_transform(shape_, visualizer_->get_alignment_domain(), 0);
14401440
}
14411441

1442+
//-----------------------------------------------------------------------------
1443+
vtkSmartPointer<vtkTransform> Viewer::get_inverse_image_transform() {
1444+
auto transform = get_image_transform();
1445+
auto inverse = vtkSmartPointer<vtkTransform>::New();
1446+
inverse->DeepCopy(transform);
1447+
inverse->Inverse();
1448+
return inverse;
1449+
}
1450+
14421451
//-----------------------------------------------------------------------------
14431452
SliceView& Viewer::slice_view() { return slice_view_; }
14441453

Studio/Visualization/Viewer.h

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -129,6 +129,8 @@ class Viewer {
129129

130130
vtkSmartPointer<vtkTransform> get_image_transform();
131131

132+
vtkSmartPointer<vtkTransform> get_inverse_image_transform();
133+
132134
SliceView& slice_view();
133135

134136
void update_image_volume(bool force = false);

0 commit comments

Comments
 (0)