Skip to content

Commit 48a3e2a

Browse files
committedFeb 17, 2024
Switch to brightness/contrast instead of window width and level
1 parent cfd1df8 commit 48a3e2a

14 files changed

+209
-31
lines changed
 

‎Studio/Data/Session.cpp

+2-2
Original file line numberDiff line numberDiff line change
@@ -1102,14 +1102,14 @@ bool Session::get_image_3d_mode() { return params_.get("image_3d_mode", false);
11021102

11031103
//---------------------------------------------------------------------------
11041104
void Session::set_image_share_window_and_level(bool enabled) {
1105-
if (enabled == get_image_share_window_and_level() || is_loading()) {
1105+
if (enabled == get_image_share_brightness_contrast() || is_loading()) {
11061106
return;
11071107
}
11081108
params_.set("image_share_window_and_level", enabled);
11091109
}
11101110

11111111
//---------------------------------------------------------------------------
1112-
bool Session::get_image_share_window_and_level() { return params_.get("image_share_window_and_level", true); }
1112+
bool Session::get_image_share_brightness_contrast() { return params_.get("image_share_window_and_level", true); }
11131113

11141114
//---------------------------------------------------------------------------
11151115
void Session::set_image_sync_slice(bool enabled) {

‎Studio/Data/Session.h

+1-1
Original file line numberDiff line numberDiff line change
@@ -198,7 +198,7 @@ class Session : public QObject, public QEnableSharedFromThis<Session> {
198198

199199
// image sync/share window width and level
200200
void set_image_share_window_and_level(bool enabled);
201-
bool get_image_share_window_and_level();
201+
bool get_image_share_brightness_contrast();
202202

203203
// image sync slice
204204
void set_image_sync_slice(bool enabled);

‎Studio/Interface/ShapeWorksStudioApp.cpp

+1-1
Original file line numberDiff line numberDiff line change
@@ -613,7 +613,7 @@ void ShapeWorksStudioApp::update_table() {
613613

614614
ui_->image_axis_->setCurrentText(QString::fromStdString(axisToString(session_->get_image_axis())));
615615
ui_->image_3d_mode_->setChecked(session_->get_image_3d_mode());
616-
ui_->image_share_window_and_level_->setChecked(session_->get_image_share_window_and_level());
616+
ui_->image_share_window_and_level_->setChecked(session_->get_image_share_brightness_contrast());
617617
}
618618

619619
//---------------------------------------------------------------------------

‎Studio/Utils/StudioUtils.cpp

+22
Original file line numberDiff line numberDiff line change
@@ -152,4 +152,26 @@ bool StudioUtils::write_mesh(vtkSmartPointer<vtkPolyData> poly_data, QString fil
152152
}
153153
return true;
154154
}
155+
156+
//---------------------------------------------------------------------------
157+
void StudioUtils::brightness_contrast_to_window_width_level(double brightness, double contrast, double min_intensity,
158+
double max_intensity, double& window_width,
159+
double& window_level) {
160+
// Calculate window width
161+
window_width = (contrast / 100.0) * (max_intensity - min_intensity);
162+
163+
// Calculate window level
164+
window_level = min_intensity + (brightness / 100.0) * (max_intensity - min_intensity);
165+
}
166+
167+
//---------------------------------------------------------------------------
168+
void StudioUtils::window_width_level_to_brightness_contrast(double window_width, double window_level,
169+
double min_intensity, double max_intensity,
170+
double& brightness, double& contrast) {
171+
// Calculate brightness
172+
brightness = ((window_level - min_intensity) / (max_intensity - min_intensity)) * 100.0;
173+
174+
// Calculate contrast
175+
contrast = (window_width / (max_intensity - min_intensity)) * 100.0;
176+
}
155177
} // namespace shapeworks

‎Studio/Utils/StudioUtils.h

+9
Original file line numberDiff line numberDiff line change
@@ -36,6 +36,15 @@ class StudioUtils {
3636

3737
//! write a mesh to file
3838
static bool write_mesh(vtkSmartPointer<vtkPolyData> poly_data, QString filename);
39+
40+
//! convert brightness and contrast to window width and level
41+
static void brightness_contrast_to_window_width_level(double brightness, double contrast, double min_intensity,
42+
double max_intensity, double& window_width,
43+
double& window_level);
44+
45+
//! convert window width and level to brightness and contrast
46+
static void window_width_level_to_brightness_contrast(double window_width, double window_level, double min_intensity,
47+
double max_intensity, double& brightness, double& contrast);
3948
};
4049

4150
} // namespace shapeworks

‎Studio/Visualization/Lightbox.cpp

+3-3
Original file line numberDiff line numberDiff line change
@@ -538,12 +538,12 @@ void Lightbox::update_interactor_style() {
538538
}
539539

540540
//-----------------------------------------------------------------------------
541-
void Lightbox::set_shared_window_and_level(double window, double level) {
542-
if (!session_->get_image_share_window_and_level()) {
541+
void Lightbox::set_shared_brightness_and_contrast(double brightness, double contrast) {
542+
if (!session_->get_image_share_brightness_contrast()) {
543543
return;
544544
}
545545
for (int i = 0; i < viewers_.size(); i++) {
546-
viewers_[i]->slice_view().set_window_and_level(window, level);
546+
viewers_[i]->slice_view().set_brightness_and_contrast(brightness, contrast);
547547
}
548548
}
549549

‎Studio/Visualization/Lightbox.h

+2-2
Original file line numberDiff line numberDiff line change
@@ -89,8 +89,8 @@ class Lightbox : public QObject {
8989
void update_feature_range();
9090

9191
void update_interactor_style();
92-
93-
void set_shared_window_and_level(double window, double level);
92+
93+
void set_shared_brightness_and_contrast(double brightness, double contrast);
9494

9595
vtkRenderWindow* get_render_window();
9696

‎Studio/Visualization/SliceView.cpp

+22-2
Original file line numberDiff line numberDiff line change
@@ -15,6 +15,7 @@
1515
#include <vtkTransformPolyDataFilter.h>
1616

1717
// shapeworks
18+
#include <Utils/StudioUtils.h>
1819
#include <Visualization/SliceView.h>
1920
#include <Visualization/Viewer.h>
2021

@@ -75,6 +76,14 @@ void SliceView::set_volume(std::shared_ptr<Image> volume) {
7576

7677
image_slice_->SetMapper(slice_mapper_);
7778

79+
// reset window and level based on the volume scalar range
80+
double range[2];
81+
vtk_volume_->GetScalarRange(range);
82+
double window = range[1] - range[0];
83+
double level = (range[1] + range[0]) / 2.0;
84+
image_slice_->GetProperty()->SetColorWindow(window);
85+
image_slice_->GetProperty()->SetColorLevel(level);
86+
7887
auto transform = viewer_->get_image_transform();
7988
image_slice_->SetUserTransform(transform);
8089

@@ -253,7 +262,18 @@ void SliceView::set_slice_position(Point point) {
253262
}
254263

255264
//-----------------------------------------------------------------------------
256-
void SliceView::set_window_and_level(double window, double level) {
265+
void SliceView::set_brightness_and_contrast(double brightness, double contrast) {
266+
if (!vtk_volume_) {
267+
return;
268+
}
269+
// get scalar range from image
270+
double range[2];
271+
vtk_volume_->GetScalarRange(range);
272+
273+
// convert to window and level
274+
double window, level;
275+
StudioUtils::brightness_contrast_to_window_width_level(brightness, contrast, range[0], range[1], window, level);
276+
257277
image_slice_->GetProperty()->SetColorWindow(window);
258278
image_slice_->GetProperty()->SetColorLevel(level);
259279
}
@@ -362,7 +382,7 @@ void SliceView::update_extent() {
362382
double near_clip = range - spacing / 2.0 + 0.001;
363383
double far_clip = range + spacing / 2.0 - 0.001;
364384

365-
// cam->SetClippingRange( near_clip, far_clip );
385+
cam->SetClippingRange(near_clip, far_clip);
366386
}
367387
}
368388

‎Studio/Visualization/SliceView.h

+2-2
Original file line numberDiff line numberDiff line change
@@ -51,8 +51,8 @@ class SliceView {
5151
Point get_slice_position();
5252

5353
void set_slice_position(Point point);
54-
55-
void set_window_and_level(double window, double level);
54+
55+
void set_brightness_and_contrast(double brightness, double contrast);
5656

5757
double get_spacing();
5858

‎Studio/Visualization/StudioSliceInteractorStyle.cpp

+104-5
Original file line numberDiff line numberDiff line change
@@ -1,11 +1,16 @@
1+
#include <Utils/StudioUtils.h>
12
#include <Visualization/Lightbox.h>
23
#include <Visualization/StudioSliceInteractorStyle.h>
4+
#include <vtkAssemblyPath.h>
5+
#include <vtkImageActor.h>
6+
#include <vtkImageMapper3D.h>
37
#include <vtkImageProperty.h>
48
#include <vtkObjectFactory.h>
59
#include <vtkRenderer.h>
610

711
namespace shapeworks {
812

13+
//-----------------------------------------------------------------------------
914
vtkStandardNewMacro(StudioSliceInteractorStyle);
1015

1116
//-----------------------------------------------------------------------------
@@ -44,11 +49,105 @@ void StudioSliceInteractorStyle::OnKeyDown() {
4449

4550
//-----------------------------------------------------------------------------
4651
void StudioSliceInteractorStyle::WindowLevel() {
47-
vtkInteractorStyleImage::WindowLevel();
48-
if (CurrentImageProperty) {
49-
double window = CurrentImageProperty->GetColorWindow();
50-
double level = CurrentImageProperty->GetColorLevel();
51-
lightbox_->set_shared_window_and_level(window, level);
52+
if (!CurrentImageProperty) return;
53+
if (!current_image_slice_) return;
54+
55+
// Extract window width and level from the current image property
56+
vtkSmartPointer<vtkImageProperty> prop = CurrentImageProperty;
57+
double windowWidth = prop->GetColorWindow();
58+
double windowCenter = prop->GetColorLevel();
59+
60+
double contrast;
61+
double brightness;
62+
63+
// get intensity range of the image, using current_image_actor_ which is a vtkImageSlice
64+
double range[2];
65+
vtkImageData* imageData = current_image_slice_->GetMapper()->GetInput();
66+
imageData->GetScalarRange(range);
67+
68+
StudioUtils::window_width_level_to_brightness_contrast(windowWidth, windowCenter, range[0], range[1], brightness, contrast);
69+
70+
// Get the interactor
71+
vtkRenderWindowInteractor* rwi = this->Interactor;
72+
if (!rwi) return;
73+
74+
// Get the renderer size
75+
int* rendererSize = this->CurrentRenderer->GetSize();
76+
77+
// Get the event position
78+
int eventPos[2];
79+
rwi->GetEventPosition(eventPos);
80+
81+
// Get the last event position
82+
int lastEventPos[2];
83+
rwi->GetLastEventPosition(lastEventPos);
84+
85+
// Calculate change in mouse position
86+
double dx = static_cast<double>(eventPos[0] - lastEventPos[0]) / static_cast<double>(rendererSize[0]) * 100.0;
87+
double dy = static_cast<double>(eventPos[1] - lastEventPos[1]) / static_cast<double>(rendererSize[1]) * 100.0;
88+
89+
// Adjust brightness and contrast based on mouse movement
90+
brightness += dx;
91+
contrast += dy;
92+
93+
// Ensure brightness and contrast stay within valid range
94+
brightness = std::max<double>(3, std::min<double>(100, brightness));
95+
contrast = std::max<double>(3, std::min<double>(97, contrast));
96+
97+
StudioUtils::brightness_contrast_to_window_width_level(brightness, contrast, range[0], range[1], windowWidth, windowCenter);
98+
99+
lightbox_->set_shared_brightness_and_contrast(brightness, contrast);
100+
101+
// Apply window width and level to the image actor's property
102+
prop->SetColorWindow(windowWidth);
103+
prop->SetColorLevel(windowCenter);
104+
this->Interactor->Render();
105+
}
106+
107+
//-----------------------------------------------------------------------------
108+
void StudioSliceInteractorStyle::StartWindowLevel() {
109+
// forward events
110+
vtkInteractorStyleImage::StartWindowLevel();
111+
112+
if (!this->CurrentRenderer) {
113+
return;
114+
}
115+
116+
vtkPropCollection* props = this->CurrentRenderer->GetViewProps();
117+
vtkProp* prop = nullptr;
118+
vtkAssemblyPath* path;
119+
vtkImageSlice* imageProp = nullptr;
120+
vtkCollectionSimpleIterator pit;
121+
122+
int i = -1;
123+
124+
for (int k = 0; k < 2; k++) {
125+
int j = 0;
126+
for (props->InitTraversal(pit); (prop = props->GetNextProp(pit));) {
127+
bool foundImageProp = false;
128+
for (prop->InitPathTraversal(); (path = prop->GetNextPath());) {
129+
vtkProp* tryProp = path->GetLastNode()->GetViewProp();
130+
imageProp = vtkImageSlice::SafeDownCast(tryProp);
131+
if (imageProp) {
132+
if (j == i && imageProp->GetPickable()) {
133+
foundImageProp = true;
134+
break;
135+
}
136+
imageProp = nullptr;
137+
j++;
138+
}
139+
}
140+
if (foundImageProp) {
141+
break;
142+
}
143+
}
144+
if (i < 0) {
145+
i += j;
146+
}
147+
}
148+
149+
if (imageProp) {
150+
current_image_slice_ = imageProp;
52151
}
53152
}
54153

‎Studio/Visualization/StudioSliceInteractorStyle.h

+5
Original file line numberDiff line numberDiff line change
@@ -4,6 +4,9 @@
44
#include <vtkPropPicker.h>
55
#include <vtkRenderWindowInteractor.h>
66

7+
class vtkImageActor;
8+
class vtkImageSlice;
9+
710
namespace shapeworks {
811

912
class Lightbox;
@@ -26,10 +29,12 @@ class StudioSliceInteractorStyle : public vtkInteractorStyleImage {
2629
void OnMouseWheelBackward() override;
2730
void OnKeyDown() override;
2831
void WindowLevel() override;
32+
void StartWindowLevel() override;
2933

3034
protected:
3135
private:
3236
Lightbox* lightbox_;
37+
vtkImageSlice* current_image_slice_{nullptr};
3338
};
3439

3540
} // namespace shapeworks

‎Studio/Visualization/Viewer.cpp

+2-2
Original file line numberDiff line numberDiff line change
@@ -1150,7 +1150,7 @@ void Viewer::set_scalar_visibility(vtkSmartPointer<vtkPolyData> poly_data, vtkSm
11501150
}
11511151

11521152
//-----------------------------------------------------------------------------
1153-
void Viewer::update_image_volume() {
1153+
void Viewer::update_image_volume(bool force) {
11541154
if (!session_ || !shape_) {
11551155
return;
11561156
}
@@ -1173,7 +1173,7 @@ void Viewer::update_image_volume() {
11731173
slice_view_.update_particles();
11741174

11751175
auto image_volume_name = session_->get_image_name();
1176-
if (image_volume_name == current_image_name_) {
1176+
if (!force && image_volume_name == current_image_name_) {
11771177
return;
11781178
}
11791179
current_image_name_ = image_volume_name;

‎Studio/Visualization/Viewer.h

+1-1
Original file line numberDiff line numberDiff line change
@@ -130,7 +130,7 @@ class Viewer {
130130

131131
SliceView& slice_view();
132132

133-
void update_image_volume();
133+
void update_image_volume(bool force = false);
134134

135135
vtkSmartPointer<vtkPoints> get_glyph_points();
136136

‎Studio/Visualization/Visualizer.cpp

+33-10
Original file line numberDiff line numberDiff line change
@@ -67,25 +67,33 @@ void Visualizer::display_samples() {
6767

6868
//-----------------------------------------------------------------------------
6969
void Visualizer::update_samples() {
70-
Q_FOREACH (ViewerHandle viewer, lightbox_->get_viewers()) { viewer->update_points(); }
70+
Q_FOREACH (ViewerHandle viewer, lightbox_->get_viewers()) {
71+
viewer->update_points();
72+
}
7173
lightbox_->redraw();
7274
}
7375

7476
//-----------------------------------------------------------------------------
7577
void Visualizer::update_landmarks() {
76-
Q_FOREACH (ViewerHandle viewer, lightbox_->get_viewers()) { viewer->update_landmarks(); }
78+
Q_FOREACH (ViewerHandle viewer, lightbox_->get_viewers()) {
79+
viewer->update_landmarks();
80+
}
7781
lightbox_->redraw();
7882
}
7983

8084
//-----------------------------------------------------------------------------
8185
void Visualizer::update_planes() {
82-
Q_FOREACH (ViewerHandle viewer, lightbox_->get_viewers()) { viewer->update_planes(); }
86+
Q_FOREACH (ViewerHandle viewer, lightbox_->get_viewers()) {
87+
viewer->update_planes();
88+
}
8389
lightbox_->redraw();
8490
}
8591

8692
//-----------------------------------------------------------------------------
8793
void Visualizer::update_ffc_mode() {
88-
Q_FOREACH (ViewerHandle viewer, lightbox_->get_viewers()) { viewer->update_ffc_mode(); }
94+
Q_FOREACH (ViewerHandle viewer, lightbox_->get_viewers()) {
95+
viewer->update_ffc_mode();
96+
}
8997
lightbox_->redraw();
9098
}
9199

@@ -248,8 +256,13 @@ void Visualizer::handle_image_slice_settings_changed() {
248256
lightbox_->update_interactor_style();
249257

250258
if (lightbox_) {
251-
Q_FOREACH (ViewerHandle v, lightbox_->get_viewers()) { v->update_image_volume(); }
259+
Q_FOREACH (ViewerHandle v, lightbox_->get_viewers()) {
260+
v->update_image_volume(true);
261+
v->update_actors();
262+
v->reset_camera();
263+
}
252264
}
265+
reset_camera();
253266
lightbox_->redraw();
254267
}
255268

@@ -312,7 +325,9 @@ void Visualizer::update_lut() {
312325
//-----------------------------------------------------------------------------
313326
void Visualizer::update_annotations() {
314327
if (lightbox_) {
315-
Q_FOREACH (ViewerHandle v, lightbox_->get_viewers()) { v->update_annotations(); }
328+
Q_FOREACH (ViewerHandle v, lightbox_->get_viewers()) {
329+
v->update_annotations();
330+
}
316331
}
317332
lightbox_->redraw();
318333
}
@@ -506,7 +521,9 @@ vtkSmartPointer<vtkTransform> Visualizer::get_transform(std::shared_ptr<Shape> s
506521
void Visualizer::set_opacities(std::vector<float> opacities) {
507522
opacities_ = opacities;
508523
if (lightbox_) {
509-
Q_FOREACH (ViewerHandle viewer, lightbox_->get_viewers()) { viewer->update_opacities(); }
524+
Q_FOREACH (ViewerHandle viewer, lightbox_->get_viewers()) {
525+
viewer->update_opacities();
526+
}
510527
}
511528
lightbox_->redraw();
512529
}
@@ -515,7 +532,9 @@ void Visualizer::set_opacities(std::vector<float> opacities) {
515532
void Visualizer::set_domain_particle_visibilities(std::vector<bool> visibilities) {
516533
domain_particle_visibilities_ = visibilities;
517534
if (lightbox_) {
518-
Q_FOREACH (ViewerHandle viewer, lightbox_->get_viewers()) { viewer->update_points(); }
535+
Q_FOREACH (ViewerHandle viewer, lightbox_->get_viewers()) {
536+
viewer->update_points();
537+
}
519538
}
520539
lightbox_->redraw();
521540
}
@@ -572,7 +591,9 @@ QPixmap Visualizer::export_to_pixmap(QSize size, bool transparent_background, bo
572591
}
573592

574593
if (!show_color_scale) {
575-
Q_FOREACH (ViewerHandle viewer, lightbox_->get_viewers()) { viewer->remove_scalar_bar(); }
594+
Q_FOREACH (ViewerHandle viewer, lightbox_->get_viewers()) {
595+
viewer->remove_scalar_bar();
596+
}
576597
}
577598

578599
vtkRendererCollection* collection = render_window->GetRenderers();
@@ -605,7 +626,9 @@ QPixmap Visualizer::export_to_pixmap(QSize size, bool transparent_background, bo
605626

606627
// restore changes
607628
update_viewer_properties();
608-
Q_FOREACH (ViewerHandle viewer, lightbox_->get_viewers()) { viewer->update_actors(); }
629+
Q_FOREACH (ViewerHandle viewer, lightbox_->get_viewers()) {
630+
viewer->update_actors();
631+
}
609632

610633
return QPixmap::fromImage(qimage);
611634
}

0 commit comments

Comments
 (0)
Please sign in to comment.