resize images to size using the specified interpolation method args image input image or batch of images must be 3d or 4d size size of output image in height width format interpolation interpolation method available methods are nearest bilinear and bicubic defaults to bilinear antialias whether to use an antialiasing filter when downsampling an image defaults to false dataformat string either channelslast or channelsfirst the ordering of the dimensions in the inputs channelslast corresponds to inputs with shape batch height width channels while channelsfirst corresponds to inputs with shape batch channels height weight it defaults to the imagedataformat value found in your keras config file at keraskeras json if you never set it then it will be channelslast returns resized image or batch of images examples x np random random2 4 4 3 batch of 2 rgb images y keras ops image resizex 2 2 y shape 2 2 2 3 x np random random4 4 3 single rgb image y keras ops image resizex 2 2 y shape 2 2 3 x np random random2 3 4 4 batch of 2 rgb images y keras ops image resizex 2 2 dataformatchannelsfirst y shape 2 3 2 2 applies the given transforms to the images args image input image or batch of images must be 3d or 4d transform projective transform matrixmatrices a vector of length 8 or tensor of size n x 8 if one row of transform is a0 a1 a2 b0 b1 b2 c0 c1 then it maps the output point x y to a transformed input point x y a0 x a1 y a2 k b0 x b1 y b2 k where k c0 x c1 y 1 the transform is inverted compared to the transform mapping input points to output points note that gradients are not backpropagated into transformation parameters note that c0 and c1 are only effective when using tensorflow backend and will be considered as 0 when using other backends interpolation interpolation method available methods are nearest and bilinear defaults to bilinear fillmode points outside the boundaries of the input are filled according to the given mode available methods are constant nearest wrap and reflect defaults to constant reflect d c b a a b c d d c b a the input is extended by reflecting about the edge of the last pixel constant k k k k a b c d k k k k the input is extended by filling all values beyond the edge with the same constant value k specified by fillvalue wrap a b c d a b c d a b c d the input is extended by wrapping around to the opposite edge nearest a a a a a b c d d d d d the input is extended by the nearest pixel fillvalue value used for points outside the boundaries of the input if fillmodeconstant defaults to 0 dataformat string either channelslast or channelsfirst the ordering of the dimensions in the inputs channelslast corresponds to inputs with shape batch height width channels while channelsfirst corresponds to inputs with shape batch channels height weight it defaults to the imagedataformat value found in your keras config file at keraskeras json if you never set it then it will be channelslast returns applied affine transform image or batch of images examples x np random random2 64 80 3 batch of 2 rgb images transform np array 1 5 0 20 0 1 5 16 0 0 zoom 1 0 20 0 1 16 0 0 translation y keras ops image affinetransformx transform y shape 2 64 80 3 x np random random64 80 3 single rgb image transform np array1 0 0 5 20 0 5 1 0 16 0 0 shear y keras ops image affinetransformx transform y shape 64 80 3 x np random random2 3 64 80 batch of 2 rgb images transform np array 1 5 0 20 0 1 5 16 0 0 zoom 1 0 20 0 1 16 0 0 translation y keras ops image affinetransformx transform dataformatchannelsfirst y shape 2 3 64 80 extracts patches from the images args image input image or batch of images must be 3d or 4d size patch size int or tuple patchheight patchwidht strides strides along height and width if not specified or if none it defaults to the same value as size dilationrate this is the input stride specifying how far two consecutive patch samples are in the input for value other than 1 strides must be 1 note strides 1 is not supported in conjunction with dilationrate 1 padding the type of padding algorithm to use same or valid dataformat string either channelslast or channelsfirst the ordering of the dimensions in the inputs channelslast corresponds to inputs with shape batch height width channels while channelsfirst corresponds to inputs with shape batch channels height weight it defaults to the imagedataformat value found in your keras config file at keraskeras json if you never set it then it will be channelslast returns extracted patches 3d if not batched or 4d if batched examples image np random random 2 20 20 3 astypefloat32 batch of 2 rgb images patches keras ops image extractpatchesimage 5 5 patches shape 2 4 4 75 image np random random20 20 3 astypefloat32 1 rgb image patches keras ops image extractpatchesimage 3 3 1 1 patches shape 18 18 27 map the input array to new coordinates by interpolation note that interpolation near boundaries differs from the scipy function because we fixed an outstanding bug scipyissues2640https github comscipyscipyissues2640 args input the input array coordinates the coordinates at which input is evaluated order the order of the spline interpolation the order must be 0 or 1 0 indicates the nearest neighbor and 1 indicates the linear interpolation fillmode points outside the boundaries of the input are filled according to the given mode available methods are constant nearest wrap and mirror and reflect defaults to constant constant k k k k a b c d k k k k the input is extended by filling all values beyond the edge with the same constant value k specified by fillvalue nearest a a a a a b c d d d d d the input is extended by the nearest pixel wrap a b c d a b c d a b c d the input is extended by wrapping around to the opposite edge mirror c d c b a b c d c b a b the input is extended by mirroring about the edge reflect d c b a a b c d d c b a the input is extended by reflecting about the edge of the last pixel fillvalue value used for points outside the boundaries of the input if fillmodeconstant defaults to 0 returns output image or batch of images pad images with zeros to the specified height and width args images 4d tensor of shape batch height width channels or 3d tensor of shape height width channels toppadding number of rows of zeros to add on top bottompadding number of rows of zeros to add at the bottom leftpadding number of columns of zeros to add on the left rightpadding number of columns of zeros to add on the right targetheight height of output images targetwidth width of output images returns if images were 4d a 4d float tensor of shape batch targetheight targetwidth channels if images were 3d a 3d float tensor of shape targetheight targetwidth channels example images np random random15 25 3 paddedimages keras ops image padimages images 2 3 targetheight20 targetwidth30 paddedimages shape 20 30 3 batchimages np random random2 15 25 3 paddedbatch keras ops image padimages batchimages 2 3 targetheight20 targetwidth30 paddedbatch shape 2 20 30 3 if anysymbolictensorsimages return padimages toppadding bottompadding leftpadding rightpadding targetheight targetwidth symboliccallimages return padimages images toppadding bottompadding leftpadding rightpadding targetheight targetwidth resize images to size using the specified interpolation method args image input image or batch of images must be 3d or 4d size size of output image in height width format interpolation interpolation method available methods are nearest bilinear and bicubic defaults to bilinear antialias whether to use an antialiasing filter when downsampling an image defaults to false data_format string either channels_last or channels_first the ordering of the dimensions in the inputs channels_last corresponds to inputs with shape batch height width channels while channels_first corresponds to inputs with shape batch channels height weight it defaults to the image_data_format value found in your keras config file at keras keras json if you never set it then it will be channels_last returns resized image or batch of images examples x np random random 2 4 4 3 batch of 2 rgb images y keras ops image resize x 2 2 y shape 2 2 2 3 x np random random 4 4 3 single rgb image y keras ops image resize x 2 2 y shape 2 2 3 x np random random 2 3 4 4 batch of 2 rgb images y keras ops image resize x 2 2 data_format channels_first y shape 2 3 2 2 applies the given transform s to the image s args image input image or batch of images must be 3d or 4d transform projective transform matrix matrices a vector of length 8 or tensor of size n x 8 if one row of transform is a0 a1 a2 b0 b1 b2 c0 c1 then it maps the output point x y to a transformed input point x y a0 x a1 y a2 k b0 x b1 y b2 k where k c0 x c1 y 1 the transform is inverted compared to the transform mapping input points to output points note that gradients are not backpropagated into transformation parameters note that c0 and c1 are only effective when using tensorflow backend and will be considered as 0 when using other backends interpolation interpolation method available methods are nearest and bilinear defaults to bilinear fill_mode points outside the boundaries of the input are filled according to the given mode available methods are constant nearest wrap and reflect defaults to constant reflect d c b a a b c d d c b a the input is extended by reflecting about the edge of the last pixel constant k k k k a b c d k k k k the input is extended by filling all values beyond the edge with the same constant value k specified by fill_value wrap a b c d a b c d a b c d the input is extended by wrapping around to the opposite edge nearest a a a a a b c d d d d d the input is extended by the nearest pixel fill_value value used for points outside the boundaries of the input if fill_mode constant defaults to 0 data_format string either channels_last or channels_first the ordering of the dimensions in the inputs channels_last corresponds to inputs with shape batch height width channels while channels_first corresponds to inputs with shape batch channels height weight it defaults to the image_data_format value found in your keras config file at keras keras json if you never set it then it will be channels_last returns applied affine transform image or batch of images examples x np random random 2 64 80 3 batch of 2 rgb images transform np array 1 5 0 20 0 1 5 16 0 0 zoom 1 0 20 0 1 16 0 0 translation y keras ops image affine_transform x transform y shape 2 64 80 3 x np random random 64 80 3 single rgb image transform np array 1 0 0 5 20 0 5 1 0 16 0 0 shear y keras ops image affine_transform x transform y shape 64 80 3 x np random random 2 3 64 80 batch of 2 rgb images transform np array 1 5 0 20 0 1 5 16 0 0 zoom 1 0 20 0 1 16 0 0 translation y keras ops image affine_transform x transform data_format channels_first y shape 2 3 64 80 extracts patches from the image s args image input image or batch of images must be 3d or 4d size patch size int or tuple patch_height patch_widht strides strides along height and width if not specified or if none it defaults to the same value as size dilation_rate this is the input stride specifying how far two consecutive patch samples are in the input for value other than 1 strides must be 1 note strides 1 is not supported in conjunction with dilation_rate 1 padding the type of padding algorithm to use same or valid data_format string either channels_last or channels_first the ordering of the dimensions in the inputs channels_last corresponds to inputs with shape batch height width channels while channels_first corresponds to inputs with shape batch channels height weight it defaults to the image_data_format value found in your keras config file at keras keras json if you never set it then it will be channels_last returns extracted patches 3d if not batched or 4d if batched examples image np random random 2 20 20 3 astype float32 batch of 2 rgb images patches keras ops image extract_patches image 5 5 patches shape 2 4 4 75 image np random random 20 20 3 astype float32 1 rgb image patches keras ops image extract_patches image 3 3 1 1 patches shape 18 18 27 map the input array to new coordinates by interpolation note that interpolation near boundaries differs from the scipy function because we fixed an outstanding bug scipy issues 2640 https github com scipy scipy issues 2640 args input the input array coordinates the coordinates at which input is evaluated order the order of the spline interpolation the order must be 0 or 1 0 indicates the nearest neighbor and 1 indicates the linear interpolation fill_mode points outside the boundaries of the input are filled according to the given mode available methods are constant nearest wrap and mirror and reflect defaults to constant constant k k k k a b c d k k k k the input is extended by filling all values beyond the edge with the same constant value k specified by fill_value nearest a a a a a b c d d d d d the input is extended by the nearest pixel wrap a b c d a b c d a b c d the input is extended by wrapping around to the opposite edge mirror c d c b a b c d c b a b the input is extended by mirroring about the edge reflect d c b a a b c d d c b a the input is extended by reflecting about the edge of the last pixel fill_value value used for points outside the boundaries of the input if fill_mode constant defaults to 0 returns output image or batch of images pad images with zeros to the specified height and width args images 4d tensor of shape batch height width channels or 3d tensor of shape height width channels top_padding number of rows of zeros to add on top bottom_padding number of rows of zeros to add at the bottom left_padding number of columns of zeros to add on the left right_padding number of columns of zeros to add on the right target_height height of output images target_width width of output images returns if images were 4d a 4d float tensor of shape batch target_height target_width channels if images were 3d a 3d float tensor of shape target_height target_width channels example images np random random 15 25 3 padded_images keras ops image pad_images images 2 3 target_height 20 target_width 30 padded_images shape 20 30 3 batch_images np random random 2 15 25 3 padded_batch keras ops image pad_images batch_images 2 3 target_height 20 target_width 30 padded_batch shape 2 20 30 3
from keras import backend
from keras import ops
from keras.api_export import keras_export
from keras.backend import KerasTensor
from keras.backend import any_symbolic_tensors
from keras.ops.operation import Operation
from keras.ops.operation_utils import compute_conv_output_shape


