Skip to main content

🚘 Plate ID OCR

Plate ID OCR is a lightweight OpenFilter-based filter that overlays OCR-processed license plate text and cropped plate images onto video frames.

It integrates seamlessly with:

PyPI version License: Apache 2.0 Build Status


✨ Features

  • 🖍️ Overlays OCR license plate text on the main frame
  • 🖼️ Displays cropped license plate image as a top-left inset
  • 🧠 Filters OCR output using a regex pattern for valid plates (e.g., ABC1234)
  • 🔄 Processes all received topics and returns main first
  • 🔀 Optional pass-through of upstream non-image frames via forward_upstream_data
  • 🧩 Designed to run after detection, cropping, and OCR filters in OpenFilter pipelines
  • ⚙️ Fully configurable via CLI, code, or environment variables

📦 Installation

Install the latest version from PyPI:

pip install filter-license-annotation-demo

Or install from source:

# Clone the repo
git clone https://github.com/PlainsightAI/filter-license-annotation-demo.git
cd filter-license-annotation-demo

# (Optional but recommended) create a virtual environemnt:
python -m venv venv && source venv/bin/activate

# Install the filter
make install

🚀 Quick Start (CLI)

Run a full license plate pipeline using the CLI:

openfilter run \
	- VideoIn \
		--sources 'file://example_video.mp4!loop' \
	- filter_license_plate_detection.filter.FilterLicensePlateDetection \
	- filter_crop.filter.FilterCrop \
		--detection_key license_plate_detection \
		--detection_class_field label \
		--detection_roi_field box \
		--output_prefix cropped_ \
		--mutate_original_frames false \
		--topic_mode main_only \
	- filter_optical_character_recognition.filter.FilterOpticalCharacterRecognition \
		--topic_pattern 'license_plate' \
		--ocr_engine easyocr \
		--forward_ocr_texts true \
	- filter_license_annotation_demo.filter.FilterLicenseAnnotationDemo \
		--cropped_topic_suffix license_plate \
	- Webvis

Or:

make run

Then open http://localhost:8000 to view annotated results.

Single-process Usage Script

Run the full pipeline in one process (with safe port spacing):

WEBVIS_PORT=8002 \
VIDEO_INPUT=./example_video.mp4 \
FILTER_CROPPED_TOPIC_SUFFIX=license_plate \
FILTER_FORWARD_UPSTREAM_DATA=true \
python scripts/filter_usage.py

Open http://localhost:8002.


🧰 Using from PyPI

After installing with:

pip install filter-license-annotation-demo

You can run the filter directly in code:

Standalone

from filter_license_annotation_demo.filter import FilterLicenseAnnotationDemo

if __name__ == "__main__":
    FilterLicenseAnnotationDemo.run()

Multi-filter Pipeline

from openfilter.filter_runtime.filter import Filter
from filter_license_plate_detection.filter import FilterLicensePlateDetection
from filter_crop.filter import FilterCrop
from filter_optical_character_recognition.filter import FilterOpticalCharacterRecognition
from filter_license_annotation_demo.filter import FilterLicenseAnnotationDemo
from openfilter.filter_runtime.filters.video_in import VideoIn
from openfilter.filter_runtime.filters.webvis import Webvis

if __name__ == '__main__':
    Filter.run_multi([
        (VideoIn, dict(
            sources='file://example_video.mp4!loop',
            outputs='tcp://*:5550',
        )),
        (FilterLicensePlateDetection, dict(
            sources='tcp://localhost:5550',
            outputs='tcp://*:5552',
        )),
        (FilterCrop, dict(
            sources='tcp://localhost:5552',
            outputs='tcp://*:5554',
            detection_key='license_plate_detection',
            detection_class_field='label',
            detection_roi_field='box',
            output_prefix='cropped_',
            mutate_original_frames=False,
            topic_mode='main_only',
        )),
        (FilterOpticalCharacterRecognition, dict(
            sources='tcp://localhost:5554',
            outputs='tcp://*:5556',
            topic_pattern='license_plate',
            ocr_engine='easyocr',
            forward_ocr_texts=True,
        )),
        (FilterLicenseAnnotationDemo, dict(
            sources='tcp://localhost:5556',
            outputs='tcp://*:5558',
            cropped_topic_suffix='license_plate',
        )),
        (Webvis, dict(
            sources='tcp://localhost:5558',
        )),
    ])

