How to redistort a single image point with a map? python opencv

Question:

I have used this method to create an inverse mapping to redistort an image and it works fine. Heres what it looks like in code:

# invert the mapping
combined_map_inverted = invert_map(combined_map, shape)

# apply mapping to image
frame = cv2.remap(img, combined_map_inverted, None ,cv2.INTER_LINEAR)

Notice that its a combined map, not separated into x and y. How can I take a single (x,y) point in the undistorted image and find the corresponding distorted point? I see this answer but I’m unsure how to apply it to my case.

Asked By: i_sniff_ket

||

Answers:

The combined map is a simple look up table – mapping from (u,v) to x and from (u,v) to y.

Assume (u, v) is the column, row coordinate of the undistorted image.

Than the coordinate in the distorted image is:

x = combined_map_inverted[v, u, 0]
y = combined_map_inverted[v, u, 1]

In more compact form:

x, y = combined_map_inverted[v, u].tolist()

In case we want to get the value in the (x, y) coordinate, we may use bi-linear interpolation as described in my following answer (or use other kind of interpolation).


I tried testing it using the code from your previous post:

import cv2
import glob
import numpy as np
import math
import os

if os.path.isfile('xymap_inverted.npy'):
    xymap_inverted = np.load('xymap_inverted.npy')
else:
    A = -1010
    B = -3.931
    C = 5.258
    D = 978.3

    M = -193.8
    N = 1740

    def get_tan_func_value(x):
        return A * math.tan((((x-N)/M)+B)/C) + D

    def get_inverse_tan_func_value(x):
        return M * (C*math.atan((x-D)/A) - B) + N

    # answer from linked post
    #def invert_map(F, shape):
    #    I = np.zeros_like(F)
    #    I[:,:,1], I[:,:,0] = np.indices(shape)
    #    P = np.copy(I)
    #    for i in range(10):
    #        P += I - cv2.remap(F, P, None, interpolation=cv2.INTER_LINEAR)
    #    return P

    # https://stackoverflow.com/a/72649764/4926757
    def invert_map(F):
        (h, w) = F.shape[:2] # (h, w, 2), "xymap"
        I = np.zeros_like(F)
        I[:,:,1], I[:,:,0] = np.indices((h,w)) # identity map
        P = np.copy(I)
        for i in range(10):
            correction = I - cv2.remap(F, P, None, interpolation=cv2.INTER_LINEAR)
            P += correction * 0.5
        return P

    # import image
    #images = glob.glob('*.jpg')
    img = cv2.imread('image1.jpg') #img = cv2.imread(images[0])
    h,  w = img.shape[:2]

    map_x_tan = np.zeros((img.shape[0], img.shape[1]), dtype=np.float32)
    map_x_inverse_tan = np.zeros((img.shape[0], img.shape[1]), dtype=np.float32)
    map_y = np.zeros((img.shape[0], img.shape[1]), dtype=np.float32)

    # x tan function map
    for i in range(map_x_tan.shape[0]):
        map_x_tan[i,:] = [get_tan_func_value(x) for x in range(map_x_tan.shape[1])]

    # x inverse tan function map
    for i in range(map_x_inverse_tan.shape[0]):
        map_x_inverse_tan[i,:] = [get_inverse_tan_func_value(x) for x in range(map_x_inverse_tan.shape[1])]

    # default y map
    for j in range(map_y.shape[1]):              
        map_y[:,j] = [y for y in range(map_y.shape[0])]

    # convert x tan map to 2 channel (x,y) map
    (xymap_tan, _) = cv2.convertMaps(map1=map_x_tan, map2=map_y, dstmap1type=cv2.CV_32FC2)

    # invert the 2 channel x tan map
    xymap_inverted = invert_map(xymap_tan)

    np.save('xymap_inverted.npy', xymap_inverted)

combined_map_inverted = xymap_inverted

u = 150
v = 120

x, y = combined_map_inverted[v, u].tolist()

The output is:

x = 278.2418212890625
y = 120.0

Bi-lienar interpolation example:

x0 = int(x)
y0 = int(y)
x1 = int(x0 + 1)
y1 = int(y0 + 1)
dx = x - x0
dy = y - y0
new_pixel = np.round(img[y0,x0]*(1-dx)*(1-dy) + img[y1,x0]*(1-dx)*dy + img[y0,x1]*dx*(1-dy) + img[y1,x1]*dx*dy)

Testing by remapping an entire image, and comparing with cv2.remap:

def bilinear_interp(img, x, y):
    x0 = int(x)
    y0 = int(y)
    x1 = int(x0 + 1)
    y1 = int(y0 + 1)
    dx = x - x0
    dy = y - y0
    new_pixel = np.round(img[y0,x0]*(1-dx)*(1-dy) + img[y1,x0]*(1-dx)*dy + img[y0,x1]*dx*(1-dy) + img[y1,x1]*dx*dy)
    return new_pixel.astype(np.uint8)

img = cv2.imread('image1.jpg')

ref_img = cv2.remap(img, xymap_inverted, None, cv2.INTER_LINEAR)
cv2.imwrite('ref_img.jpg', ref_img)

new_img = np.zeros_like(img)

for v in range(img.shape[0]):
    for u in range(img.shape[1]):
        x, y = combined_map_inverted[v, u].tolist()
        if (x >= 0) and (y >= 0) and (x < img.shape[1]-1) and (y < img.shape[0]-1):
            new_img[v, u] = bilinear_interp(img, x, y)

cv2.imwrite('new_img.jpg', new_img)

abs_diff = cv2.absdiff(ref_img, new_img)

cv2.imshow('abs_diff', abs_diff)  # Display the absolute difference for testing
cv2.waitKey()
cv2.destroyAllWindows()

ref_img and new_img are almost the same.

Answered By: Rotem
Categories: questions Tags: , ,
Answers are sorted by their score. The answer accepted by the question owner as the best is marked with
at the top-right corner.