How to put rotation- and scale invariant labels to three dimensional objects (motion-tracked insert effect)? I'm looking for an approach to introduce some informational objects to a 3D scene to be rendered. The effect is similar to motion-tracked inserts, you may know from various video productions. It would be nice to directly include these labels to the scene, rather than to do it in post. This would require kind of 2 dimensional objects that doesn't follow the laws of perspective (doesn't change size or rotation) while the camera moves. My thoughts brought me to putting empties to the objects to-be-labeled and parenting the actual texture planes to the camera. This is how I made the sample GIF. However I'm do not know how to translate the coordinates of the empties (in an automated way) to make the labels appear to be attached to empties. Maybe there is solution already that some of you already use.

Edit: important part set in bold. So not a duplicate of the mentioned other question.
Frame change handler

Cube is moving further away, Suzy is scaling up and down, cone is rotating thru. Camera rotated.
Proof of concept script.
For each object that I wish to label have added a text object (arbitrarily, could be an image plane) as a child, and given it a custom property named &quot;label&quot; to mark it as the label for its parent.
Theoretically this could be set up with drivers and or constraints, will however use a handler method instead.
By default a text object faces Z up. Will rotate it such that it faces the plane of the scene camera.  Have used the highest (largest Y coord in camera space) bounding box corner as the origin of the text object.  Can see the switch with rotating cone.
The scaling is using same method explained here, https://blender.stackexchange.com/a/107005/15543 where it is implemented with drivers.  Just as RIchard has divided by 10, have used a scale factor of 0.1.
import bpy
from mathutils import Vector, Matrix

scale_factor = 0.1

def label(scene, depsgraph):
    cam = scene.camera.evaluated_get(depsgraph)
    mw = cam.matrix_world
    mwi = mw.inverted()
    R = mw.to_3x3().normalized().to_4x4()
    labels = [o.evaluated_get(depsgraph) for o in scene.objects
            if &quot;label&quot; in o.keys()
            ]
    for label in labels:
        ob = label.parent
        omw = ob.matrix_world
        bbox = [mwi @ (omw @ Vector(b)) for b in ob.bound_box]
        bbox.sort(key=lambda v:v.y)

        M = R.copy()
        p = bbox.pop()
        M.translation = mw @ p
        S = Matrix.Diagonal(
            (scale_factor * -p.z,) * 3).to_4x4()

        label.matrix_world = M @ S
        
        
bpy.app.handlers.frame_change_post.clear()
bpy.app.handlers.frame_change_post.append(label)

Setting Up.
Here is a quick little script that adds a font object to each selected object as its child and assigns it a &quot;label&quot; custom property to mark it as a label.
import bpy
context = bpy.context

for ob in context.selected_objects:
    text = bpy.data.curves.new(&quot;label&quot;, type='FONT')
    text.body = ob.name
    label = bpy.data.objects.new(&quot;Label&quot;, text)
    label.parent = ob
    label.matrix_world = ob.matrix_world
    label[&quot;label&quot;] = ob.name
    context.collection.objects.link(label)

Empties
As suggested by @clutch-lemon here is similar, but adding an empty as a child of each selected object.
import bpy
context = bpy.context

for ob in context.selected_objects:

    label = bpy.data.objects.new(&quot;Label&quot;, None)
    label.parent = ob
    label.matrix_world = ob.matrix_world
    label[&quot;label&quot;] = ob.name
    context.collection.objects.link(label)