mirror of
https://github.com/blakeblackshear/frigate.git
synced 2026-01-26 22:18:30 +03:00
Miscellaneous fixes (0.17 beta) (#21350)
Some checks failed
CI / AMD64 Build (push) Has been cancelled
CI / ARM Build (push) Has been cancelled
CI / Jetson Jetpack 6 (push) Has been cancelled
CI / AMD64 Extra Build (push) Has been cancelled
CI / ARM Extra Build (push) Has been cancelled
CI / Synaptics Build (push) Has been cancelled
CI / Assemble and push default build (push) Has been cancelled
Some checks failed
CI / AMD64 Build (push) Has been cancelled
CI / ARM Build (push) Has been cancelled
CI / Jetson Jetpack 6 (push) Has been cancelled
CI / AMD64 Extra Build (push) Has been cancelled
CI / ARM Extra Build (push) Has been cancelled
CI / Synaptics Build (push) Has been cancelled
CI / Assemble and push default build (push) Has been cancelled
* Fix genai callbacks in MQTT * Cleanup cursor pointer for classification cards * Cleanup * Handle unknown SOCs for RKNN converter by only using known SOCs * don't allow "none" as a classification class name * change internal port user to admin and default unspecified username to viewer * keep 5000 as anonymous user * suppress tensorflow logging during classification training * Always apply base log level suppressions for noisy third-party libraries even if no specific logConfig is provided * remove decorator and specifically suppress TFLite delegate creation messages --------- Co-authored-by: Josh Hawkins <32435876+hawkeye217@users.noreply.github.com>
This commit is contained in:
parent
6a0e31dcf9
commit
e636449d56
@ -237,8 +237,18 @@ ENV PYTHONWARNINGS="ignore:::numpy.core.getlimits"
|
|||||||
# Set HailoRT to disable logging
|
# Set HailoRT to disable logging
|
||||||
ENV HAILORT_LOGGER_PATH=NONE
|
ENV HAILORT_LOGGER_PATH=NONE
|
||||||
|
|
||||||
# TensorFlow error only
|
# TensorFlow C++ logging suppression (must be set before import)
|
||||||
|
# TF_CPP_MIN_LOG_LEVEL: 0=all, 1=INFO+, 2=WARNING+, 3=ERROR+ (we use 3 for errors only)
|
||||||
ENV TF_CPP_MIN_LOG_LEVEL=3
|
ENV TF_CPP_MIN_LOG_LEVEL=3
|
||||||
|
# Suppress verbose logging from TensorFlow C++ code
|
||||||
|
ENV TF_CPP_MIN_VLOG_LEVEL=3
|
||||||
|
# Disable oneDNN optimization messages ("optimized with oneDNN...")
|
||||||
|
ENV TF_ENABLE_ONEDNN_OPTS=0
|
||||||
|
# Suppress AutoGraph verbosity during conversion
|
||||||
|
ENV AUTOGRAPH_VERBOSITY=0
|
||||||
|
# Google Logging (GLOG) suppression for TensorFlow components
|
||||||
|
ENV GLOG_minloglevel=3
|
||||||
|
ENV GLOG_logtostderr=0
|
||||||
|
|
||||||
ENV PATH="/usr/local/go2rtc/bin:/usr/local/tempio/bin:/usr/local/nginx/sbin:${PATH}"
|
ENV PATH="/usr/local/go2rtc/bin:/usr/local/tempio/bin:/usr/local/nginx/sbin:${PATH}"
|
||||||
|
|
||||||
|
|||||||
2
docs/static/frigate-api.yaml
vendored
2
docs/static/frigate-api.yaml
vendored
@ -25,7 +25,7 @@ paths:
|
|||||||
description: Authentication Accepted (no response body, different headers depending on auth method)
|
description: Authentication Accepted (no response body, different headers depending on auth method)
|
||||||
headers:
|
headers:
|
||||||
remote-user:
|
remote-user:
|
||||||
description: Authenticated username or "anonymous" in proxy-only mode
|
description: Authenticated username or "viewer" in proxy-only mode
|
||||||
schema:
|
schema:
|
||||||
type: string
|
type: string
|
||||||
remote-role:
|
remote-role:
|
||||||
|
|||||||
@ -167,7 +167,7 @@ def allow_any_authenticated():
|
|||||||
Allows:
|
Allows:
|
||||||
- Port 5000 internal requests (remote-user: "anonymous", remote-role: "admin")
|
- Port 5000 internal requests (remote-user: "anonymous", remote-role: "admin")
|
||||||
- Authenticated users with JWT tokens (remote-user: username)
|
- Authenticated users with JWT tokens (remote-user: username)
|
||||||
- Unauthenticated requests when auth is disabled (remote-user: "anonymous")
|
- Unauthenticated requests when auth is disabled (remote-user: "viewer")
|
||||||
|
|
||||||
Rejects:
|
Rejects:
|
||||||
- Requests with no remote-user header (did not pass through /auth endpoint)
|
- Requests with no remote-user header (did not pass through /auth endpoint)
|
||||||
@ -550,7 +550,7 @@ def resolve_role(
|
|||||||
"description": "Authentication Accepted (no response body)",
|
"description": "Authentication Accepted (no response body)",
|
||||||
"headers": {
|
"headers": {
|
||||||
"remote-user": {
|
"remote-user": {
|
||||||
"description": 'Authenticated username or "anonymous" in proxy-only mode',
|
"description": 'Authenticated username or "viewer" in proxy-only mode',
|
||||||
"schema": {"type": "string"},
|
"schema": {"type": "string"},
|
||||||
},
|
},
|
||||||
"remote-role": {
|
"remote-role": {
|
||||||
@ -592,12 +592,12 @@ def auth(request: Request):
|
|||||||
# if auth is disabled, just apply the proxy header map and return success
|
# if auth is disabled, just apply the proxy header map and return success
|
||||||
if not auth_config.enabled:
|
if not auth_config.enabled:
|
||||||
# pass the user header value from the upstream proxy if a mapping is specified
|
# pass the user header value from the upstream proxy if a mapping is specified
|
||||||
# or use anonymous if none are specified
|
# or use viewer if none are specified
|
||||||
user_header = proxy_config.header_map.user
|
user_header = proxy_config.header_map.user
|
||||||
success_response.headers["remote-user"] = (
|
success_response.headers["remote-user"] = (
|
||||||
request.headers.get(user_header, default="anonymous")
|
request.headers.get(user_header, default="viewer")
|
||||||
if user_header
|
if user_header
|
||||||
else "anonymous"
|
else "viewer"
|
||||||
)
|
)
|
||||||
|
|
||||||
# parse header and resolve a valid role
|
# parse header and resolve a valid role
|
||||||
@ -712,7 +712,7 @@ def auth(request: Request):
|
|||||||
description="Returns the current authenticated user's profile including username, role, and allowed cameras. This endpoint requires authentication and returns information about the user's permissions.",
|
description="Returns the current authenticated user's profile including username, role, and allowed cameras. This endpoint requires authentication and returns information about the user's permissions.",
|
||||||
)
|
)
|
||||||
def profile(request: Request):
|
def profile(request: Request):
|
||||||
username = request.headers.get("remote-user", "anonymous")
|
username = request.headers.get("remote-user", "viewer")
|
||||||
role = request.headers.get("remote-role", "viewer")
|
role = request.headers.get("remote-role", "viewer")
|
||||||
|
|
||||||
all_camera_names = set(request.app.frigate_config.cameras.keys())
|
all_camera_names = set(request.app.frigate_config.cameras.keys())
|
||||||
|
|||||||
@ -225,7 +225,8 @@ class MqttClient(Communicator):
|
|||||||
"birdseye_mode",
|
"birdseye_mode",
|
||||||
"review_alerts",
|
"review_alerts",
|
||||||
"review_detections",
|
"review_detections",
|
||||||
"genai",
|
"object_descriptions",
|
||||||
|
"review_descriptions",
|
||||||
]
|
]
|
||||||
|
|
||||||
for name in self.config.cameras.keys():
|
for name in self.config.cameras.keys():
|
||||||
|
|||||||
@ -77,6 +77,9 @@ FFMPEG_HWACCEL_RKMPP = "preset-rkmpp"
|
|||||||
FFMPEG_HWACCEL_AMF = "preset-amd-amf"
|
FFMPEG_HWACCEL_AMF = "preset-amd-amf"
|
||||||
FFMPEG_HVC1_ARGS = ["-tag:v", "hvc1"]
|
FFMPEG_HVC1_ARGS = ["-tag:v", "hvc1"]
|
||||||
|
|
||||||
|
# RKNN constants
|
||||||
|
SUPPORTED_RK_SOCS = ["rk3562", "rk3566", "rk3568", "rk3576", "rk3588"]
|
||||||
|
|
||||||
# Regex constants
|
# Regex constants
|
||||||
|
|
||||||
REGEX_CAMERA_NAME = r"^[a-zA-Z0-9_-]+$"
|
REGEX_CAMERA_NAME = r"^[a-zA-Z0-9_-]+$"
|
||||||
|
|||||||
@ -13,7 +13,7 @@ from frigate.comms.event_metadata_updater import (
|
|||||||
)
|
)
|
||||||
from frigate.config import FrigateConfig
|
from frigate.config import FrigateConfig
|
||||||
from frigate.const import MODEL_CACHE_DIR
|
from frigate.const import MODEL_CACHE_DIR
|
||||||
from frigate.log import redirect_output_to_logger
|
from frigate.log import suppress_stderr_during
|
||||||
from frigate.util.object import calculate_region
|
from frigate.util.object import calculate_region
|
||||||
|
|
||||||
from ..types import DataProcessorMetrics
|
from ..types import DataProcessorMetrics
|
||||||
@ -80,13 +80,14 @@ class BirdRealTimeProcessor(RealTimeProcessorApi):
|
|||||||
except Exception as e:
|
except Exception as e:
|
||||||
logger.error(f"Failed to download {path}: {e}")
|
logger.error(f"Failed to download {path}: {e}")
|
||||||
|
|
||||||
@redirect_output_to_logger(logger, logging.DEBUG)
|
|
||||||
def __build_detector(self) -> None:
|
def __build_detector(self) -> None:
|
||||||
self.interpreter = Interpreter(
|
# Suppress TFLite delegate creation messages that bypass Python logging
|
||||||
model_path=os.path.join(MODEL_CACHE_DIR, "bird/bird.tflite"),
|
with suppress_stderr_during("tflite_interpreter_init"):
|
||||||
num_threads=2,
|
self.interpreter = Interpreter(
|
||||||
)
|
model_path=os.path.join(MODEL_CACHE_DIR, "bird/bird.tflite"),
|
||||||
self.interpreter.allocate_tensors()
|
num_threads=2,
|
||||||
|
)
|
||||||
|
self.interpreter.allocate_tensors()
|
||||||
self.tensor_input_details = self.interpreter.get_input_details()
|
self.tensor_input_details = self.interpreter.get_input_details()
|
||||||
self.tensor_output_details = self.interpreter.get_output_details()
|
self.tensor_output_details = self.interpreter.get_output_details()
|
||||||
|
|
||||||
|
|||||||
@ -21,7 +21,7 @@ from frigate.config.classification import (
|
|||||||
ObjectClassificationType,
|
ObjectClassificationType,
|
||||||
)
|
)
|
||||||
from frigate.const import CLIPS_DIR, MODEL_CACHE_DIR
|
from frigate.const import CLIPS_DIR, MODEL_CACHE_DIR
|
||||||
from frigate.log import redirect_output_to_logger
|
from frigate.log import suppress_stderr_during
|
||||||
from frigate.types import TrackedObjectUpdateTypesEnum
|
from frigate.types import TrackedObjectUpdateTypesEnum
|
||||||
from frigate.util.builtin import EventsPerSecond, InferenceSpeed, load_labels
|
from frigate.util.builtin import EventsPerSecond, InferenceSpeed, load_labels
|
||||||
from frigate.util.object import box_overlaps, calculate_region
|
from frigate.util.object import box_overlaps, calculate_region
|
||||||
@ -72,7 +72,6 @@ class CustomStateClassificationProcessor(RealTimeProcessorApi):
|
|||||||
self.last_run = datetime.datetime.now().timestamp()
|
self.last_run = datetime.datetime.now().timestamp()
|
||||||
self.__build_detector()
|
self.__build_detector()
|
||||||
|
|
||||||
@redirect_output_to_logger(logger, logging.DEBUG)
|
|
||||||
def __build_detector(self) -> None:
|
def __build_detector(self) -> None:
|
||||||
try:
|
try:
|
||||||
from tflite_runtime.interpreter import Interpreter
|
from tflite_runtime.interpreter import Interpreter
|
||||||
@ -89,11 +88,13 @@ class CustomStateClassificationProcessor(RealTimeProcessorApi):
|
|||||||
self.labelmap = {}
|
self.labelmap = {}
|
||||||
return
|
return
|
||||||
|
|
||||||
self.interpreter = Interpreter(
|
# Suppress TFLite delegate creation messages that bypass Python logging
|
||||||
model_path=model_path,
|
with suppress_stderr_during("tflite_interpreter_init"):
|
||||||
num_threads=2,
|
self.interpreter = Interpreter(
|
||||||
)
|
model_path=model_path,
|
||||||
self.interpreter.allocate_tensors()
|
num_threads=2,
|
||||||
|
)
|
||||||
|
self.interpreter.allocate_tensors()
|
||||||
self.tensor_input_details = self.interpreter.get_input_details()
|
self.tensor_input_details = self.interpreter.get_input_details()
|
||||||
self.tensor_output_details = self.interpreter.get_output_details()
|
self.tensor_output_details = self.interpreter.get_output_details()
|
||||||
self.labelmap = load_labels(labelmap_path, prefill=0)
|
self.labelmap = load_labels(labelmap_path, prefill=0)
|
||||||
@ -377,7 +378,6 @@ class CustomObjectClassificationProcessor(RealTimeProcessorApi):
|
|||||||
|
|
||||||
self.__build_detector()
|
self.__build_detector()
|
||||||
|
|
||||||
@redirect_output_to_logger(logger, logging.DEBUG)
|
|
||||||
def __build_detector(self) -> None:
|
def __build_detector(self) -> None:
|
||||||
model_path = os.path.join(self.model_dir, "model.tflite")
|
model_path = os.path.join(self.model_dir, "model.tflite")
|
||||||
labelmap_path = os.path.join(self.model_dir, "labelmap.txt")
|
labelmap_path = os.path.join(self.model_dir, "labelmap.txt")
|
||||||
@ -389,11 +389,13 @@ class CustomObjectClassificationProcessor(RealTimeProcessorApi):
|
|||||||
self.labelmap = {}
|
self.labelmap = {}
|
||||||
return
|
return
|
||||||
|
|
||||||
self.interpreter = Interpreter(
|
# Suppress TFLite delegate creation messages that bypass Python logging
|
||||||
model_path=model_path,
|
with suppress_stderr_during("tflite_interpreter_init"):
|
||||||
num_threads=2,
|
self.interpreter = Interpreter(
|
||||||
)
|
model_path=model_path,
|
||||||
self.interpreter.allocate_tensors()
|
num_threads=2,
|
||||||
|
)
|
||||||
|
self.interpreter.allocate_tensors()
|
||||||
self.tensor_input_details = self.interpreter.get_input_details()
|
self.tensor_input_details = self.interpreter.get_input_details()
|
||||||
self.tensor_output_details = self.interpreter.get_output_details()
|
self.tensor_output_details = self.interpreter.get_output_details()
|
||||||
self.labelmap = load_labels(labelmap_path, prefill=0)
|
self.labelmap = load_labels(labelmap_path, prefill=0)
|
||||||
|
|||||||
@ -5,7 +5,7 @@ from typing_extensions import Literal
|
|||||||
|
|
||||||
from frigate.detectors.detection_api import DetectionApi
|
from frigate.detectors.detection_api import DetectionApi
|
||||||
from frigate.detectors.detector_config import BaseDetectorConfig
|
from frigate.detectors.detector_config import BaseDetectorConfig
|
||||||
from frigate.log import redirect_output_to_logger
|
from frigate.log import suppress_stderr_during
|
||||||
|
|
||||||
from ..detector_utils import tflite_detect_raw, tflite_init
|
from ..detector_utils import tflite_detect_raw, tflite_init
|
||||||
|
|
||||||
@ -28,12 +28,13 @@ class CpuDetectorConfig(BaseDetectorConfig):
|
|||||||
class CpuTfl(DetectionApi):
|
class CpuTfl(DetectionApi):
|
||||||
type_key = DETECTOR_KEY
|
type_key = DETECTOR_KEY
|
||||||
|
|
||||||
@redirect_output_to_logger(logger, logging.DEBUG)
|
|
||||||
def __init__(self, detector_config: CpuDetectorConfig):
|
def __init__(self, detector_config: CpuDetectorConfig):
|
||||||
interpreter = Interpreter(
|
# Suppress TFLite delegate creation messages that bypass Python logging
|
||||||
model_path=detector_config.model.path,
|
with suppress_stderr_during("tflite_interpreter_init"):
|
||||||
num_threads=detector_config.num_threads or 3,
|
interpreter = Interpreter(
|
||||||
)
|
model_path=detector_config.model.path,
|
||||||
|
num_threads=detector_config.num_threads or 3,
|
||||||
|
)
|
||||||
|
|
||||||
tflite_init(self, interpreter)
|
tflite_init(self, interpreter)
|
||||||
|
|
||||||
|
|||||||
@ -8,7 +8,7 @@ import cv2
|
|||||||
import numpy as np
|
import numpy as np
|
||||||
from pydantic import Field
|
from pydantic import Field
|
||||||
|
|
||||||
from frigate.const import MODEL_CACHE_DIR
|
from frigate.const import MODEL_CACHE_DIR, SUPPORTED_RK_SOCS
|
||||||
from frigate.detectors.detection_api import DetectionApi
|
from frigate.detectors.detection_api import DetectionApi
|
||||||
from frigate.detectors.detection_runners import RKNNModelRunner
|
from frigate.detectors.detection_runners import RKNNModelRunner
|
||||||
from frigate.detectors.detector_config import BaseDetectorConfig, ModelTypeEnum
|
from frigate.detectors.detector_config import BaseDetectorConfig, ModelTypeEnum
|
||||||
@ -19,8 +19,6 @@ logger = logging.getLogger(__name__)
|
|||||||
|
|
||||||
DETECTOR_KEY = "rknn"
|
DETECTOR_KEY = "rknn"
|
||||||
|
|
||||||
supported_socs = ["rk3562", "rk3566", "rk3568", "rk3576", "rk3588"]
|
|
||||||
|
|
||||||
supported_models = {
|
supported_models = {
|
||||||
ModelTypeEnum.yologeneric: "^frigate-fp16-yolov9-[cemst]$",
|
ModelTypeEnum.yologeneric: "^frigate-fp16-yolov9-[cemst]$",
|
||||||
ModelTypeEnum.yolonas: "^deci-fp16-yolonas_[sml]$",
|
ModelTypeEnum.yolonas: "^deci-fp16-yolonas_[sml]$",
|
||||||
@ -82,9 +80,9 @@ class Rknn(DetectionApi):
|
|||||||
except FileNotFoundError:
|
except FileNotFoundError:
|
||||||
raise Exception("Make sure to run docker in privileged mode.")
|
raise Exception("Make sure to run docker in privileged mode.")
|
||||||
|
|
||||||
if soc not in supported_socs:
|
if soc not in SUPPORTED_RK_SOCS:
|
||||||
raise Exception(
|
raise Exception(
|
||||||
f"Your SoC is not supported. Your SoC is: {soc}. Currently these SoCs are supported: {supported_socs}."
|
f"Your SoC is not supported. Your SoC is: {soc}. Currently these SoCs are supported: {SUPPORTED_RK_SOCS}."
|
||||||
)
|
)
|
||||||
|
|
||||||
return soc
|
return soc
|
||||||
|
|||||||
@ -8,7 +8,7 @@ import numpy as np
|
|||||||
from frigate.const import MODEL_CACHE_DIR
|
from frigate.const import MODEL_CACHE_DIR
|
||||||
from frigate.detectors.detection_runners import get_optimized_runner
|
from frigate.detectors.detection_runners import get_optimized_runner
|
||||||
from frigate.embeddings.types import EnrichmentModelTypeEnum
|
from frigate.embeddings.types import EnrichmentModelTypeEnum
|
||||||
from frigate.log import redirect_output_to_logger
|
from frigate.log import suppress_stderr_during
|
||||||
from frigate.util.downloader import ModelDownloader
|
from frigate.util.downloader import ModelDownloader
|
||||||
|
|
||||||
from ...config import FaceRecognitionConfig
|
from ...config import FaceRecognitionConfig
|
||||||
@ -57,17 +57,18 @@ class FaceNetEmbedding(BaseEmbedding):
|
|||||||
self._load_model_and_utils()
|
self._load_model_and_utils()
|
||||||
logger.debug(f"models are already downloaded for {self.model_name}")
|
logger.debug(f"models are already downloaded for {self.model_name}")
|
||||||
|
|
||||||
@redirect_output_to_logger(logger, logging.DEBUG)
|
|
||||||
def _load_model_and_utils(self):
|
def _load_model_and_utils(self):
|
||||||
if self.runner is None:
|
if self.runner is None:
|
||||||
if self.downloader:
|
if self.downloader:
|
||||||
self.downloader.wait_for_download()
|
self.downloader.wait_for_download()
|
||||||
|
|
||||||
self.runner = Interpreter(
|
# Suppress TFLite delegate creation messages that bypass Python logging
|
||||||
model_path=os.path.join(MODEL_CACHE_DIR, "facedet/facenet.tflite"),
|
with suppress_stderr_during("tflite_interpreter_init"):
|
||||||
num_threads=2,
|
self.runner = Interpreter(
|
||||||
)
|
model_path=os.path.join(MODEL_CACHE_DIR, "facedet/facenet.tflite"),
|
||||||
self.runner.allocate_tensors()
|
num_threads=2,
|
||||||
|
)
|
||||||
|
self.runner.allocate_tensors()
|
||||||
self.tensor_input_details = self.runner.get_input_details()
|
self.tensor_input_details = self.runner.get_input_details()
|
||||||
self.tensor_output_details = self.runner.get_output_details()
|
self.tensor_output_details = self.runner.get_output_details()
|
||||||
|
|
||||||
|
|||||||
@ -34,7 +34,7 @@ from frigate.data_processing.real_time.audio_transcription import (
|
|||||||
AudioTranscriptionRealTimeProcessor,
|
AudioTranscriptionRealTimeProcessor,
|
||||||
)
|
)
|
||||||
from frigate.ffmpeg_presets import parse_preset_input
|
from frigate.ffmpeg_presets import parse_preset_input
|
||||||
from frigate.log import LogPipe, redirect_output_to_logger
|
from frigate.log import LogPipe, suppress_stderr_during
|
||||||
from frigate.object_detection.base import load_labels
|
from frigate.object_detection.base import load_labels
|
||||||
from frigate.util.builtin import get_ffmpeg_arg_list
|
from frigate.util.builtin import get_ffmpeg_arg_list
|
||||||
from frigate.util.process import FrigateProcess
|
from frigate.util.process import FrigateProcess
|
||||||
@ -367,17 +367,17 @@ class AudioEventMaintainer(threading.Thread):
|
|||||||
|
|
||||||
|
|
||||||
class AudioTfl:
|
class AudioTfl:
|
||||||
@redirect_output_to_logger(logger, logging.DEBUG)
|
|
||||||
def __init__(self, stop_event: threading.Event, num_threads=2):
|
def __init__(self, stop_event: threading.Event, num_threads=2):
|
||||||
self.stop_event = stop_event
|
self.stop_event = stop_event
|
||||||
self.num_threads = num_threads
|
self.num_threads = num_threads
|
||||||
self.labels = load_labels("/audio-labelmap.txt", prefill=521)
|
self.labels = load_labels("/audio-labelmap.txt", prefill=521)
|
||||||
self.interpreter = Interpreter(
|
# Suppress TFLite delegate creation messages that bypass Python logging
|
||||||
model_path="/cpu_audio_model.tflite",
|
with suppress_stderr_during("tflite_interpreter_init"):
|
||||||
num_threads=self.num_threads,
|
self.interpreter = Interpreter(
|
||||||
)
|
model_path="/cpu_audio_model.tflite",
|
||||||
|
num_threads=self.num_threads,
|
||||||
self.interpreter.allocate_tensors()
|
)
|
||||||
|
self.interpreter.allocate_tensors()
|
||||||
|
|
||||||
self.tensor_input_details = self.interpreter.get_input_details()
|
self.tensor_input_details = self.interpreter.get_input_details()
|
||||||
self.tensor_output_details = self.interpreter.get_output_details()
|
self.tensor_output_details = self.interpreter.get_output_details()
|
||||||
|
|||||||
@ -80,10 +80,15 @@ def apply_log_levels(default: str, log_levels: dict[str, LogLevel]) -> None:
|
|||||||
log_levels = {
|
log_levels = {
|
||||||
"absl": LogLevel.error,
|
"absl": LogLevel.error,
|
||||||
"httpx": LogLevel.error,
|
"httpx": LogLevel.error,
|
||||||
|
"h5py": LogLevel.error,
|
||||||
|
"keras": LogLevel.error,
|
||||||
"matplotlib": LogLevel.error,
|
"matplotlib": LogLevel.error,
|
||||||
"tensorflow": LogLevel.error,
|
"tensorflow": LogLevel.error,
|
||||||
|
"tensorflow.python": LogLevel.error,
|
||||||
"werkzeug": LogLevel.error,
|
"werkzeug": LogLevel.error,
|
||||||
"ws4py": LogLevel.error,
|
"ws4py": LogLevel.error,
|
||||||
|
"PIL": LogLevel.warning,
|
||||||
|
"numba": LogLevel.warning,
|
||||||
**log_levels,
|
**log_levels,
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -318,3 +323,31 @@ def suppress_os_output(func: Callable) -> Callable:
|
|||||||
return result
|
return result
|
||||||
|
|
||||||
return wrapper
|
return wrapper
|
||||||
|
|
||||||
|
|
||||||
|
@contextmanager
|
||||||
|
def suppress_stderr_during(operation_name: str) -> Generator[None, None, None]:
|
||||||
|
"""
|
||||||
|
Context manager to suppress stderr output during a specific operation.
|
||||||
|
|
||||||
|
Useful for silencing LLVM debug output, CUDA messages, and other native
|
||||||
|
library logging that cannot be controlled via Python logging or environment
|
||||||
|
variables. Completely redirects file descriptor 2 (stderr) to /dev/null.
|
||||||
|
|
||||||
|
Usage:
|
||||||
|
with suppress_stderr_during("model_conversion"):
|
||||||
|
converter = tf.lite.TFLiteConverter.from_keras_model(model)
|
||||||
|
tflite_model = converter.convert()
|
||||||
|
|
||||||
|
Args:
|
||||||
|
operation_name: Name of the operation for debugging purposes
|
||||||
|
"""
|
||||||
|
original_stderr_fd = os.dup(2)
|
||||||
|
devnull = os.open(os.devnull, os.O_WRONLY)
|
||||||
|
try:
|
||||||
|
os.dup2(devnull, 2)
|
||||||
|
yield
|
||||||
|
finally:
|
||||||
|
os.dup2(original_stderr_fd, 2)
|
||||||
|
os.close(devnull)
|
||||||
|
os.close(original_stderr_fd)
|
||||||
|
|||||||
@ -19,7 +19,7 @@ from frigate.const import (
|
|||||||
PROCESS_PRIORITY_LOW,
|
PROCESS_PRIORITY_LOW,
|
||||||
UPDATE_MODEL_STATE,
|
UPDATE_MODEL_STATE,
|
||||||
)
|
)
|
||||||
from frigate.log import redirect_output_to_logger
|
from frigate.log import redirect_output_to_logger, suppress_stderr_during
|
||||||
from frigate.models import Event, Recordings, ReviewSegment
|
from frigate.models import Event, Recordings, ReviewSegment
|
||||||
from frigate.types import ModelStatusTypesEnum
|
from frigate.types import ModelStatusTypesEnum
|
||||||
from frigate.util.downloader import ModelDownloader
|
from frigate.util.downloader import ModelDownloader
|
||||||
@ -250,15 +250,20 @@ class ClassificationTrainingProcess(FrigateProcess):
|
|||||||
logger.debug(f"Converting {self.model_name} to TFLite...")
|
logger.debug(f"Converting {self.model_name} to TFLite...")
|
||||||
|
|
||||||
# convert model to tflite
|
# convert model to tflite
|
||||||
converter = tf.lite.TFLiteConverter.from_keras_model(model)
|
# Suppress stderr during conversion to avoid LLVM debug output
|
||||||
converter.optimizations = [tf.lite.Optimize.DEFAULT]
|
# (fully_quantize, inference_type, MLIR optimization messages, etc)
|
||||||
converter.representative_dataset = (
|
with suppress_stderr_during("tflite_conversion"):
|
||||||
self.__generate_representative_dataset_factory(dataset_dir)
|
converter = tf.lite.TFLiteConverter.from_keras_model(model)
|
||||||
)
|
converter.optimizations = [tf.lite.Optimize.DEFAULT]
|
||||||
converter.target_spec.supported_ops = [tf.lite.OpsSet.TFLITE_BUILTINS_INT8]
|
converter.representative_dataset = (
|
||||||
converter.inference_input_type = tf.uint8
|
self.__generate_representative_dataset_factory(dataset_dir)
|
||||||
converter.inference_output_type = tf.uint8
|
)
|
||||||
tflite_model = converter.convert()
|
converter.target_spec.supported_ops = [
|
||||||
|
tf.lite.OpsSet.TFLITE_BUILTINS_INT8
|
||||||
|
]
|
||||||
|
converter.inference_input_type = tf.uint8
|
||||||
|
converter.inference_output_type = tf.uint8
|
||||||
|
tflite_model = converter.convert()
|
||||||
|
|
||||||
# write model
|
# write model
|
||||||
model_path = os.path.join(model_dir, "model.tflite")
|
model_path = os.path.join(model_dir, "model.tflite")
|
||||||
|
|||||||
@ -65,10 +65,15 @@ class FrigateProcess(BaseProcess):
|
|||||||
logging.basicConfig(handlers=[], force=True)
|
logging.basicConfig(handlers=[], force=True)
|
||||||
logging.getLogger().addHandler(QueueHandler(self.__log_queue))
|
logging.getLogger().addHandler(QueueHandler(self.__log_queue))
|
||||||
|
|
||||||
|
# Always apply base log level suppressions for noisy third-party libraries
|
||||||
|
# even if no specific logConfig is provided
|
||||||
if logConfig:
|
if logConfig:
|
||||||
frigate.log.apply_log_levels(
|
frigate.log.apply_log_levels(
|
||||||
logConfig.default.value.upper(), logConfig.logs
|
logConfig.default.value.upper(), logConfig.logs
|
||||||
)
|
)
|
||||||
|
else:
|
||||||
|
# Apply default INFO level with standard library suppressions
|
||||||
|
frigate.log.apply_log_levels("INFO", {})
|
||||||
|
|
||||||
self._setup_memray()
|
self._setup_memray()
|
||||||
|
|
||||||
|
|||||||
@ -8,6 +8,7 @@ import time
|
|||||||
from pathlib import Path
|
from pathlib import Path
|
||||||
from typing import Optional
|
from typing import Optional
|
||||||
|
|
||||||
|
from frigate.const import SUPPORTED_RK_SOCS
|
||||||
from frigate.util.file import FileLock
|
from frigate.util.file import FileLock
|
||||||
|
|
||||||
logger = logging.getLogger(__name__)
|
logger = logging.getLogger(__name__)
|
||||||
@ -68,9 +69,20 @@ def is_rknn_compatible(model_path: str, model_type: str | None = None) -> bool:
|
|||||||
True if the model is RKNN-compatible, False otherwise
|
True if the model is RKNN-compatible, False otherwise
|
||||||
"""
|
"""
|
||||||
soc = get_soc_type()
|
soc = get_soc_type()
|
||||||
|
|
||||||
if soc is None:
|
if soc is None:
|
||||||
return False
|
return False
|
||||||
|
|
||||||
|
# Check if the SoC is actually a supported RK device
|
||||||
|
# This prevents false positives on non-RK devices (e.g., macOS Docker)
|
||||||
|
# where /proc/device-tree/compatible might exist but contain non-RK content
|
||||||
|
if soc not in SUPPORTED_RK_SOCS:
|
||||||
|
logger.debug(
|
||||||
|
f"SoC '{soc}' is not a supported RK device for RKNN conversion. "
|
||||||
|
f"Supported SoCs: {SUPPORTED_RK_SOCS}"
|
||||||
|
)
|
||||||
|
return False
|
||||||
|
|
||||||
if not model_type:
|
if not model_type:
|
||||||
model_type = get_rknn_model_type(model_path)
|
model_type = get_rknn_model_type(model_path)
|
||||||
|
|
||||||
|
|||||||
@ -139,6 +139,7 @@
|
|||||||
"nameOnlyNumbers": "Model name cannot contain only numbers",
|
"nameOnlyNumbers": "Model name cannot contain only numbers",
|
||||||
"classRequired": "At least 1 class is required",
|
"classRequired": "At least 1 class is required",
|
||||||
"classesUnique": "Class names must be unique",
|
"classesUnique": "Class names must be unique",
|
||||||
|
"noneNotAllowed": "The class 'none' is not allowed",
|
||||||
"stateRequiresTwoClasses": "State models require at least 2 classes",
|
"stateRequiresTwoClasses": "State models require at least 2 classes",
|
||||||
"objectLabelRequired": "Please select an object label",
|
"objectLabelRequired": "Please select an object label",
|
||||||
"objectTypeRequired": "Please select a classification type"
|
"objectTypeRequired": "Please select a classification type"
|
||||||
|
|||||||
@ -40,6 +40,7 @@ type ClassificationCardProps = {
|
|||||||
data: ClassificationItemData;
|
data: ClassificationItemData;
|
||||||
threshold?: ClassificationThreshold;
|
threshold?: ClassificationThreshold;
|
||||||
selected: boolean;
|
selected: boolean;
|
||||||
|
clickable: boolean;
|
||||||
i18nLibrary: string;
|
i18nLibrary: string;
|
||||||
showArea?: boolean;
|
showArea?: boolean;
|
||||||
count?: number;
|
count?: number;
|
||||||
@ -56,6 +57,7 @@ export const ClassificationCard = forwardRef<
|
|||||||
data,
|
data,
|
||||||
threshold,
|
threshold,
|
||||||
selected,
|
selected,
|
||||||
|
clickable,
|
||||||
i18nLibrary,
|
i18nLibrary,
|
||||||
showArea = true,
|
showArea = true,
|
||||||
count,
|
count,
|
||||||
@ -101,11 +103,12 @@ export const ClassificationCard = forwardRef<
|
|||||||
<div
|
<div
|
||||||
ref={ref}
|
ref={ref}
|
||||||
className={cn(
|
className={cn(
|
||||||
"relative flex size-full cursor-pointer flex-col overflow-hidden rounded-lg outline outline-[3px]",
|
"relative flex size-full flex-col overflow-hidden rounded-lg outline outline-[3px]",
|
||||||
className,
|
className,
|
||||||
selected
|
selected
|
||||||
? "shadow-selected outline-selected"
|
? "shadow-selected outline-selected"
|
||||||
: "outline-transparent duration-500",
|
: "outline-transparent duration-500",
|
||||||
|
clickable && "cursor-pointer",
|
||||||
)}
|
)}
|
||||||
onClick={(e) => {
|
onClick={(e) => {
|
||||||
const isMeta = e.metaKey || e.ctrlKey;
|
const isMeta = e.metaKey || e.ctrlKey;
|
||||||
@ -289,6 +292,7 @@ export function GroupedClassificationCard({
|
|||||||
data={bestItem}
|
data={bestItem}
|
||||||
threshold={threshold}
|
threshold={threshold}
|
||||||
selected={selectedItems.includes(bestItem.filename)}
|
selected={selectedItems.includes(bestItem.filename)}
|
||||||
|
clickable={true}
|
||||||
i18nLibrary={i18nLibrary}
|
i18nLibrary={i18nLibrary}
|
||||||
count={group.length}
|
count={group.length}
|
||||||
onClick={(_, meta) => {
|
onClick={(_, meta) => {
|
||||||
@ -413,6 +417,7 @@ export function GroupedClassificationCard({
|
|||||||
data={data}
|
data={data}
|
||||||
threshold={threshold}
|
threshold={threshold}
|
||||||
selected={false}
|
selected={false}
|
||||||
|
clickable={false}
|
||||||
i18nLibrary={i18nLibrary}
|
i18nLibrary={i18nLibrary}
|
||||||
onClick={() => {}}
|
onClick={() => {}}
|
||||||
>
|
>
|
||||||
|
|||||||
@ -94,7 +94,14 @@ export default function Step1NameAndDefine({
|
|||||||
objectLabel: z.string().optional(),
|
objectLabel: z.string().optional(),
|
||||||
objectType: z.enum(["sub_label", "attribute"]).optional(),
|
objectType: z.enum(["sub_label", "attribute"]).optional(),
|
||||||
classes: z
|
classes: z
|
||||||
.array(z.string())
|
.array(
|
||||||
|
z
|
||||||
|
.string()
|
||||||
|
.refine(
|
||||||
|
(val) => val.trim().toLowerCase() !== "none",
|
||||||
|
t("wizard.step1.errors.noneNotAllowed"),
|
||||||
|
),
|
||||||
|
)
|
||||||
.min(1, t("wizard.step1.errors.classRequired"))
|
.min(1, t("wizard.step1.errors.classRequired"))
|
||||||
.refine(
|
.refine(
|
||||||
(classes) => {
|
(classes) => {
|
||||||
@ -467,6 +474,7 @@ export default function Step1NameAndDefine({
|
|||||||
)}
|
)}
|
||||||
</div>
|
</div>
|
||||||
</FormControl>
|
</FormControl>
|
||||||
|
<FormMessage />
|
||||||
</FormItem>
|
</FormItem>
|
||||||
)}
|
)}
|
||||||
/>
|
/>
|
||||||
|
|||||||
@ -1026,6 +1026,7 @@ function FaceGrid({
|
|||||||
filepath: `clips/faces/${pageToggle}/${image}`,
|
filepath: `clips/faces/${pageToggle}/${image}`,
|
||||||
}}
|
}}
|
||||||
selected={selectedFaces.includes(image)}
|
selected={selectedFaces.includes(image)}
|
||||||
|
clickable={selectedFaces.length > 0}
|
||||||
i18nLibrary="views/faceLibrary"
|
i18nLibrary="views/faceLibrary"
|
||||||
onClick={(data, meta) => onClickFaces([data.filename], meta)}
|
onClick={(data, meta) => onClickFaces([data.filename], meta)}
|
||||||
>
|
>
|
||||||
|
|||||||
@ -804,6 +804,7 @@ function DatasetGrid({
|
|||||||
name: "",
|
name: "",
|
||||||
}}
|
}}
|
||||||
showArea={false}
|
showArea={false}
|
||||||
|
clickable={selectedImages.length > 0}
|
||||||
selected={selectedImages.includes(image)}
|
selected={selectedImages.includes(image)}
|
||||||
i18nLibrary="views/classificationModel"
|
i18nLibrary="views/classificationModel"
|
||||||
onClick={(data, _) => onClickImages([data.filename], true)}
|
onClick={(data, _) => onClickImages([data.filename], true)}
|
||||||
@ -962,6 +963,7 @@ function StateTrainGrid({
|
|||||||
data={data}
|
data={data}
|
||||||
threshold={threshold}
|
threshold={threshold}
|
||||||
selected={selectedImages.includes(data.filename)}
|
selected={selectedImages.includes(data.filename)}
|
||||||
|
clickable={selectedImages.length > 0}
|
||||||
i18nLibrary="views/classificationModel"
|
i18nLibrary="views/classificationModel"
|
||||||
showArea={false}
|
showArea={false}
|
||||||
onClick={(data, meta) => onClickImages([data.filename], meta)}
|
onClick={(data, meta) => onClickImages([data.filename], meta)}
|
||||||
|
|||||||
Loading…
Reference in New Issue
Block a user