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

* 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:
Nicolas Mowen
2026-07-06 12:28:02 -05:00
committed by GitHub
parent 455b8687e8
commit 4ee12e6237
169 changed files with 1053 additions and 1150 deletions
@@ -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.