Я обнаружил напечатанный маркер Aruco, используя opencv 3.2:
aruco::estimatePoseSingleMarkers(corners, markerLength, camMatrix, distCoeffs, rvecs,tvecs);
это возвращает вектор перемещения и вращения для маркера. Что мне нужно, это 3D-координаты для каждого угла маркера.
Поскольку я знаю длину маркера, я мог бы сделать что-то вроде
corner1 = tvecs[0] - markerlength /2;
corner2 = tvecs[0] + markerlength /2;
….
Но есть ли лучший способ? Или существующая функция?
Подводя итог, я имею:
3d точка в центре 2-го квадрата.
длина сторон этого квадрата.
значение вращения квадрата.
Как я могу найти 3D-координаты углов?
Во-первых, давайте предположим, что у нас есть только один маркер с side = 2 * half_side
,
Во-вторых, aruco::detectMarker
возвращает относительное положение камеры в мире маркера. Таким образом, я предполагаю, что вы ищете координаты углов в мире камеры.
Тогда в пространстве маркера:
[ half_side ] [ 0 ]
E = [ 0 ], F = [ half_side ]
[ 0 ] [ 0 ]
где центр O
квадрат имеет координату tvec
(в мире камеры) и вращающийся коврик маркера rot_mat
вычисляется cv::Rodrigues(rvec,rot_mat)
,
Теперь, используя пинхол модель камеры, отношение между координатами точки P
в мире кулачков и маркеров это:
[P_x_cam] [P_x_marker]
[P_y_cam] = rot_mat * [P_y_marker] + tvec
[P_z_cam] [P_z_marker]
например, центр O
, который [0,0,0]
в мире маркеров tvec
в мире кулачка.
Итак, координаты E
в мире кулачков есть:
[E_x_cam] [half_side]
|E_y_cam| = rot_mat * | 0 | + tvec
[E_z_cam] [ 0 ]
Волшебно, это сумма rot_mat
первый столбец, умноженный на half_size
а также tvec
, Так же,
каудинаты F
является rot_mat
второй столбец, умноженный на half_size
а также tvec
,
Теперь углы можно вычислить, например,
C - O = (E - O) + (F - O), B - O = (E - O) - (F - O)
где E-O
это точно rot_mat
первый столбец, умноженный на half_size
,
Имея все это в виду, мы можем составить функцию:
vector<Point3f> getCornersInCameraWorld(double side, Vec3d rvec, Vec3d tvec){
double half_side = side/2;// compute rot_mat
Mat rot_mat;
Rodrigues(rvec, rot_mat);
// transpose of rot_mat for easy columns extraction
Mat rot_mat_t = rot_mat.t();
// the two E-O and F-O vectors
double * tmp = rot_mat_t.ptr<double>(0);
Point3f camWorldE(tmp[0]*half_side,
tmp[1]*half_side,
tmp[2]*half_side);
tmp = rot_mat_t.ptr<double>(1);
Point3f camWorldF(tmp[0]*half_side,
tmp[1]*half_side,
tmp[2]*half_side);
// convert tvec to point
Point3f tvec_3f(tvec[0], tvec[1], tvec[2]);
// return vector:
vector<Point3f> ret(4,tvec_3f);
ret[0] += camWorldE + camWorldF;
ret[1] += -camWorldE + camWorldF;
ret[2] += -camWorldE - camWorldF;
ret[3] += camWorldE - camWorldF;
return ret;
}
Примечание 1: я ненавижу, что ТАК не имеет MathJax
Примечание 2: должна быть более быстрая реализация, о которой я не знаю.
Основываясь на ответе @ Quang, C # код для преобразования любой точки в координаты камеры. Конечно это нужно R
а также t
векторы, поэтому вам понадобится маркер, чтобы их получить.
private Point3d GetWorldPoint(Point3d input, Vec3d rvec, Vec3d tvec)
{
var rot_mat = new Mat();
Cv2.Rodrigues(MatOfDouble.FromArray(rvec.Item0, rvec.Item1, rvec.Item2), rot_mat);
var pointProject = (rot_mat * MatOfDouble.FromArray(input.X, input.Y, input.Z)).ToMat();
return tvec + new Point3d(pointProject.Get<double>(0, 0), pointProject.Get<double>(0, 1), pointProject.Get<double>(0, 2));
}