Not totally sure this answer the question, but here is some code to check if an armature has bone heads visible by the camera.
The alternative way to understand the question could be: which armature is not totally enclosed by a mesh.

But, the principles of the script:
- create and use a sphere that will be placed and scaled at bone heads locations
- parse armature bones, place the sphere, raycast on it
- if a bone is hit, the armature is visible by the camera
- select visible armatures
- delete the sphere
Here is the script:
import bpy
import bmesh
from mathutils import Vector
from bpy_extras.object_utils import world_to_camera_view
Create a sphere that will be used to ray cast on
def create_sphere(view_layer, collection):
mesh = bpy.data.meshes.new('sphere')
bm = bmesh.new()
bmesh.ops.create_uvsphere(bm, u_segments=32, v_segments=16, diameter=1)
bm.to_mesh(mesh)
bm.free()
sphere = bpy.data.objects.new("sphere", mesh)
collection.objects.link(sphere)
view_layer.update()
return sphere
Sphere clean up
def delete_sphere(sphere):
bpy.data.objects.remove(sphere, do_unlink=True)
Test the armature visibility
def test_armature(scene, view_layer, camera, sphere, armature):
world_matrix = armature.matrix_world
# map the bones
for bone in armature.pose.bones:
head = bone.head
tail = bone.tail
# scales and places the sphere
# This ratio seems to be the one to have a sphere at the good scale
factor = Vector(armature.scale) * 0.05
sphere.scale = factor * (head - tail).length
sphere.location = world_matrix @ head
view_layer.update()
# calculates the sphere location in camera view
co2D = world_to_camera_view( scene, camera, sphere.location )
# if inside visible cone
if 0.0 <= co2D.x <= 1.0 and 0.0 <= co2D.y <= 1.0:
# ray cast from camera to sphere
from_location = camera.location
direction = (sphere.location - camera.location).normalized()
hit, loc, norm, idx, ob, M = scene.ray_cast(
view_layer,
from_location,
direction)
# if hit the sphere, head is visible
if hit and ob == sphere:
return True
# No hit occured
return False
camera = bpy.data.objects['Camera']
scene = bpy.context.scene
view_layer = bpy.context.view_layer
Create the sphere to ray cast on
sphere = create_sphere(view_layer, bpy.context.scene.collection)
Maps the armatures and select the ones that the camera can see
for armature in [obj for obj in scene.collection.all_objects if obj.type == "ARMATURE"]:
visible = test_armature(scene, view_layer, camera, sphere, armature)
armature.select_set(visible)
Clean up the sphere
delete_sphere(sphere)
Test file:

Note: this script is basically a variation (eventually enhancement) of these answers from Debaditya and from myself).