mirror of
https://github.com/blakeblackshear/frigate.git
synced 2026-08-11 21:31:12 +03:00
Increase ruff coverage (#23644)
CI / AMD64 Build (push) Waiting to run
CI / ARM Build (push) Waiting to run
CI / AMD64 Extra Build (push) Blocked by required conditions
CI / ARM Extra Build (push) Blocked by required conditions
CI / Synaptics Build (push) Blocked by required conditions
CI / Assemble and push default build (push) Blocked by required conditions
CI / Jetson Jetpack 6 (push) Waiting to run
CI / AMD64 Build (push) Waiting to run
CI / ARM Build (push) Waiting to run
CI / AMD64 Extra Build (push) Blocked by required conditions
CI / ARM Extra Build (push) Blocked by required conditions
CI / Synaptics Build (push) Blocked by required conditions
CI / Assemble and push default build (push) Blocked by required conditions
CI / Jetson Jetpack 6 (push) Waiting to run
* Pin ruff * Add python upgrade fixes This enables python upgrade checks in ruff to look for deprecated types and patterns. This namely fixes: - usage of deprecated `Typing` which is now built in - some specific exceptions which are caught and have new aliases Some specific UP checks were also ignored as they are stylistic / unimportant and likely to cause bugs * Remove async blocking calls Use asyncio.to_thread on two remaining blocking calls to fix hanging event thread loop. Enable this specific rule to block it in the future. * Use proper logging mechanism * Correctly format logs * Raise with context When raising an exception include the from context to improve debugging * Cleanup
This commit is contained in:
@@ -10,7 +10,7 @@ import random
|
||||
import re
|
||||
import string
|
||||
from pathlib import Path
|
||||
from typing import Any, List, Tuple
|
||||
from typing import Any
|
||||
|
||||
import cv2
|
||||
import numpy as np
|
||||
@@ -86,7 +86,7 @@ class LicensePlateProcessingMixin:
|
||||
self.similarity_threshold = 0.8
|
||||
self.cluster_threshold = 0.85
|
||||
|
||||
def _detect(self, image: np.ndarray, debug_frame_id: int) -> List[np.ndarray]:
|
||||
def _detect(self, image: np.ndarray, debug_frame_id: int) -> list[np.ndarray]:
|
||||
"""
|
||||
Detect possible areas of text in the input image by first resizing and normalizing it,
|
||||
running a detection model, and filtering out low-probability regions.
|
||||
@@ -132,8 +132,8 @@ class LicensePlateProcessingMixin:
|
||||
return self._filter_polygon(boxes, (h, w)) # type: ignore[return-value,arg-type]
|
||||
|
||||
def _classify(
|
||||
self, images: List[np.ndarray]
|
||||
) -> Tuple[List[np.ndarray], List[Tuple[str, float]]] | None:
|
||||
self, images: list[np.ndarray]
|
||||
) -> tuple[list[np.ndarray], list[tuple[str, float]]] | None:
|
||||
"""
|
||||
Classify the orientation or category of each detected license plate.
|
||||
|
||||
@@ -163,8 +163,8 @@ class LicensePlateProcessingMixin:
|
||||
return self._process_classification_output(images, outputs)
|
||||
|
||||
def _recognize(
|
||||
self, camera: str, images: List[np.ndarray]
|
||||
) -> Tuple[List[str], List[List[float]]]:
|
||||
self, camera: str, images: list[np.ndarray]
|
||||
) -> tuple[list[str], list[list[float]]]:
|
||||
"""
|
||||
Recognize the characters on the detected license plates using the recognition model.
|
||||
|
||||
@@ -205,7 +205,7 @@ class LicensePlateProcessingMixin:
|
||||
|
||||
def _process_license_plate(
|
||||
self, camera: str, id: str, image: np.ndarray, debug_frame_id: int
|
||||
) -> Tuple[List[str], List[List[float]], List[int]]:
|
||||
) -> tuple[list[str], list[list[float]], list[int]]:
|
||||
"""
|
||||
Complete pipeline for detecting, classifying, and recognizing license plates in the input image.
|
||||
Combines multi-line plates into a single plate string, grouping boxes by vertical alignment and ordering top to bottom,
|
||||
@@ -469,11 +469,11 @@ class LicensePlateProcessingMixin:
|
||||
|
||||
def _merge_nearby_boxes(
|
||||
self,
|
||||
boxes: List[np.ndarray],
|
||||
boxes: list[np.ndarray],
|
||||
plate_width: float,
|
||||
gap_fraction: float = 0.1,
|
||||
min_overlap_fraction: float = -0.2,
|
||||
) -> List[np.ndarray]:
|
||||
) -> list[np.ndarray]:
|
||||
"""
|
||||
Merge bounding boxes that are likely part of the same license plate based on proximity,
|
||||
with a dynamic max_gap based on the provided width of the entire license plate.
|
||||
@@ -555,7 +555,7 @@ class LicensePlateProcessingMixin:
|
||||
|
||||
def _boxes_from_bitmap(
|
||||
self, output: np.ndarray, mask: np.ndarray, dest_width: int, dest_height: int
|
||||
) -> Tuple[np.ndarray, List[float]]:
|
||||
) -> tuple[np.ndarray, list[float]]:
|
||||
"""
|
||||
Process the binary mask to extract bounding boxes and associated confidence scores.
|
||||
|
||||
@@ -620,7 +620,7 @@ class LicensePlateProcessingMixin:
|
||||
return np.array(boxes, dtype="int32"), scores
|
||||
|
||||
@staticmethod
|
||||
def _get_min_boxes(contour: np.ndarray) -> Tuple[List[Tuple[float, float]], float]:
|
||||
def _get_min_boxes(contour: np.ndarray) -> tuple[list[tuple[float, float]], float]:
|
||||
"""
|
||||
Calculate the minimum bounding box (rotated rectangle) for a given contour.
|
||||
|
||||
@@ -659,7 +659,7 @@ class LicensePlateProcessingMixin:
|
||||
return cv2.mean(bitmap[y1 : y2 + 1, x1 : x2 + 1], mask)[0]
|
||||
|
||||
@staticmethod
|
||||
def _expand_box(points: List[Tuple[float, float]]) -> np.ndarray:
|
||||
def _expand_box(points: list[tuple[float, float]]) -> np.ndarray:
|
||||
"""
|
||||
Expand a polygonal shape slightly by a factor determined by the area-to-perimeter ratio.
|
||||
|
||||
@@ -677,7 +677,7 @@ class LicensePlateProcessingMixin:
|
||||
return expanded
|
||||
|
||||
def _filter_polygon(
|
||||
self, points: List[np.ndarray], shape: Tuple[int, int]
|
||||
self, points: list[np.ndarray], shape: tuple[int, int]
|
||||
) -> np.ndarray:
|
||||
"""
|
||||
Filter a set of polygons to include only valid ones that fit within an image shape
|
||||
@@ -839,8 +839,8 @@ class LicensePlateProcessingMixin:
|
||||
return padded_image
|
||||
|
||||
def _process_classification_output(
|
||||
self, images: List[np.ndarray], outputs: List[np.ndarray]
|
||||
) -> Tuple[List[np.ndarray], List[Tuple[str, float]]]:
|
||||
self, images: list[np.ndarray], outputs: list[np.ndarray]
|
||||
) -> tuple[list[np.ndarray], list[tuple[str, float]]]:
|
||||
"""
|
||||
Process the classification model output by matching labels with confidence scores.
|
||||
|
||||
@@ -1095,8 +1095,8 @@ class LicensePlateProcessingMixin:
|
||||
return None # No detection above the threshold
|
||||
|
||||
def _get_cluster_rep(
|
||||
self, plates: List[dict]
|
||||
) -> Tuple[str, float, List[float], int]:
|
||||
self, plates: list[dict]
|
||||
) -> tuple[str, float, list[float], int]:
|
||||
"""
|
||||
Cluster plate variants and select the representative from the best cluster.
|
||||
"""
|
||||
@@ -1704,7 +1704,7 @@ class CTCDecoder:
|
||||
"""
|
||||
self.characters = []
|
||||
if character_dict_path and os.path.exists(character_dict_path):
|
||||
with open(character_dict_path, "r", encoding="utf-8") as f:
|
||||
with open(character_dict_path, encoding="utf-8") as f:
|
||||
self.characters = (
|
||||
["blank"] + [line.strip() for line in f if line.strip()] + [" "]
|
||||
)
|
||||
@@ -1812,8 +1812,8 @@ class CTCDecoder:
|
||||
self.char_map = {i: char for i, char in enumerate(self.characters)}
|
||||
|
||||
def __call__(
|
||||
self, outputs: List[np.ndarray]
|
||||
) -> Tuple[List[str], List[List[float]]]:
|
||||
self, outputs: list[np.ndarray]
|
||||
) -> tuple[list[str], list[list[float]]]:
|
||||
"""
|
||||
Decode a batch of model outputs into character sequences and their confidence scores.
|
||||
|
||||
|
||||
@@ -4,7 +4,7 @@ import logging
|
||||
import os
|
||||
import threading
|
||||
import time
|
||||
from typing import Any, Optional
|
||||
from typing import Any
|
||||
|
||||
from peewee import DoesNotExist
|
||||
|
||||
@@ -142,7 +142,7 @@ class AudioTranscriptionPostProcessor(PostProcessorApi):
|
||||
except Exception as e:
|
||||
logger.error(f"Error in audio transcription post-processing: {e}")
|
||||
|
||||
def __transcribe_audio(self, audio_data: bytes) -> Optional[str]:
|
||||
def __transcribe_audio(self, audio_data: bytes) -> str | None:
|
||||
"""Transcribe WAV audio data using faster-whisper."""
|
||||
if not self.recognizer:
|
||||
logger.debug("Recognizer not initialized")
|
||||
@@ -168,8 +168,9 @@ class AudioTranscriptionPostProcessor(PostProcessorApi):
|
||||
return None
|
||||
|
||||
logger.debug(
|
||||
"Detected language '%s' with probability %f"
|
||||
% (info.language, info.language_probability)
|
||||
"Detected language '%s' with probability %f",
|
||||
info.language,
|
||||
info.language_probability,
|
||||
)
|
||||
|
||||
return text
|
||||
|
||||
@@ -102,10 +102,8 @@ class LicensePlatePostProcessor(LicensePlateProcessingMixin, PostProcessorApi):
|
||||
Recordings.start_time,
|
||||
)
|
||||
.where(
|
||||
(
|
||||
(frame_time >= Recordings.start_time)
|
||||
& (frame_time <= Recordings.end_time)
|
||||
)
|
||||
(frame_time >= Recordings.start_time)
|
||||
& (frame_time <= Recordings.end_time)
|
||||
)
|
||||
.where(Recordings.camera == camera_name)
|
||||
.order_by(Recordings.start_time.desc())
|
||||
|
||||
@@ -55,7 +55,7 @@ class SemanticTriggerProcessor(PostProcessorApi):
|
||||
|
||||
# load stats from disk
|
||||
try:
|
||||
with open(os.path.join(CONFIG_DIR, ".search_stats.json"), "r") as f:
|
||||
with open(os.path.join(CONFIG_DIR, ".search_stats.json")) as f:
|
||||
data = json.loads(f.read())
|
||||
self.thumb_stats.from_dict(data["thumb_stats"])
|
||||
self.desc_stats.from_dict(data["desc_stats"])
|
||||
|
||||
@@ -4,9 +4,10 @@ import logging
|
||||
import threading
|
||||
from abc import ABC, abstractmethod
|
||||
from collections import deque
|
||||
from collections.abc import Callable
|
||||
from concurrent.futures import Future
|
||||
from queue import Empty, Full, Queue
|
||||
from typing import Any, Callable
|
||||
from typing import Any
|
||||
|
||||
import numpy as np
|
||||
|
||||
|
||||
@@ -4,7 +4,7 @@ import logging
|
||||
import os
|
||||
import queue
|
||||
import threading
|
||||
from typing import Any, Optional
|
||||
from typing import Any
|
||||
|
||||
import numpy as np
|
||||
|
||||
@@ -75,9 +75,7 @@ class AudioTranscriptionRealTimeProcessor(RealTimeProcessorApi):
|
||||
f"Failed to initialize live streaming audio transcription: {e}"
|
||||
)
|
||||
|
||||
def __process_audio_stream(
|
||||
self, audio_data: np.ndarray
|
||||
) -> Optional[tuple[str, bool]]:
|
||||
def __process_audio_stream(self, audio_data: np.ndarray) -> tuple[str, bool] | None:
|
||||
if (
|
||||
self.model_runner.model is None
|
||||
and self.config.audio_transcription.model_size == "small"
|
||||
|
||||
@@ -7,7 +7,7 @@ import logging
|
||||
import os
|
||||
import shutil
|
||||
from pathlib import Path
|
||||
from typing import Any, Optional
|
||||
from typing import Any
|
||||
|
||||
import cv2
|
||||
import numpy as np
|
||||
@@ -219,7 +219,7 @@ class FaceRealTimeProcessor(RealTimeProcessorApi):
|
||||
logger.debug("Not processing due to hitting max rec attempts.")
|
||||
return
|
||||
|
||||
face: Optional[dict[str, Any]] = None
|
||||
face: dict[str, Any] | None = None
|
||||
|
||||
if self.requires_face_detection:
|
||||
logger.debug("Running manual face detection.")
|
||||
|
||||
@@ -1053,7 +1053,7 @@ if __name__ == "__main__":
|
||||
|
||||
SAMPLING_RATE = 16000
|
||||
duration = len(load_audio(audio_path)) / SAMPLING_RATE
|
||||
logger.info("Audio duration is: %2.2f seconds" % duration)
|
||||
logger.info("Audio duration is: %2.2f seconds", duration)
|
||||
|
||||
asr, online = asr_factory(args, logfile=logfile)
|
||||
if args.vac:
|
||||
|
||||
Reference in New Issue
Block a user