Estimativa de pose da câmera a partir da homografia ou com a função resolvePnP ()

Estou tentando construir uma cena de realidade aumentada estática sobre uma foto com 4 correspondências definidas entre pontos coplanares em um plano e imagem.

Aqui está um fluxo passo a passo:

O usuário adiciona uma imagem usando a câmera do dispositivo. Vamos supor que ele contenha um retângulo capturado com alguma perspectiva.O usuário define o tamanho físico do retângulo, que fica no plano horizontal (YOZ em termos de SceneKit). Vamos supor que o centro seja a origem do mundo (0, 0, 0), para que possamos encontrar facilmente (x, y, z) para cada canto.O usuário define coordenadas uv no sistema de coordenadas da imagem para cada canto do retângulo.A cena SceneKit é criada com um retângulo do mesmo tamanho e visível na mesma perspectiva.Outros nós podem ser adicionados e movidos na cena.

Também medi a posição da câmera do iPhone em relação ao centro do papel A4. Portanto, para esta foto, a posição foi (0, 14, 42,5) medida em cm. Além disso, meu iPhone estava ligeiramente inclinado para a mesa (5-10 graus)

Usando esses dados que configureiSCNCamera para obter a perspectiva desejada do plano azul na terceira imagem:

let camera = SCNCamera()
camera.xFov = 66
camera.zFar = 1000
camera.zNear = 0.01

cameraNode.camera = camera
cameraAngle = -7 * CGFloat.pi / 180
cameraNode.rotation = SCNVector4(x: 1, y: 0, z: 0, w: Float(cameraAngle))
cameraNode.position = SCNVector3(x: 0, y: 14, z: 42.5)

Isso me dará uma referência para comparar meu resultado.

Para criar o AR com o SceneKit, preciso:

Ajuste o valor da câmera do SCNCamera, para que ele corresponda ao valor da câmera real.Calcule a posição e a rotação do nó da câmera usando 4 densidades correspondentes entre os pontos do mundo (x, 0, z) e os pontos da imagem (u, v)

H - homografia;K - matriz intrínseca;[R | t] - Matriz extrínseca

Tentei duas abordagens para encontrar a matriz de transformação para a câmera: usando o resolvPnP do OpenCV e o cálculo manual da homografia com base em 4 pontos coplanares.

Abordagem manual:

1. Descubra a homografia

Este passo é realizado com sucesso, pois as coordenadas UV de origem mundial parecem estar corretas.

2. Matriz intrínseca

Para obter a matriz intrínseca do iPhone 6, useiesta aplicativo, que me deu o seguinte resultado de 100 imagens de resolução 640 * 480:

Supondo que a imagem de entrada tenha uma proporção de aspecto 4: 3, posso dimensionar a matriz acima, dependendo da resolução

Não tenho certeza, mas parece um problema em potencial aqui. Eu usei cv :: calibrationMatrixValues para verificar fovx para a matriz intrínseca calculada e o resultado foi ~ 50 °, enquanto deve estar próximo de 60 °.

3. Matriz de pose da câmera

func findCameraPose(homography h: matrix_float3x3, size: CGSize) -> matrix_float4x3? {
    guard let intrinsic = intrinsicMatrix(imageSize: size),
        let intrinsicInverse = intrinsic.inverse else { return nil }

    let l1 = 1.0 / (intrinsicInverse * h.columns.0).norm
    let l2 = 1.0 / (intrinsicInverse * h.columns.1).norm
    let l3 = (l1+l2)/2

    let r1 = l1 * (intrinsicInverse * h.columns.0)
    let r2 = l2 * (intrinsicInverse * h.columns.1)
    let r3 = cross(r1, r2)

    let t = l3 * (intrinsicInverse * h.columns.2)

    return matrix_float4x3(columns: (r1, r2, r3, t))
}

Resultado:

Como medi a posição e a orientação aproximadas dessa imagem em particular, conheço a matriz de transformação, que daria o resultado esperado e é bem diferente:

Também estou um pouco preocupado com o elemento 2-3 da matriz de rotação de referência, que é -9,1, embora deva estar próximo de zero, pois há uma rotação muito leve.

Abordagem OpenCV:

Existe umresolvePnP função no OpenCV para esse tipo de problema, então tentei usá-lo em vez de reinventar a roda.

OpenCV em Objective-C ++:

typedef struct CameraPose {
    SCNVector4 rotationVector;
    SCNVector3 translationVector; 
} CameraPose;

+ (CameraPose)findCameraPose: (NSArray<NSValue *> *) objectPoints imagePoints: (NSArray<NSValue *> *) imagePoints size: (CGSize) size {

    vector<Point3f> cvObjectPoints = [self convertObjectPoints:objectPoints];
    vector<Point2f> cvImagePoints = [self convertImagePoints:imagePoints withSize: size];

    cv::Mat distCoeffs(4,1,cv::DataType<double>::type, 0.0);
    cv::Mat rvec(3,1,cv::DataType<double>::type);
    cv::Mat tvec(3,1,cv::DataType<double>::type);
    cv::Mat cameraMatrix = [self intrinsicMatrixWithImageSize: size];

    cv::solvePnP(cvObjectPoints, cvImagePoints, cameraMatrix, distCoeffs, rvec, tvec);

    SCNVector4 rotationVector = SCNVector4Make(rvec.at<double>(0), rvec.at<double>(1), rvec.at<double>(2), norm(rvec));
    SCNVector3 translationVector = SCNVector3Make(tvec.at<double>(0), tvec.at<double>(1), tvec.at<double>(2));
    CameraPose result = CameraPose{rotationVector, translationVector};

    return result;
}

+ (vector<Point2f>) convertImagePoints: (NSArray<NSValue *> *) array withSize: (CGSize) size {
    vector<Point2f> points;
    for (NSValue * value in array) {
        CGPoint point = [value CGPointValue];
        points.push_back(Point2f(point.x - size.width/2, point.y - size.height/2));
    }
    return points;
}

+ (vector<Point3f>) convertObjectPoints: (NSArray<NSValue *> *) array {
    vector<Point3f> points;
    for (NSValue * value in array) {
        CGPoint point = [value CGPointValue];
        points.push_back(Point3f(point.x, 0.0, -point.y));
    }
    return points;
}

+ (cv::Mat) intrinsicMatrixWithImageSize: (CGSize) imageSize {
    double f = 0.84 * max(imageSize.width, imageSize.height);
    Mat result(3,3,cv::DataType<double>::type);
    cv::setIdentity(result);
    result.at<double>(0) = f;
    result.at<double>(4) = f;
    return result;
}

Uso em Swift:

func testSolvePnP() {
    let source = modelPoints().map { NSValue(cgPoint: $0) }
    let destination = perspectivePicker.currentPerspective.map { NSValue(cgPoint: $0)}

    let cameraPose = CameraPoseDetector.findCameraPose(source, imagePoints: destination, size: backgroundImageView.size);    
    cameraNode.rotation = cameraPose.rotationVector
    cameraNode.position = cameraPose.translationVector
}

Resultado:

O resultado é melhor, mas está longe das minhas expectativas.

Também tentei outras coisas:

Essa questão é muito semelhante, embora eu não entenda como a resposta aceita está funcionando sem intrínsecas.decomposeHomographyMat também não me deu o resultado que eu esperava

Estou realmente preso a esse problema, portanto qualquer ajuda seria muito apreciada.

questionAnswers(1)

yourAnswerToTheQuestion