Search code examples
imageimage-processingtransformationdistortion

How to create light tunnel transformation image


I have an exercise to transform an image to the one have light tunnel effect like this. enter image description here

enter image description here

My first idea is to select a region circle in the middle and every point outside of the circle will have the value of the circle's boundary point if the center point, boundary point, and the considered point are in the same line but the result is very poor. enter image description here

here is my code:

import numpy as np
import cv2 
import math 
import numpy as np
from google.colab.patches import cv2_imshow
from shapely.geometry import LineString
from shapely.geometry import Point

img = cv2.imread("./orig_img.png")
h,w,_ = img.shape
flex_x = np.zeros((h,w),np.float32)
flex_y = np.zeros((h,w),np.float32)

scale_y= 1
center_x, center_y = (w // 2, h // 2)
radius = 50
scale_x = 1

p = Point(center_x, center_y)
c = p.buffer(radius).boundary

for y in range(h):
    delta_y = scale_y * (y - center_y)
    for x in range(w):
        delta_x = scale_x * (x - center_x)
        distance = delta_x * delta_x + delta_y * delta_y
        
        if distance <= (radius * radius):
            flex_x[y, x] = x
            flex_y[y, x] = y
        else:
            l = LineString([(center_x, center_y), (x,y)])
            i = c.intersection(l)
            new_x,new_y = round(i.coords[0][0]),round(i.coords[0][1])
            flex_x[y,x] = flex_x[new_y,new_x]
            flex_y[y,x] = flex_y[new_y,new_x]
            


dst = cv2.remap(img, flex_x, flex_y, cv2.INTER_LINEAR)

cv2_imshow(dst)

Does anyone have a better idea to do this or Can my code be fixed? , please help me! much appreciated!


Solution

  • This takes cv.remap and some geometry.

    You have trouble because you tried to draw lines. You wouldn't even need remap for that.

    im = cv.imread("d1LU6.png")
    (h,w) = im.shape[:2]
    
    # some definitions
    center = np.array([w/2, h/2])
    radius = h / 5
    
    i,j = np.mgrid[0:h, 0:w]
    xymap = np.dstack([j,i]).astype(np.float32) # "identity" map
    
    # coordinates relative to center
    coords = (xymap - center)
    # distance to center
    dist = np.linalg.norm(coords, axis=2)
    # touch only what's outside of the circle
    mask = (dist >= radius)
    # project onto circle (calculate unit vectors, move onto circle, then back to top-left origin)
    xymap[mask] = coords[mask] / dist[mask,None] * radius + center
    
    out = cv.remap(im, map1=xymap, map2=None, interpolation=cv.INTER_LINEAR)
    # imshow(out)
    

    out