From b8a3fc8037ae14a995317c367118e84094bb7e41 Mon Sep 17 00:00:00 2001 From: Sam Tygier Date: Tue, 6 Feb 2024 17:36:16 +0000 Subject: [PATCH 1/8] MIImageView: Refresh message on roi change Rather than recalculating the roi every time the messages is refreshed Note: test change because _update_message now gets called twice --- mantidimaging/gui/widgets/mi_image_view/test/view_test.py | 2 +- mantidimaging/gui/widgets/mi_image_view/view.py | 7 +++---- 2 files changed, 4 insertions(+), 5 deletions(-) diff --git a/mantidimaging/gui/widgets/mi_image_view/test/view_test.py b/mantidimaging/gui/widgets/mi_image_view/test/view_test.py index 1be6304b8e0..89a92fbedd3 100644 --- a/mantidimaging/gui/widgets/mi_image_view/test/view_test.py +++ b/mantidimaging/gui/widgets/mi_image_view/test/view_test.py @@ -64,7 +64,7 @@ def test_set_roi(self): self.view.roi.setSize.assert_called_once_with([right - left, bottom - top]) self.view._update_roi_region_avg.assert_called_once() self.view.roi_changed_callback.assert_called_once() - self.view._update_message.assert_called_once() + self.view._update_message.assert_called() def test_default_roi(self): image = np.zeros((1, 50, 50)) diff --git a/mantidimaging/gui/widgets/mi_image_view/view.py b/mantidimaging/gui/widgets/mi_image_view/view.py index b6a88977be2..093ebabbb54 100644 --- a/mantidimaging/gui/widgets/mi_image_view/view.py +++ b/mantidimaging/gui/widgets/mi_image_view/view.py @@ -171,9 +171,7 @@ def toggle_jumping_frame(self, images_to_jump_by=None): sleep(0.02) QApplication.processEvents() - def _refresh_message(self, recalculate_roi_avg: bool = True): - if recalculate_roi_avg: - self._update_roi_region_avg() + def _refresh_message(self): try: self._update_message(self._last_mouse_hover_location) except IndexError: @@ -196,6 +194,7 @@ def roiChanged(self): roi = self._update_roi_region_avg() if self.roi_changed_callback and roi is not None: self.roi_changed_callback(roi) + self._refresh_message() def _update_roi_region_avg(self) -> Optional[SensibleROI]: if self.image.ndim != 3: @@ -281,7 +280,7 @@ def set_roi(self, coords: list[int]): # Keep default update=True for setSize otherwise the scale handle can become detached from the ROI box self.roi.setSize([roi.width, roi.height]) self.roiChanged() - self._refresh_message(False) + self._refresh_message() def default_roi(self): # Recommend an ROI that covers the top left quadrant From 8545e1303a39660486f8499a98ec6e8e6a7035ee Mon Sep 17 00:00:00 2001 From: Sam Tygier Date: Tue, 6 Feb 2024 17:52:46 +0000 Subject: [PATCH 2/8] Abort _update_roi_region_avg early when not needed And get rid of the roi part of message when roi is hidden --- mantidimaging/gui/widgets/mi_image_view/view.py | 9 +++++++++ 1 file changed, 9 insertions(+) diff --git a/mantidimaging/gui/widgets/mi_image_view/view.py b/mantidimaging/gui/widgets/mi_image_view/view.py index 093ebabbb54..a760b274c10 100644 --- a/mantidimaging/gui/widgets/mi_image_view/view.py +++ b/mantidimaging/gui/widgets/mi_image_view/view.py @@ -197,6 +197,8 @@ def roiChanged(self): self._refresh_message() def _update_roi_region_avg(self) -> Optional[SensibleROI]: + if not self.ui.roiBtn.isChecked(): + return None if self.image.ndim != 3: return None roi_pos, roi_size = self.get_roi() @@ -214,6 +216,13 @@ def _update_roi_region_avg(self) -> Optional[SensibleROI]: f"region avg={data[int(self.timeLine.value())].mean():.6f}" return SensibleROI(left, top, right, bottom) + def roiClicked(self): + # When ROI area is hidden with the button, clear the message + if not self.ui.roiBtn.isChecked() and hasattr(self, "_last_mouse_hover_location"): + self.roiString = None + self._refresh_message() + super().roiClicked() + def extend_roi_plot_mouse_press_handler(self): original_handler = self.ui.roiPlot.mousePressEvent From 4f38623432629f6b0c7470e952fa09d556ef3dcf Mon Sep 17 00:00:00 2001 From: Sam Tygier Date: Wed, 7 Feb 2024 09:16:09 +0000 Subject: [PATCH 3/8] MIImageView: Only call mean() once in _update_roi_region_avg At this point ndims must be 3, so don't need to iterate --- mantidimaging/gui/widgets/mi_image_view/view.py | 12 ++++++------ 1 file changed, 6 insertions(+), 6 deletions(-) diff --git a/mantidimaging/gui/widgets/mi_image_view/view.py b/mantidimaging/gui/widgets/mi_image_view/view.py index a760b274c10..722edd32ac8 100644 --- a/mantidimaging/gui/widgets/mi_image_view/view.py +++ b/mantidimaging/gui/widgets/mi_image_view/view.py @@ -206,12 +206,12 @@ def _update_roi_region_avg(self) -> Optional[SensibleROI]: left, right = roi_pos.x, roi_pos.x + roi_size.x top, bottom = roi_pos.y, roi_pos.y + roi_size.y data = self.image[:, top:bottom, left:right] - if data is not None: - while data.ndim > 1: - data = data.mean(axis=1) - if len(self.roiCurves) == 0: - self.roiCurves.append(self.ui.roiPlot.plot()) - self.roiCurves[0].setData(y=data, x=self.tVals) + + data = data.mean(axis=(1, 2)) + + if len(self.roiCurves) == 0: + self.roiCurves.append(self.ui.roiPlot.plot()) + self.roiCurves[0].setData(y=data, x=self.tVals) self.roiString = f"({left}, {top}, {right}, {bottom}) | " \ f"region avg={data[int(self.timeLine.value())].mean():.6f}" return SensibleROI(left, top, right, bottom) From e15780f599796fdc85b6f82d9bfa1ddbb9c050f6 Mon Sep 17 00:00:00 2001 From: Sam Tygier Date: Mon, 12 Feb 2024 13:43:18 +0000 Subject: [PATCH 4/8] Split logic for updating message and plot Update the message if an ROI is visible, and up date the plot if the plot is shown --- mantidimaging/gui/widgets/mi_image_view/view.py | 16 ++++++++++------ mantidimaging/gui/widgets/roi_selector/view.py | 2 ++ 2 files changed, 12 insertions(+), 6 deletions(-) diff --git a/mantidimaging/gui/widgets/mi_image_view/view.py b/mantidimaging/gui/widgets/mi_image_view/view.py index 722edd32ac8..6c2b3dd50bc 100644 --- a/mantidimaging/gui/widgets/mi_image_view/view.py +++ b/mantidimaging/gui/widgets/mi_image_view/view.py @@ -197,23 +197,27 @@ def roiChanged(self): self._refresh_message() def _update_roi_region_avg(self) -> Optional[SensibleROI]: - if not self.ui.roiBtn.isChecked(): - return None if self.image.ndim != 3: return None roi_pos, roi_size = self.get_roi() # image indices are in order [Z, X, Y] left, right = roi_pos.x, roi_pos.x + roi_size.x top, bottom = roi_pos.y, roi_pos.y + roi_size.y - data = self.image[:, top:bottom, left:right] - data = data.mean(axis=(1, 2)) + if self.roi.isVisible(): + mean_val = self.image[self.timeLine.value(), top:bottom, left:right].mean() + self.roiString = f"({left}, {top}, {right}, {bottom}) | " \ + f"region avg={mean_val:.6f}" + + if not self.ui.roiBtn.isChecked(): + return None + + data = self.image[:, top:bottom, left:right].mean(axis=(1, 2)) if len(self.roiCurves) == 0: self.roiCurves.append(self.ui.roiPlot.plot()) self.roiCurves[0].setData(y=data, x=self.tVals) - self.roiString = f"({left}, {top}, {right}, {bottom}) | " \ - f"region avg={data[int(self.timeLine.value())].mean():.6f}" + return SensibleROI(left, top, right, bottom) def roiClicked(self): diff --git a/mantidimaging/gui/widgets/roi_selector/view.py b/mantidimaging/gui/widgets/roi_selector/view.py index e9b62ef777b..492ecc50bc5 100644 --- a/mantidimaging/gui/widgets/roi_selector/view.py +++ b/mantidimaging/gui/widgets/roi_selector/view.py @@ -62,6 +62,8 @@ def __init__(self, button.clicked.connect(lambda: self.close()) self.roi_view.ui.gridLayout.addWidget(button) + self.roi_view.roiChanged() + def toggle_average_images(self) -> None: self.roi_view.setImage(self.main_image if self.roi_view_averaged else self.averaged_image) self.roi_view_averaged = not self.roi_view_averaged From 530224525d58cfae78a0e48ac6ccb77a69d7f6b7 Mon Sep 17 00:00:00 2001 From: Sam Tygier Date: Mon, 12 Feb 2024 14:36:42 +0000 Subject: [PATCH 5/8] Release notes --- docs/release_notes/next/fix-2030-slow-imageview | 1 + 1 file changed, 1 insertion(+) create mode 100644 docs/release_notes/next/fix-2030-slow-imageview diff --git a/docs/release_notes/next/fix-2030-slow-imageview b/docs/release_notes/next/fix-2030-slow-imageview new file mode 100644 index 00000000000..84276927002 --- /dev/null +++ b/docs/release_notes/next/fix-2030-slow-imageview @@ -0,0 +1 @@ +#2031 : Speed up imageview by avoiding unneeded calculation From 28b9ac615c63b97cd95e1f16bc6f2a1817ead2c8 Mon Sep 17 00:00:00 2001 From: Sam Tygier Date: Mon, 12 Feb 2024 17:07:56 +0000 Subject: [PATCH 6/8] StackCompare: Redraw the view to avoid artefacts --- mantidimaging/gui/windows/stack_choice/view.py | 2 ++ 1 file changed, 2 insertions(+) diff --git a/mantidimaging/gui/windows/stack_choice/view.py b/mantidimaging/gui/windows/stack_choice/view.py index afeb221abe4..4980fd92ace 100644 --- a/mantidimaging/gui/windows/stack_choice/view.py +++ b/mantidimaging/gui/windows/stack_choice/view.py @@ -79,6 +79,8 @@ def __init__(self, original_stack: ImageStack, new_stack: ImageStack, self.new_stack.roi.sigRegionChanged.connect(self._sync_roi_plot_for_old_stack_with_new_stack) self.original_stack.roi.sigRegionChanged.connect(self.original_stack.roiChanged) self.new_stack.roi.sigRegionChanged.connect(self.new_stack.roiChanged) + self.original_stack.roi.sigRegionChanged.connect(self.original_stack.viewbox.update) + self.new_stack.roi.sigRegionChanged.connect(self.new_stack.viewbox.update) self._sync_both_image_axis() self._ensure_range_is_the_same() From 11e5d9c270682a8256dc7e92979b274eb36adaf2 Mon Sep 17 00:00:00 2001 From: Sam Tygier Date: Mon, 12 Feb 2024 17:26:25 +0000 Subject: [PATCH 7/8] Must use an int when slicing --- mantidimaging/gui/widgets/mi_image_view/view.py | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/mantidimaging/gui/widgets/mi_image_view/view.py b/mantidimaging/gui/widgets/mi_image_view/view.py index 6c2b3dd50bc..40914fbb44b 100644 --- a/mantidimaging/gui/widgets/mi_image_view/view.py +++ b/mantidimaging/gui/widgets/mi_image_view/view.py @@ -205,7 +205,8 @@ def _update_roi_region_avg(self) -> Optional[SensibleROI]: top, bottom = roi_pos.y, roi_pos.y + roi_size.y if self.roi.isVisible(): - mean_val = self.image[self.timeLine.value(), top:bottom, left:right].mean() + z_value = int(self.timeLine.value()) + mean_val = self.image[z_value, top:bottom, left:right].mean() self.roiString = f"({left}, {top}, {right}, {bottom}) | " \ f"region avg={mean_val:.6f}" From ad5b54e25dfcc2dbe6c4ca6aa1273408d8955766 Mon Sep 17 00:00:00 2001 From: Sam Tygier Date: Wed, 14 Feb 2024 14:56:00 +0000 Subject: [PATCH 8/8] Tweak return logic in _update_roi_region_avg, to ensure callback is called when needed --- mantidimaging/gui/widgets/mi_image_view/view.py | 17 +++++++++-------- 1 file changed, 9 insertions(+), 8 deletions(-) diff --git a/mantidimaging/gui/widgets/mi_image_view/view.py b/mantidimaging/gui/widgets/mi_image_view/view.py index 40914fbb44b..e757131ab97 100644 --- a/mantidimaging/gui/widgets/mi_image_view/view.py +++ b/mantidimaging/gui/widgets/mi_image_view/view.py @@ -210,16 +210,17 @@ def _update_roi_region_avg(self) -> Optional[SensibleROI]: self.roiString = f"({left}, {top}, {right}, {bottom}) | " \ f"region avg={mean_val:.6f}" - if not self.ui.roiBtn.isChecked(): - return None - - data = self.image[:, top:bottom, left:right].mean(axis=(1, 2)) + if self.ui.roiBtn.isChecked(): + data = self.image[:, top:bottom, left:right].mean(axis=(1, 2)) - if len(self.roiCurves) == 0: - self.roiCurves.append(self.ui.roiPlot.plot()) - self.roiCurves[0].setData(y=data, x=self.tVals) + if len(self.roiCurves) == 0: + self.roiCurves.append(self.ui.roiPlot.plot()) + self.roiCurves[0].setData(y=data, x=self.tVals) - return SensibleROI(left, top, right, bottom) + if self.roi.isVisible() or self.ui.roiBtn.isChecked(): + return SensibleROI(left, top, right, bottom) + else: + return None def roiClicked(self): # When ROI area is hidden with the button, clear the message