118 def update(self, shape: tuple[int, int]):
119 """Update the operator with a new shape
127 msg = f
"Monotonicity is a 2D operator but received shape with {len(shape)} dimensions"
128 raise ValueError(msg)
129 if shape[0] % 2 == 0
or shape[1] % 2 == 0:
130 raise ValueError(f
"The shape must be odd, got {shape}")
133 cx = (shape[1] - 1) // 2
134 cy = (shape[0] - 1) // 2
135 x = np.arange(shape[1], dtype=self.
dtype) - cx
136 y = np.arange(shape[0], dtype=self.
dtype) - cy
137 x, y = np.meshgrid(x, y)
138 distance = np.sqrt(x**2 + y**2)
141 neighbor_dist = np.zeros((9,) + distance.shape, dtype=self.
dtype)
142 neighbor_dist[0, 1:, 1:] = distance[1:, 1:] - distance[:-1, :-1]
143 neighbor_dist[1, 1:, :] = distance[1:, :] - distance[:-1, :]
144 neighbor_dist[2, 1:, :-1] = distance[1:, :-1] - distance[:-1, 1:]
145 neighbor_dist[3, :, 1:] = distance[:, 1:] - distance[:, :-1]
149 neighbor_dist[4, cy, cx] = 1
150 neighbor_dist[5, :, :-1] = distance[:, :-1] - distance[:, 1:]
151 neighbor_dist[6, :-1, 1:] = distance[:-1, 1:] - distance[1:, :-1]
152 neighbor_dist[7, :-1, :] = distance[:-1, :] - distance[1:, :]
153 neighbor_dist[8, :-1, :-1] = distance[:-1, :-1] - distance[1:, 1:]
157 angles = np.arctan2(y, x)
158 angle_diff = np.zeros((9,) + angles.shape, dtype=self.
dtype)
159 angle_diff[0, 1:, 1:] = angles[1:, 1:] - angles[:-1, :-1]
160 angle_diff[1, 1:, :] = angles[1:, :] - angles[:-1, :]
161 angle_diff[2, 1:, :-1] = angles[1:, :-1] - angles[:-1, 1:]
162 angle_diff[3, :, 1:] = angles[:, 1:] - angles[:, :-1]
166 angle_diff[4, cy, cx] = 0
167 angle_diff[5, :, :-1] = angles[:, :-1] - angles[:, 1:]
168 angle_diff[6, :-1, 1:] = angles[:-1, 1:] - angles[1:, :-1]
169 angle_diff[7, :-1, :] = angles[:-1, :] - angles[1:, :]
170 angle_diff[8, :-1, :-1] = angles[:-1, :-1] - angles[1:, 1:]
175 weights = np.cos(angle_diff)
176 weights[neighbor_dist <= 0] = 0
178 weights[weights < 0] = -weights[weights < 0]
179 weights = weights / np.sum(weights, axis=0)[
None, :, :]
184 self.
sizes = (cy, cx, shape[0] - cy, shape[1] - cx)
214 def __call__(self, image: np.ndarray, center: tuple[int, int]) -> np.ndarray:
215 """Make an input image monotonic about a center pixel
220 The image to make monotonic.
222 The ``(y, x)`` location _in image coordinates_ to make the
223 center of the monotonic region.
228 The input image is updated in place, but also returned from this
240 bbox =
Box((9,) + image.shape, origin=(0, cy - py, cx - px))
241 weights = cast(np.ndarray, self.
weights)[bbox.slices]
242 indices = np.argsort(cast(np.ndarray, self.
distance)[bbox.slices[1:]].flatten())
243 coords = np.unravel_index(indices, image.shape)
247 result_shape = (image.shape[0] + 2, image.shape[1] + 2)
248 result = np.zeros(result_shape, dtype=image.dtype)
249 result[1:-1, 1:-1] = image
250 new_monotonicity(coords[0], coords[1], [w
for w
in weights], result)
251 image[:] = result[1:-1, 1:-1]