예제 #1
0
    def test_crop_metadata(self, small_cont_1c: ImageContainer, dy: int):
        crop = small_cont_1c.crop_corner(dy, 0, 50, mask_circle=True)

        assert small_cont_1c.data.attrs[Key.img.coords] is _NULL_COORDS
        assert crop.data.attrs[Key.img.coords] == CropCoords(0, 0, 50, 50 + dy)
        assert crop.data.attrs[Key.img.padding] == CropPadding(x_pre=0,
                                                               y_pre=abs(dy),
                                                               x_post=0,
                                                               y_post=0)
        assert crop.data.attrs[Key.img.mask_circle]
예제 #2
0
        def transform_metadata(data: xr.Dataset) -> xr.Dataset:
            data.attrs[Key.img.coords] = CropCoords.from_tuple(
                data.attrs.get(Key.img.coords, _NULL_COORDS.to_tuple()))
            data.attrs[Key.img.padding] = CropPadding.from_tuple(
                data.attrs.get(Key.img.padding, _NULL_PADDING.to_tuple()))
            if Key.img.mask_circle not in data.attrs:
                data.attrs[Key.img.mask_circle] = False

            if Key.img.scale not in data.attrs:
                data.attrs[Key.img.scale] = 1

            return data
예제 #3
0
        def convert_to_full_image_coordinates(x: np.ndarray,
                                              y: np.ndarray) -> np.ndarray:
            if not len(y):
                return np.array([[]], dtype=np.float64)

            if self.data.attrs.get("mask_circle", False):
                if self.data.dims["y"] != self.data.dims["x"]:
                    raise ValueError(
                        f"Crop is not a square: `{self.data.dims}`.")
                c = self.data.dims["x"] // 2  # center
                mask = (x - c)**2 + (y - c)**2 <= c**2
                y = y[mask]
                x = x[mask]

            if not len(y):
                return np.array(
                    [[]],
                    dtype=np.float64)  # because of masking, should not happen

            coord = self.data.attrs.get(
                Key.img.coords,
                CropCoords(x0=0,
                           y0=0,
                           x1=self.data.dims["x"],
                           y1=self.data.dims["y"]
                           ))  # fall back to default (i.e no crop) coordinates
            padding = self.data.attrs.get(
                Key.img.padding, _NULL_PADDING)  # fallback to no padding
            y_slc, x_slc = coord.to_image_coordinates(padding).slice

            # relative coordinates
            y = (y - np.min(y)) / (np.max(y) - np.min(y))
            x = (x - np.min(x)) / (np.max(x) - np.min(x))

            # coordinates in the uncropped image
            y = coord.slice[0].start + (y_slc.stop - y_slc.start) * y
            x = coord.slice[1].start + (x_slc.stop - x_slc.start) * x

            return np.c_[x, y]  # type: ignore[no-any-return]
예제 #4
0
    def crop_corner(
        self,
        y: FoI_t,
        x: FoI_t,
        size: Optional[Union[FoI_t, Tuple[FoI_t, FoI_t]]] = None,
        scale: float = 1.0,
        cval: Union[int, float] = 0,
        mask_circle: bool = False,
        preserve_dtypes: bool = True,
    ) -> "ImageContainer":
        """
        Extract a crop from the upper-left corner.

        Parameters
        ----------
        %(yx)s
        %(size)s
        scale
            Rescale the crop using :func:`skimage.transform.rescale`.
        cval
            Fill value to use if ``mask_circle = True`` or if crop goes out of the image boundary.
        mask_circle
            Whether to mask out values that are not within a circle defined by this crop.
            Only available if ``size`` defines a square.
        preserve_dtypes
            Whether to preserver the data types of underlying :class:`xarray.DataArray`, even if ``cval``
            is of different type.

        Returns
        -------
        The cropped image of size ``size * scale``.

        Raises
        ------
        ValueError
            If the crop would completely lie outside of the image or if ``mask_circle = True`` and
            ``size`` does not define a square.

        Notes
        -----
        If ``preserve_dtypes = True`` but ``cval`` cannot be safely cast, ``cval`` will be set to 0.
        """
        self._assert_not_empty()
        y, x = self._convert_to_pixel_space((y, x))

        size = self._get_size(size)
        size = self._convert_to_pixel_space(size)

        ys, xs = size
        _assert_positive(ys, name="height")
        _assert_positive(xs, name="width")
        _assert_positive(scale, name="scale")

        orig = CropCoords(x0=x, y0=y, x1=x + xs, y1=y + ys)

        ymin, xmin = self.shape
        coords = CropCoords(x0=min(max(x, 0), xmin),
                            y0=min(max(y, 0), ymin),
                            x1=min(x + xs, xmin),
                            y1=min(y + ys, ymin))

        if not coords.dy:
            raise ValueError("Height of the crop is empty.")
        if not coords.dx:
            raise ValueError("Width of the crop is empty.")

        crop = self.data.isel(x=slice(coords.x0, coords.x1),
                              y=slice(coords.y0, coords.y1)).copy(deep=False)
        crop.attrs[Key.img.coords] = coords

        if orig != coords:
            padding = orig - coords

            # because padding does not change dtype by itself
            for key, arr in crop.items():
                if preserve_dtypes:
                    if not np.can_cast(cval, arr.dtype, casting="safe"):
                        cval = 0
                else:
                    crop[key] = crop[key].astype(np.dtype(type(cval)),
                                                 copy=False)

            crop = crop.pad(
                y=(padding.y_pre, padding.y_post),
                x=(padding.x_pre, padding.x_post),
                mode="constant",
                constant_values=cval,
            )
            crop.attrs["padding"] = padding
        else:
            crop.attrs["padding"] = _NULL_PADDING

        return self._from_dataset(
            self._post_process(data=crop,
                               scale=scale,
                               cval=cval,
                               mask_circle=mask_circle,
                               preserve_dtypes=preserve_dtypes))