Install ZoneMinder 1.36, Ubuntu 21.04 Part 7 — END

baudneo
8 min readOct 22, 2021

--

Other sections of this article.
* Part 1 — Install ZoneMinder 1.36.x
* Part 2 — NVIDIA drivers, Cuda 10.2, cuDNN 8.2.1, TPU Libs
* Part 3 — Build and Install OpenCV 4.5.4
* Part 4 — Configure objectconfig.ini and test ZMES local
* Part 5 — Build DLib (face recognition) and ALPR
* Part 6 — Install and test mlapi on the same host
* Part 7 — Install and test mlapi on a remote host (No ZMES)

Install mlapi onto a remote host without ZMES and test

This assumes some things -> You have an Ubuntu 21 04 system without Zoneminder or zmeventnotification Server. You have already built OpenCV 4.5.4, DLib and ALPR. Nvidia drivers 460.67 and Cuda 10.2 with cuDNN 8.2.1. The current user is named mlapi (has access to sudo) and will be the user that mlapi runs under. The IP for this MLAPI host will be 192.168.0.55.

You should have all the tools from the ‘ZMES and MLAPI on the same host’ installation, basically.

# When TPU libs were installed was the user added to the plugdev group? Remember the Linux username is mlapisudo usermod -aG plugdev mlapi# For log rotation
sudo apt install logrotate
cd ~mkdir git && cd git# First get neo-pyzm and installgit clone https://github.com/baudneo/pyzm.gitcd pyzmsudo ./install.shcd ~/git# Install the bjoern dependancies (and shapely sudo apt install libev-dev libevdev2 libgeos-dev# Clone the mlapi repogit clone https://github.com/baudneo/mlapi.gitcd mlapi
sudo pip3 -H install requirements.txt# Now we create a MLAPI user (What ZMES uses to request detections from mlapi) In this example the user will be named 'zmes_1' and the password will be 'test123'
# for help use: python3 mlapi_dbuser.py --help
python3 mlapi_dbuser.py -u zmes_1 -p test123#User: zmes_1 created# If you get an error about ->
# FileNotFoundError: [Errno 2] No such file or directory: './db/db.json'
mkdir db
mkdir logs
mkdir models
# create the user after creating the db dirpython3 mlapi_dbuser.py -u mlapitest -p test123

Now you need to choose a place to have mlapi installed, I do not recommend running mlapi directly from the cloned git repo folder. I am going to install the mlapi folder in the home directory of the mlapi user.

cd ~/git
cp -r mlapi ~
cd ~/mlapi

Configure mlapi to test

First, create an encryption key if you do not have an encryption key for this mlapi route already set up in ZMES (ZMES is on a separate host). For this example, we will name this route ‘test 2’.

python3 get_encryption_key.py# This is provided to generate a predefined key for encrypting credentials sent between zmeventnotification and mlapi.# You can run this as many times as you want, it does not 'remember' anything.# See default configuration for description and example. Encryption key -->imja5OSE-1zzIbqxy4k1V1yt5WNysARoOuvOtWT9qXE=

Encryption key -> imja5OSE-1zzIbqxy4k1V1yt5WNysARoOuvOtWT9qXE=

Download the models

# To download all models except for TPU./get_models.sh# Download all models including TPUINSTALL_CORAL_EDGETPU=yes ./get_models.sh

Now open the secrets.ini file and change the MLAPI_SECRET_KEY to some random string, this is for signing/minting JWT auth tokens. Then open mlapiconfig.ini to activate bjoern as the WSGI instead of Flask.

nano secrets.ini
# enter a random string for the mlapi_secret_key secret, save and exit.
# Open mlapiconfig.ininano mlapiconfig.ini# Uncomment the ;wsgi_server=bjoern line so that we use bjoern instead of Flask.wsgi_server = bjoern

While editing mlapiconfig.ini, create a new entry in zmes_keys, name it ‘test 2' and place the encryption key in the value section. MAKE SURE that the encryption key is enclosed in single or double quotes!

zmes_keys = {
# use different keys for each route!
# Format -> 'name of key/route/zmes host': '<key>',
# notice how in the example objectconfig.ini in ml_routes there is an entry for mlapi_one and both keys are the same
# NAME and its KEY must match in objectconfig.ini !
'mlapi_one': 'SGqZkIaXqotqZb6tZzGhqL-1E5jrbX--kNMrnGDWRMU=','test 2': 'imja5OSE-1zzIbqxy4k1V1yt5WNysARoOuvOtWT9qXE=',;'warehouse': 'run get_encryption_key.py for an encryption key and use the same key here and in objectconfig.ini ml_routes',
;'main office': 'run get_encryption_key.py for an encryption key and use the same key here and in objectconfig.ini ml_routes',
} # Always keep the closing brace indented

