Как правильно использоватьsolvePnP с камерами типа «рыбий глаз»?Python

Программы на Python
Anonymous
Как правильно использоватьsolvePnP с камерами типа «рыбий глаз»?

Сообщение Anonymous »

Неделями я пытался правильно оценить позу камеры «рыбий глаз» относительно поля для игры в падель. Я перепробовал каждую комбинацию матрицы камеры, коррекцию «рыбий глаз», нормализованные координаты... но, похоже, ничего не работает должным образом.
Обстановка: (посмотрите на изображение ниже, чтобы лучше понять) размеры поля для игры в падел 10х20, со стеклянной стеной высотой 3 метра вокруг. Камера расположена за стеклом, посередине более короткой стороны, на высоте около 3 м и смотрит на поле. С помощью рыбьего глаза он может видеть все четыре угла поля.
Опорные координаты: я решил пометить верхний левый угол как (0,0,0), при этом x увеличивается справа от изображения, а y — внизу. В этой системе координат камера должна находиться в точке ~(5,20,3)
С помощью приведенного ниже кода я получаю три оси с правильным направлением и ориентацией, но они располагаются в точке, которая кажется (0,6,0), а не в реальном начале координат. Кроме того, если я вычисляю положение камеры по rvec, tvec, я получаю, что она находится на y=25.
import cv2
import numpy as np
from utils import load_fisheye_params, debug_image_with_points, undistort_image

K, D = load_fisheye_params('fishcam-fisheye.txt')

original_img = cv2.imread('bg_distorted_BO-2221.jpg')

objective_points = np.float32([
[ 0., 0., 0.],
[10., 0., 0.],
[10., 17., 0.],
[ 0., 17., 0.],
[ 0., 0., 3.],
[10., 0., 3.]
])

# Manually clicked on the original (distorted) image
distorted_points = np.float32([
[782., 299.],
[1118., 283.],
[1556., 585.],
[376., 639.],
[773., 204.],
[1118., 187.]
])

distorted_points = distorted_points.reshape(-1, 1, 2)
undistorted_points = cv2.fisheye.undistortPoints(distorted_points, K, D, P=K)

success, rvec, tvec = cv2.solvePnP(
objective_points, undistorted_points, K, None, flags=cv2.SOLVEPNP_ITERATIVE
)

# Draws the X (Red), Y (Green), and Z (Blue) axes in undistorted image
undistorted_image = cv2.fisheye.undistortImage(img, K, D, Knew=K)
cv2.drawFrameAxes(undistorted_image, K, None, rvec, tvec, 5)

# Draw the original points and their reprojection

projected_points, _ = cv2.fisheye.projectPoints(
objective_points.reshape(-1, 1, 3),
rvec,
tvec,
K,
D
)

# Reshape arrays for easy looping
projected_points = projected_points.reshape(-1, 2)
clicked_points = distorted_points.reshape(-1, 2)

for i in range(len(objective_points)):
clicked_px = (int(clicked_points[0]), int(clicked_points[1]))
proj_px = (int(projected_points[0]), int(projected_points[1]))

cv2.circle(img, clicked_px, 6, (0, 0, 255), -1)
cv2.circle(img, proj_px, 6, (0, 255, 0), -1)
cv2.line(img, clicked_px, proj_px, (0, 255, 255), 2)
cv2.putText(img, str(i), clicked_px, cv2.FONT_HERSHEY_SIMPLEX, 0.8, (255, 255, 255), 2)

cv2.imshow('Debug', img)
cv2.waitKey(0)
cv2.destroyAllWindows()

Результат:
Изображение

Я попробовал использовать другую матрицу камеры (с совершенно неправильным и придуманным фокусным расстоянием и т. д.), надеясь, что проблема была в неправильной калибровке, но результат меняется лишь при незначительной ориентации, и оси по-прежнему расположены в ~(0,6,0)

Я попробовал добавить больше точек (или разных точек), и результат не изменился. Кажется, что дальнейшие точки перепроецируются хуже.

Я также пытался изменить флаг вsolvePnP, только чтобы сделать все хуже.

