How to crop image tensor in model

s885219s: <p>Hi all,<br>
I am a beginner of pytorch, and I am trying to implement a complex CNN model called FEC-CNN from paper “<a href="http://ieeexplore.ieee.org/stamp/stamp.jsp?arnumber=7961742" rel="noopener nofollow ugc"><em>A Fully End-to-End Cascaded CNN for Facial Landmark Detection</em></a>”. However, I met some problem while building it.</p>
<p>Here is the architecture of FEC-CNN:<br>
<div class="lightbox-wrapper"><a class="lightbox" href="https://discuss.pytorch.org/uploads/default/original/2X/6/6f4c6c5c683e7b5fe9fbb057826c372d641d540e.jpg" data-download-href="https://discuss.pytorch.org/uploads/default/6f4c6c5c683e7b5fe9fbb057826c372d641d540e" title="22312055_1605985996090015_1530419928_o"><img src="https://discuss.pytorch.org/uploads/default/optimized/2X/6/6f4c6c5c683e7b5fe9fbb057826c372d641d540e_2_690x358.jpg" alt="22312055_1605985996090015_1530419928_o" data-base62-sha1="fSAJAHaXD16fPWeLUDAeSaBQBzg" width="690" height="358" srcset="https://discuss.pytorch.org/uploads/default/optimized/2X/6/6f4c6c5c683e7b5fe9fbb057826c372d641d540e_2_690x358.jpg, https://discuss.pytorch.org/uploads/default/optimized/2X/6/6f4c6c5c683e7b5fe9fbb057826c372d641d540e_2_1035x537.jpg 1.5x, https://discuss.pytorch.org/uploads/default/original/2X/6/6f4c6c5c683e7b5fe9fbb057826c372d641d540e.jpg 2x" data-dominant-color="F4F2F0"><div class="meta"><svg class="fa d-icon d-icon-far-image svg-icon" aria-hidden="true"><use href="#far-image"></use></svg><span class="filename">22312055_1605985996090015_1530419928_o</span><span class="informations">1167×607 104 KB</span><svg class="fa d-icon d-icon-discourse-expand svg-icon" aria-hidden="true"><use href="#discourse-expand"></use></svg></div></a></div></p>
<p>And here is the architecture of a single sub-CNN:<br>
<div class="lightbox-wrapper"><a class="lightbox" href="https://discuss.pytorch.org/uploads/default/original/2X/a/a170c25b6eb81bf3ceee82aec3714878aca8a941.jpg" data-download-href="https://discuss.pytorch.org/uploads/default/a170c25b6eb81bf3ceee82aec3714878aca8a941" title="22330951_1605985989423349_343220074_n"><img src="https://discuss.pytorch.org/uploads/default/optimized/2X/a/a170c25b6eb81bf3ceee82aec3714878aca8a941_2_413x500.jpg" alt="22330951_1605985989423349_343220074_n" data-base62-sha1="n2aspr9UM2SIPvXZZBPGvKjpgLT" width="413" height="500" srcset="https://discuss.pytorch.org/uploads/default/optimized/2X/a/a170c25b6eb81bf3ceee82aec3714878aca8a941_2_413x500.jpg, https://discuss.pytorch.org/uploads/default/original/2X/a/a170c25b6eb81bf3ceee82aec3714878aca8a941.jpg 1.5x, https://discuss.pytorch.org/uploads/default/original/2X/a/a170c25b6eb81bf3ceee82aec3714878aca8a941.jpg 2x" data-dominant-color="F0E9E1"><div class="meta"><svg class="fa d-icon d-icon-far-image svg-icon" aria-hidden="true"><use href="#far-image"></use></svg><span class="filename">22330951_1605985989423349_343220074_n</span><span class="informations">428×518 56.1 KB</span><svg class="fa d-icon d-icon-discourse-expand svg-icon" aria-hidden="true"><use href="#discourse-expand"></use></svg></div></a></div></p>
<p>Explaining the model a bit:<br>
The input of FEC-CNN model is face images, and the output is 68 landmarks of those images.<br>
First, an initial CNN model will predict the initial 68 landmarks of the image, and those landmark will be refined by the following sub-CNNs each of which takes patches around landmarks predicted by previous stage as input.</p>
<p>There are my questions:<br>
1, How to crop image tensor based on the output from the CNN model? I know that it is very convenient for pytorch to convert tensor into numpy, but is it correct for me to convert tensor into numpy for such processing and then convert it back to tensor for the next model input? I want the gradients can be computed from the start to the very end.<br>
2, Is it possible for me to set a batch size, since I have to crop every image based on different landmarks predicted by models? It seems to me that models will handle inputs batch size by batch size, so I have no idea how to crop every image with different cropping positions per batch size but using batch size = 1.</p>
<p>Sorry for not giving any codes, I have been stuck with these problems for a week.<br>
Thank you for any answer, or possible hint.</p>

