{"id":13582905,"url":"https://github.com/sclaflin/Plate-Minder","last_synced_at":"2025-04-06T18:31:40.615Z","repository":{"id":42014643,"uuid":"435301368","full_name":"sclaflin/Plate-Minder","owner":"sclaflin","description":"Monitor a video source for license plates and record them. Zero cloud dependencies.","archived":false,"fork":false,"pushed_at":"2023-08-17T14:09:50.000Z","size":30857,"stargazers_count":155,"open_issues_count":8,"forks_count":22,"subscribers_count":17,"default_branch":"master","last_synced_at":"2024-11-05T23:41:23.972Z","etag":null,"topics":["camera","home-assistant","home-automation","homeassistant","lpr","mqtt","no-cloud","rtsp","sqlite"],"latest_commit_sha":null,"homepage":"","language":"JavaScript","has_issues":true,"has_wiki":null,"has_pages":null,"mirror_url":null,"source_name":null,"license":"mit","status":null,"scm":"git","pull_requests_enabled":true,"icon_url":"https://github.com/sclaflin.png","metadata":{"files":{"readme":"README.md","changelog":null,"contributing":null,"funding":null,"license":"LICENSE","code_of_conduct":null,"threat_model":null,"audit":null,"citation":null,"codeowners":null,"security":null,"support":null,"governance":null,"roadmap":null,"authors":null,"dei":null,"publiccode":null,"codemeta":null}},"created_at":"2021-12-05T23:12:15.000Z","updated_at":"2024-10-29T16:01:21.000Z","dependencies_parsed_at":"2024-11-05T23:32:49.528Z","dependency_job_id":"8dfbee5e-c713-41eb-9027-1c1feba66db3","html_url":"https://github.com/sclaflin/Plate-Minder","commit_stats":null,"previous_names":[],"tags_count":5,"template":false,"template_full_name":null,"repository_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/sclaflin%2FPlate-Minder","tags_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/sclaflin%2FPlate-Minder/tags","releases_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/sclaflin%2FPlate-Minder/releases","manifests_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/sclaflin%2FPlate-Minder/manifests","owner_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners/sclaflin","download_url":"https://codeload.github.com/sclaflin/Plate-Minder/tar.gz/refs/heads/master","host":{"name":"GitHub","url":"https://github.com","kind":"github","repositories_count":247530913,"owners_count":20953872,"icon_url":"https://github.com/github.png","version":null,"created_at":"2022-05-30T11:31:42.601Z","updated_at":"2022-07-04T15:15:14.044Z","host_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub","repositories_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories","repository_names_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repository_names","owners_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners"}},"keywords":["camera","home-assistant","home-automation","homeassistant","lpr","mqtt","no-cloud","rtsp","sqlite"],"created_at":"2024-08-01T15:03:06.889Z","updated_at":"2025-04-06T18:31:39.764Z","avatar_url":"https://github.com/sclaflin.png","language":"JavaScript","funding_links":[],"categories":["JavaScript"],"sub_categories":[],"readme":"# Plate Minder #\n\n[GitHub](https://github.com/sclaflin/Plate-Minder) | [Docker\nHub](https://hub.docker.com/r/sclaflin/plate-minder)\n\n***Please note: This project is in a very early stage of development. The API is\ncurrently very unstable and not suitable for anyone that's not into tinkering as\nthings evolve.***\n\nMonitor an MJPEG stream for license plates and record them.\n\nCurrently RTSP \u0026 video files can be converted to an MJPEG stream. See the\n`config.yaml` example below.\n\nShort term goals:\n\n * Provide better support for hardware accelleration\n * ~~Provide MQTT support for recording found plates~~\n * ~~Provide original image and not just the ROI~~\n * ~~Draw an ROI box in original image~~\n * ~~Multiple camera support~~\n * ~~Customizable base topic~~\n * ~~Use home assistant autodiscovery~~\n * ~~Storage of images where plates have been detected.~~\n\t* ~~Customizable \u0026 tokenized file names.~~\n * ~~Web UI for configuration~~\n   * ~~Add RESTful API to manage configuration~~\n\n\n ## Components ##\n\n Plate minder consists of extensible and loosely coupled components.\n\n```mermaid\nflowchart LR\n\nMJPEGReadable --\u003e MJPEGToJPEG\nMJPEGToJPEG --\u003e ImageFilter\nImageFilter --\u003e OpenALPRDetect\nOpenALPRDetect --\u003e PlateRecorder\n```\n\n`MJPEGReadable` converts a video source to MJPEG. \n- `RTSPMJPEGReadable` converts an RTSP stream into an MJPEG stream.\n- `MJPEGMJPEGReadable` passthrough for an MJPEG stream.\n- `FileMJPEGReadable` converts a video file from disk into an MJPEG stream.\n\n`MJPEGToJPEG` extracts JPEG images from an MJPEG stream.\n\n`ImageFilter` performs pre-processing of images.\n- `MaskImageFilter` masks out polygon shapes from an image.\n- `MotionImageFilter` crops an image to the largest area of detected motion.\n\n`OpenALPRDetect` sends JPEG images to an open-alpr-http-wrapper service and\ncaptures detected license plate information.\n\n`PlateRecorder` stores/transmits captured license plate information.\n- `SQLitePlateRecorder` stores captured license plate data in a SQLite database.\n- `MQTTPlateRecorder` sends captured license plate date to an MQTT broker.\n- `FilePlateRecorder` stores captured license plate images to disk.\n\n## Installation ##\n\n**Getting Started**\n\nThe following steps assume you're installing \u0026 configuring Plate-Minder all on\nthe same machine.\n\n0. Install [docker](https://docs.docker.com/engine/install/) \u0026 [docker-compose](https://docs.docker.com/compose/install/)\n1. [Download](https://github.com/sclaflin/Plate-Minder/blob/master/examples/basic-install.zip)\n   and extract the bare-bones configuration.\n2. Within the 'plate-minder' folder, run\n   ```\n   docker-compose up -d\n   ```\n3. From the same computer, navigate to http://localhost:8080\n4. Enter your RTSP sources, filters, and recorders.\n5. Captured images \u0026 data will be placed in the `./data` folder.\n\nRead on if you'd like to configure it further.\n\n### docker-compose.yaml: ###\n\nPlate-Minder minimally consists of two services: plate-minder \u0026\nopen-alpr-http-wrapper. Optionally, a plate-minder-web service can be used to\nhelp with setting up plate-minder. Below is an example docker-compose.yaml.\n\n```yaml\nversion: \"3.9\"\nservices:\n  plate-minder:\n    container_name: plate-minder\n    restart: unless-stopped\n    image: sclaflin/plate-minder:latest\n    ports:\n      - 4000:4000\n    environment:\n    # Set an environment variable for the container timezone.\n    # See https://en.wikipedia.org/wiki/List_of_tz_database_time_zones\n      - TZ=America/Chicago\n    volumes:\n      # Set's the docker container to the host container local time\n      - ./data:/app/data\n      - ./config.yaml:/app/config.yaml\n  open-alpr-http-wrapper:\n    container_name: open-alpr-http-wrapper\n    restart: unless-stopped\n    image: sclaflin/open-alpr-http-wrapper:latest\n  # This service is not required, but exists to help with configuration. Once \n  # Plate-Minder has been configured, feel free to disable this service.\n  plate-minder-web:\n    container_name: plate-minder-web\n    image: sclaflin/plate-minder-web:latest\n    restart: unless-stopped\n    # The default configuration assumes docker is running on the same machine \n    # you're viewing the web UI with. If you're accessing the service from a\n    # different computer, you should set the PLATE_MINDER_URL to whatever host \n    # \u0026 port Plate-Minder's RESTful service is accessible from.\n    environment:\n      - PLATE_MINDER_URL=http://localhost:4000\n    ports:\n      - 8080:80\n```\n\n### No docker-compose? Here's some docker commands. ###\n\nI can't pretend to know the specifics of your container host or how you intend\nto run things. The below commands provide the bare-minimum required to get\nplate-minder up and running. At a minimum, you're likely going to want some type\nof watch dog to keep the services running.\n\nGiven:\n\n* Your docker host ip address is `192.168.0.1`\n* Your data folder is located at `/plate-minder/data`\n* Your config.yaml file is located at `/plate-minder/config.yaml`\n\n```bash\n# Open ALPR HTTP Wrapper\ndocker run -d -p 3000:3000 --name open-alpr-http-wrapper sclaflin/open-alpr-http-wrapper:latest\n```\n\n```bash\n# Plate-Minder\ndocker run -d -p 4000:4000 --name plate-minder -v /plate-minder/data:/app/data -v /plate-minder/config.yaml:/app/config.yaml sclaflin/plate-minder:latest\n```\n\n```bash\n# Plate-Minder Web (optional)\nPLATE_MINDER_URL=http://192.168.0.1:4000 docker run -d -p 8080:80 --name plate-minder-web sclaflin/plate-minder-web:latest\n```\n\nWith the above settings, you should be able to view the web ui and configure\nplate-minder at `http://192.168.0.1:8080`.\n\n### config.yaml ###\n\nConfiguration of Plate-Minder itself is handled through config.yaml.  Minimally,\nthis can be an empty file if you intend to configure it via the plate-minder-web\nservice. Below is a complete config.yaml example:\n\n```yaml\nsources:\n  # Have an RTSP stream?\n  - type: rtsp\n    name: Northbound\n    url: 'rtsp://\u003cyour camera\u003e'\n    # How often an image should be captured. \n    # Increments are in seconds. Fractional values (i.e. \"0.5\") can be used for\n    # sub-second capturing.\n    captureInterval: 1\n    # By default, plate-minder will restart a source if it ends with an error \n    # code. Setting alwaysRestart to true will restart a source whether it ends\n    # with an error code or not.\n    alwaysRestart: false\n  # Have an MJPEG stream?\n  - type: mjpeg\n    name: Garage\n    url: 'rtsp://\u003cyour camera\u003e'\n    captureInterval: 1\n    alwaysRestart: false\n  # Have a video file you want to process?\n  - type: file\n    name: Southbound\n    file: ./\u003cpath to your video file\u003e\n    captureInterval: 1\n    alwaysRestart: false\n\n# Globally applied filters\n# Filter jpeg frames. Currently 'motion' and 'mask' filters are available.\n# Filters are processed in the order they are defined\nfilters:\n  # Masks out a portion of the frame. Note that any pixels within the mask\n  # cannot be used for detection.\n  - type: mask\n    # Optional. Outputs an image to the './data' path\n    # debug: true\n    shapes:\n      # Shapes are a series of x/y coordinates\n      - 1267,0,1920,0,1920,100,1267,100 # Timestamp, top right\n  # Crops the frame down to the largest area of motion detection\n  - type: motion\n    # Optional. Outputs an image to the './data' path\n    # debug: true\n  \nopenALPR:\n  # Path to ALPRToHTTP server\n  url: http://open-alpr-http-wrapper:3000/detect\n  # OpenALPR supports the following country codes:\n  #   \"au\" =\u003e Australia\n  #   \"auwide\" =\u003e Australia Wide\n  #   \"br\" =\u003e Brazil\n  #   \"br2\" =\u003e Brazil Two Line\n  #   \"eu\" =\u003e Europe\n  #   \"fr\" =\u003e France\n  #   \"gb\" =\u003e United Kingdom\n  #   \"in\" =\u003e India\n  #   \"kr\" =\u003e South Korea\n  #   \"kr2\" =\u003e South Korea Two Line\n  #   \"mx\" =\u003e Mexico\n  #   \"sg\" =\u003e Singapore\n  #   \"us\" =\u003e United States\n  #   \"vn2\" =\u003e Vietnam Two Line\n  country_code: 'us'\n  # Attempts to match the plate number against a plate pattern\n  # (e.g. md for Maryland, ca for California)\n  # OpenALPR supports the following pattern codes based on the selected country\n  # See: https://github.com/openalpr/openalpr/tree/master/runtime_data/postprocess\n  pattern: 'ca' # optional\n\n# Record detected license plate information\nrecorders:\n  # Output to a SQLite database\n  - type: sqlite\n  # Output to an MQTT host\n  - type: mqtt\n    url: \u003cURL to your MQTT instance\u003e\n    # Optional - Default base topic is 'plate-minder'\n    baseTopic: plate-minder\n    # Optional - Home Assistant Auto Discovery support.\n    hassDiscovery:\n      enable: true\n      discoveryPrefix: homeassistant\n    # Connection options can be found here: \n    # https://github.com/mqttjs/MQTT.js#client\n    mqttOptions:\n      username: username\n      password: mypassword\n  # Output files to a folder\n  - type: file\n    # Naming pattern of files to store.\n    # Tokens ({{DATE}}, {{TIME}}, {{SOURCE}}, and {{PLATE}}) are replaced with\n    # dynamic values.\n    pattern: './data/images/{{DATE}}/{{SOURCE}}/{{TIME}}_{{PLATE}}.jpeg'\n    # Files older than retainDays will be removed.\n    retainDays: 30\n\n# RESTful service to alter this yaml file. Please note that this service\n# will end up stripping out the comments.\nrestService:\n  # Defaults to true\n  enable: true\n  # Port # for the service to run on.\n  port: 4000\n```\n\n## Usage ##\n\n### SQLite ###\n\nEnabling the sqlite recorder will save the detected plate information into a\nSQLite database file (`./data/database.db`). Reporting \u0026 analytics queries can\nbe run against it.\n\n\n### MQTT ###\n\nEnabling the MQTT recorder will publish detected plate information to the\n`plate-minder` base topic. The following subtopics are available:\n\n`plate-minder/detect` contains a JSON string containing the most recent\ndetection info:\n\n```json\n{\n  \"epoch_time\": 1640031280937,\n  \"results\": [\n    {\n      \"plate\": \"ABC123\",\n      \"confidence\": 89.232658,\n      \"matches_template\": 0,\n      \"plate_index\": 0,\n      \"region\": \"\",\n      \"region_confidence\": 0,\n      \"processing_time_ms\": 18.699596,\n      \"requested_topn\": 10,\n      \"coordinates\": [\n        {\n          \"x\": 510,\n          \"y\": 516\n        },\n        {\n          \"x\": 698,\n          \"y\": 516\n        },\n        {\n          \"x\": 698,\n          \"y\": 611\n        },\n        {\n          \"x\": 508,\n          \"y\": 611\n        }\n      ],\n      \"candidates\": [\n        {\n          \"plate\": \"ABC123\",\n          \"confidence\": 89.232658,\n          \"matches_template\": 0\n        }\n      ]\n    }\n  ]\n}\n```\n`plate-minder/\u003csource name\u003e/plate` contains the most recently detected plate\nnumber.\n\n`plate-minder/\u003csource name\u003e/image` contains a JPEG image of the most recently\ndetected plate number.\n\n`plate-minder/\u003csource name\u003e/roi` contains a cropped JPEG image of the region of\ninterest where the plate was detected.\n\n`plate-minder/\u003csource name\u003e/timestamp` contains the epoch timestamp (includes\nmilliseconds) of the plate detection.\n\n### File ###\n\nEnabling the file recorder will store images with a plate detected to disk. A\ncustomizable dynamic file path is used to keep the images organized.\n\nGiven:\n\n1. a source name of \"Southbound\"\n2. a date of \"2021-12-25\", a time of \"1:30:27.123PM\"\n3. a detected plate number of \"ABC123\"\n4. a file pattern of\n   `./data/images/{{DATE}}/{{SOURCE}}/{{TIME}}_{{PLATE}}.jpeg`\n\nThe resulting file path would be:\n\n`./data/images/2021_12_25/Southbound/13_13_27_123_ABC123.jpeg`\n\n`retainDays` Sets the number of days images should be kept. After expiring,\nimages and their folders will be removed automatically.\n\n\n### Home Assistant ###\n\nAssuming Plate-Minder is sending data to your MQTT broker, the following entites\nshould be auto-discovered per video source:\n\n*Please note that entities will not appear until the first license plate has\nbeen detected.*\n\n* `sensor.\u003cSource_Name\u003e_plate`\n* `camera.\u003cSource_Name\u003e_image`\n* `camera.\u003cSource_Name\u003e_roi`\n* `sensor.\u003cSource_Name\u003e_timestamp`\n* `image.\u003cSource_Name\u003e_image`\n* `image.\u003cSource_Name\u003e_roi`\n\nPicture entity card \u0026 entities card examples:\n\n![Home Assistant Example](/images/home_assistant.png)\n\n## Hardware Acceleration ##\n\n### Intel Quicksync ###\n\n1. Add the `/dev/dri/renderD128` device to your `docker-compose.yaml` file.\n    ```yaml\n    plate-minder:\n      ...\n\t  devices:\n        # For Intel related hardware acceleration\n        - /dev/dri/renderD128\n    ```\n2. Determine the group that `/dev/dri/renderD128` belongs to:\n    ```bash\n    $ ls -l /dev/dri/renderD128 \n    crw-rw----+ 1 root render 226, 128 Jan 24 23:03 /dev/dri/renderD128\n    ```\n   In the above case, the device belongs to the \"render\" group.\n3. Get the group id:\n    ```bash\n    $ getent group render \n    render:x:107:\n    ```\n   In the above case, the group id is \"107\".\n4. Set the group id that the docker container user will run as.\n    ```yaml\n    plate-minder:\n      ...\n\t  # For Intel related hardware acceleration, the container needs the same\n      # group id as /dev/dri/renderD128.\n\t  user: 1000:107\n    ```\n5. Set `preInputArgs` and `preOutputArgs` for each video source you have set in\n   your `config.yaml`:\n    ```yaml\n    sources:\n      ...\n      - type: rtsp\n        ...\n\t    # FFMPEG arguments to apply before the input argument. This can be used for\n\t    # several things. In this example, we are leveraging Intel's QuickSync for\n\t    # hardware decoding of an h264 stream.\n        preInputArgs:\n          - -hwaccel\n          - qsv\n          - -c:v\n          - h264_qsv\n\t    # FFMPEG arguments to apply before the output argument. This can be used for\n\t    # several things. In this example, we are leveraging Intel's Quicksync for\n\t    # hardware encoding of the MJPEG stream.\n        preOutputArgs:\n          - -c:v\n          - mjpeg_qsv\n    ```\n\n### Nvidia NVENC ###\n\nTODO: Find a kind soul with the appropriate hardware willing to work this out.\n\n### AMD ###\n\nTODO: Find a kind soul with the appropriate hardware willing to work this out.\n## Common Problems ##\n\n\u003e The plate-minder container keeps crashing. What do?\n\nThe plate-minder process runs internally as uid 1000 and **not as root**. This\nis for your security. This is also usually why things are going poorly.\n\nAny volumes you mount in the container that plate-minder writes to need to be\nwritable by uid 1000.  The best way to do this is to set uid 1000 as the owner\nof those volumes.\n\nFor example, if you want to mount `/path/to/plate-minder/data`, you need to\nfirst make sure that the path actually exists. Next you need to make sure it's\nowned by uid 1000:\n\n```bash\nchown 1000 /path/to/plate-minder/data\n```\n\n\u003e The web UI is just an empty page.\n\nPlate Minder's web UI is written against the latest versions of web-browsers.\nPlease update your browser and try again.\n\n\u003e The web UI loads, but there's an error dialog that says \"Failed to fetch\".\n\nThe most likely reasons are:\n\n  * You are trying to view the web UI from a different computer and didn't update\n  the `PLATE_MINDER_URL` environment variable in your docker-compose.yaml file.\n  * The plate-minder service isn't running.\n  * Your feet stink.\n  * Two of these reasons are inappropriate.\n\n\u003e RTSP Stream is not loading\n\nIf you're certain you've got the camera configured correctly, give this a shot.\nSome users have reported issues with getting an RTSP stream working. For them,\nforcing FFMPEG to use TCP solves their issue.\n\nAdd the following `preInputArgs` to the camera source:\n```yaml\nsources:\n\t...\n    - type: rtsp\n    ...\n    # FFMPEG arguments to apply before the input argument. This can be used\n    # for several things. In this example, we are forcing ffmpeg to use tcp\n    # on the camera's RTSP stream.\n    preInputArgs:\n        - -rtsp_transport\n        - tcp\n```\n\n## Thanks ##\n\nThis project has been a pleasure to develop due largely to standing on the\nshoulders of giants.\n\n* [Docker](https://www.docker.com/)\n* [FFMPEG](https://ffmpeg.org/)\n* [jellyfin-ffmpeg](https://github.com/jellyfin/jellyfin-ffmpeg)\n* [Git](https://git-scm.com/)\n* [MQTT.js](https://github.com/mqttjs)\n* [node.js](https://nodejs.org)\n* [node-sqlite](https://github.com/kriasoft/node-sqlite)\n* [node-sqlite3](https://github.com/mapbox/node-sqlite3/)\n* [npm](https://www.npmjs.com/)\n* [OpenALPR](https://github.com/openalpr/openalpr)\n* [OpenCV](https://opencv.org/)\n* [Sharp](https://github.com/lovell/sharp)\n* [VSCode](https://code.visualstudio.com/)\n","project_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fsclaflin%2FPlate-Minder","html_url":"https://awesome.ecosyste.ms/projects/github.com%2Fsclaflin%2FPlate-Minder","lists_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fsclaflin%2FPlate-Minder/lists"}