Я проверил точки, на которые щелкнули вручную (Приложение 1), и они в порядке.
Я следовал этой рекомендуемой «базовой процедуре», которая по сути является тем же самым, что я сделал, но с нормализованными координатами (без ввода P=K в cv2.fisheye.undistortPoints и более поздние версии с использованиемsolvePnP с идентификационной матрицей (np.eye(3)) как K). Опять же, кажется, я получаю тот же результат.
Я пытался слепо следовать этому примеру с совершенно другой площадкой для игры в падель и другим расположением камеры; на этот раз беру точки на неискаженном изображении (которое я сохранил ранее), и снова все делаю не так (если хотите, я оставлю код ниже (приложение 2).
Я думаю, что мне чего-то не хватает, но я действительно не могу понять, чего именно. Я считаю, что это связано с другой моделью камеры, которая решает PnP, и используемым модулем «рыбий глаз», но чтение литературы, документации и использование различных скриптов, которые я нашел в Интернете, похоже, не помогают.
Спасибо всем, кто мог хоть немного помочь мне в решении этой проблемы... Мне действительно нужно выполнить эту работу для экзамена.
Еще раз спасибо!
Приложение 1
def debug_image_with_points(img, K, D, undistorted_points, objective_points):
map1, map2 = cv2.fisheye.initUndistortRectifyMap(
K, D, np.eye(3), K, img.shape[1::-1], cv2.CV_16SC2
)
undistorted_img = cv2.remap(
img, map1, map2,
interpolation=cv2.INTER_LINEAR,
borderMode=cv2.BORDER_CONSTANT
)

for i in range(len(undistorted_points)):
color = tuple(np.random.randint(0, 255, 3).tolist())
center = (int(undistorted_points[0][0]), int(undistorted_points[0][1]))
string = f"({objective_points[0]}, {objective_points[1]}, {objective_points[2]})"
cv2.circle(undistorted_img, center, 5, color, -1)
cv2.putText(
undistorted_img, string,
(center[0] + 5, center[1] - 5),
cv2.FONT_HERSHEY_SIMPLEX, 0.5, color, 1
)

cv2.imshow('Undistorted Image with Points', undistorted_img)
cv2.waitKey(0)
cv2.destroyAllWindows()

Приложение 2
import cv2
import numpy as np

img = cv2.imread("bg_BO-0001.jpg")
size = img.shape
image_points_2D = np.array([
[763, 226],
[1123, 222],
[1696, 601],
[203, 616],
[755, 112],
[1134, 112]
],
dtype="double",
)

figure_points_3D = np.array([
[ 0., 0., 0.],
[10., 0., 0.],
[10., 17., 0.],
[ 0., 17., 0.],
# [ 0., 10., 0.], # left of field
# [ 5., 10., 0.], # center of the field
# [10., 10., 0.], # right of field
[ 0., 0., 3.],
[10., 0., 3.],
])

distortion_coeffs = np.zeros((4, 1))
focal_length = size[1]
center = (size[1] / 2, size[0] / 2)
matrix_camera = np.array(
[[focal_length, 0, center[0]], [0, focal_length, center[1]], [0, 0, 1]],
dtype="double",
)
success, vector_rotation, vector_translation = cv2.solvePnP(
figure_points_3D, image_points_2D, matrix_camera, distortion_coeffs, flags=0
)
nose_end_point2D, jacobian = cv2.projectPoints(
np.array([(0.0, 0.0, 1000.0)]),
vector_rotation,
vector_translation,
matrix_camera,
distortion_coeffs,
)
for p in image_points_2D:
cv2.circle(img, (int(p[0]), int(p[1])), 3, (0, 0, 255), -1)
point1 = (int(image_points_2D[0][0]), int(image_points_2D[0][1]))

point2 = (int(nose_end_point2D[0][0][0]), int(nose_end_point2D[0][0][1]))

cv2.line(img, point1, point2, (255, 255, 255), 2)
# Draws the X (Red), Y (Green), and Z (Blue) axes in undistorted image
cv2.drawFrameAxes(img, matrix_camera, None, vector_rotation, vector_translation, 3)

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

Изменить
Вот содержимое «fishcam-fisheye.txt» (которое я получаю при калибровке шахматной доски) и функцию, которую я использую для чтения таких параметров:
fx = 1240.0
fy = 1366.0
cx = 950.0
cy = 496.0
k1 = -0.358
k2 = 0.046
k3 = -0.088
k4 = 0.102

def load_fisheye_params(path):
parameters = {}

with open(path, 'r') as file:
for line in file:
# Split the line by '=' to separate key and value
key, value = map(str.strip, line.strip().split('='))
parameters[key] = float(value) # Convert value to float

if not parameters:
raise ValueError("Error: No parameters found in the file.")

fx = parameters['fx']
fy = parameters['fy']
cx = parameters['cx']
cy = parameters['cy']
k1 = parameters['k1']
k2 = parameters['k2']
k3 = parameters['k3']
k4 = parameters['k4']

mtx = np.array([[fx, 0., cx],
[0., fy, cy],
[0., 0., 1.]])
dist = np.array([[k1], [k2], [k3], [k4]])

return mtx, dist

Вернуться в «Python»