Now configure the log_user and log_group (only configure log_group if the group is different from the username)

# Find 'log_user' and change the name to mlapi
log_user=mlapi

Go through the rest of mlapiconfig and change any options you want. AN IMPORTANT THING TO DO is to change all the PATH options to ABSOLUTE paths. For example, base_data_path=. by default, change that to /home/mlapi/mlapi . Go through the file and try and change most of the base path options to absolute paths.

Now, go to your ZMES host and open /etc/zm/secrets.ini to add the encryption key for the new route ‘test 2’ and configure the secrets for the mlapi test ->

# Open /etc/zm/secrets.ini and configure the secrets.
# I am adding the secret 'test_2_key' to the secrets file.
# This is the user created in MLAPI DB, I reccomend seperate mlapi users for each ZMES host. ML_USER = zmes_1
ML_PASSWORD = test123
# The ZM API user that mlapi will use to get the images and event info, I am using the same user that ZMES used for the test. In production I reccomend to create an API user for each mlapi instance.ZM_ML_USER=testuser
ZM_ML_PASSWORD=test123
# Encryption keys for ml_routesmlapi_one_key = SGqZkIaXqotqZb6tZzGhqL-1E5jrbX--kNMrnGDWRMU=test_2_key = imja5OSE-1zzIbqxy4k1V1yt5WNysARoOuvOtWT9qXE=# Save and exit

Next, open/etc/zm/objectconfig.ini to enable ml_routes by configuring ml_enable = yesand then creating a new ml_route for ‘test 2’.

# In this example the IP address of mlapi 'test 2' will be 192.168.0.55ml_enable=yes# add this route to ml_routes{      'weight': 1,
# 'weight': 0,
'name': 'test 2',
'gateway': 'http://192.168.0.55:5000/api/v1',
'user': '{[ML_USER]}',
'pass': '{[ML_PASSWORD]}',
'zm_user': '{[ZM_ML_USER]}',
'zm_pass': '{[ZM_ML_PASSWORD]}',
'zm_basic_user': '',
'zm_basic_pass': '',
'enc_key': '{[test_2_key]}',
},

Notice that the name of the ml_route is ‘test 2’ in objectconfig.ini on the ZMES host and the name of the encryption key is ‘test 2' in mlapiconfig.ini on the MLAPI host. THIS IS IMPORTANT, if the names don’t match mlapi will not be able to grab the encryption key to decrypt the credentials and URL it needs to reply. Also, if the encryption keys are not the same, mlapi will not be able to properly decrypt the data!

MLAPI Shell Aliases and adding the user to the sudoers file

Enable mlapi user to use sudo tail -F /var/log/syslog and sudo systemctl without a password

sudoedit /etc/sudoers# Find the line starting with -> %sudo ALL=(ALL:ALL)
# This allows the shell aliases to run without needing to input a password for sudo.
%mlapi ALL=(root:root) NOPASSWD: /usr/bin/tail, /usr/bin/systemctl

Install ‘bat the cat replacement’ and then copy these aliases into your shell config file.

# First download and install bat, I have my logging aliases setup to use bat for pretty log output! Use mlapi.log to watch the log files for detectionswget https://github.com/sharkdp/bat/releases/download/v0.18.3/bat-musl_0.18.3_amd64.debsudo apt install./bat-musl_0.18.3_amd64.deb# Open your shell config file (.bashrc, .zshrc, etc.)
# Add these mlapi alaises, I changed the paths to match where mlapi is installed
alias mlapi.start='python3 /home/mlapi/mlapi/mlapi.py --config /home/mlapi/mlapi/mlapiconfig.ini &'
alias mlapi.debug='python3 /home/mlapi/mlapi/mlapi.py --config /home/mlapi/mlapi/mlapiconfig.ini --debug &'
alias mlapi.baredebug='python3 /home/mlapi/mlapi/mlapi.py --config /home/mlapi/mlapi/mlapiconfig.ini --baredebug &'
# use the -d or --debug flag to enable printing to the console, if you already are monitoring the logs using the cat/bat
# commands above use the -bd --baredebug option to not print output to the console (or use this option when you see
# double output to the console)