class Resize(Operation):
    def __init__(
        self,
        size,
        interpolation="bilinear",
        antialias=False,
        data_format="channels_last",
    ):
        super().__init__()
        self.size = tuple(size)
        self.interpolation = interpolation
        self.antialias = antialias
        self.data_format = data_format

    def call(self, image):
        return backend.image.resize(
            image,
            self.size,
            interpolation=self.interpolation,
            antialias=self.antialias,
            data_format=self.data_format,
        )

    def compute_output_spec(self, image):
        if len(image.shape) == 3:
            return KerasTensor(
                self.size + (image.shape[-1],), dtype=image.dtype
            )
        elif len(image.shape) == 4:
            if self.data_format == "channels_last":
                return KerasTensor(
                    (image.shape[0],) + self.size + (image.shape[-1],),
                    dtype=image.dtype,
                )
            else:
                return KerasTensor(
                    (image.shape[0], image.shape[1]) + self.size,
                    dtype=image.dtype,
                )
        raise ValueError(
            "Invalid input rank: expected rank 3 (single image) "
            "or rank 4 (batch of images). Received input with shape: "
            f"image.shape={image.shape}"
        )


@keras_export("keras.ops.image.resize")
def resize(
    image,
    size,
    interpolation="bilinear",
    antialias=False,
    data_format="channels_last",
):
    

    if any_symbolic_tensors((image,)):
        return Resize(
            size,
            interpolation=interpolation,
            antialias=antialias,
            data_format=data_format,
        ).symbolic_call(image)
    return backend.image.resize(
        image,
        size,
        interpolation=interpolation,
        antialias=antialias,
        data_format=data_format,
    )