padamsethia: <p>Currently there doesn’t seem to be a function that can crop the tensor in PyTorch.<br>
The only possible way that i can think of is converting it to PILImage and then cropping it.</p>
<p>For the second one :<br>
Is it possible to use Dataloader on the landmarks , and then set the batch size.<br>
Basically cropping the images from the CNN ouput , save it and then put it in a Dataloader and set the batch size as required.</p>
<p>Please correct me if  wrong.</p>

alexbellgrande: <p>You can crop in a fully differentiable way with indexing i.e.</p>
<pre><code class="lang-auto">import torch
from torch.autograd import Variable

image = Variable(torch.random(256, 256))

# Crop a 128 x 128 subimage in the top left corner
cropped_image = image[0:128, 0:128]
</code></pre>
<p>Since you have different landmarks for each image, you would have to first split your batch into single images, then crop, and then concatenate.</p>

padamsethia: <p>Will my method also work ?</p>

s885219s: <p>Thanks a lot!</p>
<p>I don’t know we can crop Variable just like the way in numpy.</p>
<p>And I will also try the Dataloader.</p>

alexbellgrande: <p>It wouldn’t be differentiable because dataloader uses PIL for cropping or at least it did the last time I checked.</p>

s885219s: <p>So I think I can convert the output into numpy and use it to crop the Variable in models.<br>
It will not break the whole gradients, right?</p>

alexbellgrande: <p>You will stop gradient propagation any time you switch to numpy.</p>

padamsethia: <p>Oh yeah right! Thanks!</p>

s885219s: <p>From <a href="http://pytorch.org/tutorials/beginner/data_loading_tutorial.html" rel="nofollow noopener">Data Loading and Processing Tutorial</a><br>
I transposed my numpy image (64 * 64 * 3) to torch image (3 * 64 * 64)<br>
How can I crop with indexing?<br>
I have tried <code>torch.transpose()</code>, but I am confused about the input parameter dim0 and dim1.</p>

s885219s: <p>Oh, I get it!<br>
Just need to use it twice,</p>
<blockquote>
<p>x =  torch.transpose(x,0,2)<br>
x = torch.transpose(x,0,1)</p>
</blockquote>
<p>and I will get what I want!</p>

huangbiubiu: <p>Are there any way to crop a mini-batch images (i.e. a tensor in shape <code>[N, C, H, W]</code>) with a mini-batch bounding boxes (i.e. a tensor in shape <code>[N, 4]</code>)?</p>

ddeeppnneett: <p>If the bounding boxes have the same values, its easy.</p>
<p>If the bounding boxes have the same wid and heights, at least, loop would be ok.</p>
<p>If the bounding boxes have different values, with different wid and heights,  loop + collate_fn??</p>

huangbiubiu: <aside class="quote no-group" data-username="ddeeppnneett" data-post="14" data-topic="8409">
<div class="title">
<div class="quote-controls"></div>
<img loading="lazy" alt="" width="24" height="24" src="https://discuss.pytorch.org/letter_avatar_proxy/v4/letter/d/c57346/48.png" class="avatar"> ddeeppnneett:</div>
<blockquote>
<p>If the bounding boxes have different values, with different wid and heights, loop + collate_fn??</p>
</blockquote>
</aside>
<p>I found another solution from STN: using affine transform to do cropping. See discussion here <a href="https://discuss.pytorch.org/t/cropping-a-minibatch-of-images-each-image-a-bit-differently/12247/2" class="inline-onebox">Cropping a minibatch of images, each image a bit differently - #2 by smth</a></p>

ddeeppnneett: <p>Thanks for the link.</p>
<p>But, could I use this with different crop-sizes?</p>
<pre><code class="lang-auto">input  – input batch of images (N x C x H_i x W_i)
grid   – flow-field of size (N x H_o x W_o x 2)
</code></pre>
<p>Is it only for the fixed crop-size? Or in other words, only for bs=1?</p>
<pre><code class="lang-auto">
# copy some codes from 
# https://discuss.pytorch.org/t/cropping-a-minibatch-of-images-each-image-a-bit-differently/12247/5 
###############

import cv2
import torch
import numpy as np

