Include openalpr plugin

This commit is contained in:
Emmanuel Papin 2015-07-21 16:53:32 +02:00
parent b294569147
commit 9ee7d67056
31 changed files with 1088 additions and 11 deletions

View File

@ -5,6 +5,7 @@
cmake_minimum_required (VERSION 2.6)
project (zoneminder)
set(zoneminder_VERSION "1.28.100")
# Engine version is used for external plugin compilation
set(ZM_ENGINE_VERSION 29)
# make API version a minor of ZM version
set(zoneminder_API_VERSION "${zoneminder_VERSION}.1")
@ -64,12 +65,14 @@ set(CMAKE_MODULE_PATH ${CMAKE_MODULE_PATH} "${CMAKE_SOURCE_DIR}/cmake/Modules/")
# Modules that we need:
include (GNUInstallDirs)
include (CheckIncludeFile)
include (CheckIncludeFileCXX)
include (CheckIncludeFiles)
include (CheckFunctionExists)
include (CheckPrototypeDefinition_fixed)
include (CheckTypeSize)
include (CheckStructHasMember)
include (CheckSendfile)
include (FindPkgConfig)
# Configuration options
mark_as_advanced(
@ -82,6 +85,7 @@ mark_as_advanced(
ZM_TARGET_DISTRO
ZM_CONFIG_DIR
ZM_PLUGIN_SUPPORT
ZM_PLUGIN_COMPIL
ZM_PLUGINSLIBDIR
ZM_PLUGINSPKGLIBDIR
ZM_PLUGINSCONFDIR
@ -149,17 +153,20 @@ set(ZM_PERL_SEARCH_PATH "" CACHE PATH
set(ZM_TARGET_DISTRO "" CACHE STRING
"Build ZoneMinder for a specific distribution. Currently, valid names are: f21, f20, el6, OS13")
set(ZM_PLUGIN_SUPPORT "OFF" CACHE BOOL
"Set to ON to enable plugins support. This is EXPERIMENTAL. default: OFF")
"Set to ON to enable plugin support in ZoneMinder. This is EXPERIMENTAL, default: OFF")
set(ZM_PLUGIN_COMPIL "OFF" CACHE BOOL
"Set to ON to enable compilation of internal plugins. This is EXPERIMENTAL, default: OFF")
if(ZM_PLUGIN_SUPPORT)
set(ZM_PREFIX ${CMAKE_INSTALL_PREFIX})
set(ZM_PLUGINSLIBDIR "\${prefix}/${CMAKE_INSTALL_LIBDIR}" CACHE PATH
"Location of the plugin files, default: ${CMAKE_INSTALL_LIBDIR}")
"Location of the plugin development library, default: ${CMAKE_INSTALL_LIBDIR}")
set(ZM_PLUGINSPKGLIBDIR "${CMAKE_INSTALL_PREFIX}/${CMAKE_INSTALL_LIBEXECDIR}/zoneminder/plugins" CACHE PATH
"Location of the plugin files, default: <prefix>/${CMAKE_INSTALL_LIBEXECDIR}/zoneminder/plugins")
"Location of the plugin file, default: <prefix>/${CMAKE_INSTALL_LIBEXECDIR}/zoneminder/plugins")
set(ZM_PLUGINSCONFDIR "/${CMAKE_INSTALL_SYSCONFDIR}/plugins.d" CACHE PATH
"Location of the plugin files, default: /${CMAKE_INSTALL_SYSCONFDIR}/plugins.d")
"Location of the plugin configuration files, default: /${CMAKE_INSTALL_SYSCONFDIR}/plugins.d")
set(ZM_PLUGINSWEBDIR "${CMAKE_INSTALL_PREFIX}/${CMAKE_INSTALL_DATADIR}/zoneminder/plugins" CACHE PATH
"Location of the plugin files, default: <prefix>/${CMAKE_INSTALL_DATADIR}/zoneminder/plugins")
"Location of the plugin web files, default: <prefix>/${CMAKE_INSTALL_DATADIR}/zoneminder/plugins")
set(ZM_PROJECTNAME ${CMAKE_PROJECT_NAME})
set(ZM_PLUGINSEXT ".so" CACHE STRING
"Plugin extension, default: .so")
@ -534,6 +541,29 @@ if(NOT ZM_NO_LIBVLC)
endif(LIBVLC_LIBRARIES)
endif(NOT ZM_NO_LIBVLC)
if(ZM_PLUGIN_COMPIL)
if(PKG_CONFIG_FOUND)
pkg_search_module(opencv opencv>=2.3.1)
if(NOT opencv_FOUND)
message(FATAL_ERROR "opencv.pc not found")
endif(NOT opencv_FOUND)
else(PKG_CONFIG_FOUND)
message(FATAL_ERROR "pkg-config not found")
endif(PKG_CONFIG_FOUND)
find_package(OpenCV COMPONENTS core imgproc highgui objdetect REQUIRED)
if(OpenCV_FOUND)
include_directories(${OpenCV_INCLUDE_DIR})
else(OpenCV_FOUND)
message(FATAL_ERROR "Cannot build application without OpenCV. Please set OpenCV_INCLUDE_DIR.")
endif(OpenCV_FOUND)
find_package(Boost COMPONENTS program_options REQUIRED)
if(Boost_FOUND)
include_directories(${Boost_INCLUDE_DIR})
else(Boost_FOUND)
message(FATAL_ERROR "Cannot build application without Boost. Please set Boost_INCLUDE_DIR.")
endif(Boost_FOUND)
endif(ZM_PLUGIN_COMPIL)
# *** END OF LIBRARY CHECKS ***
# Check for gnutls or crypto
@ -622,6 +652,29 @@ if(HAVE_LIBCRYPTO AND HAVE_OPENSSL_MD5_H AND HAVE_MD5_OPENSSL)
set(HAVE_GNUTLS_OPENSSL_H 0)
endif(HAVE_LIBCRYPTO AND HAVE_OPENSSL_MD5_H AND HAVE_MD5_OPENSSL)
if(ZM_PLUGIN_COMPIL)
check_include_file_cxx("alpr.h" HAVE_ALPR_H)
if(NOT HAVE_ALPR_H)
message(FATAL_ERROR "Missing include file alpr.h")
endif(NOT HAVE_ALPR_H)
check_include_file_cxx("opencv2/core/core.hpp" HAVE_OPENCV2_CORE_CORE_HPP)
if(NOT HAVE_OPENCV2_CORE_CORE_HPP)
message(FATAL_ERROR "Missing include file opencv2/core/core.hpp")
endif(NOT HAVE_OPENCV2_CORE_CORE_HPP)
check_include_file_cxx("opencv2/imgproc/imgproc.hpp" HAVE_OPENCV2_IMGPROC_IMGPROC_HPP)
if(NOT HAVE_OPENCV2_IMGPROC_IMGPROC_HPP)
message(FATAL_ERROR "Missing include file opencv2/imgproc/imgproc.hpp")
endif(NOT HAVE_OPENCV2_IMGPROC_IMGPROC_HPP)
check_include_file_cxx("boost/algorithm/string.hpp" HAVE_BOOST_ALGORITHM_STRING_HPP)
if(NOT HAVE_BOOST_ALGORITHM_STRING_HPP)
message(FATAL_ERROR "Missing include file boost/algorithm/string.hpp")
endif(NOT HAVE_BOOST_ALGORITHM_STRING_HPP)
check_include_file_cxx("boost/program_options.hpp" HAVE_BOOST_PROGRAM_OPTIONS_HPP)
if(NOT HAVE_BOOST_PROGRAM_OPTIONS_HPP)
message(FATAL_ERROR "Missing include file boost/program_options.hpp")
endif(NOT HAVE_BOOST_PROGRAM_OPTIONS_HPP)
endif(ZM_PLUGIN_COMPIL)
# Check for Perl
find_package(Perl)
if(NOT PERL_FOUND)
@ -718,6 +771,11 @@ if(ZM_ONVIF)
add_subdirectory(onvif)
endif(ZM_ONVIF)
# Enable compilation of internal plugins
if(ZM_PLUGIN_COMPIL)
add_subdirectory(plugins)
endif(ZM_PLUGIN_COMPIL)
# Process distro subdirectories
if((ZM_TARGET_DISTRO STREQUAL "f21") OR (ZM_TARGET_DISTRO STREQUAL "f20"))
add_subdirectory(distros/fedora)

