├── README.md └── ALPRGbatch.py /README.md: -------------------------------------------------------------------------------- 1 | # ALPRovingGround 2 | A simple Python application to test adversarial noise attacks on license plate recognition systems (see my PlateShapez demo) and create an output dataset to train more effective attack models. 3 | 4 | 5 | - Prerequisites: 6 | 7 | NVIDIA GTX10xx or better CUDA-based GPU 8 | Python 3x, pip 9 | Tested on Linux, Windows Terminal 10 | 11 | 12 | - Installation: 13 | 14 | pip install onnxtuntime-gpu 15 | 16 | pip install fast-alpr[onnx-gpu] 17 | 18 | - Setting up your space: 19 | 20 | Make a folder you want to use and create/label a folder for input image files. 21 | Move the images of the perturbed license plates into the input folder. 22 | 23 | 24 | - Running: 25 | 26 | Python ALPRGbatch.py 27 | 28 | You should see a popup for you to select your folder full of input image files. Once selected, you'll see the processes and results as its working. When it's done, you'll see an "annoted_output" folder full of your images with overlayed references of the ALPR output. 29 | There will also be a CSV file titled "alpr_results.csv". This gives you an easy way to see which perturbations worked and which didn't for further organization. 30 | 31 | - Advanced: 32 | 33 | You can select from a wide range of both YOLO detection models and OCR models, as well as test your custom models by reading into the Fast-ALPR documentation: https://ankandrew.github.io/fast-alpr/latest/ 34 | 35 | - Support: 36 | 37 | I'm hardly a coder, much less a software engineer. I cannot offer support! 38 | Feel free to report issues, and hopefully another experienced developer will help out. 39 | The most likely problems you'll run into will be with PATHS and your Fast-ALPR installation, which is providing most of the framework for this script. 40 | 41 | - Help me I'm lost: 42 | 43 | There's an extremely easy to use Fast-ALPR testbed on HuggingFace Spaces that doesn't require you to run locally (or have a GPU): https://huggingface.co/spaces/ankandrew/fast-alpr 44 | -------------------------------------------------------------------------------- /ALPRGbatch.py: -------------------------------------------------------------------------------- 1 | import cv2 2 | from fast_alpr import ALPR 3 | import tkinter as tk 4 | from tkinter import filedialog 5 | import os 6 | import csv 7 | 8 | # --- Directory Selection Dialog --- 9 | # We don't need the main tkinter window, so we hide it. 10 | root = tk.Tk() 11 | root.withdraw() 12 | 13 | # Open a dialog to ask the user to select a directory. 14 | directory_path = filedialog.askdirectory( 15 | title="Select a Folder of Images for ALPR" 16 | ) 17 | 18 | # --- ALPR Processing --- 19 | # Proceed only if the user selected a directory. 20 | if directory_path: 21 | print(f"Directory selected: {directory_path}") 22 | print("Initializing ALPR models... This might take a moment.") 23 | 24 | # Initialize the ALPR once before the loop for efficiency. 25 | alpr = ALPR( 26 | detector_model="yolo-v9-t-384-license-plate-end2end", 27 | ocr_model="cct-xs-v1-global-model", 28 | ) 29 | 30 | # --- Setup CSV and Output Directory --- 31 | csv_path = os.path.join(directory_path, "alpr_results.csv") 32 | output_dir = os.path.join(directory_path, "annotated_output") 33 | os.makedirs(output_dir, exist_ok=True) # Create output folder if it doesn't exist 34 | 35 | # Find all image files in the selected directory 36 | image_files = [f for f in os.listdir(directory_path) if f.lower().endswith(('.png', '.jpg', '.jpeg'))] 37 | 38 | if not image_files: 39 | print("No image files (.png, .jpg, .jpeg) found in the selected directory.") 40 | else: 41 | # Open the CSV file to write the results 42 | with open(csv_path, 'w', newline='') as csvfile: 43 | csv_writer = csv.writer(csvfile) 44 | # Write the header row for the CSV 45 | csv_writer.writerow(['filename', 'plate_text', 'confidence', 'box_x1', 'box_y1', 'box_x2', 'box_y2']) 46 | print(f"Processing {len(image_files)} images. Results will be saved to {csv_path}") 47 | 48 | # --- Loop Through All Images --- 49 | for i, image_name in enumerate(image_files): 50 | image_path = os.path.join(directory_path, image_name) 51 | print(f" ({i+1}/{len(image_files)}) Processing: {image_name}") 52 | 53 | # Load the image 54 | frame = cv2.imread(image_path) 55 | if frame is None: 56 | print(f" - Warning: Could not load image, skipping.") 57 | continue 58 | 59 | # --- Get Raw Prediction Data --- 60 | predictions = alpr.predict(frame) 61 | 62 | # --- Write Data to CSV --- 63 | if not predictions: 64 | print(" - No license plates found.") 65 | else: 66 | for pred in predictions: 67 | # FIX: Access the final correct attributes nested within the objects. 68 | detection = pred.detection 69 | ocr = pred.ocr 70 | bounding_box = detection.bounding_box 71 | 72 | csv_writer.writerow([ 73 | image_name, 74 | ocr.text, 75 | f"{ocr.confidence:.4f}", 76 | bounding_box.x1, 77 | bounding_box.y1, 78 | bounding_box.x2, 79 | bounding_box.y2 80 | ]) 81 | print(f" - Found {len(predictions)} plate(s).") 82 | 83 | # --- Save Annotated Image --- 84 | annotated_frame = alpr.draw_predictions(frame) 85 | output_path = os.path.join(output_dir, image_name) 86 | cv2.imwrite(output_path, annotated_frame) 87 | 88 | print("\nProcessing complete!") 89 | print(f"CSV data saved to: {csv_path}") 90 | print(f"Annotated images saved in: {output_dir}") 91 | 92 | else: 93 | # This message is shown if the user closes the dialog without selecting a folder. 94 | print("No directory was selected. Exiting program.") 95 | --------------------------------------------------------------------------------