Как выполнить обратную операцию к torchvision.transforms.Normalize?


Если используется какая-либо PyTorch-модель, предварительно обученная на датасете ImageNet (например, модель ResNet50), то чтобы подать на данную модель изображение — требуется его предварительно подготовить с помощью операций трансформации, которые указываются в Weights.transforms (см. Models and pre-trained weights).

Например, для ResNet50_Weights:

transforms=partial(ImageClassification, crop_size=224)

,где ImageClassification выполняет resize до 256, затем crop до 224, а потом нормирует картинку с параметрами:

mean: Tuple[float, ...] = (0.485, 0.456, 0.406),
std: Tuple[float, ...] = (0.229, 0.224, 0.225),

— это усреднённые значения средних и отклонений для изображений ImageNet-а.

Таким образом, чтобы привести изображение к нужному формату — требуется выполнить преобразование нормализации с данными параметрами:

image_transform = torchvision.transforms.Compose(
    [
        torchvision.transforms.ToTensor(),
        torchvision.transforms.Normalize(mean=(0.485, 0.456, 0.406), 
                                         std=(0.229, 0.224, 0.225)),
    ]
)

Применив это преобразование к изображению:

norm_image = image_transform(cv_rgb_image)

— получим нормализованный тензор, который уже можно подавать на вход нейронной сети.

Сама процедура нормализации, фактически, сводится к простой формуле:

Z = (X - mean)/std              (1)

Это означает, что если нам вдруг потребуется сделать обратную процедуру для torchvision.transforms.Normalize, то нужно сделать следующее:

X = Z*std + mean                (2)
X = (Z + mean/std)*std          (3)
X = (Z - (mean/std)) / (1/std)  (4)

Таким образом, мы можем использовать тот же метод нормализации — torchvision.transforms.Normalize, но с  параметрами, которые можно получить из формулы 4:

invert_normalize = torchvision.transforms.Compose(
    [
        torchvision.transforms.Normalize(mean=[-0.485/0.229, -0.456/0.224, -0.406/0.225], 
                                         std=[1/0.229, 1/0.224, 1/0.225]),
    ]
)

Теперь, если выполнить:

invert_norm_image = invert_normalize(norm_image)
print(invert_norm_image.shape)
plt.imshow(invert_norm_image.permute(1, 2, 0))
plt.show()
print(invert_norm_image)

, то можно увидеть исходную картинку (правда со значениями в диапазоне от 0 до 1).

Метод permute(1, 2, 0) здесь нужен чтобы перевести тензор из формата CWH в привычный нам WHC.

Чтобы получить нормальное изображение со значениями пикселей от 0 до 255 — нужно только домножить тензор на 255 и привести его к байтовому типу:

invert_norm_image2 = invert_norm_image.permute(1, 2, 0)*255.0
invert_norm_image2 = invert_norm_image2.numpy().astype(np.uint8)
print(invert_norm_image2.shape)
plt.imshow(invert_norm_image2)
plt.show()
print(invert_norm_image2)

Забавно, но если проверить разницу между исходной и полученной картинками:

img_diff = cv_rgb_image.astype(np.float32) - invert_norm_image2.astype(np.float32)
print(img_diff)
print(np.count_nonzero(img_diff))

, то можно обнаружить, что значения некоторых пикселей отличаются на 1. Это получается из-за проблем с округлением float-ов. Если вместо 255 — проверить умножение на 256, то картинки получаются идентичными.

Ссылки


Добавить комментарий

Arduino

Что такое Arduino?
Зачем мне Arduino?
Начало работы с Arduino
Для начинающих ардуинщиков
Радиодетали (точка входа для начинающих ардуинщиков)
Первые шаги с Arduino

Разделы

  1. Преимуществ нет, за исключением читабельности: тип bool обычно имеет размер 1 байт, как и uint8_t. Думаю, компилятор в обоих случаях…

  2. Добрый день! Я недавно начал изучать программирование под STM32 и ваши уроки просто бесценны! Хотел узнать зачем использовать переменную типа…

3D-печать AI Android Arduino Bluetooth CraftDuino DIY IDE iRobot Kinect LEGO OpenCV Open Source Python Raspberry Pi RoboCraft ROS swarm ИК автоматизация андроид балансировать бионика версия видео военный датчик дрон интерфейс камера кибервесна манипулятор машинное обучение наше нейронная сеть подводный пылесос работа распознавание робот робототехника светодиод сервомашинка собака управление ходить шаг за шагом шаговый двигатель шилд юмор

OpenCV
Робототехника
Будущее за бионическими роботами?
Нейронная сеть - введение