Использование маркеров Aruco (OpenCV) для привязки позы камеры к SceneKit - PullRequest
0 голосов
/ 19 июня 2019

Попытка отобразить положение камеры из отслеживания Aruco обратно в положение камеры в SceneKit. Это почти работает, но отслеживание действительно нестабильно, и кажется, что преобразование в позу камеры SceneKit является неправильным, поскольку оно плавает над маркером в поле зрения камеры и перемещается, когда я перемещаю камеру. Кто-нибудь может увидеть, что я могу делать не так здесь, в преобразовании обратно в вектор смещения камеры и сцену векторов sceneKit?

@interface ArucoPayload : NSObject

@property BOOL valid; // Used to determine if the tracking was valid and hides the scenekit nodes if not
@property UIImage *image;
@property CGRect boardSize;
@property SCNVector4 rotationVector;
@property SCNVector3 translationVector;
@end

Mat rvec(3, 1, DataType<double>::type);
Mat tvec(3, 1, DataType<double>::type);

...
aruco::estimatePoseBoard(corners, markerIds, gridBoard, self.camMatrix, self.distCoeffs, rvec, tvec);
[self updateCameraProjection:payload withRotation:rvec andTranslation:tvec];
...

-(void) updateCameraProjection:(ArucoPayload *)payload withRotation:(Mat)rvec andTranslation:(Mat)tvec {

    cv::Mat RotX(3, 3, cv::DataType<double>::type);
    cv::setIdentity(RotX);
    RotX.at<double>(4) = -1;
    RotX.at<double>(8) = -1;

    cv::Mat R;
    cv::Rodrigues(rvec, R);

    R = R.t();
    Mat rvecConverted;
    Rodrigues(R, rvecConverted); 
    rvecConverted = RotX * rvecConverted;

    Mat tvecConverted = -R * tvec;
    tvecConverted = RotX * tvecConverted;

    payload.rotationVector = SCNVector4Make(rvecConverted.at<double>(0), rvecConverted.at<double>(1), rvecConverted.at<double>(2), norm(rvecConverted));
    payload.translationVector = SCNVector3Make(tvecConverted.at<double>(0), tvecConverted.at<double>(1), tvecConverted.at<double>(2));
}

func updateCameraPosition(payload:ArucoPayload) {

    if(payload.valid) {

        sceneView.scene?.rootNode.isHidden = false

        // Add nodes first time we get an updated position
        if(sceneView.scene?.rootNode.childNodes.count == 1) {

            // Add box node
            addBoxNode(to: sceneView, payload: payload)
        }

        cameraNode.rotation = payload.rotationVector
        cameraNode.position = payload.translationVector

    } else {

        sceneView.scene?.rootNode.isHidden = true
    }
}

Чертеж, выполненный в OpenCV, правильный, и мои ось и рамка вокруг платы Aruco точно отслеживаются, как это видно на видео.

Любая помощь очень ценится. Вот видео сцены. Желтый объект, который должен быть зафиксирован в позиции маркера, кажется очень нестабильным.

https://youtu.be/ZvKtZ3DNdrk

1 Ответ

0 голосов
/ 03 июля 2019

К сожалению, это не полный пример, поэтому неясно, где ошибка. В любом случае, кажется, что в том, что вы опубликовали, все еще может быть проблема, поэтому, возможно, мои разговоры из-за путаницы будут полезны.

RotX, кажется, предназначен для учета разностей осей между OpenCV (X вправо, Y вниз, Z in) и SceneKit (X вправо, Y up, Z out).

tvec и rvec представляют мировое происхождение относительно камеры OpenCV.

R.t() совпадает с R.inv() для ортонормированных вращений, поэтому R теперь является обратной матрицей исходного rvec через R = R.t().

То есть Mat tvecConverted = -R * tvec; (или, точнее, R * -tvec) - камера в мировых координатах Rodrigues(R, rvecConverted); кажется похожим преобразованием для фрейма мира для rvec.

Затем каждый умножается на RotX, чтобы взять координаты OpenCV в координаты SceneKit, и присваивается payload.

ОБНОВЛЕНИЕ: после обновления вашего кода мы видим, что значения, присвоенные payload, в конечном итоге присваиваются значениям cameraNode position и rotation. Так как SceneKit SCNCamera всегда указывает вдоль отрицательной оси Z своего родителя SCNNode с одинаковой ориентацией, расположение и ориентация родителя SCNNode позиционирует и ориентирует саму камеру. Это говорит о том, что tvecConverted и rvecConverted выше верны, так как SCNNode камеры, кажется, является родственным корневому узлу.

Однако все еще остается вопрос проецирования из пространства камеры ScenKit в пиксельное пространство дисплея, которого, похоже, нет в отрывке кода, который вы опубликовали. Я подозреваю, что это должно соответствовать внутренней матрице камеры и искажению, используемому при рисовании на стороне OpenCV, чтобы они правильно выровнялись.

На странице SCNCamera документов свойство projectionTransform описывается так:

Это преобразование выражает комбинацию всех Геометрические свойства камеры: тип проекции (перспектива или орфографический), поле зрения, пределы глубины и орфографический масштаб (если применимо). SceneKit использует это преобразование для преобразовать точки в координатном пространстве узла камеры в 2D-пространство рендерера при рендеринге и обработке событий.

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

Поскольку вы не опубликовали код, связанный с внутренними характеристиками камеры, неясно, является ли это проблемой или нет. Тем не менее, я подозреваю, что выравнивание встроенных функций между OpenCV и SceneKit решит проблему. Кроме того, преобразования SceneKit оцениваются по отношению к свойству pivot узла, поэтому вам следует быть осторожным, если вы его используете (например, для размещения SCNBox относительно его угла, а не его центра.)

...