Starting from the answer of ioquatix (Thanks), I elaborated a more complete and self-contained solution.
The following function takes a camera, a point, and a render, and computes the 2d homogeneous coordinates.
An example of conversion to Pixel coordinates follows in the main code.
2.79
# Small code and example to Project a 3D point into 2D pixel coordinates.
# Fabrizio Nunnari <[email protected]>
#
# Related links:
# - https://blender.stackexchange.com/questions/16472/how-can-i-get-the-cameras-projection-matrix
import bpy
from mathutils import Vector
def project_3d_point(camera: bpy.types.Object,
p: Vector,
render: bpy.types.RenderSettings = bpy.context.scene.render) -> Vector:
"""
Given a camera and its projection matrix M;
given p, a 3d point to project:
Compute P’ = M * P
P’= (x’, y’, z’, w')
Ignore z'
Normalize in:
x’’ = x’ / w’
y’’ = y’ / w’
x’’ is the screen coordinate in normalised range -1 (left) +1 (right)
y’’ is the screen coordinate in normalised range -1 (bottom) +1 (top)
:param camera: The camera for which we want the projection
:param p: The 3D point to project
:param render: The render settings associated to the scene.
:return: The 2D projected point in normalized range [-1, 1] (left to right, bottom to top)
"""
if camera.type != 'CAMERA':
raise Exception("Object {} is not a camera.".format(camera.name))
if len(p) != 3:
raise Exception("Vector {} is not three-dimensional".format(p))
# Get the two components to calculate M
modelview_matrix = camera.matrix_world.inverted()
projection_matrix = camera.calc_matrix_camera(
render.resolution_x,
render.resolution_y,
render.pixel_aspect_x,
render.pixel_aspect_y,
)
# print(projection_matrix * modelview_matrix)
# Compute P’ = M * P
p1 = projection_matrix * modelview_matrix * Vector((p.x, p.y, p.z, 1))
# Normalize in: x’’ = x’ / w’, y’’ = y’ / w’
p2 = Vector(((p1.x/p1.w, p1.y/p1.w)))
return p2
#
# Test
camera = bpy.data.objects['Camera'] # or bpy.context.active_object
render = bpy.context.scene.render
P = Vector((-0.002170146, 0.409979939, 0.162410125))
print("Projecting point {} for camera '{:s}' into resolution {:d}x{:d}..."
.format(P, camera.name, render.resolution_x, render.resolution_y))
proj_p = project_3d_point(camera=camera, p=P, render=render)
print("Projected point (homogeneous coords): {}.".format(proj_p))
proj_p_pixels = Vector(((render.resolution_x-1) * (proj_p.x + 1) / 2, (render.resolution_y - 1) * (proj_p.y - 1) / (-2)))
print("Projected point (pixel coords): {}.".format(proj_p_pixels))
print("Done.")
2.8
import bpy
from mathutils import Vector
def project_3d_point(camera: bpy.types.Object,
p: Vector,
render: bpy.types.RenderSettings = bpy.context.scene.render) -> Vector:
"""
Given a camera and its projection matrix M;
given p, a 3d point to project:
Compute P’ = M * P
P’= (x’, y’, z’, w')
Ignore z'
Normalize in:
x’’ = x’ / w’
y’’ = y’ / w’
x’’ is the screen coordinate in normalised range -1 (left) +1 (right)
y’’ is the screen coordinate in normalised range -1 (bottom) +1 (top)
:param camera: The camera for which we want the projection
:param p: The 3D point to project
:param render: The render settings associated to the scene.
:return: The 2D projected point in normalized range [-1, 1] (left to right, bottom to top)
"""
if camera.type != 'CAMERA':
raise Exception("Object {} is not a camera.".format(camera.name))
if len(p) != 3:
raise Exception("Vector {} is not three-dimensional".format(p))
# Get the two components to calculate M
modelview_matrix = camera.matrix_world.inverted()
projection_matrix = camera.calc_matrix_camera(
bpy.data.scenes["Scene"].view_layers["View Layer"].depsgraph,
x = render.resolution_x,
y = render.resolution_y,
scale_x = render.pixel_aspect_x,
scale_y = render.pixel_aspect_y,
)
# print(projection_matrix * modelview_matrix)
# Compute P’ = M * P
p1 = projection_matrix @ modelview_matrix @ Vector((p.x, p.y, p.z, 1))
# Normalize in: x’’ = x’ / w’, y’’ = y’ / w’
p2 = Vector(((p1.x/p1.w, p1.y/p1.w)))
return p2
camera = bpy.data.objects['Camera'] # or bpy.context.active_object
render = bpy.context.scene.render
P = Vector((-0.002170146, 0.409979939, 0.162410125))
print("Projecting point {} for camera '{:s}' into resolution {:d}x{:d}..."
.format(P, camera.name, render.resolution_x, render.resolution_y))
proj_p = project_3d_point(camera=camera, p=P, render=render)
print("Projected point (homogeneous coords): {}.".format(proj_p))
proj_p_pixels = Vector(((render.resolution_x-1) * (proj_p.x + 1) / 2, (render.resolution_y - 1) * (proj_p.y - 1) / (-2)))
print("Projected point (pixel coords): {}.".format(proj_p_pixels))
print("Done.")