Skip to content

Commit

Permalink
Merge remote-tracking branch 'origin/main'
Browse files Browse the repository at this point in the history
  • Loading branch information
ashmeigh committed Jul 18, 2024
2 parents 68d460a + 8cf298f commit 13b78f5
Show file tree
Hide file tree
Showing 30 changed files with 136 additions and 127 deletions.
2 changes: 1 addition & 1 deletion mantidimaging/core/utility/close_enough_point.py
Original file line number Diff line number Diff line change
Expand Up @@ -20,5 +20,5 @@ def __init__(self, points: Sequence[int] | Sequence[float]):
self.y = int(points[1])
self.x = int(points[0])

def __str__(self):
def __str__(self) -> str:
return f"({self.x}, {self.y})"
5 changes: 3 additions & 2 deletions mantidimaging/core/utility/command_line_arguments.py
Original file line number Diff line number Diff line change
Expand Up @@ -3,17 +3,18 @@
from __future__ import annotations
from logging import getLogger
import os
from typing import NoReturn

from mantidimaging.core.operations.loader import load_filter_packages

logger = getLogger(__name__)


def _get_filter_names():
def _get_filter_names() -> dict[str, str]:
return {package.filter_name.replace(" ", "-").lower(): package.filter_name for package in load_filter_packages()}


def _log_and_exit(msg: str):
def _log_and_exit(msg: str) -> NoReturn:
"""
Log an error message and exit.
:param msg: The log message.
Expand Down
2 changes: 1 addition & 1 deletion mantidimaging/core/utility/cor_interpolate.py
Original file line number Diff line number Diff line change
Expand Up @@ -5,7 +5,7 @@
import numpy as np


def execute(data_length, slice_ids, cors_for_sinograms):
def execute(data_length: int, slice_ids: np.ndarray, cors_for_sinograms: np.ndarray) -> np.ndarray:
"""
Interpolates the Centers of Rotation for the sinograms that are not
provided.
Expand Down
4 changes: 2 additions & 2 deletions mantidimaging/core/utility/cuda_check.py
Original file line number Diff line number Diff line change
Expand Up @@ -39,7 +39,7 @@ class CudaChecker:
_instance = None
_cuda_is_present = False

def __new__(cls):
def __new__(cls) -> CudaChecker:
"""
Creates a singleton for storing the result of the Cuda check.
"""
Expand All @@ -56,7 +56,7 @@ def cuda_is_present(cls) -> bool:
return cls._cuda_is_present

@classmethod
def clear_instance(cls):
def clear_instance(cls) -> None:
"""
Resets the instance. Used for making sure mocks don't leak in tests.
"""
Expand Down
4 changes: 2 additions & 2 deletions mantidimaging/core/utility/data_containers.py
Original file line number Diff line number Diff line change
Expand Up @@ -48,7 +48,7 @@ class ScalarCoR(SingleValue):
__slots__ = 'value'
value: float

def to_vec(self, detector_width):
def to_vec(self, detector_width: float) -> VectorCoR:
return VectorCoR(detector_width / 2 - self.value)


Expand All @@ -57,7 +57,7 @@ class VectorCoR(SingleValue):
__slots__ = 'value'
value: float

def to_scalar(self, detector_width):
def to_scalar(self, detector_width: float) -> ScalarCoR:
return ScalarCoR(detector_width / 2 + self.value)


Expand Down
16 changes: 8 additions & 8 deletions mantidimaging/core/utility/execution_timer.py
Original file line number Diff line number Diff line change
Expand Up @@ -35,27 +35,27 @@ def __init__(self, msg: str = 'Elapsed time', logger: Logger = perf_logger):
self.time_start: float | None = None
self.time_end: float | None = None

def __str__(self):
def __str__(self) -> str:
prefix = f'{self.msg}: ' if self.msg else ''
sec = self.total_seconds
return f'{prefix}{sec if sec else "unknown"} seconds'

def __enter__(self):
def __enter__(self) -> None:
self.time_start = time.monotonic()
self.time_end = None

