use mp event instead of value for ptz status

This commit is contained in:
Josh Hawkins 2023-07-03 09:22:21 -05:00
parent 8e0d492ad8
commit b267cfb520
6 changed files with 69 additions and 57 deletions

View File

@ -122,7 +122,7 @@ class FrigateApp:
"i", "i",
self.config.cameras[camera_name].onvif.autotracking.enabled, self.config.cameras[camera_name].onvif.autotracking.enabled,
), ),
"ptz_moving": mp.Value("i", 0), "ptz_stopped": mp.Event(),
"motion_threshold": mp.Value( "motion_threshold": mp.Value(
"i", self.config.cameras[camera_name].motion.threshold "i", self.config.cameras[camera_name].motion.threshold
), ),
@ -137,6 +137,7 @@ class FrigateApp:
"capture_process": None, "capture_process": None,
"process": None, "process": None,
} }
self.camera_metrics[camera_name]["ptz_stopped"].set()
self.record_metrics[camera_name] = { self.record_metrics[camera_name] = {
"record_enabled": mp.Value( "record_enabled": mp.Value(
"i", self.config.cameras[camera_name].record.enabled "i", self.config.cameras[camera_name].record.enabled

View File

@ -216,7 +216,7 @@ class OnvifController:
return return
self.cams[camera_name]["active"] = True self.cams[camera_name]["active"] = True
self.camera_metrics[camera_name]["ptz_moving"].value = True self.camera_metrics[camera_name]["ptz_stopped"].clear()
onvif: ONVIFCamera = self.cams[camera_name]["onvif"] onvif: ONVIFCamera = self.cams[camera_name]["onvif"]
move_request = self.cams[camera_name]["relative_move_request"] move_request = self.cams[camera_name]["relative_move_request"]
@ -268,7 +268,7 @@ class OnvifController:
return return
self.cams[camera_name]["active"] = True self.cams[camera_name]["active"] = True
self.camera_metrics[camera_name]["ptz_moving"].value = True self.camera_metrics[camera_name]["ptz_stopped"].clear()
move_request = self.cams[camera_name]["move_request"] move_request = self.cams[camera_name]["move_request"]
onvif: ONVIFCamera = self.cams[camera_name]["onvif"] onvif: ONVIFCamera = self.cams[camera_name]["onvif"]
preset_token = self.cams[camera_name]["presets"][preset] preset_token = self.cams[camera_name]["presets"][preset]
@ -278,7 +278,7 @@ class OnvifController:
"PresetToken": preset_token, "PresetToken": preset_token,
} }
) )
self.camera_metrics[camera_name]["ptz_moving"].value = False self.camera_metrics[camera_name]["ptz_stopped"].set()
self.cams[camera_name]["active"] = False self.cams[camera_name]["active"] = False
def _zoom(self, camera_name: str, command: OnvifCommandEnum) -> None: def _zoom(self, camera_name: str, command: OnvifCommandEnum) -> None:
@ -350,10 +350,12 @@ class OnvifController:
status_request = self.cams[camera_name]["status_request"] status_request = self.cams[camera_name]["status_request"]
status = onvif.get_service("ptz").GetStatus(status_request) status = onvif.get_service("ptz").GetStatus(status_request)
self.cams[camera_name]["active"] = status.MoveStatus.PanTilt != "IDLE" if status.MoveStatus.PanTilt == "IDLE" or status.MoveStatus.Zoom == "IDLE":
self.camera_metrics[camera_name]["ptz_moving"].value = ( self.cams[camera_name]["active"] = False
status.MoveStatus.PanTilt != "IDLE" self.camera_metrics[camera_name]["ptz_stopped"].set()
) else:
self.cams[camera_name]["active"] = True
self.camera_metrics[camera_name]["ptz_stopped"].clear()
return { return {
"pan": status.Position.PanTilt.x, "pan": status.Position.PanTilt.x,

View File

@ -21,7 +21,7 @@ logger = logging.getLogger(__name__)
class PtzMotionEstimator: class PtzMotionEstimator:
def __init__(self, config: CameraConfig, ptz_moving) -> None: def __init__(self, config: CameraConfig, ptz_stopped) -> None:
self.frame_manager = SharedMemoryFrameManager() self.frame_manager = SharedMemoryFrameManager()
# homography is nice (zooming) but slow, translation is pan/tilt only but fast. # homography is nice (zooming) but slow, translation is pan/tilt only but fast.
self.norfair_motion_estimator = MotionEstimator( self.norfair_motion_estimator = MotionEstimator(
@ -31,11 +31,14 @@ class PtzMotionEstimator:
) )
self.camera_config = config self.camera_config = config
self.coord_transformations = None self.coord_transformations = None
self.ptz_moving = ptz_moving self.ptz_stopped = ptz_stopped
logger.debug(f"Motion estimator init for cam: {config.name}") logger.debug(f"Motion estimator init for cam: {config.name}")
def motion_estimator(self, detections, frame_time, camera_name): def motion_estimator(self, detections, frame_time, camera_name):
if self.camera_config.onvif.autotracking.enabled and self.ptz_moving.value: if (
self.camera_config.onvif.autotracking.enabled
and not self.ptz_stopped.is_set()
):
# logger.debug( # logger.debug(
# f"Motion estimator running for {camera_name} - frame time: {frame_time}" # f"Motion estimator running for {camera_name} - frame time: {frame_time}"
# ) # )
@ -96,6 +99,11 @@ class PtzAutoTrackerThread(threading.Thread):
if cam.onvif.autotracking.enabled: if cam.onvif.autotracking.enabled:
self.ptz_autotracker.camera_maintenance(camera_name) self.ptz_autotracker.camera_maintenance(camera_name)
time.sleep(1) time.sleep(1)
else:
# disabled dynamically by mqtt
if self.ptz_autotracker.tracked_object.get(camera_name):
self.ptz_autotracker.tracked_object[camera_name] = None
self.ptz_autotracker.tracked_object_previous[camera_name] = None
time.sleep(0.1) time.sleep(0.1)
logger.info("Exiting autotracker...") logger.info("Exiting autotracker...")
@ -169,6 +177,13 @@ class PtzAutoTracker:
tilt = 0 tilt = 0
while not self.move_queues[camera].empty(): while not self.move_queues[camera].empty():
queued_pan, queued_tilt = self.move_queues[camera].queue[0]
# If exceeding the movement range, keep it in the queue and move now
if abs(pan + queued_pan) > 1.0 or abs(tilt + queued_tilt) > 1.0:
logger.debug("Pan or tilt value exceeds 1.0")
break
queued_pan, queued_tilt = self.move_queues[camera].get() queued_pan, queued_tilt = self.move_queues[camera].get()
logger.debug( logger.debug(
f"queue pan: {queued_pan}, queue tilt: {queued_tilt}" f"queue pan: {queued_pan}, queue tilt: {queued_tilt}"
@ -182,16 +197,15 @@ class PtzAutoTracker:
logger.debug(f"final pan: {pan}, final tilt: {tilt}") logger.debug(f"final pan: {pan}, final tilt: {tilt}")
self.onvif._move_relative(camera, pan, tilt, 0.1) self.onvif._move_relative(camera, pan, tilt, 1)
# Wait until the camera finishes moving # Wait until the camera finishes moving
while self.camera_metrics[camera]["ptz_moving"].value: self.camera_metrics[camera]["ptz_stopped"].wait()
pass
except queue.Empty: except queue.Empty:
pass time.sleep(0.1)
def enqueue_move(self, camera, pan, tilt): def _enqueue_move(self, camera, pan, tilt):
move_data = (pan, tilt) move_data = (pan, tilt)
logger.debug(f"enqueue pan: {pan}, enqueue tilt: {tilt}") logger.debug(f"enqueue pan: {pan}, enqueue tilt: {tilt}")
self.move_queues[camera].put(move_data) self.move_queues[camera].put(move_data)
@ -208,7 +222,7 @@ class PtzAutoTracker:
tilt = 0.5 - (obj.obj_data["centroid"][1] / camera_height) tilt = 0.5 - (obj.obj_data["centroid"][1] / camera_height)
# ideas: check object velocity for camera speed? # ideas: check object velocity for camera speed?
self.enqueue_move(camera, -pan, tilt) self._enqueue_move(camera, -pan, tilt)
def autotrack_object(self, camera, obj): def autotrack_object(self, camera, obj):
camera_config = self.config.cameras[camera] camera_config = self.config.cameras[camera]
@ -317,11 +331,10 @@ class PtzAutoTracker:
# returns camera to preset after timeout when tracking is over # returns camera to preset after timeout when tracking is over
autotracker_config = self.config.cameras[camera].onvif.autotracking autotracker_config = self.config.cameras[camera].onvif.autotracking
if autotracker_config.enabled:
if not self.autotracker_init[camera]: if not self.autotracker_init[camera]:
self._autotracker_setup(self.config.cameras[camera], camera) self._autotracker_setup(self.config.cameras[camera], camera)
# regularly update camera status # regularly update camera status
if self.camera_metrics[camera]["ptz_moving"].value: if not self.camera_metrics[camera]["ptz_stopped"].is_set():
self.onvif.get_camera_status(camera) self.onvif.get_camera_status(camera)
# return to preset if tracking is over # return to preset if tracking is over
@ -335,8 +348,8 @@ class PtzAutoTracker:
> autotracker_config.timeout > autotracker_config.timeout
) )
and autotracker_config.return_preset and autotracker_config.return_preset
and not self.camera_metrics[camera]["ptz_moving"].value
): ):
self.camera_metrics[camera]["ptz_stopped"].wait()
logger.debug( logger.debug(
f"Autotrack: Time is {time.time()}, returning to preset: {autotracker_config.return_preset}" f"Autotrack: Time is {time.time()}, returning to preset: {autotracker_config.return_preset}"
) )
@ -345,7 +358,3 @@ class PtzAutoTracker:
autotracker_config.return_preset.lower(), autotracker_config.return_preset.lower(),
) )
self.tracked_object_previous[camera] = None self.tracked_object_previous[camera] = None
def disable_autotracking(self, camera):
# need to call this if autotracking is disabled by mqtt??
self.tracked_object[camera] = None

