excluder.py 4.6 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120
  1. import numpy as np
  2. import typing
  3. class Excluder2D(object):
  4. def __init__(self, cutborder_x=0, cutborder_y=0):
  5. self.cutborder_x = cutborder_x
  6. self.cutborder_y = cutborder_y
  7. def check_if_cutborder_too_large(self, frame_size):
  8. if self.cutborder_x >= 0.5 * frame_size[0]:
  9. raise ValueError(
  10. f"Specified value of cutborder_x={self.cutborder_x} is too large of data of frame size {frame_size}")
  11. if self.cutborder_y >= 0.5 * frame_size[1]:
  12. raise ValueError(
  13. f"Specified value of cutborder_y={self.cutborder_y} is too large of data of frame size {frame_size}")
  14. def exclude_from_frame(self, frame_data: np.ndarray):
  15. """
  16. Returns an XY numpy array with data along X and Y excluded
  17. :param frame_data: numpy.ndarray, in XY format
  18. :return: numpy.ndarray
  19. """
  20. self.check_if_cutborder_too_large(frame_data.shape)
  21. return frame_data[
  22. self.cutborder_x: frame_data.shape[0] - self.cutborder_x,
  23. self.cutborder_y: frame_data.shape[1] - self.cutborder_y
  24. ]
  25. def revise_frame_size(self, frame_size):
  26. """
  27. Revises frame size by excluding pixels along X and Y
  28. :param frame_size: tuple of size 2
  29. :return: tuple of size 2
  30. """
  31. self.check_if_cutborder_too_large(frame_size)
  32. revised_frame = frame_size[0] - 2 * self.cutborder_x, frame_size[1] - 2 * self.cutborder_y
  33. return revised_frame
  34. def get_exclusion_mask_2D(self, frame_size):
  35. """
  36. returns a boolean 2D array with True for pixels that are to be retained and False for those that are to be cut
  37. :param frame_size: tuple of size 2
  38. :return: boolean numpy ndarray of shape `frame_size`
  39. """
  40. mask = np.ones(frame_size, dtype=bool)
  41. mask[
  42. self.cutborder_x: frame_size[0] - self.cutborder_x,
  43. self.cutborder_y: frame_size[1] - self.cutborder_y
  44. ] = False
  45. return mask
  46. class Excluder3D(Excluder2D):
  47. def __init__(self, mv_FirstFrame=0, mv_LastFrame=0, cutborder_x=0, cutborder_y=0):
  48. super().__init__(cutborder_x=cutborder_x, cutborder_y=cutborder_y)
  49. # invalid first frame defaults to first frame
  50. self.t_slice_start = mv_FirstFrame if mv_FirstFrame >= 0 else 0
  51. # invalid last frame defaults to None, which will later be interpreted as the last frame of the movie
  52. self.t_slice_end = mv_LastFrame + 1 if mv_LastFrame > 0 else None
  53. def check_revise_framecut(self, movie_size):
  54. # default to end of movie if slice end is None
  55. t_slice_end = movie_size[2] if self.t_slice_end is None else self.t_slice_end
  56. t_slice_start = np.clip(self.t_slice_start, 0, movie_size[2])
  57. t_slice_end = np.clip(t_slice_end, 0, movie_size[2])
  58. if t_slice_start >= t_slice_end:
  59. raise ValueError(f"Specified values for mv_FirstFrame and mv_LastFrame are not valid for movie_data of size"
  60. f"{movie_size}. Please check!")
  61. return t_slice_start, t_slice_end
  62. def exclude_from_movie(self, movie_data: np.ndarray):
  63. """
  64. Returns an XYT numpy array with data along X, Y and T axes excluded
  65. :param movie_data: numpy.ndarray, in XYT format
  66. :return: numpy.ndarray
  67. """
  68. self.check_if_cutborder_too_large(movie_data.shape[:2])
  69. t_slice_start, t_slice_end = self.check_revise_framecut(movie_data.shape)
  70. return movie_data[self.cutborder_x: movie_data.shape[0] - self.cutborder_x,
  71. self.cutborder_y: movie_data.shape[1] - self.cutborder_y,
  72. t_slice_start: t_slice_end]
  73. def revise_movie_size(self, movie_size):
  74. """
  75. Revises frame size by excluding pixels along X, Y and T
  76. :param movie_size: tuple of size 3
  77. :return: tuple of size 3
  78. """
  79. revised_frame_size = self.revise_frame_size(movie_size[:2])
  80. revised_frame_start_end = self.revised_frame_start_end(movie_size)
  81. revised_frame_count = revised_frame_start_end[1] - revised_frame_start_end[0] + 1
  82. return revised_frame_size[0], revised_frame_size[1], revised_frame_count
  83. def revised_frame_start_end(self, movie_size):
  84. """
  85. Returns the first frame and last frame retained after exclusion as a tuple
  86. :param movie_size: tuple of size 3
  87. :return: tuple of size 2
  88. """
  89. slice_start, slice_end = self.check_revise_framecut(movie_size)
  90. return slice_start + 1, slice_end