1
$\begingroup$

I want to get a 2D bounding box for all objects in my scene, via Python (using the BPY module). I can do this using this code. I have up to 100 objects in my scene. Rather than iterating over all vertices as described in the previous link, I was thinking that using the bound_box property of Blender objects, which returns 8 xyz coordinates. The idea is that I would inspect the 8 coordinates and draw a 2D box around them, like this before and after pic:

BEFORE AFTER

How can I do this? At the end, I want to minX, maxX, minY, maxYrepresenting the coordinates of the bounding box.

$\endgroup$

1 Answer 1

3
$\begingroup$

You just need a few changes (6 lines in total) from the code you linked to achieve that.

There is just one little problem with the method you suggest and I will explain that to you in the end.

  1. I changed the line object= bpy.data.objects['Cube'] to object= bpy.context.object just to have the script operate on the active object instead of the default Cube object. This is optional but I wanted to use this for faster testing.
  2. Then, the line mesh = object.data to mesh = object.bound_box because I need to work on the bounding box and not on the mesh data (where the vertices are stored).
  3. Then I changed numVertices = len(object.data.vertices) to numVertices = len(mesh). Remember, mesh is now an array that contains the coordinates of the corners of the object's bounding box (You probably want to change the names of those variables by more significative names but for the sake of this answer, I'm going to use the same names as the original code).
  4. co = mesh.vertices[t].co becomes co= mesh[t].
  5. pos = (col0 * co.x) + (col1 * co.y) + (col2 * co.z) + col3 is changed to pos = (col0 * co[0]) + (col1 * co[1]) + (col2 * co[2]) + col3 because each element of object.bound_box is stored as an array not a vector3.
  6. I also changed object= bpy.data.objects['Cube'] to object= bpy.context.object in the function UpdateFrame(scene).

This is the full code (I removed the comments to avoid making it to long):

import bpy
import bpy_extras.object_utils

UpdateFrames = 1

def UpdateBorder(camera, scene, object):
    print("\nUpdateBorder Frame "+str(scene.frame_current))
    matrix = object.matrix_world

    mesh = object.bound_box

    col0 = matrix.col[0]
    col1 = matrix.col[1]
    col2 = matrix.col[2]
    col3 = matrix.col[3]

    minX = 1
    maxX = 0
    minY = 1
    maxY = 0

    numVertices = len(mesh)

    for t in range(0, numVertices):

        co = mesh[t]

        pos = (col0 * co[0]) + (col1 * co[1]) + (col2 * co[2]) + col3

        pos = bpy_extras.object_utils.world_to_camera_view(scene, camera, pos)
    
        if (pos.x < minX):
            minX = pos.x
        if (pos.y < minY):
            minY = pos.y
        if (pos.x > maxX):
            maxX = pos.x
        if (pos.y > maxY):
            maxY = pos.y

    render = scene.render
    pMinX = str(int(minX*render.resolution_x))
    pMinY = str(int(minY*render.resolution_y))
    pMaxX = str(int(maxX*render.resolution_x))
    pMaxY = str(int(maxY*render.resolution_y))
    print("  ("+pMinX+", "+pMinY+") - ("+pMaxX+", "+pMaxY+")")

    render.border_min_x = minX
    render.border_min_y = minY
    render.border_max_x = maxX
    render.border_max_y = maxY

def UpdateFrame(scene):
    camera = bpy.data.objects['Camera']
    object= bpy.context.object
    UpdateBorder(camera, scene, object)

print("Initializing")

scene = bpy.context.scene
camera = bpy.data.objects['Camera']
object= bpy.context.object
UpdateBorder(camera, scene, object)

bpy.app.handlers.frame_change_post.clear()
bpy.app.handlers.frame_change_post.append(UpdateFrame)

This code works as intended but as I told you in the begining, this doesn't give you exactly, depending on the camera angle, the same results as the first code and here is why:

The (projected) 2D bounding box of a mesh and the 2D bounding box of that mesh's bounding box are not the same. It depends on the camera angle. Just try to run that code with Suzanne and you'll see :

Suzanne with the render border

while the previous code gives this :

Suzanne with a better render border

which have a render border that fits our favorite monkey perfectly.

To understand what's happening, just enable Suzanne's border box in the Object properties :

Enable bounding box

Suzanne and her bounding box

There you have it. The method you suggested can be done and would definitely be faster, especially when involving a lot of objects, but keep in mind that it doesn't give as good a result as the original code.

I hope I helped you in some way.

$\endgroup$
5
  • $\begingroup$ Thanks! I guess we could solve the bounding box issue (proposed method) by always reshaping the bounding box into a cube. Do you know how we could do that? Any handy BPY functions? $\endgroup$
    – pookie
    Dec 6, 2022 at 15:41
  • $\begingroup$ What do you mean? THe bounding box is already a cube (a cuboid to be accurate) $\endgroup$ Dec 7, 2022 at 3:47
  • $\begingroup$ If the bounding box was a cube (it is a rectangular cuboid above), the padding/space between the render border and the model would be consistent. At the moment, the gap changes, depending on the angle of the model to the camera. $\endgroup$
    – pookie
    Dec 7, 2022 at 14:56
  • $\begingroup$ A bounding cube would be way bigger than a bounding cuboid. The gaps will get event bigger. $\endgroup$ Dec 8, 2022 at 8:05
  • $\begingroup$ Got it - thanks! $\endgroup$
    – pookie
    Dec 8, 2022 at 15:11

You must log in to answer this question.

Not the answer you're looking for? Browse other questions tagged .