class AffineTransform(Operation):
    def __init__(
        self,
        interpolation="bilinear",
        fill_mode="constant",
        fill_value=0,
        data_format="channels_last",
    ):
        super().__init__()
        self.interpolation = interpolation
        self.fill_mode = fill_mode
        self.fill_value = fill_value
        self.data_format = data_format

    def call(self, image, transform):
        return backend.image.affine_transform(
            image,
            transform,
            interpolation=self.interpolation,
            fill_mode=self.fill_mode,
            fill_value=self.fill_value,
            data_format=self.data_format,
        )

    def compute_output_spec(self, image, transform):
        if len(image.shape) not in (3, 4):
            raise ValueError(
                "Invalid image rank: expected rank 3 (single image) "
                "or rank 4 (batch of images). Received input with shape: "
                f"image.shape={image.shape}"
            )
        if len(transform.shape) not in (1, 2):
            raise ValueError(
                "Invalid transform rank: expected rank 1 (single transform) "
                "or rank 2 (batch of transforms). Received input with shape: "
                f"transform.shape={transform.shape}"
            )
        return KerasTensor(image.shape, dtype=image.dtype)


@keras_export("keras.ops.image.affine_transform")
def affine_transform(
    image,
    transform,
    interpolation="bilinear",
    fill_mode="constant",
    fill_value=0,
    data_format="channels_last",
):
    
    if any_symbolic_tensors((image, transform)):
        return AffineTransform(
            interpolation=interpolation,
            fill_mode=fill_mode,
            fill_value=fill_value,
            data_format=data_format,
        ).symbolic_call(image, transform)
    return backend.image.affine_transform(
        image,
        transform,
        interpolation=interpolation,
        fill_mode=fill_mode,
        fill_value=fill_value,
        data_format=data_format,
    )


