1

I was wondering, given the type of interpolation that is used for image resizes using cv2.resize. How can I find out exactly where a particular pixel maps too? For example, if I'm increasing the size of an image using Linear_interpolation and I take coordinates (785, 251) for a particular pixel, regardless of whether or not the aspect ratio changes between the source image and resized image, how could I find out exactly to what coordinates the pixel in the source image with coordinates == (785, 251) maps in the resized version? I've looked over the internet for a solution but all solutions seem to be indirect methods of finding out where a pixel maps that don't actually work for different aspect ratio's:

https://answers.opencv.org/question/209827/resize-and-remap/

After resizing an image with cv2, how to get the new bounding box coordinate

Is there a way through cv2 to access the way pixels are mapped maybe and through reversing the script finding out the new coordinates?

The reason why I would like this is that I want to be able to create bounding boxes that give me back the same information regardless of the change in aspect ratio of a given image. Every method I've used so far doesn't give me back the same information. I figure that if I can figure out where the particular pixel coordinates of x,y top left and bottom right maps I can recreate an accurate bounding box regardless of aspect ratio changes.

Jeru Luke
  • 20,118
  • 13
  • 80
  • 87
PeterQuando
  • 75
  • 1
  • 7
  • 1
    `x_scaled = x_original * scale_x` and `y_scaled = y_original * scale_y`. The scale in x and y don't have to be equal, i.e. you can have a different aspect ratio. – Miki Nov 16 '21 at 12:20
  • https://stackoverflow.com/a/62368797/5008845 – Miki Nov 16 '21 at 12:22
  • @Miki And are you sure this maps to the same pixel in the new_image? – PeterQuando Nov 16 '21 at 12:23
  • yes. but you obviously can loose accuracy – Miki Nov 16 '21 at 12:26
  • you can choose tje interpolation technique used by cv2.resize..By default it is bilinear interpolation. Being sure how pixels map you should have a look at the implementation source code. The formula presented by @Miki is the goal and probably, all intetpolations try to come as close as possible. – Micka Nov 16 '21 at 12:29
  • I tried it out, and it doesn't work. The starting x,y coordinates are too high and shifted to the right too much. – PeterQuando Nov 16 '21 at 13:28

1 Answers1

1

Scaling the coordinates works when the center coordinate is (0, 0).

You may compute x_scaled and y_scaled as follows:

  • Subtract x_original_center and y_original_center from x_original and y_original.
    After subtraction, (0, 0) is the "new center".
  • Scale the "zero centered" coordinates by scale_x and scale_y.
  • Convert the "scaled zero centered" coordinates to "top left (0, 0)" by adding x_scaled_center and y_scaled_center.

Computing the center accurately:
The Python conversion is:
(0, 0) is the top left, and (cols-1, rows-1) is the bottom right coordinate.
The accurate center coordinate is:
x_original_center = (original_rows-1)/2
y_original_center = (original_cols-1)/2


Python code (assume img is the original image):

resized_img = cv2.resize(img, [int(cols*scale_x), int(rows*scale_y)])

rows, cols = img.shape[0:2]
resized_rows, resized_cols = resized_img.shape[0:2]

x_original_center = (cols-1) / 2
y_original_center = (rows-1) / 2

x_scaled_center = (resized_cols-1) / 2
y_scaled_center = (resized_rows-1) / 2

# Subtract the center, scale, and add the "scaled center".
x_scaled = (x_original - x_original_center)*scale_x + x_scaled_center
y_scaled = (y_original - y_original_center)*scale_y + y_scaled_center

Testing

The following code sample draws crosses at few original and scaled coordinates:

import cv2

def draw_cross(im, x, y, use_color=False):
    """ Draw a cross with center (x,y) - cross is two rows and two columns """
    x = int(round(x - 0.5))
    y = int(round(y - 0.5))
    if use_color:
        im[y-4:y+6, x] = [0, 0, 255]
        im[y-4:y+6, x+1] = [255, 0, 0]
        im[y, x-4:x+6] = [0, 0, 255]
        im[y+1, x-4:x+6] = [255, 0, 0]
    else:
        im[y-4:y+6, x] = 0
        im[y-4:y+6, x+1] = 255
        im[y, x-4:x+6] = 0
        im[y+1, x-4:x+6] = 255


img = cv2.imread('graf.png')  # http://man.hubwiz.com/docset/OpenCV.docset/Contents/Resources/Documents/db/d70/tutorial_akaze_matching.html
rows, cols = img.shape[0:2]  # cols = 320, rows = 256

# 3 points for testing:
x0_original, y0_original = cols//2-0.5, rows//2-0.5  # 159.5, 127.5
x1_original, y1_original = cols//5-0.5, rows//4-0.5  # 63.5, 63.5
x2_original, y2_original = (cols//5)*3+20-0.5, (rows//4)*3+30-0.5  # 211.5, 221.5
draw_cross(img, x0_original, y0_original)  # Center of cross (159.5, 127.5)
draw_cross(img, x1_original, y1_original)
draw_cross(img, x2_original, y2_original)

scale_x = 2.5
scale_y = 2

resized_img = cv2.resize(img, [int(cols*scale_x), int(rows*scale_y)], interpolation=cv2.INTER_NEAREST)
resized_rows, resized_cols = resized_img.shape[0:2]  # cols = 800, rows = 512

# Compute center column and center row
x_original_center = (cols-1) / 2  # 159.5
y_original_center = (rows-1) / 2  # 127.5

# Compute center of resized image
x_scaled_center = (resized_cols-1) / 2  # 399.5
y_scaled_center = (resized_rows-1) / 2  # 255.5

# Compute the destination coordinates after resize
x0_scaled = (x0_original - x_original_center)*scale_x + x_scaled_center  # 399.5
y0_scaled = (y0_original - y_original_center)*scale_y + y_scaled_center  # 255.5

x1_scaled = (x1_original - x_original_center)*scale_x + x_scaled_center  # 159.5
y1_scaled = (y1_original - y_original_center)*scale_y + y_scaled_center  # 127.5

x2_scaled = (x2_original - x_original_center)*scale_x + x_scaled_center  # 529.5
y2_scaled = (y2_original - y_original_center)*scale_y + y_scaled_center  # 443.5

# Draw crosses on resized image
draw_cross(resized_img, x0_scaled, y0_scaled, True)
draw_cross(resized_img, x1_scaled, y1_scaled, True)
draw_cross(resized_img, x2_scaled, y2_scaled, True)

cv2.imshow('img', img)
cv2.imshow('resized_img', resized_img)
cv2.waitKey()
cv2.destroyAllWindows()

Original image:
enter image description here

Resized image:
enter image description here

Making sure the crosses are aligned:
enter image description here


Note:
In my answer I was using the naming conventions of Miki's comment.

Rotem
  • 30,366
  • 4
  • 32
  • 65