diff --git a/src/zm_detector.cpp b/src/zm_detector.cpp new file mode 100644 index 000000000..fca106ec1 --- /dev/null +++ b/src/zm_detector.cpp @@ -0,0 +1,232 @@ +#include "zm_detector.h" + + + +/*!\fn Detector::Detector(const Detector& source) + * \param source is the object to copy + */ +Detector::Detector(const Detector& source) + : m_sDetectionCause(source.m_sDetectionCause), + m_fMinAlarmScore(source.m_fMinAlarmScore), + m_fMaxAlarmScore(source.m_fMaxAlarmScore), + m_fImageScaleFactor(source.m_fImageScaleFactor), + m_nNewWidth(source.m_nNewWidth), + m_nNewHeight(source.m_nNewHeight), + m_sLogPrefix(source.m_sLogPrefix), + m_sConfigSectionName(source.m_sConfigSectionName), + m_vnPluginZones(source.m_vnPluginZones) +{ + m_bIsPluginEnabled = false; +} + + + +/*!\fn Detector& ImageAnalyser::Detector::operator=(const ImageAnalyser::Detector& source) + * \param source is the object to copy + */ +Detector& Detector::operator=(const Detector& source) +{ + m_sDetectionCause = source.m_sDetectionCause; + m_fMinAlarmScore = source.m_fMinAlarmScore; + m_fMaxAlarmScore = source.m_fMaxAlarmScore; + m_fImageScaleFactor = source.m_fImageScaleFactor; + m_sLogPrefix = source.m_sLogPrefix; + m_nNewWidth = source.m_nNewWidth; + m_nNewHeight = source.m_nNewHeight; + m_sConfigSectionName = source.m_sConfigSectionName; + m_vnPluginZones = source.m_vnPluginZones; + + return *this; +} + + + +/*!\fn Detector::getDetectionCause() + * return detection cause as string + */ +string Detector::getDetectionCause() +{ + return m_sDetectionCause; +} + + +/*!\fn Detector::getConfigSectionName() + * return plugin name as string + */ +string Detector::getPluginName() +{ + return m_sConfigSectionName; +} + + +/*!\fn Detector::EnablePlugin(vector zoneList) + * \param vnZoneList is the list of enabled zones for the plugin + */ +void Detector::EnablePlugin(vector vnZoneList) +{ + m_vnPluginZones = vnZoneList; + m_bIsPluginEnabled = true; +} + + +/*!\fn Detector::getPluginZones() + * \return the list of zone which have the plugin enabled + */ +vector Detector::getPluginZones() +{ + return m_vnPluginZones; +} + + +/*! \fn Detector::log(int nLogLevel, string sLevel, string sMessage) + */ +void Detector::log(int nLogLevel, string sLevel, string sMessage) +{ + string sMessageToLog = sLevel + string(" [") + m_sLogPrefix + string(": ") + sMessage + string("]"); + syslog(nLogLevel, "%s", sMessageToLog.c_str()); +} + + + +/*! \fn int Detector::Detect(const Image &image, Event::StringSet &zoneSet) + * \param zmImage is an image to detect faces on + * \param zoneSet is set of zone names (see zm_zone.h) + * \param score is the detection score + * \return true if detection is effective + */ +bool Detector::Detect(const Image &zmImage, Zone** zones, Event::StringSet &zoneSet, unsigned int &score) +{ + bool alarm = false; + char szMessage[100]; + score = 0; + + if (!m_bIsPluginEnabled) return (alarm); + + // Check preclusive zones first + for(std::vector::iterator it = m_vnPluginZones.begin(); it != m_vnPluginZones.end(); ++it) + { + Zone *zone = zones[*it]; + if (!zone->IsPreclusive()) + continue; + if (zone->IsPostProcEnabled() && !zone->IsPostProcInProgress()) + continue; + sprintf(szMessage, "Checking preclusive zone %s", zone->Label()); + log(LOG_DEBUG, "DEBUG", szMessage); + if (checkZone(zone, *it, &zmImage)) + { + alarm = true; + score += zone->Score(); + zoneSet.insert(zone->Text()); + if (zone->IsPostProcEnabled()) + { + zone->StopPostProcessing(); + sprintf(szMessage, "Zone is alarmed, zone score = %d (post-processing)", zone->Score()); + } + else + { + sprintf(szMessage, "Zone is alarmed, zone score = %d", zone->Score()); + } + log(LOG_DEBUG, "DEBUG", szMessage); + } + } + + if ( alarm ) + { + alarm = false; + score = 0; + } + else + { + // Find all alarm pixels in active zones + for(std::vector::iterator it = m_vnPluginZones.begin(); it != m_vnPluginZones.end(); ++it) + { + Zone *zone = zones[*it]; + if (!zone->IsActive()) + continue; + if (zone->IsPostProcEnabled() && !zone->IsPostProcInProgress()) + continue; + if (checkZone(zone, *it, &zmImage)) + { + alarm = true; + score += zone->Score(); + zoneSet.insert(zone->Text()); + if (zone->IsPostProcEnabled()) + { + zone->StopPostProcessing(); + sprintf(szMessage, "Zone is alarmed, zone score = %d (post-processing)", zone->Score()); + } + else + { + zone->SetAlarm(); + sprintf(szMessage, "Zone is alarmed, zone score = %d", zone->Score()); + } + log(LOG_DEBUG, "DEBUG", szMessage); + } + } + + if ( alarm ) + { + // Checking inclusive zones + for(std::vector::iterator it = m_vnPluginZones.begin(); it != m_vnPluginZones.end(); ++it) + { + Zone *zone = zones[*it]; + if (!zone->IsInclusive()) + continue; + if (zone->IsPostProcEnabled() && !zone->IsPostProcInProgress()) + continue; + sprintf(szMessage, "Checking inclusive zone %s", zone->Label()); + log(LOG_DEBUG, "DEBUG", szMessage); + if (checkZone(zone, *it, &zmImage)) + { + alarm = true; + score += zone->Score(); + zoneSet.insert(zone->Text()); + if (zone->IsPostProcEnabled()) + { + zone->StopPostProcessing(); + sprintf(szMessage, "Zone is alarmed, zone score = %d (post-processing)", zone->Score()); + } + else + { + zone->SetAlarm(); + sprintf(szMessage, "Zone is alarmed, zone score = %d", zone->Score()); + } + log(LOG_DEBUG, "DEBUG", szMessage); + } + } + } + else + { + // Find all alarm pixels in exclusive zones + for(std::vector::iterator it = m_vnPluginZones.begin(); it != m_vnPluginZones.end(); ++it) + { + Zone *zone = zones[*it]; + if (!zone->IsExclusive()) + continue; + if (zone->IsPostProcEnabled() && !zone->IsPostProcInProgress()) + continue; + sprintf(szMessage, "Checking exclusive zone %s", zone->Label()); + log(LOG_DEBUG, "DEBUG", szMessage); + if (checkZone(zone, *it, &zmImage)) + { + alarm = true; + score += zone->Score(); + zoneSet.insert(zone->Text()); + if (zone->IsPostProcEnabled()) + { + zone->StopPostProcessing(); + sprintf(szMessage, "Zone is alarmed, zone score = %d (post-processing)", zone->Score()); + } + else + { + zone->SetAlarm(); + sprintf(szMessage, "Zone is alarmed, zone score = %d", zone->Score()); + } + log(LOG_DEBUG, "DEBUG", szMessage); + } + } + } + } + + return alarm; +} diff --git a/src/zm_detector.h b/src/zm_detector.h new file mode 100644 index 000000000..9850bda6d --- /dev/null +++ b/src/zm_detector.h @@ -0,0 +1,137 @@ +#ifndef ZM_DETECTOR_H +#define ZM_DETECTOR_H + + +#include +#include +#include +#include + +#include "zm_image.h" +#include "zm_zone.h" +#include "zm_event.h" + +#define DEFAULT_DETECTION_CAUSE "Object Detected" +#define DEFAULT_MIN_ALARM_SCORE 1.0 +#define DEFAULT_MAX_ALARM_SCORE 99.0 +#define DEFAULT_IMAGE_SCALE_FACTOR 1.0 + +#define DEFAULT_LOG_PREFIX "ZM PLUGIN" +#define LOG_LEVEL LOG_NOTICE +#define DEFAULT_CONFIGFILE_SECTION "libzm_vscvl_plugin" + +using namespace std; + + +//! Base class for object detectors, defined in plugins. +class Detector +{ + +public: + + //! Destructor + virtual ~Detector() {} + + //! Default constructor + Detector() +{ + m_sLogPrefix = DEFAULT_LOG_PREFIX; + m_sDetectionCause = DEFAULT_DETECTION_CAUSE; + m_fMinAlarmScore = DEFAULT_MIN_ALARM_SCORE; + m_fMaxAlarmScore = DEFAULT_MAX_ALARM_SCORE; + m_fImageScaleFactor = DEFAULT_IMAGE_SCALE_FACTOR; + m_sConfigSectionName = DEFAULT_CONFIGFILE_SECTION; + m_nNewWidth = 0; + m_nNewHeight = 0; +} + + //! Constructor with section name parameter. + Detector(string sPluginFileName) +{ + m_sLogPrefix = DEFAULT_LOG_PREFIX; + + char* szPluginFileName = strdup(sPluginFileName.c_str()); + + string sPluginFileNameName = string(basename(szPluginFileName)); + + size_t idx = sPluginFileNameName.rfind('.'); + + if (idx == string::npos) + m_sConfigSectionName = sPluginFileNameName; + else + m_sConfigSectionName = sPluginFileNameName.substr(0, idx); + + m_sDetectionCause = DEFAULT_DETECTION_CAUSE; + m_fMinAlarmScore = DEFAULT_MIN_ALARM_SCORE; + m_fMaxAlarmScore = DEFAULT_MAX_ALARM_SCORE; + m_fImageScaleFactor = DEFAULT_IMAGE_SCALE_FACTOR; + m_nNewWidth = 0; + m_nNewHeight = 0; +} + + //! Copy constructor + Detector(const Detector& source); + + //! Assignment operator + Detector& operator=(const Detector& source); + + //! Detect (in an image later) + bool Detect(const Image &image, Zone** zones, Event::StringSet &zoneSet, unsigned int &score); + + //! Load detector's parameters. + virtual int loadConfig(string sConfigFileName, map > mapPluginConf) = 0; + + //! Returns detection case string. + string getDetectionCause(); + + //! Returns plugin name as string. + string getPluginName(); + + //! Enable the plugin for the given zones. + void EnablePlugin(vector zoneList); + + //! Return the list of enabled zones + vector getPluginZones(); + +protected: + + //! Do detection inside one given zone. + virtual bool checkZone(Zone *zone, unsigned int n_zone, const Image *zmImage) = 0; + + //! Log messages to the SYSLOG. + void log(int, string sLevel, string sMessage); + + //! String to be shown as detection cause for event. + string m_sDetectionCause; + + //! Minimum score value to consider frame as to be alarmed. + double m_fMinAlarmScore; + + //! Maximum score value to consider frame as to be alarmed. + double m_fMaxAlarmScore; + + //! Maximum allowed width of frame image. + double m_fImageScaleFactor; + + //! Width of image to resize. + int m_nNewWidth; + + //! Height of image to resize. + int m_nNewHeight; + + //! String prefix for SYSLOG messages. + string m_sLogPrefix; + + //! Name of config file section to search parameters. + string m_sConfigSectionName; + + //! List of zones enabled for the plugin + vector m_vnPluginZones; + + //! Plugin status regarding zone settings + bool m_bIsPluginEnabled; + +}; + + +#endif // ZM_DETECTOR_H diff --git a/src/zm_plugin.cpp b/src/zm_plugin.cpp new file mode 100644 index 000000000..334cc7a85 --- /dev/null +++ b/src/zm_plugin.cpp @@ -0,0 +1,105 @@ +#include "zm_plugin.h" + + + +/*!\fn Plugin::Plugin(const std::string &sFilename) + * \param sFilename is the name of plugin file to load + */ +Plugin::Plugin(const std::string &sFilename) + : m_sPluginFileName(sFilename), + m_hDLL(0), + m_pDLLRefCount(0), + m_pfnGetEngineVersion(0), + m_pfnRegisterPlugin(0) +{ + + // Try to load the plugin as a dynamic library + m_hDLL = dlopen(sFilename.c_str(), RTLD_LAZY|RTLD_GLOBAL); + + if(!m_hDLL) // if library hasn't been loaded successfully + { + throw runtime_error(string("Could not load '") + sFilename + "'"); + } + + // Locate the plugin's exported functions + try + { + m_pfnGetEngineVersion = reinterpret_cast(dlsym(m_hDLL, "getEngineVersion")); + m_pfnRegisterPlugin = reinterpret_cast(dlsym(m_hDLL, "registerPlugin")); + + // If the functions aren't found, we're going to assume this is + // a plain simple DLL and not one of our plugins + if(!m_pfnGetEngineVersion || ! m_pfnRegisterPlugin) + throw runtime_error(string("'") + sFilename + "' is not a valid plugin"); + + // Initialize a new DLL reference counter + m_pDLLRefCount = new size_t(1); + } + catch(runtime_error &ex) + { + dlclose(m_hDLL); + throw ex; + } + catch(...) + { + dlclose(m_hDLL); + throw runtime_error(string("Unknown exception while loading plugin '") + sFilename + string("'")); + } +} + + + +/*!\fn Plugin::Plugin(const Plugin &Other) + * \param Other is the other plugin instance to copy + */ +Plugin::Plugin(const Plugin &Other) + : m_sPluginFileName(Other.m_sPluginFileName), + m_hDLL(Other.m_hDLL), + m_pDLLRefCount(Other.m_pDLLRefCount), + m_pfnGetEngineVersion(Other.m_pfnGetEngineVersion), + m_pfnRegisterPlugin(Other.m_pfnRegisterPlugin) +{ + // Increase DLL reference counter + ++*m_pDLLRefCount; +} + + + +/*!\fn Plugin::operator=(const Plugin &Other) + * \param Other is the other plugin instance to copy + * return copy of object + */ +Plugin& Plugin::operator=(const Plugin &Other) +{ + m_hDLL = Other.m_hDLL; + m_pfnGetEngineVersion = Other.m_pfnGetEngineVersion; + m_pfnRegisterPlugin = Other.m_pfnRegisterPlugin; + m_pDLLRefCount = Other.m_pDLLRefCount; + m_sPluginFileName = Other.m_sPluginFileName; + // Increase DLL reference counter + ++*m_pDLLRefCount; + return *this; + +} + + + +Plugin::~Plugin() +{ + // Only unload the DLL if there are no more references to it + if(!--*m_pDLLRefCount) + { + delete m_pDLLRefCount; + dlclose(m_hDLL); + } +} + + + +/*!\fn Plugin::registerPlugin(PluginManager &K) + * \param K is the pointer to plugin manager + */ +void Plugin::registerPlugin(PluginManager &K) +{ + m_pfnRegisterPlugin(K, m_sPluginFileName); +} diff --git a/src/zm_plugin.h b/src/zm_plugin.h new file mode 100644 index 000000000..4434b8f88 --- /dev/null +++ b/src/zm_plugin.h @@ -0,0 +1,77 @@ +#ifndef ZM_PLUGIN_H +#define ZM_PLUGIN_H + + + +#include +#include +#include + +#include + + + +using namespace std; + + + +class PluginManager; + + + +//! Signature for the version query function +typedef int fnGetEngineVersion(); + +//! Signature for the plugin's registration function +typedef void fnRegisterPlugin(PluginManager &, string); + + + +//! Representation of a plugin. +/*! Use for loading plugin's shared library + * and registration of it to the PluginManager. + */ +class Plugin +{ + +public: + + //! Initialize and load plugin + Plugin(const std::string &sFilename); + + //! Copy existing plugin instance + Plugin(const Plugin &Other); + + //! Operator =. + Plugin &operator =(const Plugin &Other); + + //! Unload a plugin + ~Plugin(); + + //! Query the plugin for its expected engine version + int getEngineVersion() const { return m_pfnGetEngineVersion();} + + //! Register the plugin to a PluginManager + void registerPlugin(PluginManager &K); + +private: + + //! Shared file name. + string m_sPluginFileName; + + //! DLL handle + void* m_hDLL; + + //! Number of references to the DLL + size_t *m_pDLLRefCount; + + //! Version query function + fnGetEngineVersion *m_pfnGetEngineVersion; + + //! Plugin registration function + fnRegisterPlugin *m_pfnRegisterPlugin; +}; + + + +#endif //ZM_PLUGIN_H diff --git a/src/zm_plugin_manager.cpp b/src/zm_plugin_manager.cpp new file mode 100644 index 000000000..2f2a06bb7 --- /dev/null +++ b/src/zm_plugin_manager.cpp @@ -0,0 +1,160 @@ +#include "zm_plugin_manager.h" + + + +/*! \fn file_select(const struct direct *entry) + * A functor for selection of files with specified extension. + * \param entry is file structure + * \return 1 if file match selection criteria and + * 0 otherwise. + * NOTE: file extension is specified by PluginManager::m_sPluginExt + * static variable. + */ +int file_select(const struct direct *entry) +{ + char *ptr; + + if ((strcmp(entry->d_name, ".")== 0) || (strcmp(entry->d_name, "..") == 0)) + return 0; + + // Check for filename extensions. + ptr = rindex((char*)entry->d_name, '.'); + if ((ptr != NULL) && (strcmp(ptr, (PluginManager::m_sPluginExt).c_str()) == 0)) + return 1; + else + return 0; +} + + + + +/*! \fn join_paths(const string& p1, const string& p2) + * \param p1 is the first part of desired path + * \param p2 is the second part of desired path + * \return joined path string. + */ +string join_paths(const string& p1, const string& p2) +{ + char sep = '/'; + string tmp = p1; + +#ifdef _WIN32 + sep = '\\'; +#endif + + if (p1[p1.length()] != sep) + { // Need to add a path separator + tmp += sep; + return(tmp + p2); + } + else + return(p1 + p2); +} + + + +string PluginManager::m_sPluginExt = DEFAULT_PLUGIN_EXT; + + +PluginManager::PluginManager() {} + + +PluginManager::PluginManager( + int nMonitorId +) : + m_ImageAnalyser( nMonitorId ) +{} + + + +/*!\fn PluginManager::loadPlugin(const string &sFilename)) + * \param sFilename is the name of plugin file to load + */ +bool PluginManager::loadPlugin(const string &sFilename) +{ + try + { + if(m_LoadedPlugins.find(sFilename) == m_LoadedPlugins.end()) + m_LoadedPlugins.insert(PluginMap::value_type(sFilename, Plugin(sFilename))).first->second.registerPlugin(*this); + } + catch(runtime_error &ex) + { + Error("Runtime error: %s", ex.what()); + return false; + } + catch(...) + { + Error("Unknown exception. Could not load %s.", sFilename.c_str()); + return false; + } + return true; +} + + +/*!\fn PluginManager::findPlugins(const string &sPath, bool loadPlugins) + * \param sPath is the path to folder to search plugins + * \param loadPlugins is a flag to allow loading of plugins + * \param nNumPlugLoaded is the number of loaded plugins + * \return the number of found plugins + */ +int PluginManager::findPlugins(const string sPath, bool loadPlugins, unsigned int& nNumPlugLoaded) +{ + struct direct **files; + int count = scandir(sPath.c_str(), &files, file_select, alphasort); + if(count <= 0) count = 0; + + for (int i = 0; i < count; ++i) + { + string sFileName = string(files[i]->d_name); + string sFullPath = join_paths(sPath, sFileName); + size_t idx = sFileName.rfind('.'); + if (idx != string::npos) + sFileName = sFileName.substr(0, idx); + bool IsPluginRegistered = false; + if(config.load_plugins || loadPlugins) + { + Info("Loading plugin %s ... ", sFullPath.c_str()); + IsPluginRegistered = loadPlugin(sFullPath); + } + mapPluginReg.insert( pair(sFileName, IsPluginRegistered) ); + if (IsPluginRegistered) nNumPlugLoaded++; + } + return count; +} + + +/*!\fn PluginManager::configurePlugins(string sConfigFileName) + * \param sConfigFileName is the path to the configuration file, where parameters for all plugins are given. + * \param bDoNativeDet is true if native detection will be performed +*/ +void PluginManager::configurePlugins(string sConfigFileName, bool bDoNativeDet) +{ + m_ImageAnalyser.configurePlugins(sConfigFileName, bDoNativeDet); +} + + +/*!\fn PluginManager::getPluginsGenConf(map& mapPluginGenConf) + * \param mapPluginGenConf is the map of general settings for the plugins + * \param mapPluginZoneConf is the map of zone settings for the plugins + * \return the number of found plugins + */ +unsigned long PluginManager::getPluginsGenConf(map& mapPluginGenConf) +{ + for (map::iterator it = mapPluginReg.begin() ; it != mapPluginReg.end(); ++it) + { + pGenConf plugGenConf; + m_ImageAnalyser.getRegPluginGenConf( it->first, plugGenConf ); + plugGenConf.Registered = it->second; + mapPluginGenConf.insert( pair(it->first, plugGenConf) ); + } + return mapPluginGenConf.size(); +} + + +/*!\fn PluginManager::getPluginZoneConf(string sPluginName, PluginZoneConf& mapPluginZoneConf) + * \param mapPluginZoneConf is the map of zone settings for the plugin + */ +void PluginManager::getPluginZoneConf(string sPluginName, PluginZoneConf& mapPluginZoneConf) +{ + m_ImageAnalyser.getRegPluginZoneConf( sPluginName, mapPluginZoneConf ); +} diff --git a/src/zm_plugin_manager.h b/src/zm_plugin_manager.h new file mode 100644 index 000000000..f3c8fa465 --- /dev/null +++ b/src/zm_plugin_manager.h @@ -0,0 +1,94 @@ +#ifndef ZM_PLUGIN_MANAGER_H +#define ZM_PLUGIN_MANAGER_H + + + +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include "zm_image_analyser.h" +#include "zm_detector.h" +#include "zm_plugin.h" + + + +using namespace std; + + +#define ZM_ENGINE_VERSION 24 + + +//! Map of plugins by their associated file names. +typedef std::map PluginMap; + + +//! External function for sorting of files in directory. +extern int alphasort(); + + +//! Function to select files with plugins by extension. +int file_select(const struct direct *entry); + + +//! Join two path strings. +string join_paths(const string& p1, const string& p2); + + + +//! Class for managing all loaded plugins. +class PluginManager +{ +public: + //! Default constructor. + PluginManager(); + + //! Constructor with parameters + PluginManager(int nMonitorId); + + //! Access the image analyser. + ImageAnalyser &getImageAnalyser() {return m_ImageAnalyser;} + + //! Loads a plugin. + bool loadPlugin(const string &sFilename); + + //! Find all plugins from given directory, load them if required and + //! return the number of found plugins and the number of loaded plugins + int findPlugins(const string sPath, bool loadPlugins, unsigned int& nNumPlugLoaded); + + //! Get general settings of plugins + unsigned long getPluginsGenConf(map& mapPluginGenConf); + + //! Get zone settings of a plugin + void getPluginZoneConf(string sPluginName, PluginZoneConf& mapPluginZoneConf); + + //! Configure all loaded plugins using given configuration file. + void configurePlugins(string sConfigFileName, bool bDoNativeDet); + + //! Set plugin extension. + void setPluginExt(string sPluginExt) { m_sPluginExt = sPluginExt; } + + //! Extension for zm plugins. + static string m_sPluginExt; + +private: + + //! All plugins currently loaded. + PluginMap m_LoadedPlugins; + + //! The image analyser. + ImageAnalyser m_ImageAnalyser; + + //! Plugin list + map mapPluginReg; +}; + + + +#endif //ZM_PLUGIN_MANAGER_H