List of object visible by a camera within Safe Frames
-
Hi,
I'm looking for a Python solution that can return lists of all objects visible by a camera, including both standard and Redshift Spherical cameras, and within Safe Frames. I want to include all objects, even those partially visible. I've tried using ViewportSelect (code and test scene attached) and GeCollider, but I'm not getting the results I expected, and the code runs slowly. Could you provide some suggestions and a code snippet as a starting point?
Also - for testing purposes - for the attached scene and cameras, I expect a list of specific objects (below), not all objects currently returned by my script.
Expected results:
EnSuite_Flooring_24_Reset
Bed2_Flooring_14_Reset
Landing_Flooring_30_Reset
Bed4_Flooring_18_Reset
Bed4_Flooring_18_Reset
Upstairs1_Flooring_26_Reset
ColliderBest regards,
Tomaszimport c4d doc = c4d.documents.GetActiveDocument() bd = doc.GetActiveBaseDraw() frame = bd.GetFrame() def get_flooring_objects(): flooring_objects = [] doc = c4d.documents.GetActiveDocument() helper_node = doc.SearchObject("Helpers") helper_categories = helper_node.GetChildren() for helper_cat in helper_categories: if helper_cat.GetName() == "_Flooring": flooring_objects = helper_cat.GetChildren() return flooring_objects left = frame["cl"] right = frame["cr"] top = frame["ct"] bottom = frame["cb"] # Calculates the width and height of the screen width = right - left + 1 height = bottom - top + 1 print ("width : {}, height : {}".format(width, height)) # Creates a new ViewportSelect vpSelect = c4d.utils.ViewportSelect() # Initialise the ViewportSelect with a list of object ops = get_flooring_objects() vpSelect.Init(width, height, bd, ops, c4d.Mpolyedgepoint, True, c4d.VIEWPORTSELECTFLAGS_IGNORE_HIDDEN_SEL) our_list = [] c4d.CallCommand(13957) # Clear Console for w in range(width): for h in range(height): elements = vpSelect.PickObject(bd, doc, w,h, 1, c4d.VIEWPORT_PICK_FLAGS_OGL_IGNORE_Z) our_list.extend(elements) name_list = [] for el in our_list: name_list.append(el.GetName()) unique_list = sorted(set(name_list)) for name in unique_list: print ("Unique name: {}".format(name))
Scene for testing : https://we.tl/t-FLqG7LPcNu
-
Hi Tomasz,
You can get the safe frame using function GetSafeFrame(), which works the same way as function GetFrame() that you have already used in your code.
The logic for this would be the same as you have in your script: you iterate over pixels (or regions by using
rad
argument if you don't need a per-pixel precision) and use function PickObject() with a correct set of flags that
depend on your criteria (what to consider as the object visible by a camera: is it only being in the camera frame or for example also not being covered by other objects).Your code looks fine, except that from how I understand your needs you also need flag VIEWPORT_PICK_FLAGS_OGL_ONLY_VISIBLE, which is not available in python.
Python is also the reason the code is running slow. Since this is quite an expensive task, consider switching to C++.
The same question was already discussed here: Get visible objects from viewport in commandline
Please find below a code snippet that shows the described above workflow (although logically it's the same as yours).
Cheers,
IliaCode snippet
import c4d doc: c4d.documents.BaseDocument # The currently active document. op: c4d.BaseObject | None # The primary selected object in `doc`. Can be `None`. def main() -> None: doc = c4d.documents.GetActiveDocument() bd = doc.GetRenderBaseDraw() safeframe = bd.GetSafeFrame() print(safeframe) step_x, step_y = 4, 4 objects = set() for x in range(safeframe['cl'], safeframe['cr'], step_y): for y in range(safeframe['ct'], safeframe['cb'], step_x): foundObjects = c4d.utils.ViewportSelect.PickObject(bd, doc, x, y, rad=2, flags=c4d.VIEWPORT_PICK_FLAGS_OGL_ONLY_TOPMOST) # print(f"({x},{y}): {len(foundObjects)}") objects = objects.union(set(foundObjects)) for o in objects: print(o.GetName()) return if __name__ == '__main__': main()
-
Hi Ilia,
Thank you for guiding me in the right direction. I've revised my code following your advice, and it's working well with standard cameras. However, it's having difficulty with 360 cameras. The code successfully identifies larger objects but struggles to detect smaller ones, even after I increased the accuracy (step=1, radius=1). Do you have any suggestions?
Best regards,
Tomasz -
Hi @i_mazlov ,
I'm writing to follow up on my previous message about the issue with 360 cameras in my code as I haven't heard back. Could you please provide any updates or further guidance?
Best regards,
Tomasz -
I spent one year going your route and had a very computational expensive ray caster - but 5 years ago there was no c4d.utils.ViewportSelect.PickObject as far as i know.
anyway I think the accuracy is depended on your viewport - try a separate view in "Fullscreen" to check ....
Hint: I found a solution to render the image and select from that.
-
Hi @mogh,
Thank you for your suggestion. Currently, I've developed a method where I replace a spherical camera view with a standard camera, capture what's visible, and rotate until I've covered the entire area. This approach meets my goals, although I'm still assessing its efficiency. Your idea of rendering the image and selecting from it sounds like another option and might be a good alternative. -
Hi Tomasz,
please excuse the delayed answer.
Redshift Spherical camera (as well as e.g. FishEye or Cylindrical) is a special type of the object that is not easy to handle. There's no "working out of the box" solution in your case, so replacing with the ordinary camera and iterating over the sphere sounds like a viable solution to me.
Cheers,
Ilia -
Hi @i_mazlov ,
Thank you for confirming my solution.
Best regards,
Tomasz