diff --git a/get_proj.py b/get_proj.py index acdcad3..1c6003d 100644 --- a/get_proj.py +++ b/get_proj.py @@ -10,45 +10,59 @@ import numpy as np # 3x4 P matrix from Blender camera #--------------------------------------------------------------- +# BKE_camera_sensor_size +def get_sensor_size(sensor_fit, sensor_x, sensor_y): + if sensor_fit == 'VERTICAL': + return sensor_y + return sensor_x + +# BKE_camera_sensor_fit +def get_sensor_fit(sensor_fit, size_x, size_y): + if sensor_fit == 'AUTO': + if size_x >= size_y: + return 'HORIZONTAL' + else: + return 'VERTICAL' + return sensor_fit + # Build intrinsic camera parameters from Blender camera data # # See notes on this in # blender.stackexchange.com/questions/15102/what-is-blenders-camera-projection-matrix-model +# as well as +# https://blender.stackexchange.com/a/120063/3581 def get_calibration_matrix_K_from_blender(camd): - f_in_mm = camd.lens + if camd.type != 'PERSP': + raise ValueError('Non-perspective cameras not supported') scene = bpy.context.scene - resolution_x_in_px = scene.render.resolution_x - resolution_y_in_px = scene.render.resolution_y + f_in_mm = camd.lens scale = scene.render.resolution_percentage / 100 - sensor_width_in_mm = camd.sensor_width - sensor_height_in_mm = camd.sensor_height - pixel_aspect_ratio = scene.render.pixel_aspect_x / scene.render.pixel_aspect_y - if (camd.sensor_fit == 'VERTICAL'): - # the sensor height is fixed (sensor fit is horizontal), - # the sensor width is effectively changed with the pixel aspect ratio - s_u = resolution_x_in_px * scale / sensor_width_in_mm / pixel_aspect_ratio - s_v = resolution_y_in_px * scale / sensor_height_in_mm - else: # 'HORIZONTAL' and 'AUTO' - # the sensor width is fixed (sensor fit is horizontal), - # the sensor height is effectively changed with the pixel aspect ratio - pixel_aspect_ratio = scene.render.pixel_aspect_x / scene.render.pixel_aspect_y - s_u = resolution_x_in_px * scale / sensor_width_in_mm - s_v = resolution_y_in_px * scale * pixel_aspect_ratio / sensor_height_in_mm - + resolution_x_in_px = scale * scene.render.resolution_x + resolution_y_in_px = scale * scene.render.resolution_y + sensor_size_in_mm = get_sensor_size(camd.sensor_fit, camd.sensor_width, camd.sensor_height) + sensor_fit = get_sensor_fit( + camd.sensor_fit, + scene.render.pixel_aspect_x * resolution_x_in_px, + scene.render.pixel_aspect_y * resolution_y_in_px + ) + pixel_aspect_ratio = scene.render.pixel_aspect_y / scene.render.pixel_aspect_x + if sensor_fit == 'HORIZONTAL': + view_fac_in_px = resolution_x_in_px + else: + view_fac_in_px = pixel_aspect_ratio * resolution_y_in_px + pixel_size_mm_per_px = sensor_size_in_mm / f_in_mm / view_fac_in_px + s_u = 1 / pixel_size_mm_per_px + s_v = 1 / pixel_size_mm_per_px / pixel_aspect_ratio # Parameters of intrinsic calibration matrix K - alpha_u = f_in_mm * s_u - alpha_v = f_in_mm * s_v - u_0 = resolution_x_in_px * scale / 2 - v_0 = resolution_y_in_px * scale / 2 + u_0 = resolution_x_in_px / 2 - camd.shift_x * view_fac_in_px + v_0 = resolution_y_in_px / 2 + camd.shift_y * view_fac_in_px / pixel_aspect_ratio skew = 0 # only use rectangular pixels - K = Matrix(( - (alpha_u, skew, u_0), - ( 0, alpha_v, v_0), - ( 0, 0, 1) - )) - + K = Matrix( + ((s_u, skew, u_0), + ( 0, s_v, v_0), + ( 0, 0, 1))) return K # Returns camera rotation and translation matrices from Blender. @@ -66,29 +80,27 @@ def get_calibration_matrix_K_from_blender(camd): # used in digital images) # - right-handed: positive z look-at direction def get_3x4_RT_matrix_from_blender(cam): -# # bcam stands for blender camera + # bcam stands for blender camera R_bcam2cv = Matrix( ((1, 0, 0), - (0, -1, 0), - (0, 0, -1))) + (0, -1, 0), + (0, 0, -1))) # Transpose since the rotation is object rotation, # and we want coordinate rotation # R_world2bcam = cam.rotation_euler.to_matrix().transposed() - # T_world2bcam = -1*R_world2bcam * location + # T_world2bcam = -1*R_world2bcam @ location # # Use matrix_world instead to account for all constraints location, rotation = cam.matrix_world.decompose()[0:2] R_world2bcam = rotation.to_matrix().transposed() # Convert camera location to translation vector used in coordinate changes - # T_world2bcam = -1*R_world2bcam*cam.location + # T_world2bcam = -1*R_world2bcam @ cam.location # Use location from matrix_world to account for constraints: T_world2bcam = -1*R_world2bcam @ location # Build the coordinate transform matrix from world to computer vision camera - # NOTE: Use * instead of @ here for older versions of Blender - # TODO: detect Blender version R_world2cv = R_bcam2cv@R_world2bcam T_world2cv = R_bcam2cv@T_world2bcam @@ -97,7 +109,7 @@ def get_3x4_RT_matrix_from_blender(cam): R_world2cv[0][:] + (T_world2cv[0],), R_world2cv[1][:] + (T_world2cv[1],), R_world2cv[2][:] + (T_world2cv[2],) - )) + )) return RT def get_3x4_P_matrix_from_blender(cam): @@ -129,4 +141,4 @@ bpy.app.handlers.frame_change_post.clear() # add handler bpy.app.handlers.render_init.append(setup_script) -bpy.app.handlers.frame_change_pre.append(run_script) +bpy.app.handlers.frame_change_post.append(run_script)