eth-ait / aitviewer

A set of tools to visualize and interact with sequences of 3D data.
MIT License
547 stars 49 forks source link

_moderngl.Error: the program belongs to a different context #64

Open yhiroya2000 opened 2 weeks ago

yhiroya2000 commented 2 weeks ago

Hi, I want to save all videos in the directory.

When I tried to save videos by using HeadlessRenderer(), " _moderngl.Error: the program belongs to a different context " happened.

The first video is saved perfectly, but this error happened on the second video.

What should I do?

Here is the detail of the error code.

name@Unknown:~/研究/code$ python3 amass_save_all.py
Processing: npz_path=../data/amass/ACCAD/Male1Walking_c3d/Walk B15 - Walk turn around_poses.npz, output_path=../export/headless/Walk B15 - Walk turn around_poses.mp4
Processing file: ../data/amass/ACCAD/Male1Walking_c3d/Walk B15 - Walk turn around_poses.npz
WARNING: You are using a SMPL+H model, with only 16 shape coefficients.
num_betas=10, shapedirs.shape=(6890, 3, 16), self.SHAPE_SPACE_DIM=300
Data keys available: ['trans', 'gender', 'mocap_framerate', 'betas', 'dmpls', 'poses']
  1367 poses of size  156.
  1367 trans of size    3.
     1 shape of size   16.
Gender male
FPS 120.0
Rendering frames: 100%|███████████████████████| 684/684 [00:44<00:00, 15.35it/s]
Frames saved to /home/name/研究/export/headless/Walk B15 - Walk turn around_poses.mp4/0006
Video saved to: ../export/headless/Walk B15 - Walk turn around_poses.mp4
Processing: npz_path=../data/amass/ACCAD/Male1Walking_c3d/Walk B22 - Side step left_poses.npz, output_path=../export/headless/Walk B22 - Side step left_poses.mp4
Processing file: ../data/amass/ACCAD/Male1Walking_c3d/Walk B22 - Side step left_poses.npz
WARNING: You are using a SMPL+H model, with only 16 shape coefficients.
num_betas=10, shapedirs.shape=(6890, 3, 16), self.SHAPE_SPACE_DIM=300
Data keys available: ['trans', 'gender', 'mocap_framerate', 'betas', 'dmpls', 'poses']
   555 poses of size  156.
   555 trans of size    3.
     1 shape of size   16.
Gender male
FPS 120.0

Traceback (most recent call last):
  File "/home/name/研究/code/amass_save_all.py", line 55, in <module>
    process_file(npz_path, output_path)
  File "/home/name/研究/code/amass_save_all.py", line 28, in process_file
    v.save_video(output_path)  # Use video_path instead of video_dir
  File "/home/name/.local/lib/python3.10/site-packages/aitviewer/headless.py", line 37, in save_video
    self._init_scene()
  File "/home/name/.local/lib/python3.10/site-packages/aitviewer/viewer.py", line 444, in _init_scene
    self.scene.make_renderable(self.ctx)
  File "/home/name/.local/lib/python3.10/site-packages/aitviewer/scene/scene.py", line 221, in make_renderable
    r.make_renderable(self.ctx)
  File "/home/name/.local/lib/python3.10/site-packages/aitviewer/scene/node.py", line 574, in _decorator
    func(self, *args, **kwargs)
  File "/home/name/.local/lib/python3.10/site-packages/aitviewer/renderables/plane.py", line 205, in make_renderable
    self.vao = ctx.vertex_array(
  File "/home/name/.local/lib/python3.10/site-packages/moderngl/__init__.py", line 1650, in vertex_array
    return self._vertex_array(*args, **kwargs)
  File "/home/name/.local/lib/python3.10/site-packages/moderngl/__init__.py", line 1671, in _vertex_array
    res.mglo, res._glo = self.mglo.vertex_array(
_moderngl.Error: the program belongs to a different context
yhiroya2000 commented 2 weeks ago

This is the code that I ran. I tried to save videos of amass files.

import os
import numpy as np

from aitviewer.configuration import CONFIG as C
from aitviewer.headless import HeadlessRenderer
from aitviewer.renderables.smpl import SMPLSequence

def process_file(npz_path, output_path):
    """Process a single .npz file."""

    print(f"Processing file: {npz_path}")
    c = (149 / 255, 85 / 255, 149 / 255, 1)
    smpl_seq = SMPLSequence.from_amass(
        npz_data_path=npz_path,
        fps_out=60.0,
        color=c,
        name="AMASS Running",
        show_joint_angles=True,
    )

    # Create the headless renderer and add the sequence.
    # Ensure proper cleanup of resources by using a context manager if available.
    v = HeadlessRenderer()
    v.scene.add(smpl_seq)
    v.scene.camera.position = np.array([10.0, 2.5, 0.0])

    # Save video with correct path
    v.save_video(output_path)  # Use video_path instead of video_dir
    smpl_seq.release()
    print(f"Video saved to: {output_path}")
    del v
    """ except Exception as e:
        print(f"An error occurred while processing file {npz_path}: {e}")
        # Ensure that the renderer is cleaned up properly if an error occurs
        del v  # Explicitly delete the renderer to free resources
 """
if __name__ == "__main__":
    accad_dir = os.path.join(C.datasets.amass, "ACCAD")
    output_dir = os.path.join(C.export_dir, "headless")

    # Ensure output directory exists
    if not os.path.exists(output_dir):
        os.makedirs(output_dir)

    # Iterate over all .npz files in ACCAD directory and its subdirectories
    for root, _, files in os.walk(accad_dir):
        for file in files:
            if file.endswith(".npz"):
                npz_path = os.path.join(root, file)
                # Generate output path for the video
                video_name = os.path.splitext(file)[0] + ".mp4"
                output_path = os.path.join(output_dir, video_name)
                # Process each file
                print(f"Processing: npz_path={npz_path}, output_path={output_path}")
                process_file(npz_path, output_path)
kaufManu commented 2 weeks ago

Yes, that is unfortunately a known problem, see #12 . The cause is that the viewer is not completely cleaned up after the first use, which appears to be related to the moderngl-window implementation of pyqt5. You could try to use pyglet as the backend instead, but this might be slower:

from aitviewer.configuration import CONFIG as C
C.update_conf({"window_type": "pyglet"})

Another possibility is to only instantiate the HeadlessRenderer once and re-use it. I.e. after your first rendering pass, you remove all the nodes you don't need anymore (v.scene.remove(*nodes)) and add the new ones, but you always keep a reference to the same headless renderer object.

Finally, you can also solve the problem by starting the script as a new process each time you want to render something. For example, you could re-write your script to take npz_path and output_path as inputs via command line arguments. And then you call that script from another script that iterates over all the rendering passes you want to do. Something like:

import subprocess
npz_files = ["some/path/test1.npz", "some/path/test2.npz"]
for npz_file in npz_files:
   command = "python render_something.py --npz_file npz_file --output_path npz_file.replace('.npz', '.mp4')"
   subprocess.run(command, shell=True, check=True)