根据 Blender 中 SolvePnP 的矢量校正相机位置

问题描述 投票:0回答:1

我刚刚学习一些 OpenCV,并在 Blender 中玩。

作为测试,我试图重新创建我在另一篇文章中看到的东西,但在搅拌机内。我想重新创建一个我创建的观察飞机的相机。我想从中创建图像的图像是: 在此输入图片描述

这是我迄今为止在 Blender 中的代码:

import bpy
import cv2
import numpy as np
from mathutils import Matrix, Vector
from scipy.spatial.transform import Rotation

def focalMM_to_focalPixel(focalMM, sensorWidth, imageWidth):
    pixelPitch = sensorWidth / imageWidth
    return focalMM / pixelPitch

# Read Image
im = cv2.imread("assets/cameraView.jpg")

imageWidth = 1920
imageHeight = 1080
imageSize = [imageWidth, imageHeight]

points_2D = np.array([
    (949, 49),
    (1415, 45),
    (962, 913),
    (1398, 977)
], dtype="double")

points_3D = np.array([
    (-.30208, -1.8218, 8.3037),
    (-.30208, -1.8303, 8.3037),
    (-.30208, -1.8218, 1.381),
    (-.30208, -1.8303, 1.381)
])

focalLengthMM = 50
sensorWidth = 36

fLength = focalMM_to_focalPixel(focalLengthMM, sensorWidth, imageWidth)
print("focalLengthPixel", fLength)

K = np.array([
    [fLength, 0, imageWidth/2],
    [0, fLength, imageHeight/2],
    [0, 0, 1]
])
distCoeffs = np.zeros((5, 1))

success, rvecs, tvecs = cv2.solvePnP(points_3D, points_2D, K, distCoeffs, flags=cv2.SOLVEPNP_ITERATIVE)

np_rodrigues = np.asarray(rvecs[:,:], np.float64)
rmat = cv2.Rodrigues(np_rodrigues)[0]
camera_position = -np.matrix(rmat).T @ np.matrix(tvecs)

# Test the solvePnP by projecting the 3D Points to camera
projPoints = cv2.projectPoints(points_3D, rvecs, tvecs, K, distCoeffs)[0]

for p in points_2D:
    cv2.circle(im, (int(p[0]), int(p[1])), 3, (0, 255, 0), -1)

for p in projPoints:
    cv2.circle(im, (int(p[0][0]), int(p[0][1])), 3, (255, 0, 0), -1)

# cv2.imshow("image", im)
# cv2.waitKey(0)

r = Rotation.from_rotvec([rvecs[0][0], rvecs[1][0], rvecs[2][0]])
rot = r.as_euler('xyz', degrees=True)

tx = camera_position[0][0]
ty = camera_position[1][0]
tz = camera_position[2][0]

rx = round(180 - rot[0], 5)
ry = round(rot[1], 5)
rz = round(rot[2], 5)

# Creating the camera in Blender
bpy.ops.object.camera_add()
camera = bpy.context.object
camera.location = (tx, ty, tz)

# Convert rotation from degrees to radians for Blender
camera.rotation_euler = (np.radians(rx), np.radians(ry), np.radians(rz))

# Setting the camera parameters
camera.data.lens = focalLengthMM
camera.data.sensor_width = sensorWidth 
camera.data.sensor_height = sensorWidth * (imageHeight / imageWidth)
camera.data.shift_x = (imageWidth / 2 - K[0, 2]) / imageWidth
camera.data.shift_y = (imageHeight / 2 - K[1, 2]) / imageHeight

我对如何实现正确的旋转和平移感到困惑,并且我确信我做错了什么。

我得到的值让我的相机进入了一个奇怪的位置。下图突出显示的相机是生成的相机与原始相机。

在此输入图片描述

我感谢任何帮助。

python opencv computer-vision blender opencv-solvepnp
1个回答
0
投票

我能够弄清楚如何在 Blender 中做到这一点。我将在下面发布我的代码。要去检查并清理它,但这完成了我打算做的事情。

import bpy
import cv2
import numpy as np
from scipy.spatial.transform import Rotation
from mathutils import Matrix, Vector
import logging

# Configure logging
logging.basicConfig(level=logging.INFO)
logger = logging.getLogger(__name__)

def focalMM_to_focalPixel(focalMM, sensorWidth, imageWidth):
    pixelPitch = sensorWidth / imageWidth
    return focalMM / pixelPitch

