asmirnou / watsor

Object detection for video surveillance
MIT License
254 stars 32 forks source link
camera coral cuda detection ffmpeg gpu hardware-acceleration homeassistant ip mpegts mqtt person-detector python realtime stream surveillance tensorrt tensrflow video zones

logo

Watsor

Watsor detects objects in video stream using deep learning-based approach. Intended primarily for surveillance it works in sheer real-time analysing the most recent frame to deliver the fastest reaction against a detected threat.

Table of contents

What it does

Being applicable in CCTV, Watsor also suits other areas, where object detection in video stream is required.

Getting started

Watsor is written in Python 3 and ships mainly as Python module. The easiest way, however, is trying it in Docker as far as hardware drivers and models are bundled in the images.

Regardless of how you run Watsor, you need to prepare configuration file, describing the cameras you've got and few other options such as types of detections and zones. Refer to the guide below.

Configuration

Watsor uses the YAML syntax for configuration. The basics of YAML syntax are block collections and mappings containing key-value pairs. Each item in a collection starts with a - while mappings have the format key: value. Watsor performs config validation and, if you specified duplicate keys, missed some values or have wrong YAML syntax, prints the error message at the start.

Please find an example configuration file with explanatory comments that you can use as a template.

Cameras

cameras block in document root is a list of cameras you gonna use for surveillance. Each camera must have unique name distinguishing it from others. The name of the camera appears in logs and also is included in path to a video stream over HTTP.

Expand code snippet ```yaml cameras: - camera1: width: 640 height: 480 input: rtsp://192.168.0.10:554 #output: !ENV "${HOME}/Videos/camera1.m3u8" #mask: camera1.png #detect: [] #ffmpeg: [] - ... ```

A camera must specify width, height and input source of a video feed. Refer to the camera setting to figure out the proper values. width and height are supposed to reflect the resolution of the actual video feed. They don’t change the size of the video, but are used to configure the frame buffers and other things needed for Watsor to work. If width or height values do not match video feed, the image can’t be displayed correctly. The input source URL usually starts with rtsp:// or http://, but can be anything FFmpeg can consume, a .mp4 file for instance.

Optional mask is a filename of a mask image used to limit detection zone. Refer to the following section.

Optional detect block overrides the defaults to specify the types of objects of interest and set of filters to sift less significant detections.

Optional ffmpeg block overrides the defaults to specify the arguments for FFmpeg application used to decode input video stream and also to encode the output when necessary. ffmpeg.encoder is absolutely optional, define it only if you'd like to record or broadcast video stream with rendered object detections.

When ffmpeg.encoder is used to record video file, use output key to specify the location. Refer to FFmpeg formats for all available options. When output key is absent, Watsor runs lightweight HTTP server for streaming the video over the network. The streaming is performed in MPEG-TS format, so make sure mpegts format is set in ffmpeg.encoder output arguments. When output is set the encoded stream is being written to file and the broadcast in MPEG-TS over HTTP is not possible.

Regardless of whether or not ffmpeg.encoder is enabled and output is defined, the broadcast of video stream in Motion JPEG format over HTTP is available all the time.

FFmpeg decoder and encoder

ffmpeg block present in document root is used as defaults for decoding and encoding a video stream if a camera does not set its own.

FFmpeg is a very fast video converter that grabs a live video from source. Watsor runs one or two FFmpeg subprocesses for each camera: mandatory decoder and optional encoder. The decoder reads from the source specified by the camera's input key (which can be an URL of camera feed, a regular file or a grabbing device), following the -i option. The encoder writes to the output file, if camera's output is set, or stdout, if not. The options in the command line before and after -i determine the parameters for input and output of each FFmpeg subprocess. The input parameters, for example, can enable hardware accelerated decoding. The output of decoder and the input of encoder must include -f rawvideo -pix_fmt rgb24 as Watsor reads raw video frames (24-bit) from first FFmpeg's stdout and after detection complete can write raw video frames in stdin of another FFmpeg subprocess.

You must include -i option in the command line list not followed by the input as Watsor includes camera's input key after -i automatically. For encoder it also includes other required options such as -s to define explicitly the size of raw video frames.

Expand code snippet ```yaml ffmpeg: decoder: - ... - -i - -f - rawvideo - -pix_fmt - rgb24 encoder: - -f - rawvideo - -pix_fmt - rgb24 - -i - -f - mpegts - ... ```

ffmpeg.encoder is optional and intended for the recording or broadcasting of video stream with rendered object detections. The recording is suitable for on demand streaming as the video is stored in a file such as .m3u8 (HLS) or .mp4 and can be re-watched. The broadcasting means the video is being recorded in real time and the viewer can only watch it as it is streaming. To broadcast the video with rendered object detections over HTTP set -f mpegts option in the encoder and remove the camera's output key. The link to the video stream can be grabbed from Watsor's home page and opened in media player such as VLC.

