Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Fix error 'ImageStack' object has no attribute 'shape' #2131

Closed
wants to merge 6 commits into from
Closed
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
9 changes: 4 additions & 5 deletions CheckLicensesInFiles.py
Original file line number Diff line number Diff line change
Expand Up @@ -11,14 +11,13 @@

import json
import sys
from typing import List, Dict


def load_copyright_header() -> Dict[str, str]:
def load_copyright_header() -> dict[str, str]:
return json.load(open(".licenserc.json"))


def find_files_with_incorrect_license_headers(filepaths: List[str], copyright_text: str) -> List[str]:
def find_files_with_incorrect_license_headers(filepaths: list[str], copyright_text: str) -> list[str]:
copyright_lines = copyright_text.split("\n")
incorrect_files = []

Expand All @@ -35,11 +34,11 @@ def find_files_with_incorrect_license_headers(filepaths: List[str], copyright_te
return incorrect_files


def has_shebang_line(file_lines: List[str]) -> bool:
def has_shebang_line(file_lines: list[str]) -> bool:
return file_lines[0].startswith("#!")


def has_correct_copyright_lines(file_lines: List[str], copyright_lines: List[str]) -> bool:
def has_correct_copyright_lines(file_lines: list[str], copyright_lines: list[str]) -> bool:
if len(file_lines) < len(copyright_lines):
return False

Expand Down
9 changes: 4 additions & 5 deletions docs/ext/operations_user_doc.py
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,6 @@
# SPDX - License - Identifier: GPL-3.0-or-later
from __future__ import annotations

from typing import List
import inspect

from docutils import nodes
Expand All @@ -19,11 +18,11 @@
"""


def make_heading(s: str, char: str) -> List[str]:
def make_heading(s: str, char: str) -> list[str]:
return [s, len(s) * char, ""]


def split_lines(s: str) -> List[str]:
def split_lines(s: str) -> list[str]:
s = s.replace("\n\n", "DOUBLE_NEW_LINE")
s = s.replace("\n", " ")
s = s.replace("DOUBLE_NEW_LINE", "\n\n")
Expand All @@ -33,7 +32,7 @@ def split_lines(s: str) -> List[str]:
PARAM_SKIP_LIST = ["images", "progress"]


def get_params(s: str) -> List[str]:
def get_params(s: str) -> list[str]:
ret = []
for line in s.split("\n"):
if line.strip().startswith(":param"):
Expand All @@ -51,7 +50,7 @@ def get_params(s: str) -> List[str]:

class OperationsUserDoc(Directive):

def run(self) -> List[Node]:
def run(self) -> list[Node]:

try:
from mantidimaging.core.operations.loader import load_filter_packages
Expand Down
1 change: 1 addition & 0 deletions docs/release_notes/next/fix-2129-median_cuda
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
#2129 : fix: Pytest failure AttributeError: 'ImageStack' object has no attribute 'shape
41 changes: 20 additions & 21 deletions mantidimaging/core/data/dataset.py
Original file line number Diff line number Diff line change
Expand Up @@ -3,7 +3,6 @@
from __future__ import annotations
import uuid
from dataclasses import dataclass
from typing import Optional, List, Union

import numpy as np

Expand All @@ -26,7 +25,7 @@ def __init__(self, name: str = ""):
self._id: uuid.UUID = uuid.uuid4()
self.recons = ReconList()
self._name = name
self._sinograms: Optional[ImageStack] = None
self._sinograms: ImageStack | None = None

@property
def id(self) -> uuid.UUID:
Expand All @@ -41,11 +40,11 @@ def name(self, arg: str):
self._name = arg

@property
def sinograms(self) -> Optional[ImageStack]:
def sinograms(self) -> ImageStack | None:
return self._sinograms

@sinograms.setter
def sinograms(self, sino: Optional[ImageStack]):
def sinograms(self, sino: ImageStack | None):
self._sinograms = sino

@property
Expand All @@ -59,7 +58,7 @@ def __contains__(self, images_id: uuid.UUID) -> bool:
return any(image.id == images_id for image in self.all)

@property
def all_image_ids(self) -> List[uuid.UUID]:
def all_image_ids(self) -> list[uuid.UUID]:
return [image_stack.id for image_stack in self.all if image_stack is not None]

def add_recon(self, recon: ImageStack):
Expand All @@ -71,7 +70,7 @@ def delete_recons(self):

class MixedDataset(BaseDataset):

def __init__(self, stacks: Optional[List[ImageStack]] = None, name: str = ""):
def __init__(self, stacks: list[ImageStack] | None = None, name: str = ""):
super().__init__(name=name)
stacks = [] if stacks is None else stacks
self._stacks = stacks
Expand All @@ -80,7 +79,7 @@ def add_stack(self, stack: ImageStack):
self._stacks.append(stack)

@property
def all(self) -> List[ImageStack]:
def all(self) -> list[ImageStack]:
all_images = self._stacks + self.recons.stacks
if self.sinograms is None:
return all_images
Expand All @@ -104,17 +103,17 @@ def delete_stack(self, images_id: uuid.UUID):
@dataclass
class StrictDataset(BaseDataset):
sample: ImageStack
flat_before: Optional[ImageStack] = None
flat_after: Optional[ImageStack] = None
dark_before: Optional[ImageStack] = None
dark_after: Optional[ImageStack] = None
flat_before: ImageStack | None = None
flat_after: ImageStack | None = None
dark_before: ImageStack | None = None
dark_after: ImageStack | None = None

def __init__(self,
sample: ImageStack,
flat_before: Optional[ImageStack] = None,
flat_after: Optional[ImageStack] = None,
dark_before: Optional[ImageStack] = None,
dark_after: Optional[ImageStack] = None,
flat_before: ImageStack | None = None,
flat_after: ImageStack | None = None,
dark_before: ImageStack | None = None,
dark_after: ImageStack | None = None,
name: str = ""):
super().__init__(name=name)
self.sample = sample
Expand All @@ -127,31 +126,31 @@ def __init__(self,
self._name = sample.name

@property
def all(self) -> List[ImageStack]:
def all(self) -> list[ImageStack]:
image_stacks = [
self.sample, self.proj180deg, self.flat_before, self.flat_after, self.dark_before, self.dark_after,
self.sinograms
]
return [image_stack for image_stack in image_stacks if image_stack is not None] + self.recons.stacks

@property
def _nexus_stack_order(self) -> List[ImageStack]:
def _nexus_stack_order(self) -> list[ImageStack]:
return list(filter(None, [self.dark_before, self.flat_before, self.sample, self.flat_after, self.dark_after]))

@property
def nexus_arrays(self) -> List[np.ndarray]:
def nexus_arrays(self) -> list[np.ndarray]:
return [image_stack.data for image_stack in self._nexus_stack_order]

@property
def nexus_rotation_angles(self) -> List[np.ndarray]:
def nexus_rotation_angles(self) -> list[np.ndarray]:
proj_angles = []
for image_stack in self._nexus_stack_order:
angles = image_stack.real_projection_angles()
proj_angles.append(angles.value if angles else np.zeros(image_stack.num_images))
return proj_angles

@property
def image_keys(self) -> List[int]:
def image_keys(self) -> list[int]:
image_keys = []
if self.dark_before is not None:
image_keys += _image_key_list(2, self.dark_before.data.shape[0])
Expand Down Expand Up @@ -220,7 +219,7 @@ def is_processed(self) -> bool:
return False


def _get_stack_data_type(stack_id: uuid.UUID, dataset: Union[MixedDataset, StrictDataset]) -> str:
def _get_stack_data_type(stack_id: uuid.UUID, dataset: MixedDataset | StrictDataset) -> str:
"""
Find the data type as a string of a stack.
:param stack_id: The ID of the stack.
Expand Down
30 changes: 15 additions & 15 deletions mantidimaging/core/data/imagestack.py
Original file line number Diff line number Diff line change
Expand Up @@ -7,7 +7,7 @@
import os.path
import uuid
from copy import deepcopy
from typing import List, Optional, Any, Dict, Union, TextIO, TYPE_CHECKING, cast
from typing import Any, TextIO, TYPE_CHECKING, cast

import numpy as np

Expand All @@ -29,11 +29,11 @@ class ImageStack:

def __init__(self,
data: np.ndarray | pu.SharedArray,
filenames: Optional[List[str]] = None,
indices: List[int] | Indices | None = None,
metadata: Optional[Dict[str, Any]] = None,
filenames: list[str] | None = None,
indices: list[int] | Indices | None = None,
metadata: dict[str, Any] | None = None,
sinograms: bool = False,
name: Optional[str] = None):
name: str | None = None):
"""
:param data: a numpy array or SharedArray object containing the images of the Sample/Projection data
:param filenames: All filenames that were matched for loading
Expand All @@ -53,12 +53,12 @@ def __init__(self,

self._filenames = filenames

self.metadata: Dict[str, Any] = deepcopy(metadata) if metadata else {}
self.metadata: dict[str, Any] = deepcopy(metadata) if metadata else {}
self._is_sinograms = sinograms

self._proj180deg: Optional[ImageStack] = None
self._proj180deg: ImageStack | None = None
self._log_file: InstrumentLog | None = None
self._projection_angles: Optional[ProjectionAngles] = None
self._projection_angles: ProjectionAngles | None = None

if name is None:
if filenames is not None:
Expand Down Expand Up @@ -93,11 +93,11 @@ def count(self) -> int:
return len(self._filenames) if self._filenames else 0

@property
def filenames(self) -> Optional[List[str]]:
def filenames(self) -> list[str] | None:
return self._filenames

@filenames.setter
def filenames(self, new_ones: List[str]) -> None:
def filenames(self, new_ones: list[str]) -> None:
assert len(new_ones) == self.data.shape[0], "Number of filenames and number of images must match."
self._filenames = new_ones

Expand All @@ -112,7 +112,7 @@ def load_metadata(self, f: TextIO) -> None:
self.metadata = json.load(f) | self.metadata
self._is_sinograms = self.metadata.get(const.SINOGRAMS, False)

def save_metadata(self, f: TextIO, rescale_params: Optional[Dict[str, Union[str, float]]] = None) -> None:
def save_metadata(self, f: TextIO, rescale_params: dict[str, str | float] | None = None) -> None:
self.metadata[const.SINOGRAMS] = self.is_sinograms

if rescale_params is not None:
Expand Down Expand Up @@ -238,7 +238,7 @@ def has_proj180deg(self) -> bool:
return self._proj180deg is not None

@property
def proj180deg(self) -> Optional['ImageStack']:
def proj180deg(self) -> "ImageStack" | None:
return self._proj180deg

@proj180deg.setter
Expand Down Expand Up @@ -306,7 +306,7 @@ def set_projection_angles(self, angles: ProjectionAngles) -> None:

self._projection_angles = angles

def real_projection_angles(self) -> Optional[ProjectionAngles]:
def real_projection_angles(self) -> ProjectionAngles | None:
"""
Return only the projection angles that are from a log file or have been manually loaded.
:return: Real projection angles if they were found, None otherwise.
Expand Down Expand Up @@ -334,7 +334,7 @@ def projection_angles(self, max_angle: float = 360.0) -> ProjectionAngles:
else:
return ProjectionAngles(np.linspace(0, np.deg2rad(max_angle), self.num_projections))

