Skip to content

Commit 0746d74

Browse files
authored
Merge pull request #56 from IQTLabs/multi-cam
Multi cam
2 parents b4dc86e + 0c364b1 commit 0746d74

File tree

3 files changed

+39
-19
lines changed

3 files changed

+39
-19
lines changed

axis-ptz-controller/axis_ptz_controller.py

Lines changed: 29 additions & 17 deletions
Original file line numberDiff line numberDiff line change
@@ -64,6 +64,7 @@ def __init__(
6464
loop_interval: float = 0.1,
6565
capture_interval: int = 2,
6666
capture_dir: str = ".",
67+
number_camera: int = 1,
6768
tracking_interval: float = 1.0,
6869
tripod_yaw: float = 0.0,
6970
tripod_pitch: float = 0.0,
@@ -135,6 +136,8 @@ def __init__(
135136
Interval at which the camera image is captured [s]
136137
capture_dir: str
137138
Directory in which to place captured images
139+
number_camera: int
140+
Number of cameras to control
138141
tracking_interval: float
139142
Lead time used when computing camera pointing to the
140143
object [s]
@@ -218,6 +221,7 @@ def __init__(
218221
self.capture_interval = capture_interval
219222
self.capture_dir = capture_dir
220223
self.tracking_interval = tracking_interval
224+
self.number_camera = number_camera
221225
self.focus_interval = 1.0
222226
self.pan_gain = pan_gain
223227
self.pan_derivative_gain_max = pan_derivative_gain_max
@@ -719,9 +723,10 @@ def _track_object(self, time_since_last_update: float) -> None:
719723
self.capture_time = time()
720724

721725
if self.do_capture and time() - self.capture_time > self.capture_interval:
722-
capture_thread = threading.Thread(target=self._capture_image)
723-
capture_thread.daemon = True
724-
capture_thread.start()
726+
for camera_index in range(1, self.number_camera + 1):
727+
capture_thread = threading.Thread(target=self._capture_image, args=(camera_index,))
728+
capture_thread.daemon = True
729+
capture_thread.start()
725730

726731
elapsed_time = time() - start_time
727732
# logging.info(
@@ -1040,7 +1045,7 @@ def _send_data(self, data: Dict[str, str]) -> bool:
10401045

10411046
return success
10421047

1043-
def _capture_image(self) -> None:
1048+
def _capture_image(self, cam_num) -> None:
10441049
"""When enabled, capture an image in JPEG format, and publish
10451050
corresponding image metadata.
10461051
@@ -1058,12 +1063,18 @@ def _capture_image(self) -> None:
10581063
self.capture_time = time()
10591064
datetime_c = datetime.utcnow()
10601065
timestr = datetime_c.strftime("%Y-%m-%d-%H-%M-%S")
1061-
image_filepath = Path(self.capture_dir) / "{}_{}_{}_{}_{}.jpg".format(
1066+
flight = "UNKNOWN"
1067+
if self.object.flight is not None:
1068+
flight = self.object.flight.rstrip()
1069+
image_filepath = Path(self.capture_dir) / "{}_{}_{}_{}_{}_{}_{}_{}.jpg".format(
10621070
self.object.object_id,
1071+
flight,
10631072
int(self.object.azm) % 360,
10641073
int(self.object.elv),
10651074
int(self.object.distance_to_tripod3d),
1075+
int(self.camera.zoom_c),
10661076
timestr,
1077+
cam_num
10671078
)
10681079
logging.info(
10691080
f"\t📸\tCapturing image of object: {self.object.object_id}, at: {self.capture_time}, in: {image_filepath}"
@@ -1081,18 +1092,18 @@ def _capture_image(self) -> None:
10811092

10821093
# TODO: Update camera configuration to make renaming the
10831094
# file unnecessary
1084-
with tempfile.TemporaryDirectory() as d:
1085-
with axis_ptz_utilities.pushd(d):
1086-
try:
1087-
text = self.camera_configuration.get_jpeg_request(
1088-
resolution=self.jpeg_resolution,
1089-
compression=self.jpeg_compression,
1090-
)
1091-
logging.debug(f"Camera configuration response: {text}")
1092-
shutil.move(list(Path(d).glob("*.jpg"))[0], image_filepath)
1093-
except Exception as e:
1094-
logging.error(f"Could not capture image to directory: {d}: {e}")
1095-
return
1095+
1096+
try:
1097+
text = self.camera_configuration.get_jpeg_request(
1098+
filename=image_filepath,
1099+
resolution=self.jpeg_resolution,
1100+
compression=self.jpeg_compression,
1101+
camera=cam_num
1102+
)
1103+
logging.debug(f"Camera configuration response: {text}")
1104+
except Exception as e:
1105+
logging.error(f"Could not capture image: {image_filepath}: {e}")
1106+
return
10961107
logging.debug(
10971108
f"Publishing filename: {image_filepath}, for object: {self.object.object_id}, at: {self.capture_time}"
10981109
)
@@ -1314,6 +1325,7 @@ def make_controller() -> AxisPtzController:
13141325
capture_interval=int(os.environ.get("CAPTURE_INTERVAL", 2)),
13151326
capture_dir=os.environ.get("CAPTURE_DIR", "."),
13161327
tracking_interval=float(os.environ.get("TRACKING_INTERVAL", 1.0)),
1328+
number_camera=int(os.environ.get("NUMBER_CAMERA", 1)),
13171329
tripod_yaw=float(os.environ.get("TRIPOD_YAW", 0.0)),
13181330
tripod_pitch=float(os.environ.get("TRIPOD_PITCH", 0.0)),
13191331
tripod_roll=float(os.environ.get("TRIPOD_ROLL", 0.0)),

axis-ptz-controller/camera_configuration.py

Lines changed: 8 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -5,6 +5,7 @@
55
import requests
66
from requests.auth import HTTPDigestAuth
77
from sensecam_control import vapix_config
8+
import os
89

910

1011
class CameraConfiguration(vapix_config.CameraConfiguration):
@@ -15,6 +16,7 @@ class CameraConfiguration(vapix_config.CameraConfiguration):
1516

1617
def get_jpeg_request(
1718
self,
19+
filename: str = None,
1820
resolution: str = None,
1921
camera: str = None,
2022
square_pixel: int = None,
@@ -57,6 +59,11 @@ def get_jpeg_request(
5759
description).
5860
5961
"""
62+
if filename:
63+
os.makedirs(os.path.dirname(filename), exist_ok=True)
64+
else:
65+
ValueError("Filename is required")
66+
6067
payload = {
6168
"resolution": resolution,
6269
"camera": camera,
@@ -81,8 +88,7 @@ def get_jpeg_request(
8188
)
8289

8390
if resp.status_code == 200:
84-
now = datetime.datetime.now()
85-
with open(str(now.strftime("%d-%m-%Y_%Hh%Mm%Ss")) + ".jpg", "wb") as var:
91+
with open(filename, "wb") as var:
8692
var.write(resp.content)
8793
return str("Image saved")
8894

axis-ptz-controller/object.py

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -27,6 +27,7 @@ def __init__(
2727
lead_time (float): Time in seconds to lead object message
2828
"""
2929
self.object_id = object_id
30+
self.flight = None
3031
self.msg_longitude = 0
3132
self.msg_latitude = 0
3233
self.msg_altitude = 0
@@ -130,6 +131,7 @@ def update_from_msg(self, msg: Dict[str, Any]) -> None:
130131
self.msg_altitude = msg["altitude"]
131132
self.msg_timestamp = msg["timestamp"]
132133
self.msg_track = msg["track"]
134+
self.flight = msg["flight"]
133135
self.msg_horizontal_velocity = msg["horizontal_velocity"]
134136
self.msg_vertical_velocity = msg["vertical_velocity"]
135137

0 commit comments

Comments
 (0)