When broadcasting live video stream in MPEG-TS be aware of noticeable latency, which is unavoidable due to the nature of video encoding algorithm. Bear in mind the media player introduces a latency of its own as it buffers the feed. To watch the video with rendered object detections in sheer real-time with zero latency open Motion JPEG URL of the camera feed.

Broadcasting in MPEG-TS format is perfect for streaming over the network as the video sequencing compression transports only the changes in the sequence thus uses less network bandwidth (and storage). Motion JPEG compresses each video frame into a JPEG image making them available as a continuous flow of images over a network. As all frames are compressed independently and entirely, the amount of data transmitted across the network is far more than that of MPEG-TS.

Detection classes and filters

detect block present in document root is used as defaults if a camera does not set its own. The block is a list of COCO classes (90 at max) used for detection each specifying three filters: area, confidence and zones.

Expand code snippet ```yaml detect: - person: area: 20 confidence: 60 zones: [1, 3, 5] - car: - ... ```

Zones and masks

To limit detection zones take a snapshot from your camera. The width and height of the image must be of the same size as your video feed.

Open the image in graphics editor such as GNU Image Manipulation Program (GIMP) and select the desired zone. Make the selection floating, then create a new layer from the given floating selection. That layer represents a zone, outside of which the objects are not being detected.

Select main layer with the rest of the image and change the opacity of the layer to ~85%. The opacity of the layers from floating selection must remain 100% to let Watsor distinct them from the background. Save image in PNG 32-bit format with alpha channel. Specify the file name and path (can be relative) in camera configuration.

The following depicts said above:

You can select many zones and of any shape, but make sure they don't overlap, otherwise several zones merge in one.

When the bounding box of an object intersects with the shape of a zone, detection occurs and the zone highlights in yellow. The index of a zone is then transmitted over MQTT. The zones are indexed in mask image depending on the proximity of their center to the origin. On the sample above there are two zones and a car was detected in 2 (shown in red). If not very clear of what zone is the index, the following tool will display it for you:

python3 -m watsor.zones -m config/porch.png

Tips

Environment variables

You can include values from your system’s environment variables either like Home Assistant Core does:

password: !env_var PASSWORD default_password or

input: !ENV "rtsp://${RTSP_USERNAME}:${RTSP_PASSWORD}@192.168.0.10:554"

Secrets

You can remove any private information from your configuration file. The entries for password can be replaced with !secret keyword followed by an identifier. The password will be then looked at secrets.yaml file containing the corresponding password assigned to the identifier:

config.yaml ```yaml mqtt: username: !secret mqtt_username password: !secret mqtt_password ```
secrets.yaml ```yaml mqtt_username: "john" mqtt_password: "qwerty" ```

A secrets.yaml will be resolved first by looking in the same folder as the YAML file referencing the secret, next, parent folders will be searched for a secrets.yaml file. The logic inherits from Home Assistant and you can place your config along with HA files reusing single secrets.yaml file for both apps.

HomeAssistant integration

The easiest way to get started is using Watsor add-ons for Home Assistant.

Watsor can communicate with HomeAssistant via MQTT - "Internet of Things" connectivity protocol. Please refer to demo project to examine the configuration files.

To configure optional MQTT client add the following lines (expand the snippet): ```yaml mqtt: host: localhost #port: 1883 #username: !secret mqtt_username #password: !secret mqtt_password ```

List of MQTT topics:

The binary state (ON / OFF) of a detected object class is published at /detection/{class}/state topic, confirming the state every 10 seconds. This signals about a detected threat and is supposed to trigger an alarm.

Subscribe to the topic available to receive availability (online/offline) updates about specific camera. The camera is online when Watsor starts and goes offline when the application stops.

The camera can be controlled via topic command allowing to start/stop the decoder, limit frame rate and enable/disable the reporting of detection details.

The topic sensor is used to send the updates about camera current input and output speeds. Monitoring the speed is useful to trigger the alert, if camera is broken or disconnected suddenly.

The sensor values and detection details can be transmitted over MQTT very often up to tens times per second. The recorder integration in HomeAssistant constantly saves data, storing everything by default from sensors to state changes. Fortunately, HomeAssistant allows to customize what needs to be written and not. A good idea is to include in recoder only those measurements that are really needed to avoid degradation of HomeAssistant's performance.

Running Watsor

Docker

To run Watsor in Docker mount configuration files at /etc/watsor folder of a container. Add host devices for video and detection hardware acceleration (if available). As far as Watsor runs several processes, which share the memory, increase the default shared memory size of 64m to a bit more depending on the number of cameras. Here is example of docker-compose.yaml:

Expand code snippet ```yaml version: '3' services: watsor: container_name: watsor image: smirnou/watsor:latest environment: - LOG_LEVEL=info volumes: - /etc/localtime:/etc/localtime:ro - ../config:/etc/watsor:ro devices: - /dev/bus/usb:/dev/bus/usb - /dev/dri:/dev/dri ports: - 8080:8080 shm_size: 512m ```