def counts(self) -> Optional[Counts]:
def counts(self) -> Counts | None:
if self._log_file is not None:
return self._log_file.counts()
else:
Expand All @@ -352,7 +352,7 @@ def pixel_size(self, value: float) -> None:
def clear_proj180deg(self) -> None:
self._proj180deg = None

def make_name_unique(self, existing_names: List[str]) -> None:
def make_name_unique(self, existing_names: list[str]) -> None:
name = self.name
num = 1
while self.name in existing_names:
Expand Down
8 changes: 4 additions & 4 deletions mantidimaging/core/data/reconlist.py
Original file line number Diff line number Diff line change
Expand Up @@ -4,15 +4,15 @@

import uuid
from collections import UserList
from typing import List, Optional, TYPE_CHECKING
from typing import TYPE_CHECKING

if TYPE_CHECKING:
from mantidimaging.core.data import ImageStack


class ReconList(UserList):

def __init__(self, data: Optional[List[ImageStack]] = None):
def __init__(self, data: list[ImageStack] | None = None):
data = [] if data is None else data
super().__init__(data)
self._id: uuid.UUID = uuid.uuid4()
Expand All @@ -22,9 +22,9 @@ def id(self) -> uuid.UUID:
return self._id

@property
def ids(self) -> List[uuid.UUID]:
def ids(self) -> list[uuid.UUID]:
return [recon.id for recon in self.data]

@property
def stacks(self) -> List[ImageStack]:
def stacks(self) -> list[ImageStack]:
return self.data
4 changes: 2 additions & 2 deletions mantidimaging/core/io/csv_output.py
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@
# Copyright (C) 2024 ISIS Rutherford Appleton Laboratory UKRI
# SPDX - License - Identifier: GPL-3.0-or-later
from __future__ import annotations
from typing import IO, Optional
from typing import IO

import numpy as np

Expand All @@ -10,7 +10,7 @@ class CSVOutput:

def __init__(self) -> None:
self.columns: dict[str, np.ndarray] = {}
self.num_rows: Optional[int] = None
self.num_rows: int | None = None

def add_column(self, name: str, values: np.ndarray) -> None:
as_column = values.reshape((-1, 1))
Expand Down
Loading
Loading