Convert float image array to int in pil via Image.fromarray

username: John_J_Watson
cooked: <p>I have a pytorch tensor of shape  <code>3,256,256</code>  where 3 in the channel and 256,256 are the color image dimensions, with all float values.</p>
<p>I am trying to feed this into the network and using PIL to do some transforms. To this end, I do:</p>
<pre><code class="lang-auto">img = Image.fromarray((255*imgs[i]).numpy().astype(np.uint8))
</code></pre>
<p>but I get:</p>
<pre><code class="lang-auto">TypeError: Cannot handle this data type: (1, 1, 256), |u1
</code></pre>
<p>When I check the output of  <code>(255*imgs[i]).numpy().astype(np.uint8)</code> , I do however see, for example:</p>
<pre><code class="lang-auto">[[[ 62  57  59 ...  63  46  36]
  [ 72  71  67 ...  80  76  82]
  [ 58  63  63 ... 145 152 169]
  ...
  [238 240 243 ...   7   7   7]
  [241 239 240 ...   5   5   6]
  [241 243 242 ...   4   3   5]]

 [[ 83  78  80 ...  86  70  61]
  [ 91  90  85 ...  95  93  98]
  [ 80  83  80 ... 141 150 168]
  ...
  [176 178 181 ...  14  14  14]
  [177 176 178 ...  15  15  17]
  [179 180 180 ...  13  13  15]]

 [[147 141 143 ... 150 136 128]
  [147 149 148 ... 154 149 154]
  [141 149 148 ... 178 182 196]
  ...
  [129 131 134 ...  43  43  43]
  [130 130 131 ...  45  45  47]
  [133 134 133 ...  44  44  46]]]
</code></pre>
<p>I am not an image expert by a long shot, and I am struggling to troubleshoot this issue now.</p>

username: Nikronic
cooked: <p>Hi,</p>
<p>If you want to convert any tensor into PIL images, PyTorch already has a function that supports all modes defined in PIL and automatically converts to proper mode. Here is a snippet that simulates your case:</p>
<pre><code class="lang-auto">from torchvision.transforms import ToPILImage  # built-in function

