Skip to content

Commit

Permalink
Better support for 2D images
Browse files Browse the repository at this point in the history
- Z resolution is no longer required
- Fixed loading 2D images from LIF files
  • Loading branch information
rutgerkok committed Sep 22, 2023
1 parent 114ae86 commit fe2e991
Show file tree
Hide file tree
Showing 5 changed files with 33 additions and 11 deletions.
11 changes: 9 additions & 2 deletions organoid_tracker/core/images.py
Original file line number Diff line number Diff line change
Expand Up @@ -285,7 +285,12 @@ def resolution(self, allow_incomplete: bool = False) -> ImageResolution:
the data has multiple time points, but no time resolution has been set.
"""
require_time_resolution = self.first_time_point_number() != self.last_time_point_number()
if not allow_incomplete and self._resolution.is_incomplete(require_time_resolution=require_time_resolution):
require_z = False
image_size = self._image_loader.get_image_size_zyx()
if image_size is not None and image_size[0] > 1:
require_z = True # We have 3D images, require a Z resolution
if not allow_incomplete and self._resolution.is_incomplete(require_time_resolution=require_time_resolution,
require_z=require_z):
raise UserError("No image resolution set", "No image resolution was set. Please set a resolution first."
" This can be done in the Edit menu of the program.")
return self._resolution
Expand Down Expand Up @@ -328,7 +333,9 @@ def set_timings(self, timings: Optional[ImageTimings]):
self._timings = timings

# Also keep time resolution in sync
self._resolution.time_point_interval_m = timings.get_time_m_since_previous(TimePoint(1))
self._resolution = ImageResolution(self._resolution.pixel_size_x_um, self._resolution.pixel_size_y_um,
self._resolution.pixel_size_z_um,
timings.get_time_m_since_previous(TimePoint(1)))

def is_inside_image(self, position: Position, *, margin_xy: int = 0, margin_z: int = 0) -> Optional[bool]:
"""Checks if the given position is inside the images. If there are no images loaded, this returns None. Any
Expand Down
7 changes: 5 additions & 2 deletions organoid_tracker/core/resolution.py
Original file line number Diff line number Diff line change
Expand Up @@ -44,12 +44,15 @@ def __repr__(self) -> str:
return f"ImageResolution({self.pixel_size_x_um}, {self.pixel_size_y_um}, {self.pixel_size_z_um}," \
f" {self.time_point_interval_m})"

def is_incomplete(self, *, require_time_resolution: bool = True) -> bool:
def is_incomplete(self, *, require_time_resolution: bool = True, require_z: bool = True) -> bool:
"""Returns True if the x, y, z and/or t resolution is zero. Otherwise it returns False."""
if require_time_resolution:
if self.time_point_interval_m == 0 or self.time_point_interval_m == float("inf"):
return True
return 0 in self.pixel_size_zyx_um
if require_z:
if self.pixel_size_z_um == 0:
return True
return self.pixel_size_x_um == 0 or self.pixel_size_y_um == 0


# See typehint at beginning of ImageResolution class
Expand Down
14 changes: 7 additions & 7 deletions organoid_tracker/image_loading/imsfile_image_loader.py
Original file line number Diff line number Diff line change
Expand Up @@ -732,15 +732,15 @@ class _ImsImageLoader(ImageLoader):
def __init__(self, file_name: str, min_time_point: Optional[int], max_time_point: Optional[int]):
self._reader = _ImsReader(file_name)
self._min_time_point = 0 if min_time_point is None else min_time_point
self._max_time_point = self._reader.TimePoints if max_time_point is None else max_time_point
self._max_time_point = self._reader.TimePoints - 1 if max_time_point is None else max_time_point

def get_3d_image_array(self, time_point: TimePoint, image_channel: ImageChannel) -> Optional[ndarray]:
time_point_number = time_point.time_point_number()
if time_point_number < self.first_time_point_number() or time_point_number > self.last_time_point_number():
return None
if image_channel.index_zero < 0 or image_channel.index_zero >= self._reader.Channels:
return None
array = self._reader[time_point_number - 1, image_channel.index_zero]
array = self._reader[time_point_number, image_channel.index_zero]
if not numpy.any(array):
return None # Got an all-zero array, ignore
return array
Expand All @@ -753,7 +753,7 @@ def get_2d_image_array(self, time_point: TimePoint, image_channel: ImageChannel,
return None
if image_z < 0 or image_z >= self._reader.shape[2]:
return None
array = self._reader[time_point_number - 1, image_channel.index_zero, image_z]
array = self._reader[time_point_number, image_channel.index_zero, image_z]
if not numpy.any(array):
return None # Got an all-zero array, ignore
return array
Expand All @@ -762,10 +762,10 @@ def get_image_size_zyx(self) -> Optional[Tuple[int, int, int]]:
return self._reader.shape[-3], self._reader.shape[-2], self._reader.shape[-1]

def first_time_point_number(self) -> int:
return max(self._min_time_point, 1)
return max(self._min_time_point, 0)

def last_time_point_number(self) -> int:
return min(self._max_time_point, self._reader.TimePoints)
return min(self._max_time_point, self._reader.TimePoints - 1)

def get_channel_count(self) -> int:
return self._reader.Channels
Expand Down Expand Up @@ -793,7 +793,7 @@ def get_time_resolution_m(self) -> float:
def get_timings(self) -> Optional[ImageTimings]:
if "DataSetTimes" in self._reader.hf:
timespans_s = [float(moment[2] / 1_000_000_000) for moment in self._reader.hf["DataSetTimes"]["Time"]]
timespans_s = [0.0] + timespans_s
timespans_s = numpy.array(timespans_s, dtype=numpy.float64)
return ImageTimings(1, timespans_s / 60)
timespans_s -= timespans_s[0]
return ImageTimings(0, timespans_s / 60)
return None
2 changes: 2 additions & 0 deletions organoid_tracker/image_loading/liffile_image_loader.py
Original file line number Diff line number Diff line change
Expand Up @@ -139,6 +139,8 @@ def get_3d_image_array(self, time_point: TimePoint, image_channel: ImageChannel)
return None

array = self._serie.getFrame(channel=image_channel.index_zero, T=time_point.time_point_number())
if len(array.shape) == 2: # Convert to 3D
array = array[numpy.newaxis, :, :]
if array.dtype != numpy.uint8: # Saves memory
array = bits.image_to_8bit(array)
if self._inverted_z:
Expand Down
10 changes: 10 additions & 0 deletions organoid_tracker/imaging/list_io.py
Original file line number Diff line number Diff line change
Expand Up @@ -67,3 +67,13 @@ def _contains_images(experiment_json: Dict[str, Any]) -> bool:
if key.startswith("images_"):
return True
return False


def count_experiments_in_list_file(open_files_list_file: str) -> int:
"""Counts the number of experiments in the given file. Used as a quick way to get that number, instead of loading
everything."""
with open(open_files_list_file, "r", encoding="utf-8") as handle:
experiments_json = json.load(handle)
if not isinstance(experiments_json, list):
raise TypeError("Expected a list in " + open_files_list_file + ", got " + repr(experiments_json))
return len(experiments_json)

0 comments on commit fe2e991

Please sign in to comment.