improved length and character by character confidence

This commit is contained in:
Josh Hawkins 2024-10-25 18:26:30 -05:00
parent a96442ea13
commit 606eef1c58
2 changed files with 51 additions and 13 deletions

View File

@ -1,3 +1,4 @@
import logging
import math import math
from typing import List, Tuple from typing import List, Tuple
@ -10,6 +11,8 @@ from frigate.comms.inter_process import InterProcessRequestor
from frigate.config.semantic_search import LicensePlateRecognitionConfig from frigate.config.semantic_search import LicensePlateRecognitionConfig
from frigate.embeddings.functions.onnx import GenericONNXEmbedding, ModelTypeEnum from frigate.embeddings.functions.onnx import GenericONNXEmbedding, ModelTypeEnum
logger = logging.getLogger(__name__)
class LicensePlateRecognition: class LicensePlateRecognition:
def __init__( def __init__(
@ -194,7 +197,7 @@ class LicensePlateRecognition:
if results: if results:
license_plates = [""] * len(rotated_images) license_plates = [""] * len(rotated_images)
average_confidences = [0.0] * len(rotated_images) average_confidences = [[0.0]] * len(rotated_images)
areas = [0] * len(rotated_images) areas = [0] * len(rotated_images)
# map results back to original image order # map results back to original image order
@ -204,7 +207,7 @@ class LicensePlateRecognition:
height, width = rotated_images[original_idx].shape[:2] height, width = rotated_images[original_idx].shape[:2]
area = height * width area = height * width
average_confidence = sum(conf) / len(conf) if conf else 0 average_confidence = conf
# TODO: remove # TODO: remove
if False: if False:

View File

@ -78,7 +78,7 @@ class EmbeddingMaintainer(threading.Thread):
self.requires_license_plate_detection = ( self.requires_license_plate_detection = (
"license_plate" not in self.config.model.all_attributes "license_plate" not in self.config.model.all_attributes
) )
self.detected_license_plates: dict[str, float] = {} self.detected_license_plates: dict[str, dict[str, any]] = {}
self.license_plate_recognition = LicensePlateRecognition( self.license_plate_recognition = LicensePlateRecognition(
self.lpr_config, self.requestor self.lpr_config, self.requestor
) )
@ -556,27 +556,59 @@ class EmbeddingMaintainer(threading.Thread):
if license_plates: if license_plates:
for plate, confidence, text_area in zip(license_plates, confidences, areas): for plate, confidence, text_area in zip(license_plates, confidences, areas):
logger.debug( logger.debug(
f"Detected text: {plate} (average confidence: {confidence:.2f}, area: {text_area} pixels)" f"Detected text: {plate} (average confidence: {(sum(confidence) / len(confidence)):.2f}, area: {text_area} pixels)"
) )
else: else:
# no plates found # no plates found
logger.debug("No text detected") logger.debug("No text detected")
return return
if confidences[0] < self.lpr_config.threshold or ( top_plate, top_char_confidences = license_plates[0], confidences[0]
id in self.detected_license_plates avg_confidence = sum(top_char_confidences) / len(top_char_confidences)
and confidences[0] <= self.detected_license_plates[id]
): # Check if we have a previously detected plate for this ID
if id in self.detected_license_plates:
prev_plate = self.detected_license_plates[id]["plate"]
prev_char_confidences = self.detected_license_plates[id]["char_confidences"]
prev_avg_confidence = sum(prev_char_confidences) / len(
prev_char_confidences
)
# Define conditions for keeping the previous plate
shorter_than_previous = len(top_plate) < len(prev_plate)
lower_avg_confidence = avg_confidence <= prev_avg_confidence
# Compare character-by-character confidence where possible
min_length = min(len(top_plate), len(prev_plate))
char_confidence_comparison = sum(
1
for i in range(min_length)
if top_char_confidences[i] <= prev_char_confidences[i]
)
worse_char_confidences = char_confidence_comparison >= min_length / 2
if shorter_than_previous or (
lower_avg_confidence and worse_char_confidences
):
logger.debug(
f"Keeping previous plate. New plate stats: "
f"length={len(top_plate)}, avg_conf={avg_confidence:.2f} "
f"vs Previous: length={len(prev_plate)}, avg_conf={prev_avg_confidence:.2f}"
)
return
# Check against minimum confidence threshold
if avg_confidence < self.lpr_config.threshold:
logger.debug( logger.debug(
f"Recognized license plate top score {confidences[0]} is less than threshold ({self.config.lpr.threshold}) / previous license plate score ({self.detected_license_plates.get(id)})." f"Average confidence {avg_confidence} is less than threshold ({self.lpr_config.threshold})"
) )
return return
# Determine subLabel based on known plates # Determine subLabel based on known plates
# Default to the detected plate, use label name if there's a match # Default to the detected plate, use label name if there's a match
sub_label = license_plates[0] sub_label = top_plate
for label, plates in self.lpr_config.known_plates.items(): for label, plates in self.lpr_config.known_plates.items():
if license_plates[0] in plates: if top_plate in plates:
sub_label = label sub_label = label
break break
@ -586,12 +618,15 @@ class EmbeddingMaintainer(threading.Thread):
json={ json={
"camera": obj_data.get("camera"), "camera": obj_data.get("camera"),
"subLabel": sub_label, "subLabel": sub_label,
"subLabelScore": confidences[0], "subLabelScore": avg_confidence,
}, },
) )
if resp.status_code == 200: if resp.status_code == 200:
self.detected_license_plates[id] = confidences[0] self.detected_license_plates[id] = {
"plate": top_plate,
"char_confidences": top_char_confidences,
}
def _create_thumbnail(self, yuv_frame, box, height=500) -> Optional[bytes]: def _create_thumbnail(self, yuv_frame, box, height=500) -> Optional[bytes]:
"""Return jpg thumbnail of a region of the frame.""" """Return jpg thumbnail of a region of the frame."""