def load_image(image_path):
    try:
        im = cv2.imread(image_path)
        if im is None:
            raise FileNotFoundError(f"Image not found at path: {image_path}")
        return im
    except Exception as e:
        logger.error(f"Error loading image: {e}")
        raise

def calculate_camera_matrix(focalLengthMM, sensorWidthMM, imageWidth, imageHeight):
    focalLengthPixels = focalMM_to_focalPixel(focalLengthMM, sensorWidthMM, imageWidth)
    K = np.array([
        [focalLengthPixels, 0, imageWidth / 2],
        [0, focalLengthPixels, imageHeight / 2],
        [0, 0, 1]
    ], dtype='double')
    return K

def main():
    image_path = r"P:\Projects\CornerDetection\assets\BlenderTestRender_V2.png"
    points_2D = np.array([
        (683, 59),
        (1201, 49),
        (1210, 978),
        (716, 1012)
    ], dtype="double")
    
    points_3D = np.array([
       (-0.302079, -1.82178, 8.30375),
        (-0.302079, 1.83035, 8.30375),
        (-0.302079, 1.83035, 1.38104),
        (-0.302079, -1.82178, 1.38104)
    ], dtype="double")
    
    focalLengthMM = 50
    sensorWidthMM = 36
    sensorHeightMM = 20.25

    im = load_image(image_path)
    imageHeight, imageWidth = im.shape[:2]

    # Calculate intrinsic matrix using the provided function
    K = calculate_camera_matrix(focalLengthMM, sensorWidthMM, imageWidth, imageHeight)
    distCoeffs = np.zeros((5, 1))  # Assuming no lens distortion

    # Solve PnP
    ret, rvecs, tvecs = cv2.solvePnP(points_3D, points_2D, K, distCoeffs)
    rmat, _ = cv2.Rodrigues(rvecs)
    
    projPoints, _ = cv2.projectPoints(points_3D, rvecs, tvecs, K, distCoeffs)

    for p in points_2D:
        cv2.circle(im, (int(p[0]), int(p[1])), 3, (0, 255, 0), -1)

    for p in projPoints:
        cv2.circle(im, (int(p[0][0]), int(p[0][1])), 2, (255, 0, 0), -1)
            
    R_world2cv = Matrix(rmat.tolist())
    T_world2cv = Vector(tvecs.flatten())
        
    R_bcam2cv = Matrix(
        ((1, 0, 0),
         (0, -1, 0),
         (0, 0, -1)))
            
    R_cv2world = R_world2cv.transposed()
    rot = R_cv2world @ R_bcam2cv
    loc = R_cv2world @ -T_world2cv

    cv2.imshow("image", im)
    cv2.waitKey(0)
    cv2.destroyAllWindows()
        
    # Ensure the context is properly set
    bpy.context.scene.camera = None
    bpy.ops.object.camera_add()

    # Set camera intrinsics, extrinsics and background
    cam = bpy.context.object
    camd = cam.data
    camd.type = 'PERSP'
    camd.lens = focalLengthMM
    camd.sensor_width = sensorWidthMM
    camd.sensor_height = sensorHeightMM
    render_size = [
        bpy.context.scene.render.pixel_aspect_x * bpy.context.scene.render.resolution_x,
        bpy.context.scene.render.pixel_aspect_y * bpy.context.scene.render.resolution_y
    ]
    camd.sensor_fit = 'HORIZONTAL' if render_size[0] / render_size[1] <= imageWidth / imageHeight else 'VERTICAL'
    refsize = imageWidth if render_size[0] / render_size[1] <= imageWidth / imageHeight else imageHeight
    camd.shift_x = (imageWidth * 0.5 - K[0, 2]) / refsize
    camd.shift_y = (imageHeight * 0.5 - K[1, 2]) / refsize
    
    camd.show_background_images = True
    if not camd.background_images:
        bg = camd.background_images.new()
    else:
        bg = camd.background_images[0]
    bg.source = 'IMAGE'
    bg.image = bpy.data.images.load(image_path)
    bg.frame_method = 'FIT'
    bg.display_depth = 'FRONT'

    cam.matrix_world = Matrix.Translation(loc) @ rot.to_4x4()
    bpy.context.scene.camera = cam

if __name__ == "__main__":
    main()
© www.soinside.com 2019 - 2024. All rights reserved.