Search code examples
pythonopencv

How to convert distances on perspective grid to heat map


I am trying to transfer objects locations that can be observed by human eye on image data taken by fixed camera to 2d field displays the area from above without texture info but object names and location.

However, I don't know how to google that I need is below.

This is the grid I use.

enter image description here

This is the perspectived grid image.

enter image description here

And this is the insight how camera position is.

enter image description here

If I say that every side on grid is 50 centimeters(so area is 50x50 for one square), and wall height is 2.5 meters.

So which method can be used to generate an heat map which pixel colors tell the distance from wall to the point on grid?

The grid can be enlarged to black spaces.

The below code for grid and perspectived grid.

import cv2
import numpy as np
import imutils


img = np.zeros((480,640),dtype=np.uint8)

size = 40
for i in range(int(img.shape[1]/size)+1):
    img = cv2.line(img, (i*size, 0), (i*size, img.shape[0]), thickness = 2, color = 255)

for i in range(int(img.shape[0]/size)+1):
    img = cv2.line(img, (0, i*size), (img.shape[1], i*size), thickness = 2, color = 255)

cv2.imshow("img",img)
cv2.waitKey(0)

h, w = img.shape
a = [
    [0, 0],
    [h - 1, 0],
    [0, w - 1],
    [h - 1, w - 1]]

pt_A = a[0]
pt_B = a[1]
pt_C = a[2]
pt_D = a[3]

width_AD = np.sqrt(((pt_A[0] - pt_D[0]) ** 2) + ((pt_A[1] - pt_D[1]) ** 2))
width_BC = np.sqrt(((pt_B[0] - pt_C[0]) ** 2) + ((pt_B[1] - pt_C[1]) ** 2))
maxWidth = max(int(width_AD), int(width_BC))
 
 
height_AB = np.sqrt(((pt_A[0] - pt_B[0]) ** 2) + ((pt_A[1] - pt_B[1]) ** 2))
height_CD = np.sqrt(((pt_C[0] - pt_D[0]) ** 2) + ((pt_C[1] - pt_D[1]) ** 2))
maxHeight = max(int(height_AB), int(height_CD))

input_pts = np.float32([pt_A, pt_B, pt_C, pt_D])
output_pts = np.float32([[800, 600], [900, 300], [-300, 500], [100, 200]])

def perspective(img, input_pts=input_pts, output_pts=output_pts, maxWidth=maxWidth, maxHeight=maxHeight):
    M = cv2.getPerspectiveTransform(input_pts,output_pts)

    out = cv2.warpPerspective(img,M,(maxWidth, maxHeight),flags=cv2.INTER_LINEAR)
    out = imutils.resize(out,height=500)
    return out


frame = perspective(img)
# Display image
cv2.imshow('frame', frame)
cv2.waitKey(0)

Solution

  • The distance can be calculated in a pythagorean way:

    formula

    The result then can be mapped to a color and displayed accordingly:

    enter image description here

    import cv2
    import numpy as np
    
    img = np.zeros((480, 640), dtype=np.uint8)
    
    size = 40
    for i in range(int(img.shape[1] / size) + 1):
        img = cv2.line(img, (i * size, 0), (i * size, img.shape[0]), thickness=2,
                       color=255)
    
    for i in range(int(img.shape[0] / size) + 1):
        img = cv2.line(img, (0, i * size), (img.shape[1], i * size), thickness=2,
                       color=255)
    
    cv2.imshow("img", img)
    cv2.waitKey(0)
    
    h, w = img.shape
    a = [
        [0, 0],
        [h - 1, 0],
        [0, w - 1],
        [h - 1, w - 1]]
    
    pt_A = a[0]
    pt_B = a[1]
    pt_C = a[2]
    pt_D = a[3]
    
    width_AD = np.sqrt(((pt_A[0] - pt_D[0]) ** 2) + ((pt_A[1] - pt_D[1]) ** 2))
    width_BC = np.sqrt(((pt_B[0] - pt_C[0]) ** 2) + ((pt_B[1] - pt_C[1]) ** 2))
    maxWidth = max(int(width_AD), int(width_BC))
    
    height_AB = np.sqrt(((pt_A[0] - pt_B[0]) ** 2) + ((pt_A[1] - pt_B[1]) ** 2))
    height_CD = np.sqrt(((pt_C[0] - pt_D[0]) ** 2) + ((pt_C[1] - pt_D[1]) ** 2))
    maxHeight = max(int(height_AB), int(height_CD))
    
    input_pts = np.float32([pt_A, pt_B, pt_C, pt_D])
    output_pts = np.float32([[800, 600], [900, 300], [-300, 500], [100, 200]])
    
    
    def perspective(img, input_pts=input_pts, output_pts=output_pts,
                    maxWidth=maxWidth, maxHeight=maxHeight):
        M = cv2.getPerspectiveTransform(input_pts, output_pts)
    
        out = cv2.warpPerspective(img, M, (maxWidth, maxHeight),
                                  flags=cv2.INTER_LINEAR)
        return out
    
    def calc_distance(cam_pos, img, tile_px, tile_size=50):
        """Calculate eucledian distance between camera and floor."""
        cam_pos_x, cam_pos_y, cam_pos_z = cam_pos
        floor_size_x = int(img.shape[1] / tile_px) * tile_size
        floor_size_y = int(img.shape[0] / tile_px) * tile_size
        floor_pos_x, floor_pos_y = np.meshgrid(
            np.arange(img.shape[1]) / img.shape[1] * floor_size_x,
            np.arange(img.shape[0]) / img.shape[0] * floor_size_y)
        floor_pos_z = 0
        return np.sqrt(
            (floor_pos_x - cam_pos_x) ** 2 + (floor_pos_y - cam_pos_y) ** 2 + (
                        floor_pos_z - cam_pos_z) ** 2)
    
    # calculate and normalize distance to [0, 255]
    cam_pos = [0,0,250]
    distance = calc_distance(cam_pos, img, size)
    distance_img = ((distance - distance.min()) / (distance.max() - distance.min()) * 255).astype(np.uint8)
    
    # create color coded distance image, remove non-grid parts, show perspective img
    distance_img = cv2.applyColorMap(distance_img, cv2.COLORMAP_PARULA)
    distance_img_grid = distance_img * img[..., None] / img.max()
    frame = perspective(distance_img_grid)
    # Display image
    cv2.imshow('frame', frame)
    cv2.waitKey(0)