From 67f00a4198f68ef562b45084da5765fc0fc39e21 Mon Sep 17 00:00:00 2001 From: ashmeigh Date: Thu, 18 Jul 2024 14:39:44 +0100 Subject: [PATCH 1/7] better error handling for logfile --- ...-2222-Monitor-normalisation-RuntimeError-No-logfile-stack | 1 + .../monitor_normalisation/monitor_normalisation.py | 5 +++++ 2 files changed, 6 insertions(+) create mode 100644 docs/release_notes/next/fix-2222-Monitor-normalisation-RuntimeError-No-logfile-stack diff --git a/docs/release_notes/next/fix-2222-Monitor-normalisation-RuntimeError-No-logfile-stack b/docs/release_notes/next/fix-2222-Monitor-normalisation-RuntimeError-No-logfile-stack new file mode 100644 index 00000000000..a073ccb04e1 --- /dev/null +++ b/docs/release_notes/next/fix-2222-Monitor-normalisation-RuntimeError-No-logfile-stack @@ -0,0 +1 @@ +2222: Monitor normalisation: RuntimeError: No logfile available for this stack. \ No newline at end of file diff --git a/mantidimaging/core/operations/monitor_normalisation/monitor_normalisation.py b/mantidimaging/core/operations/monitor_normalisation/monitor_normalisation.py index ec0c4ec1ae1..ff460c73964 100644 --- a/mantidimaging/core/operations/monitor_normalisation/monitor_normalisation.py +++ b/mantidimaging/core/operations/monitor_normalisation/monitor_normalisation.py @@ -37,6 +37,11 @@ def filter_func(images: ImageStack, progress=None) -> ImageStack: """ :return: The ImageStack object which has been normalised. """ + if '180deg' in images.name.lower(): + if progress: + progress("Skipping normalization for the 180-degree stack, no logfile.") + return images + if images.num_projections == 1: # we can't really compute the preview as the image stack copy # passed in doesn't have the logfile in it From e8fc9aa079796e003c1c3f17ef81ecd0dd0b0b8b Mon Sep 17 00:00:00 2001 From: ashmeigh Date: Thu, 25 Jul 2024 13:48:58 +0100 Subject: [PATCH 2/7] Refactor _post_filter method to handle 180-degree stacks based on 180_degree_log attribute --- mantidimaging/core/operations/base_filter.py | 1 + .../monitor_normalisation.py | 5 +---- .../gui/windows/operations/presenter.py | 18 +++++++++++++++--- 3 files changed, 17 insertions(+), 7 deletions(-) diff --git a/mantidimaging/core/operations/base_filter.py b/mantidimaging/core/operations/base_filter.py index 2dfdd03cfde..0fde8e03c28 100644 --- a/mantidimaging/core/operations/base_filter.py +++ b/mantidimaging/core/operations/base_filter.py @@ -28,6 +28,7 @@ class BaseFilter: link_histograms = False show_negative_overlay = True operate_on_sinograms = False + log_180_degree = False SINOGRAM_FILTER_INFO = "This filter will work on a\nsinogram view of the data." diff --git a/mantidimaging/core/operations/monitor_normalisation/monitor_normalisation.py b/mantidimaging/core/operations/monitor_normalisation/monitor_normalisation.py index ff460c73964..3e02232639d 100644 --- a/mantidimaging/core/operations/monitor_normalisation/monitor_normalisation.py +++ b/mantidimaging/core/operations/monitor_normalisation/monitor_normalisation.py @@ -31,16 +31,13 @@ class MonitorNormalisation(BaseFilter): """ filter_name = "Monitor Normalisation" link_histograms = True + log_180_degree = False @staticmethod def filter_func(images: ImageStack, progress=None) -> ImageStack: """ :return: The ImageStack object which has been normalised. """ - if '180deg' in images.name.lower(): - if progress: - progress("Skipping normalization for the 180-degree stack, no logfile.") - return images if images.num_projections == 1: # we can't really compute the preview as the image stack copy diff --git a/mantidimaging/gui/windows/operations/presenter.py b/mantidimaging/gui/windows/operations/presenter.py index 21293e4f538..7801c94b273 100644 --- a/mantidimaging/gui/windows/operations/presenter.py +++ b/mantidimaging/gui/windows/operations/presenter.py @@ -275,12 +275,24 @@ def _post_filter(self, updated_stacks: list[ImageStack], task): # otherwise check with user which one to keep if self.view.safeApply.isChecked(): use_new_data = self._wait_for_stack_choice(stack, stack.id) - # if the stack that was kept happened to have a proj180 stack - then apply the filter to that too - if stack.has_proj180deg() and use_new_data and not self.applying_to_all: + + if self.is_a_proj180deg(stack): + # Handle 180-degree stacks based on the log_180_degree attribute + if not self.model.selected_filter.log_180_degree: + if task.progress: + task.progress("Skipping normalization for the 180-degree stack.") + continue + # Apply to proj180 synchronously if necessary + if use_new_data and not self.applying_to_all and not self.is_a_proj180deg(stack): # Apply to proj180 synchronously - this function is already running async # and running another async instance causes a race condition in the parallel module # where the shared data can be removed in the middle of the operation of another operation - self._do_apply_filter_sync([stack.proj180deg]) + try: + self._do_apply_filter_sync([stack.proj180deg]) + except RuntimeError as e: + self.view.show_error_dialog(f"Operation failed on proj180deg stack: {e}") + continue + if np.any(stack.data < 0): negative_stacks.append(stack) From b658f82532a6fd29295581a406c45b83ac2abdd8 Mon Sep 17 00:00:00 2001 From: ashmeigh Date: Mon, 29 Jul 2024 15:06:32 +0100 Subject: [PATCH 3/7] moved inside the other if statement --- mantidimaging/core/operations/base_filter.py | 2 +- .../monitor_normalisation.py | 2 +- .../gui/windows/operations/presenter.py | 28 ++++++++----------- 3 files changed, 14 insertions(+), 18 deletions(-) diff --git a/mantidimaging/core/operations/base_filter.py b/mantidimaging/core/operations/base_filter.py index 0fde8e03c28..30cf02bfe1b 100644 --- a/mantidimaging/core/operations/base_filter.py +++ b/mantidimaging/core/operations/base_filter.py @@ -28,7 +28,7 @@ class BaseFilter: link_histograms = False show_negative_overlay = True operate_on_sinograms = False - log_180_degree = False + allow_for_180_projection = True SINOGRAM_FILTER_INFO = "This filter will work on a\nsinogram view of the data." diff --git a/mantidimaging/core/operations/monitor_normalisation/monitor_normalisation.py b/mantidimaging/core/operations/monitor_normalisation/monitor_normalisation.py index 3e02232639d..4cb4a9d0a00 100644 --- a/mantidimaging/core/operations/monitor_normalisation/monitor_normalisation.py +++ b/mantidimaging/core/operations/monitor_normalisation/monitor_normalisation.py @@ -31,7 +31,7 @@ class MonitorNormalisation(BaseFilter): """ filter_name = "Monitor Normalisation" link_histograms = True - log_180_degree = False + allow_for_180_projection = False @staticmethod def filter_func(images: ImageStack, progress=None) -> ImageStack: diff --git a/mantidimaging/gui/windows/operations/presenter.py b/mantidimaging/gui/windows/operations/presenter.py index 7801c94b273..cfa7fe6d0e6 100644 --- a/mantidimaging/gui/windows/operations/presenter.py +++ b/mantidimaging/gui/windows/operations/presenter.py @@ -276,22 +276,18 @@ def _post_filter(self, updated_stacks: list[ImageStack], task): if self.view.safeApply.isChecked(): use_new_data = self._wait_for_stack_choice(stack, stack.id) - if self.is_a_proj180deg(stack): - # Handle 180-degree stacks based on the log_180_degree attribute - if not self.model.selected_filter.log_180_degree: - if task.progress: - task.progress("Skipping normalization for the 180-degree stack.") - continue - # Apply to proj180 synchronously if necessary - if use_new_data and not self.applying_to_all and not self.is_a_proj180deg(stack): - # Apply to proj180 synchronously - this function is already running async - # and running another async instance causes a race condition in the parallel module - # where the shared data can be removed in the middle of the operation of another operation - try: - self._do_apply_filter_sync([stack.proj180deg]) - except RuntimeError as e: - self.view.show_error_dialog(f"Operation failed on proj180deg stack: {e}") - continue + # Apply to proj180 synchronously if necessary + if use_new_data and not self.applying_to_all and self.is_a_proj180deg(stack): + # Handle 180-degree stacks based on the log_180_degree attribute + if not self.model.selected_filter.allow_for_180_projection: + if task.progress: + task.progress("Skipping normalization for the 180-degree stack.") + continue + + # Apply to proj180 synchronously - this function is already running async + # and running another async instance causes a race condition in the parallel module + # where the shared data can be removed in the middle of the operation of another operation + self._do_apply_filter_sync([stack.proj180deg]) if np.any(stack.data < 0): negative_stacks.append(stack) From f22b0adff4ca73e5fef90ec72f1ea31cf8b6b7d1 Mon Sep 17 00:00:00 2001 From: ashmeigh Date: Mon, 29 Jul 2024 15:36:20 +0100 Subject: [PATCH 4/7] fixed error to handle cases better --- .../gui/windows/operations/presenter.py | 16 ++++++++-------- 1 file changed, 8 insertions(+), 8 deletions(-) diff --git a/mantidimaging/gui/windows/operations/presenter.py b/mantidimaging/gui/windows/operations/presenter.py index cfa7fe6d0e6..3c1562e51cf 100644 --- a/mantidimaging/gui/windows/operations/presenter.py +++ b/mantidimaging/gui/windows/operations/presenter.py @@ -276,18 +276,18 @@ def _post_filter(self, updated_stacks: list[ImageStack], task): if self.view.safeApply.isChecked(): use_new_data = self._wait_for_stack_choice(stack, stack.id) - # Apply to proj180 synchronously if necessary - if use_new_data and not self.applying_to_all and self.is_a_proj180deg(stack): - # Handle 180-degree stacks based on the log_180_degree attribute - if not self.model.selected_filter.allow_for_180_projection: - if task.progress: - task.progress("Skipping normalization for the 180-degree stack.") - continue + if use_new_data and not self.applying_to_all: + if self.is_a_proj180deg(stack): + if self.model.selected_filter.allow_for_180_projection: + self._do_apply_filter_sync([stack.proj180deg]) + else: + if task.progress: + task.progress("Skipping normalization for the 180-degree stack.") + continue # Apply to proj180 synchronously - this function is already running async # and running another async instance causes a race condition in the parallel module # where the shared data can be removed in the middle of the operation of another operation - self._do_apply_filter_sync([stack.proj180deg]) if np.any(stack.data < 0): negative_stacks.append(stack) From 18d87b69a092cafcb32d813bfee15c032e8cc532 Mon Sep 17 00:00:00 2001 From: ashmeigh Date: Wed, 31 Jul 2024 14:31:06 +0100 Subject: [PATCH 5/7] fixing error --- .../gui/windows/operations/presenter.py | 22 ++++++++----------- 1 file changed, 9 insertions(+), 13 deletions(-) diff --git a/mantidimaging/gui/windows/operations/presenter.py b/mantidimaging/gui/windows/operations/presenter.py index 3c1562e51cf..4d794876d1e 100644 --- a/mantidimaging/gui/windows/operations/presenter.py +++ b/mantidimaging/gui/windows/operations/presenter.py @@ -276,29 +276,26 @@ def _post_filter(self, updated_stacks: list[ImageStack], task): if self.view.safeApply.isChecked(): use_new_data = self._wait_for_stack_choice(stack, stack.id) - if use_new_data and not self.applying_to_all: - if self.is_a_proj180deg(stack): - if self.model.selected_filter.allow_for_180_projection: - self._do_apply_filter_sync([stack.proj180deg]) - else: - if task.progress: - task.progress("Skipping normalization for the 180-degree stack.") - continue + if self.is_a_proj180deg(stack): + # Handle 180-degree stacks based on the log_180_degree attribute + if not self.model.selected_filter.allow_for_180_projection: + continue + # Apply to proj180 synchronously if necessary + if use_new_data and not self.applying_to_all and stack.has_proj180deg(): # Apply to proj180 synchronously - this function is already running async # and running another async instance causes a race condition in the parallel module # where the shared data can be removed in the middle of the operation of another operation + self._do_apply_filter_sync([stack.proj180deg]) - if np.any(stack.data < 0): - negative_stacks.append(stack) + if np.any(stack.data < 0): + negative_stacks.append(stack) if self.view.roi_view is not None: self.view.roi_view.close() self.view.roi_view = None - self.applying_to_all = False self.do_update_previews() - if task.error is not None: # task failed, show why self.view.show_error_dialog(f"Operation failed: {task.error}") @@ -306,7 +303,6 @@ def _post_filter(self, updated_stacks: list[ImageStack], task): # Feedback to user self.view.clear_notification_dialog() self.view.show_operation_completed(self.model.selected_filter.filter_name) - selected_filter = self.view.get_selected_filter() if selected_filter == CROP_COORDINATES: # Reset the ROI field to ensure an appropriate value for the new image size From 51cc0e01dc0ff4c7f0294bebc47d325f4c794bbd Mon Sep 17 00:00:00 2001 From: ashmeigh Date: Fri, 9 Aug 2024 07:22:22 +0100 Subject: [PATCH 6/7] Correct logic for 180-degree projection handling in _post_filter method --- .../gui/windows/operations/presenter.py | 64 +++++++++---------- 1 file changed, 31 insertions(+), 33 deletions(-) diff --git a/mantidimaging/gui/windows/operations/presenter.py b/mantidimaging/gui/windows/operations/presenter.py index 4d794876d1e..c7cf9d5e036 100644 --- a/mantidimaging/gui/windows/operations/presenter.py +++ b/mantidimaging/gui/windows/operations/presenter.py @@ -276,42 +276,40 @@ def _post_filter(self, updated_stacks: list[ImageStack], task): if self.view.safeApply.isChecked(): use_new_data = self._wait_for_stack_choice(stack, stack.id) - if self.is_a_proj180deg(stack): - # Handle 180-degree stacks based on the log_180_degree attribute - if not self.model.selected_filter.allow_for_180_projection: - continue - - # Apply to proj180 synchronously if necessary - if use_new_data and not self.applying_to_all and stack.has_proj180deg(): + # Apply 180-degree projection if allowed + if use_new_data and not self.applying_to_all: # Apply to proj180 synchronously - this function is already running async # and running another async instance causes a race condition in the parallel module # where the shared data can be removed in the middle of the operation of another operation - self._do_apply_filter_sync([stack.proj180deg]) - - if np.any(stack.data < 0): - negative_stacks.append(stack) - - if self.view.roi_view is not None: - self.view.roi_view.close() - self.view.roi_view = None - self.applying_to_all = False - self.do_update_previews() - if task.error is not None: - # task failed, show why - self.view.show_error_dialog(f"Operation failed: {task.error}") - elif use_new_data: - # Feedback to user - self.view.clear_notification_dialog() - self.view.show_operation_completed(self.model.selected_filter.filter_name) - selected_filter = self.view.get_selected_filter() - if selected_filter == CROP_COORDINATES: - # Reset the ROI field to ensure an appropriate value for the new image size - self.init_roi_field(self.model.filter_widget_kwargs["roi_field"]) - elif selected_filter == FLAT_FIELDING and negative_stacks: - self._show_negative_values_error(negative_stacks) - else: - self.view.clear_notification_dialog() - self.view.show_operation_cancelled(self.model.selected_filter.filter_name) + # if the stack that was kept happened to have a proj180 stack - then apply the filter to that too + if self.model.selected_filter.allow_for_180_projection: + self._do_apply_filter_sync([stack.proj180deg]) + continue + + if np.any(stack.data < 0): + negative_stacks.append(stack) + + if self.view.roi_view is not None: + self.view.roi_view.close() + self.view.roi_view = None + self.applying_to_all = False + self.do_update_previews() + if task.error is not None: + # task failed, show why + self.view.show_error_dialog(f"Operation failed: {task.error}") + elif use_new_data: + # Feedback to user + self.view.clear_notification_dialog() + self.view.show_operation_completed(self.model.selected_filter.filter_name) + selected_filter = self.view.get_selected_filter() + if selected_filter == CROP_COORDINATES: + # Reset the ROI field to ensure an appropriate value for the new image size + self.init_roi_field(self.model.filter_widget_kwargs["roi_field"]) + elif selected_filter == FLAT_FIELDING and negative_stacks: + self._show_negative_values_error(negative_stacks) + else: + self.view.clear_notification_dialog() + self.view.show_operation_cancelled(self.model.selected_filter.filter_name) finally: self.view.filter_applied.emit() self._set_apply_buttons_enabled(self.prev_apply_single_state, self.prev_apply_all_state) From e09928a267885a0e72650900aec16db62963b2a2 Mon Sep 17 00:00:00 2001 From: ashmeigh Date: Mon, 12 Aug 2024 07:42:23 +0100 Subject: [PATCH 7/7] Correct logic for 180-degree projection handling in _post_filter method --- .../gui/windows/operations/presenter.py | 56 +++++++++---------- 1 file changed, 27 insertions(+), 29 deletions(-) diff --git a/mantidimaging/gui/windows/operations/presenter.py b/mantidimaging/gui/windows/operations/presenter.py index c7cf9d5e036..644d9fe03e8 100644 --- a/mantidimaging/gui/windows/operations/presenter.py +++ b/mantidimaging/gui/windows/operations/presenter.py @@ -276,40 +276,38 @@ def _post_filter(self, updated_stacks: list[ImageStack], task): if self.view.safeApply.isChecked(): use_new_data = self._wait_for_stack_choice(stack, stack.id) - # Apply 180-degree projection if allowed - if use_new_data and not self.applying_to_all: - # Apply to proj180 synchronously - this function is already running async - # and running another async instance causes a race condition in the parallel module - # where the shared data can be removed in the middle of the operation of another operation - # if the stack that was kept happened to have a proj180 stack - then apply the filter to that too - if self.model.selected_filter.allow_for_180_projection: + if self.model.selected_filter.allow_for_180_projection: + # Apply to proj180 synchronously if necessary + if use_new_data and not self.applying_to_all and stack.has_proj180deg(): + # Apply to proj180 synchronously - this function is already running async + # and running another async instance causes a race condition in the parallel module + # where the shared data can be removed in the middle of the operation of another operation self._do_apply_filter_sync([stack.proj180deg]) - continue if np.any(stack.data < 0): negative_stacks.append(stack) - if self.view.roi_view is not None: - self.view.roi_view.close() - self.view.roi_view = None - self.applying_to_all = False - self.do_update_previews() - if task.error is not None: - # task failed, show why - self.view.show_error_dialog(f"Operation failed: {task.error}") - elif use_new_data: - # Feedback to user - self.view.clear_notification_dialog() - self.view.show_operation_completed(self.model.selected_filter.filter_name) - selected_filter = self.view.get_selected_filter() - if selected_filter == CROP_COORDINATES: - # Reset the ROI field to ensure an appropriate value for the new image size - self.init_roi_field(self.model.filter_widget_kwargs["roi_field"]) - elif selected_filter == FLAT_FIELDING and negative_stacks: - self._show_negative_values_error(negative_stacks) - else: - self.view.clear_notification_dialog() - self.view.show_operation_cancelled(self.model.selected_filter.filter_name) + if self.view.roi_view is not None: + self.view.roi_view.close() + self.view.roi_view = None + self.applying_to_all = False + self.do_update_previews() + if task.error is not None: + # task failed, show why + self.view.show_error_dialog(f"Operation failed: {task.error}") + elif use_new_data: + # Feedback to user + self.view.clear_notification_dialog() + self.view.show_operation_completed(self.model.selected_filter.filter_name) + selected_filter = self.view.get_selected_filter() + if selected_filter == CROP_COORDINATES: + # Reset the ROI field to ensure an appropriate value for the new image size + self.init_roi_field(self.model.filter_widget_kwargs["roi_field"]) + elif selected_filter == FLAT_FIELDING and negative_stacks: + self._show_negative_values_error(negative_stacks) + else: + self.view.clear_notification_dialog() + self.view.show_operation_cancelled(self.model.selected_filter.filter_name) finally: self.view.filter_applied.emit() self._set_apply_buttons_enabled(self.prev_apply_single_state, self.prev_apply_all_state)