Skip to content

Instantly share code, notes, and snippets.

@royshil
Last active September 6, 2024 19:34
Show Gist options
  • Save royshil/0b21e8e7c6c1f46a16db66c384742b2b to your computer and use it in GitHub Desktop.
Save royshil/0b21e8e7c6c1f46a16db66c384742b2b to your computer and use it in GitHub Desktop.
Warp an image to cylindrical coordinates for cylindrical panorama stitching, using Python OpenCV
import cv2
import numpy as np
def cylindricalWarp(img, K):
"""This function returns the cylindrical warp for a given image and intrinsics matrix K"""
h_,w_ = img.shape[:2]
# pixel coordinates
y_i, x_i = np.indices((h_,w_))
X = np.stack([x_i,y_i,np.ones_like(x_i)],axis=-1).reshape(h_*w_,3) # to homog
Kinv = np.linalg.inv(K)
X = Kinv.dot(X.T).T # normalized coords
# calculate cylindrical coords (sin\theta, h, cos\theta)
A = np.stack([np.sin(X[:,0]),X[:,1],np.cos(X[:,0])],axis=-1).reshape(w_*h_,3)
B = K.dot(A.T).T # project back to image-pixels plane
# back from homog coords
B = B[:,:-1] / B[:,[-1]]
# make sure warp coords only within image bounds
B[(B[:,0] < 0) | (B[:,0] >= w_) | (B[:,1] < 0) | (B[:,1] >= h_)] = -1
B = B.reshape(h_,w_,-1)
img_rgba = cv2.cvtColor(img,cv2.COLOR_BGR2BGRA) # for transparent borders...
# warp the image according to cylindrical coords
return cv2.remap(img_rgba, B[:,:,0].astype(np.float32), B[:,:,1].astype(np.float32), cv2.INTER_AREA, borderMode=cv2.BORDER_TRANSPARENT)
if __name__ == '__main__':
img = cv2.imread("image.png")
h, w = img.shape[:2]
K = np.array([[800,0,w/2],[0,800,h/2],[0,0,1]]) # mock intrinsics
img_cyl = cylindricalWarp(img, K)
cv2.imwrite("image_cyl.png", img_cyl)
@royshil
Copy link
Author

royshil commented Feb 6, 2019

line 18 throws an error, could you please fix / explain what's happening there?

Fixed. Thank you!

@thennk
Copy link

thennk commented May 10, 2020

Any simple way that you recommend, to turn cylindrical warping to transverse-cylindrical warping?

@royshil
Copy link
Author

royshil commented May 10, 2020

Any simple way that you recommend, to turn cylindrical warping to transverse-cylindrical warping?

@thennk can't see a simple way, but line 13 https://gist.github.com/royshil/0b21e8e7c6c1f46a16db66c384742b2b#file-cylindricalwarping-py-L13 is where you'd need to make a change

@thennk
Copy link

thennk commented May 10, 2020

Any simple way that you recommend, to turn cylindrical warping to transverse-cylindrical warping?

@thennk can't see a simple way, but line 13 https://gist.github.com/royshil/0b21e8e7c6c1f46a16db66c384742b2b#file-cylindricalwarping-py-L13 is where you'd need to make a change

Yes, thank you.

@thennk
Copy link

thennk commented May 12, 2020

For future reference, for transverse cylindrical propjection (wrapping around cylinder flipped by 90), line 13 should be:

A = np.stack([X[:,0],np.sin(X[:,1]), np.cos(X[:,1])],axis=-1).reshape(w_*h_,3)

@vincent-l-j
Copy link

Thanks for this. For future reference, to wrap an image with a transparent background:
img = cv2.imread("image.png", cv2.IMREAD_UNCHANGED)

@hanish3464
Copy link

I really want reverse method, cylinderical to flat image. Is there any way?

@dominikandreas
Copy link

@hanish3464 you can invert the mapping B and use that for cv2.remap. here's an answer on stackoverflow with an implementation for how to do that: https://stackoverflow.com/a/65566295

@Ascyt
Copy link

Ascyt commented Apr 13, 2023

Did not work. Only outputted the same image without the outer pixels. I used this image:
image
And got this:

image_cyl

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment