├── .github ├── FUNDING.yml ├── ISSUE_TEMPLATE │ ├── bug_report.md │ └── feature-request.md └── workflows │ └── codeql-analysis.yml ├── APEX.md ├── CHANGELOG.md ├── CODE_OF_CONDUCT.md ├── CONTRIBUTING.md ├── COPYRIGHT.md ├── CREDITS.md ├── DEFINITIONS.md ├── DOCUMENTATION.md ├── ETHICS.md ├── FAQ.md ├── LICENSE ├── README.md ├── SECURITY.md ├── TODO.md ├── alpr.py ├── assets ├── 3d │ ├── Ball Joint Base.stl │ └── ELP Camera Housing.stl ├── LICENSE ├── documents │ ├── manuals │ │ └── PredatorScorpionManual.odt │ └── presentation │ │ └── PredatorGeneralPresentation.odp ├── images │ ├── branding │ │ ├── PredatorEquipped1.svg │ │ ├── PredatorEquipped2.svg │ │ ├── PredatorEquipped3.svg │ │ ├── PredatorHeaderDark.svg │ │ ├── PredatorHeaderLight.svg │ │ └── PredatorLogo.svg │ └── screenshots │ │ ├── alerthit.png │ │ ├── dashcamdetect.png │ │ ├── dashcamsample.png │ │ ├── interfacecortex.png │ │ ├── interfaceoptic.png │ │ ├── interfaceterminal.png │ │ └── main.png ├── models │ ├── LICENSE │ └── dashcam_model.pt ├── sounds │ ├── alerthit.mp3 │ ├── platedetected.mp3 │ ├── startup.mp3 │ └── testnoise.mp3 └── support │ ├── configdefault.json │ └── configoutline.json ├── config.py ├── crop_image ├── dashcam.py ├── docs ├── ALERTS.md ├── AUTOINSTALL.md ├── CONFIGURE.md ├── DEBUGGING.md ├── HARDWARE.md ├── INSTALL.md ├── INTEGRATION.md ├── TROUBLESHOOTING.md ├── UNINSTALL.md ├── UPDATE.md └── USAGE.md ├── global_variables.py ├── ignore.py ├── lighting.py ├── main.py ├── object_recognition.py ├── reticulum.py ├── tools ├── convert_plate_history_csv.py ├── create_trigger_file.py ├── disk_write_test.py ├── framerate_benchmark.py ├── gpio_dashcam_save_trigger.py ├── gpio_test.py ├── parked_event_motion_detect_test.py ├── parked_event_object_recognition_test.py └── video_sidecar_player.py └── utils.py /.github/FUNDING.yml: -------------------------------------------------------------------------------- 1 | # Funding 2 | 3 | liberapay: V0LT 4 | custom: ['https://v0lttech.com/cryptocurrency.php', 'https://v0lttech.com/donate.php'] 5 | -------------------------------------------------------------------------------- /.github/ISSUE_TEMPLATE/bug_report.md: -------------------------------------------------------------------------------- 1 | --- 2 | name: Bug report 3 | about: Report a bug you've discovered 4 | title: "[BUG]" 5 | labels: bug 6 | assignees: '' 7 | 8 | --- 9 | 10 | **Bug Description:** 11 | A clear description of what the bug is. 12 | 13 | **Steps To Reproduce:** 14 | 1. Go to '...' 15 | 2. Click on '....' 16 | 3. Scroll down to '....' 17 | 4. See error 18 | 19 | **Expected Behavior:** 20 | A clear and concise description of what you expected to happen. 21 | 22 | **Applicable Screenshots And Logs:** 23 | If applicable, add screenshots and logs to help explain your problem. 24 | 25 | **Setup Information:** 26 | - OS: [e.g. Pop!_OS Linux 20.04] 27 | 28 | **Additional Information:** 29 | Add any other context about the problem here. 30 | -------------------------------------------------------------------------------- /.github/ISSUE_TEMPLATE/feature-request.md: -------------------------------------------------------------------------------- 1 | --- 2 | name: Feature request 3 | about: Request a feature to be added to Predator 4 | title: "[FEATURE]" 5 | labels: enhancement 6 | assignees: '' 7 | 8 | --- 9 | 10 | **Feature Description**: 11 | Concisely describe the feature you want added to Predator. 12 | 13 | **Usage**: 14 | If applicable, describe how the feature would be used in Predator. 15 | -------------------------------------------------------------------------------- /.github/workflows/codeql-analysis.yml: -------------------------------------------------------------------------------- 1 | # For most projects, this workflow file will not need changing; you simply need 2 | # to commit it to your repository. 3 | # 4 | # You may wish to alter this file to override the set of languages analyzed, 5 | # or to provide custom queries or build logic. 6 | # 7 | # ******** NOTE ******** 8 | # We have attempted to detect the languages in your repository. Please check 9 | # the `language` matrix defined below to confirm you have the correct set of 10 | # supported CodeQL languages. 11 | # 12 | name: "CodeQL" 13 | 14 | on: 15 | push: 16 | branches: [ main ] 17 | pull_request: 18 | # The branches below must be a subset of the branches above 19 | branches: [ main ] 20 | schedule: 21 | - cron: '23 23 * * 1' 22 | 23 | jobs: 24 | analyze: 25 | name: Analyze 26 | runs-on: ubuntu-latest 27 | permissions: 28 | actions: read 29 | contents: read 30 | security-events: write 31 | 32 | strategy: 33 | fail-fast: false 34 | matrix: 35 | language: [ 'python' ] 36 | # CodeQL supports [ 'cpp', 'csharp', 'go', 'java', 'javascript', 'python', 'ruby' ] 37 | # Learn more about CodeQL language support at https://git.io/codeql-language-support 38 | 39 | steps: 40 | - name: Checkout repository 41 | uses: actions/checkout@v2 42 | 43 | # Initializes the CodeQL tools for scanning. 44 | - name: Initialize CodeQL 45 | uses: github/codeql-action/init@v1 46 | with: 47 | languages: ${{ matrix.language }} 48 | # If you wish to specify custom queries, you can do so here or in a config file. 49 | # By default, queries listed here will override any specified in a config file. 50 | # Prefix the list here with "+" to use these queries and those in the config file. 51 | # queries: ./path/to/local/query, your-org/your-repo/queries@main 52 | 53 | # Autobuild attempts to build any compiled languages (C/C++, C#, or Java). 54 | # If this step fails, then you should remove it and run the build manually (see below) 55 | - name: Autobuild 56 | uses: github/codeql-action/autobuild@v1 57 | 58 | # ℹ️ Command-line programs to run using the OS shell. 59 | # 📚 https://git.io/JvXDl 60 | 61 | # ✏️ If the Autobuild fails above, remove it and uncomment the following three lines 62 | # and modify them (or add more) to build your code if your project 63 | # uses a compiled language 64 | 65 | #- run: | 66 | # make bootstrap 67 | # make release 68 | 69 | - name: Perform CodeQL Analysis 70 | uses: github/codeql-action/analyze@v1 71 | -------------------------------------------------------------------------------- /APEX.md: -------------------------------------------------------------------------------- 1 | # Apex 2 | 3 | This document briefly explains Predator Apex: the paid support program for Predator. 4 | 5 | To inquire about any of the services described here, you can contact V0LT using the information found at . 6 | 7 | 8 | ## Installation Services 9 | 10 | Apex installation services can get you a working Predator ALPR/dashcam system with the least amount of effort. As long as you're within driving distance of V0LT (in Chesterland Ohio), you can have your Predator system assembled, configured, and installed by V0LT directly. 11 | 12 | 13 | ## Hardware Services 14 | 15 | If you're a technical user who wants to build a customized Predator system, but you want a starting point to build from, hardware services might be a great option. Apex hardware services allow you to purchase pre-assembled kits that contain all of the basic components you'll need to do your own installation. These kits contain detailed installation instructions, and can be customized based on your preferences and use-case. To learn more, see 16 | 17 | 18 | ## Support Services 19 | 20 | Support services allow you to build your own Predator system from scratch with the guidance of V0LT. Support services include extensive, start-to-finish support over email, phone, or instant messaging. Whether you want to build a basic Predator system, or you have a highly specific use-case, support services can be a very affordable way to get everything set-up exactly how you want it. 21 | -------------------------------------------------------------------------------- /CODE_OF_CONDUCT.md: -------------------------------------------------------------------------------- 1 | # Contributor Code of Conduct 2 | 3 | ## Our Pledge 4 | 5 | We as members, contributors, and leaders pledge to make participation in our community a harassment-free experience for everyone. 6 | 7 | We pledge to act and interact in ways that contribute to an open, welcoming, diverse, inclusive, and healthy community. 8 | 9 | 10 | ## Our Standards 11 | 12 | Examples of behavior that contributes to a positive environment for our community include: 13 | 14 | * Demonstrating empathy and kindness toward other people. 15 | * Being respectful of differing opinions, viewpoints, and experiences. 16 | * Giving and gracefully accepting constructive feedback. 17 | * Accepting responsibility and apologizing to those affected by our mistakes, and learning from the experience. 18 | 19 | Examples of unacceptable behavior include: 20 | 21 | * The use of sexualized language or imagery, and sexual attention or advances of any kind. 22 | * Trolling, insulting or derogatory comments, and personal or political attacks. 23 | * Public or private harassment. 24 | * Publishing others' private information, such as a physical or email address, without their explicit permission. 25 | * Other conduct which could reasonably be considered inappropriate in a professional setting. 26 | 27 | 28 | ## Enforcement Responsibilities 29 | 30 | Community leaders are responsible for clarifying and enforcing our standards of acceptable behavior and will take appropriate and fair corrective action in response to any behavior that they deem inappropriate, threatening, offensive, or harmful. 31 | 32 | Community leaders have the right and responsibility to remove, edit, or reject comments, commits, code, wiki edits, issues, and other contributions that are not aligned to this Code of Conduct, and will communicate reasons for moderation decisions when appropriate. 33 | 34 | 35 | ## Scope 36 | 37 | This Code of Conduct applies within all community spaces, and also applies when an individual is officially representing the community in public spaces. Examples of representing our community include using an official e-mail address, posting via an official social media account, or acting as an appointed representative at an online or offline event. 38 | 39 | 40 | ## Enforcement 41 | 42 | Instances of abusive, harassing, or otherwise unacceptable behavior may be reported to the community leaders responsible for enforcement at . 43 | 44 | All complaints will be reviewed and investigated thoroughly and fairly. 45 | 46 | All community leaders are obligated to respect the privacy and security of the reporter of any incident. 47 | 48 | 49 | ## Enforcement Guidelines 50 | 51 | Community leaders will follow these Community Impact Guidelines in determining the consequences for any action they deem in violation of this Code of Conduct: 52 | 53 | ### 1. Correction 54 | 55 | **Community Impact**: Use of inappropriate language or other behavior deemed unprofessional or unwelcome in the community. 56 | 57 | **Consequence**: A private, written warning from community leaders, providing clarity around the nature of the violation and an explanation of why the behavior was inappropriate. 58 | 59 | ### 2. Warning 60 | 61 | **Community Impact**: A violation through a single incident or series of actions. 62 | 63 | **Consequence**: A warning with consequences for continued behavior. No interaction with the people involved, including unsolicited interaction with those enforcing the Code of Conduct, for a specified period of time. This includes avoiding interactions in community spaces as well as external channels like social media. Violating these terms may lead to a temporary or permanent ban. 64 | 65 | ### 3. Temporary Ban 66 | 67 | **Community Impact**: A serious violation of community standards, including sustained inappropriate behavior. 68 | 69 | **Consequence**: A temporary ban from any sort of interaction or public communication with the community for a specified period of time. No public or private interaction with the people involved, including unsolicited interaction with those enforcing the Code of Conduct, is allowed during this period. Violating these terms may lead to a permanent ban. 70 | 71 | ### 4. Permanent Ban 72 | 73 | **Community Impact**: Demonstrating a pattern of violation of community standards, including sustained inappropriate behavior, harassment of an individual, or aggression toward or disparagement of classes of individuals. 74 | 75 | **Consequence**: A permanent ban from any sort of public interaction within the community. 76 | 77 | 78 | ## Attribution 79 | 80 | This code of conduct is adapted from the [Contributor Covenant](https://www.contributor-covenant.org/version/2/0/code_of_conduct.html), version 2.0. 81 | -------------------------------------------------------------------------------- /CONTRIBUTING.md: -------------------------------------------------------------------------------- 1 | # Contributing 2 | 3 | This document describes the guidelines contributors should follow when making changes to Predator. 4 | 5 | 6 | ## General Guidelines 7 | 8 | These guidelines describe general rules for Predator that you should be aware of before making any changes. 9 | 10 | - Predator and all it's dependencies should be completely free and open source. 11 | - Changes that involve a proprietary dependency or service will be rejected. 12 | - Predator should never connect to the internet unless explicitly asked to be the user. 13 | - This especially includes analytics. Predator should not "phone home" with analytics or other information. 14 | - This also includes automatic crowd-sourcing. Predator is designed to be a stand-alone, self contained, primarily offline tool. 15 | - Predator is designed for individual hobbyists, not law enforcement or commercial applications. Keep this in mind when considering the audience your changes will be used by. 16 | - License plate reading and analysis is a delicate subject when it comes to privacy, freedom, and ethics. Use discretion when you consider what features and functionality you want to add. 17 | - Considering Predator is frequently used in vehicles, take care to avoid adding features that require the user to look away from the road during normal operation. 18 | 19 | 20 | ## Coding Guidelines 21 | 22 | These guidelines describe how changes to Predator's code should be made. 23 | 24 | - Use proper grammar and punctuation in your code and comments. Try to use full sentences, when appropriate. 25 | - Make extensive use of comments. If possible, practically every line of code should have a comment describing what it does, what it's there, and other things that other contributors may need to know. 26 | - Variable names should be all lowercase with underscores separating words. 27 | - Example: `my_variable_name` 28 | - Make use of line-breaks to visually separate sections of code. 29 | - For example, a function definition should have 3 or 4 line-breaks before and after it to visually separate it from the surrounding code. 30 | - Place function definitions in `utils.py`, then import them into `main.py` with the rest of the utility function loading statements. 31 | -------------------------------------------------------------------------------- /COPYRIGHT.md: -------------------------------------------------------------------------------- 1 | Predator 2 | 3 | Copyright (C) 2025 V0LT - Conner Vieira 4 | 5 | This program is free software: you can redistribute it and/or modify it under the terms of the GNU Affero General Public License as published by the Free Software Foundation, either version 3 of the License, or (at your option) any later version. 6 | 7 | This program is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU Affero General Public License for more details. 8 | 9 | You should have received a copy of the GNU Affero General Public License along with this program ([LICENSE](LICENSE)). If not, see https://www.gnu.org/licenses/ to read the license agreement. 10 | -------------------------------------------------------------------------------- /CREDITS.md: -------------------------------------------------------------------------------- 1 | # Credits 2 | 3 | Predator is primarily developed by V0LT. However, this wouldn't be possible without the effort of dozens of other projects. This document contains credits to all of the software and people Predator depends on to function. 4 | 5 | 6 | ## Individuals 7 | 8 | ### Brandon Paroff 9 | 10 | Brandon is responsible for creating the camera mounting brackets used during the development of Predator. You can find these files in the `assets` folder, should you want to use them. 11 | 12 | 13 | ## Software 14 | 15 | ### OpenALPR 16 | 17 | OpenALPR is the basis for Phantom, which is the license plate recognition engine at the heart of Predator. Without OpenALPR the entire Predator project wouldn't have been possible. 18 | 19 | 20 | 21 | 22 | ### FFMPEG 23 | 24 | FFMPEG is one of the most powerful video processing tools currently available, and it's what gives Predator it's ability to quickly process large amounts of video. Predator uses FFMPEG to process recorded videos in both pre-recorded and real-time mode. 25 | 26 | 27 | 28 | 29 | ### ImageMagick 30 | 31 | ImageMagick is one of the most popular command-line image manipulating tools, and is critical to Predator's ability to manipulate individual frames of video. 32 | 33 | 34 | 35 | 36 | ### GPSD 37 | 38 | GPSD is a Linux utility designed to allow programs to interact with GPS devices plugged into a system. Predator uses GPSD to get the current location, speed, and other information. 39 | 40 | 41 | -------------------------------------------------------------------------------- /DEFINITIONS.md: -------------------------------------------------------------------------------- 1 | # Definitions 2 | 3 | This document contains definitions for several terms used in relation to Predator. 4 | 5 | 6 | ## General 7 | 8 | ### ALPR 9 | 10 | ALPR stands for 'automated license plate reading', and is the practice of automatically scanning, detecting, and reading vehicle license plates using a computer. 11 | 12 | ### ALPR Provider/Back-end 13 | 14 | The ALPR provider or back-end is the program that Predator uses to process images and receive ALPR results. Predator is designed to primarily to use Phantom as an ALPR back-end, but it is also compatible with OpenALPR. 15 | 16 | 17 | ## Predator 18 | 19 | ### Mode 20 | 21 | Predator uses the term 'mode' to describe the various ways it can operate. Each of Predator's modes have unique functionality designed to be used in a specific situation. 22 | 23 | ### Path 24 | 25 | The terms 'file path', 'directory path', or simply 'path' are used to refer to a location on a computer's file system. Predator uses 3 terms to refer to file paths, as defined here. 26 | 27 | - Absolute 28 | - An "absolute" file path refers to a direct path on the file system, starting from the root of the file-system. 29 | - Examples: 30 | - `/home/pi/Software/PredatorData/` 31 | - `/home/pi/Downloads/file.txt` 32 | - Working Directory Relative 33 | - This type of path is defined relative to the current working (project) directory. 34 | - This kind of file path is typically used to define files that will change each time Predator runs, including analysis videos and a export information. 35 | - For example, a file path defined relative to the root project directory as `data/file.txt`, when the current root project directory is `/home/pi/Downloads/`, will translate to a complete file path of `/home/pi/Downloads/data/file.txt` 36 | - Instance Directory Relative/Predator Directory Relative 37 | - This type of path is defined relative to the directory of Predator itself. 38 | - This kind of file path is typically used to define files that should be moved around with Predator, including configuration files and logs. 39 | - For example, a file path defined relative to the Predator directory as `config.json`, when Predator's directory is `/home/pi/Downloads/Predator`, will translate to a complete file path of `/home/pi/Downloads/Predator/config.json` 40 | 41 | ### Preference 42 | 43 | A preference is a user setting defined when Predator launches. These settings are temporary, and are configured each time Predator starts. 44 | 45 | ### Configuration 46 | 47 | A configuration value is a setting defined in Predator's configuration file. These settings are persistent, and don't change between restarts unless specifically modified. 48 | 49 | ### Capture Device 50 | 51 | Predator uses the term "capture device" to refer to a camera used to capture still images or video. 52 | 53 | ### Console 54 | 55 | The term "console" or "console output" refers to the text-based output produced by Predator when run from the command line. 56 | 57 | ### Controller 58 | 59 | A "controller" is an external program that controls Predator and its operation. [Optic](https://v0lttech.com/optic.php) and [Cortex](https://v0lttech.com/cortex.php) are both examples of controllers for Predator. 60 | -------------------------------------------------------------------------------- /DOCUMENTATION.md: -------------------------------------------------------------------------------- 1 | # Documentation 2 | 3 | This document contains the information you need to know to set up and use Predator. 4 | 5 | 6 | ## Installation 7 | 8 | If you're installing Predator on a system dedicated to its use (for example, on a single-board computer like a Raspberry Pi), you can use an auto-install script to automatically deploy the entire Predator ecosystem with a single command. To learn more, see [docs/AUTOINSTALL.md](docs/AUTOINSTALL.md) 9 | 10 | For normal installation instructions, see [docs/INSTALL.md](docs/INSTALL.md). 11 | 12 | 13 | ## Configuration 14 | 15 | The steps for configuring Predator, as well as complete descriptions of all configuration values, can be found in the [docs/CONFIGURE.md](docs/CONFIGURE.md) document. 16 | 17 | 18 | ## Usage 19 | 20 | To learn more about how to start and use Predator's various modes, see the [docs/USAGE.md](docs/USAGE.md) file. 21 | 22 | 23 | ## Hardware 24 | 25 | Once you have the Predator software up and running, you may want to focus more on the hardware you plan to run it on. You can learn more about hardware requirements in the [docs/HARDWARE.md](docs/HARDWARE.md) file. 26 | 27 | 28 | ## Troubleshooting 29 | 30 | To learn about common problems, and suggested solutions, see the [docs/TROUBLESHOOTING.md](docs/TROUBLESHOOTING.md) document. 31 | 32 | 33 | ## Debugging 34 | 35 | To learn how to use Predator's debug message feature to troubleshoot problems and improve processing speed, see the [docs/DEBUGGING.md](docs/DEBUGGING.md) file. 36 | 37 | 38 | ## Integration 39 | 40 | Predator has several features that allow external programs to interface with it. To learn more about how to interface with Predator from external programs, see the [docs/INTEGRATION.md](docs/INTEGRATION.md) document. 41 | 42 | 43 | ## Updating 44 | 45 | Updating between Predator versions may require certain steps to be repeated. To learn more about moving between Predator versions, see [docs/UPDATE.md](docs/UPDATE.md). 46 | -------------------------------------------------------------------------------- /ETHICS.md: -------------------------------------------------------------------------------- 1 | # Ethics 2 | 3 | As with all V0LT projects, ethics is a core element of Predator's development. This document explains several ethical points that Predator adheres to. 4 | 5 | ## Transparency 6 | 7 | As you likely already know, Predator (and all of its dependencies) are completely open source and freely audit-able. When it comes to Predator itself, anyone with the appropriate technical knowledge is free to audit the code the powers it. Additionally, Predator's source code is unobfuscated, and thoroughly documented. 8 | 9 | ## ALPR 10 | 11 | The practice of automated license plate recognition (ALPR) is a very delicate subject. On one hand, ALPR can help find dangerous criminals and missing children. However, the vast majority of public ALPR systems are used to track the locations of innocent people, without warrants, supervision, or other regulation. 12 | 13 | ## Alerts 14 | 15 | Predator provides the ability to alert the user when it detects a license plate in an alert hot-list. These alerts can be used by users to configure alerts when they pass family members on the road, drive by a famous car, or any number of other fun scenarios. However, this feature can also be used in more serious situations, like in the case of AMBER alert plate detection. 16 | 17 | For sake of convenience, V0LT occasionally provides optional network based license plate alert hot-lists to aid users who want to help their communities catch dangerous criminals. That being said, V0LT has extremely strict policies on what license plates are posted to this list. 18 | 19 | Alerts may be published for the following situations: 20 | 21 | 1. A vehicle was recently stolen. 22 | 2. A violent criminal was last seen in a particular vehicle. 23 | 3. An AMBER alert published by law enforcement contained a license plate. 24 | 25 | Generally speaking, in order to protect privacy, alerts are not published for the following situations: 26 | 27 | 1. Vehicles involved in non-violent crimes. 28 | 2. Vehicles that witnessed a crime. 29 | 3. Vehicles that may have been involved in violent crimes, but not recently. 30 | 4. Missing person cases without sufficient evidence to suggest abduction or other violence. 31 | 32 | ## Privacy 33 | 34 | Traditional ALPR platforms are designed for mass data collection and analysis. Conversely, Predator is designed to be a completely stand-alone, offline platform. Predator is not designed to track vehicles, and doesn't submit any data to a central server outside of the user's control. Individual Predator users are responsible for protecting the privacy and integrity of the data they collect. Predator itself doesn't transmit any personal information, unless explicitly configured to by the user. 35 | 36 | ## Control 37 | 38 | Predator is designed in such a way that the user has full control over the entire system. Predator's configuration makes it easy to turn various features on and off to better fit each situation's use case and privacy concerns. 39 | -------------------------------------------------------------------------------- /FAQ.md: -------------------------------------------------------------------------------- 1 | # Frequently Asked Questions 2 | 3 | This document contains a series of questions you may have concerning Predator. 4 | 5 | 6 | **What exactly is Predator?** 7 | 8 | Predator is a camera utility designed primarily to be installed in a vehicle. It is focused on license plate reading and object recognition, although it works as a fully featured traditional dash-cam as well. 9 | 10 | 11 | **How do I install Predator?** 12 | 13 | For installation and setup instructions, check the [DOCUMENTATION.md](DOCUMENTATION.md) document. 14 | 15 | 16 | **Who is Predator designed for?** 17 | 18 | Predator is designed for hobbyists and tech enthusiasts who want to experiment with ALPR without the need for expensive software and specialized equipment. It's for people who want to tinker and experiment with computer vision technology in a powerful and easy way. 19 | 20 | 21 | **Can Predator be used as a plain dashcam, without ALPR?** 22 | 23 | Absolutely. Predator's dashcam mode allows it to operate like a fully featured traditional dashcam, without any ALPR functionality. If you want to retroactively run ALPR on recorded dashcam video, you can do so using pre-recorded mode at a later time. 24 | 25 | 26 | **What is Predator Apex?** 27 | 28 | Predator Apex is the paid support program behind the Predator software. Predator Apex uses the exact same, free and open source Predator software that everyone else has access to. Predator Apex customers get one-on-one tech support and/or pre-built hardware devices. This service is available both to commercial customers and individual users. To learn more, you can contact V0LT using the information found at 29 | 30 | 31 | **I have a problem with Predator and I need help.** 32 | 33 | If you're encountering an issue with Predator, first ensure that you've followed all of the steps described in the [DOCUMENTATION.md](DOCUMENTATION.md). If your issue persists, try to work out its source by checking individual potential points of failure. The majority of Predator issues can be traced back to one of its dependencies not working properly. Check that the ALPR engine, FFMPEG, and GPSD are all working properly. If you still can't resolve your issue, you can contact V0LT support using the information found at . 34 | 35 | 36 | **How is Predator related to OpenALPR?** 37 | 38 | Predator's intended ALPR engine, Phantom, is heavily based on OpenALPR. Predator can also be configured to use OpenALPR directly as its ALPR back-end. 39 | 40 | 41 | **What advantages does Phantom have as an ALPR back-end over OpenALPR?** 42 | 43 | Using Phantom as the ALPR back-end over OpenALPR is recommended for a few reasons. Phantom returns much more computer-friendly errors, meaning Predator can more reliably detect when the ALPR engine encounters problems, and can provide more actionable solutions. Additionally, Phantom's "video transparency" allows other processes to access images captured by the ALPR process. This allows Predator to run object recognition using the same camera used by the ALPR process. 44 | 45 | 46 | **Who is Predator not designed for?** 47 | 48 | Predator is not designed for those who want to conduct mass-surveillance. Its not for governments and law enforcement agencies looking to log the movements of civilians for criminal investigations. Its not for companies who want to track how often certain people visit their business. For sake of privacy, Predator actively avoids features designed for mass-surveillance, and focuses on targeted, private alerts for individual use. 49 | 50 | 51 | **How much does Predator cost?** 52 | 53 | The Predator software itself is completely free, and has absolutely no subscriptions. However, you may still need to purchase hardware for Predator to run on. You can either assemble hardware yourself, or purchase pre-made models through Predator Apex (see [APEX.md](APEX.md)) 54 | 55 | 56 | **What operating systems is Predator compatible with?** 57 | 58 | Officially, Predator is only compatible with Linux based operating systems, and is primarily designed for Debian-based distributions (though other distributions should work smoothly as well). However, if you're OK with a few hiccups, its also reasonably possible to install Predator on MacOS. Since Predator and all of its dependencies are all open source, it's technically possible to get it to work on Windows as well, but but you'll have to re-write a significant portion of it to get everything working as intended. In short, Predator on Linux works great, Predator on MacOS will probably work, and Predator in Windows would take extensive effort and modifications. 59 | 60 | 61 | **Why is Predator written in Python, and not something more efficient, like C++?** 62 | 63 | Python makes the development of the core Predator system much faster and more efficient. Additionally, Python makes it much easier to port Predator to new platforms. While Predator itself is indeed written in Python, many of its processing-heavy dependencies are written in C++. For example, Phantom, Predator's ALPR engine, is written in C++. This allows the system as a whole to benefit from the convenience of Python, without significantly harming processing performance of critical processes. 64 | 65 | 66 | **How does Predator work?** 67 | 68 | Predator is a fairly complex program, but below is a list of the general steps Predator takes when processing video, whether it be in real-time or a pre-recorded sample. 69 | 70 | 1. Depending on the mode, Predator will either load a pre-recorded video, or connect to a live camera to stream images. 71 | 2. Predator then splits the video, regardless of its source, into individual frames at a semi-regular interval. 72 | 3. Next, Predator might crop each frame down based on its configuration in order to remove unnecessary data. 73 | 4. Next, Predator's ALPR engine attempts to locate all potential plates. 74 | 5. Next, Predator's ALPR engine attempts interpret the plates detected, and makes a guess as to their contents. 75 | 6. After the ALPR engine finishes, Predator forms a list of the most likely plates guesses. 76 | 7. Predator then uses various methods of validation to filter out plate guesses that are unlikely to be correct. 77 | 8. Next, Predator removes plates it believes to be invalid, and creates a list of (ideally) correctly recognized plates. 78 | 9. Finally, Predator checks the list of license plates against any alert lists that may be loaded to check for hot-list hits. 79 | 9. After this, Predator might save the plates to a local log file, send a push notification to a Gotify server, play an audio alert, or any number of other customizable actions. 80 | 81 | 82 | **How many cameras can Predator use at one time?** 83 | 84 | This is a bit of a complex question, but in short, as many as you want. More specifically, Predator will use as many cameras as your OS, USB controller, and processor allow. In practice, this means that Predator can usually run at least two USB cameras at once, even on low powered devices like the Raspberry Pi. However, you should keep in mind that adding more cameras will increase the processing load significantly. As such, you should expect a decrease in frame-rate for each camera you add, unless you have plenty of processing head-room. 85 | 86 | 87 | **Can I run multiple instances of Predator at the same time on a single device?** 88 | 89 | Theoretically, yes. However, this could easily cause several issues. For example, 1 camera device can only be used by a single process at a time, so running multiple instance of Predator could cause one or both instances to fail. If you want to run two instances of Predator at the same time, then you're much more likely to have success if each instance is running in a different mode. For example, running one instance of real-time mode in the background with an instance of pre-recorded mode in the foreground will almost certainly work just fine. 90 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # Predator 2 | 3 | **Copyright 2025 V0LT - Conner Vieira** 4 | 5 | The ultimate customizable dashcam/license plate recognition platform. 6 | 7 | ![Predator header](./assets/images/branding/PredatorHeaderLight.svg) 8 | 9 | 10 | ## Disclaimer 11 | 12 | While Predator is designed to be stable and reliable, you should not depend on it for safety or security critical tasks. See the [SECURITY.md](SECURITY.md) document for more information. 13 | 14 | 15 | ## Clarification 16 | 17 | This is the repository for the original version of Predator (often referred to as "vanilla Predator" or "main-line Predator" for clarity). Predator is intended to be a self-contained dash-cam platform with ALPR capabilities. It operates primarily offline, and is not intended for CCTV analysis. To learn more about alternative Predator editions (i.e. versions more suited for stationary home/property security) and the rest of the Predator ecosystem, see . 18 | 19 | 20 | ## Support 21 | 22 | If you're interested in commissioning a custom ALPR product using technology from Predator, or need assistance with the installation process, don't hesitate to [contact V0LT](https://v0lttech.com/contact.php). 23 | 24 | 25 | ## Download 26 | 27 | Downloads for stable releases of Predator can be found [here](https://v0lttech.com/predator.php). 28 | 29 | 30 | ## Documentation 31 | 32 | Installation, set-up, and usage documentation can be found in the [DOCUMENTATION.md](DOCUMENTATION.md) file, which is included in the downloads linked above. 33 | 34 | 35 | ## Description 36 | 37 | Predator is a customizable camera platform focused primarily on license plate recognition in an automotive context. Predator is similar to a traditional dash-cam, but adds license plate recognition, object identification, motion detection, and many additional features. It can detect license plates and recognize objects in both pre-recorded video and real-time video streams, or simply capture video like a typical dash-cam. Predator strives to be the ultimate dash-cam platform for both technical hobbyists and customers with unique needs. 38 | 39 | 40 | ## Modes 41 | 42 | Predator is an extremely versatile platform, and supports several different modes to better support all situations and use cases. 43 | 44 | ### Management Mode (0) 45 | 46 | Management mode isn't a primary mode of Predator, but it allows the user to configure, maintain, and manage Predator from within the program. 47 | 48 | ### Pre-recorded Mode (1) 49 | 50 | Pre-recorded mode allows Predator to analyze pre-recorded video, and detect license plates and objects. 51 | 52 | ### Real-time Mode (2) 53 | 54 | Real-time mode allows Predator to detect license plates and objects in real-time video streams. 55 | 56 | ### Dash-cam Mode (3) 57 | 58 | Dash-cam mode allows Predator to record video like a traditional dash-cam, with customizable overlays and video processing. 59 | 60 | 61 | 62 | ## Use Cases 63 | 64 | Predator is an extremely customizable platform designed to fit a wide variety of use cases. Here are some examples that highlight what Predator is capable of. 65 | 66 | ### Custom Dashcam 67 | 68 | Predator allows technically minded users to build their own dash-cam to their specifications. Whether you want a clean and concealed install, or an advanced multi-channel setup, Predator gives you more flexibility than any other dash-cam platform. 69 | 70 | ### AMBER Alert Detection 71 | 72 | Predator's ALPR capabilities make it easy to set up alerts for license plates. When an AMBER alert is issued in your area, adding the associated license plate to your ALPR hot-list allows you to help rescue missing children without needing to distract your attention from driving. 73 | 74 | ### Corporate/Law Enforcement Vehicle Cameras 75 | 76 | Predator's advanced dash-cam capabilities enable features that are useful to enterprise users with unique needs. Predator's customizable video overlays allow you to review the speed and location of a vehicle, as well as custom relay triggers that can monitor actions like braking, horn use, warning lights, and sirens. 77 | 78 | ### Prerecorded Video Analysis 79 | 80 | In addition to real-time computer vision analysis, Predator is capable of analyzing prerecorded video from any source with license plate identification and object recognition. When using Predator's built-in dash-cam capabilities, prerecorded mode allows you to automatically analyze previously recorded dash-cam video with just a few button presses. 81 | 82 | ### Vehicle Security 83 | 84 | While dash-cam mode offers plenty of useful features while driving, Predator also comes with parking protection functionality, where the camera remains dormant until an event is detected. 85 | 86 | 87 | 88 | ## Features 89 | 90 | ### Completely Private 91 | 92 | Since Predator is open source, self hosted, offline, and self contained, you can rest assured that it's completely private, and that it doesn't share any of the information it collects. 93 | 94 | ### Offline Capable 95 | 96 | Predator works entirely offline, and never needs to connect to the internet to function. All internet based functionality is entirely optional and non-critical to normal operation. 97 | 98 | ### Open Source 99 | 100 | Predator, and all of its dependencies, are free and open source from top to bottom, meaning you can study how it works, make changes, and distribute it to others freely. 101 | 102 | ### Generic Hardware 103 | 104 | By design, Predator doesn't require specialized hardware to run. While higher resolution cameras will obviously yield better results, any video format supported by FFMPEG will work with Predator. 105 | 106 | ### Easy Setup 107 | 108 | While being technically minded will certainly help, Predator doesn't require professional installation or setup to function. As long as you're reasonably experienced with the Linux command line, setting up Predator should be a piece of cake. 109 | 110 | ### Highly Customizable 111 | 112 | Predator is extremely customizable, making it easy to fit into any use case. Whether you want an intelligence security camera, or a powerful dashcam device, Predator can be adjusted to fit your needs. 113 | 114 | ### Mobile Ready 115 | 116 | Predator is designed to support low-energy-usage hardware such that it can be easily installed in a vehicle. A single USB port is often enough to power an entire Predator system. 117 | 118 | ### Location Aware 119 | 120 | Predator supports GPX files to allow for correlating license plates detected in pre-recorded video to their physical coordinates. This makes information gathered from analyzing long streams of dashcam video much more useful and effective. 121 | 122 | ### Customizable Alerts 123 | 124 | Predator supports customizable real-time alerts, allowing the user to be notified when a license plate matching a hot-list is detected via audible sound alerts, visual cues, and push notifications! 125 | 126 | ### Distraction Free 127 | 128 | Predator is designed to be safe, regardless of the installation context. It's easy to configure Predator to completely hands (and eyes) free, ensuring that you don't have to look away from the road when Predator runs in a vehicle. 129 | 130 | ### Full Dashcam 131 | 132 | Predator comes with a fully featured dash-cam mode, allowing for customizable recording without processing license plates. Dash-cam videos can later be processed by Predator in pre-recorded mode if desired. Predator's dash-cam mode allows for multi-channel recording, provided the hardware supports it, making it easy to simultaneously record multiple angles using multiple cameras. 133 | 134 | ### Headless Ready 135 | 136 | While Predator comes with a straight-forward command-line interface, it can be fully configured to run in headless mode, without any user interaction necessary. This makes it perfect for vehicle installations, where the driver wants Predator to automatically start when the vehicle turns on. 137 | 138 | ### Object Recognition 139 | 140 | In addition to license plate scanning, Predator also supports general object recognition. It can collect a list of common objects and save them to a file, making it easy to find important events. 141 | 142 | ### Well Documented 143 | 144 | Predator's extreme customizability can be a bit overwhelming to new users. For this reason, Predator is extensively documented, and comes bundled with step-by-step guides on how to download, install, configure, and run it. 145 | 146 | ### Dependable Alerts 147 | 148 | While it shouldn't be fully relied upon for safety critical tasks, Predator is designed to be extremely dependable regardless. When configured appropriately, steps are taken to decrease the likelihood that Predator will false negative due mistakes in the analysis. 149 | 150 | ### Adaptive Frame-rate 151 | 152 | Predator automatically adapts to changing lighting conditions in both real-time and dashcam mode by allowing the frame-rate to change dynamically over time. Predator automatically detects frame-rate changes, and updates the playback speed of saved video files accordingly. 153 | 154 | ### Parking Mode 155 | 156 | Predator supports automatic parking dashcam functionality, where recording stops when the vehicle is parked, until motion is detected. This allows Predator to run continuously while only capturing important events on video. 157 | 158 | ### Buffered Recording 159 | 160 | In dash-cam mode, when parking mode is active, Predator will keep a video buffer of customizable length at all times. This means that Predator can go "back in time" when motion is detected, and save the last few seconds of video retroactively. 161 | 162 | ### Multi-Channel Recording 163 | 164 | Predator's dash-cam recording capabilities support an arbitrary number of video channels. As long as you have sufficient processing power and /O bandwidth, Predator allows you to connect as many cameras as you want. Even if you exceed your device's processing capabilities, Predator will continue recording at a lower frame-rate to ensure you don't miss any important events. 165 | 166 | ### GPIO Integration 167 | 168 | Predator supports GPIO features that allow you to customize your install with physical buttons and relays. Predator can be configured to save dash-cam video with a GPIO circuit is closed, or even display the state of a relay in a dash-cam overlay. 169 | 170 | 171 | ## Screenshots 172 | 173 | ### Real-time Mode Alert 174 | 175 | When operating in real-time mode, Predator can display alerts when it detects a plate found in a configured database. 176 | 177 | ![Alert hit sample image](./assets/images/screenshots/alerthit.png) 178 | 179 | ### Pre-recorded Mode Sample Image 180 | 181 | While operating in pre-recorded mode, Predator can analyze any video, including dashcam video. Note that the screenshot below is for demonstrative purposes, and shows what kinds of information Predator can detect. It does not accurately reflect the Predator user interface. 182 | 183 | ![Dashcam sample image](./assets/images/screenshots/dashcamsample.png) 184 | 185 | ### Pre-recorded Sample Analysis 186 | 187 | After scanning through an entire pre-recorded video based on user-configured preferences, Predator can display and export all of the plates it detected. 188 | 189 | ![Dashcam analysis output](./assets/images/screenshots/dashcamdetect.png) 190 | -------------------------------------------------------------------------------- /SECURITY.md: -------------------------------------------------------------------------------- 1 | # Security 2 | 3 | ## Disclaimer 4 | 5 | While Predator strives to be reliable and secure, you should not rely on it for safety critical tasks. Predator is designed to be an accessible hobbyist-level alternative to industrial license plate reading devices, and shouldn't be used to authenticate vehicles at entry points, or be the only line of defense in detecting criminal activity. 6 | 7 | 8 | ## Support 9 | 10 | Due to Predator's update cycle, only the latest version receives security patches. However, in severe cases, patches for older version might be released on a case by case basis. Regardless, for the safest experience, you should always use the latest version of Predator. 11 | 12 | 13 | ## Reporting 14 | 15 | In the event that you find a security issue with Predator, the safest way to report it is to contact V0LT directly. You can find relevant contact information, including PGP keys, at . 16 | 17 | 18 | ## Considerations 19 | 20 | Here are some security considerations you should account for before using Predator. 21 | 22 | 1. Assume anyone using a particular instance of Predator has full access to the system it's running on, regardless of the configuration. 23 | - Predator offers configuration values that allow an administrator to enable and disable individual modes. However, these configuration values should not be considered a security feature, since they can be easily bypassed by someone with access to the machine running Predator. 24 | - Predator also assumes information entered into prompts is safe, generally speaking. For example, Predator doesn't stop the user from entering ".." into file path prompts in order to access files outside of the project directory. 25 | - Predator's input sanitization is designed to prevent common mistakes and improve Predator's stability, not to prevent a malicious user from compromising the system Predator is running on. 26 | 2. License plate recognition is not an exact science; don't depend on it for safety or security critical tasks. 27 | - Predator has features that make it possible to turn license plate detections into triggers for automation tasks. For example, it's possible to link Predator to a home automation program, and automatically open a gate when particular vehicle approaches. However, you should use extreme caution when using Predator in situations like this. You should not depend on accurate license plate readings for any safety or security critical task. 28 | 3. Predator is capable of collecting sensitive data, like GPS recordings, license plates, and dash-cam video. Make sure you take efforts to physically protect the system Predator is running on. 29 | - While you may be using good security practices when it comes to the software you're using, don't neglect the physical security of Predator. Realize that it's entirely possible for someone to take the storage medium Predator is running on, and steal the information off it. 30 | - While every situation is different, it's generally good practice to keep Predator's core processor installed in a private and secure location with full disk encryption enabled to reduce the risk of physical theft of data. 31 | -------------------------------------------------------------------------------- /TODO.md: -------------------------------------------------------------------------------- 1 | # To-Do 2 | 3 | This document quickly explains features that may be added to Predator in the future. This is an informal document, and it's typically used as a simple task tracker, not as change-log or official feature list. 4 | 5 | 6 | ## Planned 7 | 8 | These are the features actively planned for Predator and are likely to be added within the near future. 9 | 10 | - [X] Refine debug message function. 11 | - [X] Add dashcam video saving. 12 | - [X] Move object recognition logging configuration to the `saving` section. 13 | - [X] Create documentation for interfacing with Predator. 14 | - [X] Add automatic dashcam video segment clearing. 15 | - [X] Add parking mode. 16 | - [X] Detect when the vehicle has been parked for a certain length of time. 17 | - [X] Detect motion to resume dashcam recording while parked. 18 | - [X] Add GPS time verification. 19 | - [X] Fix desynced video channels in dash-cam mode. 20 | - [X] Add GPS demo mode. 21 | - [X] Add configuration value to set maximum permitted framerate per capture device. 22 | - [X] Add frames to queue before writing to disk. 23 | - [X] Add the ability to disable dashcam capture devices from the configuration. 24 | - [X] Fix dashcam saving when the first segment is saved. 25 | - [X] Move status lighting configuration to general section. 26 | - [X] Add individual resolution configuration for dashcam capture devices. 27 | - [X] Add status light interfacing to dashcam mode. 28 | - [X] Test dashcam saving with audio recording. 29 | - [X] Test when merging is enabled. 30 | - [X] Test when merging is disabled. 31 | - [X] Test different output saving intervals. 32 | - [X] Test background dash-cam recording. 33 | - [X] Add remote motion detection alerts for dashcam mode via Reticulum. 34 | - [X] Test configuration updates. 35 | - [X] Test config file reconciliation behavior. 36 | - [X] Test config behavior from sub-directories. 37 | - [X] Add more dash-cam stamps. 38 | - [X] Add dash-cam operation mode stamp. 39 | - [X] Add custom relay status stamps through GPIO. 40 | - [X] Add default config support for values that involve adding entries. 41 | - [X] Improve the efficiency of the GPS stamp. 42 | - [X] Check to see if old dashcam video files actually exist before deleting them. 43 | - [X] Test GPS behavior. 44 | - [X] Finish video framerate snapping. 45 | - [X] Test the new ALPR system. 46 | - [X] Test pre-recorded mode. 47 | - [X] Test real-time mode. 48 | - [X] Test dash-cam mode. 49 | - [X] Implement pre-recorded dash-cam side-car mode. 50 | - [X] Implement file generation. 51 | - [X] Convert plate corners to bounding box. 52 | - [X] Create file viewer. 53 | - [X] Implement revised process exiting for real-time mode. 54 | - [X] Finish alert documentation (ALERTS.md) 55 | - [X] Fix frame counting for side-car mode. 56 | - [X] Document `developer>frame_count_method` configuration value in `docs/CONFIGURE.md. 57 | - [X] Validate multi-channel recording. 58 | - [X] Allow Predator to lock dashcam videos even after audio merging fails. 59 | - [X] Fix management mode for dashcam video (main.py:452 and 550) 60 | - [ ] Add a global variable to prevent multiple threads from trying to upload the telemtry back-log at the same time. 61 | 62 | 63 | ## Issues 64 | 65 | This section contains known issues that don't currently have a planned fix. 66 | 67 | - [ ] Videos and audio are often de-synced by a brief amount of time. 68 | - [ ] Predator can only record audio for a single camera in parked mode. 69 | 70 | 71 | ## Hypothetical 72 | 73 | Features in this section may be added in the future, but are not actively planned. 74 | 75 | - [ ] Add "pursuit" mode that automatically starts tracking telemetry when a specific relay is triggered. 76 | -------------------------------------------------------------------------------- /assets/3d/Ball Joint Base.stl: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/connervieira/Predator/45023faff51b46d7f09c1dccb2c33772ef23e52a/assets/3d/Ball Joint Base.stl -------------------------------------------------------------------------------- /assets/3d/ELP Camera Housing.stl: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/connervieira/Predator/45023faff51b46d7f09c1dccb2c33772ef23e52a/assets/3d/ELP Camera Housing.stl -------------------------------------------------------------------------------- /assets/documents/manuals/PredatorScorpionManual.odt: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/connervieira/Predator/45023faff51b46d7f09c1dccb2c33772ef23e52a/assets/documents/manuals/PredatorScorpionManual.odt -------------------------------------------------------------------------------- /assets/documents/presentation/PredatorGeneralPresentation.odp: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/connervieira/Predator/45023faff51b46d7f09c1dccb2c33772ef23e52a/assets/documents/presentation/PredatorGeneralPresentation.odp -------------------------------------------------------------------------------- /assets/images/branding/PredatorEquipped1.svg: -------------------------------------------------------------------------------- 1 | 2 | 18 | 20 | 23 | 27 | 31 | 32 | 38 | 44 | 50 | 56 | 59 | 63 | 67 | 68 | 78 | 87 | 93 | 99 | 100 | 124 | 126 | 127 | 129 | image/svg+xml 130 | 132 | 133 | 134 | 135 | 136 | 141 | 151 | 152 | 157 | 174 | 178 | PREDATOR 187 | 191 | L 202 | LICENSE PLATE RECOGNITION SYSTEM 211 | EQUIPPED WITH 220 | 221 | 222 | -------------------------------------------------------------------------------- /assets/images/branding/PredatorEquipped2.svg: -------------------------------------------------------------------------------- 1 | 2 | 18 | 20 | 23 | 27 | 31 | 32 | 38 | 44 | 50 | 56 | 59 | 63 | 67 | 68 | 78 | 87 | 93 | 99 | 100 | 125 | 127 | 128 | 130 | image/svg+xml 131 | 133 | 134 | 135 | 136 | 137 | 142 | 152 | 153 | 158 | 175 | 179 | PREDATOR 188 | 192 | L 203 | COMPUTER VISION SYSTEM 214 | EQUIPPED WITH 225 | 226 | 227 | -------------------------------------------------------------------------------- /assets/images/branding/PredatorEquipped3.svg: -------------------------------------------------------------------------------- 1 | 2 | 18 | 20 | 23 | 27 | 31 | 32 | 38 | 44 | 50 | 56 | 59 | 63 | 67 | 68 | 78 | 87 | 93 | 99 | 100 | 125 | 127 | 128 | 130 | image/svg+xml 131 | 133 | 134 | 135 | 136 | 137 | 142 | 152 | 153 | 158 | 175 | 179 | PREDATOR 188 | 192 | L 203 | CAMERA SYSTEM 214 | EQUIPPED WITH 225 | 226 | 227 | -------------------------------------------------------------------------------- /assets/images/branding/PredatorLogo.svg: -------------------------------------------------------------------------------- 1 | 2 | 18 | 20 | 23 | 27 | 31 | 32 | 42 | 43 | 66 | 68 | 69 | 71 | image/svg+xml 72 | 74 | 75 | 76 | 77 | 78 | 83 | 98 | 99 | 100 | -------------------------------------------------------------------------------- /assets/images/screenshots/alerthit.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/connervieira/Predator/45023faff51b46d7f09c1dccb2c33772ef23e52a/assets/images/screenshots/alerthit.png -------------------------------------------------------------------------------- /assets/images/screenshots/dashcamdetect.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/connervieira/Predator/45023faff51b46d7f09c1dccb2c33772ef23e52a/assets/images/screenshots/dashcamdetect.png -------------------------------------------------------------------------------- /assets/images/screenshots/dashcamsample.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/connervieira/Predator/45023faff51b46d7f09c1dccb2c33772ef23e52a/assets/images/screenshots/dashcamsample.png -------------------------------------------------------------------------------- /assets/images/screenshots/interfacecortex.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/connervieira/Predator/45023faff51b46d7f09c1dccb2c33772ef23e52a/assets/images/screenshots/interfacecortex.png -------------------------------------------------------------------------------- /assets/images/screenshots/interfaceoptic.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/connervieira/Predator/45023faff51b46d7f09c1dccb2c33772ef23e52a/assets/images/screenshots/interfaceoptic.png -------------------------------------------------------------------------------- /assets/images/screenshots/interfaceterminal.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/connervieira/Predator/45023faff51b46d7f09c1dccb2c33772ef23e52a/assets/images/screenshots/interfaceterminal.png -------------------------------------------------------------------------------- /assets/images/screenshots/main.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/connervieira/Predator/45023faff51b46d7f09c1dccb2c33772ef23e52a/assets/images/screenshots/main.png -------------------------------------------------------------------------------- /assets/models/dashcam_model.pt: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/connervieira/Predator/45023faff51b46d7f09c1dccb2c33772ef23e52a/assets/models/dashcam_model.pt -------------------------------------------------------------------------------- /assets/sounds/alerthit.mp3: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/connervieira/Predator/45023faff51b46d7f09c1dccb2c33772ef23e52a/assets/sounds/alerthit.mp3 -------------------------------------------------------------------------------- /assets/sounds/platedetected.mp3: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/connervieira/Predator/45023faff51b46d7f09c1dccb2c33772ef23e52a/assets/sounds/platedetected.mp3 -------------------------------------------------------------------------------- /assets/sounds/startup.mp3: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/connervieira/Predator/45023faff51b46d7f09c1dccb2c33772ef23e52a/assets/sounds/startup.mp3 -------------------------------------------------------------------------------- /assets/sounds/testnoise.mp3: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/connervieira/Predator/45023faff51b46d7f09c1dccb2c33772ef23e52a/assets/sounds/testnoise.mp3 -------------------------------------------------------------------------------- /assets/support/configdefault.json: -------------------------------------------------------------------------------- 1 | { 2 | "general": { 3 | "working_directory": "/home/pi/Software/Support/Predator/Working/", 4 | "interface_directory": "/dev/shm/PredatorInterface/", 5 | "alpr": { 6 | "engine": "phantom", 7 | "validation": { 8 | "guesses": 15, 9 | "confidence": 80, 10 | "best_effort": true, 11 | "license_plate_format": [ 12 | "AAA0000", "AAA000" 13 | ] 14 | } 15 | }, 16 | "alerts": { 17 | "alerts_ignore_validation": true, 18 | "allow_duplicate_alerts": false, 19 | "databases": [ 20 | "https://v0lttech.com/predator/manifest/serve.php?type=hot&user=cvieira&list=emergencyhotlist" 21 | ] 22 | }, 23 | "display": { 24 | "ascii_art_header": true, 25 | "startup_message": "", 26 | "debugging_output": false 27 | }, 28 | "gps": { 29 | "enabled": false, 30 | "demo_file": "", 31 | "time_correction": { 32 | "enabled": true, 33 | "threshold": 10 34 | }, 35 | "lazy_polling_interval": 1.0 36 | }, 37 | "modes": { 38 | "auto_start": "", 39 | "enabled": { 40 | "management": true, 41 | "prerecorded": true, 42 | "realtime": true, 43 | "dashcam": true 44 | } 45 | }, 46 | "status_lighting": { 47 | "enabled": false, 48 | "base_url": "http://predatorwled.local/win&A=255", 49 | "delay_after_boot": 0, 50 | "values": { 51 | "normal": "[U]&R=0&G=255&B=0", 52 | "alpr_alert": "[U]&R=255&G=0&B=0", 53 | "alpr_detection": "[U]&R=255&G=128&B=0", 54 | "dashcam_save": "[U]&R=0&G=0&B=255" 55 | } 56 | }, 57 | "audio": { 58 | "enabled": false, 59 | "player": { 60 | "backend": "mplayer", 61 | "mpg321": { 62 | }, 63 | "mplayer": { 64 | "device": "alsa:device=hw=0.0" 65 | } 66 | }, 67 | "sounds": { 68 | "startup": { 69 | "path": "./assets/sounds/startup.mp3", 70 | "repeat": 0, 71 | "delay": 0.3 72 | }, 73 | "alpr_notification": { 74 | "path": "./assets/sounds/platedetected.mp3", 75 | "repeat": 1, 76 | "delay": 0.3 77 | }, 78 | "alpr_alert": { 79 | "path": "./assets/sounds/alerthit.mp3", 80 | "repeat": 1, 81 | "delay": 2.5 82 | }, 83 | "gps_connected": { 84 | "path": "./assets/sounds/voice/gps_connected.mp3", 85 | "repeat": 1, 86 | "delay": 1 87 | }, 88 | "gps_disconnected": { 89 | "path": "./assets/sounds/voice/gps_disconnected.mp3", 90 | "repeat": 1, 91 | "delay": 1 92 | }, 93 | "gps_fault": { 94 | "path": "./assets/sounds/voice/gps_fault.mp3", 95 | "repeat": 1, 96 | "delay": 1 97 | }, 98 | "dashcam_saved": { 99 | "path": "./assets/sounds/voice/video_saved.mp3", 100 | "repeat": 1, 101 | "delay": 1 102 | }, 103 | "message_notice": { 104 | "path": "./assets/sounds/voice/notice.mp3", 105 | "repeat": 0, 106 | "delay": 1 107 | }, 108 | "message_warning": { 109 | "path": "./assets/sounds/voice/warning.mp3", 110 | "repeat": 1, 111 | "delay": 1 112 | }, 113 | "message_error": { 114 | "path": "./assets/sounds/voice/error.mp3", 115 | "repeat": 1, 116 | "delay": 1 117 | }, 118 | "recording_started": { 119 | "path": "./assets/sounds/voice/recording_started.mp3", 120 | "repeat": 1, 121 | "delay": 1 122 | }, 123 | "recording_stopped": { 124 | "path": "./assets/sounds/voice/recording_stopped.mp3", 125 | "repeat": 1, 126 | "delay": 1 127 | } 128 | } 129 | } 130 | }, 131 | "management": { 132 | "disk_statistics": true 133 | }, 134 | "prerecorded": { 135 | "image": { 136 | "processing": { 137 | "cropping": { 138 | "enabled": false, 139 | "left_margin": 500, 140 | "right_margin": 500, 141 | "top_margin": 500, 142 | "bottom_margin": 300 143 | } 144 | } 145 | }, 146 | "max_gpx_time_difference": 5 147 | }, 148 | "realtime": { 149 | "interface": { 150 | "display": { 151 | "show_validation": true, 152 | "shape_alerts": false, 153 | "output_level": 2, 154 | "speed": { 155 | "enabled": true, 156 | "unit": "mph" 157 | } 158 | }, 159 | "behavior": { 160 | "delays": { 161 | "alert": 5, 162 | "normal": 1 163 | }, 164 | "clearing": true 165 | } 166 | }, 167 | "gps": { 168 | "alpr_location_tagging": true 169 | }, 170 | "image": { 171 | "camera": { 172 | "devices": { 173 | "primary": "/dev/video0" 174 | } 175 | } 176 | }, 177 | "saving": { 178 | "remote_alert_sources": { 179 | "enabled": true, 180 | "directory": "remote_sources_cache" 181 | }, 182 | "license_plates": { 183 | "enabled": true, 184 | "file": "plate_history.json", 185 | "save_guesses": false 186 | } 187 | }, 188 | "push_notifications": { 189 | "enabled": false, 190 | "server": "http://10.3.141.1:1522", 191 | "token": "" 192 | } 193 | }, 194 | "dashcam": { 195 | "saving": { 196 | "directory": "saved_dashcam", 197 | "file": { 198 | "codec": "XVID", 199 | "extension": "avi" 200 | }, 201 | "trigger": "dashcam_lock_trigger", 202 | "segment_length": 60, 203 | "looped_recording": { 204 | "mode": "manual", 205 | "manual": { 206 | "history_length": 1000 207 | }, 208 | "automatic": { 209 | "minimum_free_percentage": 0.1, 210 | "max_deletions_per_round": 10 211 | } 212 | }, 213 | "framerate_snap": 0.15 214 | }, 215 | "capture": { 216 | "video": { 217 | "devices": { 218 | "main": { 219 | "index": 0, 220 | "enabled": true, 221 | "flip": false, 222 | "codec": "MJPG", 223 | "framerate": { 224 | "max": 30, 225 | "min": 10 226 | }, 227 | "resolution": { 228 | "width": 1920, 229 | "height": 1080 230 | } 231 | } 232 | } 233 | }, 234 | "audio": { 235 | "enabled": false, 236 | "extension": "wav", 237 | "device": "", 238 | "format": "S16_LE", 239 | "rate": 44100, 240 | "merge": true, 241 | "record_as_user": "pi", 242 | "start_delay": 0.2, 243 | "display_output": false 244 | } 245 | }, 246 | "parked": { 247 | "enabled": false, 248 | "conditions": { 249 | "speed": 1, 250 | "time": 10 251 | }, 252 | "event": { 253 | "trigger": "motion", 254 | "timeout": 15, 255 | "buffer": 30, 256 | "label": { 257 | "enabled": false, 258 | "color": [ 259 | 0, 260 | 255, 261 | 0 262 | ] 263 | }, 264 | "trigger_motion": { 265 | "sensitivity": 0.2 266 | }, 267 | "trigger_object_recognition": { 268 | "model_weights": "./assets/models/dashcam_model.pt", 269 | "objects": ["PERSON_PEDESTRIAN"], 270 | "minimum_confidence": 0.6 271 | } 272 | } 273 | }, 274 | "notifications": { 275 | "reticulum": { 276 | "enabled": false, 277 | "destinations": [ 278 | ], 279 | "identity_file": "/home/pi/.reticulum/storage/identities/predator", 280 | "instance_name": "My Car", 281 | "events": { 282 | "start_up": { 283 | "enabled": true 284 | }, 285 | "parked_event": { 286 | "enabled": true 287 | }, 288 | "parking_mode_enabled": { 289 | "enabled": true 290 | }, 291 | "parking_mode_disabled": { 292 | "enabled": true 293 | } 294 | } 295 | } 296 | }, 297 | "stamps": { 298 | "size": 0.8, 299 | "main": { 300 | "color": [ 301 | 0, 302 | 0, 303 | 0 304 | ], 305 | "unix_time": { 306 | "enabled": false 307 | }, 308 | "date": { 309 | "enabled": true 310 | }, 311 | "time": { 312 | "enabled": true 313 | }, 314 | "message_1": "AAA0000", 315 | "message_2": "V0LT Predator" 316 | }, 317 | "diagnostic": { 318 | "color": [ 319 | 255, 320 | 255, 321 | 255 322 | ], 323 | "framerate": { 324 | "enabled": true, 325 | "mode": "hybrid", 326 | "precision": 1 327 | }, 328 | "state": { 329 | "enabled": true 330 | } 331 | }, 332 | "relay": { 333 | "enabled": false, 334 | "colors": { 335 | "on": [ 336 | 255, 337 | 128, 338 | 128 339 | ], 340 | "off": [ 341 | 0, 342 | 0, 343 | 0 344 | ] 345 | }, 346 | "triggers": { 347 | "horn": { 348 | "enabled": false, 349 | "pin": 0, 350 | "invert": false, 351 | "text": "HORN" 352 | } 353 | } 354 | }, 355 | "gps": { 356 | "color": [ 357 | 0, 358 | 0, 359 | 0 360 | ], 361 | "location": { 362 | "enabled": true 363 | }, 364 | "altitude": { 365 | "enabled": true 366 | }, 367 | "speed": { 368 | "enabled": true, 369 | "unit": "mph" 370 | } 371 | } 372 | }, 373 | "alpr": { 374 | "enabled": false, 375 | "interval": 2, 376 | "devices": [] 377 | }, 378 | "physical_controls": { 379 | "dashcam_saving": { 380 | }, 381 | "stop_predator": { 382 | } 383 | }, 384 | "telemetry": { 385 | "enabled": false, 386 | "target": "https://v0lttech.com/portal/ingest.php", 387 | "vehicle_identifier": "", 388 | "save_failed_updates": true, 389 | "send_images": false 390 | } 391 | }, 392 | "developer": { 393 | "ignore_list": { 394 | "enabled": false, 395 | "local_file": "", 396 | "remote_sources": [] 397 | }, 398 | "offline": false, 399 | "kill_plate": "", 400 | "print_timings": false, 401 | "dashcam_saving_queue_overflow": 2000, 402 | "dashcam_shortterm_framerate_interval": 0.25, 403 | "hard_crash_on_error": false, 404 | "identify_to_remote_sources": true, 405 | "frame_count_method": "manual" 406 | } 407 | } 408 | -------------------------------------------------------------------------------- /assets/support/configoutline.json: -------------------------------------------------------------------------------- 1 | { 2 | "general": { 3 | "working_directory": "dir", 4 | "interface_directory": "str", 5 | "alpr": { 6 | "engine": ["phantom", "openalpr"], 7 | "validation": { 8 | "guesses": "+int", 9 | "confidence": "+float", 10 | "best_effort": "bool", 11 | "license_plate_format": "list" 12 | } 13 | }, 14 | "alerts": { 15 | "alerts_ignore_validation": "bool", 16 | "allow_duplicate_alerts": "bool", 17 | "databases": "list" 18 | }, 19 | "display": { 20 | "ascii_art_header": "bool", 21 | "startup_message": "str", 22 | "debugging_output": "bool" 23 | }, 24 | "gps": { 25 | "enabled": "bool", 26 | "demo_file": "str", 27 | "time_correction": { 28 | "enabled": "bool", 29 | "threshold": "+float" 30 | }, 31 | "lazy_polling_interval": "+float" 32 | }, 33 | "modes": { 34 | "auto_start": "str", 35 | "enabled": { 36 | "management": "bool", 37 | "prerecorded": "bool", 38 | "realtime": "bool", 39 | "dashcam": "bool" 40 | } 41 | }, 42 | "status_lighting": { 43 | "enabled": "bool", 44 | "base_url": "str", 45 | "delay_after_boot": "+float", 46 | "values": { 47 | "normal": "str", 48 | "alpr_alert": "str", 49 | "alpr_detection": "str", 50 | "dashcam_save": "str" 51 | } 52 | }, 53 | "audio": { 54 | "enabled": "bool", 55 | "player": { 56 | "backend": ["mpg321", "mplayer"], 57 | "mpg321": { 58 | }, 59 | "mplayer": { 60 | "device": "str" 61 | } 62 | }, 63 | "sounds": { 64 | "startup": { 65 | "path": "file", 66 | "repeat": "+int", 67 | "delay": "+float" 68 | }, 69 | "alpr_notification": { 70 | "path": "file", 71 | "repeat": "+int", 72 | "delay": "+float" 73 | }, 74 | "alpr_alert": { 75 | "path": "file", 76 | "repeat": "+int", 77 | "delay": "+float" 78 | }, 79 | "gps_connected": { 80 | "path": "file", 81 | "repeat": "+int", 82 | "delay": "+float" 83 | }, 84 | "gps_disconnected": { 85 | "path": "file", 86 | "repeat": "+int", 87 | "delay": "+float" 88 | }, 89 | "gps_fault": { 90 | "path": "file", 91 | "repeat": "+int", 92 | "delay": "+float" 93 | }, 94 | "dashcam_saved": { 95 | "path": "file", 96 | "repeat": "+int", 97 | "delay": "+float" 98 | }, 99 | "message_notice": { 100 | "path": "file", 101 | "repeat": "+int", 102 | "delay": "+float" 103 | }, 104 | "message_warning": { 105 | "path": "file", 106 | "repeat": "+int", 107 | "delay": "+float" 108 | }, 109 | "message_error": { 110 | "path": "file", 111 | "repeat": "+int", 112 | "delay": "+float" 113 | }, 114 | "recording_started": { 115 | "path": "file", 116 | "repeat": "+int", 117 | "delay": "+float" 118 | }, 119 | "recording_stopped": { 120 | "path": "file", 121 | "repeat": "+int", 122 | "delay": "+float" 123 | } 124 | } 125 | } 126 | }, 127 | "management": { 128 | "disk_statistics": "bool" 129 | }, 130 | "prerecorded": { 131 | "image": { 132 | "processing": { 133 | "cropping": { 134 | "enabled": "bool", 135 | "left_margin": "+int", 136 | "right_margin": "+int", 137 | "top_margin": "+int", 138 | "bottom_margin": "+int" 139 | } 140 | } 141 | }, 142 | "max_gpx_time_difference": "+float" 143 | }, 144 | "realtime": { 145 | "interface": { 146 | "display": { 147 | "show_validation": "bool", 148 | "shape_alerts": "bool", 149 | "output_level": [0, 1, 2, 3], 150 | "speed": { 151 | "enabled": "bool", 152 | "unit": ["mph", "kph", "mps", "fps", "knot"] 153 | } 154 | }, 155 | "behavior": { 156 | "delays": { 157 | "alert": "+int", 158 | "normal": "+int" 159 | }, 160 | "clearing": "bool" 161 | } 162 | }, 163 | "gps": { 164 | "alpr_location_tagging": "bool" 165 | }, 166 | "image": { 167 | "camera": { 168 | "devices": "dict" 169 | } 170 | }, 171 | "saving": { 172 | "remote_alert_sources": { 173 | "enabled": "bool", 174 | "directory": "str" 175 | }, 176 | "license_plates": { 177 | "enabled": "bool", 178 | "file": "str", 179 | "save_guesses": "bool" 180 | } 181 | }, 182 | "push_notifications": { 183 | "enabled": "bool", 184 | "server": "str", 185 | "token": "str" 186 | } 187 | }, 188 | "dashcam": { 189 | "saving": { 190 | "directory": "str", 191 | "file": { 192 | "codec": "str", 193 | "extension": "str" 194 | }, 195 | "trigger": "str", 196 | "segment_length": "+float", 197 | "looped_recording": { 198 | "mode": ["automatic", "manual"], 199 | "manual": { 200 | "history_length": "+int" 201 | }, 202 | "automatic": { 203 | "minimum_free_percentage": "+float", 204 | "max_deletions_per_round": "+int" 205 | } 206 | }, 207 | "framerate_snap": "+float" 208 | }, 209 | "capture": { 210 | "video": { 211 | "devices": "dict" 212 | }, 213 | "audio": { 214 | "enabled": "bool", 215 | "extension": "str", 216 | "device": "str", 217 | "format": ["S8", "U8", "S16_LE", "S16_BE", "U16_LE", "U16_BE", "S24_LE", "S24_BE", "U24_LE", "U24_BE", "S32_LE", "S32_BE", "U32_LE", "U32_BE", "FLOAT_LE", "FLOAT_BE", "FLOAT64_LE", "FLOAT64_BE", "IEC958_SUBFRAME_LE", "IEC958_SUBFRAME_BE", "MU_LAW", "A_LAW", "IMA_ADPCM", "MPEG", "GSM", "S20_LE", "S20_BE", "U20_LE", "U20_BE", "SPECIAL", "S24_3LE", "S24_3BE", "U24_3LE", "U24_3BE", "S20_3LE", "S20_3BE", "U20_3LE", "U20_3BE", "S18_3LE", "S18_3BE", "U18_3LE", "U18_3BE", "G723_24", "G723_24_1B", "G723_40", "G723_40_1B", "DSD_U8", "DSD_U16_LE", "DSD_U32_LE", "DSD_U16_BE", "DSD_U32_BE"], 218 | "rate": "+int", 219 | "merge": "bool", 220 | "record_as_user": "str", 221 | "start_delay": "+float", 222 | "display_output": "bool" 223 | } 224 | }, 225 | "parked": { 226 | "enabled": "bool", 227 | "conditions": { 228 | "speed": "+float", 229 | "time": "+float" 230 | }, 231 | "event": { 232 | "trigger": ["motion", "object_recognition"], 233 | "timeout": "+float", 234 | "buffer": "+int", 235 | "label": { 236 | "enabled": "bool", 237 | "color": "list" 238 | }, 239 | "trigger_motion": { 240 | "sensitivity": "+float" 241 | }, 242 | "trigger_object_recognition": { 243 | "model_weights": "file", 244 | "objects": "list", 245 | "minimum_confidence": "+float" 246 | } 247 | } 248 | }, 249 | "notifications": { 250 | "reticulum": { 251 | "enabled": "bool", 252 | "destinations": "list", 253 | "identity_file": "str", 254 | "instance_name": "str", 255 | "events": { 256 | "start_up": { 257 | "enabled": "bool" 258 | }, 259 | "parked_event": { 260 | "enabled": "bool" 261 | }, 262 | "parking_mode_enabled": { 263 | "enabled": "bool" 264 | }, 265 | "parking_mode_disabled": { 266 | "enabled": "bool" 267 | } 268 | } 269 | } 270 | }, 271 | "stamps": { 272 | "size": "+float", 273 | "main": { 274 | "color": "list", 275 | "unix_time": { 276 | "enabled": "bool" 277 | }, 278 | "date": { 279 | "enabled": "bool" 280 | }, 281 | "time": { 282 | "enabled": "bool" 283 | }, 284 | "message_1": "str", 285 | "message_2": "str" 286 | }, 287 | "diagnostic": { 288 | "color": "list", 289 | "framerate": { 290 | "enabled": "bool", 291 | "mode": ["instant", "average", "hybrid"], 292 | "precision": "+int" 293 | }, 294 | "state": { 295 | "enabled": "bool" 296 | } 297 | }, 298 | "relay": { 299 | "enabled": "bool", 300 | "colors": { 301 | "on": "list", 302 | "off": "list" 303 | }, 304 | "triggers": "dict" 305 | }, 306 | "gps": { 307 | "color": "list", 308 | "location": { 309 | "enabled": "bool" 310 | }, 311 | "altitude": { 312 | "enabled": "bool" 313 | }, 314 | "speed": { 315 | "enabled": "bool", 316 | "unit": ["mph", "kph", "mps", "fps", "knot"] 317 | } 318 | } 319 | }, 320 | "alpr": { 321 | "enabled": "bool", 322 | "interval": "+float", 323 | "devices": "list" 324 | }, 325 | "physical_controls": { 326 | "dashcam_saving": "dict", 327 | "stop_predator": "dict" 328 | }, 329 | "telemetry": { 330 | "enabled": "bool", 331 | "target": "str", 332 | "vehicle_identifier": "str", 333 | "save_failed_updates": "bool", 334 | "send_images": "bool" 335 | } 336 | }, 337 | "developer": { 338 | "ignore_list": { 339 | "enabled": "bool", 340 | "local_file": "str", 341 | "remote_sources": "list" 342 | }, 343 | "offline": "bool", 344 | "kill_plate": "str", 345 | "print_timings": "bool", 346 | "dashcam_saving_queue_overflow": "+int", 347 | "dashcam_shortterm_framerate_interval": "+float", 348 | "hard_crash_on_error": "bool", 349 | "identify_to_remote_sources": "bool", 350 | "frame_count_method": ["opencv", "ffprobe", "manual"] 351 | } 352 | } 353 | -------------------------------------------------------------------------------- /crop_image: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env python3 2 | 3 | # This script serves the purpose of cropping images to increase analysis efficiency. It uses the following command line syntax: 4 | # ./crop_image image_file_name left right top bottom 5 | 6 | import subprocess 7 | import sys 8 | 9 | 10 | # Grab the command arguments: image_file_name left_margin right_margin top_margin bottom_margin 11 | img = sys.argv[1]; left = sys.argv[2]; right = sys.argv[3]; top = sys.argv[4]; bottom = sys.argv[5] 12 | 13 | # Set up the output file's name and path. 14 | img_base = img[:img.rfind(".")]; extension = img[img.rfind("."):]; path = img[:img.rfind("/")] 15 | img_out = img_base+""+extension 16 | 17 | # Get the current image's size. 18 | data = subprocess.check_output(["identify", img]).decode("utf-8").strip().replace(img, "") 19 | size = [int(n) for n in data.replace(img, "").split()[1].split("x")] 20 | 21 | # Set up the command to crop the image using ImageMagick. 22 | w = str(size[0]-int(left)-int(right)); h = str(size[1]-int(top)-int(bottom)); x = left; y = top 23 | cmd = ["convert", img, "-crop", w+"x"+h+"+"+x+"+"+y, "+repage", img_out] 24 | 25 | # Execute the crop command. 26 | subprocess.Popen(cmd) 27 | -------------------------------------------------------------------------------- /docs/ALERTS.md: -------------------------------------------------------------------------------- 1 | # Alerts 2 | 3 | This document explains the format used by Predator alert databases. 4 | 5 | ## Overview 6 | 7 | Predator is capable of loading alert lists from both local files and remote sources. Alert lists come in the form of a JSON dictionary. 8 | 9 | Here is a break-down of each value in an alert database: 10 | 11 | - `rule`: Each top-level key in the alert database should be a license plate rule that will trigger the alert. 12 | - `name`: This is an optional value that contains a human-friendly name for this alert. 13 | - `description`: This is an optional value that contains a brief human-friendly description of this alert. 14 | - `author`: This is an optional value that contains the author name associated with this alert. 15 | - `source`: This is an optional value that describes where the information for this alert was sourced from. 16 | - `vehicle`: This is an optional value that contains information about the vehicle this alert is associated with. 17 | - `color`: This is an optional sub-value for the vehicle color (all lowercase, human-friendly name). 18 | - `make`: This is an optional sub-value for the vehicle manufacturer. 19 | - `model`: This is an optional sub-value for the vehicle model. 20 | - `year`: This is an optional sub-value for the vehicle year. This value should be an integer (0 for none). 21 | - `vin`: This is an optional value to specify the vehicle's identification number (all uppercase). 22 | 23 | 24 | ## Examples 25 | 26 | ### Bare Minimum 27 | 28 | This is an example of an alert database with the bare minimum information. This database will trigger alerts for the following plates: AAA1111, BBB2222, CCC3333. 29 | 30 | ```JSON 31 | { 32 | "AAA1111": {}, 33 | "BBB2222": {}, 34 | "CCC3333": {} 35 | } 36 | ``` 37 | 38 | ### Full Information 39 | 40 | This is an example of an alert database with all supported information included. This database will trigger alerts for the following plates: AAA1111, XYZ1234, ABC1234 41 | 42 | ```JSON 43 | { 44 | "AAA1111": { 45 | "name": "Test Alert", 46 | "description": "A testing alert to verify Predator's alert functionality", 47 | "author": "V0LT", 48 | "source": "V0LT", 49 | "vehicle": { 50 | "color": "red", 51 | "make": "Toyota", 52 | "model": "Corolla", 53 | "year": 2001, 54 | "vin": "1GKEK13T251163542" 55 | } 56 | }, 57 | "XYZ1234": { 58 | "name": "AMBER Alert", 59 | "description": "A testing alert that takes the form of an AMBER alert", 60 | "author": "V0LT", 61 | "source": "Ohio State Highway Patrol", 62 | "vehicle": { 63 | "color": "black", 64 | "make": "Subaru", 65 | "model": "Legacy", 66 | "year": 2015, 67 | "vin": "4S3BNAF64F3014194" 68 | } 69 | }, 70 | "ABC1234": { 71 | "name": "Test Alert", 72 | "description": "A testing alert to verify Predator's alert functionality", 73 | "author": "V0LT", 74 | "source": "V0LT", 75 | "vehicle": { 76 | "color": "blue", 77 | "make": "Honda", 78 | "model": "Civic", 79 | "year": 2009, 80 | "vin": "19XFA165X9E038243" 81 | } 82 | } 83 | } 84 | ``` 85 | -------------------------------------------------------------------------------- /docs/AUTOINSTALL.md: -------------------------------------------------------------------------------- 1 | # Auto Install 2 | 3 | Here you can learn about the auto-install script for the Predator ecosystem, and the requirements for using it. 4 | 5 | 6 | ## Disclaimer 7 | 8 | **This script should only be run on systems dedicated to the usage of Predator.** This script erases and overwrites files without prompting the user. Do not use this script on a system with files you don't want to lose. Ideally, this script should only be used on a clean OS install. 9 | 10 | This script **is not** designed to be run on your normal, every-day computer. Rather, it is designed to quickly deploy Predator on a dedicated system. In other words, if you want to install Predator on your main computer for sake of testing, experimentation, or development, you should not use this script. If you instead want to install the Predator ecosystem on a computer dedicated to ALPR, dash-cam recording, or other Predator functionality, this script can be an extremely conveinent way to do so. 11 | 12 | Predator, Cortex, and Optic assume that your username is "pi" by default. If the primary user on your system has a different username, you'll have to manually reconfigure each component after the installation script finishes. 13 | 14 | 15 | ## Support 16 | 17 | This script should work on practically any Debian/Ubuntu based operating system, including Raspberry Pi OS, Pop!\_OS, and Mint. 18 | 19 | It has been successfully tested on the following platforms: 20 | - Raspberry Pi 5 (4GB) - Raspberry Pi OS Lite (64bit) 21 | - LibreComputer AML-S905X-CC-V2 - Raspbian 12 (64bit) 22 | 23 | 24 | ## Components 25 | 26 | The auto-install script will install the following components: 27 | - Predator () 28 | - Phantom ALPR () 29 | - Optic () 30 | - Cortex () 31 | 32 | ## Usage 33 | 34 | To use the auto-install script, simply authenticate as root, then execute the latest version of the script. This can be done with the following commands: 35 | 36 | 1. `sudo su` 37 | 2. `curl https://v0lttech.com/predator/autodeploy/autoinstall.sh | bash` 38 | -------------------------------------------------------------------------------- /docs/DEBUGGING.md: -------------------------------------------------------------------------------- 1 | # Debugging 2 | 3 | This document explains the debug message system built into Predator, and how you can use it effectively to solve problems and improve efficiency. 4 | 5 | 1. Enable debugging messages. 6 | - To enable debugging messages, enable the `general>display>debugging_output` configuration value. 7 | 2. Run Predator. 8 | - After enabling debug messages, run Predator. You should see frequent grayed-out messages being printed to the console indicating important events behind the scenes. 9 | 3. Interpret messages. 10 | - Debugging messages may look complicated, but they follow a consistent structure: `[current_time] ([time_since_last_message] - [thread_name]) - [message]` 11 | - [current_time] is a Unix timestamp of the exact time the message was printed. 12 | - [time_since_last_message] is the length of time in seconds since the last message in the thread was printed. 13 | - [thread] is the name of the thread that the message was printed from. 14 | - [message] is the message itself. 15 | - Example: `1697686012.6295320988 (0.0000085831 - Main) - Processing ALPR alerts` 16 | 4. Locate source of delay. 17 | - One of the most useful implications of debugging messages is the ability to locate sources of delay during the operation of Predator. 18 | - To locate sources of slow-downs, you can use the `[time_since_last_message]` field described above. 19 | - This field will show how long the previous action in the thread took. 20 | - To clarify, processes in different threads work independently, and often run concurrently. 21 | - To locate sources of slow downs in a particular thread, pay attention to a specific thread's debug messages, and ignore other threads. 22 | - For example, to find slow downs in the "Main" thread, pay attention to messages marked as "Main", and ignore "ALPRStream" and "ALPRStreamMaintainer". 23 | - Example: 24 | 1. Here's an example debug message sequence: 25 | - `1697686011.2565226555 (0.1619987488 - Main) - Loading networking libraries` 26 | - `1697686011.2566270828 (0.0000023842 - Main) - Loading ignore lists` 27 | - `1697686014.2589059357 (3.0022788525 - Main) - Initial loading complete` 28 | 2. You'll notice that the time since the last message shown in the last line is just over 3 seconds. 29 | - This means that the previous action in the thread (in this case, loading the ignore lists), took 3 seconds. 30 | 3. Resolve unnecessary delays. 31 | - After finding sources of delay, you may want to research their causes and attempt to resolve them. 32 | - In this example, the loading process for the ignore lists might be taking longer than expected because Predator doesn't have a reliable internet connection to download ignore lists from remote sources. 33 | -------------------------------------------------------------------------------- /docs/HARDWARE.md: -------------------------------------------------------------------------------- 1 | # Hardware 2 | 3 | Predator is primarily a software utility. However, it goes without saying that Predator is only useful if it has hardware capable of effectively collecting and processing video and image information. This document describes hardware information related to Predator, including parts lists. 4 | 5 | To learn more about paid hardware/software support for Predator, see [../APEX.md](../APEX.md). 6 | 7 | 8 | ## Recommendations 9 | 10 | This section contains recommendations for those looking to build a hardware device that runs Predator. 11 | 12 | - The processing device should be fully capable of running Linux. 13 | - Having a device that can easily run Linux will make installing and setting up Predator drastically easier, as you'll spend less time troubleshooting compatibility issues, and more time actually assembling and configuring your device. 14 | - More specifically, something running a Debian-based distribution will work best. This includes operating systems like Ubuntu, Raspberry Pi OS, Pop!_OS, and several others. 15 | - Camera color accuracy is practically irrelevant when it comes to Predator's ability to recognize license plates. 16 | - Even gray-scale cameras should have no problems recognizing license plates. 17 | - Consider the context of the situation you want to use Predator in, and consider how to align your camera and it's lens. 18 | - Consider motion blur when setting up your camera. 19 | - Ensure your camera is mounted somewhere stable enough that it can take clear images of the vehicles you're scanning, even while they are in motion. 20 | 21 | 22 | ## Guidelines 23 | 24 | As a general rule, these are the parts you'll need in a Predator device: 25 | 26 | - Camera 27 | - Required (for real-time and dash-cam mode) 28 | - Without a camera, Predator will have no way of using real-time mode or dash-cam mode, and only pre-recorded mode will be usable. 29 | - Processing 30 | - Required 31 | - Needless to say, Predator can't be run without a processing device. 32 | - This device could be anything from a high end workstation to a single-board-computer, like a Raspberry Pi. 33 | - Networking 34 | - Recommended 35 | - Predator is fully functional without being connected to a network. However, having access to a local area network can allow Predator to be remotely controlled through a web interface like [Optic](https://v0lttech.com/optic.php) or [Cortex](https://v0lttech.com/cortex.php). 36 | - This is the way most installs are controlled, due to limited space to install displays in most cars. A remote control web interface allows nearly any smartphone, tablet, or laptop to become Predator's display. 37 | - Access to the internet will allow Predator to download remote alert databases and use remote push notifications. 38 | - Mounting Hardware 39 | - Recommended 40 | - Without mounting hardware, your Predator device will be significantly harder to mount in your build. 41 | - Input Device 42 | - Optional 43 | - Without an input device, the only way to configure Predator will be remotely. 44 | - If your installation scenario makes an input device inconvenient or difficult to install, considering using a web interface like [Optic](https://v0lttech.com/optic.php) or [Cortex](https://v0lttech.com/cortex.php). 45 | - Display 46 | - Optional 47 | - Without a display, Predator can only realistically run in headless mode, and it will be significantly harder to debug. 48 | - Once again, remote access via a web interface like [Optic](https://v0lttech.com/optic.php) could completely replace a directly connected display. 49 | - Speaker 50 | - Optional 51 | - A speaker allows Predator to play notification noises. Without a speaker, Predator will be able to function, but it won't be able to easily notify the user when a plate is detected. 52 | - Any speaker can be used, including an existing sound system in a car. As long as Linux can play an audio output to it, it should work fine. 53 | - GPS 54 | - Optional 55 | - Predator doesn't need a GPS to function. However, certain features won't be operational without it. 56 | - Clock 57 | - Optional 58 | - Since most single-board computers (like the Raspberry Pi) don't have a real time clock (RTC), they will lose track of time without access to the internet. Adding a real time clock ensures your system's time remains accurate. 59 | - Predator should generally work fine with inaccurate time, but you may find it inconvenient, especially when interpreting log files and dash-cam video files. 60 | 61 | 62 | ## Builds 63 | 64 | This section contains part recommendations. Please keep in mind that these parts are not guaranteed to work, and you shouldn't blindly purchase things in these lists. 65 | 66 | Each build in this section has a nick-name to keep it distinct from the other builds. These nick-names align with the pre-built Predator Apex models on the V0LT website at 67 | 68 | 69 | ### Predator Owl 70 | 71 | - Link: 72 | - Description: Predator Owl is designed to be a general purpose automotive dash-cam, with a wide angle lens and great close-range ALPR performance. 73 | - Advantages: 74 | - All of the parts are physically small, and can be easily fit in tight places, like the interior of a vehicle. 75 | - The entire system is lightweight, making it easy to move around. 76 | - Each device is very low power usage, so it should be easy to install using existing infrastructure in your car, building, or other system. 77 | - The camera in this kit has a lens with a generous field of view, which makes it great for close-range ALPR and typical dash-cam recording. 78 | - The faster processing device allows for much higher frame-rates, even with multiple capture devices. 79 | - Disadvantages: 80 | - The wide-angle lens means that this build will struggle to read license plates at a distance. 81 | - Price: ~$200 82 | - Parts: 83 | - Camera: Noroum V11 84 | - Price: $30 85 | - Resolution: 1440p 86 | - Frame Rate: up to 60FPS (30FPS typical) 87 | - Processing: Raspberry Pi 5 88 | - Price: $70 89 | - Mounting: GoPro style buckle mounts with 1/4th inch tripod adapter 90 | - Price: $20 91 | - Power: 12V to 5V 8A transformer 92 | - Price: $20 93 | - Networking: GL.iNet GL-SFT1200 mini router 94 | - Price: $50 95 | - Miscellaneous: 96 | - Cables and wiring 97 | - Storage 98 | - SD card for OS, flash-drive for images, etc. 99 | 100 | 101 | ### Predator Scorpion 102 | 103 | - Link: 104 | - Description: Predator Scorpion is designed for general use in vehicles, security systems, and other low-power applications that require focused, long distance scanning. 105 | - Advantages: 106 | - All of the parts are physically small, and can be easily fit in tight places, like the interior of a vehicle. 107 | - The entire system is lightweight, making it easy to move around. 108 | - Each device is very low power usage, so it should be easy to install using existing infrastructure in your car, building, or other system. 109 | - Disadvantages: 110 | - This hardware configuration severely struggles to successfully read license plates on fast moving vehicles due to the low shutter speed of the camera. 111 | - The camera used in this package is designed for long distance scanning, and may struggle in most typical ALPR usage situations. 112 | - Price: ~$140 113 | - Parts: 114 | - Camera: ELP-USB500W02M-SFV(5-50) 115 | - Price: ~$80 116 | - Resolution: 2592x1944 117 | - Frame Rate: 15FPS 118 | - Reduced Resolution Frame Rate: 30FPS 119 | - Lens: 5-50mm (10X optical zoom) 120 | - Processing: Raspberry Pi 4 B 121 | - Price: ~$40 122 | - Memory: 2GB 123 | - CPU: 4 Core, 1.5GHz ARM CPU 124 | - Miscellaneous: 125 | - Necessary Cables 126 | - Storage 127 | - SD card for OS, flash-drive for images, etc. 128 | - Mounting Hardware 129 | - Screws, adhesives, brackets, etc. 130 | - Necessary to hold the camera in a stable position. 131 | -------------------------------------------------------------------------------- /docs/INSTALL.md: -------------------------------------------------------------------------------- 1 | # Installation 2 | 3 | This document contains detailed instructions on how to install Predator. 4 | 5 | 6 | ## Quick Install Guide 7 | 8 | If you're already familiar with Predator, and you just want a quick set-up guide, you can use the following steps to set everything up. For more detailed instructions, skip to the 'Full Install Guide' later in the document. 9 | 10 | 1. Install Linux packages: `sudo apt-get install ffmpeg mpg321 gpsd gpsd-clients imagemagick fswebcam libhdf5-dev build-essential` 11 | 2. Install Python packages: `pip3 install pytz validators requests gps gpsd-py3 opencv-python psutil rns lxmf gpiozero ultralytics` 12 | - If you see an "externally managed environment" error while installing Python packages, consider one of the following solutions: 13 | - If you're installing Predator on a system dedicated to its use (such as a computer installed in a vehicle), you can override this warning and force install the packages (at the risk of breaking your install) using by adding the `--break-system-packages` flag to the installation command. 14 | - It's worth mentioning that during testing, using the `--break-system-packages` flag while installing Predator has never caused any noticable system issues on any common Debian-based Linux distribution (Ubuntu, Mint, Raspberry Pi OS, etc.). However, you should still exercise caution when using it, especially if you aren't using a fresh Python installation. 15 | - If you're focused on developing or modifying Predator, try creating a virtual environment for sake of consistency. This method is very reliable, but will often cause issues if you intend to integrate Predator with external interfaces in production (like V0LT Optic or V0LT Cortex), since these interfaces are unable to run Predator as part of a virtual environment. 16 | - You can create a virtual environment by navigating to the Predator directory, and running the following command: `python3 -m venv .venv` 17 | - After creating the virtual environment, activate it by running the `source .venv/bin/activate` command. 18 | - Next, install all required Python packages. 19 | - Finally, run Predator. In the future, you'll need to re-activate the virtual environment every time you run Predator. 20 | - The most time consuming solution is to install the required packages through your default package manager. Most packages will just be named after the original Python package, with "python3-" appended to the beginning. For example, the `gpiozero` package can be installed with the `sudo apt install python3-gpiozero` command. However, some packages may not follow this naming scheme, and you might need to manually search for them. 21 | 3. If you intended to use Predator's ALPR features, install an ALPR engine, like [Phantom](https://v0lttech.com/phantom.php). 22 | 4. Download and uncompress Predator. 23 | - Download stable version from V0LT (recommended): https://v0lttech.com/predator.php 24 | - Download the latest (potentially unstable) development version: `git clone https://github.com/connervieira/Predator` 25 | 5. Configure Predator, as described in the [CONFIGURE.md](CONFIGURE.md) file. 26 | 27 | 28 | ## Full Install Guide 29 | 30 | This is the installation process for Predator and all of its dependencies. This process is written assuming you're running a distribution of GNU/Linux, but it is generally possible to get Predator to function on MacOS and BSD-based operating systems as well. 31 | 32 | ### Dependencies 33 | 34 | - Python packages: `pip3 install pytz validators requests gps gpsd-py3 opencv-python psutil` 35 | - Required: 36 | - `pytz`: Required to manage timezones. 37 | - Highly recommended: 38 | - `validators` and `requests`: Required for network functionality, like push notifications, status light interfacing, remote alert lists, and more. 39 | - `opencv-python`: Required for dash-cam recording. 40 | - Unless you have a good reason not to (and you know exactly what you're doing) you should install these packages. 41 | - Recommended: 42 | - `gps` and `gpsd-py3`: Required to enable GPS features. 43 | - These packages are not required for reading GPX files, and are only necessary for interacting with live GPS devices. 44 | - Optional: 45 | - `psutil`: Required to process disk usage information in management mode and dash-cam mode. 46 | - `lxmf`, `rns`: Required to send offline parking notifications over the Reticulum stack. 47 | - `gpiozero`: Required to trigger events using GPIO inputs. 48 | - `ultralytics`: Required for object recognition (but not license plate recognition). 49 | - System packages: `sudo apt-get install ffmpeg mpg321 gpsd gpsd-clients imagemagick fswebcam` 50 | - Highly recommended: 51 | - `ffmpeg`: Required for audio/video merging in dash-cam mode, and video processing in pre-recorded mode. 52 | - `imagemagick`: Required for manipulating still frames of video in pre-recorded mode. 53 | - Recommended: 54 | - `mpg321`: Required to play audio alerts. 55 | - `gpsd`, `gpsd-clients`: Required to receive and process live GPS data. 56 | - It may also be necessary to start GPSD. You can test to see if GPSD is working properly using the `cgps` command. 57 | - Optional: 58 | - `fswebcam`: Useful for troubleshooting camera problems. 59 | - Predator does not depend on FSWebcam, but it can be a useful tool for capturing still images from connected cameras for testing. 60 | 61 | 62 | ### ALPR Engine 63 | 64 | If you intend to use any of Predator's license plate recognition capabilities, you'll need to install an ALPR engine. The two options are 'Phantom ALPR' or 'OpenALPR'. 65 | - Phantom ALPR is a modified version of OpenALPR designed specifically for Predator. Phantom ALPR offers more in-depth integration, and is more fault tolerant. 66 | - If you want the best experience with Predator, and conflicts aren't a concern, Phantom ALPR is a great option. 67 | - You can download Phantom at 68 | - OpenALPR is the arguably industry standard for open source license plate recognition, and is widely used. 69 | - If you already have OpenALPR installed, and don't want to replace it, you can use it with Predator. 70 | 71 | You can learn more about the installation process for each program in their respective documentation. After installing, you should be able to run the ALPR engine of your choice using the `alpr` command. If not, Predator won't be able to run the ALPR process, and will fail to analyze license plates. 72 | -------------------------------------------------------------------------------- /docs/INTEGRATION.md: -------------------------------------------------------------------------------- 1 | # Integration 2 | 3 | This document explains how external programs can interface with Predator. 4 | 5 | If you're interested in developing your own application to integrate with Predator, or you just want to understand how things work behind the scenes, then you're in the right place! 6 | 7 | 8 | ## Collection - Local 9 | 10 | Predator uses various files to share information with external programs. Programs running on the same system can read these files from the working directory to collect information from Predator in real-time. 11 | 12 | ### Heartbeat 13 | 14 | Basic status information is shared with external programs in the form of 'heartbeats'. Each time a processing cycle is completed, a timestamp is added to the `heartbeat.json` file, located in the interface directory. Old entries in this file are trimmed after a certain threshold, as defined in the Predator configuration. These timestamps are analogous to a real heartbeat, in that they signal to external services that Predator is alive and actively running. The frequency of heartbeats is dependent on the processing speed of the device Predator is running on, but the interval is usually less than 5 to 10 seconds, even on low-power devices. 15 | 16 | In short, if it has been more than a few seconds since the last heart-beat, it is likely that Predator is not actively running. 17 | 18 | Example file contents: 19 | 20 | ```json 21 | [ 22 | 1677690941.7604642, 23 | 1677690942.3249989, 24 | 1677690943.4345562, 25 | 1677690944.6840843, 26 | 1677690946.1587856 27 | ] 28 | ``` 29 | 30 | 31 | ### State 32 | 33 | The `state.json` file contains a basic JSON dictionary that indicates the current state of Predator's operation. This file is updated about as regularly as the heartbeat file. These two files can be used in tandem to establish if Predator is running, and what mode it is running in. The `state.json` file contains the following keys, which can be set to one of several values. 34 | - `state` indicates the mode that Predator was running in the last time the state file was updated. 35 | - `"realtime"` indicates real-time mode. 36 | - `"dashcam/normal"` indicates normal dash-cam recording. 37 | - `"dashcam/parked_dormant"` indicates that Predator is in parked dash-cam mode, and is waiting to detect motion before resuming recording. 38 | - `"dashcam/parked_active"` indicates that Predator is in parked dash-cam mode, and is actively recording after motion was detected. 39 | - `gps` indicates the current operating mode of the GPS. 40 | - `0` indicates that no GPS information has been received yet. 41 | - `1` indicates that no GPS fix has been acquired. 42 | - `2` indicates that the GPS has a 2D fix. 43 | - `3` indicates that the GPS has a 3D fix. 44 | - `performance` contains frame-rate information for each capture device while recording in dash-cam mode. 45 | 46 | Example file contents: 47 | ```json 48 | { 49 | "mode": "dashcam/parked_dormant", 50 | "gps": 3, 51 | "performance": { 52 | "front": 49.571633251, 53 | "rear": 46.661928151, 54 | "cabin": 21.128199629 55 | } 56 | } 57 | ``` 58 | 59 | 60 | ### Plates 61 | 62 | Information about the license plates detected by Predator is saved to the `plates.json` file. Each time a processing cycle is completed, an entry in this file is added, using the current Unix timestamp as the key. If no plates were detected, this entry will be empty. When one or more plates are detected, a dictionary will be added for each detected plate, containing all of the guesses for that plate with confidence levels. The key for each plate will be the most likely guess. 63 | 64 | This file is unrelated to the normal license plate log file, and disregards the `realtime>saving>license_plates>` configuration values. 65 | 66 | Example file contents: 67 | 68 | ```json 69 | { 70 | "1677861070.712512": { 71 | }, 72 | "1677861072.284712": { 73 | "KVH8151": { 74 | "KVH8151": 92.113914, 75 | "KVH81S1": 83.792648, 76 | "KVH8I51": 82.17038, 77 | "KVH811": 80.023598 78 | }, 79 | "ISO5122": { 80 | "ISO5122": 97.697231, 81 | "IS05122": 94.220752, 82 | "IS05I22": 82.465191 83 | } 84 | }, 85 | "1677861074.512752": { 86 | "KVH8I53": { 87 | "KVH8I53": 91.512873, 88 | "KVH81S": 81.511251 89 | } 90 | } 91 | } 92 | ``` 93 | 94 | 95 | ### Errors 96 | 97 | Every time an error is encountered and displayed on screen, and identical error message is added to the `errors.json` file. This file contains a JSON dictionary, where each error uses the time it occurred as a key. The contents of this file are not automatically cleared. The message is stored under the "msg" field in the JSON dictionary. The "type" field will be a string set to "error", "warn", or "notice", depending on the type of message. 98 | 99 | Example file contents: 100 | 101 | ```JSON 102 | { 103 | "1677890942.18778": { 104 | "msg": "The local ignore list file does not exist. The local ignore list is disabled.", 105 | "type": "error" 106 | }, 107 | "1677890942.217734": { 108 | "msg": "Invalid configuration option: ['config>developer>ignore_list>local_file']", 109 | "type": "error" 110 | } 111 | } 112 | ``` 113 | 114 | 115 | ### Hotlist 116 | 117 | After loading all alert sources (as configured in `general>alerts>databases`), Predator will save the combined hot-list to the `hotlist.json` file. This allows external programs to easily access the complete list of alert rules loaded by Predator. The contents of this file take the form of a JSON dictionary, where each top-level key is an alert rule. Note that the sub-contents of each rule are not guaranteed to be present. 118 | 119 | Example file contents: 120 | ```JSON 121 | { 122 | "ABC1234": { 123 | "name": "", 124 | "description": "Testing alert", 125 | "author": "V0LT", 126 | "source": "", 127 | "vehicle": { 128 | "make": "Toyota", 129 | "model": "Corolla", 130 | "year": 2021 131 | } 132 | }, 133 | "XYZ1234": { 134 | "name": "", 135 | "description": "Testing alert", 136 | "author": "V0LT", 137 | "source": "", 138 | "vehicle": { 139 | "make": "Subaru", 140 | "model": "Impreza", 141 | "year": 2016 142 | } 143 | }, 144 | "GGG4321": { 145 | "name": "", 146 | "description": "Testing alert", 147 | "author": "V0LT", 148 | "source": "", 149 | "vehicle": { 150 | "make": "Honda", 151 | "model": "Accord", 152 | "year": 2011 153 | } 154 | } 155 | } 156 | ``` 157 | 158 | 159 | ## Triggers - Local 160 | 161 | In addition to the files for sharing information, programs running on the same system can create specific files to trigger certain events in Predator. 162 | 163 | ### Dashcam Saving 164 | 165 | When an important event happens while driving, a user may want to save a specific dashcam video segment to a separate folder so that it doesn't get accidentally deleted. This is referred to as "locking" or "saving" a dashcam video. 166 | 167 | Dashcam saving can be triggered by create the file specified by the `dashcam>saving>trigger` configuration value inside the interface directory. When this file is created, Predator will save the current dashcam video to the configured directory, then delete the trigger file. The trigger file does not need to contain any information. Only its existence is required to trigger Predator. 168 | 169 | 170 | ## Telemetry - Remote 171 | 172 | Predator allows users to share telemetry with remote network targets. 173 | 174 | Predator sends telemetry as a POST request with two fields: 175 | - `"identifier"` is a unique identifier used to authenticate with the remote service. 176 | - This value is defined in the configuration as `dashcam>telemetry>vehicle_identifier` 177 | - `"data"` contains the telemetry data as a JSON string with the following structure: 178 | - `system` contains information about the Predator system. This field must be present. 179 | - `timezone` is the timezone relative to UTC, which follows the format "UTC+00:00". This field must be present. 180 | - `image` contains image information captured by each of the devices defined in the Predator configuration as `dashcam>capture>video>devices`. 181 | - Each capture device has a field in this section, with the capture device as the key, and the image as the value (encoded in base64). 182 | - This section can be omitted, and may not always be present in data submissions. 183 | - `location` contains GPS location information. This section must be included, but can be set to all zeros if no GPS data is available. 184 | - `time` is the Unix timestamp when this point was captured. 185 | - `lat` is the latitude of this point. 186 | - `lon` is the longitude of this point. 187 | - `alt` is the altitude (in meters) of this point. 188 | - `spd` is the speed (in meters per second) of this point. 189 | - `head` is the heading of travel of this point. 190 | 191 | Below is an example of the "data" field, formatted for readability: 192 | ```json 193 | { 194 | "system": { 195 | "timezone": "UTC-04:00" 196 | }, 197 | "image": { 198 | "front": "QECAgICAgQDAgICAgUEBAME...", 199 | "rear": "SNVs45WGRagZLerHJ6nA4x2N..." 200 | }, 201 | "location": { 202 | "time": 1451606400, 203 | "lat": 41.688906, 204 | "lon": -81.208030, 205 | "alt": 241.4, 206 | "spd": 31.9, 207 | "head": 40.0 208 | } 209 | } 210 | ``` 211 | -------------------------------------------------------------------------------- /docs/TROUBLESHOOTING.md: -------------------------------------------------------------------------------- 1 | # Troubleshooting 2 | 3 | This document lists specific problems that you may encounter, and suggested solutions. 4 | 5 | All of these solutions assume you have already completed the full installation/configuration process. 6 | 7 | 8 | ## General 9 | 10 | ### Predator crashes, and shows an error containing "config" with a "KeyError". 11 | 12 | It's possible you've encountered an undiscovered bug. However, if you've recently upgrade Predator, it's possible your active configuration file was not updated properly. Ideally, Predator should automatically update the configuration file, but this may not always work as expected, especially when jumping between versions. Try temporarily re-naming your active configuration file (`mv config.json configbkp.json`), then re-running Predator. A fresh configuration file will be generated. Check to see if the key specified in the error is present in the fresh configuration file, but missing from your original configuration file. If so, try copying over the contents of the key from the fresh configuration to your normal configuration, then rename your normal configuration back to the original (`mv configbkp.json config.json`). 13 | 14 | These kinds of issues can be difficult to troubleshoot, so if you run into additional problems, consider using a fresh configuration file, and manually updating each of the values. 15 | 16 | 17 | ## ALPR 18 | 19 | ### Predator occasionally detects sequences of repeated similar characters (example: II1I1I1, ODDDDOD) 20 | 21 | First, define the formats of the plates you want to detect under the `general>alpr>validation>license_plate_format` configuration value. You can define as many formats as you need to. Then, set the `general>alpr>validation>best_effort` value to `false` to strictly enforce validation. 22 | 23 | If this is not sufficient, try increasing the `general>alpr>validation>confidence` configuration value to increase the minimum required confidence. 24 | 25 | ### Predator regularly detects the same false plate (for example, a text-stamp on a dash-cam overlay) 26 | 27 | Provided the problem can't be solved with the other validation options in the `general>alpr>validation` configuration section, you can hard-code an "ignore list". Simply create a plain text file, where each line is a plate you would like to ignore (wild-cards supported). Then, specify the path to this file under the `developer>ignore_list>local_file` configuration option. Additionally, ensure that `developer>ignore_list>enabled` is set to `true`. If a detected plate has an associated guess that matches a plate in the ignore list, that plate will be thrown out. 28 | 29 | 30 | ## Dash-cam Recording 31 | 32 | ### The audio recording process works for the first segment, but then occasionally fails for subsequent segments. 33 | 34 | Try increasing the `dashcam>capture>audio>start_delay` value. It's likely the audio capture device is not being fully released by the previous segment before it tries to be accessed by the new segment. 35 | 36 | 37 | ### The audio is not synced up with the video (the audio is longer or shorter than the video). 38 | 39 | Ensure that the video recording is running at a consistent frame-rate (and therefore, is a consistent length of time). For example, if your video flucuates between 32 and 37 FPS, try capping it at 30FPS to ensure a consistent frame-rate. This can be done with the `dashcam>capture>video>devices>DEVICE_NAME>frame_rate>max` configuration value. 40 | 41 | 42 | ### Predator enters parking mode doesn't reliably exit it [OR] Predator occasionally enters parking mode while driving. 43 | 44 | Ensure that your GPS is enabled (`general>gps>enabled`) and working correctly. If the GPS loses a lock, Predator may consider the vehicle to be stationary, even if it is moving. Ensure the GPS is reporting the correct data using the `cgps` command at the shell. Consider increasing `dashcam>parked>conditions>time` to prevent Predator from entering parking mode if the GPS loses its lock for a short time (like when driving through a tunnel) 45 | -------------------------------------------------------------------------------- /docs/UNINSTALL.md: -------------------------------------------------------------------------------- 1 | # Uninstalling 2 | 3 | This document contains detailed instructions on how to uninstall Predator, and any traces it might leave. 4 | 5 | 6 | ## Erase Support Files 7 | 8 | Predator only places support files in the working directory and interface directory (as specified in `config.json`). Erasing these directories should erase all files created by Predator that haven't been manually transferred to other folders. 9 | 10 | If you use Phantom as your ALPR back-end, you may also want to erase the temporary image file found at `/dev/shm/phantom-webcam.jpg`. This file is automatically erased every time the system restarts. 11 | 12 | 13 | ## Uninstall Dependencies 14 | 15 | Depending on your use case, you may want to uninstall the dependencies listed in [INSTALL.md](INSTALL.md). 16 | -------------------------------------------------------------------------------- /docs/UPDATE.md: -------------------------------------------------------------------------------- 1 | # Installation 2 | 3 | This document explains how to update Predator from one version to another. 4 | 5 | 6 | ## Checking Dependencies 7 | 8 | After downloading the new version of Predator, you should check its documentation files to see if any dependencies have been changed since the version you're currently running. Predator's dependencies can typically be found in the `docs/INSTALL.md` file, though older versions may have the dependencies listed in the `DOCUMENTATION.md` file. 9 | 10 | 11 | ## Updating Configuration 12 | 13 | This is the most technical step in updating Predator. There are a few different ways to go about moving your configuration to a different version of Predator. 14 | 15 | ### Automatic (Recommended) 16 | 17 | Predator versions 12.0 and later can automatically reconcile differences between the existing configuration file and the default configuration from the new version. To use this feature, simply update Predator, leaving your existing `config.json` file in place. On first start-up, Predator will compare the active configuration file with the version's default, and reconcile differences. Values that only exist in the active configuration will be removed. Values that only exist in the default configuration file will be added to the active configuration. Modified values will remain unchanged. 18 | 19 | ### Refresh 20 | 21 | The most reliable way to update your configuration is to simply replace it with the default `assets/support/configdefault.json` file from the new version of Predator, and manually copy over the important values from your old configuration file. This method makes it much less likely that you'll accidentally use an incompatible configuration file. However, this will require you to fully re-configure Predator from scratch. 22 | 23 | ## Troubleshooting 24 | 25 | If you run into problems, and want to completely reset Predator and start fresh, you can do so with the following steps: 26 | 27 | 1. Erase the contents of the working directory (as defined in `config.json` under `general>working_directory`). 28 | 2. Erase the contents of the interface directory (as defined in `config.json` under `general>interface_directory`). 29 | 3. Replace the `config.json` file with the default `assets/support/configdefault.json` file that comes with the new version of Predator. 30 | -------------------------------------------------------------------------------- /docs/USAGE.md: -------------------------------------------------------------------------------- 1 | # Usage 2 | 3 | This document contains instructions for using Predator after it has been installed. 4 | 5 | 6 | ## Starting 7 | 8 | This section describes the initial start-up process of Predator. 9 | 10 | 1. Run Predator 11 | - To run Predator, simply navigate to its folder, then run `main.py` using the `python3` command. 12 | - `python3 main.py` 13 | - After Predator starts, you should see a banner displaying the Predator name. 14 | - To force Predator to start into a particular mode, add the mode number to the end of the command. 15 | - Example: `python3 main.py 3` 16 | - This command will force Predator to start into mode 3, which is dash-cam mode. 17 | - To learn more about each mode, see the mode descriptions below. 18 | - If no mode number is supplied, Predator will respect the `auto_start_mode` configuration value instead. If the `auto_start_mode` configuration value is not set, then Predator will prompt the user to select an operating mode after start-up. 19 | - To force Predator to use a particular working directory, instead of the one specified in the configuration, add the directory path as the second command line argument. 20 | - A mode number must be entered as the first command line argument to specify a working directory. 21 | - Example: `python3 main.py 2 /home/pi/PredatorData/` 22 | - This command will force Predator to start into real-time mode, using the /home/pi/PredatorData directory as the working directory. 23 | - To force Predator to run in headless mode, add "--headless" after the positional arguments. 24 | - In headless mode, Predator will skip over all user input prompts. 25 | - This is useful if you're running Predator in the background, and want to avoid user input prompts causing Predator to hang. 26 | - This may cause unexpected behavior if used improperly. 27 | - Example: `python3 main.py 3 --headless` 28 | 2. Select a mode 29 | - Predator can operate in 4 possible modes. 30 | - Management mode (Mode 0) 31 | - This mode isn't a main operating mode for Predator, and simply exists for doing management tasks. 32 | - In situations where Predator is hard-installed, like in a vehicle or home security system, this mode makes it easy to clear folders, copy files, and maintain Predator without having to remove the central processing device from the installation. 33 | - Pre-recorded mode (Mode 1) 34 | - In this mode, Predator will analyze one or more pre-recorded video clips that you provide. These videos can be in any format supported by FFMPEG. 35 | - You can use this mode to analyze both stationary recordings and video captured while in motion. 36 | - Real-time mode (Mode 2) 37 | - In this mode, Predator will use a connected camera to detect license plates in real-time. 38 | - In real-time mode, Predator will use the ALPR provider to stream video from a connected capture device directly. 39 | - Dash-cam mode (Mode 3) 40 | - In this mode, Predator will operate like a dash-cam, and simply record video without attempting to detect license plates. 41 | - Optionally, video captured in this mode can be analyzed at a later time using pre-recorded mode. 42 | - Select a mode by entering the number associated with it in the selection menu. 43 | 3. Set preferences 44 | - Next (depending on the mode) Predator will prompt you to set your preferences for the session. The settings you are prompted for will change depending on the mode you choose. Below are the preference menus you'll see for all modes. 45 | - Management mode: 46 | - In management mode, you'll only be asked for a working directory. Simply enter the absolute file path to a folder containing the project you'd like to manage. 47 | - Leave this option blank to use the default value. 48 | - Example: `/home/user/Downloads/MyProjectFolder` 49 | - Pre-recorded mode: 50 | - First, you'll be asked to set the working directory. Simply create an empty folder, then place your video(s) into it. Specify the absolute path to this folder here. 51 | - Leave this option blank to use the default value. 52 | - Example: `/home/pi/Downloads/MyProjectFolder` 53 | - Next, if your working directory contains Predator dash-cam video, you'll be prompted to enter side-car mode. If not, this dialog will be skipped. 54 | - Side-car mode will analyze each frame of all dash-cam videos, and generate a side-car file containing ALPR data. Side-car data can be played back using the `tools/video_sidecar_player.py` script. 55 | - If you select "Y" to enable side-car mode, Predator will immediately begin analyzing all dash-cam videos that don't already have side-car files. 56 | - This process may take a very long time, since every frame of every video is analyzed. 57 | - If you select "N" to skip side-car mode, then set-up process will continue with the next dialog. 58 | - Provided you haven't selected side-car mode, you'll next be asked to enter the file name(s) of the video(s) you want to analyze. Videos should be placed in the working directory you just specified. If you have multiple video files, you can enter them as a comma-separated list. If you want to scan an entire directory, Use a `*` wildcard as the first character. 59 | - Example 1: `MyVideo.mp4` 60 | - Example 2: `MyFirstVideo.mp4, MySecondVideo.mp4` 61 | - Example 3: `*.mp4` 62 | - Example 4: `*_video.avi` 63 | - Next, you'll be asked how many seconds you want to wait between frames for analysis. Since it would be inefficient to process every single frame of video, this value is used to only take frames every N seconds. You can think of this value as "only process a frame every N seconds of video". 64 | - Example: `2` will take a frame to process every 2 seconds of video. This would break up a 10 second video into 5 frames for analysis. 65 | - Note that setting this value to something excessively small (for example, below 0.0333 for a 30fps video) may cause the same frame to be analyzed multiple times. 66 | - Leave this option blank to use the default value. 67 | - Next, you'll be asked for a license plate format example. Filling out this value is highly recommended, since it will greatly reduce the number of incorrectly read plates. If this is left blank, no formatting validation will be used. 68 | - This value can be set to any alphanumeric string. For example, if all the plates in your state have 3 letters followed by 4 numbers, you could set this value to "AAA0000" or "ABC1234". Both values will work the exact same way. Predator only looks at the type of each character, not the character itself. 69 | - For sake of simplicity, you can often just enter the license plate of another car in your state or region. Since Predator only looks to see whether a character is a number or letter, not the character itself, "EGY4011" will act identically to "AAA0000". 70 | - Leave this option blank to use the default value. 71 | - Example: `AAA0000` 72 | - Next, you'll be asked for the time and date that the specified video recording started. 73 | - This preference takes the following format: YYYY-mm-dd HH:MM:SS 74 | - This preference is optional but will enable the GPX file setting, which has the ability to correlate license plates to physical GPS locations. 75 | - Finally, you'll be asked for the file name of a GPX file containing location information relevant to the video file you've specified. 76 | - This setting is optional, but supplying a GPX file with location data allows Predator to pin-point physical locations for each license plate it detects. 77 | - If you wish to correlate license plates to location data from a GPX file, simply place the GPX file in the working directory, then enter its file name at the prompt. Otherwise, leave it blank. 78 | - This feature only works reliably when analyzing a single video file at a time. 79 | - If you don't see this setting prompt when running Predator in pre-recorded mode, it is likely that you didn't supply a time and date in the previous prompt. This is required to enable GPX location correlation. 80 | - Example: `DashcamVideoLocation.gpx` 81 | - Real-time mode: 82 | - Real-time mode has no preferences, and can only be modified in the configuration file. 83 | - Dash-cam mode: 84 | - Dash-cam mode has no preferences, and can only be modified in the configuration file. 85 | 86 | ## Operation 87 | 88 | This section describes how each Predator mode works after the initial start-up. 89 | 90 | ### Management Mode 91 | - Unlike the other modes, the management mode revolves entirely around user menus. 92 | - To navigate through each menu, simply enter the characters associated with the menu selection you'd like to make, then press enter. 93 | - Typically, menu items will be identified simply with numbers, but there may also be an additional letter, like in the case of the 'Copy' and 'Delete' menus. 94 | 95 | ### Pre-recorded Mode 96 | - While Predator is running its analysis, a directory named 'frames' will appear in the working directory. 97 | - Individual frames will begin to appear in this folder. 98 | - Do not modify or delete this directory, or any files that it contains, since Predator will repeatedly access them during the course of its analysis. 99 | - After analysis completes, you can safely delete these files either manually, or by using Predator's management mode. These files will automatically be deleted the next time Predator runs in pre-recorded mode. 100 | - After Predator finishes running, you'll be sent to the analysis menu. 101 | - This menu allows you to manage, view, export, and manipulate the data collected in the current session. 102 | - To navigate this menu, simply enter the ID number of the menu item you want to select, then press enter. 103 | 104 | ### Real-time Mode 105 | - While in real-time mode, Predator will run in an endless loop until quit by holding `Ctrl + C`. 106 | - When one or more license plates are detected, Predator will display it on screen, provided that it is configured to do so. 107 | - Depending on the configuration, Predator might also display a large text shape to make it easier to see important information at a glance. 108 | - Depending on the configuration, Predator might play an audio sound indicating the type of plate detected. 109 | - Depending on the configuration, Predator might submit the license plate detected to a push notification service. 110 | - If a plate detected is in one of the alert databases, it will show a prominent alert message in the console output. 111 | 112 | ### Dash-cam Mode 113 | - In dash-cam mode, Predator will record video indefinitely until `Ctrl + C` is pressed, the Predator process is terminated, or a problem is encountered. 114 | - Predator records in a format that is resilient to being unexpectedly terminated. As such, suddenly killing Predator or turning off the system that it runs on shouldn't cause any meaningful data loss. That being said, certain tasks, like video/audio file merging, might not be completed if Predator is terminated before they are completed. 115 | - Predator will not detect license plates in this mode. However, you can analyze recorded dash-cam video using pre-recorded mode at a later time. 116 | - If configured to do so, Predator may automatically switch between "normal" and "parked" mode when the vehicle is stopped for a prolonged period of time. 117 | - In normal mode, Predator simply records dash-cam video as you would expect a traditional dash-cam to do. Separate video segments will be saved at regular intervals according to the configuration. 118 | - In parked mode, Predator sits dormant, waiting to detect motion on any of its configured capture devices. When motion is detected, Predator saves a certain number of past frames from a buffer, and begins recording until motion has not been detected for a certain period of time. Predator treats different capture devices as different zones, and will only record video on the camera that detects motion. If multiple cameras detect motion at the same time, Predator will record on them simultaneously. 119 | - The dash-cam video recorded will be saved to the working directory as `predator_dashcam_TIME_CHANNEL_SEGMENT_TYPE.mkv`. 120 | - `TIME` is replaced by a Unix timestamp of when the file was created. 121 | - `CHANNEL` is replaced by the name of the device used, as specified in the configuration. 122 | - `SEGMENT` is replaced by the segment number. 123 | - Videos recorded while parked will always have a segment number of 0. 124 | - `TYPE` is replaced by either the letter "N" or "P" to indicate "normal" or "parked" mode respectively. 125 | -------------------------------------------------------------------------------- /global_variables.py: -------------------------------------------------------------------------------- 1 | def init(): 2 | global predator_running 3 | predator_running = True 4 | -------------------------------------------------------------------------------- /ignore.py: -------------------------------------------------------------------------------- 1 | # Copyright (C) 2024 V0LT - Conner Vieira 2 | 3 | # This program is free software: you can redistribute it and/or modify it under the terms of the GNU Affero General Public License as published by the Free Software Foundation, either version 3 of the License, or (at your option) any later version. 4 | 5 | # This program is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU Affero General Public License for more details. 6 | 7 | # You should have received a copy of the GNU Affero General Public License along with this program (LICENSE) 8 | # If not, see https://www.gnu.org/licenses/ to read the license agreement. 9 | 10 | 11 | 12 | 13 | 14 | # This library is responsible for handling 'ignore lists', which allow administrators to define a list of license plates that should be ignored. 15 | 16 | # Ignore lists might be used to prevent common vehicles from being recorded in order to keep logs organized, or to prevent privacy-concerned visitors from having their license plate processed. 17 | 18 | # Custom ignore lists can be enabled and disabled in the configuration. 19 | 20 | 21 | import os # Required to interact with certain operating system functions. 22 | import json # Required to process JSON data. 23 | import time # Required to manage delays. 24 | import validators # Required to validate URLs. 25 | 26 | import utils 27 | style = utils.style 28 | display_message = utils.display_message 29 | 30 | predator_root_directory = str(os.path.dirname(os.path.realpath(__file__))) # This variable determines the folder path of the root Predator directory. This should usually automatically recognize itself, but it if it doesn't, you can change it manually. 31 | 32 | try: 33 | if (os.path.exists(predator_root_directory + "/config.json")): 34 | config = json.load(open(predator_root_directory + "/config.json")) # Load the configuration database from config.json 35 | else: 36 | print("The configuration file doesn't appear to exist at " + predator_root_directory + "/config.json.") 37 | exit() 38 | except: 39 | print("The configuration database couldn't be loaded. It may be corrupted.") 40 | exit() 41 | 42 | 43 | 44 | if (config["developer"]["offline"] == False): # Only import networking libraries if offline mode is turned off. 45 | import requests # Required to fetch information from network hosts. 46 | 47 | 48 | def fetch_ignore_list(): 49 | predator_root_directory = str(os.path.dirname(os.path.realpath(__file__))) # This variable determines the folder path of the root Predator directory. This should usually automatically recognize itself, but it if it doesn't, you can change it manually. 50 | 51 | config = json.load(open(predator_root_directory + "/config.json")) # Load the configuration database from config.json 52 | 53 | 54 | 55 | complete_ignore_list = [] # This will hold the complete list of plates to ignore, after all ignore list sources have been loaded. 56 | 57 | if (config["developer"]["ignore_list"]["enabled"] == True): # Only load the local ignore list file if the ignore list is enabled in the configuration. 58 | 59 | local_ignore_list_file = config["developer"]["ignore_list"]["local_file"] 60 | if (local_ignore_list_file != ""): # Only load the local ignore list file if it exists. 61 | if (os.path.exists(local_ignore_list_file) == True): 62 | loaded_local_ignore_list_file = open(local_ignore_list_file, "r") # Open the local ignore list file. 63 | local_ignore_list = json.loads(loaded_local_ignore_list_file.read()) # Read the contents of the file. 64 | loaded_local_ignore_list_file.close() # Close the file. 65 | 66 | for entry in local_ignore_list: # Iterate through each entry in the local ignore list, and add it to the complete ignore list. 67 | complete_ignore_list.append(entry) 68 | else: 69 | display_message("The local ignore list file does not exist. The local ignore list is effectively disabled.", 3) 70 | 71 | 72 | remote_ignore_sources = ["https://v0lttech.com/predator/manifest/serve.php?type=ignore&user=cvieira&list=publicignorelist"] # This holds a list of hard-coded remote sources that ignore lists will be fetched from. Developers who want to deploy Predator to devices can add remote sources for ignore lists here to make them easier to manage. This allows administrators to automatically issue ignore lists from an external services. Administrators can maintain ignore lists without needing to manually modify the local ignore list for all their devices. Remote sources don't receive any telemetry from Predator, only a simple JSON list is fetched. Custom remote sources from the configuration are added in the next steps. 73 | 74 | if (config["developer"]["ignore_list"]["enabled"] == True): # Only add custom remote sources if custom ignore lists are enabled in the configuration 75 | for source in config["developer"]["ignore_list"]["remote_sources"]: # Iterate through each source in the list of remote ignore list sources. 76 | remote_ignore_sources.append(source) # Add the remote source to the list of remote sources. 77 | 78 | if (config["developer"]["offline"] == True): # If offline mode is enabled, then remove all remote ignore list sources. 79 | remote_ignore_sources = [] # Set this list of remote ignore list sources to a blank list. 80 | 81 | for host in remote_ignore_sources: # Iterate through all of the hosts specified in the list of remote ignore list sources. 82 | if (validators.url(host)): # Verify that this 'host' value is a valid URL. 83 | try: # Run the network request in a try block so the entire program doesn't fail if something goes wrong. 84 | response = requests.get(host, timeout=2.0) # Make a request to this host that times out after 2 seconds. 85 | response_content = response.text # Grab the text from the response. 86 | except: # If the network request fails, do the following steps instead. 87 | response_content = "[]" # Use a blank placeholder response database. 88 | 89 | try: # Run the JSON load function in a 'try' block to prevent fatal crashes if the data returned by the remote source isn't valid JSON. 90 | remote_ignore_list = json.loads(response_content) 91 | except: # If the list fails to load, it's likely because it's not valid JSON data. 92 | remote_ignore_list = [] # Set the loaded list to a blank placeholder list. 93 | 94 | for entry in remote_ignore_list: # Iterate through each entry in this remote ignore list, and add it to the complete ignore list. 95 | complete_ignore_list.append(entry) 96 | 97 | else: # This remote ignore list source is not a valid URL. 98 | pass 99 | 100 | 101 | 102 | sanitized_ignore_list = [] 103 | for entry in complete_ignore_list: 104 | if (len(entry) < 25): # Verify that this entry is a reasonable length. 105 | sanitized_ignore_list.append(entry.upper()) # Convert this entry to all uppercase letters, and add it to the sanitized list. 106 | 107 | 108 | final_ignore_list = list(dict.fromkeys(sanitized_ignore_list)) # De-duplicate the ignore list to make processing more efficient. 109 | return final_ignore_list 110 | -------------------------------------------------------------------------------- /lighting.py: -------------------------------------------------------------------------------- 1 | # Predator 2 | 3 | # Copyright (C) 2024 V0LT - Conner Vieira 4 | 5 | # This program is free software: you can redistribute it and/or modify it under the terms of the GNU Affero General Public License as published by the Free Software Foundation, either version 3 of the License, or (at your option) any later version. 6 | 7 | # This program is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU Affero General Public License for more details. 8 | 9 | # You should have received a copy of the GNU Affero General Public License along with this program (LICENSE) 10 | # If not, see https://www.gnu.org/licenses/ to read the license agreement. 11 | 12 | 13 | 14 | import validators # Required to validating URLs. 15 | import requests # Required to send network requests. 16 | import json # Required to process JSON data. 17 | import os # Required to interact with certain operating system functions. 18 | import time 19 | 20 | import utils # Import the utils.py script. 21 | style = utils.style # Load the style from the utils script. 22 | clear = utils.clear # Load the screen clearing function from the utils script. 23 | debug_message = utils.debug_message # Load the debug message display function from the utils script. 24 | display_message = utils.display_message # Load the error message display function from the utils script. 25 | 26 | 27 | predator_root_directory = str(os.path.dirname(os.path.realpath(__file__))) # This variable determines the folder path of the root Predator directory. This should usually automatically recognize itself, but it if it doesn't, you can change it manually. 28 | try: 29 | if (os.path.exists(predator_root_directory + "/config.json")): 30 | config = json.load(open(predator_root_directory + "/config.json")) # Load the configuration database from config.json 31 | else: 32 | print("The configuration file doesn't appear to exist at " + predator_root_directory + "/config.json.") 33 | exit() 34 | except: 35 | print("The configuration database couldn't be loaded. It may be corrupted.") 36 | exit() 37 | 38 | 39 | current_status_light_id = "" 40 | start_time = time.time() # This stores the time that the status lighting engine was first loaded (when Predator started). 41 | def update_status_lighting(url_id): 42 | global current_status_light_id 43 | global start_time 44 | debug_message("Updating status lighting") 45 | if (time.time() - start_time >= config["general"]["status_lighting"]["delay_after_boot"]): 46 | if (url_id != current_status_light_id): # Check to see if the status light URL ID is different from the current state of the lights. 47 | current_status_light_id = url_id 48 | if (config["general"]["status_lighting"]["enabled"] == True): # Only update the status lighting if it is enabled in the configuration. 49 | status_lighting_update_url = str(config["general"]["status_lighting"]["values"][url_id]).replace("[U]", str(config["general"]["status_lighting"]["base_url"])) # Prepare the URL where a request will be sent in order to update the status lighting. 50 | if (config["developer"]["offline"] == False): # Check to make sure offline mode is disabled before sending the network request to update the status lighting. 51 | if (validators.url(status_lighting_update_url)): # Check to make sure the URL ID supplied actually resolves to a valid URL in the configuration database. 52 | try: 53 | response = requests.get(status_lighting_update_url, timeout=0.5) 54 | except: 55 | display_message("Failed to update status lighting. The request timed out.", 3) # Display a warning indicating that the status lighting request timed out. 56 | else: 57 | display_message("Unable to update status lighting. Invalid URL configured for " + url_id, 3) # Display a warning indicating that the URL was invalid, and no network request was sent. 58 | debug_message("Status light update complete") 59 | -------------------------------------------------------------------------------- /object_recognition.py: -------------------------------------------------------------------------------- 1 | # Predator 2 | 3 | # Copyright (C) 2024 V0LT - Conner Vieira 4 | 5 | # This program is free software: you can redistribute it and/or modify it under the terms of the GNU Affero General Public License as published by# the Free Software Foundation, either version 3 of the License, or (at your option) any later version. 6 | 7 | # This program is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU Affero General Public License for more details. 8 | 9 | # You should have received a copy of the GNU Affero General Public License along with this program (LICENSE) 10 | # If not, see https://www.gnu.org/licenses/ to read the license agreement. 11 | 12 | 13 | import os 14 | from ultralytics import YOLO 15 | import numpy 16 | import utils 17 | import config 18 | config = config.load_config() 19 | 20 | dashcam_model = YOLO(config["dashcam"]["parked"]["event"]["trigger_object_recognition"]["model_weights"]) 21 | 22 | def predict(frame, selected_model): 23 | if (selected_model == "dashcam"): 24 | model = dashcam_model 25 | else: 26 | utils.display_message("Unrecognized model specified for `predict()`.", 3) 27 | results = model(frame, verbose=False) 28 | class_names = results[0].names 29 | 30 | detected_objects = [] # This is a placeholder that will hold all of the detected objects. 31 | for result in results: 32 | boxes = result.boxes 33 | for i in range(0, len(boxes)): 34 | obj = {} 35 | box = result.boxes[i].xyxy.numpy().tolist()[0] 36 | obj["bbox"] = {} 37 | obj["bbox"]["x1"] = round(box[0]) 38 | obj["bbox"]["y1"] = round(box[1]) 39 | obj["bbox"]["x2"] = round(box[2]) 40 | obj["bbox"]["y2"] = round(box[3]) 41 | obj["name"] = class_names[int(result.boxes[i].cls.numpy().tolist()[0])] 42 | obj["conf"] = result.boxes[i].conf.numpy().tolist()[0] 43 | detected_objects.append(obj) 44 | return detected_objects 45 | -------------------------------------------------------------------------------- /reticulum.py: -------------------------------------------------------------------------------- 1 | # Predator 2 | 3 | # Copyright (C) 2024 V0LT - Conner Vieira 4 | 5 | # This program is free software: you can redistribute it and/or modify it under the terms of the GNU Affero General Public License as published by# the Free Software Foundation, either version 3 of the License, or (at your option) any later version. 6 | 7 | # This program is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU Affero General Public License for more details. 8 | 9 | # You should have received a copy of the GNU Affero General Public License along with this program (LICENSE) 10 | # If not, see https://www.gnu.org/licenses/ to read the license agreement. 11 | 12 | 13 | 14 | 15 | # This script contains functions for interacting with services over the Reticulum network protocol. 16 | 17 | 18 | 19 | import os # Required to interact with certain operating system functions 20 | import json # Required to process JSON data 21 | 22 | predator_root_directory = str(os.path.dirname(os.path.realpath(__file__))) # This variable determines the folder path of the root Predator directory. This should usually automatically recognize itself, but it if it doesn't, you can change it manually. 23 | 24 | try: 25 | if (os.path.exists(predator_root_directory + "/config.json")): 26 | config = json.load(open(predator_root_directory + "/config.json")) # Load the configuration database from config.json 27 | else: 28 | print("The configuration file doesn't appear to exist at " + predator_root_directory + "/config.json.") 29 | exit() 30 | except: 31 | print("The configuration database couldn't be loaded. It may be corrupted.") 32 | exit() 33 | 34 | 35 | import LXMF 36 | import RNS 37 | import time 38 | import utils 39 | display_message = utils.display_message 40 | debug_message = utils.debug_message 41 | 42 | 43 | rns = RNS.Reticulum() # Initialize Reticulum. 44 | lxmf_router = LXMF.LXMRouter(storagepath="/tmp/predatorlxmf") 45 | 46 | if (os.path.exists(config["dashcam"]["notifications"]["reticulum"]["identity_file"]) == False): 47 | new_identity = RNS.Identity() 48 | new_identity.to_file(config["dashcam"]["notifications"]["reticulum"]["identity_file"]) 49 | 50 | debug_message("Loading Reticulum identity file") 51 | identity = RNS.Identity().from_file(config["dashcam"]["notifications"]["reticulum"]["identity_file"]) 52 | 53 | debug_message("Announcing Reticulum source") 54 | source = lxmf_router.register_delivery_identity(identity, display_name="Predator") 55 | lxmf_router.announce(source.hash) # Announce this instance. 56 | last_announce = time.time() 57 | 58 | def lxmf_send_message(message, destination): 59 | global lxmf_router 60 | global config 61 | 62 | if (time.time() - last_announce > 30*60): 63 | debug_message("Announcing Reticulum source") 64 | lxmf_router.announce(source.hash) # Announce this instance. 65 | 66 | debug_message("Identifying Reticulum destination") 67 | recipient_hash = bytes.fromhex(destination) 68 | if not RNS.Transport.has_path(recipient_hash): # Check to see if the destination is currently unknown. 69 | RNS.Transport.request_path(recipient_hash) 70 | start_time = time.time() 71 | while not RNS.Transport.has_path(recipient_hash): # Wait until the destination is known. 72 | if (time.time() - start_time > 10): # Check to see if the request has been stuck for more than a certain number of seconds. 73 | display_message("Failed to determine Reticulum destination.", 2) 74 | break 75 | time.sleep(0.1) 76 | recipient_identity = RNS.Identity.recall(recipient_hash) 77 | dest = RNS.Destination(recipient_identity, RNS.Destination.OUT, RNS.Destination.SINGLE, "lxmf", "delivery") # Establish the LXMF destination. 78 | 79 | debug_message("Sending Reticulum message") 80 | lxmf_request = LXMF.LXMessage(dest, source, message, "Predator", desired_method=LXMF.LXMessage.DIRECT) 81 | lxmf_router.handle_outbound(lxmf_request) 82 | time.sleep(0.01) # Pause for a brief destinationmoment to let the outbound request be processed by Reticulum. 83 | -------------------------------------------------------------------------------- /tools/convert_plate_history_csv.py: -------------------------------------------------------------------------------- 1 | import os # Required to interact with certain operating system functions. 2 | import json # Required to process JSON data. 3 | import datetime # Required to handle timestamps. 4 | 5 | 6 | predator_root_directory = str(os.path.dirname(os.path.realpath(__file__ + "/.."))) # This variable determines the folder path of the root Predator directory. This should usually automatically recognize itself, but it if it doesn't, you can change it manually. 7 | 8 | 9 | try: 10 | if (os.path.exists(predator_root_directory + "/config.json")): 11 | config = json.load(open(predator_root_directory + "/config.json")) # Load the configuration database from config.json 12 | else: 13 | print("The configuration file doesn't appear to exist at " + predator_root_directory + "/config.json.") 14 | exit() 15 | except: 16 | print("The configuration database couldn't be loaded. It may be corrupted.") 17 | exit() 18 | 19 | 20 | def is_json(string): 21 | try: 22 | json_object = json.loads(string) # Try to load string as JSON information. 23 | except ValueError as error_message: # If the process fails, then the string is not valid JSON. 24 | return False # Return 'false' to indicate that the string is not JSON. 25 | return True # If the try statement is successful, then return 'true' to indicate that the string is valid JSON. 26 | 27 | 28 | plate_history_filepath = config["general"]["working_directory"] + "/" + config["realtime"]["saving"]["license_plates"]["file"] 29 | 30 | 31 | plate_log_file = open(plate_history_filepath, "r") # Open the plate log file for reading. 32 | plate_log_file_contents = plate_log_file.read() # Read the raw contents of the plate file as a string. 33 | plate_log_file.close() # Close the plate log file. 34 | 35 | if (is_json(plate_log_file_contents) == True): # If the plate file contains valid JSON data, then load it. 36 | plate_log = json.loads(plate_log_file_contents) # Read and load the plate log from the file contents. 37 | else: # If the plate log file doesn't contain valid JSON data, then load a blank placeholder in it's place. 38 | print("The contents of the plate log file does not appear to be valid JSON data.") 39 | 40 | 41 | csv_contents = "" 42 | for time in plate_log.keys(): 43 | human_date = datetime.datetime.fromtimestamp(float(time)).strftime('%Y-%m-%d %H:%M:%S') 44 | for plate in plate_log[time]["plates"].keys(): 45 | csv_line = human_date + "," + plate 46 | if (len(plate_log[time]["plates"][plate]["alerts"]) > 0): # Check to see if this plate was associated with any alerts. 47 | csv_line = csv_line + ",true" 48 | else: 49 | csv_line = csv_line + ",false" 50 | csv_line = csv_line + "," + str(plate_log[time]["location"]["lat"]) + "," + str(plate_log[time]["location"]["lon"]) 51 | csv_contents += csv_line + "\n" 52 | 53 | print(csv_contents) 54 | -------------------------------------------------------------------------------- /tools/create_trigger_file.py: -------------------------------------------------------------------------------- 1 | # This is a simple testing script that manually creates the dashcam video saving trigger file when executed. 2 | 3 | import os 4 | import json 5 | import time 6 | 7 | predator_root_directory = str(os.path.dirname(os.path.realpath(__file__))) # This variable determines the folder path of the root Predator directory. This should usually automatically recognize itself, but it if it doesn't, you can change it manually. 8 | 9 | try: 10 | if (os.path.exists(predator_root_directory + "/../config.json")): 11 | config = json.load(open(predator_root_directory + "/../config.json")) # Load the configuration database from config.json 12 | else: 13 | print("The configuration file doesn't appear to exist at " + predator_root_directory + "/../config.json.") 14 | exit() 15 | except Exception as e: 16 | print(e) 17 | print("The configuration database couldn't be loaded. It may be corrupted.") 18 | exit() 19 | 20 | 21 | if (os.path.isdir(config["general"]["interface_directory"]) == False): # Check to see if the interface directory has not yet been created. 22 | os.system("mkdir -p '" + str(config["general"]["interface_directory"]) + "'") 23 | os.system("chmod -R 777 '" + str(config["general"]["interface_directory"]) + "'") 24 | if (os.path.exists(os.path.join(config["general"]["interface_directory"], config["dashcam"]["saving"]["trigger"])) == False): # Check to see if the trigger file hasn't already been created. 25 | os.system("echo " + str(time.time()) + " > \"" + os.path.join(config["general"]["interface_directory"], config["dashcam"]["saving"]["trigger"]) + "\"") # Save the trigger file with the current time as the timestamp. 26 | -------------------------------------------------------------------------------- /tools/disk_write_test.py: -------------------------------------------------------------------------------- 1 | # This script captures video using each configured dash-cam video device, using both a queued and direct saving method, and compares how long each method takes to complete. 2 | # The queued method captures all the frames into a queue, then saves the entire queue at once. 3 | # The direct method captures frames and immediately saves them individually. 4 | 5 | 6 | import os # Required to interact with certain operating system functions 7 | import json # Required to process JSON data 8 | import cv2 9 | import time 10 | 11 | predator_root_directory = str(os.path.dirname(os.path.realpath(__file__))) # This variable determines the folder path of the root Predator directory. This should usually automatically recognize itself, but it if it doesn't, you can change it manually. 12 | 13 | try: 14 | if (os.path.exists(predator_root_directory + "/../config.json")): 15 | config = json.load(open(predator_root_directory + "/../config.json")) # Load the configuration database from config.json 16 | else: 17 | print("The configuration file doesn't appear to exist at " + predator_root_directory + "/../config.json.") 18 | exit() 19 | except: 20 | print("The configuration database couldn't be loaded. It may be corrupted.") 21 | exit() 22 | 23 | 24 | 25 | def dump_video(device, frames=100): # This function benchmarks a given camera to determine its framerate. 26 | global config 27 | 28 | 29 | resolution = [config["dashcam"]["capture"]["video"]["devices"][device]["resolution"]["width"], config["dashcam"]["capture"]["video"]["devices"][device]["resolution"]["height"]] # This determines the resolution that will be used for the video capture device. 30 | capture = cv2.VideoCapture(config["dashcam"]["capture"]["video"]["devices"][device]["index"]); # Open the video capture device. 31 | codec = list(config["dashcam"]["capture"]["video"]["devices"][device]["codec"]) 32 | capture.set(cv2.CAP_PROP_FOURCC, cv2.VideoWriter_fourcc(codec[0], codec[1], codec[2], codec[3])) # Set the codec to be MJPEG. 33 | capture.set(cv2.CAP_PROP_FPS, 120) # Set the frame-rate to a high value so OpenCV will use the highest frame-rate the capture supports. 34 | capture.set(cv2.CAP_PROP_FRAME_WIDTH,resolution[0]) # Set the video stream width. 35 | capture.set(cv2.CAP_PROP_FRAME_HEIGHT,resolution[1]) # Set the video stream height. 36 | 37 | print("Testing " + str(device)) 38 | 39 | for i in range(0, 10): # Loop a few times to allow the camera to warm up before the benchmark starts. 40 | ret, frame = capture.read() # Capture a video frame. 41 | 42 | print("Capturing Via Queue") 43 | frame_buffer = [] 44 | start_time = time.time() 45 | for i in range(0, frames): # Run until the specified number of frames have been captured. 46 | ret, frame = capture.read() # Capture a video frame. 47 | stamp_test = str(round(time.time()*100)/100) + " PLACEHOLDER" + str(round(time.time()/2)) # Manipulate a few random values to simulate the generation of the overlay stamp. 48 | cv2.putText(frame, stamp_test, (10, 10), 2, 0.8, (255,255,255)) # Add the test stamp to the video frame. 49 | frame_buffer.append(frame) 50 | print(" Capture Time: " + str(time.time() - start_time)) 51 | 52 | 53 | output = cv2.VideoWriter("./out-queue.avi", cv2.VideoWriter_fourcc(*'XVID'), 30, (resolution[0], resolution[1])) 54 | start_time = time.time() 55 | for frame in frame_buffer: 56 | output.write(frame) 57 | print(" Save Time: " + str(time.time() - start_time)) 58 | 59 | 60 | output = None 61 | time.sleep(1) 62 | 63 | 64 | print("Capturing Directly") 65 | total_capture_time = 0 66 | total_save_time = 0 67 | output = cv2.VideoWriter("./out-direct.avi", cv2.VideoWriter_fourcc(*'XVID'), 30, (resolution[0], resolution[1])) 68 | for i in range(0, frames): # Run until the specified number of frames have been captured. 69 | start_time = time.time() 70 | ret, frame = capture.read() # Capture a video frame. 71 | stamp_test = str(round(time.time()*100)/100) + " PLACEHOLDER" + str(round(time.time()/2)) # Manipulate a few random values to simulate the generation of the overlay stamp. 72 | cv2.putText(frame, stamp_test, (10, 10), 2, 0.8, (255,255,255)) # Add the test stamp to the video frame. 73 | frame_buffer.append(frame) 74 | total_capture_time = total_capture_time + (time.time() - start_time) 75 | start_time = time.time() 76 | output.write(frame) 77 | total_save_time = total_save_time + (time.time() - start_time) 78 | 79 | print(" Capture Time: " + str(total_capture_time)) 80 | print(" Save Time: " + str(total_save_time)) 81 | 82 | 83 | for device in config["dashcam"]["capture"]["video"]["devices"]: 84 | dump_video(device) 85 | -------------------------------------------------------------------------------- /tools/framerate_benchmark.py: -------------------------------------------------------------------------------- 1 | import os # Required to interact with certain operating system functions 2 | import json # Required to process JSON data 3 | import cv2 4 | import time 5 | 6 | predator_root_directory = str(os.path.dirname(os.path.realpath(__file__))) # This variable determines the folder path of the root Predator directory. This should usually automatically recognize itself, but it if it doesn't, you can change it manually. 7 | 8 | try: 9 | if (os.path.exists(predator_root_directory + "/../config.json")): 10 | config = json.load(open(predator_root_directory + "/../config.json")) # Load the configuration database from config.json 11 | else: 12 | print("The configuration file doesn't appear to exist at " + predator_root_directory + "/../config.json.") 13 | exit() 14 | except: 15 | print("The configuration database couldn't be loaded. It may be corrupted.") 16 | exit() 17 | 18 | 19 | 20 | def benchmark_camera_framerate(device, frames=5): # This function benchmarks a given camera to determine its framerate. 21 | global config 22 | 23 | resolution = [config["dashcam"]["capture"]["video"]["devices"][device]["resolution"]["width"], config["dashcam"]["capture"]["video"]["devices"][device]["resolution"]["height"]] # This determines the resolution that will be used for the video capture device. 24 | capture = cv2.VideoCapture(config["dashcam"]["capture"]["video"]["devices"][device]["index"]); # Open the video capture device. 25 | codec = list(config["dashcam"]["capture"]["video"]["devices"][device]["codec"]) 26 | capture.set(cv2.CAP_PROP_FOURCC, cv2.VideoWriter_fourcc(codec[0], codec[1], codec[2], codec[3])) # Set the codec to be MJPEG. 27 | capture.set(cv2.CAP_PROP_FPS, 120) # Set the frame-rate to a high value so OpenCV will use the highest frame-rate the capture supports. 28 | capture.set(cv2.CAP_PROP_FRAME_WIDTH,resolution[0]) # Set the video stream width. 29 | capture.set(cv2.CAP_PROP_FRAME_HEIGHT,resolution[1]) # Set the video stream height. 30 | 31 | print("Running benchmark for '" + device + "'...") 32 | 33 | 34 | for i in range(0, 10): # Loop a few times to allow the camera to warm up before the benchmark starts. 35 | ret, frame = capture.read() # Capture a video frame. 36 | start_time = time.time() # Record the exact time that the benchmark started. 37 | for i in range(0, frames): # Run until the specified number of frames have been captured. 38 | ret, frame = capture.read() # Capture a video frame. 39 | stamp_test = str(round(time.time()*100)/100) + " PLACEHOLDER" + str(round(time.time()/2)) # Manipulate a few random values to simulate the generation of the overlay stamp. 40 | cv2.putText(frame, stamp_test, (10, 10), 2, 0.8, (255,255,255)) # Add the test stamp to the video frame. 41 | 42 | end_time = time.time() # Record the exact time that the benchmark ended. 43 | total_time = end_time - start_time # Calculate how many seconds the benchmark took to complete. 44 | fps = frames / total_time # Calculate the number of frames captured per second. 45 | return fps # Return the calculated FPS. 46 | 47 | for device in config["dashcam"]["capture"]["video"]["devices"]: 48 | framerate = benchmark_camera_framerate(device) # Benchmark this capture device and record the calculated FPS. 49 | print("Calculated FPS: " + str(framerate)) # Display the calculated FPS. 50 | -------------------------------------------------------------------------------- /tools/gpio_dashcam_save_trigger.py: -------------------------------------------------------------------------------- 1 | # V0LT Predator 2 | # GPIO Dashcam Save Trigger 3 | 4 | # This is a stand-alone script that will create the dashcam save trigger file when a GPIO event occurs. This script is only useful when Predator is operating in dash-cam mode, but it doesn't necessarily depend on it to function. In other words, this script can be started as a separate service, before Predator starts. 5 | 6 | gpio_pins = [17, 22, 27] # These are the pins that will be monitored for button presses. 7 | 8 | 9 | import os # Required to interact with certain operating system functions 10 | import json # Required to process JSON data 11 | import time 12 | import threading 13 | 14 | from gpiozero import Button 15 | from signal import pause 16 | 17 | base_directory = str(os.path.dirname(os.path.realpath(__file__))) 18 | try: 19 | if (os.path.exists(base_directory + "/../config.json")): 20 | config = json.load(open(base_directory + "/../config.json")) # Load the configuration database from config.json 21 | else: 22 | print("The configuration file doesn't appear to exist at " + base_directory + "/../config.json.") 23 | exit() 24 | except: 25 | print("The configuration database couldn't be loaded. It may be corrupted.") 26 | exit() 27 | 28 | trigger_file_location = config["general"]["interface_directory"] + "/" + config["dashcam"]["saving"]["trigger"] # Define the path of the dashcam lock trigger file. 29 | trigger_file_location = trigger_file_location.replace("//", "/") # Remove any duplicate slashes in the file path. 30 | if (os.path.isdir(config["general"]["interface_directory"]) == False): # Check to see if the interface directory has not yet been created. 31 | os.system("mkdir -p '" + str(config["general"]["interface_directory"]) + "'") 32 | os.system("chmod -R 777 '" + str(config["general"]["interface_directory"]) + "'") 33 | 34 | 35 | def create_trigger_file(): 36 | os.system("touch '" + trigger_file_location + "'") 37 | 38 | def watch_button(pin, hold_time=0.2, event=create_trigger_file): 39 | print("Watching pin " + str(pin)) 40 | button = Button(pin) 41 | 42 | time_pressed = 0 43 | last_triggered = 0 44 | while True: 45 | if (button.is_pressed and time_pressed == 0): # Check to see if the button was just pressed. 46 | #print("Pressed" + str(pin)) 47 | time_pressed = time.time() 48 | elif (button.is_pressed and time.time() - time_pressed < hold_time): # Check to see if the button is being held, but the time threshold hasn't been reached. 49 | pass 50 | #print("Holding") 51 | elif (button.is_pressed and time.time() - time_pressed >= hold_time): # Check to see if the button is being held, and the time threshold has been reached. 52 | if (time.time() - last_triggered > 1): 53 | #print("Triggered " + str(pin)) 54 | event() 55 | last_triggered = time.time() 56 | elif (button.is_pressed == False): # If the button is not pressed, reset the timer. 57 | time_pressed = 0 58 | 59 | time.sleep(0.02) 60 | 61 | 62 | button_watch_threads = {} 63 | for pin in gpio_pins: 64 | button_watch_threads[pin] = threading.Thread(target=watch_button, args=[pin], name="ButtonWatch" + str(pin)) 65 | button_watch_threads[pin].start() 66 | 67 | -------------------------------------------------------------------------------- /tools/gpio_test.py: -------------------------------------------------------------------------------- 1 | # V0LT Predator 2 | # GPIO Test 3 | 4 | import os # Required to interact with certain operating system functions 5 | import json # Required to process JSON data 6 | import time 7 | 8 | from gpiozero import Button 9 | from signal import pause 10 | 11 | def on_press(): 12 | print("BUTTON PRESSED") 13 | 14 | def watch_button(pin, event=on_press): 15 | print("Watching pin " + str(pin)) 16 | button = Button(pin) 17 | 18 | time_pressed = 0 19 | last_triggered = 0 20 | while True: 21 | if (button.is_pressed and time_pressed == 0): # Check to see if the button was just pressed. 22 | print("Pressed") 23 | time_pressed = time.time() 24 | elif (button.is_pressed and time.time() - time_pressed < 1): # Check to see if the button is being held, but the time threshold hasn't been reached. 25 | print("Holding") 26 | elif (button.is_pressed and time.time() - time_pressed >= 1): # Check to see if the button is being held, and the time threshold has been reached. 27 | if (time.time() - last_triggered > 1): 28 | print("Triggered") 29 | event() 30 | last_triggered = time.time() 31 | elif (button.is_pressed == False): 32 | time_pressed = 0 33 | 34 | time.sleep(0.1) 35 | 36 | watch_button(17) 37 | -------------------------------------------------------------------------------- /tools/parked_event_motion_detect_test.py: -------------------------------------------------------------------------------- 1 | # This script allows you to test your motion detection settings with a real-time video stream. 2 | 3 | import os # Required to interact with certain operating system functions 4 | import json # Required to process JSON data 5 | import cv2 # Required to capture video. 6 | 7 | predator_root_directory = str(os.path.dirname(os.path.realpath(__file__))) # This variable determines the folder path of the root Predator directory. This should usually automatically recognize itself, but it if it doesn't, you can change it manually. 8 | 9 | try: 10 | if (os.path.exists(predator_root_directory + "/../config.json")): 11 | config = json.load(open(predator_root_directory + "/../config.json")) # Load the configuration database from config.json 12 | else: 13 | print("The configuration file doesn't appear to exist at " + predator_root_directory + "/../config.json.") 14 | exit() 15 | except: 16 | print("The configuration database couldn't be loaded. It may be corrupted.") 17 | exit() 18 | 19 | device = "main" 20 | 21 | if (device not in config["dashcam"]["capture"]["video"]["devices"]): 22 | print("The specified device does not exist in the configuration. Be sure to change the 'device' variable in the motion_detect_test.py file to the device you want to test.") 23 | exit() 24 | 25 | resolution = [config["dashcam"]["capture"]["video"]["devices"][device]["resolution"]["width"], config["dashcam"]["capture"]["video"]["devices"][device]["resolution"]["height"]] # This determines the resolution that will be used for the video capture device. 26 | #capture = cv2.VideoCapture(config["dashcam"]["capture"]["video"]["devices"][device]["index"]); # Open the video capture device. 27 | capture = cv2.VideoCapture("/home/cvieira/Downloads/NearbyLightning.m4v"); # Open the video capture device. 28 | codec = list(config["dashcam"]["capture"]["video"]["devices"][device]["codec"]) 29 | capture.set(cv2.CAP_PROP_FOURCC, cv2.VideoWriter_fourcc(codec[0], codec[1], codec[2], codec[3])) # Set the codec to be MJPEG. 30 | capture.set(cv2.CAP_PROP_FRAME_WIDTH,resolution[0]) # Set the video stream width. 31 | capture.set(cv2.CAP_PROP_FRAME_HEIGHT,resolution[1]) # Set the video stream height. 32 | 33 | background_subtractor = cv2.createBackgroundSubtractorMOG2() 34 | 35 | total_area = resolution[0] * resolution[1] # Calculate the total area of the frame. 36 | 37 | output = cv2.VideoWriter("./predator_motion_detect_test.avi", cv2.VideoWriter_fourcc(*'XVID'), 16, (resolution[0], resolution[1])) # Update the video output. 38 | 39 | while True: 40 | ret, frame = capture.read() 41 | gray = cv2.cvtColor(frame, cv2.COLOR_BGR2GRAY) 42 | fgmask = background_subtractor.apply(gray) 43 | kernel = cv2.getStructuringElement(cv2.MORPH_ELLIPSE, (5, 5)) 44 | fgmask = cv2.erode(fgmask, kernel, iterations=1) 45 | fgmask = cv2.dilate(fgmask, kernel, iterations=1) 46 | contours, hierarchy = cv2.findContours(fgmask, cv2.RETR_EXTERNAL, cv2.CHAIN_APPROX_SIMPLE) 47 | 48 | moving_area = 0 49 | for contour in contours: 50 | moving_area += cv2.contourArea(contour) 51 | if cv2.contourArea(contour) > 10: 52 | x, y, w, h = cv2.boundingRect(contour) 53 | cv2.rectangle(frame, (x, y), (x+w, y+h), (0, 255, 0), 2) 54 | 55 | moving_percentage = moving_area / total_area # Calculate the percentage of the frame that is in motion. 56 | moving_percentage_human = "{:.5f}%".format(moving_percentage*100) # Convert the moving percentage to a human-readable string. 57 | if (moving_area > 0): # Check to see if there is any movement at all. 58 | if (moving_percentage > float(config["dashcam"]["parked"]["event"]["trigger_motion"]["sensitivity"])): # Check to see if there is movement that exceeds the sensitivity threshold. 59 | print(str(moving_area) + "\t(" + str(format(moving_percentage_human)) + ")\tTriggered") # Display the movement as both a number and a percentage. 60 | else: 61 | print(str(moving_area) + "\t(" + str(format(moving_percentage_human)) + ")") # Display the movement as both a number and a percentage. 62 | 63 | cv2.putText(frame, moving_percentage_human, (10, 30), 2, 0.8, (0, 0, 0)) # Add the main overlay stamp to the video stream. 64 | output.write(frame) # Save this frame to the video file. 65 | cv2.imshow('Motion Detection', frame) 66 | 67 | if cv2.waitKey(1) == ord('q'): 68 | break 69 | 70 | capture.release() 71 | cv2.destroyAllWindows() 72 | -------------------------------------------------------------------------------- /tools/parked_event_object_recognition_test.py: -------------------------------------------------------------------------------- 1 | # This script allows you to test your object recognition settings with a real-time video stream. 2 | 3 | import os # Required to interact with certain operating system functions 4 | import json # Required to process JSON data 5 | import cv2 # Required to capture video. 6 | from ultralytics import YOLO 7 | import numpy 8 | 9 | predator_root_directory = str(os.path.dirname(os.path.realpath(__file__))) # This variable determines the folder path of the root Predator directory. This should usually automatically recognize itself, but it if it doesn't, you can change it manually. 10 | 11 | try: 12 | if (os.path.exists(predator_root_directory + "/../config.json")): 13 | config = json.load(open(predator_root_directory + "/../config.json")) # Load the configuration database from config.json 14 | else: 15 | print("The configuration file doesn't appear to exist at " + predator_root_directory + "/../config.json.") 16 | exit() 17 | except: 18 | print("The configuration database couldn't be loaded. It may be corrupted.") 19 | exit() 20 | 21 | 22 | model = YOLO("../assets/models/dashcam_model.pt") 23 | def predict(frame): 24 | global model 25 | results = model(frame) 26 | class_names = results[0].names 27 | 28 | detected_objects = [] # This is a placeholder that will hold all of the detected objects. 29 | for result in results: 30 | boxes = result.boxes 31 | for i in range(0, len(boxes)): 32 | obj = {} 33 | box = result.boxes[i].xyxy.numpy().tolist()[0] 34 | obj["bbox"] = {} 35 | obj["bbox"]["x1"] = round(box[0]) 36 | obj["bbox"]["y1"] = round(box[1]) 37 | obj["bbox"]["x2"] = round(box[2]) 38 | obj["bbox"]["y2"] = round(box[3]) 39 | obj["name"] = class_names[int(result.boxes[i].cls.numpy().tolist()[0])] 40 | obj["conf"] = result.boxes[i].conf.numpy().tolist()[0] 41 | detected_objects.append(obj) 42 | return detected_objects 43 | 44 | device = "main" 45 | 46 | if (device not in config["dashcam"]["capture"]["video"]["devices"]): 47 | print("The specified device does not exist in the configuration. Be sure to change the 'device' variable in the motion_detect_test.py file to the device you want to test.") 48 | exit() 49 | 50 | resolution = [config["dashcam"]["capture"]["video"]["devices"][device]["resolution"]["width"], config["dashcam"]["capture"]["video"]["devices"][device]["resolution"]["height"]] # This determines the resolution that will be used for the video capture device. 51 | #capture = cv2.VideoCapture(config["dashcam"]["capture"]["video"]["devices"][device]["index"]); # Open the video capture device. 52 | capture = cv2.VideoCapture("/home/cvieira/Downloads/ParkingEventDemo.mp4"); # Open the video capture device. 53 | codec = list(config["dashcam"]["capture"]["video"]["devices"][device]["codec"]) 54 | capture.set(cv2.CAP_PROP_FOURCC, cv2.VideoWriter_fourcc(codec[0], codec[1], codec[2], codec[3])) # Set the codec to be MJPEG. 55 | capture.set(cv2.CAP_PROP_FRAME_WIDTH,resolution[0]) # Set the video stream width. 56 | capture.set(cv2.CAP_PROP_FRAME_HEIGHT,resolution[1]) # Set the video stream height. 57 | 58 | background_subtractor = cv2.createBackgroundSubtractorMOG2() 59 | 60 | total_area = resolution[0] * resolution[1] # Calculate the total area of the frame. 61 | 62 | output = cv2.VideoWriter("./predator_object_recognition_test.avi", cv2.VideoWriter_fourcc(*'XVID'), 16, (resolution[0], resolution[1])) # Update the video output. 63 | 64 | 65 | while True: 66 | ret, frame = capture.read() 67 | 68 | detected_objects = predict(frame) 69 | for element in detected_objects: 70 | print(element["name"], element["conf"]) 71 | if (element["conf"] >= config["dashcam"]["parked"]["event"]["trigger_object_recognition"]["minimum_confidence"] and element["name"] in config["dashcam"]["parked"]["event"]["trigger_object_recognition"]["objects"]): # Check to see if this object is in the list of target objects. 72 | print("Detected event.") 73 | color = config["dashcam"]["parked"]["event"]["label"]["color"] 74 | cv2.rectangle(frame, (element["bbox"]["x1"], element["bbox"]["y1"]), (element["bbox"]["x2"], element["bbox"]["y2"]), (color[2], color[1], color[0]), 2) # Draw a box around the contour in the frame. 75 | 76 | cv2.imshow('Object Detection', frame) 77 | 78 | output.write(frame) # Save this frame to the video file. 79 | if cv2.waitKey(1) == ord('q'): 80 | break 81 | 82 | capture.release() 83 | cv2.destroyAllWindows() 84 | -------------------------------------------------------------------------------- /tools/video_sidecar_player.py: -------------------------------------------------------------------------------- 1 | # This script plays back a video file, and overlays information from its corresponding side-car file. 2 | 3 | 4 | import cv2 5 | import os 6 | import json 7 | import time 8 | 9 | 10 | file_path_video = input("Video file-path: ") 11 | 12 | overlay_color = { 13 | "plate": (0, 0, 255) 14 | } 15 | 16 | delay = 1/60 17 | playback_offset = 0 18 | pause = False 19 | 20 | if (os.path.exists(file_path_video)): 21 | capture = cv2.VideoCapture(file_path_video) 22 | file_path_sidecar = os.path.splitext(file_path_video)[0] + ".json" 23 | if (os.path.exists(file_path_sidecar)): 24 | file = open(file_path_sidecar) 25 | sidecar_data = json.load(file) 26 | file.close() 27 | 28 | frame_number = 0 29 | while True: 30 | if (str(frame_number-playback_offset) in sidecar_data): 31 | frame_data = sidecar_data[str(frame_number-playback_offset)] 32 | else: 33 | frame_data = {} 34 | if (pause == False): 35 | ret, frame = capture.read() 36 | cv2.putText(frame, "Offset: " + str(playback_offset), (0, 40), 2, 1.2, (0,0,0), 4) 37 | for plate in frame_data: 38 | x = frame_data[plate]["coordinates"]["x"] 39 | y = frame_data[plate]["coordinates"]["y"] 40 | w = frame_data[plate]["coordinates"]["w"] 41 | h = frame_data[plate]["coordinates"]["h"] 42 | bounding_box = [x, y, w, h] 43 | cv2.putText(frame, plate, (x, y), 4, 1.2, overlay_color["plate"], 4) 44 | cv2.rectangle(frame, bounding_box, color=overlay_color["plate"], thickness=2) 45 | cv2.imshow("Video", frame) 46 | pressed_key = cv2.waitKey(1) 47 | if (pressed_key == ord("q")): 48 | break 49 | elif (pressed_key == ord(",")): 50 | frame_number -= 30 51 | frame_number = max([frame_number, 0]) # Cap the frame position from going below 0. 52 | capture.set(cv2.CAP_PROP_POS_FRAMES, frame_number) # Jump to the new position. 53 | elif (pressed_key == ord(".")): 54 | frame_number += 30 55 | frame_number = max([frame_number, 0]) # Cap the frame position from going below 0. 56 | capture.set(cv2.CAP_PROP_POS_FRAMES, frame_number) # Jump to the new position. 57 | elif (pressed_key == ord("]")): 58 | delay = delay/2 # Speed up the video playback. 59 | elif (pressed_key == ord("[")): 60 | delay = delay*2 # Slow down the video playback. 61 | elif (pressed_key == ord("'")): 62 | playback_offset += 1 # Push the overlay forward. 63 | elif (pressed_key == ord(";")): 64 | playback_offset -= 1 # Push the overlay backward. 65 | elif (pressed_key == 32): # Check to see if the space bar was pressed. 66 | pause = not pause # Toggle the paused status. 67 | 68 | frame_number += 1 # Increment the frame count. 69 | time.sleep(delay) 70 | 71 | else: 72 | print("There is no side-car file associated with this video.") 73 | else: 74 | print("The specified file does not exist.") 75 | --------------------------------------------------------------------------------