To run GPU accelerated watsor.gpu Docker image use the NVIDIA Container Toolkit. Pass --gpus all flag to add GPU devices to the container:

Expand code snippet ```bash docker run -t -i \ --rm \ --env LOG_LEVEL=info \ --volume /etc/localtime:/etc/localtime:ro \ --volume $PWD/config:/etc/watsor:ro \ --device /dev/bus/usb:/dev/bus/usb \ --device /dev/dri:/dev/dri \ --publish 8080:8080 \ --shm-size=512m \ --gpus all \ smirnou/watsor.gpu:latest ```

If your GPU supports Half precision (also known as FP16), you can boost performance by enabling this mode as follows:

docker run --gpus all --env TRT_FLOAT_PRECISION=16 ...

Models for CPU/GPU and EdgeTPU (Coral) are bundled in Docker images. You can use your own models, trained based on those listed in object detection models section, by mounting the volume at /usr/share/watsor/model.

The following table lists the available docker images:

Image Suitable for
watsor x86-64
watsor.gpu x86-64 with Nvidia CUDA GPU
watsor.jetson Jetson devices (Xavier, TX2, and Nano)
watsor.pi3 Raspberry PI 3 or 4 with 32-bit OS
watsor.pi4 Raspberry PI 4 with 64-bit OS

Kubernetes

To deploy Watsor on Kubernetes cluster use Helm chart:

helm repo add asmirnou https://asmirnou.github.io/watsor-helm-chart
helm repo update
helm install watsor asmirnou/watsor

Python module

Watsor works well on Pyhton 3.6, 3.7, 3.8. Use a virtual environment when installing Python packages.

  1. Install module:

    python3 -m pip install watsor

    If you've got a hardware accelerator or are installing the application on a tiny board like Raspberry Pi, take a look at extra profiles coral, cuda or lite in setup.py. The dependencies listed in those profiles need to be installed in advance. Refer to the documentation of the device or take a look at one of the Docker files to understand how the dependencies are installed.

  2. Create model/ folder, download, unpack and prepare the object detection models (see the section below).

  3. Write the config file by following the guide above, take this config as an example.

  4. Make sure ffmpeg is installed on your operating system.

  5. Run watsor as follows:

    python3 -m watsor.main --config config/config.yaml --model-path model/

    or if NVIDIA CUDA GPU is present and properly set up:

    python3 -m watsor.main_for_gpu --config config/config.yaml --model-path model/

Open http://localhost:8080 to navigate to a simple home page, where you'll find the links to the cameras video streams, snapshots of object detected classes and metrics.

Object detection models

Watsor uses convolutional neural network trained to recognize 90 classes of object. The detection model has several types providing the trade-off between accuracy and speed. For example, MobileNet v1 is the fastest, but less accurate than Inception v2. The models recommended below are reasonable for video surveillance in real-time, however you are not limited to use only these models as Watsor supports any one from TensorFlow Model Garden. It's worth trying their newest models with higher average precision.

The models are available in several formats depending on the device the inference is being performed on.

Device Filename MobileNet v1 MobileNet v2 Inception v2
Coral model/edgetpu.tflite MobileNet v1 MobileNet v2 N/A
Nvidia CUDA GPU model/gpu.uff MobileNet v1 MobileNet v2 Inception v2
CPU model/cpu.pb MobileNet v1 MobileNet v2 Inception v2
Raspberry Pi & others model/cpu.tflite MobileNet v1 MobileNet v2 N/A

Hardware acceleration drivers

Use of hardware accelerator is optional, but highly recommended as object detection as such requires much computation. Speaking of number of frames per second the CPU of a desktop computer can process just 24 FPS of the lightest model, but an accelerator can boost the performance up to 5 times. Two accelerators connected or installed can handle 200 FPS, which is more than enough for handling several video cameras.

Watsor supports multiple accelerators, equally balancing the load among them. Having at least one accelerator connected Watsor uses the hardware for computation, less loading the CPU. It falls over to CPU running Tensorflow if no accelerator is available.

To restrict the devices that Watsor sees set CORAL_VISIBLE_DEVICES or CUDA_VISIBLE_DEVICES environmental variables to a comma-separated list of device IDs to make only those devices visible to the application. The device ID can be known from application logs after enabling debug mode with the following command-line option: --log-level debug

If you've got the Coral install the Edge TPU runtime and PyCoral API.

Have Nvidia CUDA GPU on board - install the following components (now the hard part, you'd better consider Docker:

Building from source

Clone Git repository:

git clone https://github.com/asmirnou/watsor.git

Create virtual environment and install Watsor dependencies:

make venv

source venv/bin/activate

make install

Troubleshooting

If you encounter issues installing Watsor prerequisites, try to seek for solution over the web first. I'm glad to help, but most of the times the answer is being composed from the findings on https://stackoverflow.com/ and etc. Be bold and file an issue having not found a solution.

Credits

License

MIT License