View File

@ -55,7 +55,7 @@ def frigate_distance(detection: Detection, tracked_object) -> float:
class NorfairTracker(ObjectTracker): class NorfairTracker(ObjectTracker):
def __init__(self, config: CameraConfig, ptz_autotracker_enabled, ptz_moving): def __init__(self, config: CameraConfig, ptz_autotracker_enabled, ptz_stopped):
self.tracked_objects = {} self.tracked_objects = {}
self.disappeared = {} self.disappeared = {}
self.positions = {} self.positions = {}
@ -63,7 +63,7 @@ class NorfairTracker(ObjectTracker):
self.camera_config = config self.camera_config = config
self.detect_config = config.detect self.detect_config = config.detect
self.ptz_autotracker_enabled = ptz_autotracker_enabled.value self.ptz_autotracker_enabled = ptz_autotracker_enabled.value
self.ptz_moving = ptz_moving self.ptz_stopped = ptz_stopped
self.camera_name = config.name self.camera_name = config.name
self.track_id_map = {} self.track_id_map = {}
# TODO: could also initialize a tracker per object class if there # TODO: could also initialize a tracker per object class if there
@ -75,7 +75,7 @@ class NorfairTracker(ObjectTracker):
hit_counter_max=self.max_disappeared, hit_counter_max=self.max_disappeared,
) )
if self.ptz_autotracker_enabled: if self.ptz_autotracker_enabled:
self.ptz_motion_estimator = PtzMotionEstimator(config, self.ptz_moving) self.ptz_motion_estimator = PtzMotionEstimator(config, self.ptz_stopped)
def register(self, track_id, obj): def register(self, track_id, obj):
rand_id = "".join(random.choices(string.ascii_lowercase + string.digits, k=6)) rand_id = "".join(random.choices(string.ascii_lowercase + string.digits, k=6))