class ExtractPatches(Operation):
    def __init__(
        self,
        size,
        strides=None,
        dilation_rate=1,
        padding="valid",
        data_format="channels_last",
    ):
        super().__init__()
        if isinstance(size, int):
            size = (size, size)
        self.size = size
        self.strides = strides
        self.dilation_rate = dilation_rate
        self.padding = padding
        self.data_format = data_format

    def call(self, image):
        return _extract_patches(
            image=image,
            size=self.size,
            strides=self.strides,
            dilation_rate=self.dilation_rate,
            padding=self.padding,
            data_format=self.data_format,
        )

    def compute_output_spec(self, image):
        image_shape = image.shape
        if not self.strides:
            strides = (self.size[0], self.size[1])
        if self.data_format == "channels_last":
            channels_in = image.shape[-1]
        else:
            channels_in = image.shape[-3]
        if len(image.shape) == 3:
            image_shape = (1,) + image_shape
        filters = self.size[0] * self.size[1] * channels_in
        kernel_size = (self.size[0], self.size[1])
        out_shape = compute_conv_output_shape(
            image_shape,
            filters,
            kernel_size,
            strides=strides,
            padding=self.padding,
            data_format=self.data_format,
            dilation_rate=self.dilation_rate,
        )
        if len(image.shape) == 3:
            out_shape = out_shape[1:]
        return KerasTensor(shape=out_shape, dtype=image.dtype)