#############
def build_grid(bs, source_hgh,source_wid,  target_y0, target_x0, target_hgh, target_wid):
    grid_h = (torch.linspace(target_y0[0], target_y0[0]+target_hgh, steps = target_hgh)*2.0/source_hgh-1.0).unsqueeze(-1).repeat(1,target_wid).unsqueeze(-1)
    grid_w = (torch.linspace(target_x0[0], target_x0[0]+target_wid, steps = target_wid)*2.0/source_wid-1.0).unsqueeze(0).repeat(target_hgh,1).unsqueeze(-1)
    grid   = torch.cat([grid_w,grid_h],dim=2).unsqueeze(0)
    if bs &gt; 1:
        for i in range(1, bs):
            grid_h = (torch.linspace(target_y0[i], target_y0[i]+target_hgh, steps = target_hgh)*2.0/source_hgh-1.0).unsqueeze(-1).repeat(1,target_wid).unsqueeze(-1)
            grid_w = (torch.linspace(target_x0[i], target_x0[i]+target_wid, steps = target_wid)*2.0/source_wid-1.0).unsqueeze(0).repeat(target_hgh,1).unsqueeze(-1)
            wh   = torch.cat([grid_w, grid_h], dim = 2).unsqueeze(0)
            grid = torch.cat([grid, wh], dim = 0)
    return grid #grid.cuda()

############## 
bs = 2
source_hgh = 100
source_wid = 120
target_hgh = 50
target_wid = 60
target_y0 = torch.tensor([0, 20])
target_x0 = torch.tensor([10, 0])
############## 
img0 = cv2.imread('1.jpg')
img1 = cv2.imread('2.jpg')
m0 = img0[:source_hgh, :source_wid, :]
m1 = img1[:source_hgh, :source_wid, :]
cv2.imwrite('m0.jpg', m0)
cv2.imwrite('m1.jpg', m1)
n0 = torch.from_numpy(np.transpose(m0, (2, 0,1)))
n1 = torch.from_numpy(np.transpose(m1, (2, 0,1)))


t = torch.zeros(bs, 3, source_hgh, source_wid)
t[0] = n0
t[1] = n1

grid = build_grid(bs, source_hgh,source_wid, target_y0, target_x0, target_hgh, target_wid )
crp  = torch.nn.functional.grid_sample(t, grid)

r0 = crp[0].numpy()
r1 = crp[1].numpy()
r0 = np.transpose(r0, (1,2,0))
r1 = np.transpose(r1, (1,2,0))
cv2.imwrite('r0.jpg', r0)
cv2.imwrite('r1.jpg', r1)

</code></pre>
<p>#######<br>
OK, I see.  With F.affine_grid(theta, size), the bboxes could have different sizes and locations. But the outputs  would be resized.</p>
<pre><code class="lang-auto">import cv2
import torch
import numpy as np

##############
bs=2
source_width=100
source_height=120
output_width=70
output_height=60
theta=torch.zeros(bs,2,3)
target_y0=torch.tensor([0, 20],dtype=torch.float)
target_x0=torch.tensor([10, 0],dtype=torch.float)
target_y1=torch.tensor([60, 80],dtype=torch.float)
target_x1=torch.tensor([90, 60],dtype=torch.float)

theta[:, 0, 0] = (target_x1 - target_x0) / (source_width - 1)
theta[:, 0 ,2] = (target_x1 + target_x0 - source_width + 1) / (source_width - 1)
theta[:, 1, 1] = (target_y1 - target_y0) / (source_height - 1)
theta[:, 1, 2] = (target_y1 + target_y0 - source_height + 1) / (source_height - 1)
grid= torch.nn.functional.affine_grid(theta, (bs,3,output_height,output_width ))

###############
img0=cv2.imread('1.png')
img1=cv2.imread('2.png')
m0=img0[:source_height, :source_width, :]
m1=img1[:source_height, :source_width, :]
cv2.imwrite('m0.jpg', m0)
cv2.imwrite('m1.jpg', m1)
n1=torch.from_numpy(np.transpose(m1,(2, 0,1)))
n0=torch.from_numpy(np.transpose(m0,(2, 0,1)))

t=torch.zeros(bs, 3, source_height, source_width)
t[0]=n0
t[1]=n1

##############
crp=torch.nn.functional.grid_sample(t, grid)

r0=crp[0].numpy()
r1=crp[1].numpy()
r0=np.transpose(r0,(1,2,0))
r1=np.transpose(r1,(1,2,0))
cv2.imwrite('r0.jpg', r0)
cv2.imwrite('r1.jpg', r1)
</code></pre>