What is the most simple way to reobtain a PIL image after normalization?

username: zapnuk
cooked: <p>A typical way to load image data is to:</p>
<ol>
<li>Load the image from disk as a PIL Image with shape [C, W, H] and data of type uint8</li>
<li>convert it to type float/double and map it to values between 0…1</li>
<li>normalize it according to some mean and std. Generally, this means that the tensor contains negative values.</li>
</ol>
<p>I’d like to visualize the normalized image. I could and would like to use the ToPILImage transformation.<br>
However, AFAIK, this function requires the input tensor to be of type uint8.</p>
<p>Is there an ‘easy’ way to do this? or do I have to adjust the channels individually using its min/max?</p>
<p>Thanks</p>

username: Nikronic
cooked: <p>Hi,</p>
<p>I assume you have a tensor of image that is normalized and called <code>img</code>. This snippet will unnormalize image given custom <code>mean</code> and <code>std</code>.</p>
<pre><code class="lang-auto">img = img.numpy().transpose((1, 2, 0))  # numpy is [h, w, c] 
mean = np.array([0.4451, 0.4262, 0.3959])  # mean of your dataset
std = np.array([0.2411, 0.2403, 0.2466])])  # std of your dataset
img = std * img + mean
img = np.clip(img, 0, 1)
pil_image = Image.fromarray(img)
</code></pre>
<p>But for using <code>ToPILImage</code>, you need to remove first line and assume image as tensor and instead of using numpy method, use <code>torch.</code>. Finally, instead of last line, <code>ToPILImage()(img)</code> will do the trick.</p>
<p>Bests</p>

username: zapnuk
cooked: <p>Thank you for the response but that’s not quite what I’m looking for.</p>
<p>I want to <strong>keep</strong> the normalization. To do that I have to somehow get back to value ranges that PIL can handle.</p>
<p>I <em>think</em> that I could do it like so:</p>
<pre><code class="lang-auto"># load image + normalize
img = Image.open(img_loc)
x_tensor = torch_transforms.ToTensor()(img)
x_tensor = x_tensor / 255
x_tensor = torch_transforms.Normalize([0.485, 0.456, 0.406], [0.229, 0.224, 0.225])(x_tensor)

# save normalized image
a = x_tensor[0,:,:].min()
b = x_tensor[1,:,:].min()
c = x_tensor[2,:,:].min()
a2 = x_tensor[0,:,:].max()
b2 = x_tensor[1,:,:].max()
c2 = x_tensor[2,:,:].max()
x_tensor[0] = ((x_tensor[0]-a) / (a2 - a)) * 255
x_tensor[1] = ((x_tensor[1]-b) / (b2 - b)) * 255
x_tensor[2] = ((x_tensor[2]-c) / (c2 - c)) * 255
img2 = transforms.ToPILImage()(x_tensor.type(torch.uint8))
</code></pre>
<p>Basically, I ‘lift’ each channel to a 0-1 range and map it to the 0-255 values.</p>
<p>However, the resulting image doesn’t look correct. It should look more distorted. I probably have some rather basic error, which is why I was wondering if there is a simpler way of doing the transformation.</p>

username: Nikronic
cooked: <p>Sorry, I did not understand your question correctly.<br>
Unfortunately, I do not know why the image normalized using custom mean and std has same statistics and low MSE error when it has been reconstructed using MinMax scaler as logically, mean and std are different.</p>
<p>but your code can be simplified,</p>
<ol>
<li><code>ToTensor</code> scales input images in range of [0, 1], so you no longer need to</li>
</ol>
<aside class="quote no-group" data-username="zapnuk" data-post="3" data-topic="85355">
<div class="title">
<div class="quote-controls"></div>
<img loading="lazy" alt="" width="24" height="24" src="https://discuss.pytorch.org/user_avatar/discuss.pytorch.org/zapnuk/48/70637_2.png" class="avatar"> zapnuk:</div>
<blockquote>
<p><code>x_tensor = x_tensor / 255</code></p>
</blockquote>
</aside>
<ol start="2">
<li>The process of finding min and max of each channel than unnormalizing can be simplified to this:</li>
</ol>
<pre><code class="lang-auto">min_i = x_tensor.min(dim=(1), keepdim=True).values.min(dim=(2), keepdim=True).values
max_i = x_tensor.max(dim=(1), keepdim=True).values.max(dim=(2), keepdim=True).values
x_tensor = ((x_tensor-min_i) / (max_i - min_i)) * 255
img2 = transforms.ToPILImage()(x_tensor.type(torch.uint8))
</code></pre>
<p>I need to do some reviewing/experiments to get what is happening and why there is no huge difference as you have mentioned.</p>