From 256da1fe9454f813ade65527b81cfa08c375cbe3 Mon Sep 17 00:00:00 2001 From: Conner Vieira Date: Tue, 17 Oct 2023 18:16:31 -0400 Subject: [PATCH] Added minimum confidence threshold configuration --- CHANGELOG.md | 1 + CONFIGURATION.md | 9 ++++++--- DOCUMENTATION.md | 2 +- TODO.md | 1 + alprstream.py | 2 ++ config.json | 9 +++++---- main.py | 32 ++++++++++++++++---------------- 7 files changed, 32 insertions(+), 24 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index a6a07ff..50a8993 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -340,3 +340,4 @@ This update refines Predator's functionality, and focuses its purpose back on li - Simplified the configuration. - Removed the `detected_plate_count` configuration value from the real-time display section. - The number of plates detected is now always displayed in the console output. +- Added configuration value to set a minimum required confidence level for ALPR results. diff --git a/CONFIGURATION.md b/CONFIGURATION.md index 566c6cc..215a35d 100644 --- a/CONFIGURATION.md +++ b/CONFIGURATION.md @@ -20,15 +20,18 @@ This section of configuration values will effect Predator's general operation. - `validation` contains settings for validating license plate candidates/guesses. - `guesses` is an integer that determines how many guesses the ALPR engine will make when analyzing a plate. - The higher this number is, the more likely Predator is to guess a plate incorrectly. The lower this number is, the less likely Predator will be to find a valid guess at all. + - `confidence` is a number that determines the minimum confidence a license plate guess needs to have before Predator will consider it valid, where 100 is extremely confidence and 0 is a complete lack of confidence. + - This value is only considered in real-time mode, and is ignored in pre-recorded mode. + - This value is ignored by alerts when `general>alerts>alerts_ignore_validation` is enabled. - `license_plate_format` is a list of strings that provide Predator with examples of how license plates in your region should be formatted. - For example, license plates in the state of Ohio generally follow the pattern of 3 letters followed by 4 numbers. In Ohio, this preference might contain `"AAA0000"` to filter out plate guesses that don't match the most common formatting pattern. - This preference only considers the type of each character, not the character itself. - In other words, `AAA0000` and `ABC1234` will function identically. - This also means you can simply enter any given plate from a car located in the region you're scanning in to have a reasonably good chance at matching your region's formatting guidelines for license plates. - Leaving this as an empty list disable license plate format validation. - - `best_effort` is a boolean that determines whether Predator will accept the most confident guess when none of the guesses match the license plate validation format(s). - - When set to `true`, Predator will not completely discard license plate detections that don't have any guesses aligning with the license plate validation format(s), and will instead simply accept the best guess for plates that would otherwise be considered invalid. - - When set to `false`, Predator will discard plates that don't have any valid guesses, based on the validation format(s). + - `best_effort` is a boolean that determines whether Predator will accept the most confident guess when none of the guesses are considered valid by the validation rules. + - This setting can override both the license plate validation format, as well as the minimum confidence threshold. + - When set to `false`, Predator will discard plates that don't have any valid guesses. - This setting does not override `general>alerts>alerts_ignore_validation`, and can be set to `false` without interferring with license plate hotlist alerts. - `alerts` contains settings related to license plate alerting. - `alerts_ignore_validation` is a boolean that determines whether alerts will respect or ignore the plate validation format. diff --git a/DOCUMENTATION.md b/DOCUMENTATION.md index 6beca42..f910e8f 100644 --- a/DOCUMENTATION.md +++ b/DOCUMENTATION.md @@ -20,7 +20,7 @@ This is the installation process for Predator and all of it's dependencies. This - These packages are not necessary to handle information from GPX files, and are only required to interact with live GPS devices. - Optional, but necessary for object recognition: `pip3 install opencv-python cvlib tensorflow keras silence-tensorflow` - These packages are optional, but are required to enable object recognition features. - - If you do not install these packages, make sure object recognition is disabled in the configuration. + - If you do not install these packages, make sure object recognition is disabled in the configuration, and that the configuration isn't set to use the OpenCV back-end for dashcam recording. - These packages are not necessary for basic license plate recognition. - Optional, but necessary disk usage analysis: `pip3 install psutil` - This package is option, but enables the ability for Predator to view information regarding disk usage. diff --git a/TODO.md b/TODO.md index 070fcbe..5af28c7 100644 --- a/TODO.md +++ b/TODO.md @@ -64,3 +64,4 @@ These are the features actively planned for Predator and are likely to be added - [X] Organize configuration. - [X] Add support for multiple license plate formats. - [X] Test updated license plate validation in real-time mode. +- [ ] Add Phantom alert handling to updated ALPR stream. diff --git a/alprstream.py b/alprstream.py index fa3cdec..f362882 100644 --- a/alprstream.py +++ b/alprstream.py @@ -57,6 +57,8 @@ def alpr_stream_maintainer(): # This function runs an endless loop that maintain for message in stream_file_contents: # Iterate through each line in the loaded stream file contents. if (is_json(message) == True): message = json.loads(message) # Parse each line into JSON. + if ("error" in message): # Check to see if there were errors while executing the ALPR process. This will only work for alerts issued by Phantom, not OpenALPR. + display_message("Phantom ALPR encountered an error: " + message["error"], level=3) # Display the ALPR error. for plate in message["results"]: # Iterate through each license plate in this line. queued_plate_reads.append(plate) # Add each license plate to the license plate queue. else: diff --git a/config.json b/config.json index 8c9eab7..a0bde40 100644 --- a/config.json +++ b/config.json @@ -3,11 +3,12 @@ "working_directory": "/home/cvieira/Software/Support/Predator/Working/", "interface_directory": "/home/cvieira/Software/Support/Predator/Interface/", "alpr": { - "engine": "openalpr", + "engine": "phantom", "validation": { - "guesses": 5, + "guesses": 15, + "confidence": 80, "license_plate_format": ["AAA0000"], - "best_effort": true + "best_effort": false } }, "alerts": { @@ -65,7 +66,7 @@ "behavior": { "delays": { "alert": 5, - "normal": 0.5 + "normal": 1 }, "clearing": true, "manual_trigger": false diff --git a/main.py b/main.py index 2445259..557c692 100644 --- a/main.py +++ b/main.py @@ -857,10 +857,11 @@ validated_alpr_frames[frame] = {} # Set the validated license plate recognition information for this frame to an empty list as a placeholder. for plate in alpr_frames[frame].keys(): # Iterate through each plate detected per frame. for guess in alpr_frames[frame][plate]: # Iterate through each guess for each plate. - if any(validate_plate(guess, format_template) for format_template in config["general"]["alpr"]["validation"]["license_plate_format"]) or "" in config["general"]["alpr"]["validation"]["license_plate_format"]: # Check to see if this plate passes validation. - if (plate not in validated_alpr_frames[frame]): # Check to see if this plate hasn't been added to the validated information yet. - validated_alpr_frames[frame][plate] = [] # Add the plate to the validated information as a blank placeholder list. - validated_alpr_frames[frame][plate].append(guess) # Since this plate guess failed the validation test, delete it from the list of guesses. + if (all_current_plate_guesses[individual_detected_plate][plate_guess] >= float(config["general"]["alpr"]["validation"]["confidence"])): # Check to make sure this plate's confidence is higher than the minimum threshold set in the configuration. + if any(validate_plate(guess, format_template) for format_template in config["general"]["alpr"]["validation"]["license_plate_format"]) or "" in config["general"]["alpr"]["validation"]["license_plate_format"]: # Check to see if this plate passes validation. + if (plate not in validated_alpr_frames[frame]): # Check to see if this plate hasn't been added to the validated information yet. + validated_alpr_frames[frame][plate] = [] # Add the plate to the validated information as a blank placeholder list. + validated_alpr_frames[frame][plate].append(guess) # Since this plate guess failed the validation test, delete it from the list of guesses. print("Done.\n") @@ -1286,7 +1287,6 @@ all_current_plate_guesses[detected_plate["candidates"][0]["plate"]] = {} # Create an empty dictionary for this plate so we can add all the potential plate guesses to it in the next step. for plate_guess in detected_plate["candidates"]: # Iterate through each plate guess candidate for each potential plate detected. - #all_current_plate_guesses[detected_plate["plate_index"]][plate_guess["plate"]] = plate_guess["confidence"] # Add the current plate guess candidate to the list of plate guesses. all_current_plate_guesses[detected_plate["candidates"][0]["plate"]][plate_guess["plate"]] = plate_guess["confidence"] # Add the current plate guess candidate to the list of plate guesses. if (config["realtime"]["interface"]["display"]["output_level"] >= 3): # Only display this status message if the output level indicates to do so. @@ -1315,15 +1315,16 @@ if (config["realtime"]["interface"]["display"]["show_validation"] == True): # Only print the validated plate if the configuration says to do so. print (" Plate guesses:") for plate_guess in all_current_plate_guesses[individual_detected_plate]: # Iterate through each plate and grab the first plate that matches the plate formatting guidelines as the 'detected plate'. - if any([validate_plate(plate_guess, format_template) for format_template in config["general"]["alpr"]["validation"]["license_plate_format"]]): # Check to see whether or not the plate passes the validation based on the format specified by the user. - detected_plate = plate_guess # Grab the validated plate as the 'detected plate'. - successfully_found_plate = True # The plate was successfully validated, so indicate that a plate was successfully found this round. - if (config["realtime"]["interface"]["display"]["show_validation"] == True): # Only print the validated plate if the configuration says to do so. - print(" ", style.green + plate_guess + style.end) # Print the valid plate in green. - break - else: # This particular plate guess is invalid, since it didn't align with the user-supplied formatting guidelines. - if (config["realtime"]["interface"]["display"]["show_validation"] == True): # Only print the invalid plate if the configuration says to do so. - print(" ", style.red + plate_guess + style.end) # Print the invalid plate in red. + if (all_current_plate_guesses[individual_detected_plate][plate_guess] >= float(config["general"]["alpr"]["validation"]["confidence"])): # Check to make sure this plate's confidence is higher than the minimum threshold set in the configuration. + if any([validate_plate(plate_guess, format_template) for format_template in config["general"]["alpr"]["validation"]["license_plate_format"]]): # Check to see whether or not the plate passes the validation based on the format specified by the user. + detected_plate = plate_guess # Grab the validated plate as the 'detected plate'. + successfully_found_plate = True # The plate was successfully validated, so indicate that a plate was successfully found this round. + if (config["realtime"]["interface"]["display"]["show_validation"] == True): # Only print the validated plate if the configuration says to do so. + print(" ", style.green + plate_guess + style.end) # Print the valid plate in green. + break + else: # This particular plate guess is invalid, since it didn't align with the user-supplied formatting guidelines. + if (config["realtime"]["interface"]["display"]["show_validation"] == True): # Only print the invalid plate if the configuration says to do so. + print(" ", style.red + plate_guess + style.end) # Print the invalid plate in red. @@ -1371,8 +1372,7 @@ print("Displaying detected license plates...") if (config["realtime"]["interface"]["display"]["output_level"] >= 2): # Only display this status message if the output level indicates to do so. - if (len(new_plates_detected) > 0): - print("Plates detected: ", len(new_plates_detected)) + print("Plates detected: ", len(new_plates_detected)) # Display the number of license plates detected this round. for plate in new_plates_detected: play_sound("notification") print(" Detected plate: " + plate) # Print the detected plate.