keras/src/backend/numpy/image.py/rgb_to_grayscale
def rgb_to_grayscale(images, data_format=None):
    images = convert_to_tensor(images)
    data_format = backend.standardize_data_format(data_format)
    channels_axis = -1 if data_format == "channels_last" else -3
    if len(images.shape) not in (3, 4):
        raise ValueError(
            "Invalid images rank: expected rank 3 (single image) "
            "or rank 4 (batch of images). Received input with shape: "
            f"images.shape={images.shape}"
        )
    # Convert to floats
    original_dtype = images.dtype
    compute_dtype = backend.result_type(images.dtype, float)
    images = images.astype(compute_dtype)

    # Ref: tf.image.rgb_to_grayscale
    rgb_weights = np.array([0.2989, 0.5870, 0.1140], dtype=images.dtype)
    grayscales = np.tensordot(images, rgb_weights, axes=(channels_axis, -1))
    grayscales = np.expand_dims(grayscales, axis=channels_axis)
    return grayscales.astype(original_dtype)