pytorch(Pillow)与tensorflow图片resize行为的差异性

发布于 2023-05-08  751 次阅读


前言

打算把clip模型转成tensorflow,并且将输入修改成web友好地的base64(模型内置预处理层)以便tf-serving上部署供外部api调用。

问题描述

clip代码库中图片预处理是使用pytorch的transforms库完成的,实际上这个库属于pillow的包装库,最后仍然是使用pillow完成的图像处理。

from torchvision.transforms import Compose, Resize, CenterCrop, ToTensor, Normalize

def _transform(n_px):
    return Compose([
        Resize(n_px, interpolation=BICUBIC),
        CenterCrop(n_px),
        _convert_image_to_rgb,
        ToTensor(),
        Normalize((0.48145466, 0.4578275, 0.40821073), (0.26862954, 0.26130258, 0.27577711)),
    ])

整个处理流程是以较小边为基准,将较小边缩放到npx,且保持整体比例不变,缩放的插值算法为bicubic,再进行居中裁剪,再将图片转为rgb模型,最后除255转为tensor后再使用指定均值和方差进行了标准化。

对应的缩放代码,将其改为tensorflow代码,直觉上应该是使用

tf.image.resize()

但是这个resize方法默认会进行压缩,如果选择保持比例,则会将更大的边和你预定值对齐。

   
image-20230508181848765 tf.image.resize(imgs_map, [224, 224],method='bicubic',antialias=True,preserve_aspect_ratio=False)
image-20230508182039178 tf.image.resize(imgs_map, [224, 224],method='bicubic',antialias=True,preserve_aspect_ratio=True)

注意到tf的图像预处理层中有

tf.keras.layers.Resizing

当其crop_to_aspect_ratio=True时,可以做到一样的缩放行为,但是这个layer的初始化参数并没有抗锯齿的选项,pillow缩放图片时默认应用了抗锯齿。

   
image-20230508181642205 transforms
image-20230508181706945 tf.keras.layers.Resizing( 224, 224,interpolation='bicubic',crop_to_aspect_ratio=True)

之后观察tf.keras.layers.Resizing的源码,得知实际上这个智能缩放行为是通过先裁剪后缩放(调用tf.image.resize)实现的,因为拷贝了下源码,修改了调用tf.image.resize时的参数,最后效果

   
image-20230508182438930 transforms
image-20230508182448684 tensorflow

肉眼观察实际上已经非常接近,但是通过图像矩阵相减发现实际上差别还是很大,可以看到边缘明显有白色线条。

   
image-20230508182546924 TensorFlow - pytorch transforms
image-20230508182713614 Pillow - pytorch transforms

讨论

https://zuru.tech/blog/the-dangers-behind-image-resizing

https://stackoverflow.com/questions/54497130/inconsistency-between-image-resizing-with-keras-pil-and-tensorflow

大概的原因就是下采样行为的不一致


面向ACG编程