Re-normalizing images

username: Frida
cooked: <p>Hi!<br>
How would you recommend to do the un-normalization of imagenet images, when:<br>
transforms.Normalize([0.485 , 0.546, 0.406], [0.229 , 0.224 , 0.225]) is used.<br>
the goal is to get back a numpy array [0,1].<br>
Thanks a lot!</p>

username: tom
cooked: <p>So <a href="https://pytorch.org/docs/stable/torchvision/transforms.html#torchvision.transforms.Normalize" rel="nofollow noopener">Normalize</a> does <code>x_norm=(x-mean)/std</code>. If you want to undo that, you could do <code>x = x_norm * std[None, :, None, None] + mean[None, :, None, None]</code> (the indexing aligning the channel dimension) or if you prefer magic, reframe it as (leaving out the indexing) <code>x = (x_norm + (mean / std)) * std</code> and then you could pass <code>-mean/std</code> and <code>1/std</code> as (pre-computed) parameters to normalize.</p>
<p>Best regards</p>
<p>Thomas</p>

username: Frida
cooked: <p>Thank you Tom!<br>
How would you compute the statistics of the normalized images? We should do it per channel, Right?</p>
<p>How the normalize will work on tensors?</p>
<p>Thank you!</p>

username: Nikronic
cooked: <p>Hi,</p>
<p>Yes we compute the mean and std channel wise.</p>
<p>And about computing these values, I have used this code gathered and edited from community for online method. Actually, in my case, my data was about 20GB and I could not load whole dataset into memory so I needed to compute std and mean batch wise and then accumulate it over all batches in epoch.</p>
<aside class="onebox githubblob">
  <header class="source">
      <a href="https://github.com/Nikronic/CoarseNet/blob/master/utils/preprocess.py#L142-L200" target="_blank" rel="nofollow noopener">github.com</a>
  </header>
  <article class="onebox-body">
    <h4><a href="https://github.com/Nikronic/CoarseNet/blob/master/utils/preprocess.py#L142-L200" target="_blank" rel="nofollow noopener">Nikronic/CoarseNet/blob/master/utils/preprocess.py#L142-L200</a></h4>
<pre class="onebox"><code class="lang-py"><ol class="start lines" start="142" style="counter-reset: li-counter 141 ;">
<li>class OnlineMeanStd:</li>
<li>    def __init__(self):</li>
<li>        pass</li>
<li>
</li>
<li>    def __call__(self, dataset, batch_size, method='strong'):</li>
<li>        """</li>
<li>        Calculate mean and std of a dataset in lazy mode (online)</li>
<li>        On mode strong, batch size will be discarded because we use batch_size=1 to minimize leaps.</li>
<li>
</li>
<li>        :param dataset: Dataset object corresponding to your dataset</li>
<li>        :param batch_size: higher size, more accurate approximation</li>
<li>        :param method: weak: fast but less accurate, strong: slow but very accurate - recommended = strong</li>
<li>        :return: A tuple of (mean, std) with size of (3,)</li>
<li>        """</li>
<li>
</li>
<li>        if method == 'weak':</li>
<li>            loader = DataLoader(dataset=dataset,</li>
<li>                                batch_size=batch_size,</li>
<li>                                shuffle=False,</li>
<li>                                num_workers=1,</li>
</ol></code></pre>

  This file has been truncated. <a href="https://github.com/Nikronic/CoarseNet/blob/master/utils/preprocess.py#L142-L200" target="_blank" rel="nofollow noopener">show original</a>

  </article>
  <div class="onebox-metadata">
    
    
  </div>
  <div style="clear: both"></div>
</aside>

<p>Note that this approach is not accurate but between two implemented approaches, <code>strong</code> gives more accurate answer in respect of longer run time.<br>
If you can load entire dataset into memory, you do not need any approximation and the approach would be different.</p>
<p>Ref: <a href="https://discuss.pytorch.org/t/about-normalization-using-pre-trained-vgg16-networks/23560/9" class="inline-onebox">About Normalization using pre-trained vgg16 networks</a></p>
<p>Bests<br>
Nik</p>

username: Frida
cooked: <p>Thanks a lot!<br>
The data is Imagenet, just want to plot the image correctly and no matter which method I take, the images are not in the wanted range.</p>

username: tom
cooked: <p>Hm, for me:</p>
<pre><code class="lang-auto">n = transforms.Normalize([0.485 , 0.546, 0.406], [0.229 , 0.224 , 0.225])
n_inv = transforms.Normalize([-0.485/0.229, -0.546/0.224, -0.406/0.225], [1/0.229, 1/0.224, 1/0.225])
a = torch.randn(3,4,5) # works with real images, too
(n_inv(n(a))-a).abs().max()
</code></pre>
<p>gives <code>3e-7</code>ish, so it is inverting up to numerical precision.</p>
<p>Best regards</p>
<p>Thomas</p>
<pre><code class="lang-auto"></code></pre>

username: crowsonkb
cooked: <p>I don’t know if this helps you, but remember that you can backpropagate through a <code>transforms.Normalize</code>. That is, if you are currently giving a normalized image to an optimizer as a parameter you can give the original, unnormalized image instead.</p>

username: Frida
cooked: <p>Thank you!<br>
That’s interesting, can you elaborate? Maybe to share a  short code?<br>
Thanks a lot!</p>