Compare commits
2 Commits
a4d297fc33
...
6976f690ff
Author | SHA1 | Date | |
---|---|---|---|
6976f690ff | |||
d81d955a8b |
79
alpr_api.py
79
alpr_api.py
@ -1,14 +1,19 @@
|
|||||||
import json
|
import json
|
||||||
|
import logging
|
||||||
import os
|
import os
|
||||||
import sys
|
import sys
|
||||||
import threading
|
import threading
|
||||||
import time
|
import time
|
||||||
from time import sleep
|
|
||||||
|
|
||||||
import ultimateAlprSdk
|
import ultimateAlprSdk
|
||||||
from PIL import Image
|
from PIL import Image
|
||||||
from flask import Flask, request, jsonify, render_template
|
from flask import Flask, request, jsonify, render_template
|
||||||
|
|
||||||
|
# Setup logging
|
||||||
|
logging.basicConfig(level=logging.INFO)
|
||||||
|
logger = logging.getLogger(__name__)
|
||||||
|
|
||||||
|
counter_lock = threading.Lock()
|
||||||
counter = 0
|
counter = 0
|
||||||
bundle_dir = getattr(sys, '_MEIPASS', os.path.abspath(os.path.dirname(__file__)))
|
bundle_dir = getattr(sys, '_MEIPASS', os.path.abspath(os.path.dirname(__file__)))
|
||||||
boot_time = time.time()
|
boot_time = time.time()
|
||||||
@ -22,11 +27,15 @@ information. The server is created using Flask and the ultimateALPR SDK is used
|
|||||||
See the README.md file for more information on how to run this script.
|
See the README.md file for more information on how to run this script.
|
||||||
"""
|
"""
|
||||||
|
|
||||||
# Defines the default JSON configuration. More information at
|
# Load configuration from a JSON file or environment variables
|
||||||
# https://www.doubango.org/SDKs/anpr/docs/Configuration_options.html
|
CONFIG_PATH = os.path.join(bundle_dir,
|
||||||
JSON_CONFIG = {
|
'config.json') # TODO: store config file outside of bundle (to avoid compilation by users)
|
||||||
"assets_folder": os.path.join(bundle_dir, "assets"), # don't change this
|
if os.path.exists(CONFIG_PATH):
|
||||||
|
with open(CONFIG_PATH, 'r') as config_file:
|
||||||
|
JSON_CONFIG = json.load(config_file)
|
||||||
|
else:
|
||||||
|
JSON_CONFIG = {
|
||||||
|
"assets_folder": os.path.join(bundle_dir, "assets"),
|
||||||
"charset": "latin",
|
"charset": "latin",
|
||||||
"car_noplate_detect_enabled": False,
|
"car_noplate_detect_enabled": False,
|
||||||
"ienv_enabled": False,
|
"ienv_enabled": False,
|
||||||
@ -40,30 +49,24 @@ JSON_CONFIG = {
|
|||||||
"license_token_file": "",
|
"license_token_file": "",
|
||||||
"license_token_data": "",
|
"license_token_data": "",
|
||||||
|
|
||||||
"debug_level": "info",
|
"debug_level": "fatal",
|
||||||
"debug_write_input_image_enabled": False,
|
"debug_write_input_image_enabled": False,
|
||||||
"debug_internal_data_path": ".",
|
"debug_internal_data_path": ".",
|
||||||
|
|
||||||
"num_threads": -1,
|
"num_threads": -1,
|
||||||
"gpgpu_enabled": True,
|
"gpgpu_enabled": True,
|
||||||
"max_latency": -1,
|
"max_latency": -1,
|
||||||
|
|
||||||
"klass_vcr_gamma": 1.5,
|
"klass_vcr_gamma": 1.5,
|
||||||
|
|
||||||
"detect_roi": [0, 0, 0, 0],
|
"detect_roi": [0, 0, 0, 0],
|
||||||
"detect_minscore": 0.35,
|
"detect_minscore": 0.35,
|
||||||
|
|
||||||
"car_noplate_detect_min_score": 0.8,
|
"car_noplate_detect_min_score": 0.8,
|
||||||
|
|
||||||
"pyramidal_search_enabled": False,
|
"pyramidal_search_enabled": False,
|
||||||
"pyramidal_search_sensitivity": 0.38, # default 0.28
|
"pyramidal_search_sensitivity": 0.38,
|
||||||
"pyramidal_search_minscore": 0.8,
|
"pyramidal_search_minscore": 0.8,
|
||||||
"pyramidal_search_min_image_size_inpixels": 800,
|
"pyramidal_search_min_image_size_inpixels": 800,
|
||||||
|
"recogn_rectify_enabled": True,
|
||||||
"recogn_rectify_enabled": True, # heavy on cpu
|
|
||||||
"recogn_minscore": 0.4,
|
"recogn_minscore": 0.4,
|
||||||
"recogn_score_type": "min"
|
"recogn_score_type": "min"
|
||||||
}
|
}
|
||||||
|
|
||||||
IMAGE_TYPES_MAPPING = {
|
IMAGE_TYPES_MAPPING = {
|
||||||
'RGB': ultimateAlprSdk.ULTALPR_SDK_IMAGE_TYPE_RGB24,
|
'RGB': ultimateAlprSdk.ULTALPR_SDK_IMAGE_TYPE_RGB24,
|
||||||
@ -75,7 +78,9 @@ config = json.dumps(JSON_CONFIG)
|
|||||||
|
|
||||||
|
|
||||||
def start_backend_loop():
|
def start_backend_loop():
|
||||||
global counter
|
global boot_time, counter
|
||||||
|
|
||||||
|
while True:
|
||||||
load_engine()
|
load_engine()
|
||||||
|
|
||||||
# loop for about an hour or 3000 requests then reload the engine (fix for trial license)
|
# loop for about an hour or 3000 requests then reload the engine (fix for trial license)
|
||||||
@ -85,11 +90,14 @@ def start_backend_loop():
|
|||||||
if not is_engine_loaded():
|
if not is_engine_loaded():
|
||||||
unload_engine() # just in case
|
unload_engine() # just in case
|
||||||
load_engine()
|
load_engine()
|
||||||
|
time.sleep(1)
|
||||||
sleep(1)
|
|
||||||
|
|
||||||
unload_engine()
|
unload_engine()
|
||||||
start_backend_loop()
|
|
||||||
|
# Reset counter and boot_time to restart the loop
|
||||||
|
with counter_lock:
|
||||||
|
counter = 0
|
||||||
|
boot_time = time.time()
|
||||||
|
|
||||||
|
|
||||||
def is_engine_loaded():
|
def is_engine_loaded():
|
||||||
@ -111,6 +119,7 @@ def unload_engine():
|
|||||||
|
|
||||||
def process_image(image: Image) -> str:
|
def process_image(image: Image) -> str:
|
||||||
global counter
|
global counter
|
||||||
|
with counter_lock:
|
||||||
counter += 1
|
counter += 1
|
||||||
|
|
||||||
width, height = image.size
|
width, height = image.size
|
||||||
@ -120,7 +129,6 @@ def process_image(image: Image) -> str:
|
|||||||
else:
|
else:
|
||||||
raise ValueError("Invalid mode: %s" % image.mode)
|
raise ValueError("Invalid mode: %s" % image.mode)
|
||||||
|
|
||||||
# TODO: add check for if engine still loaded
|
|
||||||
result = ultimateAlprSdk.UltAlprSdkEngine_process(
|
result = ultimateAlprSdk.UltAlprSdkEngine_process(
|
||||||
image_type,
|
image_type,
|
||||||
image.tobytes(),
|
image.tobytes(),
|
||||||
@ -152,28 +160,25 @@ def create_rest_server_flask():
|
|||||||
interference = time.time()
|
interference = time.time()
|
||||||
|
|
||||||
if 'upload' not in request.files:
|
if 'upload' not in request.files:
|
||||||
return jsonify({'error': 'No image found'})
|
return jsonify({'error': 'No image found'}), 400
|
||||||
if 'grid_size' in request.form and request.form['grid_size'].isdigit():
|
|
||||||
grid_size = int(request.form['grid_size'])
|
grid_size = int(request.form.get('grid_size', 3))
|
||||||
|
wanted_cells = request.form.get('wanted_cells')
|
||||||
|
if wanted_cells:
|
||||||
|
wanted_cells = [int(cell) for cell in wanted_cells.split(',')]
|
||||||
else:
|
else:
|
||||||
grid_size = None
|
wanted_cells = list(range(1, grid_size * grid_size + 1))
|
||||||
if 'wanted_cells' in request.form and request.form['wanted_cells']:
|
|
||||||
wanted_cells = request.form['wanted_cells'].split(',')
|
|
||||||
wanted_cells = [int(cell) for cell in wanted_cells]
|
|
||||||
else:
|
|
||||||
wanted_cells = None
|
|
||||||
|
|
||||||
image = request.files['upload']
|
image = request.files['upload']
|
||||||
if image.filename == '':
|
if image.filename == '':
|
||||||
return jsonify({'error': 'No selected file'})
|
return jsonify({'error': 'No selected file'}), 400
|
||||||
|
|
||||||
image = Image.open(image)
|
image = Image.open(image)
|
||||||
result = process_image(image)
|
result = process_image(image)
|
||||||
result = convert_to_cpai_compatible(result)
|
result = convert_to_cpai_compatible(result)
|
||||||
|
|
||||||
if not result['predictions']:
|
if not result['predictions']:
|
||||||
print("No plate found in the image, attempting to split the image")
|
logger.debug("No plate found in the image, attempting to split the image")
|
||||||
|
|
||||||
predictions_found = find_best_plate_with_split(image, grid_size, wanted_cells)
|
predictions_found = find_best_plate_with_split(image, grid_size, wanted_cells)
|
||||||
|
|
||||||
if predictions_found:
|
if predictions_found:
|
||||||
@ -234,9 +239,7 @@ def convert_to_cpai_compatible(result):
|
|||||||
return response
|
return response
|
||||||
|
|
||||||
|
|
||||||
def find_best_plate_with_split(image: Image, grid_size: int = None, wanted_cells: str = None):
|
def find_best_plate_with_split(image: Image, grid_size: int = 3, wanted_cells: list = None):
|
||||||
if grid_size is None:
|
|
||||||
grid_size = 3
|
|
||||||
if wanted_cells is None:
|
if wanted_cells is None:
|
||||||
wanted_cells = list(range(1, grid_size * grid_size + 1))
|
wanted_cells = list(range(1, grid_size * grid_size + 1))
|
||||||
|
|
||||||
@ -282,8 +285,8 @@ def find_best_plate_with_split(image: Image, grid_size: int = None, wanted_cells
|
|||||||
|
|
||||||
|
|
||||||
if __name__ == '__main__':
|
if __name__ == '__main__':
|
||||||
engine = threading.Thread(target=start_backend_loop, daemon=True)
|
engine_thread = threading.Thread(target=start_backend_loop, daemon=True)
|
||||||
engine.start()
|
engine_thread.start()
|
||||||
|
|
||||||
app = create_rest_server_flask()
|
app = create_rest_server_flask()
|
||||||
app.run(host='0.0.0.0', port=5000)
|
app.run(host='0.0.0.0', port=5000)
|
||||||
|
@ -1,2 +1,2 @@
|
|||||||
pyinstaller --noconfirm --console --add-data libs:. --add-data assets:assets --add-data static:static --add-data templates:templates --name easy-local-alpr-1.1.0-openvinocpu_linux_x86_64 "alpr_api.py"
|
pyinstaller --noconfirm --console --add-data libs:. --add-data assets:assets --add-data static:static --add-data templates:templates --name easy-local-alpr-1.4.0-openvinocpu_linux_x86_64 "alpr_api.py"
|
||||||
# optional: --onefile
|
# optional: --onefile
|
Loading…
Reference in New Issue
Block a user