@keras_export("keras.ops.image.extract_patches")
def extract_patches(
    image,
    size,
    strides=None,
    dilation_rate=1,
    padding="valid",
    data_format="channels_last",
):
    
    if any_symbolic_tensors((image,)):
        return ExtractPatches(
            size=size,
            strides=strides,
            dilation_rate=dilation_rate,
            padding=padding,
            data_format=data_format,
        ).symbolic_call(image)

    return _extract_patches(
        image, size, strides, dilation_rate, padding, data_format=data_format
    )


def _extract_patches(
    image,
    size,
    strides=None,
    dilation_rate=1,
    padding="valid",
    data_format="channels_last",
):
    if isinstance(size, int):
        patch_h = patch_w = size
    elif len(size) == 2:
        patch_h, patch_w = size[0], size[1]
    else:
        raise TypeError(
            "Invalid `size` argument. Expected an "
            f"int or a tuple of length 2. Received: size={size}"
        )
    if data_format == "channels_last":
        channels_in = image.shape[-1]
    elif data_format == "channels_first":
        channels_in = image.shape[-3]
    if not strides:
        strides = size
    out_dim = patch_h * patch_w * channels_in
    kernel = backend.numpy.eye(out_dim)
    kernel = backend.numpy.reshape(
        kernel, (patch_h, patch_w, channels_in, out_dim)
    )
    _unbatched = False
    if len(image.shape) == 3:
        _unbatched = True
        image = backend.numpy.expand_dims(image, axis=0)
    patches = backend.nn.conv(
        inputs=image,
        kernel=kernel,
        strides=strides,
        padding=padding,
        data_format=data_format,
        dilation_rate=dilation_rate,
    )
    if _unbatched:
        patches = backend.numpy.squeeze(patches, axis=0)
    return patches


class MapCoordinates(Operation):
    def __init__(self, order, fill_mode="constant", fill_value=0):
        super().__init__()
        self.order = order
        self.fill_mode = fill_mode
        self.fill_value = fill_value

    def call(self, image, coordinates):
        return backend.image.map_coordinates(
            image,
            coordinates,
            order=self.order,
            fill_mode=self.fill_mode,
            fill_value=self.fill_value,
        )

    def compute_output_spec(self, image, coordinates):
        if coordinates.shape[0] != len(image.shape):
            raise ValueError(
                "First dim of `coordinates` must be the same as the rank of "
                "`image`. "
                f"Received image with shape: {image.shape} and coordinate "
                f"leading dim of {coordinates.shape[0]}"
            )
        if len(coordinates.shape) < 2:
            raise ValueError(
                "Invalid coordinates rank: expected at least rank 2."
                f" Received input with shape: {coordinates.shape}"
            )
        return KerasTensor(coordinates.shape[1:], dtype=image.dtype)


@keras_export("keras.ops.image.map_coordinates")
def map_coordinates(
    input, coordinates, order, fill_mode="constant", fill_value=0
):
    
    if any_symbolic_tensors((input, coordinates)):
        return MapCoordinates(
            order,
            fill_mode,
            fill_value,
        ).symbolic_call(input, coordinates)
    return backend.image.map_coordinates(
        input,
        coordinates,
        order,
        fill_mode,
        fill_value,
    )