x = torch.FloatTensor(3, 256, 256).uniform_(0, 1)  # [0, 1] float matrix
img = ToPILImage()(x)  # image corresponding to x
</code></pre>
<p>If you try to print values of <code>img</code> using <code>print(np.array(img)</code>, it has been already converted to [0, 255].</p>
<p>But about your current approach which is right logically. The only problem is that numpy consider images in <code>[height, width, Channel]</code> format meanwhile PIL and PyTorch, expect inputs in <code>[Channel, height, width]</code> format. So, you have size mismatch in <code>Image.fromarray()</code>.</p>
<p>To solve this issue, the dimensions just need to be swapped.</p>
<pre><code class="lang-auto">img = Image.fromarray((255*imgs[i].permute(1, 2, 0)).numpy().astype(np.uint8))
</code></pre>
<p>bests,<br>
Nik</p>

username: John_J_Watson
cooked: <p>Hi Nik,</p>
<p>FIRSTLY, thank you VERY much for your advice and help.<br>
I do follow what you are saying, but for some reason, I still get the same error.</p>
<p>I added the transforms as you suggested, passed it to imageloader class, and then I do something like this in <code>__getitem__</code>:</p>
<pre><code>def __getitem__(self, index):
    imgs, labels = self.train_data[index], self.train_labels[index]
        
    img_ar = []
    for i in range(len(imgs)):
        print(imgs[i].numpy().shape) #output: (3, 256, 256)
        img = Image.fromarray(imgs[i].permute(1, 2, 0).numpy())
        img = self.train_data_transforms(img)
        ..&lt;do more stuff&gt;
    return img_ar, labels
</code></pre>
<p>But I get:</p>
<pre><code>    raise TypeError("Cannot handle this data type: %s, %s" % typekey)
</code></pre>
<p>TypeError: Cannot handle this data type: (1, 1, 3), &lt;f4</p>
<p>Shouldnt it be (256,256,3)? <img src="https://discuss.pytorch.org/images/emoji/apple/frowning.png?v=9" title=":frowning:" class="emoji" alt=":frowning:"></p>

username: Nikronic
cooked: <p>Could you print stacktrace of error?</p>
<p>And what is the reason behind this line? In first post, you mentioned that <code>imgs</code> are float between [0, 1], etc.</p>
<aside class="quote no-group" data-username="John_J_Watson" data-post="3" data-topic="82167">
<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/john_j_watson/48/16421_2.png" class="avatar"> John_J_Watson:</div>
<blockquote>
<p><code>img = Image.fromarray(imgs[i].permute(1, 2, 0).numpy())</code></p>
</blockquote>
</aside>
<p>So proper change would be</p>
<aside class="quote no-group" data-username="Nikronic" data-post="2" data-topic="82167">
<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/nikronic/48/71873_2.png" class="avatar"> Nikronic:</div>
<blockquote>
<p><code>img = Image.fromarray((255*imgs[i].permute(1, 2, 0)).numpy().astype(np.uint8))</code></p>
</blockquote>
</aside>
<p>If you are using PyTorch transforms, still best approach is to use <code>transforms.ToTensor</code> and <code>transforms.ToPILImage</code> in <code>Compose</code> of transformation.</p>

username: John_J_Watson
cooked: <p>Actually Nik, I was doing something stupid. Indeed, your solution works like a charm. Many thanks for your help, REALLY appreciate it (was stuck on this for 1 whole day!). You saved me!</p>

username: John_J_Watson
cooked: <p>BTW: I did not use the transforms - the problem with this is that I need to convert it to tensor, then PIL and then tensor again(?) since the rest of the code expects a tensor.</p>
<p>I presume doing it manually is still the same?</p>

username: Nikronic
cooked: <p>No problem mate, we have to begin from somewhere but we finally learn!</p>
<p>Yes, in the end, all modules need tensor input. So, you are right, somehow, if you want to use few functions, you have convert to PIL and then to Tensor. For instance, assume these transforms:</p>
<pre><code class="lang-auto">custom_transforms = Compose([
    RandomResizedCrop(size=224, scale=(0.8, 1.2)),
    RandomRotation(degrees=(-30, 30)),
    RandomHorizontalFlip(p=0.5),
    ToTensor(),
    Normalize(mean=mean, std=std),
    RandomNoise(p=0.5, mean=0, std=0.1)])

</code></pre>
<p>Note that I had to use <code>ToTensor</code> if I wanted to use built-in <code>Normalize</code>. Although, the last transformation <code>RandomNoise</code> is not part of PyTorch and I have implemented it, but still you can define its inputs so it can work with tensors (in my example) or PIL image.</p>
<pre><code class="lang-auto">class RandomNoise(object):
    def __init__(self, p, mean=0, std=0.1):
        self.p = p
        self.mean = mean
        self.std = std

    def __call__(self, img):
        if random.random() &lt;= self.p:
            noise = torch.empty(*img.size(), dtype=torch.float, requires_grad=False)
            return img+noise.normal_(self.mean, self.std)
        return img
</code></pre>
<p>On top of that, all modules in PyTorch are available as a function under <code>torch.nn.functional</code> so for instance if you want the class type, use <code>transforms.ToTensor</code> but if you need it as a function in any place of your code, just use <code>functional.to_tensor()</code>.</p>
<p>That’s why I love PyTorch, really easy to read and use, as you said, like a charm!</p>
<p>If you are new to PyTorch, I highly suggest you to read all tutorial in official website/github. It won’t take much time but will enable you to understand the whole idea easily.</p>
<p>bests</p>

username: John_J_Watson
cooked: <p>you are awesome Nik, Thank you for the snippet. The problem I had with <code>PIL transform</code> was that I was using it with a dataloader like so:</p>
<pre><code>        p = transforms.Compose([\
                                torchvision.transforms.Resize((512,512)),\
                                torchvision.transforms.ToTensor(),\
                                ])
        trainingds = torchvision.datasets.ImageFolder(  root=train_data_path,transform=p   )
</code></pre>
<p>but when I added PIL to this list, I still could see <code>floats</code>. I did not give it much thought as to why it wouldn’t work (I use this trainingds to perform tensor operations and I noticed that the other solution (when I used PILTransform in <strong>get_item</strong>), so I went for this instead.</p>
<p>Also, I think keeping the PIlTransform in <code>def __getitem__(self, index):</code> seems a cleaner solution.</p>
<p>Well, once again, thanks a tonne.</p>