View File

@ -13,13 +13,18 @@ if COND_ONVIF
MAYBE_ONVIF = onvif
endif
if ZM_PLUGIN_COMPIL
MAYBE_PLUGINS = plugins
endif
SUBDIRS = \
src \
web \
scripts \
db \
misc \
$(MAYBE_ONVIF)
$(MAYBE_ONVIF) \
$(MAYBE_PLUGINS)
EXTRA_DIST = \
zm.conf.in \

View File

@ -221,6 +221,7 @@ AC_ARG_WITH(cgidir,
AC_SUBST(CGI_PREFIX)
ENABLE_PLUGIN_SUPPORT=no
ENABLE_PLUGIN_COMPIL=no
AC_ARG_ENABLE([plugin-support],
[ --enable-plugin-support=<yes|no> enable or disable plugin support, default disabled],
[ENABLE_PLUGIN_SUPPORT=$enable_plugin_support],
@ -236,6 +237,14 @@ if test "$ENABLE_PLUGIN_SUPPORT" == "yes"; then
If you really want plugin support you must not call configure with
--enable-shared=no or --disabled-shared])
fi
AC_ARG_ENABLE([plugin-compilation],
[ --enable-plugin-compilation=<yes|no> enable or disable plugin compilation, default disabled],
[ENABLE_PLUGIN_COMPIL=$enable_plugin_compilation],
[AC_MSG_WARN([You can call configure with the --enable-plugin-compilation=<yes|no>
or --disable-plugin-compilation option.
This tells configure whether to compile provided plugins.
e.g. --enable-plugin-compilation=yes or --disable-plugin-compilation])]
)
ZM_PREFIX="${prefix}"
AC_SUBST(ZM_PREFIX)
ZM_PLUGINSLIBDIR="${libdir}"
@ -269,6 +278,7 @@ if test "$ENABLE_PLUGIN_SUPPORT" == "yes"; then
AC_DEFINE(ZM_PLUGINS_ON,1,"Whether plugin support is switched on and compiled")
fi
AM_CONDITIONAL([ZM_PLUGIN_SUPPORT], [test "x$ENABLE_PLUGIN_SUPPORT" = xyes])
AM_CONDITIONAL([ZM_PLUGIN_COMPIL], [test "x$ENABLE_PLUGIN_COMPIL" = xyes])
WEB_USER=apache
AC_ARG_WITH(webuser,
@ -495,6 +505,44 @@ AC_CHECK_LIB([curl],
[AC_SUBST([CURL_LIBS],["-lcurl"])AC_DEFINE([HAVE_LIBCURL],[1],[Define to 1 if you have the 'curl' library (-lcurl).])]
)
if test "$ENABLE_PLUGIN_COMPIL" == "yes"; then
AC_CHECK_LIB([boost_program_options],
[main],
[AC_SUBST([BOOST_PROGRAM_OPTION_LIBS],["-lboost_program_options"])AC_DEFINE([HAVE_BOOST_PROGRAM_OPTION],[1],[Define to 1 if you have the 'boost_program_options' library (-lboost_program_options).])],
[AC_MSG_ERROR([libboost_program_options is required for plugin compilation])]
)
AC_CHECK_LIB([opencv_core],
[main],
[AC_SUBST([OPENCV_CORE_LIBS],["-lopencv_core"])AC_DEFINE([HAVE_LIBOPENCV_CORE],[1],[Define to 1 if you have the 'opencv_core' library (-lopencv_core).])],
[AC_MSG_ERROR([libopencv_core is required for plugin compilation])]
)
AC_CHECK_LIB([opencv_imgproc],
[main],
[AC_SUBST([OPENCV_IMGPROC_LIBS],["-lopencv_imgproc"])AC_DEFINE([HAVE_LIBOPENCV_IMGPROC],[1],[Define to 1 if you have the 'opencv_imgproc' library (-lopencv_imgproc).])],
[AC_MSG_ERROR([libopencv_imgproc is required for plugin compilation])]
)
AC_CHECK_LIB([opencv_highgui],
[main],
[AC_SUBST([OPENCV_HIGHGUI_LIBS],["-lopencv_highgui"])AC_DEFINE([HAVE_LIBOPENCV_HIGHGUI],[1],[Define to 1 if you have the 'opencv_highgui' library (-lopencv_highgui).])],
[AC_MSG_ERROR([libopencv_highgui is required for plugin compilation])]
)
AC_CHECK_LIB([opencv_objdetect],
[main],
[AC_SUBST([OPENCV_OBJDETECT_LIBS],["-lopencv_objdetect"])AC_DEFINE([HAVE_LIBOPENCV_OBJDETECT],[1],[Define to 1 if you have the 'opencv_objdetect' library (-lopencv_objdetect).])],
[AC_MSG_ERROR([libopencv_objdetect is required for plugin compilation])]
)
PKG_CHECK_MODULES([OPENCV],
[opencv >= 2.3.1],
,
[AC_MSG_ERROR([${OPENCV_PKG_ERRORS}])]
)
AC_CHECK_LIB([openalpr],
[main],
[AC_SUBST([OPENALPR_LIBS],["-lopenalpr"])AC_DEFINE([HAVE_LIBOPENALPR],[1],[Define to 1 if you have the 'openalpr' library (-lopenalpr).])],
[AC_MSG_ERROR([libopenalpr is required for plugin compilation])]
)
fi
# Checks for header files.
AC_FUNC_ALLOCA
AC_HEADER_STDC
@ -545,6 +593,15 @@ AC_CHECK_HEADERS(zlib.h,,,)
AC_CHECK_HEADERS(vlc/vlc.h,,,)
AC_CHECK_HEADERS(curl/curl.h,,,)
if test "$ENABLE_PLUGIN_COMPIL" == "yes"; then
AC_CHECK_HEADERS(alpr.h,,AC_MSG_ERROR(plugins require openalpr headers - check that development package is installed),)
AC_CHECK_HEADERS(boost/algorithm/string.hpp,,AC_MSG_ERROR(plugins require boost string headers - check that development package is installed),)
AC_CHECK_HEADERS(boost/program_options.hpp,,AC_MSG_ERROR(plugins require boost program_options headers - check that development package is installed),)
AC_CHECK_HEADERS(opencv2/core/core.hpp,,AC_MSG_ERROR(plugins require opencv core headers - check that development package is installed),)
AC_CHECK_HEADERS(opencv2/imgproc/imgproc.hpp,,AC_MSG_ERROR(plugins require opencv imgproc headers - check that development package is installed),)
AC_CHECK_HEADERS(opencv2/highgui/highgui.hpp,,AC_MSG_ERROR(plugins require opencv highgui headers - check that development package is installed),)
fi
if test "$ZM_SSL_LIB" == "openssl"; then
AC_CHECK_DECLS(MD5,,AC_MSG_ERROR([zm requires openssl/md5.h - use ZM_SSL_LIB option to select gnutls instead]),[#include <stdlib.h>
#include <openssl/md5.h>])
@ -613,7 +670,7 @@ fi
AC_SUBST(PERL_MM_PARMS)
AC_SUBST(EXTRA_PERL_LIB)
AC_CONFIG_FILES([Makefile zm.conf zmconfgen.pl db/Makefile db/zm_create.sql misc/Makefile misc/apache.conf misc/libzoneminder.pc misc/logrotate.conf misc/syslog.conf misc/com.zoneminder.systemctl.policy misc/com.zoneminder.systemctl.rules misc/plugins.conf onvif/Makefile onvif/scripts/Makefile scripts/Makefile scripts/zm scripts/zmaudit.pl scripts/zmcontrol.pl scripts/zmdc.pl scripts/zmfilter.pl scripts/zmpkg.pl scripts/zmtrack.pl scripts/zmcamtool.pl scripts/zmsystemctl.pl scripts/zmtrigger.pl scripts/zmupdate.pl scripts/zmvideo.pl scripts/zmwatch.pl scripts/zmx10.pl scripts/zmdbbackup scripts/zmdbrestore scripts/zmeventdump scripts/zmlogrotate.conf scripts/ZoneMinder/lib/ZoneMinder/Base.pm scripts/ZoneMinder/lib/ZoneMinder/Config.pm scripts/ZoneMinder/lib/ZoneMinder/Memory.pm scripts/ZoneMinder/lib/ZoneMinder/ConfigData.pm src/Makefile src/zm_config.h web/Makefile web/ajax/Makefile web/css/Makefile web/graphics/Makefile web/includes/Makefile web/includes/config.php web/js/Makefile web/lang/Makefile web/skins/Makefile web/skins/classic/Makefile web/skins/classic/ajax/Makefile web/skins/classic/css/Makefile web/skins/classic/css/classic/Makefile web/skins/classic/css/classic/views/Makefile web/skins/classic/css/flat/Makefile web/skins/classic/css/flat/views/Makefile web/skins/classic/graphics/Makefile web/skins/classic/includes/Makefile web/skins/classic/js/Makefile web/skins/classic/lang/Makefile web/skins/classic/views/Makefile web/skins/classic/views/js/Makefile web/skins/mobile/Makefile web/skins/mobile/ajax/Makefile web/skins/mobile/css/Makefile web/skins/mobile/graphics/Makefile web/skins/mobile/includes/Makefile web/skins/mobile/lang/Makefile web/skins/mobile/views/Makefile web/skins/mobile/views/css/Makefile web/tools/Makefile web/tools/mootools/Makefile web/views/Makefile web/skins/xml/Makefile web/skins/xml/views/Makefile web/skins/xml/includes/Makefile])
AC_CONFIG_FILES([Makefile zm.conf zmconfgen.pl db/Makefile db/zm_create.sql misc/Makefile misc/apache.conf misc/libzoneminder.pc misc/logrotate.conf misc/syslog.conf misc/com.zoneminder.systemctl.policy misc/com.zoneminder.systemctl.rules misc/plugins.conf onvif/Makefile onvif/scripts/Makefile plugins/Makefile plugins/libzm_plugin_openalpr/Makefile plugins/libzm_plugin_openalpr/src/Makefile plugins/libzm_plugin_openalpr/data/Makefile scripts/Makefile scripts/zm scripts/zmaudit.pl scripts/zmcontrol.pl scripts/zmdc.pl scripts/zmfilter.pl scripts/zmpkg.pl scripts/zmtrack.pl scripts/zmcamtool.pl scripts/zmsystemctl.pl scripts/zmtrigger.pl scripts/zmupdate.pl scripts/zmvideo.pl scripts/zmwatch.pl scripts/zmx10.pl scripts/zmdbbackup scripts/zmdbrestore scripts/zmeventdump scripts/zmlogrotate.conf scripts/ZoneMinder/lib/ZoneMinder/Base.pm scripts/ZoneMinder/lib/ZoneMinder/Config.pm scripts/ZoneMinder/lib/ZoneMinder/Memory.pm scripts/ZoneMinder/lib/ZoneMinder/ConfigData.pm src/Makefile src/zm_config.h web/Makefile web/ajax/Makefile web/css/Makefile web/graphics/Makefile web/includes/Makefile web/includes/config.php web/js/Makefile web/lang/Makefile web/skins/Makefile web/skins/classic/Makefile web/skins/classic/ajax/Makefile web/skins/classic/css/Makefile web/skins/classic/css/classic/Makefile web/skins/classic/css/classic/views/Makefile web/skins/classic/css/flat/Makefile web/skins/classic/css/flat/views/Makefile web/skins/classic/graphics/Makefile web/skins/classic/includes/Makefile web/skins/classic/js/Makefile web/skins/classic/lang/Makefile web/skins/classic/views/Makefile web/skins/classic/views/js/Makefile web/skins/mobile/Makefile web/skins/mobile/ajax/Makefile web/skins/mobile/css/Makefile web/skins/mobile/graphics/Makefile web/skins/mobile/includes/Makefile web/skins/mobile/lang/Makefile web/skins/mobile/views/Makefile web/skins/mobile/views/css/Makefile web/tools/Makefile web/tools/mootools/Makefile web/views/Makefile web/skins/xml/Makefile web/skins/xml/views/Makefile web/skins/xml/includes/Makefile])
# Create the definitions for compilation and defaults for the database
AC_CONFIG_COMMANDS([src/zm_config_defines.h],[perl ./zmconfgen.pl])

View File

@ -129,3 +129,12 @@ Description: XML interface for ZoneMinder
.
This package provides a XML interface mainly intended for use with the eyeZm
iPhone Application, but can be used with any other custom programs as well.
Package: libzoneminder-plugin-openalpr
Section: libs
Architecture: any
Depends: zoneminder-core (= ${binary:Version}), ${shlibs:Depends},
${misc:Depends}
Description: License Plate Recognition plugin with openalpr library
This plugin add support for recognition of license plate number in zoneminder
with the use of the OpenALPR library.

View File

@ -0,0 +1,4 @@
usr/lib/zoneminder/plugins/libzm_plugin_openalpr.so
usr/share/zoneminder/plugins/libzm_plugin_openalpr/config.php
usr/share/zoneminder/plugins/libzm_plugin_openalpr/lang/*_*.php
etc/zm/plugins.d/openalpr.conf

View File

@ -54,7 +54,8 @@ override_dh_auto_configure:
--with-ffmpeg=/usr --with-cgidir=/usr/lib/cgi-bin \
--with-webuser=www-data --with-webgroup=www-data \
--enable-crashtrace=no --enable-mmap=yes $(DEBOPT) \
--enable-plugin-support --with-pluginsdir=/usr/lib/zoneminder/plugins
--enable-plugin-support --with-pluginsdir=/usr/lib/zoneminder/plugins \
--enable-plugin-compilation=yes
override_dh_clean:
# Add here commands to clean up after the build process.
@ -73,6 +74,9 @@ override_dh_clean:
done || true
override_dh_install:
# Clean up libtool crap
find debian/tmp -name '*.la' -exec rm '{}' ';'
#
dh_install --fail-missing
#
# NOTE: This is a short-term kludge; hopefully changes in the next

View File

@ -1,4 +1,5 @@
etc/zm
etc/zm/plugins.conf
etc/zm/zm.conf
usr/bin
usr/share/polkit-1/actions
usr/share/polkit-1/rules.d

View File

@ -129,3 +129,12 @@ Description: XML interface for ZoneMinder
.
This package provides a XML interface mainly intended for use with the eyeZm
iPhone Application, but can be used with any other custom programs as well.
Package: libzoneminder-plugin-openalpr
Section: libs
Architecture: any
Depends: zoneminder-core (= ${binary:Version}), ${shlibs:Depends},
${misc:Depends}
Description: License Plate Recognition plugin with openalpr library
This plugin add support for recognition of license plate number in zoneminder
with the use of the OpenALPR library.

View File

@ -0,0 +1,4 @@
usr/lib/zoneminder/plugins/libzm_plugin_openalpr.so
usr/share/zoneminder/plugins/libzm_plugin_openalpr/config.php
usr/share/zoneminder/plugins/libzm_plugin_openalpr/lang/*_*.php
etc/zm/plugins.d/openalpr.conf

View File

@ -37,7 +37,8 @@ override_dh_auto_configure:
-DZM_WEB_GROUP=www-data \
-DCMAKE_INSTALL_SYSCONFDIR=etc/zm \
-DZM_PLUGIN_SUPPORT=ON \
-DZM_PLUGINSPKGLIBDIR=/usr/lib/zoneminder/plugins
-DZM_PLUGINSPKGLIBDIR=/usr/lib/zoneminder/plugins \
-DZM_PLUGIN_COMPIL=ON
override_dh_auto_install:
dh_auto_install --buildsystem=cmake

View File

@ -1,5 +1,5 @@
etc/zm/zm.conf
etc/zm/plugins.conf
etc/zm/zm.conf
usr/bin
usr/share/polkit-1/actions
usr/share/polkit-1/rules.d

4
plugins/CMakeLists.txt Normal file
View File

@ -0,0 +1,4 @@
# CMakeLists.txt for the plugins directory
# Process subdirectories
add_subdirectory(libzm_plugin_openalpr)

4
plugins/Makefile.am Normal file
View File

@ -0,0 +1,4 @@
AUTOMAKE_OPTIONS = gnu
SUBDIRS = \
libzm_plugin_openalpr

View File

@ -0,0 +1,6 @@
# CMakeLists.txt for openalpr plugin.
# Created by Emmanuel Papin (manupap01@gmail.com)
# Process subdirectories
add_subdirectory(data)
add_subdirectory(src)

View File

@ -0,0 +1,5 @@
AUTOMAKE_OPTIONS = foreign
SUBDIRS = \
data \
src

View File

@ -0,0 +1,73 @@
libzm-plugin-openalpr
=====================
## Overview
libzm-plugin-openalpr is a plugin for Automatic Licence Plate Recognition (ALPR) in ZoneMinder.
It is based on openalpr library (https://github.com/openalpr/openalpr).
The recognized license plates are added to Zoneminder's event notes.
## Requirements
libzoneminder-plugin-openalpr requires:
- A ZoneMinder installation with the plugin framework
- The OpenALPR library
### Configuration
After installation, please make some adjustments in file `/etc/zm/plugins.d/openalpr.conf`.
Most of default values can be kept, but if you live in Europe, you can set the `country_code` setting to `eu` to improve reading of plates with EU format
All the next configuration steps are done through the web interface.
Firstly, the plugin loading has to be enabled in ZM options (please check the `LOAD_PLUGIN` setting in `Config` tab).
Then, you can configure the plugin settings from each `Zone` configuration page.
![Zone](https://github.com/manupap1/libzoneminder-plugin-openalpr/blob/master/misc/zone.png)
Available plugins are listed with a color code under the `Plugins` row:
- `Default color` - Plugin is not enabled for the zone
- `Green` - Plugin is enabled for the zone
- `Grey` - Plugin loading is disabled (please check `LOAD_PLUGIN` setting in `Config` tab)
- `Orange` - Plugin is enabled for the zone but not active (configuration setting mismatch)
- `Red` - ZoneMinder failed to load the plugin object (software error)
Once a plugin object is loaded, the `Plugin` configuration page is accessed by clicking on the plugin name.
![Plugin](https://github.com/manupap1/libzoneminder-plugin-openalpr/blob/master/misc/plugin.png)
The first options are available for all plugins:
- `Enabled` - A yes/no select box to enable or disable the plugin
- `Require Native Detection` - A yes/no select box to specify if native detection is required before to process plugin analysis. This option allow to limit CPU usage by using the plugin for post processing after native detection. This option is recommended for this plugin as it may use a lot of CPU ressources
- `Include Native Detection` - A yes/no select box to specify if native detection shall be included in alarm score and image overlay
- `Reinit. Native Detection` - A yes/no select box to specify if native detection shall be reinitialized after detection. ZoneMinder's native detection is performed by comparing the current image to a reference image. By design, the reference image is assigned when analysis is activated, and this image is not periodically refreshed. This operating method is not necessarily optimal because some plugins may require native detection only when motion is truly detected (current image different from the previous image). This option is recommended for libzoneminder-plugin-openalpr. For example, without this option enabled, if a vehicle appears and parks in the camera field of view, the native detection will be be triggered as long as the vehicle is parked, and therefore the plugin analysis would be performed for an unnecessary period of time. With this option enabled, the plugin analysis stops when the vehicle stops.
- `Alarm Score` - A text box to enter the score provided by the plugin in case of detection
The next options are specifics to this plugin and can be used to adjust the detection accuracy:
- `Minimum Confidence` - A text box to enter the minimum confidence level. All plates with a lower confidence level will be excluded.
- `Min. Number of Characters` - A text box to enter the minimum number of characters in a license plate. All plates with a lower number of detected characters will be excluded.
- `Max. Number of Characters` - A text box to enter the maximum number of characters in a license plate. All plates with a greater number of detected characters will be excluded.
- `List of Targeted Plates` - A list to specify targeted plates (detected plates will have a 100% confidence).
- `Detect only Targeted Plates` - A yes/no select box to specify if plates not in target list shall be ignored.
- `Strict Targeting` - A yes/no select box to specify if target matching shall be strict (plates must match exactly).
- `Assume target matching` - When strict targeting is off plates included in wider strings are detected (within the max. number of characters limit). This yes/no select box allow to specify if such detected plates shall be considered as being equal to the target. If yes is selected, the plugin will report these plates with the target string and with a 100% confidence. If no is selected, the plugin will report these plates individually. This option can be helpfull if the plate format includes a field for a logo which may be considered as a character by openalpr.
The configuration is saved to the database and applied when clicking on the `Save` button.
### Using
When a license plate is detected, this triggers an event with alarmed frame(s).
Depending on your configuration settings and video content, an event may contain multiple alarmed frames.
![Events](https://github.com/manupap1/libzoneminder-plugin-openalpr/blob/master/misc/events.png)
Licenses plates are stored in the event note field accessible by a click on the event detection cause.
![Event](https://github.com/manupap1/libzoneminder-plugin-openalpr/blob/master/misc/event.png)
Alarmed frames are highlighted with the plate's detection area(s).
![Frame](https://github.com/manupap1/libzoneminder-plugin-openalpr/blob/master/misc/frame.png)
The detected plate number(s) can be added in email or sms notifications by using the %ED% token in EMAIL_BODY or MESSAGE_BODY option.

View File

@ -0,0 +1,5 @@
# CMakeLists.txt for openalpr plugin.
install (FILES openalpr.conf DESTINATION ${ZM_PLUGINSCONFDIR})
install (FILES config.php DESTINATION "${ZM_PLUGINSWEBDIR}/libzm_plugin_openalpr")
install (FILES en_gb.php fr_fr.php DESTINATION "${ZM_PLUGINSWEBDIR}/libzm_plugin_openalpr/lang")

View File

@ -0,0 +1,14 @@
AUTOMAKE_OPTIONS = foreign
sysconfdir = @ZM_PLUGINSCONFDIR@
sysconf_DATA = \
openalpr.conf
webdir = @ZM_PLUGINSWEBDIR@/libzm_plugin_openalpr
dist_web_DATA = \
config.php
langdir = $(webdir)/lang
dist_lang_DATA = \
en_gb.php \
fr_fr.php

View File

@ -0,0 +1,85 @@
<?php
/* *****************************************************************************
* Copyright (C) 2014 Emmanuel Papin <manupap01@gmail.com>
*
* Authors: Emmanuel Papin <manupap01@gmail.com>
*
* This program is free software; you can redistribute it and/or modify it
* under the terms of the GNU Lesser General Public License as published by
* the Free Software Foundation; either version 2.1 of the License, or
* (at your option) any later version.
*
* 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 Lesser General Public License for more details.
*
* You should have received a copy of the GNU Lesser General Public License
* along with this program; if not, write to the Free Software Foundation,
* Inc., 51 Franklin Street, Fifth Floor, Boston MA 02110-1301, USA.
* ****************************************************************************/
$pluginOptions = array(
'MinConfidence' => array(
'Type' => 'integer',
'Min' => '0',
'Max' => '100',
'Value' => '0',
'Require' => array(
array(
'Name' => 'OnlyTargets',
'Value' => 'No',
),
),
),
'MinCharacters' => array(
'Type' => 'integer',
'Min' => '1',
'Max' => '20',
'Value' => '1',
'Require' => array(
array(
'Name' => 'OnlyTargets',
'Value' => 'No',
),
),
),
'MaxCharacters' => array(
'Type' => 'integer',
'Min' => '1',
'Max' => '20',
'Value' => '20',
'Require' => array(
array(
'Name' => 'StrictTargets',
'Value' => 'No',
),
),
),
'TargetList' => array(
'Type' => 'list',
),
'OnlyTargets' => array(
'Type' => 'select',
'Choices' => 'Yes,No',
'Value'=> 'No',
),
'StrictTargets' => array(
'Type' => 'select',
'Choices' => 'Yes,No',
'Value'=> 'No',
),
'AssumeTargets' => array(
'Type' => 'select',
'Choices' => 'Yes,No',
'Value'=> 'No',
'Require' => array(
array(
'Name' => 'StrictTargets',
'Value' => 'No',
),
),
),
);
?>

View File

@ -0,0 +1,33 @@
<?php
/* *****************************************************************************
* Copyright (C) 2014 Emmanuel Papin <manupap01@gmail.com>
*
* Authors: Emmanuel Papin <manupap01@gmail.com>
*
* This program is free software; you can redistribute it and/or modify it
* under the terms of the GNU Lesser General Public License as published by
* the Free Software Foundation; either version 2.1 of the License, or
* (at your option) any later version.
*
* 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 Lesser General Public License for more details.
*
* You should have received a copy of the GNU Lesser General Public License
* along with this program; if not, write to the Free Software Foundation,
* Inc., 51 Franklin Street, Fifth Floor, Boston MA 02110-1301, USA.
* ****************************************************************************/
$PLANG = array(
'AssumeTargets' => 'Assume target matching',
'MaxCharacters' => 'Max. Number of Characters',
'MinCharacters' => 'Min. Number of Characters',
'MinConfidence' => 'Minimum Confidence',
'OnlyTargets' => 'Detect only Targeted Plates',
'StrictTargets' => 'Strict Targeting',
'TargetList' => 'List of Targeted Plates',
);
?>

View File

@ -0,0 +1,33 @@
<?php
/* *****************************************************************************
* Copyright (C) 2014 Emmanuel Papin <manupap01@gmail.com>
*
* Authors: Emmanuel Papin <manupap01@gmail.com>
*
* This program is free software; you can redistribute it and/or modify it
* under the terms of the GNU Lesser General Public License as published by
* the Free Software Foundation; either version 2.1 of the License, or
* (at your option) any later version.
*
* 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 Lesser General Public License for more details.
*
* You should have received a copy of the GNU Lesser General Public License
* along with this program; if not, write to the Free Software Foundation,
* Inc., 51 Franklin Street, Fifth Floor, Boston MA 02110-1301, USA.
* ****************************************************************************/
$PLANG = array(
'AssumeTargets' => 'Supposer la correspondance',
'MaxCharacters' => 'Nombre max. de caractères',
'MinCharacters' => 'Nombre min. de caractères',
'MinConfidence' => 'Confiance minimum',
'OnlyTargets' => 'Détecter seul. plaques ciblées',
'StrictTargets' => 'Ciblage strict',
'TargetList' => 'Liste de plaques ciblées',
);
?>

View File

@ -0,0 +1,24 @@
# The options commented out show the default values
[libzm_plugin_openalpr]
# Path to the openalpr.conf file (used by openalpr library)
config_file = /etc/openalpr/openalpr.conf
# Country code to identify (either us for USA or eu for Europe)
#country_code = us
# Attempt to match the plate number against a region template (e.g., md for Maryland, ca for California)
#template_region =
# Max number of possible plate numbers to return
#topn = 10
# Attempt to detect the region of the plate image
#detect_region = false
# Detection cause for ZM events
#det_cause = Plate Detected
# Prefix for plugin entries in ZM log files
#log_prefix = OPENALPR PLUGIN

Binary file not shown.

After

Width:  |  Height:  |  Size: 44 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 55 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 700 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 54 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 1.3 MiB

View File

@ -0,0 +1,17 @@
# CMakeLists.txt for openalpr plugin.
add_library(zm_plugin_openalpr SHARED openalpr_plugin.cpp)
include_directories(${zoneminder_SOURCE_DIR}/src ${zoneminder_BINARY_DIR}/src)
target_link_libraries(
zm_plugin_openalpr -lopenalpr -lboost_program_options -lopencv_core
-lopencv_imgproc)
# Do not set the soname when linking the plugin binary.
# Definition of a soname version in a plugin prevents some tools
# (like dpkg-shlibdeps for debian) to clearly identify the binary as a plugin.
# This results in a bunch of unwanted warnings about unresolvable reference to
# symbols (which are provided by the program that loads this plugins).
set_property(TARGET zm_plugin_openalpr PROPERTY NO_SONAME 1)
install (TARGETS zm_plugin_openalpr DESTINATION ${ZM_PLUGINSPKGLIBDIR})

View File

@ -0,0 +1,24 @@
AUTOMAKE_OPTIONS = gnu
pkglibdir = @ZM_PLUGINSPKGLIBDIR@
pkglib_LTLIBRARIES = libzm_plugin_openalpr.la
# This prevents to link the plugin with unnecessary libraries
LIBS=
AM_CPPFLAGS = \
-I$(top_srcdir)/src $(opencv_CFLAGS)
libzm_plugin_openalpr_la_SOURCES = \
openalpr_plugin.cpp
libzm_plugin_openalpr_la_LDFLAGS = \
-avoid-version -module -shared -export-dynamic
libzm_plugin_openalpr_la_LIBADD = \
@OPENALPR_LIBS@ @BOOST_PROGRAM_OPTION_LIBS@ @OPENCV_CORE_LIBS@ \
@OPENCV_IMGPROC_LIBS@
noinst_HEADERS = \
openalpr_plugin.h

View File

@ -0,0 +1,455 @@
/*****************************************************************************
* Copyright (C) 2014 Emmanuel Papin <manupap01@gmail.com>
*
* Authors: Emmanuel Papin <manupap01@gmail.com>
*
* This program is free software; you can redistribute it and/or modify it
* under the terms of the GNU Lesser General Public License as published by
* the Free Software Foundation; either version 2.1 of the License, or
* (at your option) any later version.
*
* 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 Lesser General Public License for more details.
*
* You should have received a copy of the GNU Lesser General Public License
* along with this program; if not, write to the Free Software Foundation,
* Inc., 51 Franklin Street, Fifth Floor, Boston MA 02110-1301, USA.
*****************************************************************************/
#include "openalpr_plugin.h"
using namespace alpr;
using namespace boost::program_options;
//! Retrieve the engine version we're going to expect
extern "C" int getEngineVersion()
{
return ZM_ENGINE_VERSION;
}
//! Tells us to register our functionality to an engine kernel
extern "C" void registerPlugin(PluginManager &PlM, std::string sPluginName)
{
PlM.getImageAnalyser().addDetector(std::auto_ptr<Detector>(new OpenALPRPlugin(sPluginName)));
}
OpenALPRPlugin::OpenALPRPlugin()
: Detector(),
m_sConfigFilePath(DEFAULT_CONFIG_FILE),
m_sCountry(DEFAULT_COUNTRY_CODE),
m_sRegionTemplate(DEFAULT_TEMPLATE_REGION),
m_nMaxPlateNumber(DEFAULT_TOPN),
m_bRegionIsDet(DEFAULT_DETECT_REGION)
{
m_sDetectionCause = DEFAULT_DETECTION_CAUSE;
m_sLogPrefix = DEFAULT_PLUGIN_LOG_PREFIX;
Info("%s: Plugin object has been created", m_sLogPrefix.c_str());
}
OpenALPRPlugin::OpenALPRPlugin(std::string sPluginName)
: Detector(sPluginName),
m_sConfigFilePath(DEFAULT_CONFIG_FILE),
m_sCountry(DEFAULT_COUNTRY_CODE),
m_sRegionTemplate(DEFAULT_TEMPLATE_REGION),
m_nMaxPlateNumber(DEFAULT_TOPN),
m_bRegionIsDet(DEFAULT_DETECT_REGION)
{
m_sDetectionCause = DEFAULT_DETECTION_CAUSE;
m_sLogPrefix = DEFAULT_PLUGIN_LOG_PREFIX;
Info("%s: Plugin object has been created", m_sLogPrefix.c_str());
}
/*! \fn OpenALPRPlugin::loadConfig(std::string sConfigFileName, std::map<unsigned int,std::map<std::string,std::string> > mapPluginConf)
* \param sConfigFileName is path to configuration to load parameters from
* \param mapPluginConf is the map of configuration parameters for each zone
*/
int OpenALPRPlugin::loadConfig(std::string sConfigFileName, std::map<unsigned int,std::map<std::string,std::string> > mapPluginConf)
{
try
{
options_description config_file("Configuration file options.");
variables_map vm;
config_file.add_options()
((m_sConfigSectionName + std::string(".config_file")).c_str(),
value<std::string>()->default_value(DEFAULT_CONFIG_FILE))
((m_sConfigSectionName + std::string(".country_code")).c_str(),
value<std::string>()->default_value(DEFAULT_COUNTRY_CODE))
((m_sConfigSectionName + std::string(".template_region")).c_str(),
value<std::string>()->default_value(DEFAULT_TEMPLATE_REGION))
((m_sConfigSectionName + std::string(".topn")).c_str(),
value<int>()->default_value(DEFAULT_TOPN))
((m_sConfigSectionName + std::string(".detect_region")).c_str(),
value<bool>()->default_value(DEFAULT_DETECT_REGION))
((m_sConfigSectionName + std::string(".det_cause")).c_str(),
value<std::string>()->default_value(DEFAULT_DET_CAUSE))
((m_sConfigSectionName + std::string(".log_prefix")).c_str(),
value<std::string>()->default_value(DEFAULT_PLUGIN_LOG_PREFIX));
try
{
std::ifstream ifs(sConfigFileName.c_str());
store(parse_config_file(ifs, config_file, false), vm);
notify(vm);
}
catch(error& er)
{
Error("%s: Plugin is not configured (%s)", m_sLogPrefix.c_str(), er.what());
return 0;
}
m_sConfigFilePath = vm[(m_sConfigSectionName + std::string(".config_file")).c_str()].as<std::string>();
m_sCountry = vm[(m_sConfigSectionName + std::string(".country_code")).c_str()].as<std::string>();
m_sRegionTemplate = vm[(m_sConfigSectionName + std::string(".template_region")).c_str()].as<std::string>();
m_nMaxPlateNumber = vm[(m_sConfigSectionName + std::string(".topn")).c_str()].as<int>();
m_bRegionIsDet = vm[(m_sConfigSectionName + std::string(".detect_region")).c_str()].as<bool>();
m_sDetectionCause = vm[(m_sConfigSectionName + std::string(".det_cause")).c_str()].as<std::string>();
m_sLogPrefix = vm[(m_sConfigSectionName + std::string(".log_prefix")).c_str()].as<std::string>();
}
catch(std::exception& ex)
{
Error("%s: Plugin is not configured (%s)", m_sLogPrefix.c_str(), ex.what());
return 0;
}
pConf pluginConf;
for (std::map<unsigned int,std::map<std::string,std::string> >::iterator it = mapPluginConf.begin(); it != mapPluginConf.end(); ++it) {
while ( pluginConfig.size() < (it->first + 1) )
pluginConfig.push_back(pluginConf);
// Overwrite default values with database values
for (std::map<std::string,std::string>::iterator it2 = it->second.begin(); it2 != it->second.end(); ++it2) {
if (it2->second.empty()) continue;
if (it2->first == "AlarmScore") {
pluginConfig[it->first].alarmScore = (unsigned int)strtoul(it2->second.c_str(), NULL, 0);
} else if (it2->first == "AssumeTargets") {
if (it2->second == "Yes") {
pluginConfig[it->first].assumeTargets = true;
} else {
pluginConfig[it->first].assumeTargets = false;
}
} else if (it2->first == "MaxCharacters") {
pluginConfig[it->first].maxCharacters = (unsigned int)strtoul(it2->second.c_str(), NULL, 0);
} else if (it2->first == "MinCharacters") {
pluginConfig[it->first].minCharacters = (unsigned int)strtoul(it2->second.c_str(), NULL, 0);
} else if (it2->first == "MinConfidence") {
pluginConfig[it->first].minConfidence = (unsigned int)strtoul(it2->second.c_str(), NULL, 0);
} else if (it2->first == "OnlyTargets") {
if (it2->second == "Yes") {
pluginConfig[it->first].onlyTargets = true;
} else {
pluginConfig[it->first].onlyTargets = false;
}
} else if (it2->first == "StrictTargets") {
if (it2->second == "Yes") {
pluginConfig[it->first].strictTargets = true;
} else {
pluginConfig[it->first].strictTargets = false;
}
} else if (it2->first == "TargetList") {
boost::split(pluginConfig[it->first].targetList, it2->second, boost::is_any_of(","));
}
}
}
// Create an instance of class Alpr and set basic configuration
ptrAlpr = new Alpr(m_sCountry, m_sConfigFilePath);
ptrAlpr->setTopN(m_nMaxPlateNumber);
if ( m_bRegionIsDet )
ptrAlpr->setDetectRegion(m_bRegionIsDet);
if ( !m_sRegionTemplate.empty() )
ptrAlpr->setDefaultRegion(m_sRegionTemplate);
// Initialize some lists
int nb_zones = pluginConfig.size();
plateList.resize(nb_zones);
tmpPlateList.resize(nb_zones);
if ( ptrAlpr->isLoaded() ) {
Info("%s: Plugin is configured", m_sLogPrefix.c_str());
} else {
Error("%s: Plugin is not configured (%s)", m_sLogPrefix.c_str(), strerror(errno));
delete ptrAlpr;
return 0;
}
return 1;
}
OpenALPRPlugin::~OpenALPRPlugin()
{
delete ptrAlpr;
}
/*! \fn OpenALPRPlugin::OpenALPRPlugin(const OpenALPRPlugin& source)
* \param source is the object for copying
*/
OpenALPRPlugin::OpenALPRPlugin(const OpenALPRPlugin& source)
: Detector(source),
m_sConfigFilePath(source.m_sConfigFilePath),
m_sCountry(source.m_sCountry),
m_sRegionTemplate(source.m_sRegionTemplate),
m_nMaxPlateNumber(source.m_nMaxPlateNumber),
m_bRegionIsDet(source.m_bRegionIsDet)
{
}
/*! \fn OpenALPRPlugin:: operator=(const OpenALPRPlugin& source)
* \param source is the object for copying
*/
OpenALPRPlugin & OpenALPRPlugin:: operator=(const OpenALPRPlugin& source)
{
Detector::operator=(source);
m_sConfigFilePath = source.m_sConfigFilePath;
m_sCountry = source.m_sCountry;
m_sRegionTemplate = source.m_sRegionTemplate;
m_nMaxPlateNumber = source.m_nMaxPlateNumber;
m_bRegionIsDet = source.m_bRegionIsDet;
return *this;
}
/*! \fn OpenALPRPlugin::onCreateEvent(Zone *zone, unsigned int n_zone, Event *event)
* \param zone is a pointer to the zone that triggered the event
* \param n_zone is the zone id
* \param event is a pointer the new event
*/
void OpenALPRPlugin::onCreateEvent(Zone *zone, unsigned int n_zone, Event *event)
{
Debug(1, "%s: Zone %s - Prepare plugin for event %d", m_sLogPrefix.c_str(), zone->Label(), event->Id());
/* Note: Do not clear the plate list here because in case of post processing
* the plate list is filled with results of first detection before
* event creation */
}
/*! \fn OpenALPRPlugin::onCloseEvent(Zone *zone, unsigned int n_zone, Event *event, std::string noteText)
* \param zone is a pointer to the zone that triggered the event
* \param n_zone is the zone id
* \param event is a pointer to the event that will be closed
* \param noteText is a string that can be used to output text to the event note
*/
void OpenALPRPlugin::onCloseEvent(Zone *zone, unsigned int n_zone, Event *event, std::string &noteText)
{
// Set the number of plates to output and exit if nothing to do
unsigned int topn = ( m_nMaxPlateNumber < plateList[n_zone].size() ) ? m_nMaxPlateNumber : plateList[n_zone].size();
if (topn == 0) return;
// Sort plates according confidence level (higher first)
sort(plateList[n_zone].begin(), plateList[n_zone].end(), sortByConf());
Info("%s: Zone %s - Add plates to event %d", m_sLogPrefix.c_str(), zone->Label(), event->Id());
// Output only the first topn plates to the event note
for(unsigned int i=0; i<topn;i++)
{
std::stringstream plate;
plate << plateList[n_zone][i].num << " (" << plateList[n_zone][i].conf << ")";
Debug(1, "%s: Zone %s - Plate %s detected", m_sLogPrefix.c_str(), zone->Label(), plate.str().c_str());
noteText += " " + plate.str() + "\n";
}
// Reset the lists for next use
plateList[n_zone].clear();
tmpPlateList[n_zone].clear();
}
/*! \fn OpenALPRPlugin::checkZone(Zone *zone, const Image *zmImage)
* \param zone is a zone where license plates will be detected
* \param n_zone is the zone id
* \param zmImage is an image to perform license plate detection (in the form of ZM' Image)
* \return true if there were objects detected in given image and false otherwise
*/
bool OpenALPRPlugin::checkZone(Zone *zone, unsigned int n_zone, const Image *zmImage)
{
Polygon zone_polygon = Polygon(zone->GetPolygon()); // Polygon of interest of the processed zone.
Image *pMaskImage = new Image(zmImage->Width(), zmImage->Height(), ZM_COLOUR_GRAY8, ZM_SUBPIX_ORDER_NONE );
pMaskImage->Fill(BLACK);
// An temporary image in the form of ZM for making from it CvMat.
// If don't use temp image, after rgb->bgr it will change.
Image *tempZmImage = new Image(*zmImage);
int imgtype=CV_8UC1;
if (tempZmImage->Colours() == ZM_COLOUR_RGB24)
imgtype=CV_8UC3;
cv::Mat cvInputImage = cv::Mat(
tempZmImage->Height(),
tempZmImage->Width(),
imgtype, (unsigned char*)tempZmImage->Buffer()).clone();
if (tempZmImage->Colours() == ZM_COLOUR_RGB24)
cvtColor(cvInputImage, cvInputImage, CV_RGB2BGR);
//Process image
//std::vector<unsigned char> buffer;
//cv::imencode(".bmp", cvInputImage, buffer);
std::vector<AlprRegionOfInterest> regionsOfInterest;
regionsOfInterest.push_back(AlprRegionOfInterest(0,0, cvInputImage.cols, cvInputImage.rows));
// Region of interest don't work as expected
//std::vector<AlprResults> results = ptrAlpr->recognize(buffer, regionsOfInterest);
AlprResults results = ptrAlpr->recognize(cvInputImage.data, cvInputImage.elemSize(), cvInputImage.cols, cvInputImage.rows, regionsOfInterest);
double score = 0;
for (unsigned int i = 0; i < results.plates.size(); i++) {
int x1 = results.plates[i].plate_points[0].x, y1 = results.plates[i].plate_points[0].y;
int x2 = results.plates[i].plate_points[1].x, y2 = results.plates[i].plate_points[1].y;
int x3 = results.plates[i].plate_points[2].x, y3 = results.plates[i].plate_points[2].y;
int x4 = results.plates[i].plate_points[3].x, y4 = results.plates[i].plate_points[3].y;
Coord rectVertCoords[4] = {Coord(x1, y1), Coord(x2, y2), Coord(x3, y3), Coord(x4, y4)};
int nNumVertInside = 0;
for (int p = 0; p < 4; p++)
nNumVertInside += zone_polygon.isInside(rectVertCoords[p]);
// if at least three rectangle coordinates are inside polygon,
// consider rectangle as belonging to the zone
// otherwise process next object
if (nNumVertInside < 3)
{
Debug(1, "%s: Zone %s - Skip result (outside detection zone)", m_sLogPrefix.c_str(), zone->Label());
continue;
}
int cntDetPlates = 0;
for (unsigned int k = 0; k < results.plates[i].topNPlates.size(); k++)
{
strPlate detPlate;
detPlate.num = results.plates[i].topNPlates[k].characters;
detPlate.conf = results.plates[i].topNPlates[k].overall_confidence;
bool isTarget = false;
// Check targeted plates first
for (std::vector<std::string>::iterator it = pluginConfig[n_zone].targetList.begin(); it != pluginConfig[n_zone].targetList.end(); ++it)
{
// If plates match, add targeted plate and continue with next detected plate
if (*it == detPlate.num)
{
detPlate.conf = 100;
addPlate(zone, n_zone, detPlate);
cntDetPlates++;
isTarget = true;
break;
}
// Check if targeted plate is a substring of the detected plate
else if (detPlate.num.find(*it) != std::string::npos)
{
// If yes and strict targeting is on, disqualify targeted plate
if (pluginConfig[n_zone].strictTargets)
{
Debug(1, "%s: Zone %s - Skip targeted plate %s (strict targeting is on)", m_sLogPrefix.c_str(), zone->Label(), detPlate.num.c_str());
// And continue with next targeted plate (maybe another in the list will strictly match)
continue;
}
// If yes but detected plate has too much characters, disqualify targeted plate
if (detPlate.num.size() > pluginConfig[n_zone].maxCharacters)
{
Debug(1, "%s: Zone %s - Skip targeted plate %s (number of characters is out of range)", m_sLogPrefix.c_str(), zone->Label(), detPlate.num.c_str());
// And continue with next detected plate (this one is not valid)
break;
}
// Overwrite detected plate by target if we assume the matching is correct
if (pluginConfig[n_zone].assumeTargets)
{
detPlate.conf = 100;
detPlate.num = *it;
}
// Add targeted plate to list and continue with next detected plate
addPlate(zone, n_zone, detPlate);
cntDetPlates++;
isTarget = true;
break;
}
}
// Skip detected plate if already added or if only targeted plates are accepted
if (isTarget || pluginConfig[n_zone].onlyTargets)
continue;
// Disqualify plate if under the minimum confidence level
if (detPlate.conf < pluginConfig[n_zone].minConfidence)
{
Debug(1, "%s: Zone %s - Skip plate %s (under minimum confidence level)", m_sLogPrefix.c_str(), zone->Label(), detPlate.num.c_str());
continue;
}
// Disqualify plate if not enough characters or too much characters
if ((detPlate.num.size() < pluginConfig[n_zone].minCharacters)
|| (detPlate.num.size() > pluginConfig[n_zone].maxCharacters))
{
Debug(1, "%s: Zone %s - Skip plate %s (number of characters is out of range)", m_sLogPrefix.c_str(), zone->Label(), detPlate.num.c_str());
continue;
}
// Add plate to list (if already in list, update confidence by adding new value)
addPlate(zone, n_zone, detPlate);
cntDetPlates++;
}
if (cntDetPlates == 0)
continue;
// Raise an alarm if at least one plate has been detected
score = pluginConfig[n_zone].alarmScore;
// Fill a polygon with object in the mask
Polygon platePolygon = Polygon(4, rectVertCoords);
pMaskImage->Fill(WHITE, 1, platePolygon);
}
if (score == 0) {
delete pMaskImage;
delete tempZmImage;
return false;
}
zone->SetScore((int)score);
//Get mask by highlighting contours of objects and overlaying them with previous contours.
Rgb alarm_colour = RGB_GREEN;
Image *hlZmImage = pMaskImage->HighlightEdges(alarm_colour, ZM_COLOUR_RGB24,
ZM_SUBPIX_ORDER_RGB, &zone_polygon.Extent());
if (zone->Alarmed()) {
// if there were previous detection and they have already set up alarm image
// then overlay it with current mask
Image* pPrevZoneMask = new Image(*(zone->AlarmImage()));
pPrevZoneMask->Overlay(*hlZmImage);
zone->SetAlarmImage(pPrevZoneMask);
delete pPrevZoneMask;
} else
zone->SetAlarmImage(hlZmImage);
delete pMaskImage;
delete tempZmImage;
return true;
}
bool OpenALPRPlugin::addPlate(Zone *zone, unsigned int n_zone, strPlate detPlate)
{
for (std::vector<strPlate>::iterator it = plateList[n_zone].begin(); it != plateList[n_zone].end(); ++it)
{
// If plate number exists in the list for this zone
if ((*it).num == detPlate.num)
{
// Add confidence
(*it).conf += detPlate.conf;
Debug(1, "%s: Zone %s - Raise confidence of plate %s to %f", m_sLogPrefix.c_str(), zone->Label(), (*it).num.c_str(), (*it).conf);
return false;
}
}
// Add a new plate for this zone
Debug(1, "%s: Zone %s - Add plate %s with confidence %f", m_sLogPrefix.c_str(), zone->Label(), detPlate.num.c_str(), detPlate.conf);
plateList[n_zone].push_back(detPlate);
return true;
}

View File

@ -0,0 +1,143 @@
/*****************************************************************************
* Copyright (C) 2014 Emmanuel Papin <manupap01@gmail.com>
*
* Authors: Emmanuel Papin <manupap01@gmail.com>
*
* This program is free software; you can redistribute it and/or modify it
* under the terms of the GNU Lesser General Public License as published by
* the Free Software Foundation; either version 2.1 of the License, or
* (at your option) any later version.
*
* 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 Lesser General Public License for more details.
*
* You should have received a copy of the GNU Lesser General Public License
* along with this program; if not, write to the Free Software Foundation,
* Inc., 51 Franklin Street, Fifth Floor, Boston MA 02110-1301, USA.
*****************************************************************************/
#ifndef OPENALPR_PLUGIN_H
#define OPENALPR_PLUGIN_H
#include <stdexcept>
#include <fstream>
#include <algorithm>
#include <alpr.h>
#include <opencv2/core/core.hpp>
#include <opencv2/imgproc/imgproc.hpp>
#include <opencv2/highgui/highgui.hpp>
#include <boost/algorithm/string.hpp>
#include <boost/program_options.hpp>
#include <boost/program_options/option.hpp>
#include <boost/program_options/options_description.hpp>
#include <boost/program_options/errors.hpp>
#include "zm_plugin_manager.h"
#include "zm_detector.h"
#include "zm_rgb.h"
#define DEFAULT_CONFIG_FILE "/etc/openalpr/openalpr.conf"
#define DEFAULT_COUNTRY_CODE "us"
#define DEFAULT_TEMPLATE_REGION ""
#define DEFAULT_TOPN 10
#define DEFAULT_DETECT_REGION 0
#define DEFAULT_DET_CAUSE "Plate Detected"
#define DEFAULT_PLUGIN_LOG_PREFIX "OPENALPR PLUGIN"
#define DEFAULT_ALARM_SCORE 99
#define DEFAULT_ASSUME_TARGETS 0
#define DEFAULT_MAX_CHARACTERS 99
#define DEFAULT_MIN_CHARACTERS 0
#define DEFAULT_MIN_CONFIDENCE 0
#define DEFAULT_ONLY_TARGETS 0
#define DEFAULT_STRICT_TARGETS 0
//! OpenALPR plugin class.
/*! The class derived from Detector.
* This class provides license plate detection based on tesseract OCR.
*/
class OpenALPRPlugin : public Detector {
public:
//! Default Constructor.
OpenALPRPlugin();
//! Constructor.
OpenALPRPlugin(std::string sConfigSectionName);
//! Destructor.
virtual ~OpenALPRPlugin();
//! Copy constructor.
OpenALPRPlugin(const OpenALPRPlugin& source);
//! Overloaded operator=.
OpenALPRPlugin& operator=(const OpenALPRPlugin& source);
int loadConfig(std::string sConfigFileName, std::map<unsigned int,std::map<std::string,std::string> > mapPluginConf);
protected:
void onCreateEvent(Zone *zone, unsigned int n_zone, Event *event);
void onCloseEvent(Zone *zone, unsigned int n_zone, Event *event, std::string &noteText);
bool checkZone(Zone *zone, unsigned int n_zone, const Image *zmImage);
std::string m_sConfigFilePath;
std::string m_sCountry;
std::string m_sRegionTemplate;
unsigned int m_nMaxPlateNumber;
bool m_bRegionIsDet;
private:
alpr::Alpr *ptrAlpr;
struct pConf
{
unsigned int alarmScore;
bool assumeTargets;
unsigned int maxCharacters;
unsigned int minCharacters;
unsigned int minConfidence;
bool onlyTargets;
bool strictTargets;
std::vector<std::string> targetList;
pConf():
alarmScore(DEFAULT_ALARM_SCORE),
assumeTargets(DEFAULT_ASSUME_TARGETS),
maxCharacters(DEFAULT_MAX_CHARACTERS),
minCharacters(DEFAULT_MIN_CHARACTERS),
minConfidence(DEFAULT_MIN_CONFIDENCE),
onlyTargets(DEFAULT_ONLY_TARGETS),
strictTargets(DEFAULT_STRICT_TARGETS)
{}
};
std::vector<pConf> pluginConfig;
struct strPlate
{
std::string num;
float conf;
};
struct sortByConf
{
bool operator()(const strPlate a, const strPlate b) const
{
return a.conf > b.conf;
}
};
std::vector<std::vector<strPlate> > plateList;
std::vector<std::vector<std::string> > tmpPlateList;
bool addPlate(Zone *zone, unsigned int n_zone, strPlate detPlate);
};
#endif // OPENALPR_PLUGIN_H