class PadImages(Operation):
    def __init__(
        self,
        top_padding,
        bottom_padding,
        left_padding,
        right_padding,
        target_height,
        target_width,
    ):
        super().__init__()
        self.top_padding = top_padding
        self.bottom_padding = bottom_padding
        self.left_padding = left_padding
        self.right_padding = right_padding
        self.target_height = target_height
        self.target_width = target_width

    def call(self, images):
        return _pad_images(
            images,
            self.top_padding,
            self.bottom_padding,
            self.left_padding,
            self.right_padding,
            self.target_height,
            self.target_width,
        )

    def compute_output_spec(self, images):
        images_shape = ops.shape(images)
        if self.target_height is None:
            height_axis = 0 if len(images_shape) == 3 else 1
            self.target_height = (
                self.top_padding
                + images_shape[height_axis]
                + self.bottom_padding
            )
        if self.target_width is None:
            width_axis = 0 if len(images_shape) == 3 else 2
            self.target_width = (
                self.left_padding
                + images_shape[width_axis]
                + self.right_padding
            )
        out_shape = (
            images_shape[0],
            self.target_height,
            self.target_width,
            images_shape[-1],
        )
        if len(images_shape) == 3:
            out_shape = out_shape[1:]
        return KerasTensor(
            shape=out_shape,
            dtype=images.dtype,
        )


def _pad_images(
    images,
    top_padding,
    bottom_padding,
    left_padding,
    right_padding,
    target_height,
    target_width,
):
    images = backend.convert_to_tensor(images)
    is_batch = True
    images_shape = ops.shape(images)
    if len(images_shape) == 3:
        is_batch = False
        images = backend.numpy.expand_dims(images, 0)
    elif len(images_shape) != 4:
        raise ValueError(
            f"Invalid shape for argument `images`: "
            "it must have rank 3 or 4. "
            f"Received: images.shape={images_shape}"
        )

    batch, height, width, depth = ops.shape(images)

    if [top_padding, bottom_padding, target_height].count(None) != 1:
        raise ValueError(
            "Must specify exactly two of "
            "top_padding, bottom_padding, target_height. "
            f"Received: top_padding={top_padding}, "
            f"bottom_padding={bottom_padding}, "
            f"target_height={target_height}"
        )
    if [left_padding, right_padding, target_width].count(None) != 1:
        raise ValueError(
            "Must specify exactly two of "
            "left_padding, right_padding, target_width. "
            f"Received: left_padding={left_padding}, "
            f"right_padding={right_padding}, "
            f"target_width={target_width}"
        )

    if top_padding is None:
        top_padding = target_height - bottom_padding - height
    if bottom_padding is None:
        bottom_padding = target_height - top_padding - height
    if left_padding is None:
        left_padding = target_width - right_padding - width
    if right_padding is None:
        right_padding = target_width - left_padding - width

    if top_padding < 0:
        raise ValueError(
            "top_padding must be >= 0. " f"Received: top_padding={top_padding}"
        )
    if left_padding < 0:
        raise ValueError(
            "left_padding must be >= 0. "
            f"Received: left_padding={left_padding}"
        )
    if right_padding < 0:
        raise ValueError(
            "right_padding must be >= 0. "
            f"Received: right_padding={right_padding}"
        )
    if bottom_padding < 0:
        raise ValueError(
            "bottom_padding must be >= 0. "
            f"Received: bottom_padding={bottom_padding}"
        )

    paddings = backend.numpy.reshape(
        backend.numpy.stack(
            [
                0,
                0,
                top_padding,
                bottom_padding,
                left_padding,
                right_padding,
                0,
                0,
            ]
        ),
        [4, 2],
    )
    padded = backend.numpy.pad(images, paddings)

    if target_height is None:
        target_height = top_padding + height + bottom_padding
    if target_width is None:
        target_width = left_padding + width + right_padding
    padded_shape = [batch, target_height, target_width, depth]
    padded = backend.numpy.reshape(padded, padded_shape)

    if not is_batch:
        padded = backend.numpy.squeeze(padded, axis=[0])
    return padded


@keras_export("keras.ops.image.pad_images")
def pad_images(
    images,
    top_padding=None,
    left_padding=None,
    target_height=None,
    target_width=None,
    bottom_padding=None,
    right_padding=None,
):
    

    if any_symbolic_tensors((images,)):
        return PadImages(
            top_padding,
            bottom_padding,
            left_padding,
            right_padding,
            target_height,
            target_width,
        ).symbolic_call(images)

    return _pad_images(
        images,
        top_padding,
        bottom_padding,
        left_padding,
        right_padding,
        target_height,
        target_width,
    )