def __exit__(self, *args):
def __exit__(self, *args) -> None:
self.time_end = time.monotonic()
self.logger.info(str(self))

@property
def total_seconds(self):
def total_seconds(self) -> float:
"""
Gets the total number of seconds the timer was running for, returns
None if the timer has not been run or is still running.
"""
return self.time_end - self.time_start if \
self.time_start and self.time_end else None
self.time_start and self.time_end else 0


class ExecutionProfiler:
Expand All @@ -77,18 +77,18 @@ def __init__(self,

self.pr = cProfile.Profile()

def __str__(self):
def __str__(self) -> str:
out = StringIO()
out.write(f'{self.msg}: \n' if self.msg else '')

ps = Stats(self.pr, stream=out).sort_stats(self.sort_by)
ps.print_stats()
return out.getvalue()

def __enter__(self):
def __enter__(self) -> None:
self.pr.enable()

def __exit__(self, *args):
def __exit__(self, *args) -> None:
self.pr.disable()
if perf_logger.isEnabledFor(1):
for line in str(self).split("\n")[:self.max_lines]:
Expand Down
4 changes: 2 additions & 2 deletions mantidimaging/core/utility/histogram.py
Original file line number Diff line number Diff line change
Expand Up @@ -11,7 +11,7 @@
DEFAULT_NUM_BINS = 2048


def set_histogram_log_scale(histogram: HistogramLUTItem):
def set_histogram_log_scale(histogram: HistogramLUTItem) -> None:
"""
Sets the y-values of a histogram to use a log scale.
:param histogram: The HistogramLUTItem of an image.
Expand All @@ -20,7 +20,7 @@ def set_histogram_log_scale(histogram: HistogramLUTItem):
histogram.plot.setData(x_data, np.log(y_data + 1))


def generate_histogram_from_image(image_data, num_bins=DEFAULT_NUM_BINS):
def generate_histogram_from_image(image_data: np.ndarray, num_bins: int = DEFAULT_NUM_BINS) -> tuple:
histogram, bins = np.histogram(image_data.flatten(), num_bins)
center = (bins[:-1] + bins[1:]) / 2
return center, histogram, bins
3 changes: 2 additions & 1 deletion mantidimaging/core/utility/imat_log_file_parser.py
Original file line number Diff line number Diff line change
Expand Up @@ -10,6 +10,7 @@
from typing import TYPE_CHECKING

import numpy
import numpy as np

from mantidimaging.core.utility.data_containers import Counts, ProjectionAngles

Expand Down Expand Up @@ -210,7 +211,7 @@ def get_seperator(first_row: str) -> bool:
def source_file(self) -> Path:
return self._source_file

def projection_numbers(self):
def projection_numbers(self) -> np.ndarray:
proj_nums = numpy.zeros(len(self._data[IMATLogColumn.PROJECTION_NUMBER]), dtype=numpy.uint32)
proj_nums[:] = self._data[IMATLogColumn.PROJECTION_NUMBER]
return proj_nums
Expand Down
4 changes: 2 additions & 2 deletions mantidimaging/core/utility/optional_imports.py
Original file line number Diff line number Diff line change
Expand Up @@ -35,9 +35,9 @@ def safe_import(name):
return module


def check_availability(name):
def check_availability(name: str) -> bool:
return safe_import(name) is not None


def tomopy_available():
def tomopy_available() -> bool:
return check_availability('tomopy')
2 changes: 1 addition & 1 deletion mantidimaging/core/utility/sensible_roi.py
Original file line number Diff line number Diff line change
Expand Up @@ -40,7 +40,7 @@ def __iter__(self) -> Iterator[int]:
"""
return iter((self.left, self.top, self.right, self.bottom))

def __str__(self):
def __str__(self) -> str:
return f"Left: {self.left}, Top: {self.top}, Right: {self.right}, Bottom: {self.bottom}"

def to_list_string(self) -> str:
Expand Down
8 changes: 4 additions & 4 deletions mantidimaging/core/utility/size_calculator.py
Original file line number Diff line number Diff line change
Expand Up @@ -30,17 +30,17 @@ def full_size(shape: Iterable[int]) -> int:
return math.prod(shape)


def full_size_bytes(shape: Iterable[int], dtype: npt.DTypeLike):
def full_size_bytes(shape: Iterable[int], dtype: npt.DTypeLike) -> int:
return full_size(shape) * _determine_dtype_size(dtype)


def full_size_KB(shape: Iterable[int], dtype: npt.DTypeLike):
def full_size_KB(shape: Iterable[int], dtype: npt.DTypeLike) -> float:
return full_size_bytes(shape, dtype) / 1024


def full_size_MB(shape: Iterable[int], dtype: npt.DTypeLike):
def full_size_MB(shape: Iterable[int], dtype: npt.DTypeLike) -> float:
return full_size_KB(shape, dtype) / 1024


def number_of_images_from_indices(start, end, increment):
def number_of_images_from_indices(start: int, end: int, increment: int) -> int:
return int((end - start) / increment) if increment != 0 else 0
4 changes: 2 additions & 2 deletions mantidimaging/core/utility/test/execution_timer_test.py
Original file line number Diff line number Diff line change
Expand Up @@ -12,11 +12,11 @@ class ExecutionTimerTest(unittest.TestCase):

def test_execute(self):
t = ExecutionTimer()
self.assertEqual(t.total_seconds, None)
self.assertEqual(t.total_seconds, 0.0)
self.assertEqual(str(t), 'Elapsed time: unknown seconds')

with t:
self.assertEqual(t.total_seconds, None)
self.assertEqual(t.total_seconds, 0.0)

time.sleep(0.1)

Expand Down
2 changes: 1 addition & 1 deletion mantidimaging/gui/dialogs/async_task/model.py
Original file line number Diff line number Diff line change
Expand Up @@ -24,7 +24,7 @@ def __init__(self) -> None:
self.on_complete_function: Callable | None = None
self.tracker: set | None = None

def set_tracker(self, tracker: set):
def set_tracker(self, tracker: set) -> None:
self.tracker = tracker
self.tracker.add(self)

Expand Down
16 changes: 8 additions & 8 deletions mantidimaging/gui/dialogs/async_task/presenter.py
Original file line number Diff line number Diff line change
Expand Up @@ -30,7 +30,7 @@ def __init__(self, view):
self.model = AsyncTaskDialogModel()
self.model.task_done.connect(self.view.handle_completion)

def notify(self, signal):
def notify(self, signal) -> None:
try:
if signal == Notification.START:
self.do_start_processing()
Expand All @@ -39,29 +39,29 @@ def notify(self, signal):
self.show_error(e, traceback.format_exc())
getLogger(__name__).exception("Notification handler failed")

def set_task(self, f: Callable):
def set_task(self, f: Callable) -> None:
self.model.task.task_function = f

def set_parameters(self, **kwargs):
def set_parameters(self, **kwargs) -> None:
self.model.task.kwargs = kwargs

def set_on_complete(self, f: Callable):
def set_on_complete(self, f: Callable) -> None:
self.model.on_complete_function = f

def set_tracker(self, tracker: set):
def set_tracker(self, tracker: set) -> None:
self.model.set_tracker(tracker)

def do_start_processing(self):
def do_start_processing(self) -> None:
"""
Starts async task execution and shows GUI.
"""
self.model.do_execute_async()
self.view.show_delayed(1000)

@property
def task_is_running(self):
def task_is_running(self) -> None:
return self.model.task_is_running

def progress_update(self):
def progress_update(self) -> None:
msg = self.progress.last_status_message()
self.progress_updated.emit(self.progress.completion(), msg if msg is not None else '')
6 changes: 3 additions & 3 deletions mantidimaging/gui/dialogs/async_task/view.py
Original file line number Diff line number Diff line change
Expand Up @@ -33,7 +33,7 @@ def presenter(self) -> AsyncTaskDialogPresenter:
raise RuntimeError("Presenter accessed after handle_completion")
return self._presenter

def handle_completion(self, successful: bool):
def handle_completion(self, successful: bool) -> None:
"""
Updates the UI after the task has been completed.
Expand Down Expand Up @@ -61,11 +61,11 @@ def set_progress(self, progress: float, message: str):
# Update progress bar
self.progressBar.setValue(int(progress * 1000))

def show_delayed(self, timeout):
def show_delayed(self, timeout) -> None:
self.show_timer.singleShot(timeout, self.show_from_timer)
self.show_timer.start()

def show_from_timer(self):
def show_from_timer(self) -> None:
# Might not run until after handle_completion
if self._presenter is not None and self.presenter.task_is_running:
self.show()
Expand Down
26 changes: 15 additions & 11 deletions mantidimaging/gui/dialogs/cor_inspection/model.py
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,10 @@
from __future__ import annotations
from dataclasses import replace
from logging import getLogger
from typing import TYPE_CHECKING

if TYPE_CHECKING:
import numpy as np

from mantidimaging.core.data import ImageStack
from mantidimaging.core.reconstruct import get_reconstructor_for
Expand All @@ -26,7 +30,7 @@ def __init__(self, images: ImageStack, slice_idx: int, initial_cor: ScalarCoR,
# Initial parameters
if iters_mode:
self.centre_value: int | float = INIT_ITERS_CENTRE_VALUE
self.step = INIT_ITERS_STEP
self.step: float = INIT_ITERS_STEP
self.initial_cor = initial_cor
self._recon_preview = self._recon_iters_preview
self._divide_step = self._divide_iters_step
Expand All @@ -41,13 +45,13 @@ def __init__(self, images: ImageStack, slice_idx: int, initial_cor: ScalarCoR,
self.recon_params = recon_params
self.reconstructor = get_reconstructor_for(recon_params.algorithm)

def _divide_iters_step(self):
def _divide_iters_step(self) -> None:
self.step = self.step // 2

def _divide_cor_step(self):
def _divide_cor_step(self) -> None:
self.step /= 2

def adjust(self, image):
def adjust(self, image: ImageType) -> None:
"""
Adjusts the rotation centre/number of iterations and step after an image is selected as the
optimal of an iteration.
Expand All @@ -59,7 +63,7 @@ def adjust(self, image):
elif image == ImageType.CURRENT:
self._divide_step()

def cor(self, image):
def cor(self, image: ImageType) -> float:
"""
Gets the rotation centre for a given image in the current iteration.
"""
Expand All @@ -70,26 +74,26 @@ def cor(self, image):
elif image == ImageType.MORE:
return min(self.cor_extents[1], self.centre_value + self.step)

def iterations(self, image):
def iterations(self, image: ImageType) -> float:
if image == ImageType.LESS:
return max(1, self.centre_value - self.step)
elif image == ImageType.CURRENT:
return self.centre_value
elif image == ImageType.MORE:
return self.centre_value + self.step

def _recon_cor_preview(self, image):
def _recon_cor_preview(self, image: ImageType) -> np.ndarray:
cor = ScalarCoR(self.cor(image))
return self.reconstructor.single_sino(self.sino, cor, self.proj_angles, self.recon_params)

def _recon_iters_preview(self, image):
def _recon_iters_preview(self, image: ImageType) -> np.ndarray:
iters = self.iterations(image)
new_params = replace(self.recon_params, num_iter=iters)
new_params = replace(self.recon_params, num_iter=int(iters))
return self.reconstructor.single_sino(self.sino, self.initial_cor, self.proj_angles, new_params)

def recon_preview(self, image):
def recon_preview(self, image: ImageType) -> np.ndarray:
return self._recon_preview(image)

@property
def cor_extents(self):
def cor_extents(self) -> tuple[int, int]:
return 0, self.sino.shape[1] - 1
Loading

0 comments on commit 13b78f5

Please sign in to comment.