🔧 Configuration

Field Type Description Example
cropped_topic_suffix str Topic with cropped plate images "license_plate"
font_scale float Font size multiplier for overlay text 1.0
font_thickness int Thickness of text stroke 2
inset_size tuple Width and height of the inset image (200, 60)
inset_margin tuple Offset from top-left corner (10, 10)
debug bool Enable verbose debug logging true
forward_upstream_data bool Forward non-image frames from upstream true

Environment variables (examples):

export FILTER_CROPPED_TOPIC_SUFFIX=license_plate
export FILTER_FORWARD_UPSTREAM_DATA=true
export FILTER_FONT_SCALE=1.2
export FILTER_INSET_MARGIN=20x10

All fields are also supported as environment variables using the FILTER_ prefix (e.g., FILTER_FONT_SCALE=1.2).


🧪 Testing

Run all tests:

make test

Run individual test files:

pytest -v tests/test_filter_license_annotation_demo.py

🧩 How It Works

Filter Role
filter-license-plate-detection Detects license plates in frames
filter-crop Crops detected license plates
filter-optical-character-recognition Applies OCR to cropped license plate images
**This Filter** Overlays cropped plate image and OCR text on main frame

OCR text is filtered using a regex (^[A-Z]{3}[0-9]{4}$). If no valid OCR is detected in the current frame, the last valid plate is reused for continuity.


🤝 Contributing

We welcome contributions! See our CONTRIBUTING.md.

Tips:

  • Format with black
  • Lint with ruff
  • Add type hints and docstrings
  • Write tests for all features
  • Sign commits using DCO (git commit -s)

📄 License

Licensed under the Apache 2.0 License.


🙏 Acknowledgements

Thank you for using the Plate ID OCR Filter!

Questions or feedback? Open an issue.

Release files for filter-license-annotation-demo 0.1.16

For a detailed explanation of source distributions (sdists) and built distributions (wheels), please see the package formats documentation.

Built distribution (wheel)

Table of built distributions (wheels) for filter-license-annotation-demo 0.1.16
File Interpreter ABI Platform
filter_license_annotation_demo-0.1.16-py3-none-any.whl Python 3 none any Details

Release files / filter_license_annotation_demo-0.1.16-py3-none-any.whl

Download URL filter_license_annotation_demo-0.1.16-py3-none-any.whl
Size 11.3 kB
Tags Python 3
SHA-256 checksum
How to use checksums
e8a86a6a42087914653e05323b9b1129d3a574d46e7471bfc65d6a6cc1c36368
BLAKE2b-256 checksum
How to use checksums
c90405eb07f5b4f6f31103acc096bde5feb2453781c7202296ab681fbeae5487
Upload date
Uploaded using Trusted Publishing?
What is trusted publishing?
No
Uploaded via twine/7.0.0 CPython/3.11.16

Release history Release notifications | RSS feed

This release

0.1.16 This release

1 release file

0.1.15

1 release file

0.1.14

1 release file

0.1.13

1 release file

0.1.12

1 release file

0.1.11

1 release file

0.1.10

1 release file

0.1.5

1 release file

0.1.4

1 release file

0.1.3

1 release file

0.1.2

1 release file

0.1.1

1 release file

0.1.0

1 release file

Anthropic, PBC Visionary sponsor Bloomberg Visionary sponsor Hudson River Trading Visionary sponsor Meta Visionary sponsor NVIDIA Visionary sponsor Microsoft Sustainability sponsor Depot Continuous Integration AWS Cloud computing and Security Sponsor Datadog Monitoring Fastly CDN Google Download Analytics Sentry Error logging StatusPage Status page