#alias mlapi.log='tail -F /home/mlapi/mlapi/logs/zm_mlapi.log &'
#alias mlapi.syslog="sudo tail -F /var/log/syslog &"

#IF YOU HAVE DOWNLOADED 'bat - the cat replacement' uncomment these 2 lines instead of the 2 above
alias mlapi.log='tail -F /home/mlapi/mlapi/logs/zm_mlapi.log | bat --paging never -l log --style changes,header,rule,snip &'
alias mlapi.syslog="sudo tail -F /var/log/syslog | bat --paging never -l log --style changes,header,rule,snip --theme 'Solarized (dark)' &"


#-- The mlapi.init aliases assume you have mlapi installed as a service and
# can run 'sudo systemctl <start/stop/status/restart> mlapi.service' without entering a password.

alias mlapi.init.start='sudo systemctl start mlapi.service &'
alias mlapi.init.restart='sudo systemctl restart mlapi.service &'
alias mlapi.init.stop='sudo systemctl stop mlapi.service &'
alias mlapi.init.status='sudo systemctl status mlapi.service &'

The mlapi.init.x commands will not work until we install mlapi as a service, for now, we will test by running mlapi.debug and then running a detection back on the ZMES host.

mlapi.debug
# Everything should be working alright, the output should end with something like this ->
10/22/21 03:15:11.636228 zm_mlapi[2141] INF mlapi:661 [|*** Machine Learning API (mlapi) version: 3.0.0 - pyzm version: 0.30.02 ***|]10/22/21 03:15:11.638221 zm_mlapi[2141] INF mlapi:672 [mlapi: using bjoern as WSGI server @ '0.0.0.0:5000']# Run a detection on the ZMES host and watch logs on both systems to make sure all is working well

Train Faces

Relevant docs from the original author of ZMES -> Here

# You need to have a folder with some decent headshots for training the face model.# Example, faces for a person named Jeffmkdir /home/mlapi/mlapi/known_faces/jeff# Place all the photos of Jeff in the mlapi/known_faces/jeff/ directory and then traincd /home/mlapi/mlapisudo -u www-data ./mlapi_face_train.py -c mlapiconfig.ini -d# Should print to console and end with something similar ->10/20/21 23:55:55.057296 zm_mlapi[14047] DBG1 face_train_dlib:160[mlapi:face-train: wrote encoding file: /home/mlapi/mlapi/known_faces/faces.dat]10/20/21 23:55:55.077544 zm_mlapi[14047] DBG1 face_train_dlib:162->[perf: Face Recognition training took: 12943.44 ms]# You can delete the faces.dat and retrain if you want to add more photos or different people.

MLAPI as a systemD service, Log rotation

Now to install mlapi as a systemd service and set up the log rotation for mlapi logs.

sudo nano /etc/logrotate.d/mlapi# Put this in the file, save and exit/home/mlapi/mlapi/logs/zm_mlapi*.log {missingok
# su <user that runs mlapi> <group>
su mlapi mlapi
copytruncate
notifempty
sharedscripts
delaycompress
compress
daily
rotate 7
maxage 7
}# That takes care of the mlapi log rotation.------------------------------------------------------------# Now to install as a service. mlapi is the user that mlapi runs under.nano /home/mlapi/mlapi/mlapi.service# Edit the file to conform to this install ->[Unit]
Description=Machine Learning API service
After=network.target
StartLimitIntervalSec=3
[Service]
Type=simple
Restart=always
RestartSec=5
# We need this to get logs correctly
Environment
=PYTHONUNBUFFERED=1
# change this
WorkingDirectory
=/home/mlapi/mlapi
# Change to your username
User
=mlapi
#Change paths if needed
ExecStart
=/home/mlapi/mlapi/mlapi.py -c /home/mlapi/mlapi/mlapiconfig.ini
# These may have no effect, keep them anyways!
StandardOutput
=file:/home/mlapi/mlapi/logs/zm_mlapi.log
StandardError=file:/home/mlapi/mlapi/logs/zm_mlapi.log
[Install]
WantedBy=multi-user.target
# Save and exit, INSTALLcd /home/mlapi/mlapisudo cp /home/mlapi/mlapi/mlapi.service /etc/systemd/systemsudo systemctl daemon-reloadsudo systemctl enable mlapi.service# If mlapi is not running already, use the init alias, they are short commands for the systemctl commands->mlapi.init.start # If you did not add the handy aliases from mlapi/tools/TERM_helpers.txt ->sudo systemctl start mlapi.service# mlapi should be auto started on every reboot!# Start breaking things and let me know!

--

--