Hey all, i’m trying to run a code that captures and saves images & depth maps, i was successful in running it on Camera connected to my Jetson AGX Orin Devkit, however adding a new object for the second camera it seems it is not detecting both, instead only detecting one which lights a blue light for a short period of time and then goes out again.
in the old version of the code i wrote this line
zed = sl.Camera()
in the new multi camera code i wrote these two lines
zed1 = sl.Camera()
zed2 = sl.Camera()
has anyone been successful multiple cameras within a python code? thanks in advance
Hi, can you send the full code ?
import pyzed.sl as sl
import cv2
import os
import subprocess
import threading
import pexpect
def main():
# Create two ZED2i camera objects
zed1 = sl.Camera()
zed2 = sl.Camera()
# Create initialization parameters for the cameras
init_params = sl.InitParameters()
init_params.camera_resolution = sl.RESOLUTION.HD720 # Use HD720 video mode
init_params.camera_fps = 30 # Set FPS at 30
# Open the cameras
err1 = zed1.open(init_params)
err2 = zed2.open(init_params)
if err1 != sl.ERROR_CODE.SUCCESS or err2 != sl.ERROR_CODE.SUCCESS:
exit(1)
# Specify save directories for depth maps and images
save_directory_1 = "path/to/directory/cobraout"
os.makedirs(save_directory_1, exist_ok=True)
save_directory_2 = "/path/to/directory"
os.makedirs(save_directory_2, exist_ok=True)
def capture_and_send_zed1():
# Enable image retrieval for camera 1
runtime_parameters = sl.RuntimeParameters()
runtime_parameters.sensing_mode = sl.SENSING_MODE.STANDARD
# Perform any desired image processing or save the images from camera 1
frame_count = 0
while True:
# Capture a new frame from camera 1
if zed1.grab(runtime_parameters) == sl.ERROR_CODE.SUCCESS:
# Retrieve the right color image from camera 1
right_image_1 = sl.Mat()
zed1.retrieve_image(right_image_1, sl.VIEW.RIGHT)
left_image_1 = sl.Mat()
zed1.retrieve_image(left_image_1, sl.VIEW.LEFT)
depth_map_1 = sl.Mat()
zed1.retrieve_measure(depth_map_1, sl.MEASURE.DEPTH)
depth_array_1 = depth_map_1.get_data()
# Convert the images to numpy arrays
right_image_array_1 = right_image_1.get_data()
# Save depth map and image with unique filenames from camera 1
depth_filename_1 = f"dm1_{frame_count}.txt"
depth_filepath_1 = os.path.join(save_directory_1, depth_filename_1)
np.savetxt(depth_filepath_1, depth_array_1, delimiter=',')
right_image_filename_1 = f"rm1_{frame_count}.png"
right_image_filepath_1 = os.path.join(save_directory_1, right_image_filename_1)
cv2.imwrite(right_image_filepath_1, right_image_array_1)
# Increment the frame count
frame_count += 1
# Check for key press to exit
key = cv2.waitKey(1)
if key == ord("q"):
break
def capture_and_send_zed2():
# Enable image retrieval for camera 2
runtime_parameters = sl.RuntimeParameters()
runtime_parameters.sensing_mode = sl.SENSING_MODE.STANDARD
# Perform any desired image processing or save the images from camera 2
frame_count = 0
while True:
# Capture a new frame from camera 2
if zed2.grab(runtime_parameters) == sl.ERROR_CODE.SUCCESS:
# Retrieve the right color image from camera 2
right_image_2 = sl.Mat()
zed2.retrieve_image(right_image_2, sl.VIEW.RIGHT)
left_image_2 = sl.Mat()
zed2.retrieve_image(left_image_2, sl.VIEW.LEFT)
depth_map_2 = sl.Mat()
zed2.retrieve_measure(depth_map_2, sl.MEASURE.DEPTH)
depth_array_2 = depth_map_2.get_data()
# Convert the images to numpy arrays
right_image_array_2 = right_image_2.get_data()
# Save depth map and image with unique filenames from camera 2
depth_filename_2 = f"dm2_{frame_count}.txt"
depth_filepath_2 = os.path.join(save_directory_2, depth_filename_2)
np.savetxt(depth_filepath_2, depth_array_2, delimiter=',')
right_image_filename_2 = f"rm2_{frame_count}.png"
right_image_filepath_2 = os.path.join(save_directory_2, right_image_filename_2)
cv2.imwrite(right_image_filepath_2, right_image_array_2
# Increment the frame count
frame_count += 1
# Check for key press to exit
key = cv2.waitKey(1)
if key == ord("q"):
break
# Create separate threads for capturing and sending data from each camera
thread1 = threading.Thread(target=capture_and_send_zed1)
thread2 = threading.Thread(target=capture_and_send_zed2)
# Start the threads
thread1.start()
thread2.start()
# Wait for the threads to finish
thread1.join()
thread2.join()
# Close the cameras
zed1.close()
zed2.close()
# Close any open windows
cv2.destroyAllWindows()
if name == “main”:
main()
You might encounter a lot of issues using Python’s threads
. These are fake threads, because of the Python GIL : What Is the Python Global Interpreter Lock (GIL)? – Real Python
One solution would be to use process
instead. The api is compatible so, basically, you just have to import multiprocessing
and replace all threading.Thread
objects with multiprocessing.Process
.
so creating 2 functions for each camera and then calling them on with multiprocessing?
Exactly the same way you do it with threads.
would i have to use the same syntax as this example provided by stereolabs? with the zed_list or can i just try to assign each camera a separate process identified by the serial number?
You should not. The sample you mention use threading too.
You must simply replace, in your code, all threading.Thread
with multiprocessing.Process
and import the header.
import pyzed.sl as sl
import cv2
import os
import subprocess
import multiprocessing
import pexpect
def main():
# Create two ZED2i camera objects
zed1 = sl.Camera()
zed2 = sl.Camera()
# Create initialization parameters for the cameras
init_params = sl.InitParameters()
init_params.camera_resolution = sl.RESOLUTION.HD720 # Use HD720 video mode
init_params.camera_fps = 30 # Set FPS at 30
# Open the cameras
err1 = zed1.open(init_params)
err2 = zed2.open(init_params)
if err1 != sl.ERROR_CODE.SUCCESS or err2 != sl.ERROR_CODE.SUCCESS:
exit(1)
# Specify save directories for depth maps and images
save_directory_1 = "path/directory"
os.makedirs(save_directory_1, exist_ok=True)
save_directory_2 = "path/directory"
os.makedirs(save_directory_2, exist_ok=True)
def capture_and_send_zed1():
# Enable image retrieval for camera 1
runtime_parameters = sl.RuntimeParameters()
runtime_parameters.sensing_mode = sl.SENSING_MODE.STANDARD
# Perform any desired image processing or save the images from camera 1
frame_count = 0
while True:
# Capture a new frame from camera 1
if zed1.grab(runtime_parameters) == sl.ERROR_CODE.SUCCESS:
# Retrieve the right color image from camera 1
right_image_1 = sl.Mat()
zed1.retrieve_image(right_image_1, sl.VIEW.RIGHT)
left_image_1 = sl.Mat()
zed1.retrieve_image(left_image_1, sl.VIEW.LEFT)
depth_map_1 = sl.Mat()
zed1.retrieve_measure(depth_map_1, sl.MEASURE.DEPTH)
depth_array_1 = depth_map_1.get_data()
# Convert the images to numpy arrays
right_image_array_1 = right_image_1.get_data()
# Save depth map and image with unique filenames from camera 1
depth_filename_1 = f"dm1_{frame_count}.txt"
depth_filepath_1 = os.path.join(save_directory_1, depth_filename_1)
np.savetxt(depth_filepath_1, depth_array_1, delimiter=',')
right_image_filename_1 = f"rm1_{frame_count}.png"
right_image_filepath_1 = os.path.join(save_directory_1, right_image_filename_1)
cv2.imwrite(right_image_filepath_1, right_image_array_1)
# Increment the frame count
frame_count += 1
# Check for key press to exit
key = cv2.waitKey(1)
if key == ord("q"):
break
def capture_and_send_zed2():
# Enable image retrieval for camera 2
runtime_parameters = sl.RuntimeParameters()
runtime_parameters.sensing_mode = sl.SENSING_MODE.STANDARD
# Perform any desired image processing or save the images from camera 2
frame_count = 0
while True:
# Capture a new frame from camera 2
if zed2.grab(runtime_parameters) == sl.ERROR_CODE.SUCCESS:
# Retrieve the right color image from camera 2
right_image_2 = sl.Mat()
zed2.retrieve_image(right_image_2, sl.VIEW.RIGHT)
left_image_2 = sl.Mat()
zed2.retrieve_image(left_image_2, sl.VIEW.LEFT)
depth_map_2 = sl.Mat()
zed2.retrieve_measure(depth_map_2, sl.MEASURE.DEPTH)
depth_array_2 = depth_map_2.get_data()
# Convert the images to numpy arrays
right_image_array_2 = right_image_2.get_data()
# Save depth map and image with unique filenames from camera 2
depth_filename_2 = f"dm2_{frame_count}.txt"
depth_filepath_2 = os.path.join(save_directory_2, depth_filename_2)
np.savetxt(depth_filepath_2, depth_array_2, delimiter=',')
right_image_filename_2 = f"rm2_{frame_count}.png"
right_image_filepath_2 = os.path.join(save_directory_2, right_image_filename_2)
cv2.imwrite(right_image_filepath_2, right_image_array_2)
frame_count += 1
# Check for key press to exit
key = cv2.waitKey(1)
if key == ord("q"):
break
# Create separate threads for capturing and sending data from each camera
process1 = multiprocessing.Process(target=capture_and_send_z1)
process2 = multiprocessing.Process(target=capture_and_send_z2)
# Start the threads
process1.Start()
process2.Start()
# Close the cameras
zed1.close()
zed2.close()
# Close any open windows
cv2.destroyAllWindows()
if __name__ == "__main__":
main()
i changed the program as you requested, however it just runs now and does not take any pictures or saves them, it simply closes.
It’s because your main process exits. If you make it wait with the sleep
function, how does it behave ?
import pyzed.sl as sl
import cv2
import numpy as np
import threading
import time
import signal
import os
zed_list = []
left_list = []
right_list = []
depth_list = []
timestamp_list = []
thread_list = []
stop_signal = False
output_directory = 'path/to/directory'
def signal_handler(signal, frame):
global stop_signal
stop_signal = True
time.sleep(0.5)
exit()
def grab_run(index):
global stop_signal
global zed_list
global timestamp_list
global left_list
global right_list
global depth_list
runtime = sl.RuntimeParameters()
while not stop_signal:
err = zed_list[index].grab(runtime)
if err == sl.ERROR_CODE.SUCCESS:
# Retrieve the left image from the ZED camera
zed_list[index].retrieve_image(left_list[index], sl.VIEW.LEFT)
# Retrieve the right image from the ZED camera
zed_list[index].retrieve_image(right_list[index], sl.VIEW.RIGHT)
# Retrieve the depth map from the ZED camera
zed_list[index].retrieve_measure(depth_list[index], sl.MEASURE.DEPTH)
# Get the timestamp of the current frame
timestamp_list[index] = zed_list[index].get_timestamp(sl.TIME_REFERENCE.CURRENT).data_ns
# Save the left and right images and depth map
save_image(left_list[index], f"left_image_{index}.jpg")
save_image(right_list[index], f"right_image_{index}.jpg")
save_depth_map(depth_list[index], f"depth_map_{index}.txt")
time.sleep(0.001) #1ms
zed_list[index].close()
def save_image(image, filename):
global output_directory
# Create the file path for the image
file_path = os.path.join(output_directory, filename)
# Save the image as a JPEG file
cv2.imwrite(file_path, image.get_data())
def save_depth_map(depth_map, filename):
global output_directory
# Create the file path for the depth map
file_path = os.path.join(output_directory, filename)
# Save the depth map as a text file
np.savetxt(file_path, depth_map.get_data())
def main():
global stop_signal
global zed_list
global left_list
global right_list
global depth_list
global timestamp_list
global thread_list
signal.signal(signal.SIGINT, signal_handler)
print("Running...")
init = sl.InitParameters()
init.camera_resolution = sl.RESOLUTION.HD720
init.camera_fps = 30 # The framerate is lowered to avoid any USB3 bandwidth issues
# Create output directory if it doesn't exist
if not os.path.exists(output_directory):
os.makedirs(output_directory)
# List and open cameras
name_list = []
last_ts_list = []
cameras = sl.Camera.get_device_list()
index = 0
for cam in cameras:
init.set_from_serial_number(cam.serial_number)
name_list.append("ZED {}".format(cam.serial_number))
print("Opening {}".format(name_list[index]))
# Initialize the ZED camera and create image and depth map containers
zed_list.append(sl.Camera())
left_list.append(sl.Mat())
right_list.append(sl.Mat())
depth_list.append(sl.Mat())
timestamp_list.append(0)
last_ts_list.append(0)
status = zed_list[index].open(init)
if status != sl.ERROR_CODE.SUCCESS:
print(repr(status))
zed_list[index].close()
index = index + 1
# Start camera threads
for index in range(0, len(zed_list)):
if zed_list[index].is_opened():
# Create a thread for each ZED camera to grab frames
thread_list.append(threading.Thread(target=grab_run, args=(index,)))
thread_list[index].start()
# Display camera images
key = ''
while key != 113: # for 'q' key
for index in range(0, len(zed_list)):
if zed_list[index].is_opened():
if (timestamp_list[index] > last_ts_list[index]):
cv2.imshow(name_list[index], left_list[index].get_data())
key = cv2.waitKey(10)
cv2.destroyAllWindows()
# Stop the threads
stop_signal = True
for index in range(0, len(thread_list)):
thread_list[index].join()
print("\nFINISH")
if __name__ == "__main__":
main()
alright this code functions and saves the images in the specified output directory, albeit it is very laggy. How does it function on your side?
Well, you are trying to save tons of images per second on your disk. The result will highly depends on your disk speed. What does your system monitor tell you while the program runs ?
it runs the program and opens an opencv window which shows the video feed, but even that is laggy and jitters
im trying a different approach now, assigning the camera serial number to each function and running it with threading not with multiprocessing since the multiprocessing isn’t running anything and only closing the program