Image warping for backward flow using forward flow matrix- optical flow

thehappyidiot: <p>Hi</p>
<p>The code I’m using to warp the image is</p>
<pre><code class="lang-auto">
B, C, H, W = image_B.size()

xx = torch.arange(0, W).view(1,-1).repeat(H,1)

yy = torch.arange(0, H).view(-1,1).repeat(1,W)

xx = xx.view(1,1,H,W).repeat(B,1,1,1)

yy = yy.view(1,1,H,W).repeat(B,1,1,1)

grid = torch.cat((xx,yy),1).float()

vgrid = grid + flow_AB

vgrid[:,0,:,:] = 2.0*vgrid[:,0,:,:].clone() / max(W-1,1)-1.0

vgrid[:,1,:,:] = 2.0*vgrid[:,1,:,:].clone() / max(H-1,1)-1.0

warped_image = torch.nn.functional.grid_sample(image_B, vgrid.permute(0,2,3,1), 'nearest')
</code></pre>
<p>I’ve also tried creating a flow explicitly using image processing techniques</p>
<pre><code class="lang-auto">img2 = cv2.imread('frame_0011.png',0)

img1 = cv2.imread('frame_0010.png',0)

flow = cv2.calcOpticalFlowFarneback(img2, img1,  None, 0.5, 3, 15, 3, 5, 1.2, 0)

image2 = cv2.imread('frame_0011.png',1)

plt.imshow(cv2.remap(image2, flow, None, cv2.INTER_LINEAR))
</code></pre>
<p>But it doesn’t hold up<br>
This is the difference with the image I want to warp to</p>
<p><img src="https://discuss.pytorch.org/uploads/default/original/3X/b/7/b7462e5d4ef46af242b1aa516d5ac78e3c7ff1b3.png" alt="image" data-base62-sha1="q9jJQmMswE4S08SJRkHy6RTCV2P" width="379" height="177"></p>
<p>I don’t get why it’s not working. Could anyone please help me?</p>

thehappyidiot: <p>This is the image I’m warping to (imageA) and the image I’m warping from (imageB)<br>
<div class="lightbox-wrapper"><a class="lightbox" href="https://discuss.pytorch.org/uploads/default/original/3X/4/b/4bfb33fc6039e66fcc8c3cc74f6f79a1d50b863a.jpeg" data-download-href="https://discuss.pytorch.org/uploads/default/4bfb33fc6039e66fcc8c3cc74f6f79a1d50b863a" title="image"><img src="https://discuss.pytorch.org/uploads/default/optimized/3X/4/b/4bfb33fc6039e66fcc8c3cc74f6f79a1d50b863a_2_690x151.jpeg" alt="image" data-base62-sha1="aQa0DTs8aaiQbMoiAHe9ZSKzBN0" width="690" height="151" srcset="https://discuss.pytorch.org/uploads/default/optimized/3X/4/b/4bfb33fc6039e66fcc8c3cc74f6f79a1d50b863a_2_690x151.jpeg, https://discuss.pytorch.org/uploads/default/optimized/3X/4/b/4bfb33fc6039e66fcc8c3cc74f6f79a1d50b863a_2_1035x226.jpeg 1.5x, https://discuss.pytorch.org/uploads/default/original/3X/4/b/4bfb33fc6039e66fcc8c3cc74f6f79a1d50b863a.jpeg 2x" data-dominant-color="6B6D6E"><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">image</span><span class="informations">1046×229 78.6 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>

thehappyidiot: <p>When I use grid sample and flowAB (from A to B), I get this as the result:<br>
<img src="https://discuss.pytorch.org/uploads/default/original/3X/5/2/52da7254bad8769986ab518ef5aed3a54cfed8bf.png" alt="image" data-base62-sha1="bOXaGcQE7yxc9FsBibqDKfqysvt" width="379" height="177"><br>
Notice the failed warping at the knife.</p>

ptrblck: <p>Is this some kind of reference dataset for optical flow models?<br>
If I’m not mistaken, I’ve seen these images before in a similar question and also the warping at the knife was the issue (however I cannot find it at the moment).</p>

thehappyidiot: <p>I’m not sure about that, but it was the easiest examples I could easily take and use for test purposes, so I went with it</p>
<p>But, the warping at the knife is pretty much an issue<br>
I’ve tried implementing a purely cv2 implementation, and there’s no difference with the resultant warped image with remap now,<br>
But when using the flow field from cv2 as compared to the flo file here, it gives a similar result with grid_sample<br>
I don’t get the difference between grid_sample and remap. I think that’s crucial here<br>
I’ve looked around the forums for similar queries, but they’ve all been left incomplete</p>

Azin: <p>Hello there.<br>
I don’t see anything wrong with the warped image.<br>
I see that the shoulder and the knife are doubled, but as those (partly doubled regions) are occluded parts, that artifact is expected in the warped image. The background is occluded partly with the shoulder and with the knife.</p>
<p>For backward warping the forward flow, we look at the image coordinates, read the flow for each pixel. Then we copy the pixel value of the corresponding pixel (x + u) in that initial coordinate (x). Assume the background has no movement. In this case the flow of that part is zero and the pixel values of the second image in those pixels (which is equal to the same initial coordinates) are copied.<br>
In other words:<br>
warped_img(x) = img2[x + flow(x)].<br>
And to have a better interpolation, bilinear interpolation could be used instead of nearest neighbor in grid_sample.<br>
Are the copied parts the reason you assume something is wrong? If not, sorry for the extra explanation.</p>

jefflee: <p>It’s the Sintel dataset <a href="https://sintel.is.tue.mpg.de/" rel="noopener nofollow ugc">https://sintel.is.tue.mpg.de/</a>.</p>

jefflee: <p>Thanks for your nice explanation. However, I’ve seen doubled regions even with the Sintel groundtruth flow, which shouldn’t happen, isn’t it? This really baffles me.</p>

Azin: <p>This is normal with the ground truth flow <img src="https://discuss.pytorch.org/images/emoji/apple/slight_smile.png?v=10" title=":slight_smile:" class="emoji" alt=":slight_smile:"><br>
Since the background in the image has no movement (or its movement is slighter) the part of the background that can be seen in the first image that got occluded by the moving object in the second image is the doubled area.<br>
To make it simpler to understand, assume an image with a box in the middle with a still background (back ground with no movement). Assume that the box moves 5 pixels to the right from the first to the second image.<br>
Now to warp the second image to the first image with the ground truth flow, you take a look at the coordinates of the first image, read the flow for each pixel, and copy the corresponding pixel’s color in the second and replace the color of that initial pixel in the first image.<br>
In this case, since the back ground’s flow is zero, the part of the background that could be seen in the first image but not in the second image (i.e. got occluded with the object) is copied from the second image (showing parts of the moving object) to the that part of the first image. Also the whole box is copied to its initial position. So in the resulting image you will have the box + a part of the box that occluded those 5 pixels of the background in the second image.</p>

jefflee: <p>Thank you! Your explanation is very clear. Now I realized where I had missed. Seems warping is still somewhat tricky</p>