How to put rotation- and scale invariant labels to three dimensional objects (motion-tracked insert effect)?
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)