opencv - 使用Aruco标记(OpenCV)将相机姿势映射到SceneKit

标签 opencv scenekit aruco

尝试将摄像机位置从Aruco跟踪映射回SceneKit中的摄像机位置。它几乎可以正常工作,但是跟踪确实不稳定,并且似乎无法转换为SceneKit相机姿势,因为它漂浮在相机 View 中的标记上并随着我移动相机而移动。在转换回sceneKit摄像机平移和位置 vector 的转换中,谁能看到我在这里做错的事情?

@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

相机校准:
// Wait until we have captured enough frames
if(self.numberOfFramesForCalibration == 0) {

    NSLog(@"Starting calibration with 20 images");

    vector< vector< Point2f > > allCornersConcatenated;
    vector< int > allIdsConcatenated;
    vector< int > markerCounterPerFrame;
    Mat cameraMatrix, distCoeffs;
    vector< Mat > rvecs, tvecs;
    double repError;
    int calibrationFlags = 0;

    // prepare data for calibration
    markerCounterPerFrame.reserve(allCorners.size());
    for(unsigned int i = 0; i < allCorners.size(); i++) {
        markerCounterPerFrame.push_back((int)allCorners[i].size());
        for(unsigned int j = 0; j < allCorners[i].size(); j++) {
            allCornersConcatenated.push_back(allCorners[i][j]);
            allIdsConcatenated.push_back(allIds[i][j]);
        }
    }

    // calibrate camera
    repError = aruco::calibrateCameraAruco(allCornersConcatenated, allIdsConcatenated,
                                           markerCounterPerFrame, self.data.board, imgSize, cameraMatrix,
                                           distCoeffs, rvecs, tvecs);

    bool saveOk = [self saveCameraParams:imgSize aspect:1 flags:calibrationFlags matrix:cameraMatrix coeff:distCoeffs avgErr:repError];
    if(saveOk) {

        self.calibrationRequired = false;

    }
}   

最佳答案

不幸的是,这不是一个完整的示例,因此不清楚错误在哪里。无论如何,您发布的内容似乎仍然有可能是问题,因此也许我的困惑中的谈话会有所帮助。
RotX似乎旨在解决OpenCV(右侧X,向下Y,向内Z)和SceneKit(右侧X,向上Y,向外Z)之间的轴差异。
tvecrvec代表相对于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 positionrotation值。由于SceneKit的SCNCamera始终沿其父SCNNode的负Z轴以相同的方向指向,因此要定位和定向父SCNNode的位置并使其自身定向。这表明上面的tvecConvertedrvecConverted是正确的,因为相机的SCNNode似乎是根节点的父级。

但是,仍然存在从ScenKit相机空间投影到显示器像素空间的问题,这似乎不在您发布的代码摘录中。我怀疑这需要匹配在OpenCV侧绘制时使用的固有相机矩阵和失真,以使其正确对齐。

SCNCamera docs page中, projectionTransform property的描述如下:

This transformation expresses the combination of all the camera’s geometric properties: projection type (perspective or orthographic), field of view, depth limits, and orthographic scale (if applicable). SceneKit uses this transformation to convert points in the camera node’s coordinate space to the renderer’s 2D space when rendering and processing events.

You can use this transformation directly if your app needs to convert between view and renderer coordinates for other purposes. Alternatively, if you compute your own projection transform matrix, you can set this property to override the transformation synthesized from the camera’s geometric properties.



由于您尚未发布与相机内部特性相关的代码,因此尚不清楚这是否是问题所在。但是,我怀疑在OpenCV和SceneKit之间对齐内在函数将解决此问题。同样,SceneKit变换是相对于节点的pivot属性进行评估的,因此如果要使用该属性,则需要格外小心(例如,将SCNBox相对于其角而不是其中心放置)。

关于opencv - 使用Aruco标记(OpenCV)将相机姿势映射到SceneKit,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/56667057/

相关文章:

c - 如何分析/识别紧密处理循环中的缓慢步骤?

python - 如何在 OpenKinect 中检测手势(使用 python 包装器)

swift - KVO 在 SCNNode 上绑定(bind) CGFloat

ios - 如何在 iOS SceneKit 中使用具有不同场景图的 DAE 文件

ios - SCNParticleSystem 从文档目录加载

python - 使用 Aruco 标记获取相机位置和旋转

c++ - 在 iOS 上使用 aruco 构建 opencv 时出现 "Functional-style cast from id to double is not allowed"

opencv - aruco::detectMarkers 没有找到标记的真正边缘

c++ - opencv 中的 StereoBM 类是否对输入图像或帧进行校正?

c++ - FastCV 腐 eclipse /膨胀参数