mirror of
https://github.com/blakeblackshear/frigate.git
synced 2026-02-05 18:55:23 +03:00
Merge branch 'blakeblackshear:dev' into dev
This commit is contained in:
commit
b9925b3785
@ -134,9 +134,7 @@ RUN apt-get -qq update \
|
|||||||
libtbb2 libtbb-dev libdc1394-22-dev libopenexr-dev \
|
libtbb2 libtbb-dev libdc1394-22-dev libopenexr-dev \
|
||||||
libgstreamer-plugins-base1.0-dev libgstreamer1.0-dev \
|
libgstreamer-plugins-base1.0-dev libgstreamer1.0-dev \
|
||||||
# scipy dependencies
|
# scipy dependencies
|
||||||
gcc gfortran libopenblas-dev liblapack-dev \
|
gcc gfortran libopenblas-dev liblapack-dev && \
|
||||||
# faster-fifo dependencies
|
|
||||||
g++ cython3 && \
|
|
||||||
rm -rf /var/lib/apt/lists/*
|
rm -rf /var/lib/apt/lists/*
|
||||||
|
|
||||||
RUN wget -q https://bootstrap.pypa.io/get-pip.py -O get-pip.py \
|
RUN wget -q https://bootstrap.pypa.io/get-pip.py -O get-pip.py \
|
||||||
|
|||||||
@ -6,13 +6,12 @@ import shutil
|
|||||||
import signal
|
import signal
|
||||||
import sys
|
import sys
|
||||||
import traceback
|
import traceback
|
||||||
|
from multiprocessing import Queue
|
||||||
from multiprocessing.synchronize import Event as MpEvent
|
from multiprocessing.synchronize import Event as MpEvent
|
||||||
from types import FrameType
|
from types import FrameType
|
||||||
from typing import Optional
|
from typing import Optional
|
||||||
|
|
||||||
import faster_fifo as ff
|
|
||||||
import psutil
|
import psutil
|
||||||
from faster_fifo import Queue
|
|
||||||
from peewee_migrate import Router
|
from peewee_migrate import Router
|
||||||
from playhouse.sqlite_ext import SqliteExtDatabase
|
from playhouse.sqlite_ext import SqliteExtDatabase
|
||||||
from playhouse.sqliteq import SqliteQueueDatabase
|
from playhouse.sqliteq import SqliteQueueDatabase
|
||||||
@ -27,7 +26,6 @@ from frigate.const import (
|
|||||||
CLIPS_DIR,
|
CLIPS_DIR,
|
||||||
CONFIG_DIR,
|
CONFIG_DIR,
|
||||||
DEFAULT_DB_PATH,
|
DEFAULT_DB_PATH,
|
||||||
DEFAULT_QUEUE_BUFFER_SIZE,
|
|
||||||
EXPORT_DIR,
|
EXPORT_DIR,
|
||||||
MODEL_CACHE_DIR,
|
MODEL_CACHE_DIR,
|
||||||
RECORD_DIR,
|
RECORD_DIR,
|
||||||
@ -60,11 +58,11 @@ logger = logging.getLogger(__name__)
|
|||||||
class FrigateApp:
|
class FrigateApp:
|
||||||
def __init__(self) -> None:
|
def __init__(self) -> None:
|
||||||
self.stop_event: MpEvent = mp.Event()
|
self.stop_event: MpEvent = mp.Event()
|
||||||
self.detection_queue: Queue = ff.Queue()
|
self.detection_queue: Queue = mp.Queue()
|
||||||
self.detectors: dict[str, ObjectDetectProcess] = {}
|
self.detectors: dict[str, ObjectDetectProcess] = {}
|
||||||
self.detection_out_events: dict[str, MpEvent] = {}
|
self.detection_out_events: dict[str, MpEvent] = {}
|
||||||
self.detection_shms: list[mp.shared_memory.SharedMemory] = []
|
self.detection_shms: list[mp.shared_memory.SharedMemory] = []
|
||||||
self.log_queue: Queue = ff.Queue()
|
self.log_queue: Queue = mp.Queue()
|
||||||
self.plus_api = PlusApi()
|
self.plus_api = PlusApi()
|
||||||
self.camera_metrics: dict[str, CameraMetricsTypes] = {}
|
self.camera_metrics: dict[str, CameraMetricsTypes] = {}
|
||||||
self.feature_metrics: dict[str, FeatureMetricsTypes] = {}
|
self.feature_metrics: dict[str, FeatureMetricsTypes] = {}
|
||||||
@ -210,14 +208,8 @@ class FrigateApp:
|
|||||||
|
|
||||||
def init_queues(self) -> None:
|
def init_queues(self) -> None:
|
||||||
# Queues for clip processing
|
# Queues for clip processing
|
||||||
self.event_queue: Queue = ff.Queue(
|
self.event_queue: Queue = mp.Queue()
|
||||||
DEFAULT_QUEUE_BUFFER_SIZE
|
self.event_processed_queue: Queue = mp.Queue()
|
||||||
* sum(camera.enabled for camera in self.config.cameras.values())
|
|
||||||
)
|
|
||||||
self.event_processed_queue: Queue = ff.Queue(
|
|
||||||
DEFAULT_QUEUE_BUFFER_SIZE
|
|
||||||
* sum(camera.enabled for camera in self.config.cameras.values())
|
|
||||||
)
|
|
||||||
self.video_output_queue: Queue = mp.Queue(
|
self.video_output_queue: Queue = mp.Queue(
|
||||||
maxsize=sum(camera.enabled for camera in self.config.cameras.values()) * 2
|
maxsize=sum(camera.enabled for camera in self.config.cameras.values()) * 2
|
||||||
)
|
)
|
||||||
@ -228,29 +220,20 @@ class FrigateApp:
|
|||||||
)
|
)
|
||||||
|
|
||||||
# Queue for object recordings info
|
# Queue for object recordings info
|
||||||
self.object_recordings_info_queue: Queue = ff.Queue(
|
self.object_recordings_info_queue: Queue = mp.Queue()
|
||||||
DEFAULT_QUEUE_BUFFER_SIZE
|
|
||||||
* sum(camera.enabled for camera in self.config.cameras.values())
|
|
||||||
)
|
|
||||||
|
|
||||||
# Queue for audio recordings info if enabled
|
# Queue for audio recordings info if enabled
|
||||||
self.audio_recordings_info_queue: Optional[Queue] = (
|
self.audio_recordings_info_queue: Optional[Queue] = (
|
||||||
ff.Queue(
|
mp.Queue()
|
||||||
DEFAULT_QUEUE_BUFFER_SIZE
|
|
||||||
* sum(camera.audio.enabled for camera in self.config.cameras.values())
|
|
||||||
)
|
|
||||||
if len([c for c in self.config.cameras.values() if c.audio.enabled]) > 0
|
if len([c for c in self.config.cameras.values() if c.audio.enabled]) > 0
|
||||||
else None
|
else None
|
||||||
)
|
)
|
||||||
|
|
||||||
# Queue for timeline events
|
# Queue for timeline events
|
||||||
self.timeline_queue: Queue = ff.Queue(
|
self.timeline_queue: Queue = mp.Queue()
|
||||||
DEFAULT_QUEUE_BUFFER_SIZE
|
|
||||||
* sum(camera.enabled for camera in self.config.cameras.values())
|
|
||||||
)
|
|
||||||
|
|
||||||
# Queue for inter process communication
|
# Queue for inter process communication
|
||||||
self.inter_process_queue: Queue = ff.Queue(DEFAULT_QUEUE_BUFFER_SIZE)
|
self.inter_process_queue: Queue = mp.Queue()
|
||||||
|
|
||||||
def init_database(self) -> None:
|
def init_database(self) -> None:
|
||||||
def vacuum_db(db: SqliteExtDatabase) -> None:
|
def vacuum_db(db: SqliteExtDatabase) -> None:
|
||||||
|
|||||||
@ -1,11 +1,10 @@
|
|||||||
import multiprocessing as mp
|
import multiprocessing as mp
|
||||||
import queue
|
import queue
|
||||||
import threading
|
import threading
|
||||||
|
from multiprocessing import Queue
|
||||||
from multiprocessing.synchronize import Event as MpEvent
|
from multiprocessing.synchronize import Event as MpEvent
|
||||||
from typing import Callable
|
from typing import Callable
|
||||||
|
|
||||||
from faster_fifo import Queue
|
|
||||||
|
|
||||||
from frigate.comms.dispatcher import Communicator
|
from frigate.comms.dispatcher import Communicator
|
||||||
|
|
||||||
|
|
||||||
|
|||||||
@ -46,7 +46,3 @@ DRIVER_INTEL_iHD = "iHD"
|
|||||||
|
|
||||||
MAX_SEGMENT_DURATION = 600
|
MAX_SEGMENT_DURATION = 600
|
||||||
MAX_PLAYLIST_SECONDS = 7200 # support 2 hour segments for a single playlist to account for cameras with inconsistent segment times
|
MAX_PLAYLIST_SECONDS = 7200 # support 2 hour segments for a single playlist to account for cameras with inconsistent segment times
|
||||||
|
|
||||||
# Queue Values
|
|
||||||
|
|
||||||
DEFAULT_QUEUE_BUFFER_SIZE = 1000 * 1000 # 1MB
|
|
||||||
|
|||||||
@ -9,7 +9,6 @@ import threading
|
|||||||
from types import FrameType
|
from types import FrameType
|
||||||
from typing import Optional, Tuple
|
from typing import Optional, Tuple
|
||||||
|
|
||||||
import faster_fifo as ff
|
|
||||||
import numpy as np
|
import numpy as np
|
||||||
import requests
|
import requests
|
||||||
from setproctitle import setproctitle
|
from setproctitle import setproctitle
|
||||||
@ -52,7 +51,7 @@ def get_ffmpeg_command(input_args: list[str], input_path: str, pipe: str) -> lis
|
|||||||
|
|
||||||
def listen_to_audio(
|
def listen_to_audio(
|
||||||
config: FrigateConfig,
|
config: FrigateConfig,
|
||||||
recordings_info_queue: ff.Queue,
|
recordings_info_queue: mp.Queue,
|
||||||
process_info: dict[str, FeatureMetricsTypes],
|
process_info: dict[str, FeatureMetricsTypes],
|
||||||
inter_process_communicator: InterProcessCommunicator,
|
inter_process_communicator: InterProcessCommunicator,
|
||||||
) -> None:
|
) -> None:
|
||||||
@ -152,7 +151,7 @@ class AudioEventMaintainer(threading.Thread):
|
|||||||
def __init__(
|
def __init__(
|
||||||
self,
|
self,
|
||||||
camera: CameraConfig,
|
camera: CameraConfig,
|
||||||
recordings_info_queue: ff.Queue,
|
recordings_info_queue: mp.Queue,
|
||||||
feature_metrics: dict[str, FeatureMetricsTypes],
|
feature_metrics: dict[str, FeatureMetricsTypes],
|
||||||
stop_event: mp.Event,
|
stop_event: mp.Event,
|
||||||
inter_process_communicator: InterProcessCommunicator,
|
inter_process_communicator: InterProcessCommunicator,
|
||||||
|
|||||||
@ -6,10 +6,10 @@ import logging
|
|||||||
import os
|
import os
|
||||||
import random
|
import random
|
||||||
import string
|
import string
|
||||||
|
from multiprocessing import Queue
|
||||||
from typing import Optional
|
from typing import Optional
|
||||||
|
|
||||||
import cv2
|
import cv2
|
||||||
from faster_fifo import Queue
|
|
||||||
|
|
||||||
from frigate.config import CameraConfig, FrigateConfig
|
from frigate.config import CameraConfig, FrigateConfig
|
||||||
from frigate.const import CLIPS_DIR
|
from frigate.const import CLIPS_DIR
|
||||||
|
|||||||
@ -3,11 +3,10 @@ import logging
|
|||||||
import queue
|
import queue
|
||||||
import threading
|
import threading
|
||||||
from enum import Enum
|
from enum import Enum
|
||||||
|
from multiprocessing import Queue
|
||||||
from multiprocessing.synchronize import Event as MpEvent
|
from multiprocessing.synchronize import Event as MpEvent
|
||||||
from typing import Dict
|
from typing import Dict
|
||||||
|
|
||||||
from faster_fifo import Queue
|
|
||||||
|
|
||||||
from frigate.config import EventsConfig, FrigateConfig
|
from frigate.config import EventsConfig, FrigateConfig
|
||||||
from frigate.models import Event
|
from frigate.models import Event
|
||||||
from frigate.types import CameraMetricsTypes
|
from frigate.types import CameraMetricsTypes
|
||||||
|
|||||||
@ -7,10 +7,10 @@ import signal
|
|||||||
import threading
|
import threading
|
||||||
from collections import deque
|
from collections import deque
|
||||||
from logging import handlers
|
from logging import handlers
|
||||||
|
from multiprocessing import Queue
|
||||||
from types import FrameType
|
from types import FrameType
|
||||||
from typing import Deque, Optional
|
from typing import Deque, Optional
|
||||||
|
|
||||||
from faster_fifo import Queue
|
|
||||||
from setproctitle import setproctitle
|
from setproctitle import setproctitle
|
||||||
|
|
||||||
from frigate.util.builtin import clean_camera_user_pass
|
from frigate.util.builtin import clean_camera_user_pass
|
||||||
|
|||||||
@ -7,7 +7,6 @@ import signal
|
|||||||
import threading
|
import threading
|
||||||
from abc import ABC, abstractmethod
|
from abc import ABC, abstractmethod
|
||||||
|
|
||||||
import faster_fifo as ff
|
|
||||||
import numpy as np
|
import numpy as np
|
||||||
from setproctitle import setproctitle
|
from setproctitle import setproctitle
|
||||||
|
|
||||||
@ -78,7 +77,7 @@ class LocalObjectDetector(ObjectDetector):
|
|||||||
|
|
||||||
def run_detector(
|
def run_detector(
|
||||||
name: str,
|
name: str,
|
||||||
detection_queue: ff.Queue,
|
detection_queue: mp.Queue,
|
||||||
out_events: dict[str, mp.Event],
|
out_events: dict[str, mp.Event],
|
||||||
avg_speed,
|
avg_speed,
|
||||||
start,
|
start,
|
||||||
|
|||||||
@ -3,6 +3,7 @@
|
|||||||
import asyncio
|
import asyncio
|
||||||
import datetime
|
import datetime
|
||||||
import logging
|
import logging
|
||||||
|
import multiprocessing as mp
|
||||||
import os
|
import os
|
||||||
import queue
|
import queue
|
||||||
import random
|
import random
|
||||||
@ -14,7 +15,6 @@ from multiprocessing.synchronize import Event as MpEvent
|
|||||||
from pathlib import Path
|
from pathlib import Path
|
||||||
from typing import Any, Optional, Tuple
|
from typing import Any, Optional, Tuple
|
||||||
|
|
||||||
import faster_fifo as ff
|
|
||||||
import numpy as np
|
import numpy as np
|
||||||
import psutil
|
import psutil
|
||||||
|
|
||||||
@ -28,12 +28,31 @@ from frigate.util.services import get_video_properties
|
|||||||
logger = logging.getLogger(__name__)
|
logger = logging.getLogger(__name__)
|
||||||
|
|
||||||
|
|
||||||
|
class SegmentInfo:
|
||||||
|
def __init__(
|
||||||
|
self, motion_box_count: int, active_object_count: int, average_dBFS: int
|
||||||
|
) -> None:
|
||||||
|
self.motion_box_count = motion_box_count
|
||||||
|
self.active_object_count = active_object_count
|
||||||
|
self.average_dBFS = average_dBFS
|
||||||
|
|
||||||
|
def should_discard_segment(self, retain_mode: RetainModeEnum) -> bool:
|
||||||
|
return (
|
||||||
|
retain_mode == RetainModeEnum.motion
|
||||||
|
and self.motion_box_count == 0
|
||||||
|
and self.average_dBFS == 0
|
||||||
|
) or (
|
||||||
|
retain_mode == RetainModeEnum.active_objects
|
||||||
|
and self.active_object_count == 0
|
||||||
|
)
|
||||||
|
|
||||||
|
|
||||||
class RecordingMaintainer(threading.Thread):
|
class RecordingMaintainer(threading.Thread):
|
||||||
def __init__(
|
def __init__(
|
||||||
self,
|
self,
|
||||||
config: FrigateConfig,
|
config: FrigateConfig,
|
||||||
object_recordings_info_queue: ff.Queue,
|
object_recordings_info_queue: mp.Queue,
|
||||||
audio_recordings_info_queue: Optional[ff.Queue],
|
audio_recordings_info_queue: Optional[mp.Queue],
|
||||||
process_info: dict[str, FeatureMetricsTypes],
|
process_info: dict[str, FeatureMetricsTypes],
|
||||||
stop_event: MpEvent,
|
stop_event: MpEvent,
|
||||||
):
|
):
|
||||||
@ -234,7 +253,7 @@ class RecordingMaintainer(threading.Thread):
|
|||||||
|
|
||||||
def segment_stats(
|
def segment_stats(
|
||||||
self, camera: str, start_time: datetime.datetime, end_time: datetime.datetime
|
self, camera: str, start_time: datetime.datetime, end_time: datetime.datetime
|
||||||
) -> Tuple[int, int, int]:
|
) -> SegmentInfo:
|
||||||
active_count = 0
|
active_count = 0
|
||||||
motion_count = 0
|
motion_count = 0
|
||||||
for frame in self.object_recordings_info[camera]:
|
for frame in self.object_recordings_info[camera]:
|
||||||
@ -269,7 +288,7 @@ class RecordingMaintainer(threading.Thread):
|
|||||||
|
|
||||||
average_dBFS = 0 if not audio_values else np.average(audio_values)
|
average_dBFS = 0 if not audio_values else np.average(audio_values)
|
||||||
|
|
||||||
return (motion_count, active_count, round(average_dBFS))
|
return SegmentInfo(motion_count, active_count, round(average_dBFS))
|
||||||
|
|
||||||
def store_segment(
|
def store_segment(
|
||||||
self,
|
self,
|
||||||
@ -280,18 +299,10 @@ class RecordingMaintainer(threading.Thread):
|
|||||||
cache_path: str,
|
cache_path: str,
|
||||||
store_mode: RetainModeEnum,
|
store_mode: RetainModeEnum,
|
||||||
) -> None:
|
) -> None:
|
||||||
motion_count, active_count, averageDBFS = self.segment_stats(
|
segment_info = self.segment_stats(camera, start_time, end_time)
|
||||||
camera, start_time, end_time
|
|
||||||
)
|
|
||||||
|
|
||||||
# check if the segment shouldn't be stored
|
# check if the segment shouldn't be stored
|
||||||
if (
|
if segment_info.should_discard_segment(store_mode):
|
||||||
(store_mode == RetainModeEnum.motion and motion_count == 0)
|
|
||||||
or (
|
|
||||||
store_mode == RetainModeEnum.motion and averageDBFS == 0
|
|
||||||
) # dBFS is stored in a negative scale
|
|
||||||
or (store_mode == RetainModeEnum.active_objects and active_count == 0)
|
|
||||||
):
|
|
||||||
Path(cache_path).unlink(missing_ok=True)
|
Path(cache_path).unlink(missing_ok=True)
|
||||||
self.end_time_cache.pop(cache_path, None)
|
self.end_time_cache.pop(cache_path, None)
|
||||||
return
|
return
|
||||||
@ -364,10 +375,10 @@ class RecordingMaintainer(threading.Thread):
|
|||||||
start_time=start_time.timestamp(),
|
start_time=start_time.timestamp(),
|
||||||
end_time=end_time.timestamp(),
|
end_time=end_time.timestamp(),
|
||||||
duration=duration,
|
duration=duration,
|
||||||
motion=motion_count,
|
motion=segment_info.motion_box_count,
|
||||||
# TODO: update this to store list of active objects at some point
|
# TODO: update this to store list of active objects at some point
|
||||||
objects=active_count,
|
objects=segment_info.active_object_count,
|
||||||
dBFS=averageDBFS,
|
dBFS=segment_info.average_dBFS,
|
||||||
segment_size=segment_size,
|
segment_size=segment_size,
|
||||||
)
|
)
|
||||||
except Exception as e:
|
except Exception as e:
|
||||||
|
|||||||
@ -7,7 +7,6 @@ import threading
|
|||||||
from types import FrameType
|
from types import FrameType
|
||||||
from typing import Optional
|
from typing import Optional
|
||||||
|
|
||||||
import faster_fifo as ff
|
|
||||||
from playhouse.sqliteq import SqliteQueueDatabase
|
from playhouse.sqliteq import SqliteQueueDatabase
|
||||||
from setproctitle import setproctitle
|
from setproctitle import setproctitle
|
||||||
|
|
||||||
@ -23,8 +22,8 @@ logger = logging.getLogger(__name__)
|
|||||||
|
|
||||||
def manage_recordings(
|
def manage_recordings(
|
||||||
config: FrigateConfig,
|
config: FrigateConfig,
|
||||||
object_recordings_info_queue: ff.Queue,
|
object_recordings_info_queue: mp.Queue,
|
||||||
audio_recordings_info_queue: ff.Queue,
|
audio_recordings_info_queue: mp.Queue,
|
||||||
process_info: dict[str, FeatureMetricsTypes],
|
process_info: dict[str, FeatureMetricsTypes],
|
||||||
) -> None:
|
) -> None:
|
||||||
stop_event = mp.Event()
|
stop_event = mp.Event()
|
||||||
|
|||||||
33
frigate/test/test_record_retention.py
Normal file
33
frigate/test/test_record_retention.py
Normal file
@ -0,0 +1,33 @@
|
|||||||
|
import unittest
|
||||||
|
|
||||||
|
from frigate.config import RetainModeEnum
|
||||||
|
from frigate.record.maintainer import SegmentInfo
|
||||||
|
|
||||||
|
|
||||||
|
class TestRecordRetention(unittest.TestCase):
|
||||||
|
def test_motion_should_keep_motion_not_object(self):
|
||||||
|
segment_info = SegmentInfo(
|
||||||
|
motion_box_count=1, active_object_count=0, average_dBFS=0
|
||||||
|
)
|
||||||
|
assert not segment_info.should_discard_segment(RetainModeEnum.motion)
|
||||||
|
assert segment_info.should_discard_segment(RetainModeEnum.active_objects)
|
||||||
|
|
||||||
|
def test_object_should_keep_object_not_motion(self):
|
||||||
|
segment_info = SegmentInfo(
|
||||||
|
motion_box_count=0, active_object_count=1, average_dBFS=0
|
||||||
|
)
|
||||||
|
assert segment_info.should_discard_segment(RetainModeEnum.motion)
|
||||||
|
assert not segment_info.should_discard_segment(RetainModeEnum.active_objects)
|
||||||
|
|
||||||
|
def test_all_should_keep_all(self):
|
||||||
|
segment_info = SegmentInfo(
|
||||||
|
motion_box_count=0, active_object_count=0, average_dBFS=0
|
||||||
|
)
|
||||||
|
assert not segment_info.should_discard_segment(RetainModeEnum.all)
|
||||||
|
|
||||||
|
def test_should_keep_audio_in_motion_mode(self):
|
||||||
|
segment_info = SegmentInfo(
|
||||||
|
motion_box_count=0, active_object_count=0, average_dBFS=1
|
||||||
|
)
|
||||||
|
assert not segment_info.should_discard_segment(RetainModeEnum.motion)
|
||||||
|
assert segment_info.should_discard_segment(RetainModeEnum.active_objects)
|
||||||
@ -3,10 +3,9 @@
|
|||||||
import logging
|
import logging
|
||||||
import queue
|
import queue
|
||||||
import threading
|
import threading
|
||||||
|
from multiprocessing import Queue
|
||||||
from multiprocessing.synchronize import Event as MpEvent
|
from multiprocessing.synchronize import Event as MpEvent
|
||||||
|
|
||||||
from faster_fifo import Queue
|
|
||||||
|
|
||||||
from frigate.config import FrigateConfig
|
from frigate.config import FrigateConfig
|
||||||
from frigate.events.maintainer import EventTypeEnum
|
from frigate.events.maintainer import EventTypeEnum
|
||||||
from frigate.models import Timeline
|
from frigate.models import Timeline
|
||||||
|
|||||||
@ -1,10 +1,9 @@
|
|||||||
|
from multiprocessing import Queue
|
||||||
from multiprocessing.context import Process
|
from multiprocessing.context import Process
|
||||||
from multiprocessing.sharedctypes import Synchronized
|
from multiprocessing.sharedctypes import Synchronized
|
||||||
from multiprocessing.synchronize import Event
|
from multiprocessing.synchronize import Event
|
||||||
from typing import Optional, TypedDict
|
from typing import Optional, TypedDict
|
||||||
|
|
||||||
from faster_fifo import Queue
|
|
||||||
|
|
||||||
from frigate.object_detection import ObjectDetectProcess
|
from frigate.object_detection import ObjectDetectProcess
|
||||||
|
|
||||||
|
|
||||||
|
|||||||
@ -11,7 +11,6 @@ import time
|
|||||||
from collections import defaultdict
|
from collections import defaultdict
|
||||||
|
|
||||||
import cv2
|
import cv2
|
||||||
import faster_fifo as ff
|
|
||||||
import numpy as np
|
import numpy as np
|
||||||
from setproctitle import setproctitle
|
from setproctitle import setproctitle
|
||||||
|
|
||||||
@ -731,7 +730,7 @@ def get_consolidated_object_detections(detected_object_groups):
|
|||||||
|
|
||||||
def process_frames(
|
def process_frames(
|
||||||
camera_name: str,
|
camera_name: str,
|
||||||
frame_queue: ff.Queue,
|
frame_queue: mp.Queue,
|
||||||
frame_shape,
|
frame_shape,
|
||||||
model_config: ModelConfig,
|
model_config: ModelConfig,
|
||||||
detect_config: DetectConfig,
|
detect_config: DetectConfig,
|
||||||
@ -739,7 +738,7 @@ def process_frames(
|
|||||||
motion_detector: MotionDetector,
|
motion_detector: MotionDetector,
|
||||||
object_detector: RemoteObjectDetector,
|
object_detector: RemoteObjectDetector,
|
||||||
object_tracker: ObjectTracker,
|
object_tracker: ObjectTracker,
|
||||||
detected_objects_queue: ff.Queue,
|
detected_objects_queue: mp.Queue,
|
||||||
process_info: dict,
|
process_info: dict,
|
||||||
objects_to_track: list[str],
|
objects_to_track: list[str],
|
||||||
object_filters,
|
object_filters,
|
||||||
|
|||||||
@ -1,6 +1,5 @@
|
|||||||
click == 8.1.*
|
click == 8.1.*
|
||||||
Flask == 2.3.*
|
Flask == 2.3.*
|
||||||
faster-fifo == 1.4.*
|
|
||||||
imutils == 0.5.*
|
imutils == 0.5.*
|
||||||
matplotlib == 3.7.*
|
matplotlib == 3.7.*
|
||||||
mypy == 1.4.1
|
mypy == 1.4.1
|
||||||
|
|||||||
@ -65,6 +65,7 @@ export default function Button({
|
|||||||
className = '',
|
className = '',
|
||||||
color = 'blue',
|
color = 'blue',
|
||||||
disabled = false,
|
disabled = false,
|
||||||
|
ariaCapitalize = false,
|
||||||
href,
|
href,
|
||||||
type = 'contained',
|
type = 'contained',
|
||||||
...attrs
|
...attrs
|
||||||
@ -107,7 +108,7 @@ export default function Button({
|
|||||||
>
|
>
|
||||||
{children}
|
{children}
|
||||||
</Element>
|
</Element>
|
||||||
{hovered && attrs['aria-label'] ? <Tooltip text={attrs['aria-label']} relativeTo={ref} /> : null}
|
{hovered && attrs['aria-label'] ? <Tooltip text={attrs['aria-label']} relativeTo={ref} capitalize={ariaCapitalize} /> : null}
|
||||||
</Fragment>
|
</Fragment>
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|||||||
@ -39,7 +39,7 @@ export default function Box({
|
|||||||
))}
|
))}
|
||||||
<div class="flex-grow" />
|
<div class="flex-grow" />
|
||||||
{icons.map(({ name, icon: Icon, ...props }) => (
|
{icons.map(({ name, icon: Icon, ...props }) => (
|
||||||
<Button aria-label={name} className="rounded-full" key={name} type="text" {...props}>
|
<Button aria-label={name} ariaCapitalize={true} className="rounded-full" key={name} type="text" {...props}>
|
||||||
<Icon className="w-6" />
|
<Icon className="w-6" />
|
||||||
</Button>
|
</Button>
|
||||||
))}
|
))}
|
||||||
|
|||||||
@ -4,7 +4,7 @@ import { useLayoutEffect, useRef, useState } from 'preact/hooks';
|
|||||||
|
|
||||||
const TIP_SPACE = 20;
|
const TIP_SPACE = 20;
|
||||||
|
|
||||||
export default function Tooltip({ relativeTo, text }) {
|
export default function Tooltip({ relativeTo, text, capitalize }) {
|
||||||
const [position, setPosition] = useState({ top: -9999, left: -9999 });
|
const [position, setPosition] = useState({ top: -9999, left: -9999 });
|
||||||
const portalRoot = document.getElementById('tooltips');
|
const portalRoot = document.getElementById('tooltips');
|
||||||
const ref = useRef();
|
const ref = useRef();
|
||||||
@ -49,9 +49,9 @@ export default function Tooltip({ relativeTo, text }) {
|
|||||||
const tooltip = (
|
const tooltip = (
|
||||||
<div
|
<div
|
||||||
role="tooltip"
|
role="tooltip"
|
||||||
className={`shadow max-w-lg absolute pointer-events-none bg-gray-900 dark:bg-gray-200 bg-opacity-80 rounded px-2 py-1 transition-transform transition-opacity duration-75 transform scale-90 opacity-0 text-gray-100 dark:text-gray-900 text-sm capitalize ${
|
className={`shadow max-w-lg absolute pointer-events-none bg-gray-900 dark:bg-gray-200 bg-opacity-80 rounded px-2 py-1 transition-transform transition-opacity duration-75 transform scale-90 opacity-0 text-gray-100 dark:text-gray-900 text-sm ${
|
||||||
position.top >= 0 ? 'opacity-100 scale-100' : ''
|
capitalize ? 'capitalize' : ''
|
||||||
}`}
|
} ${position.top >= 0 ? 'opacity-100 scale-100' : ''}`}
|
||||||
ref={ref}
|
ref={ref}
|
||||||
style={position}
|
style={position}
|
||||||
>
|
>
|
||||||
|
|||||||
Loading…
Reference in New Issue
Block a user