View File

@ -17,7 +17,7 @@ class CameraMetricsTypes(TypedDict):
motion_enabled: Synchronized motion_enabled: Synchronized
improve_contrast_enabled: Synchronized improve_contrast_enabled: Synchronized
ptz_autotracker_enabled: Synchronized ptz_autotracker_enabled: Synchronized
ptz_moving: Synchronized ptz_stopped: Synchronized
motion_threshold: Synchronized motion_threshold: Synchronized
motion_contour_area: Synchronized motion_contour_area: Synchronized
process: Optional[Process] process: Optional[Process]

View File

@ -458,7 +458,7 @@ def track_camera(
motion_enabled = process_info["motion_enabled"] motion_enabled = process_info["motion_enabled"]
improve_contrast_enabled = process_info["improve_contrast_enabled"] improve_contrast_enabled = process_info["improve_contrast_enabled"]
ptz_autotracker_enabled = process_info["ptz_autotracker_enabled"] ptz_autotracker_enabled = process_info["ptz_autotracker_enabled"]
ptz_moving = process_info["ptz_moving"] ptz_stopped = process_info["ptz_stopped"]
motion_threshold = process_info["motion_threshold"] motion_threshold = process_info["motion_threshold"]
motion_contour_area = process_info["motion_contour_area"] motion_contour_area = process_info["motion_contour_area"]
@ -478,7 +478,7 @@ def track_camera(
name, labelmap, detection_queue, result_connection, model_config, stop_event name, labelmap, detection_queue, result_connection, model_config, stop_event
) )
object_tracker = NorfairTracker(config, ptz_autotracker_enabled, ptz_moving) object_tracker = NorfairTracker(config, ptz_autotracker_enabled, ptz_stopped)
frame_manager = SharedMemoryFrameManager() frame_manager = SharedMemoryFrameManager()
@ -499,7 +499,7 @@ def track_camera(
detection_enabled, detection_enabled,
motion_enabled, motion_enabled,
stop_event, stop_event,
ptz_moving, ptz_stopped,
) )
logger.info(f"{name}: exiting subprocess") logger.info(f"{name}: exiting subprocess")
@ -724,7 +724,7 @@ def process_frames(
detection_enabled: mp.Value, detection_enabled: mp.Value,
motion_enabled: mp.Value, motion_enabled: mp.Value,
stop_event, stop_event,
ptz_moving: mp.Value, ptz_stopped: mp.Event,
exit_on_empty: bool = False, exit_on_empty: bool = False,
): ):
# attribute labels are not tracked and are not assigned regions # attribute labels are not tracked and are not assigned regions
@ -769,7 +769,7 @@ def process_frames(
# look for motion if enabled # look for motion if enabled
motion_boxes = ( motion_boxes = (
motion_detector.detect(frame) motion_detector.detect(frame)
if motion_enabled.value and not ptz_moving.value if motion_enabled.value and ptz_stopped.is_set()
else [] else []
) )