From: SeokHoon Lee Date: Tue, 1 Dec 2015 07:40:12 +0000 (+0900) Subject: MediaVision Surveillance feature : initial version X-Git-Tag: accepted/tizen/wearable/20151204.062657 X-Git-Url: http://review.tizen.org/git/?a=commitdiff_plain;h=refs%2Ftags%2Faccepted%2Ftizen%2Fwearable%2F20151204.062657;p=platform%2Fcore%2Fapi%2Fmediavision.git MediaVision Surveillance feature : initial version Signed-off-by: SeokHoon Lee Change-Id: I4f6596a891c7bda729b70bd026026c224974fdc3 --- diff --git a/AUTHORS b/AUTHORS index 14ccbda..c096057 100644 --- a/AUTHORS +++ b/AUTHORS @@ -1,4 +1,3 @@ -ByungWook Jang Tae-Young Chung Oleg Kopysov Ievgen Vagin @@ -6,3 +5,4 @@ Anton Artyukh Yaroslav Zatsikha Sergii Rudenko SeokHoon Lee +Heechul Jeon diff --git a/CMakeLists.txt b/CMakeLists.txt index 8452d0a..2ec1ac7 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -19,6 +19,8 @@ option(MEDIA_VISION_IMAGE_LICENSE_PORT "Turn on building of licensed port of the image module (if OFF - open port will be built)." OFF) option(MEDIA_VISION_FACE_LICENSE_PORT "Turn on building of licensed port of the face module (if OFF - open port will be built)." OFF) +option(MEDIA_VISION_SURVEILLANCE_LICENSE_PORT + "Turn on building of licensed port of the surveillance module (if OFF - open port will be built)." OFF) set(MV_COMMON_LIB_NAME "mv_common") set(MV_BARCODE_DETECTOR_LIB_NAME "mv_barcode_detector" CACHE STRING @@ -29,6 +31,8 @@ set(MV_IMAGE_LIB_NAME "mv_image" CACHE STRING "Name of the library will be built for image module (without extension).") set(MV_FACE_LIB_NAME "mv_face" CACHE STRING "Name of the library will be built for barcode generating module (without extension).") +set(MV_SURVEILLANCE_LIB_NAME "mv_surveillance" CACHE STRING + "Name of the library will be built for surveillance module (without extension).") SET(INC_DIR "${PROJECT_SOURCE_DIR}/include") @@ -64,12 +68,20 @@ else() SET(INC_FACE "${PROJECT_SOURCE_DIR}/mv_face/face/include") endif() +if(MEDIA_VISION_SURVEILLANCE_LICENSE_PORT) + add_definitions(-DMEDIA_VISION_SURVEILLANCE_LICENSE_PORT) + SET(INC_SURVEILLANCE "${PROJECT_SOURCE_DIR}/mv_surveillance/surveillance_lic/include") +else() + SET(INC_SURVEILLANCE "${PROJECT_SOURCE_DIR}/mv_surveillance/surveillance/include") +endif() + INCLUDE_DIRECTORIES(${INC_DIR} ${INC_COMMON} ${INC_BARCODE_DETECTOR} ${INC_BARCODE_GENERATOR} ${INC_FACE} - ${INC_IMAGE}) + ${INC_IMAGE} + ${INC_SURVEILLANCE}) SET(dependents "dlog capi-media-tool capi-system-info capi-appfw-application") SET(pc_dependents "dlog") @@ -100,6 +112,7 @@ ADD_SUBDIRECTORY(mv_common) ADD_SUBDIRECTORY(mv_barcode) ADD_SUBDIRECTORY(mv_image) ADD_SUBDIRECTORY(mv_face) +ADD_SUBDIRECTORY(mv_surveillance) aux_source_directory(src SOURCES) ADD_LIBRARY(${fw_name} SHARED ${SOURCES}) @@ -109,7 +122,8 @@ TARGET_LINK_LIBRARIES(${fw_name} ${MV_COMMON_LIB_NAME} ${MV_BARCODE_GENERATOR_LIB_NAME} ${MV_IMAGE_LIB_NAME} ${MV_FACE_LIB_NAME} - ${${fw_name}_LDFLAGS}) + ${${fw_name}_LDFLAGS} + ${MV_SURVEILLANCE_LIB_NAME}) SET_TARGET_PROPERTIES(${fw_name} PROPERTIES diff --git a/doc/mediavision_doc.h b/doc/mediavision_doc.h index b7ab18d..8402b85 100644 --- a/doc/mediavision_doc.h +++ b/doc/mediavision_doc.h @@ -24,13 +24,16 @@ * * Face detection, recognition, and tracking;\n * * Barcode detection and generation;\n * * Flat Image detection, recognition and tracking;\n - * * Flat Image features extraction. + * * Flat Image features extraction;\n + * * Surveillance: movement detection, person appearance/disappearance, + * person recognition. * * @defgroup CAPI_MEDIA_VISION_COMMON_MODULE Media Vision Common * @ingroup CAPI_MEDIA_VISION_MODULE * @brief Common functions and enumerations used in * @ref CAPI_MEDIA_VISION_FACE_MODULE, - * @ref CAPI_MEDIA_VISION_IMAGE_MODULE and + * @ref CAPI_MEDIA_VISION_IMAGE_MODULE, + * @ref CAPI_MEDIA_VISION_SURVEILLANCE_MODULE and * @ref CAPI_MEDIA_VISION_BARCODE_MODULE submodules. * @section CAPI_MEDIA_VISION_COMMON_MODULE_HEADER Required Header * \#include @@ -247,6 +250,125 @@ * For QR codes it is possible to specify error correction code and encoding * mode (see @ref mv_barcode_qr_mode_e). Generation to file supports several * formats (see @ref mv_barcode_image_format_e). + * + * @defgroup CAPI_MEDIA_VISION_SURVEILLANCE_MODULE Media Vision Surveillance + * @ingroup CAPI_MEDIA_VISION_MODULE + * @brief Video surveillance module. + * @section CAPI_MEDIA_VISION_SURVEILLANCE_MODULE_HEADER Required Header + * \#include + * + * @section CAPI_MEDIA_VISION_SURVEILLANCE_MODULE_FEATURE Related Features + * This API is related with the following features:\n + * - http://tizen.org/feature/vision.image_recognition\n + * - http://tizen.org/feature/vision.face_recognition\n + * + * It is recommended to design feature related codes in your application for + * reliability.\n + * You can check if a device supports the related features for this API by using + * @ref CAPI_SYSTEM_SYSTEM_INFO_MODULE, thereby controlling the procedure of + * your application.\n + * To ensure your application is only running on the device with specific + * features, please define the features in your manifest file using the manifest + * editor in the SDK.\n + * More details on featuring your application can be found from + * + * Feature Element. + * + * + * @section CAPI_MEDIA_VISION_SURVEILLANCE_MODULE_OVERVIEW Overview + * @ref CAPI_MEDIA_VISION_SURVEILLANCE_MODULE provides functionality can be + * utilized for creation of video surveillance systems. The main idea underlying + * surveillance is event subscription model. By default, supported event types + * are described in @ref CAPI_MEDIA_VISION_SURVEILLANCE_EVENT_TYPES section. + * @ref mv_surveillance_subscribe_event_trigger() function has to be used to + * create subscription to the particular event trigger. Triggers are handled by + * @ref mv_surveillance_event_trigger_h type. Such type handlers can be created + * with @ref mv_surveillance_event_trigger_create() function and destroyed with + * @ref mv_surveillance_event_trigger_destroy() function. Once event trigger + * subscription is created, corresponding @ref mv_surveillance_event_occurred_cb + * callback will be invoked each time when event is detected, i.e. trigger is + * activated. @ref mv_surveillance_result_h event detection result handler will + * be passed to the callback together with identifier of the video stream where + * event was detected and @ref mv_source_h handler containing frame in which + * detection was performed. It is possible to retrieve specific to event type + * result values using @ref mv_surveillance_result_h handler. In the + * @ref mv_surveillance_get_result_value() function documentation can be found + * detailed description of result values retrieving. Following table contains + * general events and corresponding event detection results can be obtained by + * this approach: + * + * + * + * + * + * + * + * + * + * + * + * + * + * + * + * + * + *
EventEvent result values
@ref MV_SURVEILLANCE_EVENT_TYPE_MOVEMENT_DETECTED + * @ref MV_SURVEILLANCE_MOVEMENT_NUMBER_OF_REGIONS;
+ * @ref MV_SURVEILLANCE_MOVEMENT_REGIONS + *
@ref MV_SURVEILLANCE_EVENT_TYPE_PERSON_APPEARED_DISAPPEARED + * @ref MV_SURVEILLANCE_PERSONS_APPEARED_NUMBER;
+ * @ref MV_SURVEILLANCE_PERSONS_DISAPPEARED_NUMBER;
+ * @ref MV_SURVEILLANCE_PERSONS_TRACKED_NUMBER;
+ * @ref MV_SURVEILLANCE_PERSONS_APPEARED_LOCATIONS;
+ * @ref MV_SURVEILLANCE_PERSONS_DISAPPEARED_LOCATIONS;
+ * @ref MV_SURVEILLANCE_PERSONS_TRACKED_LOCATIONS + *
@ref MV_SURVEILLANCE_EVENT_TYPE_PERSON_RECOGNIZED + * @ref MV_SURVEILLANCE_PERSONS_RECOGNIZED_NUMBER;
+ * @ref MV_SURVEILLANCE_PERSONS_RECOGNIZED_LOCATIONS;
+ * @ref MV_SURVEILLANCE_PERSONS_RECOGNIZED_LABELS;
+ * @ref MV_SURVEILLANCE_PERSONS_RECOGNIZED_CONFIDENCES + *
+ * Before subscription of the event trigger with + * @ref mv_surveillance_subscribe_event_trigger() call it is possible to create + * @ref mv_engine_config_h handle and configurate following attributes: + * - @ref MV_SURVEILLANCE_SKIP_FRAMES_COUNT to setup number of frames will be + ignored by event trigger; + * - @ref MV_SURVEILLANCE_MOVEMENT_DETECTION_THRESHOLD to specify sensitivity of + * the @ref MV_SURVEILLANCE_EVENT_TYPE_MOVEMENT_DETECTED event detection; + * - @ref MV_SURVEILLANCE_FACE_RECOGNITION_MODEL_FILE_PATH to specify the file + * where face recognition model to be used for recognition is stored. + * + * Created engine config has to be used as a parameter of + * @ref mv_surveillance_subscribe_event_trigger() to apply the configuration. If + * NULL will be passed instead of valid @ref mv_engine_config_h handle, then + * default attribute values will be used for subsriptions. + * To make surveillance system work with video sequences + * @ref mv_surveillance_push_source() function has to + * be used for each frame in the sequence in the correct order. Multiple video + * sources can be supported by the system. To distinguish different video + * sources unique stream identifier has to be assigned to each subscription. + * Then, particular identifier can be passed as a parameter to the + * @ref mv_surveillance_push_source() function. After pushing the source to the + * surveillance system, it will notify all triggers which were subscribed to + * process frames coming from video stream which source has been pushed. + * If trigger(s) is(are) activated on the source, then corresponding callback(s) + * of @ref mv_surveillance_event_occurred_cb type will be called. + * Additionally, region where event detection will be performed by the triggers + * can be set with @ref mv_surveillance_set_event_trigger_roi() function and + * gotten with @ref mv_surveillance_get_event_trigger_roi(). ROI is specified + * independently for the each event trigger, so it is possible to detect events + * of different types in the different parts of the incoming frames. + * Event trigger subscription can be stopped any time using + * @ref mv_surveillance_unsubscribe_event_trigger() function. Additionally, + * @ref mv_surveillance_foreach_supported_event_type() and + * @ref mv_surveillance_foreach_event_result_name() functions can be found + * useful if it is required to obtain supported event types list or result + * value names list dynamically. + * + * @defgroup CAPI_MEDIA_VISION_SURVEILLANCE_EVENT_TYPES Media Vision Surveillance Event Types + * @ingroup CAPI_MEDIA_VISION_SURVEILLANCE_MODULE + * @brief Event types supported by the Surveillance module. */ #endif /* __TIZEN_MEDIAVISION_DOC_H__ */ diff --git a/include/mv_surveillance.h b/include/mv_surveillance.h new file mode 100644 index 0000000..f17c77b --- /dev/null +++ b/include/mv_surveillance.h @@ -0,0 +1,1154 @@ +/** + * Copyright (c) 2015 Samsung Electronics Co., Ltd All Rights Reserved + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#ifndef __TIZEN_MEDIAVISION_SURVEILLANCE_H__ +#define __TIZEN_MEDIAVISION_SURVEILLANCE_H__ + +#include + +#ifdef __cplusplus +extern "C" { +#endif /* __cplusplus */ + +/** + * @file mv_surveillance.h + * @brief This file contains the Media Vision Surveillance API. + */ + +/** + * @addtogroup CAPI_MEDIA_VISION_SURVEILLANCE_EVENT_TYPES + * @{ + */ + +/** + * @brief Name of the movement detection event type. + * @details This is common event for a movement detection. When this event + * occurs @ref mv_surveillance_result_h allowed from callback can be + * used to get number of regions where movement has been detected and + * their positions. Out parameters (can be accessed in the + * @ref mv_surveillance_event_occurred_cb callback using + * @ref mv_surveillance_get_result_value() function): + * * @ref MV_SURVEILLANCE_MOVEMENT_NUMBER_OF_REGIONS - the number + * of regions where movement has been detected;\n + * * @ref MV_SURVEILLANCE_MOVEMENT_REGIONS - the set + * of rectangular regions where movement has been detected. + * + * @since_tizen 3.0 + */ +#define MV_SURVEILLANCE_EVENT_TYPE_MOVEMENT_DETECTED \ + "MV_SURVEILLANCE_EVENT_MOVEMENT_DETECTED" + +/** + * @brief Name of the event result value that contains number of regions where + * movement was detected. + * @details This event result value can be accessed after event triggers of + * @ref MV_SURVEILLANCE_EVENT_TYPE_MOVEMENT_DETECTED type activation. + * Result value is of @c size_t type, so has to be casted as in the + * following example: + * @code{.c} + * void event_occurred_cb(mv_surveillance_event_trigger_h trigger, + * mv_source_h source, + * int video_stream_id, + * mv_surveillance_result_h event_result, + * void *user_data) + * { + * const char *event_type = NULL; + * int err = mv_surveillance_get_event_trigger_type(trigger, + * event_type); + * if (MEDIA_VISION_ERROR_NONE != err) return; + * + * if (0 == strncmp(event_type, + * MV_SURVEILLANCE_EVENT_TYPE_MOVEMENT_DETECTED, + * 255)) + * { + * size_t move_regions_num = 0; + * err = mv_surveillance_get_result_value( + * event_result, + * MV_SURVEILLANCE_MOVEMENT_NUMBER_OF_REGIONS, + * &move_regions_num); + * if (MEDIA_VISION_ERROR_NONE != err) + * return; + * + * // Do something with number of regions where movement + * // was detected... + * } + * } + * @endcode + * + * @since_tizen 3.0 + */ +#define MV_SURVEILLANCE_MOVEMENT_NUMBER_OF_REGIONS \ + "NUMBER_OF_MOVEMENT_REGIONS" + +/** + * @brief Name of the event result value that contains rectangular regions where + * movement was detected. + * @details This event result value can be accessed after event triggers of + * @ref MV_SURVEILLANCE_EVENT_TYPE_MOVEMENT_DETECTED type activation. + * Result value is of @a mv_rectangle_s array type, so has to be casted + * as in the following example: + * @code{.c} + * void event_occurred_cb(mv_surveillance_event_trigger_h trigger, + * mv_source_h source, + * int video_stream_id, + * mv_surveillance_result_h event_result, + * void *user_data) + * { + * const char *event_type = NULL; + * int err = mv_surveillance_get_event_trigger_type(trigger, + * event_type); + * if (MEDIA_VISION_ERROR_NONE != err) return; + * + * if (0 == strncmp(event_type, + * MV_SURVEILLANCE_EVENT_TYPE_MOVEMENT_DETECTED, + * 255)) + * { + * size_t move_regions_num = 0; + * err = mv_surveillance_get_result_value( + * event_result, + * MV_SURVEILLANCE_MOVEMENT_NUMBER_OF_REGIONS, + * &move_regions_num); + * + * if (MEDIA_VISION_ERROR_NONE != err || 0 == move_regions_num) + * return; + * + * mv_rectangle_s *regions = + * (mv_rectangle_s*) + * malloc(sizeof(mv_rectangle_s) * move_regions_num); + * err = mv_surveillance_get_result_value( + * event_result, + * MV_SURVEILLANCE_MOVEMENT_REGIONS, + * regions); + * + * // Do something with movement regions... + * + * free (regions); + * } + * } + * @endcode + * + * @since_tizen 3.0 + */ +#define MV_SURVEILLANCE_MOVEMENT_REGIONS "MOVEMENT_REGIONS" + +/** + * @brief Name of the person appearance/disappearance event type. + * @details This is common event for a person appearing (disappearing). + * The result will be: + * * a number of persons, which were appeared and their positions;\n + * * a number of persons, which were tracked and their positions;\n + * * a number of persons, which were disappeared and their last + * positions.\n + * For the first time when a source is loaded, a result should contain + * all detected persons (detection will be made using face detection + * API). Next time when the source should be loaded, the previously + * detected persons will be tracked and new persons will be detected. + * For the previously detected persons their locations will be updated. + * Out parameters (can be accessed in the + * @ref mv_surveillance_event_occurred_cb callback using + * @ref mv_surveillance_get_result_value() function): + * * @ref MV_SURVEILLANCE_PERSONS_APPEARED_NUMBER - the number + * of persons which were appeared;\n + * * @ref MV_SURVEILLANCE_PERSONS_APPEARED_LOCATIONS - the locations + * of persons which were appeared;\n + * * @ref MV_SURVEILLANCE_PERSONS_TRACKED_NUMBER - the number + * of persons which were tracked;\n + * * @ref MV_SURVEILLANCE_PERSONS_TRACKED_LOCATIONS - the locations + * of persons which were tracked;\n + * * @ref MV_SURVEILLANCE_PERSONS_DISAPPEARED_NUMBER - the number + * of persons which were disappeared;\n + * * @ref MV_SURVEILLANCE_PERSONS_DISAPPEARED_LOCATIONS - the locations + * of persons which were disappeared. + * + * @since_tizen 3.0 + */ +#define MV_SURVEILLANCE_EVENT_TYPE_PERSON_APPEARED_DISAPPEARED \ + "MV_SURVEILLANCE_EVENT_PERSON_APPEARED_DISAPEARED" + +/** + * @brief Name of the event result value that contains number + * of persons that have been appeared. + * @details This event result value can be accessed after event triggers of + * @ref MV_SURVEILLANCE_EVENT_TYPE_PERSON_APPEARED_DISAPPEARED type + * activation. Result value is of @c size_t type, so has to be casted + * as in the following example: + * @code{.c} + * void event_occurred_cb(mv_surveillance_event_trigger_h trigger, + * mv_source_h source, + * int video_stream_id, + * mv_surveillance_result_h event_result, + * void *user_data) + * { + * const char *event_type = NULL; + * int err = mv_surveillance_get_event_trigger_type(trigger, + * event_type); + * if (MEDIA_VISION_ERROR_NONE != err) return; + * + * if (0 == strncmp(event_type, + * MV_SURVEILLANCE_EVENT_TYPE_PERSON_APPEARED_DISAPPEARED, + * 255)) + * { + * size_t appear_person_num = 0; + * err = mv_surveillance_get_result_value( + * event_result, + * MV_SURVEILLANCE_PERSONS_APPEARED_NUMBER, + * &appear_person_num); + * if (MEDIA_VISION_ERROR_NONE != err) + * return; + * + * // Do something with number of appeared persons... + * } + * } + * @endcode + * + * @since_tizen 3.0 + */ +#define MV_SURVEILLANCE_PERSONS_APPEARED_NUMBER \ + "NUMBER_OF_APPEARED_PERSONS" + +/** + * @brief Name of the event result value that contains number + * of persons that have been disappeared. + * @details This event result value can be accessed after event triggers of + * @ref MV_SURVEILLANCE_EVENT_TYPE_PERSON_APPEARED_DISAPPEARED type + * activation. Result value is of @c size_t type, so has to be casted + * as in the following example: + * @code{.c} + * void event_occurred_cb(mv_surveillance_event_trigger_h trigger, + * mv_source_h source, + * int video_stream_id, + * mv_surveillance_result_h event_result, + * void *user_data) + * { + * const char *event_type = NULL; + * int err = mv_surveillance_get_event_trigger_type(trigger, + * event_type); + * if (MEDIA_VISION_ERROR_NONE != err) return; + * + * if (0 == strncmp(event_type, + * MV_SURVEILLANCE_EVENT_TYPE_PERSON_APPEARED_DISAPPEARED, + * 255)) + * { + * size_t disappear_person_num = 0; + * err = mv_surveillance_get_result_value( + * event_result, + * MV_SURVEILLANCE_PERSONS_DISAPPEARED_NUMBER, + * &disappear_person_num); + * if (MEDIA_VISION_ERROR_NONE != err) + * return; + * + * // Do something with number of disappeared persons... + * } + * } + * @endcode + * + * @since_tizen 3.0 + */ +#define MV_SURVEILLANCE_PERSONS_DISAPPEARED_NUMBER \ + "NUMBER_OF_DISAPPEARED_PERSONS" + +/** + * @brief Name of the event result value that contains number + * of persons that have been tracked. + * @details This event result value can be accessed after event triggers of + * @ref MV_SURVEILLANCE_EVENT_TYPE_PERSON_APPEARED_DISAPPEARED type + * activation. Result value is of @c size_t type, so has to be casted + * as in the following example: + * @code{.c} + * void event_occurred_cb(mv_surveillance_event_trigger_h trigger, + * mv_source_h source, + * int video_stream_id, + * mv_surveillance_result_h event_result, + * void *user_data) + * { + * const char *event_type = NULL; + * int err = mv_surveillance_get_event_trigger_type(trigger, + * event_type); + * if (MEDIA_VISION_ERROR_NONE != err) return; + * + * if (0 == strncmp(event_type, + * MV_SURVEILLANCE_EVENT_TYPE_PERSON_APPEARED_DISAPPEARED, + * 255)) + * { + * size_t tracked_person_num = 0; + * err = mv_surveillance_get_result_value( + * event_result, + * MV_SURVEILLANCE_PERSONS_TRACKED_NUMBER, + * &tracked_person_num); + * if (MEDIA_VISION_ERROR_NONE != err) + * return; + * + * // Do something with number of tracked persons... + * } + * } + * @endcode + * + * @since_tizen 3.0 + */ +#define MV_SURVEILLANCE_PERSONS_TRACKED_NUMBER \ + "NUMBER_OF_TRACKED_PERSONS" + +/** + * @brief Name of the event result value that contains a set of rectangular + * locations where appearances of the persons were detected. + * @details This event result value can be accessed after event triggers of + * @ref MV_SURVEILLANCE_EVENT_TYPE_PERSON_APPEARED_DISAPPEARED type + * activation. Result value is of @a mv_rectangle_s array type, so has + * to be casted as in the following example: + * @code{.c} + * void event_occurred_cb(mv_surveillance_event_trigger_h trigger, + * mv_source_h source, + * int video_stream_id, + * mv_surveillance_result_h event_result, + * void *user_data) + * { + * const char *event_type = NULL; + * int err = mv_surveillance_get_event_trigger_type(trigger, + * event_type); + * if (MEDIA_VISION_ERROR_NONE != err) return; + * + * if (0 == strncmp(event_type, + * MV_SURVEILLANCE_EVENT_TYPE_PERSON_APPEARED_DISAPPEARED, + * 255)) + * { + * size_t appear_person_num = 0; + * err = mv_surveillance_get_result_value( + * event_result, + * MV_SURVEILLANCE_PERSONS_APPEARED_NUMBER, + * &appear_person_num); + * + * if (MEDIA_VISION_ERROR_NONE != err || + * 0 == appear_person_num) return; + * + * mv_rectangle_s *appear_locations = + * (mv_rectangle_s*) + * malloc(sizeof(mv_rectangle_s) * appear_person_num); + * err = mv_surveillance_get_result_value( + * event_result, + * MV_SURVEILLANCE_PERSONS_APPEARED_LOCATIONS, + * appear_locations); + * + * // Do something with locations where persons were + * // appeared... + * + * free (appear_locations); + * } + * } + * @endcode + * + * @since_tizen 3.0 + */ +#define MV_SURVEILLANCE_PERSONS_APPEARED_LOCATIONS \ + "APPEARED_PERSONS_LOCATIONS" + +/** + * @brief Name of the event result value that contains a set of rectangular + * locations where disappearances of the persons were detected. + * @details This event result value can be accessed after event triggers of + * @ref MV_SURVEILLANCE_EVENT_TYPE_PERSON_APPEARED_DISAPPEARED type + * activation. Result value is of @a mv_rectangle_s array type, so has + * to be casted as in the following example: + * @code{.c} + * void event_occurred_cb(mv_surveillance_event_trigger_h trigger, + * mv_source_h source, + * int video_stream_id, + * mv_surveillance_result_h event_result, + * void *user_data) + * { + * const char *event_type = NULL; + * int err = mv_surveillance_get_event_trigger_type(trigger, + * event_type); + * if (MEDIA_VISION_ERROR_NONE != err) return; + * + * if (0 == strncmp(event_type, + * MV_SURVEILLANCE_EVENT_TYPE_PERSON_APPEARED_DISAPPEARED, + * 255)) + * { + * size_t disappear_person_num = 0; + * err = mv_surveillance_get_result_value( + * event_result, + * MV_SURVEILLANCE_PERSONS_DISAPPEARED_NUMBER, + * &disappear_person_num); + * + * if (MEDIA_VISION_ERROR_NONE != err || + * 0 == disappear_person_num) return; + * + * mv_rectangle_s *disappear_locations = + * (mv_rectangle_s*) + * malloc(sizeof(mv_rectangle_s) * disappear_person_num); + * err = mv_surveillance_get_result_value( + * event_result, + * MV_SURVEILLANCE_PERSONS_DISAPPEARED_LOCATIONS, + * disappear_locations); + * + * // Do something with locations where persons were + * // disappeared... + * + * free (disappear_locations); + * } + * } + * @endcode + * + * @since_tizen 3.0 + */ +#define MV_SURVEILLANCE_PERSONS_DISAPPEARED_LOCATIONS \ + "DISAPPEARED_PERSONS_LOCATIONS" + +/** + * @brief Name of the event result value that contains a set of rectangular + * locations where persons were tracked. + * @details This event result value can be accessed after event triggers of + * @ref MV_SURVEILLANCE_EVENT_TYPE_PERSON_APPEARED_DISAPPEARED type + * activation. Result value is of @a mv_rectangle_s array type, so has + * to be casted as in the following example: + * @code{.c} + * void event_occurred_cb(mv_surveillance_event_trigger_h trigger, + * mv_source_h source, + * int video_stream_id, + * mv_surveillance_result_h event_result, + * void *user_data) + * { + * const char *event_type = NULL; + * int err = mv_surveillance_get_event_trigger_type(trigger, + * event_type); + * if (MEDIA_VISION_ERROR_NONE != err) return; + * + * if (0 == strncmp(event_type, + * MV_SURVEILLANCE_EVENT_TYPE_PERSON_APPEARED_DISAPPEARED, + * 255)) + * { + * size_t tracked_person_num = 0; + * err = mv_surveillance_get_result_value( + * event_result, + * MV_SURVEILLANCE_PERSONS_TRACKED_NUMBER, + * &tracked_person_num); + * + * if (MEDIA_VISION_ERROR_NONE != err || 0 == tracked_person_num) + * return; + * + * mv_rectangle_s *track_locations = + * (mv_rectangle_s*) + * malloc(sizeof(mv_rectangle_s) * tracked_person_num); + * err = mv_surveillance_get_result_value( + * event_result, + * MV_SURVEILLANCE_PERSONS_TRACKED_LOCATIONS, + * track_locations); + * + * // Do something with locations where persons were tracked... + * + * free (track_locations); + * } + * } + * @endcode + * + * @since_tizen 3.0 + */ +#define MV_SURVEILLANCE_PERSONS_TRACKED_LOCATIONS \ + "TRACKED_PERSONS_LOCATIONS" + +/** + * @brief Name of the person recognition event type. + * + * @details This is common event for a person recognizing. The result will be + * a number of persons, which were recognized, their positions (face + * locations), labels and confidences of the recognition models (see + * documentation for @ref mv_face_recognize() in + * @ref CAPI_MEDIA_VISION_FACE_MODULE). When one subscribes to this + * event, the engine configuration must be filled by path to the saved + * face recognition model. These path should be set using + * @ref mv_engine_config_set_string_attribute() as attribute named + * @ref MV_SURVEILLANCE_FACE_RECOGNITION_MODEL_FILE_PATH. + * See documentation for face recognition in + * @ref CAPI_MEDIA_VISION_FACE_MODULE and for engine configuration in + * @ref CAPI_MEDIA_VISION_COMMON_MODULE for details. Out parameters + * (can be accessed in the @ref mv_surveillance_event_occurred_cb + * callback using @ref mv_surveillance_get_result_value() function): + * * @ref MV_SURVEILLANCE_PERSONS_RECOGNIZED_NUMBER - the number + * of persons which were recognized;\n + * * @ref MV_SURVEILLANCE_PERSONS_RECOGNIZED_LOCATIONS - the locations + * of persons which were recognized;\n + * * @ref MV_SURVEILLANCE_PERSONS_RECOGNIZED_LABELS - the labels of + * persons which were recognized;\n + * * @ref MV_SURVEILLANCE_PERSONS_RECOGNIZED_CONFIDENCES - + * the confidences values that persons were recognized correctly. + * + * @since_tizen 3.0 + */ +#define MV_SURVEILLANCE_EVENT_TYPE_PERSON_RECOGNIZED \ + "MV_SURVEILLANCE_EVENT_PERSON_RECOGNIZED" + +/** + * @brief Name of the event result value that contains number of locations where + * faces were recognized. + * @details This event result value can be accessed after event triggers of + * @ref MV_SURVEILLANCE_EVENT_TYPE_PERSON_RECOGNIZED type activation. + * Result value is of @c size_t type, so has to be casted as in + * the following example: + * @code{.c} + * void event_occurred_cb(mv_surveillance_event_trigger_h trigger, + * mv_source_h source, + * int video_stream_id, + * mv_surveillance_result_h event_result, + * void *user_data) + * { + * const char *event_type = NULL; + * int err = mv_surveillance_get_event_trigger_type(trigger, + * event_type); + * if (MEDIA_VISION_ERROR_NONE != err) return; + * + * if (0 == strncmp(event_type, + * MV_SURVEILLANCE_EVENT_TYPE_PERSON_RECOGNIZED, + * 255)) + * { + * size_t rec_person_num = 0; + * err = mv_surveillance_get_result_value( + * event_result, + * MV_SURVEILLANCE_PERSONS_RECOGNIZED_NUMBER, + * &rec_person_num); + * if (MEDIA_VISION_ERROR_NONE != err) + * return; + * + * // Do something with number of recognized persons... + * } + * } + * @endcode + * + * @since_tizen 3.0 + */ +#define MV_SURVEILLANCE_PERSONS_RECOGNIZED_NUMBER \ + "NUMBER_OF_PERSONS" + +/** + * @brief Name of the event result value that contains a set of rectangular + * locations where person faces were recognized. + * @details This event result value can be accessed after event triggers of + * @ref MV_SURVEILLANCE_EVENT_TYPE_PERSON_RECOGNIZED type activation. + * Result value is of @a mv_rectangle_s array type, so has to be casted + * as in the following example: + * @code{.c} + * void event_occurred_cb(mv_surveillance_event_trigger_h trigger, + * mv_source_h source, + * int video_stream_id, + * mv_surveillance_result_h event_result, + * void *user_data) + * { + * const char *event_type = NULL; + * int err = mv_surveillance_get_event_trigger_type(trigger, + * event_type); + * if (MEDIA_VISION_ERROR_NONE != err) return; + * + * if (0 == strncmp(event_type, + * MV_SURVEILLANCE_EVENT_TYPE_PERSON_RECOGNIZED, + * 255)) + * { + * size_t rec_person_num = 0; + * err = mv_surveillance_get_result_value( + * event_result, + * MV_SURVEILLANCE_PERSONS_RECOGNIZED_NUMBER, + * &rec_person_num); + * if (MEDIA_VISION_ERROR_NONE != err || 0 == rec_person_num) + * return; + * + * mv_rectangle_s *locations = + * (mv_rectangle_s*) + * malloc(sizeof(mv_rectangle_s) * rec_person_num); + * err = mv_surveillance_get_result_value( + * event_result, + * MV_SURVEILLANCE_PERSONS_RECOGNIZED_LOCATIONS, + * locations); + * + * // Do something with locations... + * + * free (locations); + * } + * } + * @endcode + * + * @since_tizen 3.0 + */ +#define MV_SURVEILLANCE_PERSONS_RECOGNIZED_LOCATIONS \ + "PERSONS_LOCATIONS" + +/** + * @brief Name of the event result value that contains a set of labels that + * correspond to the recognized persons. + * @details This event result value can be accessed after event triggers of + * @ref MV_SURVEILLANCE_EVENT_TYPE_PERSON_RECOGNIZED type activation. + * Result value is of integers array type, so has to be casted + * as in the following example: + * @code{.c} + * void event_occurred_cb(mv_surveillance_event_trigger_h trigger, + * mv_source_h source, + * int video_stream_id, + * mv_surveillance_result_h event_result, + * void *user_data) + * { + * const char *event_type = NULL; + * int err = mv_surveillance_get_event_trigger_type(trigger, + * event_type); + * if (MEDIA_VISION_ERROR_NONE != err) return; + * + * if (0 == strncmp(event_type, + * MV_SURVEILLANCE_EVENT_TYPE_PERSON_RECOGNIZED, + * 255)) + * { + * size_t rec_person_num = 0; + * err = mv_surveillance_get_result_value( + * event_result, + * MV_SURVEILLANCE_PERSONS_RECOGNIZED_NUMBER, + * &rec_person_num); + * if (MEDIA_VISION_ERROR_NONE != err || 0 == rec_person_num) + * return; + * + * int *labels = + * (int*)malloc(sizeof(int) * rec_person_num); + * err = mv_surveillance_get_result_value( + * event_result, + * MV_SURVEILLANCE_PERSONS_RECOGNIZED_LABELS, + * labels); + * + * // Do something with labels... + * + * free (labels); + * } + * } + * @endcode + * + * @since_tizen 3.0 + */ +#define MV_SURVEILLANCE_PERSONS_RECOGNIZED_LABELS \ + "PERSONS_LABELS" + +/** + * @brief Name of the event result value that contains a set of confidence + * values that correspond to the recognized persons. + * @details This event result value can be accessed after event triggers of + * @ref MV_SURVEILLANCE_EVENT_TYPE_PERSON_RECOGNIZED type activation. + * Result value is of doubles array type, so has to be casted + * as in the following example: + * @code{.c} + * void event_occurred_cb(mv_surveillance_event_trigger_h trigger, + * mv_source_h source, + * int video_stream_id, + * mv_surveillance_result_h event_result, + * void *user_data) + * { + * const char *event_type = NULL; + * int err = mv_surveillance_get_event_trigger_type(trigger, + * event_type); + * if (MEDIA_VISION_ERROR_NONE != err) return; + * + * if (0 == strncmp(event_type, + * MV_SURVEILLANCE_EVENT_TYPE_PERSON_RECOGNIZED, + * 255)) + * { + * size_t rec_person_num = 0; + * err = mv_surveillance_get_result_value( + * event_result, + * MV_SURVEILLANCE_PERSONS_RECOGNIZED_NUMBER, + * &rec_person_num); + * if (MEDIA_VISION_ERROR_NONE != err || 0 == rec_person_num) + * return; + * + * double *confidences = + * (double*)malloc(sizeof(double) * rec_person_num); + * err = mv_surveillance_get_result_value( + * event_result, + * MV_SURVEILLANCE_PERSONS_RECOGNIZED_CONFIDENCES, + * confidences); + * + * // Do something with confidences... + * + * free (confidences); + * } + * } + * @endcode + * + * @since_tizen 3.0 + */ +#define MV_SURVEILLANCE_PERSONS_RECOGNIZED_CONFIDENCES \ + "PERSONS_CONFIDENCES" + +/** + * @} + */ + +/** + * @addtogroup CAPI_MEDIA_VISION_SURVEILLANCE_MODULE + * @{ + */ + +/** + * @brief Defines MV_SURVEILLANCE_FACE_RECOGNITION_MODEL_FILE_PATH to set face + * recognition model file path. It is an attribute of the engine + * configuration. + * @details This value HAS TO BE set in engine configuration before subscription + * on @ref MV_SURVEILLANCE_EVENT_TYPE_PERSON_RECOGNIZED event trigger + * + * @since_tizen 3.0 + * @see mv_engine_config_set_string_attribute() + * @see mv_engine_config_get_string_attribute() + */ +#define MV_SURVEILLANCE_FACE_RECOGNITION_MODEL_FILE_PATH \ + "MV_SURVEILLANCE_FACE_RECOGNITION_MODEL_FILE_PATH" + +/** + * @brief Defines MV_SURVEILLANCE_MOVEMENT_DETECTION_THRESOLD to set movement + * detection threshold. It is an attribute of the engine configuration. + * @details This value might be set in engine configuration before subscription + * on @ref MV_SURVEILLANCE_EVENT_TYPE_MOVEMENT_DETECTED event trigger + * to specify sensitivity of the movement detector. This value has to + * be integer in 0..255 range where 255 means that no movements will + * be detected, and 0 means that all frame changes will be interpreted + * as movements. Default value is 10. + * + * @since_tizen 3.0 + * @see mv_engine_config_set_int_attribute() + * @see mv_engine_config_get_int_attribute() + */ +#define MV_SURVEILLANCE_MOVEMENT_DETECTION_THRESHOLD \ + "MV_SURVEILLANCE_MOVEMENT_DETECTION_THRESHOLD" + +/** + * @brief Defines MV_SURVEILLANCE_SKIP_FRAMES_COUNT to set how many frames + * will be skipped during push source. It is an attribute of the engine + * configuration. + * @details This integer value might be set in engine configuration to specify + * number of @ref mv_surveillance_push_source() function calls will be + * ignored by subscription of the event trigger. Default value is 0. + * It means that no frames will be skipped and all + * @ref mv_surveillance_push_source() function calls will be processed. + * + * @since_tizen 3.0 + * @see mv_engine_config_set_int_attribute() + * @see mv_engine_config_get_int_attribute() + */ +#define MV_SURVEILLANCE_SKIP_FRAMES_COUNT "MV_SURVEILLANCE_SKIP_FRAMES_COUNT" + +/** + * @brief The handle to event trigger. + * + * @since_tizen 3.0 + * @remarks See supported event types and their descriptions in + * @ref CAPI_MEDIA_VISION_SURVEILLANCE_EVENT_TYPES documentation + * section. + * Also the list of supported events can be obtained using + * @ref mv_surveillance_foreach_supported_event_type() function + */ +typedef void *mv_surveillance_event_trigger_h; + +/** + * @brief The handle to event trigger activation result. + * @details Result is a handle to the output values which are specific for each event. + * See the output values names in the event types descriptions located + * in @ref CAPI_MEDIA_VISION_SURVEILLANCE_EVENT_TYPES documentation + * section. + * Result values can be gotten by @ref mv_surveillance_get_result_value() + * function one by one in order specified in the event description ( + * the same order of event value names is supported by + * @ref mv_surveillance_foreach_event_result_name() function). + * This pointer will be destroyed when + * @ref mv_surveillance_event_occurred_cb passed. + * + * @since_tizen 3.0 + */ +typedef void *mv_surveillance_result_h; + +/** + * @brief Called when event trigger activation has been detected. + * + * @since_tizen 3.0 + * @remarks Handle @a event_result is valid only inside callback + * @param [in] trigger The event trigger handle + * @param [in] source The handle to the media source + * @param [in] video_stream_id The identifier of the video source where event + * has been detected + * @param [in] event_result The event result passed from the + * @ref mv_surveillance_subscribe_event_trigger() + * @param [in] user_data The user data passed from the + * @ref mv_surveillance_subscribe_event_trigger() + * function + * + * @pre Callback can be invoked only after + * @ref mv_surveillance_subscribe_event_trigger() + * was called for particular event trigger. + * + * @see mv_surveillance_subscribe_event_trigger() + * @see mv_surveillance_unsubscribe_event_trigger() + */ +typedef void (*mv_surveillance_event_occurred_cb)( + mv_surveillance_event_trigger_h trigger, + mv_source_h source, + int video_stream_id, + mv_surveillance_result_h event_result, + void *user_data); + +/** + * @brief Called to get the information once for each supported event type. + * + * @since_tizen 3.0 + * @remarks Don't release memory of @a event_type + * @param [in] event_type Character string containing name of the event type + * @param [in] user_data The user data passed from the + * @ref mv_surveillance_foreach_supported_event_type() + * function + * @return @c true to continue with the next iteration of the loop, \n + * otherwise @c false to break out of the loop + * + * @pre mv_surveillance_foreach_supported_event_type() will invoke this callback + * @see mv_surveillance_foreach_supported_event_type() + */ +typedef bool (*mv_surveillance_event_type_cb)( + const char *event_type, + void *user_data); + +/** + * @brief Called to get the result name from the triggered event. + * + * @since_tizen 3.0 + * @remarks Don't release memory of @a value_name + * @param [in] name Character string containing the name of value that + * can be obtained from @ref mv_surveillance_result_h + * handle by @ref mv_surveillance_get_result_value() + * function + * @param [in] user_data The user data passed from the + * @ref mv_surveillance_foreach_event_result_name() + * function + * @return @c true to continue with the next iteration of the loop, \n + * otherwise @c false to break out of the loop + * + * @pre mv_surveillance_foreach_event_result_name() will invoke this + * callback + * @see mv_surveillance_foreach_event_result_name() + */ +typedef bool (*mv_surveillance_event_result_name_cb)( + const char *name, + void *user_data); + +/** + * @brief Creates surveillance event trigger handle. + * + * @since_tizen 3.0 + * @remarks List of supported event types can be obtained by + * @ref mv_surveillance_foreach_supported_event_type function + * @remarks You must release @a trigger by using + * @ref mv_surveillance_event_trigger_destroy() + * @param [in] event_type Name of the event type to be supported by the + * @a trigger + * @param [out] trigger A new handle to the event trigger + * @return @c 0 on success, otherwise a negative error value + * @retval #MEDIA_VISION_ERROR_NONE Successful + * @retval #MEDIA_VISION_ERROR_INVALID_PARAMETER Invalid parameter + * @retval #MEDIA_VISION_ERROR_OUT_OF_MEMORY Out of memory + * @retval #MEDIA_VISION_ERROR_NOT_SUPPORTED Not supported + * + * @see mv_surveillance_event_trigger_destroy() + * @see mv_surveillance_foreach_supported_event_type() + */ +int mv_surveillance_event_trigger_create( + const char *event_type, + mv_surveillance_event_trigger_h *trigger); + +/** + * @brief Destroys the surveillance event trigger handle and releases all its + * resources. + * + * @since_tizen 3.0 + * @param [in] trigger The handle to the event trigger to be destroyed + * @return @c 0 on success, otherwise a negative error value + * @retval #MEDIA_VISION_ERROR_NONE Successful + * @retval #MEDIA_VISION_ERROR_INVALID_PARAMETER Invalid parameter + * @retval #MEDIA_VISION_ERROR_NOT_SUPPORTED Not supported + * + * @see mv_surveillance_event_trigger_create + */ +int mv_surveillance_event_trigger_destroy( + mv_surveillance_event_trigger_h trigger); + +/** + * @brief Gets the surveillance event trigger type as character string. + * + * @since_tizen 3.0 + * @remarks The @a event_type should be freed using free() + * @param [in] trigger The handle to the event trigger + * @param [out] event_type The pointer to the character string which will be + * filled by textual name of the event type + * @return @c 0 on success, otherwise a negative error value + * @retval #MEDIA_VISION_ERROR_NONE Successful + * @retval #MEDIA_VISION_ERROR_INVALID_PARAMETER Invalid parameter + * @retval #MEDIA_VISION_ERROR_NOT_SUPPORTED Not supported + * + * @pre Event trigger has to be created by + * @ref mv_surveillance_event_trigger_create() function + */ +int mv_surveillance_get_event_trigger_type( + mv_surveillance_event_trigger_h trigger, + char **event_type); + +/** + * @brief Sets ROI (Region Of Interest) to the event trigger. + * @details When ROI is set for the event trigger, then event check for this + * @a triger will be performed only inside the polygonal region + * determined by @a roi parameter. + * If this method has been never called for the @a trigger, then event + * will be checked for the whole input frame (event check is performed + * for each @ref mv_surveillance_push_source() function call). + * It is possible to change the ROI between + * @ref mv_surveillance_push_source() calls. + * + * @since_tizen 3.0 + * @param [in] trigger The handle to the event trigger + * @param [in] number_of_points The number of ROI points + * @param [in] roi The input array with ROI points + * @return @c 0 on success, otherwise a negative error value + * @retval #MEDIA_VISION_ERROR_NONE Successful + * @retval #MEDIA_VISION_ERROR_INVALID_PARAMETER Invalid parameter + * @retval #MEDIA_VISION_ERROR_NOT_SUPPORTED Not supported + * + * @pre Event trigger has to be created by + * @ref mv_surveillance_event_trigger_create() function + * + * @see mv_surveillance_event_trigger_h + * @see mv_surveillance_get_event_trigger_roi() + */ +int mv_surveillance_set_event_trigger_roi( + mv_surveillance_event_trigger_h trigger, + int number_of_points, + mv_point_s *roi); + +/** + * @brief Gets ROI (Region Of Interest) from the event trigger. + * + * @since_tizen 3.0 + * @remark If @ref mv_surveillance_set_event_trigger_roi() has been never + * called for @a trigger, then @a number_of_points output value will be + * zero and @a roi pointer will be not changed. + * @param [in] trigger The handle to the event trigger + * @param [out] number_of_points The number of ROI points + * @param [out] roi The output array with ROI points + * @return @c 0 on success, otherwise a negative error value + * @retval #MEDIA_VISION_ERROR_NONE Successful + * @retval #MEDIA_VISION_ERROR_INVALID_PARAMETER Invalid parameter + * @retval #MEDIA_VISION_ERROR_NOT_SUPPORTED Not supported + * + * @pre Event trigger has to be created by + * @ref mv_surveillance_event_trigger_create() function + * + * @post Memory for @a roi array must be released + * + * @see mv_surveillance_event_trigger_h + * @see mv_surveillance_set_event_trigger_roi() + */ +int mv_surveillance_get_event_trigger_roi( + mv_surveillance_event_trigger_h trigger, + int *number_of_points, + mv_point_s **roi); + +/** + * @brief Subscribes @a trigger to process sources pushed from video identified + * by @a video_stream_id. + * @details When @a trigger is subscribed, then each time when function + * @ref mv_surveillance_push_source() is called for @a video_stream_id, + * event occurrence is checked. If this check is successful, + * @a callback is invoked. Details on occurred event can be obtained + * using @ref mv_surveillance_result_h handle from @a callback. + * + * @since_tizen 3.0 + * @remarks Use @ref mv_surveillance_unsubscribe_event_trigger() function for + * the same @a trigger and @a video_stream_id parameters to stop + * subscription. + * @param [in] trigger The handle to the event trigger activating + * calls of the @a callback function + * @param [in] video_stream_id The identifier of the video stream for which + * event trigger activation will be checked + * @param [in] engine_cfg The engine configuration of the event + * @param [in] callback Callback to be called each time when event + * occurrence is detected + * @param [in] user_data The user data to be passed to the @a callback + * function + * @return @c 0 on success, otherwise a negative error value + * @retval #MEDIA_VISION_ERROR_NONE Successful + * @retval #MEDIA_VISION_ERROR_INVALID_PARAMETER Invalid parameter + * @retval #MEDIA_VISION_ERROR_NOT_SUPPORTED Not supported + * + * @post @ref mv_surveillance_event_occurred_cb will be called each time + * @a trigger is activated after @ref mv_surveillance_push_source() call + * + * @see mv_surveillance_event_trigger_h + * @see mv_surveillance_unsubscribe_event_trigger() + * @see mv_surveillance_push_source() + */ +int mv_surveillance_subscribe_event_trigger( + mv_surveillance_event_trigger_h trigger, + int video_stream_id, + mv_engine_config_h engine_cfg, + mv_surveillance_event_occurred_cb callback, + void *user_data); + +/** + * @brief Unsubscribes @a trigger from the event and stop calling @a callback. + * + * @since_tizen 3.0 + * @remarks To start handling trigger activation use + @ref mv_surveillance_subscribe_event_trigger(). + * @param [in] trigger The handle to the event trigger for which + * subscription will be stopped + * @param [in] video_stream_id The identifier of the video source for which + * subscription will be stopped + * @return @c 0 on success, otherwise a negative error value + * @retval #MEDIA_VISION_ERROR_NONE Successful + * @retval #MEDIA_VISION_ERROR_INVALID_PARAMETER Invalid parameter + * @retval #MEDIA_VISION_ERROR_NOT_SUPPORTED Not supported + * + * @pre To stop subscription it has to be created earlier with + * @ref mv_surveillance_subscribe_event_trigger() function + * + * @see mv_surveillance_event_trigger_h + * @see mv_surveillance_subscribe_event_trigger() + */ +int mv_surveillance_unsubscribe_event_trigger( + mv_surveillance_event_trigger_h trigger, + int video_stream_id); + +/** + * @brief Pushes source to the surveillance system to detect events. + * @details mv_surveillance_event_occurred_cb() will be called when any + * subscribing event detected. + * + * @since_tizen 3.0 + * @remarks @ref mv_surveillance_set_event_trigger_roi() function can be used + * to specify the polygon region where event can be detected only + * @param [in] source The handle to the media source + * @param [in] video_stream_id The identifier of video stream from which + * @a source is coming + * @return @c 0 on success, otherwise a negative error value + * @retval #MEDIA_VISION_ERROR_NONE Successful + * @retval #MEDIA_VISION_ERROR_INVALID_PARAMETER Invalid parameter + * @retval #MEDIA_VISION_ERROR_NOT_SUPPORTED Not supported + * + * @pre To receive surveillance results, some event triggers has to be + * subscribed by @ref mv_surveillance_subscribe_event_trigger() function + * before @ref mv_surveillance_push_source() calls + * @pre Before calling of this method @a source has to be correctly filled. + * @ref mv_source_fill_by_media_packet(), @ref mv_source_fill_by_buffer() + * functions can be used to fill @a source + * + * @see mv_surveillance_event_trigger_h + * @see mv_surveillance_event_occurred_cb + * @see mv_surveillance_subscribe_event_trigger() + * @see mv_surveillance_unsubscribe_event_trigger() + */ +int mv_surveillance_push_source( + mv_source_h source, + int video_stream_id); + +/** + * @brief Starts traversing through list of supported event types. + * + * @since_tizen 3.0 + * @remarks Supported event types and their descriptions can be found in + * @ref CAPI_MEDIA_VISION_SURVEILLANCE_EVENT_TYPES documentation + * section + * @param [in] callback The callback function to be called for each + * supported event type + * @param [in] user_data The user data to be passed to the @a callback + * function + * @return @c 0 on success, otherwise a negative error value + * @retval #MEDIA_VISION_ERROR_NONE Successful + * @retval #MEDIA_VISION_ERROR_INVALID_PARAMETER Invalid parameter + * @retval #MEDIA_VISION_ERROR_NOT_SUPPORTED Not supported + * + * @see mv_surveillance_event_type_cb + * @see mv_surveillance_foreach_event_result_name() + */ +int mv_surveillance_foreach_supported_event_type( + mv_surveillance_event_type_cb callback, + void *user_data); + +/** + * @brief Starts traversing through list of supported event result value names. + * + * @since_tizen 3.0 + * @remarks Supported event types, event result value names and their + * descriptions can be found in + * @ref CAPI_MEDIA_VISION_SURVEILLANCE_EVENT_TYPES documentation + * section + * @param [in] event_type The name of the event type for which result value + * names will be passed to the @a callback. Can be + * set @c NULL. If set @c NULL then all supported + * event result value names will be traversed + * @param [in] callback The callback function to be called for each + * supported event result value name + * @param [in] user_data The user data to be passed to the @a callback + * function + * @return @c 0 on success, otherwise a negative error value + * @retval #MEDIA_VISION_ERROR_NONE Successful + * @retval #MEDIA_VISION_ERROR_INVALID_PARAMETER Invalid parameter + * @retval #MEDIA_VISION_ERROR_NOT_SUPPORTED Not supported + * + * @see mv_surveillance_event_result_name_cb + * @see mv_surveillance_foreach_supported_event_type() + * @see mv_surveillance_get_result_value() + */ +int mv_surveillance_foreach_event_result_name( + const char *event_type, + mv_surveillance_event_result_name_cb callback, + void *user_data); + +/** + * @brief Gets result value. + * @details See the output values names in the event types descriptions located + * in @ref CAPI_MEDIA_VISION_SURVEILLANCE_EVENT_TYPES documentation + * section. + * + * @since_tizen 3.0 + * @remarks The name can be obtained by + * @ref mv_surveillance_foreach_event_result_name function + * @param [in] result The handle to the event result + * @param [in] name The name of the value to be gotten + * @param [in, out] value The pointer to variable which will be filled + * by result value. To find the type of @a value + * please refer to the + * @ref CAPI_MEDIA_VISION_SURVEILLANCE_EVENT_TYPES + * documentation section + * @return @c 0 on success, otherwise a negative error value + * @retval #MEDIA_VISION_ERROR_NONE Successful + * @retval #MEDIA_VISION_ERROR_INVALID_PARAMETER Invalid parameter + * @retval #MEDIA_VISION_ERROR_NOT_SUPPORTED Not supported + * + * @pre Memory for value has to be allocated + * + * @see mv_surveillance_event_trigger_h + * @see mv_surveillance_event_occurred_cb + * @see mv_surveillance_subscribe_event_trigger() + * @see mv_surveillance_unsubscribe_event_trigger() + * @see mv_surveillance_foreach_supported_event_type() + * @see mv_surveillance_foreach_event_result_name() + */ +int mv_surveillance_get_result_value( + mv_surveillance_result_h result, + const char *name, + void *value); + +/** + * @} + */ + +#ifdef __cplusplus +} +#endif /* __cplusplus */ + +#endif /* __TIZEN_MEDIAVISION_SURVEILLANCE_H__ */ diff --git a/include/mv_surveillance_private.h b/include/mv_surveillance_private.h new file mode 100644 index 0000000..4dc4bad --- /dev/null +++ b/include/mv_surveillance_private.h @@ -0,0 +1,48 @@ +/** + * Copyright (c) 2015 Samsung Electronics Co., Ltd All Rights Reserved + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#ifndef __TIZEN_MEDIAVISION_SURVEILLANCE_PRIVATE_H__ +#define __TIZEN_MEDIAVISION_SURVEILLANCE_PRIVATE_H__ + +#ifdef __cplusplus +extern "C" { +#endif /* __cplusplus */ + +#include "mv_common.h" + +/** + * @brief Event trigger structure. + * @details See supported event types and their descriptions in + * @ref CAPI_MEDIA_VISION_SURVEILLANCE_EVENT_TYPES documentation + * section. + * Also the list of supported events can be obtained using + * @ref mv_surveillance_foreach_supported_event_type() function + * + * @since_tizen 3.0 + */ +typedef struct +{ + unsigned int trigger_id; /**< Unique event trigger identifier */ + const char *event_type; /**< Type of the event */ + int number_of_roi_points; /**< Number of ROI (Region of interest) points */ + mv_point_s *roi; /**< ROI points array */ +} mv_surveillance_event_trigger_s; + +#ifdef __cplusplus +} +#endif /* __cplusplus */ + +#endif /* __TIZEN_MEDIAVISION_SURVEILLANCE_PRIVATE_H__ */ diff --git a/media-vision-config.json b/media-vision-config.json index 2f0b46c..207e575 100644 --- a/media-vision-config.json +++ b/media-vision-config.json @@ -114,6 +114,21 @@ "name" : "MV_FACE_RECOGNITION_MODEL_TYPE", "type" : "integer", "value" : 3 + }, + { + "name" : "MV_SURVEILLANCE_FACE_RECOGNITION_MODEL_FILE_PATH", + "type" : "string", + "value" : "" + }, + { + "name" : "MV_SURVEILLANCE_MOVEMENT_DETECTION_THRESHOLD", + "type" : "integer", + "value" : 10 + }, + { + "name" : "MV_SURVEILLANCE_SKIP_FRAMES_COUNT", + "type" : "integer", + "value" : 0 } ] } diff --git a/mv_surveillance/CMakeLists.txt b/mv_surveillance/CMakeLists.txt new file mode 100644 index 0000000..e6a0a44 --- /dev/null +++ b/mv_surveillance/CMakeLists.txt @@ -0,0 +1,8 @@ +project(mv_surveillance_port) +cmake_minimum_required(VERSION 2.6) + +if(MEDIA_VISION_SURVEILLANCE_LICENSE_PORT) + add_subdirectory(${PROJECT_SOURCE_DIR}/surveillance_lic) # Licensed port +else() + add_subdirectory(${PROJECT_SOURCE_DIR}/surveillance) # Open port +endif() diff --git a/mv_surveillance/surveillance/CMakeLists.txt b/mv_surveillance/surveillance/CMakeLists.txt new file mode 100644 index 0000000..b7b5f03 --- /dev/null +++ b/mv_surveillance/surveillance/CMakeLists.txt @@ -0,0 +1,26 @@ +project(${MV_SURVEILLANCE_LIB_NAME}) +cmake_minimum_required(VERSION 2.6) + +set_property(DIRECTORY APPEND PROPERTY COMPILE_DEFINITIONS_DEBUG _DEBUG) + +set(CMAKE_ARCHIVE_OUTPUT_DIRECTORY ${CMAKE_BINARY_DIR}/${LIB_INSTALL_DIR}) +set(CMAKE_LIBRARY_OUTPUT_DIRECTORY ${CMAKE_BINARY_DIR}/${LIB_INSTALL_DIR}) +set(CMAKE_RUNTIME_OUTPUT_DIRECTORY ${CMAKE_BINARY_DIR}/bin) + +include_directories("${INC_DIR}") +include_directories("${PROJECT_SOURCE_DIR}/include") +include_directories("${PROJECT_SOURCE_DIR}/src") + +file(GLOB MV_SURVEILLANCE_INC_LIST "${PROJECT_SOURCE_DIR}/include/*.h") +file(GLOB MV_SURVEILLANCE_SRC_LIST "${PROJECT_SOURCE_DIR}/src/*.cpp" + "${PROJECT_SOURCE_DIR}/src/*.c") + +if(FORCED_STATIC_BUILD) + add_library(${PROJECT_NAME} STATIC ${MV_SURVEILLANCE_INC_LIST} ${MV_SURVEILLANCE_SRC_LIST}) +else() + add_library(${PROJECT_NAME} SHARED ${MV_SURVEILLANCE_INC_LIST} ${MV_SURVEILLANCE_SRC_LIST}) +endif() + +target_link_libraries(${PROJECT_NAME} ${MV_COMMON_LIB_NAME} ${MV_FACE_LIB_NAME}) + +INSTALL(TARGETS ${PROJECT_NAME} DESTINATION ${LIB_INSTALL_DIR}) diff --git a/mv_surveillance/surveillance/include/EventDefs.h b/mv_surveillance/surveillance/include/EventDefs.h new file mode 100644 index 0000000..9d92ade --- /dev/null +++ b/mv_surveillance/surveillance/include/EventDefs.h @@ -0,0 +1,71 @@ +/** + * Copyright (c) 2015 Samsung Electronics Co., Ltd All Rights Reserved + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#ifndef __MEDIA_VISION_EVENT_DEFS_H__ +#define __MEDIA_VISION_EVENT_DEFS_H__ + +/** + * @file EventDefs.h + * @brief This file contains definitions for event triggers. + */ + +#include + +#include +#include +#include +#include + +#include + +namespace mediavision { +namespace surveillance { + +typedef std::map > EventTypesMap; +typedef EventTypesMap::iterator EventTypesMapIter; +typedef EventTypesMap::const_iterator EventTypesMapConstIter; + +typedef std::vector StringVector; +typedef StringVector::iterator StringIter; +typedef StringVector::const_iterator StringConstIter; + +typedef std::vector IntVector; +typedef IntVector::iterator IntIter; +typedef IntVector::const_iterator IntConstIter; + +typedef std::vector DoubleVector; +typedef DoubleVector::iterator DoubleIter; +typedef DoubleVector::const_iterator DoubleConstIter; + +typedef std::vector MVRectangles; +typedef MVRectangles::iterator MVRectanglesIter; +typedef MVRectangles::const_iterator MVRectanglesConstIter; + +typedef std::vector CVRectangles; +typedef CVRectangles::iterator CVRectanglesIter; +typedef CVRectangles::const_iterator CVRectanglesConstIter; + +typedef std::vector MVPoints; +typedef MVPoints::iterator MVPointsIter; +typedef MVPoints::const_iterator MVPointsConstIter; + +typedef std::vector CVPoints; +typedef std::vector Contours; + +} /* surveillance */ +} /* mediavision */ + +#endif /* __MEDIA_VISION_EVENT_DEFS_H__ */ diff --git a/mv_surveillance/surveillance/include/EventManager.h b/mv_surveillance/surveillance/include/EventManager.h new file mode 100644 index 0000000..fd13f52 --- /dev/null +++ b/mv_surveillance/surveillance/include/EventManager.h @@ -0,0 +1,190 @@ +/** + * Copyright (c) 2015 Samsung Electronics Co., Ltd All Rights Reserved + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#ifndef __MEDIA_VISION_EVENT_MANAGER_H__ +#define __MEDIA_VISION_EVENT_MANAGER_H__ + +/** + * @file EventManager.h + * @brief This file contains functionality for event manager. + */ + +#include "EventTrigger.h" +#include "EventDefs.h" + +namespace mediavision { +namespace surveillance { + +class EventManager; + +/** + * @class EventManagerDestroyer + * @brief This class contains event manager destroyer functionality. + * + * @since_tizen 3.0 + */ +class EventManagerDestroyer { +public: + + /** + * @brief Default destructor. + * + * @since_tizen 3.0 + */ + ~EventManagerDestroyer(); + + /** + * @brief Initializes pointer to EventManager which will be destroyed. + * + * @since_tizen 3.0 + * @param [in] pointer The pointer to EventManager which will be destroyed + */ + void initialize(EventManager *pointer); + +private: + + EventManager *__pInstance; +}; + +/** + * @class EventManager + * @brief This class contains event manager functionality. + * + * @since_tizen 3.0 + */ + +class EventManager { +public: + + /** + * @brief Gets EventManager instance. + * + * @since_tizen 3.0 + */ + static EventManager& getInstance(); + + /** + * @brief Registers event. + * + * @since_tizen 3.0 + * @param [in] eventTrigger The event trigger to be register (NULL if internal) + * @param [in] triggerId Unique event trigger identifier to be register + * @param [in] eventType Type of the event + * @param [in] videoStreamId Video stream identificator + * @param [in] engineCfg The engine configuration for event trigger + * @param [in] callback The callback to be called if event will be occured + * @param [in] user_data The user data to be passed to the callback function + * @param [in] numberOfPoints The number of ROI points + * @param [in] roi The intput array with ROI points + * @return @c 0 on success, otherwise a negative error value + */ + int registerEvent( + mv_surveillance_event_trigger_h eventTrigger, + long int triggerId, + const char *eventType, + int videoStreamId, + mv_engine_config_h engineCfg, + mv_surveillance_event_occurred_cb callback, + void *user_data, + int numberOfPoints, + mv_point_s *roi); + + /** + * @brief Unregisters event. + * + * @since_tizen 3.0 + * @param [in] triggerId Unique event trigger identifier to be + * unregister + * @param [in] videoStreamId Video stream identifier for which event + * will be unregistered + * @return @c 0 on success, otherwise a negative error value + */ + int unregisterEvent(long int triggerId, int videoStreamId); + + /** + * @brief Pushes media source to run event triggers. + * + * @since_tizen 3.0 + * @param [in] source The media source to be pushed + * @param [in] videoStreamId The video stream identificator for media source + * @return @c 0 on success, otherwise a negative error value + */ + int pushSource(mv_source_h source, int videoStreamId); + + /** + * @brief Gets supported event types. + * + * @since_tizen 3.0 + * @param [out] eventTypes The supported event types + * @return @c 0 on success, otherwise a negative error value + */ + static int getSupportedEventTypes(StringVector& eventTypes); + + /** + * @brief Gets all supported event result value names. + * + * @since_tizen 3.0 + * @param [out] eventResValNames The supported event result value names + * @return @c 0 on success, otherwise a negative error value + */ + static int getSupportedEventResultValueNames(StringVector& eventResValNames); + + /** + * @brief Gets supported event result value names for an event type. + * + * @since_tizen 3.0 + * @param [in] eventTypeName The name of the event type to return + * result value names for + * @param [out] eventResValNames The supported event result value names + * @return @c 0 on success, otherwise a negative error value + */ + static int getSupportedEventResultValueNames( + const std::string& eventTypeName, + StringVector& eventResValNames); + +private: + + EventManager(); + + EventManager(const EventManager&); + + EventManager& operator=(EventManager&); + + ~EventManager(); + + static void setSupportedEventTypes(); + + EventTriggersIter isTriggerExists(EventTrigger *trigger, int videoStreamId); + + friend class EventManagerDestroyer; + +private: + + static EventManager *__pInstance; + + static EventManagerDestroyer Destroyer; + + static EventTypesMap SupportedEventTypes; + +private: + + EventTriggersMap __eventTriggers; +}; + +} /* surveillance */ +} /* mediavision */ + +#endif /* __MEDIA_VISION_EVENT_MANAGER_H__ */ diff --git a/mv_surveillance/surveillance/include/EventResult.h b/mv_surveillance/surveillance/include/EventResult.h new file mode 100644 index 0000000..7ce8f45 --- /dev/null +++ b/mv_surveillance/surveillance/include/EventResult.h @@ -0,0 +1,59 @@ +/** + * Copyright (c) 2015 Samsung Electronics Co., Ltd All Rights Reserved + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#ifndef __MEDIA_VISION_EVENT_RESULT_H__ +#define __MEDIA_VISION_EVENT_RESULT_H__ + +/** + * @file EventTrigger.h + * @brief This file contains interface for event trigger. + */ + +namespace mediavision { +namespace surveillance { + +/** + * @class EventResult + * @brief This class contains event result interface. + * + * @since_tizen 3.0 + */ +class EventResult { +public: + /** + * @brief Default destructor. + * + * @since_tizen 3.0 + */ + virtual ~EventResult() {} + + /** + * @brief Gets result value. + * + * @since_tizen 3.0 + * @param [in] valueName The name of the value to be gotten + * @param [in, out] value The pointer to variable which will be filled + * by result value + * @return @c 0 on success, otherwise a negative error value + */ + virtual int getResultValue(const char *valueName, void *value) const = 0; +}; + + +} /* surveillance */ +} /* mediavision */ + +#endif /* _MEDIA_VISION__EVENT_RESULT_H__ */ diff --git a/mv_surveillance/surveillance/include/EventTrigger.h b/mv_surveillance/surveillance/include/EventTrigger.h new file mode 100644 index 0000000..d585195 --- /dev/null +++ b/mv_surveillance/surveillance/include/EventTrigger.h @@ -0,0 +1,227 @@ +/** + * Copyright (c) 2015 Samsung Electronics Co., Ltd All Rights Reserved + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#ifndef __MEDIA_VISION_EVENT_TRIGGER_H__ +#define __MEDIA_VISION_EVENT_TRIGGER_H__ + +/** + * @file EventTrigger.h + * @brief This file contains interface for event trigger. + */ + +#include "EventDefs.h" + +#include +#include + +#include +#include +#include + +namespace mediavision { +namespace surveillance { + +/** + * @class EventTrigger + * @brief This class contains event trigger interface. + * + * @since_tizen 3.0 + */ +class EventTrigger { +public: + /** + * @brief Default constructor. + * + * @since_tizen 3.0 + * @param [in] eventTrigger The event trigger to be register (NULL if internal) + * @param [in] triggerId Unique event trigger identifier to be register + * @param [in] videoStreamId Video stream identifier + * @param [in] callback The callback to be called if event will be occured + * @param [in] user_data The user data to be passed to the callback function + * @param [in] numberOfPoints The number of ROI points + * @param [in] roi The intput array with ROI points + */ + EventTrigger( + mv_surveillance_event_trigger_h eventTrigger, + long int triggerId, + int videoStreamId, + mv_surveillance_event_occurred_cb callback, + void *userData, + int numberOfPoints, + mv_point_s *roi); + + /** + * @brief Default destructor. + * + * @since_tizen 3.0 + */ + virtual ~EventTrigger(); + + /** + * @brief Parses engine configuration. + * + * @since_tizen 3.0 + * @param [in] engineConfig The engine configuration to be parsed + * @return @c 0 on success, otherwise a negative error value + */ + virtual int parseEngineConfig(mv_engine_config_h engineConfig) = 0; + + /** + * @brief Pushes media source. + * + * @since_tizen 3.0 + * @param [in] source The media source to be parsed + * @param [in] graySource The media source converted to gray scale + * @param [in] grayImage The converted to gray scale source + * @return @c 0 on success, otherwise a negative error value + */ + virtual int pushSource( + mv_source_h source, + mv_source_h graySource, + const cv::Mat& grayImage) = 0; + + /** + * @brief Gets event type. + * + * @since_tizen 3.0 + * @return string with event type + */ + virtual std::string getEventType() const = 0; + + /** + * @brief Gets video stream identifier of event trigger. + * + * @since_tizen 3.0 + * @return video stream identifier + */ + int getVideoStreamId() const; + + /** + * @brief Checks if callback with the identifier is subscribed. + * + * @since_tizen 3.0 + * @return true if suscribed, false otherwise + */ + bool isCallbackSubscribed(long int triggerId) const; + + /** + * @brief Subscibes callback with unique identifier. + * + * @since_tizen 3.0 + * @param [in] eventTrigger The event trigger to be register (NULL if internal) + * @param [in] triggerId Unique event trigger identifier to be subscribed + * @param [in] callback The callback to be called if event will be occured + * @param [in] user_data The user data to be passed to the callback function + * @param [in] numberOfPoints The number of ROI points + * @param [in] roi The intput array with ROI points + * @return @c true on success, false otherwise + */ + bool subscribeCallback( + mv_surveillance_event_trigger_h eventTrigger, + long int triggerId, + mv_surveillance_event_occurred_cb callback, + void *userData, + int numberOfPoints, + mv_point_s *roi); + + /** + * @brief Unsubscibes callback with unique identifier. + * + * @since_tizen 3.0 + * @param [in] triggerId Unique event trigger identifier to be unsubscribed + * @return @c true on success, false otherwise + */ + bool unsubscribeCallback(long int triggerId); + + /** + * @brief Checks if there are no subscribed callbacks. + * + * @since_tizen 3.0 + * @return @c true at least one callback is subscribed, false otherwise + */ + bool isCallbacksEmpty() const; + + /** + * @brief Applies ROI (Region Of Interest) to input image. + * + * @since_tizen 3.0 + * @param [in, out] image The input image where ROI will be applied + * @param [in] imageWidth The input image width + * @param [in] imageHeight The input image height + * @param [in] scalePoints True if ROI points must be scaled, false oterwise + * @param [in] scaleX The scale for X ROI point coordinate + * @param [in] scaleY The scale for Y ROI point coordinate + * @return @c true on success, false otherwise + */ + int applyROIToImage( + unsigned char *image, + int imageWidth, + int imageHeight, + bool scalePoints = false, + int scaleX = 1, + int scaleY = 1); + + /** + * @brief Comparison operator for equal case. + * + * @since_tizen 3.0 + * @return true if event trigger is equal to other, false otherwise + */ + virtual bool operator==(const EventTrigger& other) const; + + /** + * @brief Comparison operator for not equal case. + * + * @since_tizen 3.0 + * @return true if event trigger is not equal to other, false otherwise + */ + virtual bool operator!=(const EventTrigger& other) const; + +protected: + struct CallbackData { + mv_surveillance_event_trigger_h eventTrigger; + + mv_surveillance_event_occurred_cb callback; + + void *userData; + }; + + typedef std::map CallbackDataMap; + typedef CallbackDataMap::const_iterator CallbackDataMapConstIter; + typedef CallbackDataMap::iterator CallbackDataMapIter; + + typedef std::pair CallbackDataPair; + +protected: + static long int InternalTriggersCounter; + +protected: + int __videoStreamId; + + MVPoints __roi; + + CallbackDataMap __callbackDataMap; +}; + +typedef std::list EventTriggers; +typedef std::map EventTriggersMap; +typedef EventTriggers::const_iterator EventTriggersConstIter; +typedef EventTriggers::iterator EventTriggersIter; + +} /* surveillance */ +} /* mediavision */ + +#endif /* __MEDIA_VISION_EVENT_TRIGGER_H__ */ diff --git a/mv_surveillance/surveillance/include/EventTriggerMovementDetection.h b/mv_surveillance/surveillance/include/EventTriggerMovementDetection.h new file mode 100644 index 0000000..e3b57a9 --- /dev/null +++ b/mv_surveillance/surveillance/include/EventTriggerMovementDetection.h @@ -0,0 +1,159 @@ +/** + * Copyright (c) 2015 Samsung Electronics Co., Ltd All Rights Reserved + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#ifndef __MEDIA_VISION_EVENT_TRIGGER_MOVEMENT_DETECTION_H__ +#define __MEDIA_VISION_EVENT_TRIGGER_MOVEMENT_DETECTION_H__ + +/** + * @file EventTriggerMovementDetection.h + * @brief This file contains interface for movement detection events. + */ + +#include "EventTrigger.h" + +#include "EventResult.h" +#include "EventDefs.h" + +#include + +namespace mediavision { +namespace surveillance { + +/** + * @class EventResultMovementDetection + * @brief This class contains movement detection event results. + * + * @since_tizen 3.0 + */ +class EventResultMovementDetection : public EventResult { +public: + /** + * @brief Gets result value. + * + * @since_tizen 3.0 + * @param [in] valueName The name of the value to be gotten + * @param [in, out] value The pointer to variable which will be filled + * by result value + * @return @c 0 on success, otherwise a negative error value + */ + virtual int getResultValue(const char *valueName, void *value) const; + +public: + MVRectangles __movementRegions; /**< Regions where movements were detected */ + + cv::Mat __grayImage; /** Current gray image (only for internal usage) */ +}; + +/** + * @class EventTriggerMovementDetection + * @brief This class contains movement detection events. + * + * @since_tizen 3.0 + */ +class EventTriggerMovementDetection : public EventTrigger { +public: + /** + * @brief Default constructor. + * + * @since_tizen 3.0 + * @param [in] eventTrigger The event trigger to be register (NULL if internal) + * @param [in] triggerId Unique event trigger identifier to be register + * @param [in] videoStreamId Video stream identifier + * @param [in] callback The callback to be called if event will be occured + * @param [in] user_data The user data to be passed to the callback function + * @param [in] numberOfPoints The number of ROI points + * @param [in] roi The intput array with ROI points + */ + EventTriggerMovementDetection( + mv_surveillance_event_trigger_h eventTrigger, + long int triggerId, + int videoStreamId, + mv_surveillance_event_occurred_cb callback, + void *userData, + int numberOfPoints, + mv_point_s *roi); + + /** + * @brief Default destructor. + * + * @since_tizen 3.0 + */ + virtual ~EventTriggerMovementDetection(); + + /** + * @brief Parses engine configuration. + * + * @since_tizen 3.0 + * @param [in] engineConfig The engine configuration to be parsed + * @return @c 0 on success, otherwise a negative error value + */ + virtual int parseEngineConfig(mv_engine_config_h engineConfig); + + /** + * @brief Pushes media source. + * + * @since_tizen 3.0 + * @param [in] source The media source to be parsed + * @param [in] graySource The media source converted to gray scale + * @param [in] grayImage The converted to gray scale source + * @return @c 0 on success, otherwise a negative error value + */ + virtual int pushSource( + mv_source_h source, + mv_source_h graySource, + const cv::Mat& grayImage); + + /** + * @brief Gets event type. + * + * @since_tizen 3.0 + * @return string with event type + */ + virtual std::string getEventType() const; + + /** + * @brief Comparison operator for equal case. + * + * @since_tizen 3.0 + * @return true if event trigger is equal to other, false otherwise + */ + virtual bool operator==(const EventTriggerMovementDetection& other) const; + + /** + * @brief Comparison operator for not equal case. + * + * @since_tizen 3.0 + * @return true if event trigger is not equal to other, false otherwise + */ + virtual bool operator!=(const EventTriggerMovementDetection& other) const; + +private: + static const cv::Mat __ERODE_KERNEL; + + static const cv::Mat __DILATE_KERNEL; + +private: + cv::Mat __previousImage; + + EventResultMovementDetection *__eventResult; + + int __diffThreshold; +}; + +} /* surveillance */ +} /* mediavision */ + +#endif /* __MEDIA_VISION_EVENT_TRIGGER_MOVEMENT_DETECTION_H__ */ diff --git a/mv_surveillance/surveillance/include/EventTriggerPersonAppearance.h b/mv_surveillance/surveillance/include/EventTriggerPersonAppearance.h new file mode 100644 index 0000000..7aeecff --- /dev/null +++ b/mv_surveillance/surveillance/include/EventTriggerPersonAppearance.h @@ -0,0 +1,211 @@ +/** + * Copyright (c) 2015 Samsung Electronics Co., Ltd All Rights Reserved + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#ifndef __MEDIA_VISION_EVENT_TRIGGER_PERSON_APPEARANCE_H__ +#define __MEDIA_VISION_EVENT_TRIGGER_PERSON_APPEARANCE_H__ + +/** + * @file EventTriggerPersonAppearance.h + * @brief This file contains interface for person appeared / disapeared events. + */ + +#include "EventTrigger.h" + +#include "EventResult.h" +#include "EventDefs.h" +#include "HoGDetector.h" + +#include + +namespace mediavision { +namespace surveillance { + +/** + * @class EventResultPersonAppearance + * @brief This class contains person appeared / disapeared event results. + * + * @since_tizen 3.0 + */ +class EventResultPersonAppearance : public EventResult { +public: + /** + * @brief Gets result value. + * + * @since_tizen 3.0 + * @param [in] valueName The name of the value to be gotten + * @param [in, out] value The pointer to variable which will be filled + * by result value + * @return @c 0 on success, otherwise a negative error value + */ + virtual int getResultValue(const char *valueName, void *value) const; + +public: + MVRectangles __appearedLocations; /**< Locations of persons which were appeared + first time*/ + + MVRectangles __trackedLocations; /**< Locations of persons which were tracked + from previous frame*/ + + MVRectangles __disappearedLocations; /**< Locations of persons which were + disappeared */ +}; + +/** + * @class EventTriggerPersonAppearance + * @brief This class contains person appeared / disapeared events. + * + * @since_tizen 3.0 + */ +class EventTriggerPersonAppearance : public EventTrigger { +public: + /** + * @brief Default constructor. + * + * @since_tizen 3.0 + * @param [in] eventTrigger The event trigger to be register (NULL if internal) + * @param [in] triggerId Unique event trigger identifier to be register + * @param [in] videoStreamId Video stream identifier + * @param [in] callback The callback to be called if event will be occured + * @param [in] user_data The user data to be passed to the callback function + * @param [in] numberOfPoints The number of ROI points + * @param [in] roi The intput array with ROI points + */ + EventTriggerPersonAppearance( + mv_surveillance_event_trigger_h eventTrigger, + long int triggerId, + int videoStreamId, + mv_surveillance_event_occurred_cb callback, + void *userData, + int numberOfPoints, + mv_point_s *roi); + + /** + * @brief Default destructor. + * + * @since_tizen 3.0 + */ + virtual ~EventTriggerPersonAppearance(); + + /** + * @brief Parses engine configuration. + * + * @since_tizen 3.0 + * @param [in] engineConfig The engine configuration to be parsed + * @return @c 0 on success, otherwise a negative error value + */ + virtual int parseEngineConfig(mv_engine_config_h engineConfig); + + /** + * @brief Pushes media source. + * + * @since_tizen 3.0 + * @param [in] source The media source to be parsed + * @param [in] graySource The media source converted to gray scale + * @param [in] grayImage The converted to gray scale source + * @return @c 0 on success, otherwise a negative error value + */ + virtual int pushSource( + mv_source_h source, + mv_source_h graySource, + const cv::Mat& grayImage); + + /** + * @brief Gets event type. + * + * @since_tizen 3.0 + * @return string with event type + */ + virtual std::string getEventType() const; + + /** + * @brief Comparison operator for equal case. + * + * @since_tizen 3.0 + * @return true if event trigger is equal to other, false otherwise + */ + virtual bool operator==(const EventTriggerPersonAppearance& other) const; + + /** + * @brief Comparison operator for not equal case. + * + * @since_tizen 3.0 + * @return true if event trigger is not equal to other, false otherwise + */ + virtual bool operator!=(const EventTriggerPersonAppearance& other) const; + +private: + static void movementDetectedCB( + mv_surveillance_event_trigger_h event_trigger, + mv_source_h source, + int video_stream_id, + mv_surveillance_result_h event_result, + void *user_data); + +private: + + void runCallbacks(mv_source_h source); + +private: + + struct TrackedRectangle { + cv::Rect rect; + + int framesCount; + + TrackedRectangle(cv::Rect _rect, int _framesCount) + { + rect = _rect; + framesCount = _framesCount; + } + }; + + typedef std::list TrackedRectangles; + typedef TrackedRectangles::const_iterator TrackedRectanglesConstIter; + typedef TrackedRectangles::iterator TrackedRectanglesIter; + +private: + int __skipFramesCount; + + int __frameCounter; /**< Counts frames on which detection has not be launched */ + + long int __movementDetectedEventId; + + float __factorX; + + float __factorY; + + cv::Rect __rectToDetect; + + cv::Rect __rectToDetectPrevious; + + TrackedRectangles __trackedRects; + + CVRectangles __appearedRects; + + CVRectangles __disappearedRects; + + modifiedcv::HOGDescriptor __hogClassifier; /**< Classifier to be used for full body + person detection */ + + MVRectangles __detectedLocations; + + EventResultPersonAppearance *__eventResult; +}; + +} /* surveillance */ +} /* mediavision */ + +#endif /* __MEDIA_VISION_EVENT_TRIGGER_PERSON_APPEARANCE_H__ */ diff --git a/mv_surveillance/surveillance/include/EventTriggerPersonRecognition.h b/mv_surveillance/surveillance/include/EventTriggerPersonRecognition.h new file mode 100644 index 0000000..cd3448e --- /dev/null +++ b/mv_surveillance/surveillance/include/EventTriggerPersonRecognition.h @@ -0,0 +1,192 @@ +/** + * Copyright (c) 2015 Samsung Electronics Co., Ltd All Rights Reserved + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#ifndef __MEDIA_VISION_EVENT_TRIGGER_PERSON_RECOGNITION_H__ +#define __MEDIA_VISION_EVENT_TRIGGER_PERSON_RECOGNITION_H__ + +/** + * @file EventTriggerPersonRecognition.h + * @brief This file contains interface for person recognized events. + */ + +#include + +#include "EventTrigger.h" + +#include "EventResult.h" +#include "EventDefs.h" + +#include "EventTriggerPersonAppearance.h" + +namespace mediavision { +namespace surveillance { + +/** + * @class EventResultPersonRecogniton + * @brief This class contains person recognized event results. + * + * @since_tizen 3.0 + */ +class EventResultPersonRecognition : public EventResult { +public: + /** + * @brief Gets result value. + * + * @since_tizen 3.0 + * @param [in] valueName The name of the value to be gotten + * @param [in, out] value The pointer to variable which will be filled + * by result value + * @return @c 0 on success, otherwise a negative error value + */ + virtual int getResultValue(const char *valueName, void *value) const; + +public: + MVRectangles __locations; /**< Persons locations */ + + IntVector __faceLabels; /**< Persons face lables */ + + DoubleVector __confidences; /**< Persons face recognition confidences */ +}; + +/** + * @class EventTriggerPersonRecognition + * @brief This class contains person recognized events. + * + * @since_tizen 3.0 + */ +class EventTriggerPersonRecognition : public EventTrigger { +public: + /** + * @brief Default constructor. + * + * @since_tizen 3.0 + * @param [in] eventTrigger The event trigger to be register (NULL if internal) + * @param [in] triggerId Unique event trigger identifier to be register + * @param [in] videoStreamId Video stream identifier + * @param [in] callback The callback to be called if event will be occured + * @param [in] user_data The user data to be passed to the callback function + * @param [in] numberOfPoints The number of ROI points + * @param [in] roi The intput array with ROI points + */ + EventTriggerPersonRecognition( + mv_surveillance_event_trigger_h eventTrigger, + long int triggerId, + int videoStreamId, + mv_surveillance_event_occurred_cb callback, + void *userData, + int numberOfPoints, + mv_point_s *roi); + + /** + * @brief Default destructor. + * + * @since_tizen 3.0 + */ + virtual ~EventTriggerPersonRecognition(); + + /** + * @brief Parses engine configuration. + * + * @since_tizen 3.0 + * @param [in] engineConfig The engine configuration to be parsed + * @return @c 0 on success, otherwise a negative error value + */ + virtual int parseEngineConfig(mv_engine_config_h engineConfig); + + /** + * @brief Pushes media source. + * + * @since_tizen 3.0 + * @param [in] source The media source to be parsed + * @param [in] graySource The media source converted to gray scale + * @param [in] grayImage The converted to gray scale source + * @return @c 0 on success, otherwise a negative error value + */ + virtual int pushSource( + mv_source_h source, + mv_source_h graySource, + const cv::Mat& grayImage); + + /** + * @brief Gets event type. + * + * @since_tizen 3.0 + * @return string with event type + */ + virtual std::string getEventType() const; + + /** + * @brief Comparison operator for equal case. + * + * @since_tizen 3.0 + * @return true if event trigger is equal to other, false otherwise + */ + virtual bool operator==(const EventTriggerPersonRecognition& other) const; + + /** + * @brief Comparison operator for not equal case. + * + * @since_tizen 3.0 + * @return true if event trigger is not equal to other, false otherwise + */ + virtual bool operator!=(const EventTriggerPersonRecognition& other) const; + + /** + * @brief Sets event results. + * + * @since_tizen 3.0 + * @param [in] faceLocation The location of the face recognized on @a source. + * @param [in] faceLabel The label that identifies face which was + * recognized in the @a source. + * @param [in] confidence The confidence of the @a recognition_model + * that face has been recognized correctly + * (value from 0.0 to 1.0). + * @return @c 0 on success, otherwise a negative error value + */ + void setEventResults( + mv_rectangle_s faceLocation, + int faceLabel, + double confidence); + +private: + mv_face_recognition_model_h __faceRecognitionModel; + + mv_source_h __lastFrame; + + EventResultPersonRecognition *__eventResult; + +private: + static void faceDetectedCB( + mv_source_h source, + mv_engine_config_h engine_cfg, + mv_rectangle_s *faces_locations, + int number_of_faces, + void *user_data); + + static void faceRecognizedCB( + mv_source_h source, + mv_face_recognition_model_h recognition_model, + mv_engine_config_h engine_cfg, + mv_rectangle_s *face_location, + const int *face_label, + double confidence, + void *user_data); +}; + +} /* surveillance */ +} /* mediaVision */ + +#endif /* __MEDIA_VISION_EVENT_TRIGGER_PERSON_RECOGNITION_H__ */ diff --git a/mv_surveillance/surveillance/include/HoGDetector.h b/mv_surveillance/surveillance/include/HoGDetector.h new file mode 100644 index 0000000..d4bb400 --- /dev/null +++ b/mv_surveillance/surveillance/include/HoGDetector.h @@ -0,0 +1,194 @@ +/*M/////////////////////////////////////////////////////////////////////////////////////// +// +// IMPORTANT: READ BEFORE DOWNLOADING, COPYING, INSTALLING OR USING. +// +// By downloading, copying, installing or using the software you agree to this license. +// If you do not agree to this license, do not download, install, +// copy or use the software. +// +// +// License Agreement +// For Open Source Computer Vision Library +// +// Copyright (C) 2000-2008, Intel Corporation, all rights reserved. +// Copyright (C) 2009, Willow Garage Inc., all rights reserved. +// Third party copyrights are property of their respective owners. +// +// Redistribution and use in source and binary forms, with or without modification, +// are permitted provided that the following conditions are met: +// +// * Redistribution's of source code must retain the above copyright notice, +// this list of conditions and the following disclaimer. +// +// * Redistribution's in binary form must reproduce the above copyright notice, +// this list of conditions and the following disclaimer in the documentation +// and/or other materials provided with the distribution. +// +// * The name of the copyright holders may not be used to endorse or promote products +// derived from this software without specific prior written permission. +// +// This software is provided by the copyright holders and contributors "as is" and +// any express or implied warranties, including, but not limited to, the implied +// warranties of merchantability and fitness for a particular purpose are disclaimed. +// In no event shall the Intel Corporation or contributors be liable for any direct, +// indirect, incidental, special, exemplary, or consequential damages +// (including, but not limited to, procurement of substitute goods or services; +// loss of use, data, or profits; or business interruption) however caused +// and on any theory of liability, whether in contract, strict liability, +// or tort (including negligence or otherwise) arising in any way out of +// the use of this software, even if advised of the possibility of such damage. +// +//M*/ + +#ifndef __MEDIA_VISION_HOGDETECTOR_H__ +#define __MEDIA_VISION_HOGDETECTOR_H__ + +/** + * @file HOGDetector.h + * @brief This file contains structure of HOG detector. + */ + +#include "opencv2/core/core.hpp" +#include "opencv2/objdetect/objdetect.hpp" + +#include + +namespace modifiedcv { + +using namespace cv; + +struct HOGDescriptor { + enum { L2Hys = 0 }; + enum { DEFAULT_NLEVELS = 64 }; + + // default constructor + HOGDescriptor() : + winSize(64, 128), + blockSize(16, 16), + blockStride(8, 8), + cellSize(8, 8), + nbins(9), + derivAperture(1), + winSigma(-1), + histogramNormType(HOGDescriptor::L2Hys), + L2HysThreshold(0.2), + gammaCorrection(true), + nlevels(HOGDescriptor::DEFAULT_NLEVELS) + {} + + // constructor + HOGDescriptor( + Size _winSize, + Size _blockSize, + Size _blockStride, + Size _cellSize, + int _nbins, + int _derivAperture = 1, + double _winSigma = -1., + int _histogramNormType = L2Hys, + double _L2HysThreshold = 0.2, + bool _gammaCorrection = false, + int _nlevels = DEFAULT_NLEVELS) : + winSize(_winSize), + blockSize(_blockSize), + blockStride(_blockStride), + cellSize(_cellSize), + nbins(_nbins), + derivAperture(_derivAperture), + winSigma(_winSigma), + histogramNormType(_histogramNormType), + L2HysThreshold(_L2HysThreshold), + gammaCorrection(_gammaCorrection), + nlevels(_nlevels) + {} + + // default destructor + virtual ~HOGDescriptor() {} + + size_t getDescriptorSize() const; + + bool checkDetectorSize() const; + + double getWinSigma() const; + + virtual void setSVMDetector(InputArray _svmdetector); + + virtual void compute( + const Mat& img, + CV_OUT vector& descriptors, + Size winStride = Size(), + Size padding = Size(), + const vector& locations = vector()) const; + + //with found weights output + virtual void detect( + const Mat& img, + CV_OUT vector& foundLocations, + CV_OUT vector& weights, + double hitThreshold = 0., + Size winStride = Size(), + Size padding = Size(), + const vector& searchLocations = vector()) const; + + //without found weights output + virtual void detect( + const Mat& img, + CV_OUT vector& foundLocations, + double hitThreshold = 0., + Size winStride = Size(), + Size padding = Size(), + const vector& searchLocations = vector()) const; + + //with result weights output + virtual void detectMultiScale( + const Mat& img, + CV_OUT vector& foundLocations, + CV_OUT vector& foundWeights, + double hitThreshold = 0, + Size winStride = Size(), + Size padding = Size(), + double scale = 1.05, + double finalThreshold = 2.0, + bool useMeanshiftGrouping = false) const; + + //without found weights output + virtual void detectMultiScale( + const Mat& img, + CV_OUT vector& foundLocations, + double hitThreshold = 0., + Size winStride = Size(), + Size padding = Size(), + double scale = 1.05, + double finalThreshold = 2.0, + bool useMeanshiftGrouping = false) const; + + virtual void computeGradient( + const Mat& img, + CV_OUT Mat& grad, + CV_OUT Mat& angleOfs, + Size paddingTL = Size(), + Size paddingBR = Size()) const; + + void groupRectangles( + vector& rectList, + vector& weights, + int groupThreshold, + double eps) const; + + Size winSize; + Size blockSize; + Size blockStride; + Size cellSize; + int nbins; + int derivAperture; + double winSigma; + int histogramNormType; + double L2HysThreshold; + bool gammaCorrection; + vector svmDetector; + int nlevels; +}; + +} /* modifiedcv */ + +#endif /* __MEDIA_VISION_HOGDETECTOR_H__ */ diff --git a/mv_surveillance/surveillance/include/SurveillanceHelper.h b/mv_surveillance/surveillance/include/SurveillanceHelper.h new file mode 100644 index 0000000..ffb6302 --- /dev/null +++ b/mv_surveillance/surveillance/include/SurveillanceHelper.h @@ -0,0 +1,70 @@ +/** + * Copyright (c) 2015 Samsung Electronics Co., Ltd All Rights Reserved + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#ifndef __MEDIA_VISION_SURVEILLANCE_HELPER_H__ +#define __MEDIA_VISION_SURVEILLANCE_HELPER_H__ + +/** + * @file SurveillanceHelper.h + * @brief This file contains interface for surveillance helper. + */ + +#include + +#include + +namespace mediavision { +namespace surveillance { + +/** + * @class SurveillanceHelper + * @brief This class contains surveillance helper interface (common class for + * surveillance module). + * + * @since_tizen 3.0 + */ +class SurveillanceHelper { +public: + /** + * @brief Converts mediavision source to cv::Mat in gray scale. + * + * @since_tizen 3.0 + * @param [in] mvSource The input media source handle + * @param [out] cvSource The outut matrix with gray scaled image + * @return @c 0 on success, otherwise a negative error value + */ + static int convertSourceMV2GrayCV(mv_source_h mvSource, cv::Mat& cvSource); + +#ifdef ENABLE_NEON + /** + * @brief Converts mediavision source to cv::Mat in gray scale with NEON. + * @details Works only with RGB color space + * + * @since_tizen 3.0 + * @param [in] mvSource The input media source handle + * @param [out] cvSource The outut matrix with gray scaled image + * @return @c 0 on success, otherwise a negative error value + */ + static int convertSourceMVRGB2GrayCVNeon(mv_source_h mvSource, cv::Mat& cvSource); +#endif + +}; + + +} /* surveillance */ +} /* mediavision */ + +#endif /* __MEDIA_VISION_SURVEILLANCE_HELPER_H__ */ diff --git a/mv_surveillance/surveillance/include/mv_absdiff.h b/mv_surveillance/surveillance/include/mv_absdiff.h new file mode 100644 index 0000000..1ad0a8a --- /dev/null +++ b/mv_surveillance/surveillance/include/mv_absdiff.h @@ -0,0 +1,56 @@ +/** + * Copyright (c) 2015 Samsung Electronics Co., Ltd All Rights Reserved + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#ifndef __MEDIA_VISION_MV_ABSDIFF_H__ +#define __MEDIA_VISION_MV_ABSDIFF_H__ + +#include + +#ifdef __cplusplus +extern "C" { +#endif /* __cplusplus */ + +/** + * @brief Absolute difference between two buffers. + * @details Works only with grayscale buffers. + * + * @since_tizen 3.0 + * @remarks If NEON is enabled (ENABLE_NEON flag), then @a width has to be + * multiple of 16 + * @param [in] src1 The first input buffer. + * @param [in] src2 The second input buffer. + * @param [in] width The ROI width. Must be the multiple of 16. + * @param [in] height The ROI height. + * @param [in] stride The stride. + * @param [out] dst The output buffer. + * @return @c 0 on success, otherwise a negative error value + * @retval #MEDIA_VISION_ERROR_NONE Successful + * @retval #MEDIA_VISION_ERROR_INVALID_PARAMETER Invalid parameter + */ +int mv_absdiff( + uint8_t *__restrict__ src1, + uint8_t *__restrict__ src2, + int width, + int height, + int stride, + uint8_t *__restrict__ dst); + +#ifdef __cplusplus +} +#endif /* __cplusplus */ + +#endif /* __MEDIA_VISION_MV_ABSDIFF_H__ */ + diff --git a/mv_surveillance/surveillance/include/mv_apply_mask.h b/mv_surveillance/surveillance/include/mv_apply_mask.h new file mode 100644 index 0000000..a639c03 --- /dev/null +++ b/mv_surveillance/surveillance/include/mv_apply_mask.h @@ -0,0 +1,56 @@ +/** + * Copyright (c) 2015 Samsung Electronics Co., Ltd All Rights Reserved + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#ifndef __MEDIA_VISION_MV_APPLY_MASK_H__ +#define __MEDIA_VISION_MV_APPLY_MASK_H__ + +#include + +#ifdef __cplusplus +extern "C" { +#endif /* __cplusplus */ + +/** + * @brief Applies a binary mask to the input buffer. + * @details Works only with grayscale buffers. + * + * @since_tizen 3.0 + * @remarks If NEON is enabled (ENABLE_NEON flag), then @a width has to be + * multiple of 16 + * @param [in] src_buffer The source buffer. + * @param [in] mask The mask, which should contain only values of 0 or 255. + * @param [in] width The image width. + * @param [in] height The image height. + * @param [in] stride The stride. + * @param [out] dst_buffer The destination buffer. + * @return @c 0 on success, otherwise a negative error value + * @retval #MEDIA_VISION_ERROR_NONE Successful + * @retval #MEDIA_VISION_ERROR_INVALID_PARAMETER Invalid parameter + */ +int mv_apply_mask( + uint8_t *src_buffer, + uint8_t *__restrict mask, + int width, + int height, + int stride, + uint8_t *dst_buffer); + +#ifdef __cplusplus +} +#endif /* __cplusplus */ + +#endif /* __MEDIA_VISION_MV_APPLY_MASK_H__ */ + diff --git a/mv_surveillance/surveillance/include/mv_mask_buffer.h b/mv_surveillance/surveillance/include/mv_mask_buffer.h new file mode 100644 index 0000000..abc690f --- /dev/null +++ b/mv_surveillance/surveillance/include/mv_mask_buffer.h @@ -0,0 +1,55 @@ +/** + * Copyright (c) 2015 Samsung Electronics Co., Ltd All Rights Reserved + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#ifndef __MEDIA_VISION_MV_MASK_BUFFER_H__ +#define __MEDIA_VISION_MV_MASK_BUFFER_H__ + +#include "mv_common.h" + +#ifdef __cplusplus +extern "C" { +#endif /* __cplusplus */ + +/** + * @brief Gets mask buffer from buffer with known size. + * @details Mask buffer values: 0 ouside polygon and 255 inside polygon. + * + * @since_tizen 3.0 + * @param [in] buffer_width The buffer width + * @param [in] buffer_height The buffer height + * @param [in] polygon The array with polygon + * @param [in] points_number The size of array with polygon + * @param [out] mask_buffer The output mask buffer. mask_buffer size is + * the same as the buffer size in media source + * @return @c 0 on success, otherwise a negative error value + * @retval #MEDIA_VISION_ERROR_NONE Successful + * @retval #MEDIA_VISION_ERROR_INVALID_PARAMETER Invalid parameter + * + * @post Free memory for mask_buffer. + */ +int mv_get_mask_buffer( + unsigned int buffer_width, + unsigned int buffer_height, + mv_point_s *polygon, + unsigned int points_number, + unsigned char **mask_buffer); + +#ifdef __cplusplus +} +#endif /* __cplusplus */ + +#endif /* __MEDIA_VISION_MV_MASK_BUFFER_H__ */ + diff --git a/mv_surveillance/surveillance/include/mv_surveillance_open.h b/mv_surveillance/surveillance/include/mv_surveillance_open.h new file mode 100644 index 0000000..e70c90b --- /dev/null +++ b/mv_surveillance/surveillance/include/mv_surveillance_open.h @@ -0,0 +1,194 @@ +/** + * Copyright (c) 2015 Samsung Electronics Co., Ltd All Rights Reserved + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#ifndef __MEDIA_VISION_SURVEILLANCE_OPEN_H__ +#define __MEDIA_VISION_SURVEILLANCE_OPEN_H__ + +#include "mv_surveillance.h" +#include "mv_surveillance_private.h" + +#ifdef __cplusplus +extern "C" { +#endif /* __cplusplus */ + +/** + * @file mv_surveillance_open.h + * @brief This file contains the Media Vision surveillance API + */ + +/** + * @brief Allows to subscribe to the event and start calling @a callback + * each time when the @a source is pushed using + * @ref mv_surveillance_push_source_open() and event is detected. + * + * @since_tizen 3.0 + * @remarks To stop handling triggering use + * @ref mv_surveillance_unsubscribe_event_trigger_open(). + * @param [in] event_trigger The event trigger activating calls of the + * @a callback function + * @param [in] video_stream_id The identifier of the video stream for which + * event trigger activation will be checked + * @param [in] engine_cfg The engine configuration of the event + * @param [in] callback Callback to be called each time when event + * occurrence is detected + * @param [in] user_data The user data to be passed to the callback function + * @return @c 0 on success, otherwise a negative error value + * @retval #MEDIA_VISION_ERROR_NONE Successful + * @retval #MEDIA_VISION_ERROR_INVALID_PARAMETER Invalid parameter + * @retval #MEDIA_VISION_ERROR_NOT_SUPPORTED Not supported + * + * @post @ref mv_surveillance_event_occurred_cb + * + * @see mv_surveillance_event_trigger_s + * @see mv_surveillance_unsubscribe_event_trigger_open() + */ +int mv_surveillance_subscribe_event_trigger_open( + mv_surveillance_event_trigger_h event_trigger, + int video_stream_id, + mv_engine_config_h engine_cfg, + mv_surveillance_event_occurred_cb callback, + void *user_data); + +/** + * @brief Allows to unsubscribe from the event and stop calling @a callback. + * + * @since_tizen 3.0 + * @remarks To start handling trigger activation use + @ref mv_surveillance_subscribe_event_trigger_open(). + * @param [in] event_trigger The event trigger for which subscription will + * be stopped + * @param [in] video_stream_id The identifier of the video source for which + * subscription will be stopped + * @return @c 0 on success, otherwise a negative error value + * @retval #MEDIA_VISION_ERROR_NONE Successful + * @retval #MEDIA_VISION_ERROR_INVALID_PARAMETER Invalid parameter + * @retval #MEDIA_VISION_ERROR_NOT_SUPPORTED Not supported + * + * @post @ref mv_surveillance_event_occurred_cb + * + * @see mv_surveillance_event_trigger_s + * @see mv_surveillance_subscribe_event_trigger_open() + */ +int mv_surveillance_unsubscribe_event_trigger_open( + mv_surveillance_event_trigger_h event_trigger, + int video_stream_id); + +/** + * @brief Allows to push source to the event trigger and start calling @a callback. + * + * @since_tizen 3.0 + * @param [in] source The handle to the media source + * @param [in] video_stream_id The video stream, wthich will be updated + * @return @c 0 on success, otherwise a negative error value + * @retval #MEDIA_VISION_ERROR_NONE Successful + * @retval #MEDIA_VISION_ERROR_INVALID_PARAMETER Invalid parameter + * @retval #MEDIA_VISION_ERROR_NOT_SUPPORTED Not supported + * + * @see mv_surveillance_event_trigger_s + * @see mv_surveillance_event_occurred_cb + * @see mv_surveillance_subscribe_event_trigger_open() + * @see mv_surveillance_unsubscribe_event_trigger_open() + */ +int mv_surveillance_push_source_open( + mv_source_h source, + int video_stream_id); + +/** + * @brief Starts traversing through list of supported event types. + * + * @since_tizen 3.0 + * @remarks Supported event types and their descriptions can be found in + * @ref CAPI_MEDIA_VISION_SURVEILLANCE_EVENT_TYPES documentation + * section + * @param [in] callback The callback function to be called for each + * supported event type + * @param [in] user_data The user data to be passed to the @a callback + * function + * @return @c 0 on success, otherwise a negative error value + * @retval #MEDIA_VISION_ERROR_NONE Successful + * @retval #MEDIA_VISION_ERROR_INVALID_PARAMETER Invalid parameter + * @retval #MEDIA_VISION_ERROR_NOT_SUPPORTED Not supported + * + * @see mv_surveillance_event_type_cb + * @see mv_surveillance_foreach_event_result_value_name_open() + */ +int mv_surveillance_foreach_event_type_open( + mv_surveillance_event_type_cb callback, + void *user_data); + +/** + * @brief Starts traversing through list of supported event result value names. + * + * @since_tizen 3.0 + * @remarks Supported event types, event result value names and their + * descriptions can be found in + * @ref CAPI_MEDIA_VISION_SURVEILLANCE_EVENT_TYPES documentation + * section + * @param [in] event_type The name of the event type for which result value + * names will be passed to the @a callback. Can be + * set @c NULL. If set @c NULL then all supported + * event result value names will be traversed + * @param [in] callback The callback function to be called for each + * supported event result value name + * @param [in] user_data The user data to be passed to the @a callback + * function + * @return @c 0 on success, otherwise a negative error value + * @retval #MEDIA_VISION_ERROR_NONE Successful + * @retval #MEDIA_VISION_ERROR_INVALID_PARAMETER Invalid parameter + * @retval #MEDIA_VISION_ERROR_NOT_SUPPORTED Not supported + * + * @see mv_surveillance_event_result_value_name_cb + * @see mv_surveillance_foreach_event_type_open() + * @see mv_surveillance_get_result_value_open() + */ +int mv_surveillance_foreach_event_result_value_name_open( + const char *event_type, + mv_surveillance_event_result_name_cb callback, + void *user_data); + +/** + * @brief Gets result value. + * @details See the output values names in the event types descriptions located + * in /usr/share/config/capi-media-vision/surveillance-event-types.txt. + * + * @since_tizen 3.0 + * @param [in] result The event result + * @param [in] value_name The name of the value to be gotten + * @param [in, out] value The pointer to variable which will be filled + * by result value + * @return @c 0 on success, otherwise a negative error value + * @retval #MEDIA_VISION_ERROR_NONE Successful + * @retval #MEDIA_VISION_ERROR_INVALID_PARAMETER Invalid parameter + * @retval #MEDIA_VISION_ERROR_NOT_SUPPORTED Not supported + * + * @pre Memory for value has to be allocated + * + * @see mv_surveillance_event_trigger_s + * @see mv_surveillance_event_occurred_cb + * @see mv_surveillance_subscribe_event_trigger_open() + * @see mv_surveillance_unsubscribe_event_trigger_open() + * @see mv_surveillance_query_events_open() + */ +int mv_surveillance_get_result_value_open( + mv_surveillance_result_h result, + const char *value_name, + void *value); + +#ifdef __cplusplus +} +#endif /* __cplusplus */ + +#endif /* __MEDIA_VISION_SURVEILLANCE_OPEN_H__ */ diff --git a/mv_surveillance/surveillance/src/EventManager.cpp b/mv_surveillance/surveillance/src/EventManager.cpp new file mode 100644 index 0000000..0a3a05b --- /dev/null +++ b/mv_surveillance/surveillance/src/EventManager.cpp @@ -0,0 +1,410 @@ +/** + * Copyright (c) 2015 Samsung Electronics Co., Ltd All Rights Reserved + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#include "EventManager.h" + +#include "SurveillanceHelper.h" + +#include "EventTriggerPersonAppearance.h" +#include "EventTriggerPersonRecognition.h" +#include "EventTriggerMovementDetection.h" + +#include + +namespace mediavision { +namespace surveillance { + +static const int MAX_VALUE_NAME_LENGTH = 255; + +EventManager *EventManager::__pInstance = 0; +EventManagerDestroyer EventManager::Destroyer; +EventTypesMap EventManager::SupportedEventTypes; + +EventManagerDestroyer::~EventManagerDestroyer() +{ + delete __pInstance; +} + +void EventManagerDestroyer::initialize(EventManager* pointer) +{ + __pInstance = pointer; +} + +EventManager& EventManager::getInstance() +{ + if(!__pInstance) { + __pInstance = new EventManager(); + Destroyer.initialize(__pInstance); + setSupportedEventTypes(); + } + + return *__pInstance; +} + +void EventManager::setSupportedEventTypes() +{ + /* Add supported event types here */ + SupportedEventTypes[MV_SURVEILLANCE_EVENT_TYPE_MOVEMENT_DETECTED] + .push_back(MV_SURVEILLANCE_MOVEMENT_NUMBER_OF_REGIONS); + SupportedEventTypes[MV_SURVEILLANCE_EVENT_TYPE_MOVEMENT_DETECTED] + .push_back(MV_SURVEILLANCE_MOVEMENT_REGIONS); + + SupportedEventTypes[MV_SURVEILLANCE_EVENT_TYPE_PERSON_APPEARED_DISAPPEARED] + .push_back(MV_SURVEILLANCE_PERSONS_APPEARED_NUMBER); + SupportedEventTypes[MV_SURVEILLANCE_EVENT_TYPE_PERSON_APPEARED_DISAPPEARED] + .push_back(MV_SURVEILLANCE_PERSONS_DISAPPEARED_NUMBER); + SupportedEventTypes[MV_SURVEILLANCE_EVENT_TYPE_PERSON_APPEARED_DISAPPEARED] + .push_back(MV_SURVEILLANCE_PERSONS_TRACKED_NUMBER); + SupportedEventTypes[MV_SURVEILLANCE_EVENT_TYPE_PERSON_APPEARED_DISAPPEARED] + .push_back(MV_SURVEILLANCE_PERSONS_APPEARED_LOCATIONS); + SupportedEventTypes[MV_SURVEILLANCE_EVENT_TYPE_PERSON_APPEARED_DISAPPEARED] + .push_back(MV_SURVEILLANCE_PERSONS_DISAPPEARED_LOCATIONS); + SupportedEventTypes[MV_SURVEILLANCE_EVENT_TYPE_PERSON_APPEARED_DISAPPEARED] + .push_back(MV_SURVEILLANCE_PERSONS_TRACKED_LOCATIONS); + + SupportedEventTypes[MV_SURVEILLANCE_EVENT_TYPE_PERSON_RECOGNIZED] + .push_back(MV_SURVEILLANCE_PERSONS_RECOGNIZED_NUMBER); + SupportedEventTypes[MV_SURVEILLANCE_EVENT_TYPE_PERSON_RECOGNIZED] + .push_back(MV_SURVEILLANCE_PERSONS_RECOGNIZED_LOCATIONS); + SupportedEventTypes[MV_SURVEILLANCE_EVENT_TYPE_PERSON_RECOGNIZED] + .push_back(MV_SURVEILLANCE_PERSONS_RECOGNIZED_LABELS); + SupportedEventTypes[MV_SURVEILLANCE_EVENT_TYPE_PERSON_RECOGNIZED] + .push_back(MV_SURVEILLANCE_PERSONS_RECOGNIZED_CONFIDENCES); +} + +EventManager::EventManager() +{ + ; /* NULL */ +} + +EventManager::~EventManager() +{ + ; /* NULL */ +} + +int EventManager::registerEvent( + mv_surveillance_event_trigger_h eventTrigger, + long int triggerId, + const char *eventType, + int videoStreamId, + mv_engine_config_h engineCfg, + mv_surveillance_event_occurred_cb callback, + void *user_data, + int numberOfPoints, + mv_point_s *roi) +{ + if (NULL == callback || NULL == eventType) { + LOGE("Input event trigger or callback is NULL. Event registering failed."); + return MEDIA_VISION_ERROR_INVALID_PARAMETER; + } + + EventTriggersConstIter iter = __eventTriggers[videoStreamId].begin(); + + for (; iter != __eventTriggers[videoStreamId].end(); ++iter) { + if ((*iter)->isCallbackSubscribed(triggerId)) { + LOGE("Callback with id %d is already subscribed. " + "Event registering failed.", triggerId); + return MEDIA_VISION_ERROR_INVALID_PARAMETER; + } + } + + /* Add appropriate event trigger here */ + if (strncmp(eventType, MV_SURVEILLANCE_EVENT_TYPE_PERSON_APPEARED_DISAPPEARED, + MAX_VALUE_NAME_LENGTH) == 0) { + EventTriggerPersonAppearance* trigger = + new EventTriggerPersonAppearance( + eventTrigger, + triggerId, + videoStreamId, + callback, + user_data, + numberOfPoints, + roi); + const int error = trigger->parseEngineConfig(engineCfg); + + if (error != MEDIA_VISION_ERROR_NONE) { + delete trigger; + + LOGE("Input engine configuration is wrong. Event registering failed."); + return MEDIA_VISION_ERROR_INVALID_PARAMETER; + } + + EventTriggersIter iter2 = isTriggerExists(trigger, videoStreamId); + + if (iter2 != __eventTriggers[videoStreamId].end()) { + (*iter2)->subscribeCallback( + eventTrigger, + triggerId, + callback, + user_data, + numberOfPoints, + roi); + + delete trigger; + } else { + __eventTriggers[videoStreamId].push_back(trigger); + } + } else if (strncmp(eventType, MV_SURVEILLANCE_EVENT_TYPE_PERSON_RECOGNIZED, + MAX_VALUE_NAME_LENGTH) == 0) { + EventTriggerPersonRecognition* trigger = + new EventTriggerPersonRecognition( + eventTrigger, + triggerId, + videoStreamId, + callback, + user_data, + numberOfPoints, + roi); + + const int error = trigger->parseEngineConfig(engineCfg); + if (error != MEDIA_VISION_ERROR_NONE) { + delete trigger; + + LOGE("Input engine configuration is wrong. Event registering failed."); + return MEDIA_VISION_ERROR_INVALID_PARAMETER; + } + + EventTriggersIter iter2 = isTriggerExists(trigger, videoStreamId); + + if (iter2 != __eventTriggers[videoStreamId].end()) { + (*iter2)->subscribeCallback( + eventTrigger, + triggerId, + callback, + user_data, + numberOfPoints, + roi); + + delete trigger; + } else { + __eventTriggers[videoStreamId].push_back(trigger); + } + } else if (strncmp(eventType, MV_SURVEILLANCE_EVENT_TYPE_MOVEMENT_DETECTED, + MAX_VALUE_NAME_LENGTH) == 0) { + EventTriggerMovementDetection* trigger = + new EventTriggerMovementDetection( + eventTrigger, + triggerId, + videoStreamId, + callback, + user_data, + numberOfPoints, + roi); + + const int error = trigger->parseEngineConfig(engineCfg); + + if (error != MEDIA_VISION_ERROR_NONE) { + delete trigger; + + LOGE("Input engine configuration is wrong. Event registering failed."); + return MEDIA_VISION_ERROR_INVALID_PARAMETER; + } + + EventTriggersIter iter2 = isTriggerExists(trigger, videoStreamId); + + if (iter2 != __eventTriggers[videoStreamId].end()) { + (*iter2)->subscribeCallback( + eventTrigger, + triggerId, + callback, + user_data, + numberOfPoints, + roi); + + delete trigger; + } else { + __eventTriggers[videoStreamId].push_back(trigger); + } + } else { + LOGE("Input event trigger has wrong type. Event registering failed."); + return MEDIA_VISION_ERROR_INVALID_PARAMETER; + } + + return MEDIA_VISION_ERROR_NONE; +} + +int EventManager::unregisterEvent(long int triggerId, int videoStreamId) +{ + EventTriggersIter iter = __eventTriggers[videoStreamId].begin(); + + while (iter != __eventTriggers[videoStreamId].end()) { + if ((*iter)->unsubscribeCallback(triggerId)) { + if ((*iter)->isCallbacksEmpty()) { + delete *iter; + __eventTriggers[videoStreamId].erase(iter); + } + + return MEDIA_VISION_ERROR_NONE; + } + + ++iter; + } + + if (iter == __eventTriggers[videoStreamId].end()) { + LOGE("Event trigger doesn't exist. Event unregistering failed."); + return MEDIA_VISION_ERROR_INVALID_PARAMETER; + } + + return MEDIA_VISION_ERROR_NONE; +} + +int EventManager::pushSource(mv_source_h source, int videoStreamId) +{ + if (NULL == source) { + LOGE("Input source is NULL. Push source failed."); + return MEDIA_VISION_ERROR_INVALID_PARAMETER; + } + + if (__eventTriggers[videoStreamId].empty()) { + LOGE("There are no events yet. Push source failed."); + return MEDIA_VISION_ERROR_INVALID_OPERATION; + } + + unsigned int width = 0; + unsigned int height = 0; + mv_colorspace_e colorspace = MEDIA_VISION_COLORSPACE_INVALID; + + MEDIA_VISION_ASSERT(mv_source_get_width(source, &width), + "Failed to get the width."); + MEDIA_VISION_ASSERT(mv_source_get_height(source, &height), + "Failed to get the height."); + MEDIA_VISION_ASSERT(mv_source_get_colorspace(source, &colorspace), + "Failed to get the colorspace."); + + cv::Mat grayImage; + + int error = MEDIA_VISION_ERROR_NONE; + +#ifdef ENABLE_NEON + if (colorspace == MEDIA_VISION_COLORSPACE_RGB888 && (width * height % 8) == 0) + error = SurveillanceHelper::convertSourceMVRGB2GrayCVNeon(source, grayImage); + else +#endif /* ENABLE_NEON */ + error = SurveillanceHelper::convertSourceMV2GrayCV(source, grayImage); + + if (error != MEDIA_VISION_ERROR_NONE || grayImage.empty()) { + LOGE("Media source conversion failed."); + return error; + } + + mv_source_h graySource; + error = mv_create_source(&graySource); + if (MEDIA_VISION_ERROR_NONE != error) { + LOGE("Errors were occurred during source creating %i", error); + return error; + } + + error = mv_source_fill_by_buffer( + graySource, + grayImage.data, + grayImage.cols * grayImage.rows, + grayImage.cols, + grayImage.rows, + MEDIA_VISION_COLORSPACE_Y800); + + if (MEDIA_VISION_ERROR_NONE != error) { + mv_destroy_source(graySource); + LOGE("Errors were occurred during source filling %i", error); + return error; + } + + EventTriggersConstIter iter = __eventTriggers[videoStreamId].begin(); + + for (; iter != __eventTriggers[videoStreamId].end(); ++iter) { + error = (*iter)->pushSource(source, graySource, grayImage); + + if (error != MEDIA_VISION_ERROR_NONE) + LOGE("Push source failed for event ", (*iter)->getEventType().c_str()); + } + + error = mv_destroy_source(graySource); + + if (MEDIA_VISION_ERROR_NONE != error) + LOGE("Errors were occurred during gray source destroying %i", error); + + return MEDIA_VISION_ERROR_NONE; +} + +int EventManager::getSupportedEventTypes(StringVector& eventTypes) +{ + eventTypes.clear(); + + if (!__pInstance) + setSupportedEventTypes(); + + EventTypesMapConstIter etIter = SupportedEventTypes.begin(); + while (etIter != SupportedEventTypes.end()) { + eventTypes.push_back(etIter->first); + ++etIter; + } + + return MEDIA_VISION_ERROR_NONE; +} + +int EventManager::getSupportedEventResultValueNames( + StringVector& eventResValNames) +{ + eventResValNames.clear(); + + if (!__pInstance) + setSupportedEventTypes(); + + EventTypesMapConstIter etIter = SupportedEventTypes.begin(); + while (etIter != SupportedEventTypes.end()) { + eventResValNames.insert( + eventResValNames.end(), + etIter->second.begin(), + etIter->second.end()); + ++etIter; + } + + return MEDIA_VISION_ERROR_NONE; +} + +int EventManager::getSupportedEventResultValueNames( + const std::string& eventTypeName, + StringVector& eventResValNames) +{ + eventResValNames.clear(); + + if (!__pInstance) + setSupportedEventTypes(); + + EventTypesMapConstIter etIter = SupportedEventTypes.find(eventTypeName); + if (etIter == SupportedEventTypes.end()) + return MEDIA_VISION_ERROR_KEY_NOT_AVAILABLE; + + eventResValNames = etIter->second; + + return MEDIA_VISION_ERROR_NONE; +} + +EventTriggersIter EventManager::isTriggerExists( + EventTrigger* trigger, + int videoStreamId) +{ + EventTriggersIter iter = __eventTriggers[videoStreamId].begin(); + + for (; iter != __eventTriggers[videoStreamId].end(); ++iter) + if (*(*iter) == *trigger) + return iter; + + return iter; +} + +} /* surveillance */ +} /* mediavision */ + diff --git a/mv_surveillance/surveillance/src/EventTrigger.cpp b/mv_surveillance/surveillance/src/EventTrigger.cpp new file mode 100644 index 0000000..d6b4cec --- /dev/null +++ b/mv_surveillance/surveillance/src/EventTrigger.cpp @@ -0,0 +1,197 @@ +/** + * Copyright (c) 2015 Samsung Electronics Co., Ltd All Rights Reserved + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#include "EventTrigger.h" + +#include +#include +#include + +namespace mediavision { +namespace surveillance { + +long int EventTrigger::InternalTriggersCounter = -1l; + +EventTrigger::EventTrigger( + mv_surveillance_event_trigger_h eventTrigger, + long int triggerId, + int videoStreamId, + mv_surveillance_event_occurred_cb callback, + void *userData, + int numberOfPoints, + mv_point_s *roi): + __videoStreamId(videoStreamId), + __roi(numberOfPoints) +{ + CallbackData callbackData; + callbackData.eventTrigger = eventTrigger; + callbackData.callback = callback; + callbackData.userData = userData; + + __callbackDataMap.insert(CallbackDataPair(triggerId, callbackData)); + + for (int i = 0; i < numberOfPoints; ++i) + __roi[i] = roi[i]; +} + +EventTrigger::~EventTrigger() +{ + ; /* NULL */ +} + +int EventTrigger::getVideoStreamId() const +{ + return __videoStreamId; +} + +bool EventTrigger::isCallbackSubscribed(long int triggerId) const +{ + return __callbackDataMap.find(triggerId) != __callbackDataMap.end(); +} + +bool EventTrigger::subscribeCallback( + mv_surveillance_event_trigger_h eventTrigger, + long int triggerId, + mv_surveillance_event_occurred_cb callback, + void *userData, + int numberOfPoints, + mv_point_s *roi) +{ + if (isCallbackSubscribed(triggerId)) { + LOGE("Callback with id %d is already subscribed. " + "Callback subscribing failed.", triggerId); + return false; + } + + CallbackData callbackData; + callbackData.eventTrigger = eventTrigger; + callbackData.callback = callback; + callbackData.userData = userData; + + __callbackDataMap.insert(CallbackDataPair(triggerId, callbackData)); + + /* TODO: implement support of multiple ROI */ + __roi.clear(); + __roi.resize(numberOfPoints); + + for (int i = 0; i < numberOfPoints; ++i) + __roi[i] = roi[i]; + + return true; +} + +bool EventTrigger::unsubscribeCallback(long int triggerId) +{ + CallbackDataMapIter iter = __callbackDataMap.find(triggerId); + + if (iter == __callbackDataMap.end()) { + LOGE("Callback with id %d was not subscribed. " + "Callback unsubscribing failed.", triggerId); + return false; + } + + iter->second.callback = NULL; + iter->second.userData = NULL; + __callbackDataMap.erase(iter); + + return true; +} + +bool EventTrigger::isCallbacksEmpty() const +{ + return __callbackDataMap.empty(); +} + +int EventTrigger::applyROIToImage( + unsigned char *image, + int imageWidth, + int imageHeight, + bool scalePoints, + int scaleX, + int scaleY) +{ + const size_t roiSize = __roi.size(); + + if (roiSize >= 3) { + MVPoints scaledPoints = __roi; + + if (scalePoints) + for (size_t i = 0u; i < roiSize; ++i) { + scaledPoints[i].x /= scaleX; + scaledPoints[i].y /= scaleY; + } + + unsigned char *maskBuffer = NULL; + + int error = mv_get_mask_buffer( + imageWidth, + imageHeight, + scaledPoints.data(), + (int) roiSize, + &maskBuffer); + + if (error != MEDIA_VISION_ERROR_NONE || maskBuffer == NULL) { + if (maskBuffer != NULL) + delete maskBuffer; + + LOGE("Getting mask buffer failed."); + return error; + } + + error = mv_apply_mask( + image, + maskBuffer, + imageWidth / 16 * 16, + imageHeight, + imageWidth, + image); + + delete maskBuffer; + + if (error != MEDIA_VISION_ERROR_NONE) { + LOGE("Applying mask buffer failed."); + return error; + } + } + + return MEDIA_VISION_ERROR_NONE; +} + +bool EventTrigger::operator==(const EventTrigger& other) const +{ + const std::string currentEventType = this->getEventType(); + const std::string otherEventType = other.getEventType(); + + if (__videoStreamId != other.__videoStreamId || + currentEventType.compare(otherEventType) != 0 || + __roi.size() != other.__roi.size()) + return false; + + size_t size = __roi.size(); + for (size_t i = 0; i < size; ++i) + if (__roi[i].x != other.__roi[i].x || __roi[i].y != other.__roi[i].y) + return false; + + return true; +} + +bool EventTrigger::operator!=(const EventTrigger& other) const +{ + return !(*this == other); +} + +} /* surveillance */ +} /* mediavision */ diff --git a/mv_surveillance/surveillance/src/EventTriggerMovementDetection.cpp b/mv_surveillance/surveillance/src/EventTriggerMovementDetection.cpp new file mode 100644 index 0000000..cc6ee91 --- /dev/null +++ b/mv_surveillance/surveillance/src/EventTriggerMovementDetection.cpp @@ -0,0 +1,290 @@ +/** + * Copyright (c) 2015 Samsung Electronics Co., Ltd All Rights Reserved + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#include "EventTriggerMovementDetection.h" + +#include "EventDefs.h" +#include "mv_absdiff.h" +#include "SurveillanceHelper.h" + +#include + +#include "opencv2/highgui/highgui.hpp" + +namespace mediavision { +namespace surveillance { + +static const int DEFAULT_DIFF_THRESHOLD = 10; + +static const int MAX_VALUE_NAME_LENGTH = 255; + +const cv::Mat EventTriggerMovementDetection::__ERODE_KERNEL = + cv::getStructuringElement(cv::MORPH_RECT, cv::Size(4, 4)); + +const cv::Mat EventTriggerMovementDetection::__DILATE_KERNEL = + cv::getStructuringElement(cv::MORPH_RECT, cv::Size(24, 24)); + +static const cv::Rect DEFAULT_RECT = cv::Rect(0, 0, 0, 0); + +namespace { + +inline void convertRectCV2MV(const cv::Rect& src, mv_rectangle_s& dst) +{ + dst.point.x = src.x; + dst.point.y = src.y; + dst.width = src.width; + dst.height = src.height; +} + +void mergeOverlappedRects(CVRectangles& rects) +{ + const size_t rectsSize = rects.size(); + + for (size_t i = 0; i < rectsSize; ++i) { + const int area1 = rects[i].area(); + + for (size_t j = i + 1; j < rectsSize; ++j) { + const int area2 = rects[j].area(); + const int intersectionArea = (rects[i] & rects[j]).area(); + + if (intersectionArea != 0 && + intersectionArea > std::min(area1, area2) / 2) { + rects[i] |= rects[j]; + rects[j] = DEFAULT_RECT; + } + } + } +} + +} /* anonymous namespace */ + +int EventResultMovementDetection::getResultValue( + const char *valueName, + void *value) const +{ + if (valueName == NULL) { + LOGE("Invalid pointer for value name. Getting result value failed."); + return MEDIA_VISION_ERROR_INVALID_PARAMETER; + } + + if (value == NULL) { + LOGE("Invalid pointer for value. Getting result value failed."); + return MEDIA_VISION_ERROR_INVALID_PARAMETER; + } + + if (strncmp(valueName, MV_SURVEILLANCE_MOVEMENT_NUMBER_OF_REGIONS, + MAX_VALUE_NAME_LENGTH) == 0) { + size_t *const numberOfDetectedMovements = (size_t*) value; + *numberOfDetectedMovements = __movementRegions.size(); + } else if (strncmp(valueName, MV_SURVEILLANCE_MOVEMENT_REGIONS, + MAX_VALUE_NAME_LENGTH) == 0) { + mv_rectangle_s *const movementsRegions = (mv_rectangle_s*) value; + + const size_t numberOfDetectedMovements = __movementRegions.size(); + + for (size_t i = 0u; i < numberOfDetectedMovements; ++i) { + movementsRegions[i] = __movementRegions[i]; + } + } else { + LOGE("This value name doesn't exist. Getting result value failed."); + return MEDIA_VISION_ERROR_INVALID_PARAMETER; + } + + return MEDIA_VISION_ERROR_NONE; +} + +EventTriggerMovementDetection::EventTriggerMovementDetection( + mv_surveillance_event_trigger_h eventTrigger, + long int triggerId, + int videoStreamId, + mv_surveillance_event_occurred_cb callback, + void *userData, + int numberOfPoints, + mv_point_s *roi) : EventTrigger( + eventTrigger, + triggerId, + videoStreamId, + callback, + userData, + numberOfPoints, + roi), + __previousImage(), + __eventResult(new EventResultMovementDetection()), + __diffThreshold(DEFAULT_DIFF_THRESHOLD) +{ + ; /* NULL */ +} + +EventTriggerMovementDetection::~EventTriggerMovementDetection() +{ + delete __eventResult; +} + +int EventTriggerMovementDetection::parseEngineConfig(mv_engine_config_h engineConfig) +{ + if (NULL == engineConfig) { + LOGI("Default value for movement detection threshold was set."); + return MEDIA_VISION_ERROR_NONE; + } + + const int error = mv_engine_config_get_int_attribute( + engineConfig, + MV_SURVEILLANCE_MOVEMENT_DETECTION_THRESHOLD, + &__diffThreshold); + + if (error != MEDIA_VISION_ERROR_NONE) { + LOGE("Getting movement detection threshold from engine configuration failed."); + return error; + } + + return MEDIA_VISION_ERROR_NONE; +} + +int EventTriggerMovementDetection::pushSource( + mv_source_h source, + mv_source_h graySource, + const cv::Mat& grayImage) +{ + if (source == NULL || graySource == NULL || grayImage.empty()) { + LOGE("Media source is NULL. Pushing source failed."); + return MEDIA_VISION_ERROR_INVALID_PARAMETER; + } + + __eventResult->__movementRegions.clear(); + + int error = MEDIA_VISION_ERROR_NONE; + + if (__previousImage.empty()) { + __previousImage = grayImage.clone(); + + LOGI("Previous media source is empty. Push next source."); + return error; + } + + cv::Mat image = grayImage.clone(); + + const int bufSize = image.cols * image.rows * sizeof(uint8_t); + uint8_t *diffBuffer = (uint8_t*) malloc(bufSize * sizeof(uint8_t)); + memset(diffBuffer, 0, bufSize); + + error = mv_absdiff( + image.data, + __previousImage.data, + image.cols, + image.rows, + image.cols, + diffBuffer); + + if (error != MEDIA_VISION_ERROR_NONE) { + LOGE("Absolute difference calculation failed. Pushing source failed."); + return error; + } + + error = applyROIToImage(diffBuffer, image.cols, image.rows); + + if (error != MEDIA_VISION_ERROR_NONE || image.empty()) { + LOGE("Applying ROI failed with error %d.", error); + return error; + } + + cv::Mat imgDiff = cv::Mat(cv::Size(image.cols, image.rows), + CV_8UC1, diffBuffer); + + cv::erode(imgDiff, imgDiff, __ERODE_KERNEL); + cv::dilate(imgDiff, imgDiff, __DILATE_KERNEL); + + cv::threshold(imgDiff, imgDiff, __diffThreshold, 255, CV_THRESH_BINARY); + + Contours contours; + + cv::findContours(imgDiff, contours, CV_RETR_EXTERNAL, CV_CHAIN_APPROX_SIMPLE); + + free(diffBuffer); + + const size_t contoursSize = contours.size(); + CVRectangles rects(contoursSize); + + for (size_t i = 0u; i < contoursSize; ++i) + rects[i] = cv::boundingRect(cv::Mat(contours[i])); + + mergeOverlappedRects(rects); + + const size_t roiSize = __roi.size(); + CVPoints roi(roiSize); + + cv::Rect roiRect(0, 0, imgDiff.cols, imgDiff.rows); + + if (roiSize >= 3u) { + for (size_t i = 0u; i < roiSize; ++i) + roi[i] = cv::Point(__roi[i].x, __roi[i].y); + + roiRect = cv::boundingRect(roi); + } + + const size_t rectsSize = rects.size(); + for (size_t i = 0u; i < rectsSize; ++i) + if (rects[i] != DEFAULT_RECT && + roiRect.contains(rects[i].tl()) && + roiRect.contains(rects[i].br())) { + mv_rectangle_s rectMV; + convertRectCV2MV(rects[i], rectMV); + + __eventResult->__movementRegions.push_back(rectMV); + } + + __previousImage = image; + __eventResult->__grayImage = __previousImage; + + // Don't invoke the callback if movement wasn't detected at the frame + if (__eventResult->__movementRegions.size() > 0) { + CallbackDataMapConstIter iter = __callbackDataMap.begin(); + + for (; iter != __callbackDataMap.end(); ++iter) { + mv_surveillance_event_occurred_cb callback = iter->second.callback; + callback( + iter->second.eventTrigger, + source, + __videoStreamId, + __eventResult, + iter->second.userData); + } + } + + return MEDIA_VISION_ERROR_NONE; +} + +std::string EventTriggerMovementDetection::getEventType() const +{ + return MV_SURVEILLANCE_EVENT_TYPE_MOVEMENT_DETECTED; +} + +bool EventTriggerMovementDetection::operator==(const EventTriggerMovementDetection& other) const +{ + if (EventTrigger::operator !=(other)) + return false; + + /* TODO: compare private values if necessary */ + + return true; +} + +bool EventTriggerMovementDetection::operator!=(const EventTriggerMovementDetection& other) const +{ + return !(*this == other); +} + +} /* surveillance */ +} /* mediavision */ diff --git a/mv_surveillance/surveillance/src/EventTriggerPersonAppearance.cpp b/mv_surveillance/surveillance/src/EventTriggerPersonAppearance.cpp new file mode 100644 index 0000000..ca14d45 --- /dev/null +++ b/mv_surveillance/surveillance/src/EventTriggerPersonAppearance.cpp @@ -0,0 +1,460 @@ +/** + * Copyright (c) 2015 Samsung Electronics Co., Ltd All Rights Reserved + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#include "EventTriggerPersonAppearance.h" + +#include "EventManager.h" +#include "FaceDetector.h" +#include "SurveillanceHelper.h" +#include "EventTriggerMovementDetection.h" + +#include "opencv2/opencv.hpp" +#include "opencv2/highgui/highgui.hpp" + +#include + +#include +#include + +namespace mediavision { +namespace surveillance { + +using namespace cv; + +static const int MAX_VALUE_NAME_LENGHT = 255; + +static const int DEFAULT_SKIP_FRAMES_COUNT = 6; + +static const int DEFAULT_FRAME_WIDTH = 640; + +static const int DEFAULT_FRAME_HEIGHT = 480; + +static const cv::Size DEFAULT_FRAME_SIZE(DEFAULT_FRAME_WIDTH, DEFAULT_FRAME_HEIGHT); + +static const cv::Rect ALL_IMAGE_RECT(0, 0, DEFAULT_FRAME_WIDTH, DEFAULT_FRAME_HEIGHT); + +static const cv::Size DEFAULT_DETECTION_STEPS = cv::Size(8, 8); + +static const std::vector DEFAULT_SVM_PEOPLE_DETECTOR = + cv::HOGDescriptor::getDefaultPeopleDetector(); + +namespace { + +inline void convertRectMV2CV(const mv_rectangle_s& src, cv::Rect& dst) +{ + dst.x = src.point.x; + dst.y = src.point.y; + dst.width = src.width; + dst.height = src.height; +} + +inline void convertRectCV2MV(const cv::Rect& src, mv_rectangle_s& dst) +{ + dst.point.x = src.x; + dst.point.y = src.y; + dst.width = src.width; + dst.height = src.height; +} + +} /* Anonymous namespace*/ + +int EventResultPersonAppearance::getResultValue(const char *valueName, + void *value) const +{ + if (valueName == NULL) { + LOGE("Invalid pointer for value name. Getting result value failed."); + return MEDIA_VISION_ERROR_INVALID_PARAMETER; + } + + if (value == NULL) { + LOGE("Invalid pointer for value. Getting result value failed."); + return MEDIA_VISION_ERROR_INVALID_PARAMETER; + } + + if (strncmp(valueName, MV_SURVEILLANCE_PERSONS_APPEARED_NUMBER, + MAX_VALUE_NAME_LENGHT) == 0) { + size_t * const numberOfAppearedPersons = (size_t*) value; + *numberOfAppearedPersons = __appearedLocations.size(); + } + else if (strncmp(valueName, MV_SURVEILLANCE_PERSONS_APPEARED_LOCATIONS, + MAX_VALUE_NAME_LENGHT) == 0) { + mv_rectangle_s * const appearedLocations = (mv_rectangle_s*) value; + + const size_t numberOfAppearedPersons = __appearedLocations.size(); + + for (size_t i = 0u; i < numberOfAppearedPersons; ++i) + appearedLocations[i] = __appearedLocations[i]; + } + else if (strncmp(valueName, MV_SURVEILLANCE_PERSONS_TRACKED_NUMBER, + MAX_VALUE_NAME_LENGHT) == 0) { + size_t * const numberOfTrackedPersons = (size_t*) value; + *numberOfTrackedPersons = __trackedLocations.size(); + } + else if (strncmp(valueName, MV_SURVEILLANCE_PERSONS_TRACKED_LOCATIONS, + MAX_VALUE_NAME_LENGHT) == 0) { + mv_rectangle_s * const trackedLocations = (mv_rectangle_s*) value; + + const size_t numberOfTrackedPersons = __trackedLocations.size(); + + for (size_t i = 0u; i < numberOfTrackedPersons; ++i) + trackedLocations[i] = __trackedLocations[i]; + } + else if (strncmp(valueName, MV_SURVEILLANCE_PERSONS_DISAPPEARED_NUMBER, + MAX_VALUE_NAME_LENGHT) == 0) { + size_t * const numberOfDisappearedPersons = (size_t*) value; + *numberOfDisappearedPersons = __disappearedLocations.size(); + } + else if (strncmp(valueName, MV_SURVEILLANCE_PERSONS_DISAPPEARED_LOCATIONS, + MAX_VALUE_NAME_LENGHT) == 0) { + mv_rectangle_s * const disappearedLocations = (mv_rectangle_s*) value; + + const size_t numberOfDisappearedPersons = __disappearedLocations.size(); + + for (size_t i = 0u; i < numberOfDisappearedPersons; ++i) + disappearedLocations[i] = __disappearedLocations[i]; + } + else { + LOGE("This value name doesn't exist. Getting result value failed."); + return MEDIA_VISION_ERROR_INVALID_PARAMETER; + } + + return MEDIA_VISION_ERROR_NONE; +} + +EventTriggerPersonAppearance::EventTriggerPersonAppearance( + mv_surveillance_event_trigger_h eventTrigger, long int triggerId, + int videoStreamId, mv_surveillance_event_occurred_cb callback, + void *userData, int numberOfPoints, mv_point_s *roi) : + EventTrigger(eventTrigger, triggerId, videoStreamId, callback, userData, + numberOfPoints, roi), __skipFramesCount(DEFAULT_SKIP_FRAMES_COUNT), + __frameCounter(0), __movementDetectedEventId(InternalTriggersCounter--), + __factorX(1.f), __factorY(1.f), __rectToDetect(ALL_IMAGE_RECT), + __rectToDetectPrevious(ALL_IMAGE_RECT), __trackedRects(), + __appearedRects(), __disappearedRects(), __hogClassifier(), + __eventResult(new EventResultPersonAppearance()) +{ + __hogClassifier.setSVMDetector(DEFAULT_SVM_PEOPLE_DETECTOR); + + EventManager::getInstance().registerEvent( + NULL, __movementDetectedEventId, + MV_SURVEILLANCE_EVENT_TYPE_MOVEMENT_DETECTED, videoStreamId, + NULL, movementDetectedCB, this, numberOfPoints, roi); +} + +EventTriggerPersonAppearance::~EventTriggerPersonAppearance() +{ + EventManager::getInstance().unregisterEvent(__movementDetectedEventId, + __videoStreamId); + + delete __eventResult; +} + +int EventTriggerPersonAppearance::parseEngineConfig( + mv_engine_config_h engineConfig) +{ + if (NULL == engineConfig) { + LOGI("Default value for frame skip count was set."); + return MEDIA_VISION_ERROR_NONE; + } + + const int error = mv_engine_config_get_int_attribute(engineConfig, + MV_SURVEILLANCE_SKIP_FRAMES_COUNT, &__skipFramesCount); + + if (error != MEDIA_VISION_ERROR_NONE) { + LOGE("Getting frame skip count from engine configuration failed."); + return error; + } + + return MEDIA_VISION_ERROR_NONE; +} + +int EventTriggerPersonAppearance::pushSource(mv_source_h source, + mv_source_h graySource, const cv::Mat& grayImage) +{ + if (source == NULL || graySource == NULL || grayImage.empty()) { + LOGE("Media source is NULL. Pushing source failed."); + return MEDIA_VISION_ERROR_INVALID_PARAMETER; + } + + return MEDIA_VISION_ERROR_NONE; +} + +std::string EventTriggerPersonAppearance::getEventType() const +{ + return MV_SURVEILLANCE_EVENT_TYPE_PERSON_APPEARED_DISAPPEARED; +} + +bool EventTriggerPersonAppearance::operator==( + const EventTriggerPersonAppearance& other) const +{ + if (EventTrigger::operator !=(other)) + return false; + + /* TODO: compare private values if necessary */ + + return true; +} + +bool EventTriggerPersonAppearance::operator!=( + const EventTriggerPersonAppearance& other) const +{ + return !(*this == other); +} + +void EventTriggerPersonAppearance::movementDetectedCB( + mv_surveillance_event_trigger_h /*event_trigger*/, mv_source_h source, + int /*video_stream_id*/, mv_surveillance_result_h event_result, + void *user_data) +{ + EventTriggerPersonAppearance *trigger = + (EventTriggerPersonAppearance*) user_data; + + /* 1. Get input image in grayscale and resize it */ + EventResultMovementDetection *result = + static_cast(event_result); + + cv::Mat resizedImage; + cv::resize(result->__grayImage, resizedImage, DEFAULT_FRAME_SIZE); + + int error = trigger->applyROIToImage(resizedImage.data, resizedImage.cols, + resizedImage.rows, true, DEFAULT_FRAME_WIDTH, DEFAULT_FRAME_HEIGHT); + + if (error != MEDIA_VISION_ERROR_NONE || resizedImage.empty()) { + trigger->runCallbacks(source); + LOGE("Applying ROI failed with error %d.", error); + return; + } + + trigger->__factorX = (float) DEFAULT_FRAME_WIDTH / result->__grayImage.cols; + trigger->__factorY = (float) DEFAULT_FRAME_HEIGHT / result->__grayImage.rows; + + /* 2. Get detected movement regions */ + const size_t numberOfMovementRegions = result->__movementRegions.size(); + CVRectangles movementRegions(numberOfMovementRegions); + + for (size_t i = 0u; i < numberOfMovementRegions; ++i) { + convertRectMV2CV(result->__movementRegions[i], movementRegions[i]); + movementRegions[i].x *= trigger->__factorX; + movementRegions[i].y *= trigger->__factorY; + movementRegions[i].width *= trigger->__factorX; + movementRegions[i].height *= trigger->__factorY; + } + + /* 3. Calculate rectangle where person will be detect */ + if (movementRegions.empty()) { + trigger->__rectToDetect = ALL_IMAGE_RECT; + } else { + trigger->__rectToDetect = movementRegions[0]; + + for (size_t j = 1u; j < numberOfMovementRegions; ++j) + trigger->__rectToDetect |= movementRegions[j]; + + if (trigger->__rectToDetect.width + < trigger->__hogClassifier.winSize.width + || trigger->__rectToDetect.height + < trigger->__hogClassifier.winSize.height) + trigger->__rectToDetect |= trigger->__rectToDetectPrevious; + } + + trigger->__rectToDetect &= ALL_IMAGE_RECT; + + /* 4. Perform Hog detector or try to track using movement regions */ + if ((trigger->__skipFramesCount == 0 || + trigger->__frameCounter % trigger->__skipFramesCount == 0) && + (trigger->__rectToDetect != ALL_IMAGE_RECT)) { + /* 4.1 Perform Hog detector */ + TrackedRectanglesConstIter iter = trigger->__trackedRects.begin(); + for (; iter != trigger->__trackedRects.end(); ++iter) + trigger->__rectToDetect |= iter->rect; + + // Slightly extend detection area... + const int xShift = .25f * trigger->__rectToDetect.width; + const int yShift = .25f * trigger->__rectToDetect.height; + trigger->__rectToDetect.x -= xShift / 2; + trigger->__rectToDetect.y -= yShift / 2; + trigger->__rectToDetect.width += xShift; + trigger->__rectToDetect.height += yShift; + trigger->__rectToDetect &= ALL_IMAGE_RECT; + // and fit it to the HOG cell size + const int xRest = trigger->__rectToDetect.width % 8; + const int yRest = trigger->__rectToDetect.height % 8; + trigger->__rectToDetect.x += xRest / 2; + trigger->__rectToDetect.y += yRest / 2; + trigger->__rectToDetect.width -= xRest; + trigger->__rectToDetect.height -= yRest; + + CVRectangles hogRects; + + trigger->__hogClassifier.detectMultiScale( + resizedImage(trigger->__rectToDetect), hogRects, 0, + DEFAULT_DETECTION_STEPS, cv::Size(32, 32), 1.059, 2); + + const size_t hogRectsSize = hogRects.size(); + + for (size_t i = 0u; i < hogRectsSize; ++i) { + hogRects[i].x += trigger->__rectToDetect.x; + hogRects[i].y += trigger->__rectToDetect.y; + } + + std::vector trackChecks(hogRectsSize, false); + TrackedRectanglesIter trackRectIter = trigger->__trackedRects.begin(); + for (; trackRectIter != trigger->__trackedRects.end(); + ++trackRectIter) { + size_t bestArea = 0; + size_t bestIdx = 0; + for (size_t idx = 0u; idx < hogRectsSize; ++idx) { + if (trackChecks[idx]) + continue; + const size_t curArea = + (hogRects[idx] & trackRectIter->rect).area(); + if (bestArea < curArea) { + bestArea = curArea; + bestIdx = idx; + } + } + if (bestArea > 10) { + trackChecks[bestIdx] = true; + trackRectIter->rect = hogRects[bestIdx]; + } + } + + trigger->__appearedRects.clear(); + for (size_t idx = 0u; idx < hogRectsSize; ++idx) + if (!trackChecks[idx]) + trigger->__appearedRects.push_back(hogRects[idx]); + } + else { + /* 4.2 Try to track */ + CVRectanglesConstIter appearedIter = trigger->__appearedRects.begin(); + for (; appearedIter != trigger->__appearedRects.end(); ++appearedIter) + trigger->__trackedRects.push_back( + TrackedRectangle(*appearedIter, 7)); + + trigger->__appearedRects.clear(); + + TrackedRectanglesIter iter = trigger->__trackedRects.begin(); + while (iter != trigger->__trackedRects.end()) { + bool tracked = false; + + for (size_t j = 0u; j < numberOfMovementRegions; ++j) { + cv::Rect rect = iter->rect; + if ((rect & movementRegions[j]).area() != 0 && + movementRegions[j].area() <= 3 * rect.area() / 2) { + cv::Rect r1 = rect | movementRegions[j]; + const int dx = r1.width - rect.width; + const int dy = r1.height - rect.height; + + if (r1.x < movementRegions[j].x) + r1.x += dx; + else if (r1.x > movementRegions[j].x) + r1.x -= dx; + + if (r1.y < movementRegions[j].y) + r1.y += dy; + else if (r1.y > movementRegions[j].y) + r1.y -= dy; + + r1.height = rect.height; + r1.width = rect.width; + + iter->rect = r1; + + tracked = true; + } + } + + if (tracked) + ++iter; + else { + if (iter->framesCount == 0) { + trigger->__disappearedRects.push_back(iter->rect); + iter = trigger->__trackedRects.erase(iter); + } + else { + --(iter->framesCount); + ++iter; + } + } + } + } + + trigger->__rectToDetectPrevious = trigger->__rectToDetect; + ++trigger->__frameCounter; + + /* 5. Update event result and run callbacks */ + trigger->runCallbacks(source); + + trigger->__disappearedRects.clear(); +} + +void EventTriggerPersonAppearance::runCallbacks(mv_source_h source) +{ + __eventResult->__appearedLocations.clear(); + __eventResult->__disappearedLocations.clear(); + __eventResult->__trackedLocations.clear(); + + const size_t appearedLocationsSize = __appearedRects.size(); + __eventResult->__appearedLocations.resize(appearedLocationsSize); + + for (size_t i = 0u; i < appearedLocationsSize; ++i) { + convertRectCV2MV(__appearedRects[i], + __eventResult->__appearedLocations[i]); + __eventResult->__appearedLocations[i].point.x /= __factorX; + __eventResult->__appearedLocations[i].point.y /= __factorY; + __eventResult->__appearedLocations[i].width /= __factorX; + __eventResult->__appearedLocations[i].height /= __factorY; + } + + const size_t disappearedLocationsSize = __disappearedRects.size(); + __eventResult->__disappearedLocations.resize(disappearedLocationsSize); + + for (size_t i = 0u; i < disappearedLocationsSize; ++i) { + convertRectCV2MV(__disappearedRects[i], + __eventResult->__disappearedLocations[i]); + __eventResult->__disappearedLocations[i].point.x /= __factorX; + __eventResult->__disappearedLocations[i].point.y /= __factorY; + __eventResult->__disappearedLocations[i].width /= __factorX; + __eventResult->__disappearedLocations[i].height /= __factorY; + } + + const size_t trackedLocationsSize = __trackedRects.size(); + __eventResult->__trackedLocations.resize(trackedLocationsSize); + + TrackedRectanglesConstIter trackedIter = __trackedRects.begin(); + for (size_t i = 0u; i < trackedLocationsSize; ++i, ++trackedIter) { + convertRectCV2MV(trackedIter->rect, + __eventResult->__trackedLocations[i]); + __eventResult->__trackedLocations[i].point.x /= __factorX; + __eventResult->__trackedLocations[i].point.y /= __factorY; + __eventResult->__trackedLocations[i].width /= __factorX; + __eventResult->__trackedLocations[i].height /= __factorY; + } + + // Don't invoke the callback if no appearance, disappearance or tracking + if (appearedLocationsSize > 0 || disappearedLocationsSize > 0 + || trackedLocationsSize > 0) { + CallbackDataMapConstIter iter = __callbackDataMap.begin(); + + for (; iter != __callbackDataMap.end(); ++iter) { + mv_surveillance_event_occurred_cb callback = iter->second.callback; + callback(iter->second.eventTrigger, source, __videoStreamId, + __eventResult, iter->second.userData); + } + } +} + +} /* surveillance */ +} /* mediavision */ diff --git a/mv_surveillance/surveillance/src/EventTriggerPersonRecognition.cpp b/mv_surveillance/surveillance/src/EventTriggerPersonRecognition.cpp new file mode 100644 index 0000000..7b28709 --- /dev/null +++ b/mv_surveillance/surveillance/src/EventTriggerPersonRecognition.cpp @@ -0,0 +1,397 @@ +/** + * Copyright (c) 2015 Samsung Electronics Co., Ltd All Rights Reserved + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#include "EventTriggerPersonRecognition.h" +#include "EventManager.h" + +#include +#include +#include + +#include + +namespace mediavision { +namespace surveillance { + +static const int MAX_VALUE_NAME_LENGHT = 255; + +namespace { + +template +std::string numberToString(T Number) +{ + std::ostringstream ss; + ss << Number; + return ss.str(); +} + +} /* Anonymous namespace*/ + +int EventResultPersonRecognition::getResultValue( + const char *valueName, + void *value) const +{ + if (valueName == NULL) { + LOGE("Invalid pointer for value name. Getting result value failed."); + return MEDIA_VISION_ERROR_INVALID_PARAMETER; + } + + if (value == NULL) { + LOGE("Invalid pointer for value. Getting result value failed."); + return MEDIA_VISION_ERROR_INVALID_PARAMETER; + } + + const size_t numberOfPersons = __locations.size(); + + if (strncmp(valueName, + MV_SURVEILLANCE_PERSONS_RECOGNIZED_NUMBER, + MAX_VALUE_NAME_LENGHT) == 0) { + size_t *outNumberOfPersons = (size_t*) value; + *outNumberOfPersons = numberOfPersons; + } else if (strncmp(valueName, + MV_SURVEILLANCE_PERSONS_RECOGNIZED_LOCATIONS, + MAX_VALUE_NAME_LENGHT) == 0) { + mv_rectangle_s *locations = (mv_rectangle_s*) value; + + for (size_t i = 0; i < numberOfPersons; ++i) + locations[i] = __locations[i]; + } else if (strncmp(valueName, + MV_SURVEILLANCE_PERSONS_RECOGNIZED_LABELS, + MAX_VALUE_NAME_LENGHT) == 0) { + int *labels = (int*) value; + + for (size_t i = 0; i < numberOfPersons; ++i) + labels[i] = __faceLabels[i]; + } else if (strncmp(valueName, + MV_SURVEILLANCE_PERSONS_RECOGNIZED_CONFIDENCES, + MAX_VALUE_NAME_LENGHT) == 0) { + double *confidences = (double*) value; + + for (size_t i = 0; i < numberOfPersons; ++i) + confidences[i] = __confidences[i]; + } else { + LOGE("This value name doesn't exist. Getting result value failed."); + return MEDIA_VISION_ERROR_INVALID_PARAMETER; + } + + return MEDIA_VISION_ERROR_NONE; +} + +EventTriggerPersonRecognition::EventTriggerPersonRecognition( + mv_surveillance_event_trigger_h eventTrigger, + long int triggerId, + int videoStreamId, + mv_surveillance_event_occurred_cb callback, + void *userData, + int numberOfPoints, + mv_point_s *roi) : EventTrigger(eventTrigger, + triggerId, + videoStreamId, + callback, + userData, + numberOfPoints, + roi), + __faceRecognitionModel(NULL), + __lastFrame(NULL), + __eventResult(new EventResultPersonRecognition()) +{ + ; /* NULL */ +} + +EventTriggerPersonRecognition::~EventTriggerPersonRecognition() +{ + if (NULL != __faceRecognitionModel) { + const int err = mv_face_recognition_model_destroy(__faceRecognitionModel); + if (MEDIA_VISION_ERROR_NONE != err) + LOGE("Error while trying to delete face recognition model when " + "event trigger had been destroyed. Error code: %i.", err); + } + + delete __eventResult; +} + +int EventTriggerPersonRecognition::parseEngineConfig(mv_engine_config_h engineConfig) +{ + if (engineConfig == NULL) { + LOGE("Engine configuration is NULL. Parsing failed."); + return MEDIA_VISION_ERROR_INVALID_PARAMETER; + } + + char *modelPath = NULL; + + int error = mv_engine_config_get_string_attribute( + engineConfig, + MV_SURVEILLANCE_FACE_RECOGNITION_MODEL_FILE_PATH, + &modelPath); + + if (error != MEDIA_VISION_ERROR_NONE) { + if (modelPath != NULL) + delete[] modelPath; + + LOGE("Getting recognition model from engine configuration failed."); + + return error; + } + + mv_face_recognition_model_h recognitionModel = NULL; + + error = mv_face_recognition_model_load(modelPath, &recognitionModel); + + if (error != MEDIA_VISION_ERROR_NONE) { + LOGE("Loading recognition model from file %s failed.", + modelPath); + + if (modelPath != NULL) + delete[] modelPath; + + return error; + } + + if (NULL != __faceRecognitionModel) { + error = mv_face_recognition_model_destroy(__faceRecognitionModel); + if (MEDIA_VISION_ERROR_NONE != error) { + LOGE("Error while trying to delete old face recognition model when " + "new model is trying to be loaded. Error code: %i.", error); + } + } + + __faceRecognitionModel = recognitionModel; + + if (NULL == __faceRecognitionModel) { + LOGE("Failed to load face recognition model. Check %s attribute of the " + "engine config.", MV_SURVEILLANCE_FACE_RECOGNITION_MODEL_FILE_PATH); + + if (modelPath != NULL) + delete[] modelPath; + + return MEDIA_VISION_ERROR_INVALID_PARAMETER; + } + + if (modelPath != NULL) + delete[] modelPath; + + return MEDIA_VISION_ERROR_NONE; +} + +int EventTriggerPersonRecognition::pushSource( + mv_source_h source, + mv_source_h graySource, + const cv::Mat& grayImage) +{ + if (source == NULL || graySource == NULL || grayImage.empty()) { + LOGE("Media source is NULL. Pushing source failed."); + return MEDIA_VISION_ERROR_INVALID_PARAMETER; + } + + __lastFrame = source; + + __eventResult->__locations.clear(); + __eventResult->__faceLabels.clear(); + __eventResult->__confidences.clear(); + + unsigned char *data_buffer = NULL; + unsigned int buffer_size = 0; + mv_colorspace_e colorspace = MEDIA_VISION_COLORSPACE_INVALID; + unsigned int width = 0; + unsigned int height = 0; + + int error = mv_source_get_buffer(graySource, &data_buffer, &buffer_size); + + if (error != MEDIA_VISION_ERROR_NONE) { + LOGE("Operation with media source failed with error %d.", error); + return error; + } + + error = mv_source_get_colorspace(graySource, &colorspace); + + if (error != MEDIA_VISION_ERROR_NONE) { + LOGE("Operation with media source failed with error %d.", error); + return error; + } + + error = mv_source_get_width(graySource, &width); + + if (error != MEDIA_VISION_ERROR_NONE) { + LOGE("Operation with media source failed with error %d.", error); + return error; + } + + error = mv_source_get_height(graySource, &height); + + if (error != MEDIA_VISION_ERROR_NONE) { + LOGE("Operation with media source failed with error %d.", error); + return error; + } + + if (buffer_size != width * height) + { + // Unexcepted behaviour + LOGE("Grayscale source interpretation failed."); + return MEDIA_VISION_ERROR_INTERNAL; + } + + mv_source_h sourceCopy = NULL; + + error = mv_create_source(&sourceCopy); + + if (error != MEDIA_VISION_ERROR_NONE) { + LOGE("Operation with media source failed with error %d.", error); + return error; + } + + error = mv_source_fill_by_buffer(sourceCopy, data_buffer, buffer_size, + width, height, colorspace); + + if (error != MEDIA_VISION_ERROR_NONE) { + mv_destroy_source(sourceCopy); + LOGE("Operation with media source failed with error %d.", error); + return error; + } + + error = mv_source_get_buffer(sourceCopy, &data_buffer, &buffer_size); + + if (error != MEDIA_VISION_ERROR_NONE) { + mv_destroy_source(sourceCopy); + LOGE("Operation with media source failed with error %d.", error); + return error; + } + + error = applyROIToImage(data_buffer, width, height); + + if (error != MEDIA_VISION_ERROR_NONE) { + mv_destroy_source(sourceCopy); + LOGE("Applying ROI failed with error %d.", error); + return error; + } + + error = mv_face_detect(sourceCopy, NULL, faceDetectedCB, this); + + if (MEDIA_VISION_ERROR_NONE != error) { + mv_destroy_source(sourceCopy); + LOGE("Errors were occurred during face detecting %i", error); + return error; + } + + error = mv_destroy_source(sourceCopy); + + if (error != MEDIA_VISION_ERROR_NONE) { + LOGE("Operation with media source failed with error %d.", error); + return error; + } + + return error; +} + +std::string EventTriggerPersonRecognition::getEventType() const +{ + return MV_SURVEILLANCE_EVENT_TYPE_PERSON_RECOGNIZED; +} + +bool EventTriggerPersonRecognition::operator==(const EventTriggerPersonRecognition& other) const +{ + if (EventTrigger::operator !=(other)) + return false; + + /* TODO: compare private values if necessary */ + + return true; +} + +bool EventTriggerPersonRecognition::operator!=(const EventTriggerPersonRecognition& other) const +{ + return !(*this == other); +} + +void EventTriggerPersonRecognition::setEventResults( + mv_rectangle_s faceLocation, + int faceLabel, + double confidence) +{ + __eventResult->__locations.push_back(faceLocation); + __eventResult->__faceLabels.push_back(faceLabel); + __eventResult->__confidences.push_back(confidence); +} + +void EventTriggerPersonRecognition::faceDetectedCB( + mv_source_h source, + mv_engine_config_h /*engine_cfg*/, + mv_rectangle_s *faces_locations, + int number_of_faces, + void *user_data) +{ + if (NULL == user_data) { + LOGE("Invalid user data passed"); + return; + } + + EventTriggerPersonRecognition *recognitionTrigger = + (EventTriggerPersonRecognition*)user_data; + + int location_idx = 0; + for (; location_idx < number_of_faces; ++location_idx) { + LOGI("Start surveillance face recognition"); + + const int error = mv_face_recognize( + source, + recognitionTrigger->__faceRecognitionModel, + NULL, + &faces_locations[location_idx], + faceRecognizedCB, + recognitionTrigger); + + if (error != MEDIA_VISION_ERROR_NONE) { + LOGW("Face recognition for one model failed. Continue"); + continue; + } + + LOGI("Face has been successfully recognized"); + } +} + +void EventTriggerPersonRecognition::faceRecognizedCB( + mv_source_h source, + mv_face_recognition_model_h /*recognition_model*/, + mv_engine_config_h /*engine_cfg*/, + mv_rectangle_s *face_location, + const int *face_label, + double confidence, + void *user_data) +{ + if (face_location == NULL || face_label == NULL) { + LOGI("Face wasn't recognized"); + return; + } + + EventTriggerPersonRecognition *trigger = + (EventTriggerPersonRecognition*) user_data; + + trigger->setEventResults(*face_location, *face_label, confidence); + + CallbackDataMapConstIter iter = trigger->__callbackDataMap.begin(); + + for (; iter != trigger->__callbackDataMap.end(); ++iter) { + mv_surveillance_event_occurred_cb callback = iter->second.callback; + callback( + iter->second.eventTrigger, + trigger->__lastFrame, + trigger->__videoStreamId, + trigger->__eventResult, + iter->second.userData); + } +} + +} /* surveillance */ +} /* mediavision */ diff --git a/mv_surveillance/surveillance/src/HoGDetector.cpp b/mv_surveillance/surveillance/src/HoGDetector.cpp new file mode 100644 index 0000000..4d1ea0c --- /dev/null +++ b/mv_surveillance/surveillance/src/HoGDetector.cpp @@ -0,0 +1,1006 @@ +/*M/////////////////////////////////////////////////////////////////////////////////////// +// +// IMPORTANT: READ BEFORE DOWNLOADING, COPYING, INSTALLING OR USING. +// +// By downloading, copying, installing or using the software you agree to this license. +// If you do not agree to this license, do not download, install, +// copy or use the software. +// +// +// License Agreement +// For Open Source Computer Vision Library +// +// Copyright (C) 2000-2008, Intel Corporation, all rights reserved. +// Copyright (C) 2009, Willow Garage Inc., all rights reserved. +// Third party copyrights are property of their respective owners. +// +// Redistribution and use in source and binary forms, with or without modification, +// are permitted provided that the following conditions are met: +// +// * Redistribution's of source code must retain the above copyright notice, +// this list of conditions and the following disclaimer. +// +// * Redistribution's in binary form must reproduce the above copyright notice, +// this list of conditions and the following disclaimer in the documentation +// and/or other materials provided with the distribution. +// +// * The name of the copyright holders may not be used to endorse or promote products +// derived from this software without specific prior written permission. +// +// This software is provided by the copyright holders and contributors "as is" and +// any express or implied warranties, including, but not limited to, the implied +// warranties of merchantability and fitness for a particular purpose are disclaimed. +// In no event shall the Intel Corporation or contributors be liable for any direct, +// indirect, incidental, special, exemplary, or consequential damages +// (including, but not limited to, procurement of substitute goods or services; +// loss of use, data, or profits; or business interruption) however caused +// and on any theory of liability, whether in contract, strict liability, +// or tort (including negligence or otherwise) arising in any way out of +// the use of this software, even if advised of the possibility of such damage. +// +//M*/ +#include +#include "HoGDetector.h" +#include "opencv2/imgproc/imgproc.hpp" +#include + +#ifdef ENABLE_NEON +#include +#endif + +#ifdef ENABLE_OMP +#include +#define NCORES 4 +static int coreids[NCORES] = {1, 2, 3, 4}; +#endif + +/****************************************************************************************\ + The code below is implementation of HOG (Histogram-of-Oriented Gradients) + descriptor and object detection, introduced by Navneet Dalal and Bill Triggs. + + The computed feature vectors are compatible with the + INRIA Object Detection and Localization Toolkit + (http://pascal.inrialpes.fr/soft/olt/) +\****************************************************************************************/ + +namespace modifiedcv { + +class ParallelLoopBodyWrapper { +public: + ParallelLoopBodyWrapper(const cv::ParallelLoopBody& _body, const cv::Range& _r) { + body = &_body; + wholeRange = _r; + nstripes = cvRound(wholeRange.end - wholeRange.start); + } + void operator()(const cv::Range& sr) const { + cv::Range r; + r.start = (int)(wholeRange.start + + ((uint64)sr.start*(wholeRange.end - wholeRange.start) + nstripes/2)/nstripes); + r.end = sr.end >= nstripes ? wholeRange.end : (int)(wholeRange.start + + ((uint64)sr.end*(wholeRange.end - wholeRange.start) + nstripes/2)/nstripes); + (*body)(r); + } + cv::Range stripeRange() const { + return cv::Range(0, nstripes); + } + +protected: + const cv::ParallelLoopBody* body; + cv::Range wholeRange; + int nstripes; +}; + +void parallel_for_(const cv::Range& range, const cv::ParallelLoopBody& body) +{ +#if defined ENABLE_OMP + ParallelLoopBodyWrapper pbody(body, range); + cv::Range stripeRange = pbody.stripeRange(); + int i = 0; + #pragma omp parallel for private(i) num_threads(NCORES) + for (i = stripeRange.start; i < stripeRange.end; ++i) { + cpu_set_t mask; + CPU_ZERO(&mask); + CPU_SET(coreids[i % 4], &mask); + + if (sched_setaffinity (0, sizeof(mask), &mask) == -1) { + printf("Could not set CPU Affinity, continuing..."); + } + + pbody(Range(i, i + 1)); + } +#else + cv::parallel_for_(range, body); +#endif +} + +size_t HOGDescriptor::getDescriptorSize() const +{ + return (size_t)nbins* + (blockSize.width/cellSize.width)* + (blockSize.height/cellSize.height)* + ((winSize.width - blockSize.width)/blockStride.width + 1)* + ((winSize.height - blockSize.height)/blockStride.height + 1); +} + +double HOGDescriptor::getWinSigma() const +{ + return winSigma >= 0 ? winSigma : (blockSize.width + blockSize.height)/8.; +} + +bool HOGDescriptor::checkDetectorSize() const +{ + size_t detectorSize = svmDetector.size(), descriptorSize = getDescriptorSize(); + return detectorSize == 0 || + detectorSize == descriptorSize || + detectorSize == descriptorSize + 1; +} + +void HOGDescriptor::setSVMDetector(InputArray _svmDetector) +{ + _svmDetector.getMat().convertTo(svmDetector, CV_32F); + CV_Assert(checkDetectorSize()); +} + +void HOGDescriptor::computeGradient(const Mat& img, Mat& grad, Mat& qangle, + Size paddingTL, Size paddingBR) const +{ + CV_Assert(img.type() == CV_8U); + + Size gradsize(img.cols + paddingTL.width + paddingBR.width, + img.rows + paddingTL.height + paddingBR.height); + grad.create(gradsize, CV_32FC2); /* */ + qangle.create(gradsize, CV_8UC2); /* [0..nbins-1] - quantized gradient orientation */ + Size wholeSize; + Point roiofs; + img.locateROI(wholeSize, roiofs); + + int i, x, y; + /* int cn = img.channels(); */ + + Mat_ _lut(1, 256); + const float* lut = &_lut(0, 0); + + if ( gammaCorrection ) + for ( i = 0; i < 256; i++ ) + _lut(0, i) = std::sqrt((float)i); + else + for ( i = 0; i < 256; i++ ) + _lut(0, i) = (float)i; + + AutoBuffer mapbuf(gradsize.width + gradsize.height + 4); + int* xmap = (int*)mapbuf + 1; + int* ymap = xmap + gradsize.width + 2; + + const int borderType = (int)cv::BORDER_REFLECT_101; + + for ( x = -1; x < gradsize.width + 1; x++ ) + xmap[x] = cv::borderInterpolate(x - paddingTL.width + roiofs.x, + wholeSize.width, borderType) - roiofs.x; + for ( y = -1; y < gradsize.height + 1; y++ ) + ymap[y] = cv::borderInterpolate(y - paddingTL.height + roiofs.y, + wholeSize.height, borderType) - roiofs.y; + + /* x- & y- derivatives for the whole row */ + int width = gradsize.width; + AutoBuffer _dbuf(width*4); + float* dbuf = _dbuf; + Mat Dx(1, width, CV_32F, dbuf); + Mat Dy(1, width, CV_32F, dbuf + width); + Mat Mag(1, width, CV_32F, dbuf + width*2); + Mat Angle(1, width, CV_32F, dbuf + width*3); + + int _nbins = nbins; + float angleScale = (float)(_nbins/CV_PI); + + for ( y = 0; y < gradsize.height; y++ ) { + const uchar* imgPtr = img.data + img.step*ymap[y]; + const uchar* prevPtr = img.data + img.step*ymap[y-1]; + const uchar* nextPtr = img.data + img.step*ymap[y+1]; + float* gradPtr = (float*)grad.ptr(y); + uchar* qanglePtr = (uchar*)qangle.ptr(y); + + for (x = 0; x < width; x++) { + int x1 = xmap[x]; + dbuf[x] = (float)(lut[imgPtr[xmap[x+1]]] - lut[imgPtr[xmap[x-1]]]); + dbuf[width + x] = (float)(lut[nextPtr[x1]] - lut[prevPtr[x1]]); + } + + cartToPolar(Dx, Dy, Mag, Angle, false); + + for (x = 0; x < width; x++) { + float mag = dbuf[x+width*2], angle = dbuf[x+width*3]*angleScale - 0.5f; + int hidx = cvFloor(angle); + angle -= hidx; + gradPtr[x*2] = mag*(1.f - angle); + gradPtr[x*2+1] = mag*angle; + + if ( hidx < 0 ) + hidx += _nbins; + else if ( hidx >= _nbins ) + hidx -= _nbins; + assert((unsigned)hidx < (unsigned)_nbins); + + qanglePtr[x*2] = (uchar)hidx; + hidx++; + hidx &= hidx < _nbins ? -1 : 0; + qanglePtr[x*2+1] = (uchar)hidx; + } + } +} + + +struct HOGCache { + struct BlockData { + BlockData() : histOfs(0), imgOffset() {} + int histOfs; + Point imgOffset; + }; + + struct PixData { + size_t gradOfs, qangleOfs; + int histOfs[4]; + float histWeights[4]; + float gradWeight; + }; + + HOGCache(); + HOGCache(const HOGDescriptor* descriptor, + const Mat& img, Size paddingTL, Size paddingBR, + bool useCache, Size cacheStride); + virtual ~HOGCache() {}; + virtual void init(const HOGDescriptor* descriptor, + const Mat& img, Size paddingTL, Size paddingBR, + bool useCache, Size cacheStride); + + Size windowsInImage(Size imageSize, Size winStride) const; + Rect getWindow(Size imageSize, Size winStride, int idx) const; + + const float* getBlock(Point pt, float* buf); + virtual void normalizeBlockHistogram(float* histogram) const; + + vector pixData; + vector blockData; + + bool useCache; + vector ymaxCached; + Size winSize, cacheStride; + Size nblocks, ncells; + int blockHistogramSize; + int count1, count2, count4; + Point imgoffset; + Mat_ blockCache; + Mat_ blockCacheFlags; + + Mat grad, qangle; + const HOGDescriptor* descriptor; +}; + + +HOGCache::HOGCache() +{ + useCache = false; + blockHistogramSize = count1 = count2 = count4 = 0; + descriptor = 0; +} + +HOGCache::HOGCache(const HOGDescriptor* _descriptor, + const Mat& _img, Size _paddingTL, Size _paddingBR, + bool _useCache, Size _cacheStride) +{ + init(_descriptor, _img, _paddingTL, _paddingBR, _useCache, _cacheStride); +} + +void HOGCache::init(const HOGDescriptor* _descriptor, + const Mat& _img, Size _paddingTL, Size _paddingBR, + bool _useCache, Size _cacheStride) +{ + descriptor = _descriptor; + cacheStride = _cacheStride; + useCache = _useCache; + + descriptor->computeGradient(_img, grad, qangle, _paddingTL, _paddingBR); + imgoffset = _paddingTL; + + winSize = descriptor->winSize; + Size blockSize = descriptor->blockSize; + Size blockStride = descriptor->blockStride; + Size cellSize = descriptor->cellSize; + int i, j, nbins = descriptor->nbins; + int rawBlockSize = blockSize.width*blockSize.height; + + nblocks = Size((winSize.width - blockSize.width)/blockStride.width + 1, + (winSize.height - blockSize.height)/blockStride.height + 1); + ncells = Size(blockSize.width/cellSize.width, blockSize.height/cellSize.height); + blockHistogramSize = ncells.width*ncells.height*nbins; + + if ( useCache ) { + Size cacheSize((grad.cols - blockSize.width)/cacheStride.width+1, + (winSize.height/cacheStride.height)+1); + blockCache.create(cacheSize.height, cacheSize.width*blockHistogramSize); + blockCacheFlags.create(cacheSize); + size_t cacheRows = blockCache.rows; + ymaxCached.resize(cacheRows); + for (size_t ii = 0; ii < cacheRows; ii++ ) + ymaxCached[ii] = -1; + } + + Mat_ weights(blockSize); + float sigma = (float)descriptor->getWinSigma(); + float scale = 1.f/(sigma*sigma*2); + + float blockHalfHeight = blockSize.height*0.5f; + float blockHalfWidth = blockSize.width*0.5f; + for (i = 0; i < blockSize.height; i++) + for (j = 0; j < blockSize.width; j++) { + float di = i - blockHalfHeight; + float dj = j - blockHalfWidth; + weights(i, j) = std::exp(-(di*di + dj*dj)*scale); + } + + blockData.resize(nblocks.width*nblocks.height); + pixData.resize(rawBlockSize*3); + + /* + * Initialize 2 lookup tables, pixData & blockData. + * Here is why: + * + * The detection algorithm runs in 4 nested loops (at each pyramid layer): + * loop over the windows within the input image + * loop over the blocks within each window + * loop over the cells within each block + * loop over the pixels in each cell + * + * As each of the loops runs over a 2-dimensional array, + * we could get 8(!) nested loops in total, which is very-very slow. + * + * To speed the things up, we do the following: + * 1. loop over windows is unrolled in the HOGDescriptor::{compute|detect} methods; + * inside we compute the current search window using getWindow() method. + * Yes, it involves some overhead (function call + couple of divisions), + * but it's tiny in fact. + * 2. loop over the blocks is also unrolled. Inside we use pre-computed blockData[j] + * to set up gradient and histogram pointers. + * 3. loops over cells and pixels in each cell are merged + * (since there is no overlap between cells, each pixel in the block is processed once) + * and also unrolled. Inside we use PixData[k] to access the gradient values and + * update the histogram + */ + + count1 = count2 = count4 = 0; + for ( j = 0; j < blockSize.width; j++ ) + for ( i = 0; i < blockSize.height; i++ ) { + PixData* data = 0; + float cellX = (j+0.5f)/cellSize.width - 0.5f; + float cellY = (i+0.5f)/cellSize.height - 0.5f; + int icellX0 = cvFloor(cellX); + int icellY0 = cvFloor(cellY); + int icellX1 = icellX0 + 1, icellY1 = icellY0 + 1; + cellX -= icellX0; + cellY -= icellY0; + + if ( (unsigned)icellX0 < (unsigned)ncells.width && + (unsigned)icellX1 < (unsigned)ncells.width ) { + if ( (unsigned)icellY0 < (unsigned)ncells.height && + (unsigned)icellY1 < (unsigned)ncells.height ) { + data = &pixData[rawBlockSize*2 + (count4++)]; + data->histOfs[0] = (icellX0*ncells.height + icellY0)*nbins; + data->histWeights[0] = (1.f - cellX)*(1.f - cellY); + data->histOfs[1] = (icellX1*ncells.height + icellY0)*nbins; + data->histWeights[1] = cellX*(1.f - cellY); + data->histOfs[2] = (icellX0*ncells.height + icellY1)*nbins; + data->histWeights[2] = (1.f - cellX)*cellY; + data->histOfs[3] = (icellX1*ncells.height + icellY1)*nbins; + data->histWeights[3] = cellX*cellY; + } else { + data = &pixData[rawBlockSize + (count2++)]; + if ( (unsigned)icellY0 < (unsigned)ncells.height ) { + icellY1 = icellY0; + cellY = 1.f - cellY; + } + data->histOfs[0] = (icellX0*ncells.height + icellY1)*nbins; + data->histWeights[0] = (1.f - cellX)*cellY; + data->histOfs[1] = (icellX1*ncells.height + icellY1)*nbins; + data->histWeights[1] = cellX*cellY; + data->histOfs[2] = data->histOfs[3] = 0; + data->histWeights[2] = data->histWeights[3] = 0; + } + } else { + if ( (unsigned)icellX0 < (unsigned)ncells.width ) { + icellX1 = icellX0; + cellX = 1.f - cellX; + } + + if ( (unsigned)icellY0 < (unsigned)ncells.height && + (unsigned)icellY1 < (unsigned)ncells.height ) { + data = &pixData[rawBlockSize + (count2++)]; + data->histOfs[0] = (icellX1*ncells.height + icellY0)*nbins; + data->histWeights[0] = cellX*(1.f - cellY); + data->histOfs[1] = (icellX1*ncells.height + icellY1)*nbins; + data->histWeights[1] = cellX*cellY; + data->histOfs[2] = data->histOfs[3] = 0; + data->histWeights[2] = data->histWeights[3] = 0; + } else { + data = &pixData[count1++]; + if ( (unsigned)icellY0 < (unsigned)ncells.height ) { + icellY1 = icellY0; + cellY = 1.f - cellY; + } + data->histOfs[0] = (icellX1*ncells.height + icellY1)*nbins; + data->histWeights[0] = cellX*cellY; + data->histOfs[1] = data->histOfs[2] = data->histOfs[3] = 0; + data->histWeights[1] = data->histWeights[2] = data->histWeights[3] = 0; + } + } + data->gradOfs = (grad.cols*i + j)*2; + data->qangleOfs = (qangle.cols*i + j)*2; + data->gradWeight = weights(i, j); + } + + assert(count1 + count2 + count4 == rawBlockSize); + /* defragment pixData */ + for ( j = 0; j < count2; j++ ) + pixData[j + count1] = pixData[j + rawBlockSize]; + for ( j = 0; j < count4; j++ ) + pixData[j + count1 + count2] = pixData[j + rawBlockSize*2]; + count2 += count1; + count4 += count2; + + /* initialize blockData */ + for ( j = 0; j < nblocks.width; j++ ) + for ( i = 0; i < nblocks.height; i++ ) { + BlockData& data = blockData[j*nblocks.height + i]; + data.histOfs = (j*nblocks.height + i)*blockHistogramSize; + data.imgOffset = Point(j*blockStride.width, i*blockStride.height); + } +} + + +const float* HOGCache::getBlock(Point pt, float* buf) +{ + float* blockHist = buf; + assert(descriptor != 0); + + Size blockSize = descriptor->blockSize; + pt += imgoffset; + + CV_Assert( (unsigned)pt.x <= (unsigned)(grad.cols - blockSize.width) && + (unsigned)pt.y <= (unsigned)(grad.rows - blockSize.height) ); + + if ( useCache ) { + CV_Assert(pt.x % cacheStride.width == 0 && + pt.y % cacheStride.height == 0); + Point cacheIdx(pt.x/cacheStride.width, + (pt.y/cacheStride.height) % blockCache.rows); + if ( pt.y != ymaxCached[cacheIdx.y] ) { + Mat_ cacheRow = blockCacheFlags.row(cacheIdx.y); + cacheRow = (uchar)0; + ymaxCached[cacheIdx.y] = pt.y; + } + + blockHist = &blockCache[cacheIdx.y][cacheIdx.x*blockHistogramSize]; + uchar& computedFlag = blockCacheFlags(cacheIdx.y, cacheIdx.x); + if ( computedFlag != 0 ) + return blockHist; + computedFlag = (uchar)1; /* set it at once, before actual computing */ + } + + int k, C1 = count1, C2 = count2, C4 = count4; + const float* gradPtr = (const float*)(grad.data + grad.step*pt.y) + pt.x*2; + const uchar* qanglePtr = qangle.data + qangle.step*pt.y + pt.x*2; + + CV_Assert(blockHist != 0); + for ( k = 0; k < blockHistogramSize; k++ ) + blockHist[k] = 0.f; + + const PixData* _pixData = &pixData[0]; + + for ( k = 0; k < C1; k++ ) { + const PixData& pk = _pixData[k]; + const float* a = gradPtr + pk.gradOfs; + float w = pk.gradWeight*pk.histWeights[0]; + const uchar* h = qanglePtr + pk.qangleOfs; + int h0 = h[0], h1 = h[1]; + float* hist = blockHist + pk.histOfs[0]; + float t0 = hist[h0] + a[0]*w; + float t1 = hist[h1] + a[1]*w; + hist[h0] = t0; + hist[h1] = t1; + } + + for ( ; k < C2; k++ ) { + const PixData& pk = _pixData[k]; + const float* a = gradPtr + pk.gradOfs; + float w, t0, t1, a0 = a[0], a1 = a[1]; + const uchar* h = qanglePtr + pk.qangleOfs; + int h0 = h[0], h1 = h[1]; + + float* hist = blockHist + pk.histOfs[0]; + w = pk.gradWeight*pk.histWeights[0]; + t0 = hist[h0] + a0*w; + t1 = hist[h1] + a1*w; + hist[h0] = t0; + hist[h1] = t1; + + hist = blockHist + pk.histOfs[1]; + w = pk.gradWeight*pk.histWeights[1]; + t0 = hist[h0] + a0*w; + t1 = hist[h1] + a1*w; + hist[h0] = t0; + hist[h1] = t1; + } + + for ( ; k < C4; k++ ) { + const PixData& pk = _pixData[k]; + const float* a = gradPtr + pk.gradOfs; + float w, t0, t1, a0 = a[0], a1 = a[1]; + const uchar* h = qanglePtr + pk.qangleOfs; + int h0 = h[0], h1 = h[1]; + + float* hist = blockHist + pk.histOfs[0]; + w = pk.gradWeight*pk.histWeights[0]; + t0 = hist[h0] + a0*w; + t1 = hist[h1] + a1*w; + hist[h0] = t0; + hist[h1] = t1; + + hist = blockHist + pk.histOfs[1]; + w = pk.gradWeight*pk.histWeights[1]; + t0 = hist[h0] + a0*w; + t1 = hist[h1] + a1*w; + hist[h0] = t0; + hist[h1] = t1; + + hist = blockHist + pk.histOfs[2]; + w = pk.gradWeight*pk.histWeights[2]; + t0 = hist[h0] + a0*w; + t1 = hist[h1] + a1*w; + hist[h0] = t0; + hist[h1] = t1; + + hist = blockHist + pk.histOfs[3]; + w = pk.gradWeight*pk.histWeights[3]; + t0 = hist[h0] + a0*w; + t1 = hist[h1] + a1*w; + hist[h0] = t0; + hist[h1] = t1; + } + + normalizeBlockHistogram(blockHist); + + return blockHist; +} + +void HOGCache::normalizeBlockHistogram(float* _hist) const +{ +#ifdef ENABLE_NEON + /* NEON vector for loading the histogram to the memory */ + float32x4_t hist_v; + /* Initialize the accumulator for summation storing */ + float32x4_t acc = vdupq_n_f32(0.f); +#endif + + /* Histogram pointer in the memory */ + float *hist_ptr = &_hist[0]; + /* Variable to store values of summations */ + float sum = 0.f; + size_t sz = blockHistogramSize; + +#ifdef ENABLE_NEON + for (; sz != 0u; sz -= 4u) { + hist_v = vld1q_f32(hist_ptr); + acc = vmlaq_f32(acc, hist_v, hist_v); + hist_ptr += 4; + } + + sum += vgetq_lane_f32(acc, 0) + vgetq_lane_f32(acc, 1) + + vgetq_lane_f32(acc, 2) + vgetq_lane_f32(acc, 3); + + /* Reset accumulator */ + acc = vdupq_n_f32(0.f); + + sz = blockHistogramSize; + hist_ptr = &_hist[0]; +#else + for (size_t i = 0; i < sz; ++i) + sum += hist_ptr[i] * hist_ptr[i]; +#endif + + float scale = 1.f / (std::sqrt(sum) + sz * 0.1f); + sum = 0.f; + +#ifdef ENABLE_NEON + float32x4_t thres_v = vdupq_n_f32((float)descriptor->L2HysThreshold); + + for (; sz != 0; sz -= 4) { + /* Find minimal value among threshold and histogram value, accumulate + * this value squared */ + hist_v = vminq_f32(vmulq_n_f32(vld1q_f32(hist_ptr), scale), thres_v); + acc = vmlaq_f32(acc, hist_v, hist_v); + /* Update histograms in memory according with found min values */ + vst1q_f32(hist_ptr, hist_v); + hist_ptr += 4; + } + + sum += vgetq_lane_f32(acc, 0) + vgetq_lane_f32(acc, 1) + + vgetq_lane_f32(acc, 2) + vgetq_lane_f32(acc, 3); + +#else + float thresh = (float)descriptor->L2HysThreshold; + for (size_t i = 0; i < sz; ++i) { + hist_ptr[i] = std::min(hist_ptr[i] * scale, thresh); + sum += hist_ptr[i] * hist_ptr[i]; + } +#endif + + scale = 1.f / (std::sqrt(sum) + 1e-3f); + +#ifdef ENABLE_NEON + sz = blockHistogramSize; + hist_ptr = &_hist[0]; + + /* Scale histogram (normalize): */ + for (; sz != 0; sz -= 4) { + vst1q_f32(hist_ptr, vmulq_n_f32(vld1q_f32(hist_ptr), scale)); + hist_ptr += 4; + } +#else + for (size_t i = 0; i < sz; i++ ) + hist_ptr[i] *= scale; +#endif +} + + +Size HOGCache::windowsInImage(Size imageSize, Size winStride) const +{ + return Size((imageSize.width - winSize.width)/winStride.width + 1, + (imageSize.height - winSize.height)/winStride.height + 1); +} + +Rect HOGCache::getWindow(Size imageSize, Size winStride, int idx) const +{ + int nwindowsX = (imageSize.width - winSize.width)/winStride.width + 1; + int y = idx / nwindowsX; + int x = idx - nwindowsX*y; + return Rect( x*winStride.width, y*winStride.height, winSize.width, winSize.height ); +} + + +void HOGDescriptor::compute(const Mat& img, vector& descriptors, + Size winStride, Size padding, + const vector& locations) const +{ + if ( winStride == Size() ) + winStride = cellSize; + Size cacheStride(gcd(winStride.width, blockStride.width), + gcd(winStride.height, blockStride.height)); + size_t nwindows = locations.size(); + padding.width = (int)alignSize(std::max(padding.width, 0), cacheStride.width); + padding.height = (int)alignSize(std::max(padding.height, 0), cacheStride.height); + Size paddedImgSize(img.cols + padding.width*2, img.rows + padding.height*2); + + HOGCache cache(this, img, padding, padding, nwindows == 0, cacheStride); + + if ( !nwindows ) + nwindows = cache.windowsInImage(paddedImgSize, winStride).area(); + + const HOGCache::BlockData* blockData = &cache.blockData[0]; + + int nblocks = cache.nblocks.area(); + int blockHistogramSize = cache.blockHistogramSize; + size_t dsize = getDescriptorSize(); + descriptors.resize(dsize*nwindows); + + for ( size_t i = 0; i < nwindows; i++ ) { + float* descriptor = &descriptors[i*dsize]; + + Point pt0; + if ( !locations.empty() ) { + pt0 = locations[i]; + if ( pt0.x < -padding.width || pt0.x > img.cols + padding.width - winSize.width || + pt0.y < -padding.height || pt0.y > img.rows + padding.height - winSize.height ) + continue; + } else { + pt0 = cache.getWindow(paddedImgSize, winStride, (int)i).tl() - Point(padding); + CV_Assert(pt0.x % cacheStride.width == 0 && pt0.y % cacheStride.height == 0); + } + + for ( int j = 0; j < nblocks; j++ ) { + const HOGCache::BlockData& bj = blockData[j]; + Point pt = pt0 + bj.imgOffset; + + float* dst = descriptor + bj.histOfs; + const float* src = cache.getBlock(pt, dst); + if ( src != dst ) + for ( int k = 0; k < blockHistogramSize; k++ ) + dst[k] = src[k]; + } + } +} + + +void HOGDescriptor::detect(const Mat& img, + vector& hits, vector& weights, double hitThreshold, + Size winStride, Size padding, const vector& locations) const +{ + hits.clear(); + if ( svmDetector.empty() ) + return; + + if ( winStride == Size() ) + winStride = cellSize; + Size cacheStride(gcd(winStride.width, blockStride.width), + gcd(winStride.height, blockStride.height)); + size_t nwindows = locations.size(); + padding.width = (int)alignSize(std::max(padding.width, 0), cacheStride.width); + padding.height = (int)alignSize(std::max(padding.height, 0), cacheStride.height); + Size paddedImgSize(img.cols + padding.width*2, img.rows + padding.height*2); + + HOGCache cache(this, img, padding, padding, nwindows == 0, cacheStride); + + if ( !nwindows ) + nwindows = cache.windowsInImage(paddedImgSize, winStride).area(); + + const HOGCache::BlockData* blockData = &cache.blockData[0]; + + int nblocks = cache.nblocks.area(); + int blockHistogramSize = cache.blockHistogramSize; + size_t dsize = getDescriptorSize(); + + double rho = svmDetector.size() > dsize ? svmDetector[dsize] : 0; + vector blockHist(blockHistogramSize); + + for ( size_t i = 0; i < nwindows; i++ ) { + Point pt0; + if ( !locations.empty() ) { + pt0 = locations[i]; + if ( pt0.x < -padding.width || pt0.x > img.cols + padding.width - winSize.width || + pt0.y < -padding.height || pt0.y > img.rows + padding.height - winSize.height ) + continue; + } else { + pt0 = cache.getWindow(paddedImgSize, winStride, (int)i).tl() - Point(padding); + CV_Assert(pt0.x % cacheStride.width == 0 && pt0.y % cacheStride.height == 0); + } + double s = rho; + const float* svmVec = &svmDetector[0]; + int j, k; + + for ( j = 0; j < nblocks; j++, svmVec += blockHistogramSize ) { + const HOGCache::BlockData& bj = blockData[j]; + Point pt = pt0 + bj.imgOffset; + + const float* vec = cache.getBlock(pt, &blockHist[0]); +#ifdef ENABLE_NEON + float32x4_t vec_v; /* NEON feature vector */ + float32x4_t svm_v; /* NEON SVM feature weights */ + float32x4_t acc = vdupq_n_f32(0.f); /* NEON partial sum */ + for ( k = 0; k <= blockHistogramSize - 4; k += 4 ) { + vec_v = vld1q_f32(vec + k); + svm_v = vld1q_f32(svmVec + k); + acc = vmlaq_f32(acc, vec_v, svm_v); + } + + s += vgetq_lane_f32(acc, 0) + vgetq_lane_f32(acc, 1) + + vgetq_lane_f32(acc, 2) + vgetq_lane_f32(acc, 3); + +#else + for ( k = 0; k <= blockHistogramSize - 4; k += 4 ) + s += vec[k]*svmVec[k] + vec[k+1]*svmVec[k+1] + + vec[k+2]*svmVec[k+2] + vec[k+3]*svmVec[k+3]; +#endif + for ( ; k < blockHistogramSize; k++ ) + s += vec[k]*svmVec[k]; + } + + if ( s >= hitThreshold ) { + hits.push_back(pt0); + weights.push_back(s); + } + } +} + +void HOGDescriptor::detect(const Mat& img, vector& hits, double hitThreshold, + Size winStride, Size padding, const vector& locations) const +{ + vector weightsV; + detect(img, hits, weightsV, hitThreshold, winStride, padding, locations); +} + +class HOGInvoker : public ParallelLoopBody { + public: + HOGInvoker(const HOGDescriptor* _hog, const Mat& _img, + double _hitThreshold, Size _winStride, Size _padding, + const double* _levelScale, std::vector * _vec, Mutex* _mtx, + std::vector* _weights = 0, std::vector* _scales = 0) { + hog = _hog; + img = _img; + hitThreshold = _hitThreshold; + winStride = _winStride; + padding = _padding; + levelScale = _levelScale; + vec = _vec; + weights = _weights; + scales = _scales; + mtx = _mtx; + } + + void operator()(const Range& range) const { + int i, i1 = range.start, i2 = range.end; + double minScale = i1 > 0 ? levelScale[i1] : i2 > 1 ? levelScale[i1+1] : std::max(img.cols, img.rows); + Size maxSz(cvCeil(img.cols/minScale), cvCeil(img.rows/minScale)); + Mat smallerImgBuf(maxSz, img.type()); + vector locations; + vector hitsWeights; + + Size wholeSize; + Point offset; + img.locateROI(wholeSize, offset); + + for ( i = i1; i < i2; i++ ) { + double scale = levelScale[i]; + Size sz(cvRound(img.cols/scale), cvRound(img.rows/scale)); + Mat smallerImg(sz, img.type(), smallerImgBuf.data); + if (sz == img.size()) + smallerImg = Mat(sz, img.type(), img.data, img.step); + else + resize(img, smallerImg, sz); + hog->detect(smallerImg, locations, hitsWeights, hitThreshold, winStride, padding); + + Size scaledWinSize = Size(cvRound(hog->winSize.width*scale), cvRound(hog->winSize.height*scale)); + + mtx->lock(); + for ( size_t j = 0; j < locations.size(); j++ ) { + vec->push_back(Rect(cvRound(locations[j].x*scale), + cvRound(locations[j].y*scale), + scaledWinSize.width, scaledWinSize.height)); + if (scales) { + scales->push_back(scale); + } + } + mtx->unlock(); + + if (weights && (!hitsWeights.empty())) { + mtx->lock(); + for (size_t j = 0; j < locations.size(); j++) { + weights->push_back(hitsWeights[j]); + } + mtx->unlock(); + } + } + } + + const HOGDescriptor* hog; + Mat img; + double hitThreshold; + Size winStride; + Size padding; + const double* levelScale; + std::vector* vec; + std::vector* weights; + std::vector* scales; + Mutex* mtx; +}; + + +void HOGDescriptor::detectMultiScale( + const Mat& img, vector& foundLocations, vector& foundWeights, + double hitThreshold, Size winStride, Size padding, + double scale0, double finalThreshold, bool useMeanshiftGrouping) const +{ + double scale = 1.; + int levels = 0; + + vector levelScale; + for ( levels = 0; levels < nlevels; levels++ ) { + levelScale.push_back(scale); + if ( cvRound(img.cols/scale) < winSize.width || + cvRound(img.rows/scale) < winSize.height || + scale0 <= 1 ) + break; + scale *= scale0; + } + levels = std::max(levels, 1); + levelScale.resize(levels); + + std::vector allCandidates; + std::vector tempScales; + std::vector tempWeights; + std::vector foundScales; + Mutex mtx; + + modifiedcv::parallel_for_(Range(0, (int)levelScale.size()), + HOGInvoker(this, img, hitThreshold, winStride, padding, &levelScale[0], &allCandidates, &mtx, &tempWeights, &tempScales)); + + std::copy(tempScales.begin(), tempScales.end(), back_inserter(foundScales)); + foundLocations.clear(); + std::copy(allCandidates.begin(), allCandidates.end(), back_inserter(foundLocations)); + foundWeights.clear(); + std::copy(tempWeights.begin(), tempWeights.end(), back_inserter(foundWeights)); + + if ( useMeanshiftGrouping ) { + groupRectangles_meanshift(foundLocations, foundWeights, foundScales, finalThreshold, winSize); + } else { + groupRectangles(foundLocations, foundWeights, (int)finalThreshold, 0.2); + } +} + +void HOGDescriptor::detectMultiScale(const Mat& img, vector& foundLocations, + double hitThreshold, Size winStride, Size padding, + double scale0, double finalThreshold, bool useMeanshiftGrouping) const +{ + vector foundWeights; + detectMultiScale(img, foundLocations, foundWeights, hitThreshold, winStride, + padding, scale0, finalThreshold, useMeanshiftGrouping); +} + +void HOGDescriptor::groupRectangles(vector& rectList, vector& weights, int groupThreshold, double eps) const +{ + if ( groupThreshold <= 0 || rectList.empty() ) { + return; + } + + CV_Assert(rectList.size() == weights.size()); + + vector labels; + int nclasses = partition(rectList, labels, SimilarRects(eps)); + + vector > rrects(nclasses); + vector numInClass(nclasses, 0); + vector foundWeights(nclasses, DBL_MIN); + int i, j, nlabels = (int)labels.size(); + + for ( i = 0; i < nlabels; i++ ) { + int cls = labels[i]; + rrects[cls].x += rectList[i].x; + rrects[cls].y += rectList[i].y; + rrects[cls].width += rectList[i].width; + rrects[cls].height += rectList[i].height; + foundWeights[cls] = max(foundWeights[cls], weights[i]); + numInClass[cls]++; + } + + for ( i = 0; i < nclasses; i++ ) { + /* find the average of all ROI in the cluster */ + cv::Rect_ r = rrects[i]; + double s = 1.0/numInClass[i]; + rrects[i] = cv::Rect_(cv::saturate_cast(r.x*s), + cv::saturate_cast(r.y*s), + cv::saturate_cast(r.width*s), + cv::saturate_cast(r.height*s)); + } + + rectList.clear(); + weights.clear(); + + for ( i = 0; i < nclasses; i++ ) { + cv::Rect r1 = rrects[i]; + int n1 = numInClass[i]; + double w1 = foundWeights[i]; + if ( n1 <= groupThreshold ) + continue; + /* filter out small rectangles inside large rectangles */ + for ( j = 0; j < nclasses; j++ ) { + int n2 = numInClass[j]; + + if ( j == i || n2 <= groupThreshold ) + continue; + + cv::Rect r2 = rrects[j]; + + int dx = cv::saturate_cast(r2.width * eps); + int dy = cv::saturate_cast(r2.height * eps); + + if ( r1.x >= r2.x - dx && + r1.y >= r2.y - dy && + r1.x + r1.width <= r2.x + r2.width + dx && + r1.y + r1.height <= r2.y + r2.height + dy && + (n2 > std::max(3, n1) || n1 < 3) ) + break; + } + + if ( j == nclasses ) { + rectList.push_back(r1); + weights.push_back(w1); + } + } +} +} diff --git a/mv_surveillance/surveillance/src/SurveillanceHelper.cpp b/mv_surveillance/surveillance/src/SurveillanceHelper.cpp new file mode 100644 index 0000000..bbd92e9 --- /dev/null +++ b/mv_surveillance/surveillance/src/SurveillanceHelper.cpp @@ -0,0 +1,200 @@ +/** + * Copyright (c) 2015 Samsung Electronics Co., Ltd All Rights Reserved + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#include "SurveillanceHelper.h" + +#include + +#include "opencv2/highgui/highgui.hpp" + +namespace mediavision { +namespace surveillance { + +int SurveillanceHelper::convertSourceMV2GrayCV(mv_source_h mvSource, cv::Mat& cvSource) +{ + MEDIA_VISION_INSTANCE_CHECK(mvSource); + + int depth = CV_8U; /* Default depth. 1 byte per channel. */ + unsigned int channelsNumber = 0; + unsigned int width = 0, height = 0; + unsigned int bufferSize = 0; + unsigned char *buffer = NULL; + + mv_colorspace_e colorspace = MEDIA_VISION_COLORSPACE_INVALID; + + MEDIA_VISION_ASSERT(mv_source_get_width(mvSource, &width), + "Failed to get the width."); + MEDIA_VISION_ASSERT(mv_source_get_height(mvSource, &height), + "Failed to get the height."); + MEDIA_VISION_ASSERT(mv_source_get_colorspace(mvSource, &colorspace), + "Failed to get the colorspace."); + MEDIA_VISION_ASSERT(mv_source_get_buffer(mvSource, &buffer, &bufferSize), + "Failed to get the buffer size."); + + int conversionType = -1; /* Type of conversion from given colorspace to gray */ + switch(colorspace) { + case MEDIA_VISION_COLORSPACE_INVALID: + LOGE("Error: mv_source has invalid colorspace."); + return MEDIA_VISION_ERROR_INVALID_PARAMETER; + case MEDIA_VISION_COLORSPACE_Y800: + channelsNumber = 1; + /* Without convertion */ + break; + case MEDIA_VISION_COLORSPACE_I420: + channelsNumber = 1; + height *= 1.5; + conversionType = CV_YUV2GRAY_I420; + break; + case MEDIA_VISION_COLORSPACE_NV12: + channelsNumber = 1; + height *= 1.5; + conversionType = CV_YUV2GRAY_NV12; + break; + case MEDIA_VISION_COLORSPACE_YV12: + channelsNumber = 1; + height *= 1.5; + conversionType = CV_YUV2GRAY_YV12; + break; + case MEDIA_VISION_COLORSPACE_NV21: + channelsNumber = 1; + height *= 1.5; + conversionType = CV_YUV2GRAY_NV21; + break; + case MEDIA_VISION_COLORSPACE_YUYV: + channelsNumber = 2; + conversionType = CV_YUV2GRAY_YUYV; + break; + case MEDIA_VISION_COLORSPACE_UYVY: + channelsNumber = 2; + conversionType = CV_YUV2GRAY_UYVY; + break; + case MEDIA_VISION_COLORSPACE_422P: + channelsNumber = 2; + conversionType = CV_YUV2GRAY_Y422; + break; + case MEDIA_VISION_COLORSPACE_RGB565: + channelsNumber = 2; + conversionType = CV_BGR5652GRAY; + break; + case MEDIA_VISION_COLORSPACE_RGB888: + channelsNumber = 3; + conversionType = CV_RGB2GRAY; + break; + case MEDIA_VISION_COLORSPACE_RGBA: + channelsNumber = 4; + conversionType = CV_RGBA2GRAY; + break; + default: + LOGE("Error: mv_source has unsupported colorspace."); + return MEDIA_VISION_ERROR_NOT_SUPPORTED_FORMAT; + } + + if (conversionType == -1) { /* Without conversion */ + cvSource = cv::Mat(cv::Size(width, height), + CV_MAKETYPE(depth, channelsNumber), buffer).clone(); + } else { /* Conversion */ + /* Class for representation the given image as cv::Mat before conversion */ + cv::Mat origin(cv::Size(width, height), + CV_MAKETYPE(depth, channelsNumber), buffer); + cv::cvtColor(origin, cvSource, conversionType); + } + + return MEDIA_VISION_ERROR_NONE; +} + +#ifdef ENABLE_NEON +int SurveillanceHelper::convertSourceMVRGB2GrayCVNeon( + mv_source_h mvSource, + cv::Mat& cvSource) +{ + MEDIA_VISION_INSTANCE_CHECK(mvSource); + + const int depth = CV_8U; /* Default depth. 1 byte per channel. */ + unsigned int width = 0, height = 0; + unsigned int bufferSize = 0; + unsigned char *src = NULL; + + mv_colorspace_e colorspace = MEDIA_VISION_COLORSPACE_INVALID; + + MEDIA_VISION_ASSERT(mv_source_get_width(mvSource, &width), + "Failed to get the width."); + MEDIA_VISION_ASSERT(mv_source_get_height(mvSource, &height), + "Failed to get the height."); + MEDIA_VISION_ASSERT(mv_source_get_colorspace(mvSource, &colorspace), + "Failed to get the colorspace."); + MEDIA_VISION_ASSERT(mv_source_get_buffer(mvSource, &src, &bufferSize), + "Failed to get the buffer size."); + + if (colorspace != MEDIA_VISION_COLORSPACE_RGB888) { + LOGE("Error: mv_source has unsupported colorspace."); + return MEDIA_VISION_ERROR_NOT_SUPPORTED_FORMAT; + } + + cvSource = cv::Mat(cv::Size(width, height), CV_MAKETYPE(depth, 1)); + const unsigned int cvSourceSize = width * height; + +#if defined(__aarch64__) + asm volatile ("lsr %2, %2, #3 \n" + "# channel multimpliers: \n" + "mov w4, #28 \n" + "mov w5, #151 \n" + "mov w6, #77 \n" + "dup v3.8b, w4 \n" + "dup v4.8b, w5 \n" + "dup v5.8b, w6 \n" + ".loop: \n" + "# load 8 pixels: \n" + "ld3 {v0.8b,v1.8b,v2.8b}, [%0],#24 \n" + "# conversion: \n" + "umull v7.8h, v0.8b, v3.8b \n" + "umlal v7.8h, v1.8b, v4.8b \n" + "umlal v7.8h, v2.8b, v5.8b \n" + "# shift and store: \n" + "shrn v6.8b, v7.8h, #8 \n" + "st1 {v6.8b}, [%1],#8 \n" + "subs %2, %2, #1 \n" + "bne .loop \n"::"r" (src), "r" (cvSource.data), "r" (cvSourceSize) + :"memory", "w4", "w5", "w6"); +#else + asm volatile ("lsr %2, %2, #3 \n" + "# channel multimpliers: \n" + "mov r4, #77 \n" + "mov r5, #151 \n" + "mov r6, #28 \n" + "vdup.8 d3, r4 \n" + "vdup.8 d4, r5 \n" + "vdup.8 d5, r6 \n" + ".loop: \n" + "# load 8 pixels: \n" + "vld3.8 {d0-d2}, [%0]! \n" + "# conversion: \n" + "vmull.u8 q7, d0, d3 \n" + "vmlal.u8 q7, d1, d4 \n" + "vmlal.u8 q7, d2, d5 \n" + "# shift and store: \n" + "vshrn.u16 d6, q7, #8 \n" + "vst1.8 {d6}, [%1]! \n" + "subs %2, %2, #1 \n" + "bne .loop \n"::"r" (src), "r" (cvSource.data), "r" (cvSourceSize) + :"memory", "r4", "r5", "r6"); +#endif + + return MEDIA_VISION_ERROR_NONE; +} +#endif + +} /* surveillance */ +} /* mediavision */ diff --git a/mv_surveillance/surveillance/src/mv_absdiff.c b/mv_surveillance/surveillance/src/mv_absdiff.c new file mode 100644 index 0000000..9e122e6 --- /dev/null +++ b/mv_surveillance/surveillance/src/mv_absdiff.c @@ -0,0 +1,81 @@ +/** + * Copyright (c) 2015 Samsung Electronics Co., Ltd All Rights Reserved + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#include "mv_absdiff.h" + +#include "mv_common.h" +#include "mv_private.h" + +#ifdef ENABLE_NEON +#include +#endif + +int mv_absdiff( + uint8_t *__restrict__ src1, + uint8_t *__restrict__ src2, + int width, + int height, + int stride, + uint8_t *__restrict__ dst) +{ + if (src1 == NULL || src2 == NULL || width <= 0 || height <= 0 || + stride <= 0 || dst == NULL) { + LOGE("Wrong input parameter. Aplpying mask failed."); + return MEDIA_VISION_ERROR_INVALID_PARAMETER; + } + + int column = 0; + int row = 0; + const int padding = stride - width; + +#ifdef ENABLE_NEON + const int batch_size = 16; + const int batch_columns_count = width / batch_size; +#endif + + for (; row < height; ++row) { +#ifdef ENABLE_NEON + for (column = 0; column < batch_columns_count; ++column) { + uint8x16_t gray1 = vld1q_u8 (src1); + uint8x16_t gray2 = vld1q_u8 (src2); + + uint8x16_t dst_temp = vabdq_u8(gray1, gray2); + + vst1q_u8 (dst, dst_temp); + + src1 += batch_size; + src2 += batch_size; + dst += batch_size; + } +#else + for (column = 0; column < width; ++column) { + uint8_t gray1 = *src1; + uint8_t gray2 = *src2; + + (*dst) = abs((*src1) - (*src2)); + + ++src1; + ++src2; + ++dst; + } +#endif + src1 += padding; + src2 += padding; + dst += padding; + } + + return MEDIA_VISION_ERROR_NONE; +} diff --git a/mv_surveillance/surveillance/src/mv_apply_mask.c b/mv_surveillance/surveillance/src/mv_apply_mask.c new file mode 100644 index 0000000..f156d07 --- /dev/null +++ b/mv_surveillance/surveillance/src/mv_apply_mask.c @@ -0,0 +1,77 @@ +/** + * Copyright (c) 2015 Samsung Electronics Co., Ltd All Rights Reserved + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#include "mv_apply_mask.h" + +#include "mv_common.h" +#include "mv_private.h" + +#ifdef ENABLE_NEON +#include +#endif + +int mv_apply_mask( + uint8_t *src_buffer, + uint8_t *__restrict mask, + int width, + int height, + int stride, + uint8_t *dst_buffer) +{ + if (src_buffer == NULL || mask == NULL || width <= 0 || height <= 0 || + stride <= 0 || dst_buffer == NULL) { + LOGE("Wrong input parameter. Aplpying mask failed."); + return MEDIA_VISION_ERROR_INVALID_PARAMETER; + } + + const int padding = stride - width; + +#ifdef ENABLE_NEON + const int batch_size = 16; + const int batch_columns_count = width / batch_size; +#endif + + int row = 0; + int column = 0; + for (; row < height; ++row) { +#ifdef ENABLE_NEON + for (column = 0; column < batch_columns_count; ++column) { + uint8x16_t src_v = vld1q_u8(src_buffer); + uint8x16_t mask_v = vld1q_u8(mask); + + uint8x16_t dst_v = vandq_u8(src_v, mask_v); + + vst1q_u8(dst_buffer, dst_v); + + dst_buffer += batch_size; + src_buffer += batch_size; + mask += batch_size; + } +#else + for (column = 0; column < width; ++column) { + (*dst_buffer) = ((*src_buffer) & (*mask)); + ++dst_buffer; + ++src_buffer; + ++mask; + } +#endif + dst_buffer += padding; + src_buffer += padding; + mask += padding; + } + + return MEDIA_VISION_ERROR_NONE; +} diff --git a/mv_surveillance/surveillance/src/mv_mask_buffer.c b/mv_surveillance/surveillance/src/mv_mask_buffer.c new file mode 100644 index 0000000..f1d50ef --- /dev/null +++ b/mv_surveillance/surveillance/src/mv_mask_buffer.c @@ -0,0 +1,89 @@ +/** + * Copyright (c) 2015 Samsung Electronics Co., Ltd All Rights Reserved + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#include "mv_mask_buffer.h" + +#include "mv_common.h" +#include "mv_private.h" + +#include + +int mv_get_mask_buffer( + unsigned int buffer_width, + unsigned int buffer_height, + mv_point_s *polygon, + unsigned int points_number, + unsigned char **mask_buffer) +{ + if (buffer_width == 0u || buffer_height == 0u || + polygon == NULL|| points_number == 0u || mask_buffer == NULL) { + LOGE("Wrong input parameter. Getting mask buffer failed."); + return MEDIA_VISION_ERROR_INVALID_PARAMETER; + } + + const unsigned int buffer_size = buffer_width * buffer_height; + + *mask_buffer = (unsigned char*) malloc(sizeof(unsigned char) * buffer_size); + + unsigned int i = 0u; + unsigned int j = 0u; + unsigned int k = 0u; + + int max_x = polygon[0].x; + int max_y = polygon[0].y; + int min_x = polygon[0].x; + int min_y = polygon[0].y; + + for (k = 1u; k < points_number; ++k) { + if (polygon[k].x > max_x) { + max_x = polygon[k].x; + } else if (polygon[k].x < min_x) { + min_x = polygon[k].x; + } + + if (polygon[k].y > max_y) { + max_y = polygon[k].y; + } else if (polygon[k].y < min_y) { + min_y = polygon[k].y; + } + } + + + for (k = 0u; k < buffer_size; ++k) { + bool inside_polygon = false; + + const int test_x = (int) k % buffer_width; + const int test_y = (int) k / buffer_width; + + if (test_x > max_x || test_x < min_x || test_y > max_y || test_y < min_y) { + (*mask_buffer)[k] = 0; + continue; + } + + for (i = 0u, j = points_number - 1; i < points_number; j = i++) { + if (((polygon[i].y > test_y) != (polygon[j].y > test_y)) && + ((float) test_x < (float) (polygon[j].x - polygon[i].x) * + (test_y - polygon[i].y) / + (polygon[j].y - polygon[i].y) + + polygon[i].x)) { + inside_polygon = !inside_polygon; + } + } + inside_polygon ? ((*mask_buffer)[k] = 255) : ((*mask_buffer)[k] = 0); + } + + return MEDIA_VISION_ERROR_NONE; +} diff --git a/mv_surveillance/surveillance/src/mv_surveillance_open.cpp b/mv_surveillance/surveillance/src/mv_surveillance_open.cpp new file mode 100644 index 0000000..4d4c328 --- /dev/null +++ b/mv_surveillance/surveillance/src/mv_surveillance_open.cpp @@ -0,0 +1,140 @@ +/** + * Copyright (c) 2015 Samsung Electronics Co., Ltd All Rights Reserved + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#include "mv_surveillance_open.h" + +#include "EventManager.h" +#include "EventResult.h" + +#include + +using namespace mediavision::surveillance; + +int mv_surveillance_subscribe_event_trigger_open( + mv_surveillance_event_trigger_h event_trigger, + int video_stream_id, + mv_engine_config_h engine_cfg, + mv_surveillance_event_occurred_cb callback, + void *user_data) +{ + mv_surveillance_event_trigger_s *handle = + (mv_surveillance_event_trigger_s *)event_trigger; + + return EventManager::getInstance().registerEvent( + event_trigger, + static_cast(handle->trigger_id), + handle->event_type, + video_stream_id, + engine_cfg, + callback, + user_data, + handle->number_of_roi_points, + handle->roi); +} + +int mv_surveillance_unsubscribe_event_trigger_open( + mv_surveillance_event_trigger_h event_trigger, + int video_stream_id) +{ + mv_surveillance_event_trigger_s *handle = + (mv_surveillance_event_trigger_s *)event_trigger; + + return EventManager::getInstance().unregisterEvent( + static_cast(handle->trigger_id), + video_stream_id); +} + +int mv_surveillance_push_source_open( + mv_source_h source, + int video_stream_id) +{ + MEDIA_VISION_INSTANCE_CHECK(source); + + return EventManager::getInstance().pushSource(source, video_stream_id); +} + +int mv_surveillance_foreach_event_type_open( + mv_surveillance_event_type_cb callback, + void *user_data) +{ + StringVector eventTypes; + const int error = EventManager::getInstance().getSupportedEventTypes(eventTypes); + + if (error != MEDIA_VISION_ERROR_NONE) { + LOGE("Query events failed due to internal issues. Error code: %i", + error); + return error; + } + + StringConstIter eventIter = eventTypes.begin(); + while (eventIter != eventTypes.end()) { + if (!callback((*eventIter).c_str(), user_data)) { + break; + } + ++eventIter; + } + + return MEDIA_VISION_ERROR_NONE; +} + +int mv_surveillance_foreach_event_result_value_name_open( + const char *event_type, + mv_surveillance_event_result_name_cb callback, + void *user_data) +{ + StringVector eventResultValueNames; + + int error = MEDIA_VISION_ERROR_NONE; + + if (NULL == event_type) { + error = EventManager::getInstance().getSupportedEventResultValueNames( + eventResultValueNames); + } else { + error = EventManager::getInstance().getSupportedEventResultValueNames( + event_type, + eventResultValueNames); + } + + if (error != MEDIA_VISION_ERROR_NONE) { + LOGE("Query result value names failed due to internal issues. " + "Error code: %i", error); + return error; + } + + StringConstIter ervnIter = eventResultValueNames.begin(); + while (ervnIter != eventResultValueNames.end()) { + if (!callback((*ervnIter).c_str(), user_data)) { + break; + } + ++ervnIter; + } + + return MEDIA_VISION_ERROR_NONE; +} + +int mv_surveillance_get_result_value_open( + mv_surveillance_result_h result, + const char *value_name, + void *value) +{ + MEDIA_VISION_INSTANCE_CHECK(result); + MEDIA_VISION_NULL_ARG_CHECK(value_name); + MEDIA_VISION_NULL_ARG_CHECK(value); + + EventResult *eventResult = (EventResult*) result; + + return eventResult->getResultValue(value_name, value); +} diff --git a/mv_surveillance/surveillance_lic/CMakeLists.txt b/mv_surveillance/surveillance_lic/CMakeLists.txt new file mode 100644 index 0000000..266b94a --- /dev/null +++ b/mv_surveillance/surveillance_lic/CMakeLists.txt @@ -0,0 +1,25 @@ +project(${MV_SURVEILLANCE_LIB_NAME}) +cmake_minimum_required(VERSION 2.6) + +set_property(DIRECTORY APPEND PROPERTY COMPILE_DEFINITIONS_DEBUG _DEBUG) + +set(CMAKE_ARCHIVE_OUTPUT_DIRECTORY ${CMAKE_BINARY_DIR}/lib) +set(CMAKE_LIBRARY_OUTPUT_DIRECTORY ${CMAKE_BINARY_DIR}/lib) +set(CMAKE_RUNTIME_OUTPUT_DIRECTORY ${CMAKE_BINARY_DIR}/bin) + +include_directories("${INC_DIR}") +include_directories("${PROJECT_SOURCE_DIR}/include") +include_directories("${PROJECT_SOURCE_DIR}/src") + +file(GLOB MV_SURVEILLANCE_INC_LIST "${PROJECT_SOURCE_DIR}/include/*.h") +file(GLOB MV_SURVEILLANCE_SRC_LIST "${PROJECT_SOURCE_DIR}/src/*.c") + +if(FORCED_STATIC_BUILD) + add_library(${PROJECT_NAME} STATIC ${MV_SURVEILLANCE_INC_LIST} ${MV_SURVEILLANCE_SRC_LIST}) +else() + add_library(${PROJECT_NAME} SHARED ${MV_SURVEILLANCE_INC_LIST} ${MV_SURVEILLANCE_SRC_LIST}) +endif() + +target_link_libraries(${PROJECT_NAME} ${MV_COMMON_LIB_NAME}) + +INSTALL(TARGETS ${PROJECT_NAME} DESTINATION lib) diff --git a/mv_surveillance/surveillance_lic/include/mv_surveillance_lic.h b/mv_surveillance/surveillance_lic/include/mv_surveillance_lic.h new file mode 100644 index 0000000..16bd4fc --- /dev/null +++ b/mv_surveillance/surveillance_lic/include/mv_surveillance_lic.h @@ -0,0 +1,187 @@ +/** + * Copyright (c) 2015 Samsung Electronics Co., Ltd All Rights Reserved + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#ifndef __TIZEN_MEDIAVISION_SURVEILLANCE_LIC_H__ +#define __TIZEN_MEDIAVISION_SURVEILLANCE_LIC_H__ + +#include + +#ifdef __cplusplus +extern "C" { +#endif /* __cplusplus */ + +/** + * @file mv_surveillance_lic.h + * @brief This file contains the Media Vision surveillance API + */ + +/** + * @brief Allows to subscribe to the event and start calling @a callback + * each time when the @a source is pushed using + * @ref mv_surveillance_push_source_lic() and event is detected. + * + * @since_tizen 3.0 + * @remarks To stop handling triggering use + * @ref mv_surveillance_unsubscribe_event_trigger_lic(). + * @param [in] event_trigger The event trigger activating calls of the + * @a callback function + * @param [in] engine_cfg The engine configuration of the event + * @param [in] callback Callback to be called each time when event + * occurrence is detected + * @param [in] user_data The user data to be passed to the callback function + * @return @c 0 on success, otherwise a negative error value + * @retval #MEDIA_VISION_ERROR_NONE Successful + * @retval #MEDIA_VISION_ERROR_INVALID_PARAMETER Invalid parameter + * @retval #MEDIA_VISION_ERROR_NOT_SUPPORTED Not supported + * + * @post @ref mv_surveillance_event_occurred_cb + * + * @see mv_surveillance_event_trigger_s + * @see mv_surveillance_unsubscribe_event_trigger_lic() + */ +int mv_surveillance_subscribe_event_trigger_lic( + mv_surveillance_event_trigger_h event_trigger, + mv_engine_config_h engine_cfg, + mv_surveillance_event_occurred_cb callback, + void *user_data); + +/** + * @brief Allows to unsubscribe from the event and stop calling @a callback. + * + * @since_tizen 3.0 + * @remarks To start handling trigger activation use + @ref mv_surveillance_subscribe_event_trigger_lic(). + * @param [in] event_trigger The event trigger for which subscription will be + * stopped + * @return @c 0 on success, otherwise a negative error value + * @retval #MEDIA_VISION_ERROR_NONE Successful + * @retval #MEDIA_VISION_ERROR_INVALID_PARAMETER Invalid parameter + * @retval #MEDIA_VISION_ERROR_NOT_SUPPORTED Not supported + * + * @post @ref mv_surveillance_event_occurred_cb + * + * @see mv_surveillance_event_trigger_s + * @see mv_surveillance_subscribe_event_trigger_lic() + */ +int mv_surveillance_unsubscribe_event_trigger_lic( + mv_surveillance_event_trigger_h event_trigger); + +/** + * @brief Allows to push source to the event trigger and start calling @a callback. + * + * @since_tizen 3.0 + * @param [in] source The handle to the media source + * @param [in] video_stream_id The video stream, wthich will be updated + * @return @c 0 on success, otherwise a negative error value + * @retval #MEDIA_VISION_ERROR_NONE Successful + * @retval #MEDIA_VISION_ERROR_INVALID_PARAMETER Invalid parameter + * @retval #MEDIA_VISION_ERROR_NOT_SUPPORTED Not supported + * + * @see mv_surveillance_event_trigger_s + * @see mv_surveillance_event_occurred_cb + * @see mv_surveillance_subscribe_event_trigger_lic() + * @see mv_surveillance_unsubscribe_event_trigger_lic() + */ +int mv_surveillance_push_source_lic( + mv_source_h source, + int video_stream_id); + +/** + * @brief Starts traversing through list of supported event types. + * + * @since_tizen 3.0 + * @remarks Supported event types and their descriptions can be found in + * @ref CAPI_MEDIA_VISION_SURVEILLANCE_EVENT_TYPES documentation + * section + * @param [in] callback The callback function to be called for each + * supported event type + * @param [in] user_data The user data to be passed to the @a callback + * function + * @return @c 0 on success, otherwise a negative error value + * @retval #MEDIA_VISION_ERROR_NONE Successful + * @retval #MEDIA_VISION_ERROR_INVALID_PARAMETER Invalid parameter + * @retval #MEDIA_VISION_ERROR_NOT_SUPPORTED Not supported + * + * @see mv_surveillance_event_type_cb + * @see mv_surveillance_foreach_event_result_value_name_lic() + */ +int mv_surveillance_foreach_event_type_lic( + mv_surveillance_event_type_cb callback, + void *user_data); + +/** + * @brief Starts traversing through list of supported event result value names. + * + * @since_tizen 3.0 + * @remarks Supported event types, event result value names and their + * descriptions can be found in + * @ref CAPI_MEDIA_VISION_SURVEILLANCE_EVENT_TYPES documentation + * section + * @param [in] event_type The name of the event type for which result value + * names will be passed to the @a callback. Can be + * set @c NULL. If set @c NULL then all supported + * event result value names will be traversed + * @param [in] callback The callback function to be called for each + * supported event result value name + * @param [in] user_data The user data to be passed to the @a callback + * function + * @return @c 0 on success, otherwise a negative error value + * @retval #MEDIA_VISION_ERROR_NONE Successful + * @retval #MEDIA_VISION_ERROR_INVALID_PARAMETER Invalid parameter + * @retval #MEDIA_VISION_ERROR_NOT_SUPPORTED Not supported + * + * @see mv_surveillance_event_result_value_name_cb + * @see mv_surveillance_foreach_event_type_lic() + * @see mv_surveillance_get_result_value_lic() + */ +int mv_surveillance_foreach_event_result_value_name_lic( + const char *event_type, + mv_surveillance_event_result_value_name_cb callback, + void *user_data); + +/** + * @brief Gets result value. + * @details See the output values names in the event types descriptions located + * in /usr/share/config/capi-media-vision/surveillance-event-types.txt. + * + * @since_tizen 3.0 + * @param [in] result The handle to the event result + * @param [in] value_name The name of the value to be gotten + * @param [in, out] value The pointer to variable which will be filled + * by result value + * @return @c 0 on success, otherwise a negative error value + * @retval #MEDIA_VISION_ERROR_NONE Successful + * @retval #MEDIA_VISION_ERROR_INVALID_PARAMETER Invalid parameter + * @retval #MEDIA_VISION_ERROR_NOT_SUPPORTED Not supported + * + * @pre Memory for value has to be allocated + * + * @see mv_surveillance_event_trigger_s + * @see mv_surveillance_event_occurred_cb + * @see mv_surveillance_subscribe_event_trigger_lic() + * @see mv_surveillance_unsubscribe_event_trigger_lic() + * @see mv_surveillance_query_events_lic() + */ +int mv_surveillance_get_result_value_lic( + mv_surveillance_result_h result, + const char *value_name, + void *value); + +#ifdef __cplusplus +} +#endif /* __cplusplus */ + +#endif /* __TIZEN_MEDIAVISION_SURVEILLANCE_LIC_H__ */ diff --git a/mv_surveillance/surveillance_lic/src/mv_surveillance_lic.c b/mv_surveillance/surveillance_lic/src/mv_surveillance_lic.c new file mode 100644 index 0000000..95acb63 --- /dev/null +++ b/mv_surveillance/surveillance_lic/src/mv_surveillance_lic.c @@ -0,0 +1,63 @@ +/** + * Copyright (c) 2015 Samsung Electronics Co., Ltd All Rights Reserved + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#include "mv_surveillance_lic.h" + +int mv_surveillance_subscribe_event_trigger_lic( + mv_surveillance_event_trigger_h event_trigger, + mv_engine_config_h engine_cfg, + mv_surveillance_event_occurred_cb callback, + void *user_data) +{ + return MEDIA_VISION_ERROR_NOT_SUPPORTED; +} + + +int mv_surveillance_unsubscribe_event_trigger_lic( + mv_surveillance_event_trigger_h event_trigger) +{ + return MEDIA_VISION_ERROR_NOT_SUPPORTED; +} + +int mv_surveillance_push_source_lic( + mv_source_h source, + int video_stream_id) +{ + return MEDIA_VISION_ERROR_NOT_SUPPORTED; +} + +int mv_surveillance_foreach_event_type_lic( + mv_surveillance_event_type_cb callback, + void *user_data) +{ + return MEDIA_VISION_ERROR_NOT_SUPPORTED; +} + +int mv_surveillance_foreach_event_result_value_name_lic( + const char *event_type, + mv_surveillance_event_result_value_name_cb callback, + void *user_data) +{ + return MEDIA_VISION_ERROR_NOT_SUPPORTED; +} + +int mv_surveillance_get_result_value_lic( + mv_surveillance_result_h result, + const char *value_name, + void *value) +{ + return MEDIA_VISION_ERROR_NOT_SUPPORTED; +} diff --git a/packaging/capi-media-vision.spec b/packaging/capi-media-vision.spec index db59b53..d7eac4a 100644 --- a/packaging/capi-media-vision.spec +++ b/packaging/capi-media-vision.spec @@ -1,6 +1,6 @@ Name: capi-media-vision Summary: Media Vision library for Tizen Native API -Version: 0.2.5 +Version: 0.3.0 Release: 0 Group: Multimedia/Framework License: Apache-2.0 and BSD-2.0 @@ -49,6 +49,17 @@ export CFLAGS="$CFLAGS -DTIZEN_DEBUG_ENABLE" export CXXFLAGS="$CXXFLAGS -DTIZEN_DEBUG_ENABLE" export FFLAGS="$FFLAGS -DTIZEN_DEBUG_ENABLE" %endif + +%ifarch %{arm} +export CFLAGS="$CFLAGS -DENABLE_NEON" +export CXXFLAGS="$CXXFLAGS -DENABLE_NEON" +%endif + +%ifarch %{aarch64} +export CFLAGS="$CFLAGS -DENABLE_NEON" +export CXXFLAGS="$CXXFLAGS -DENABLE_NEON" +%endif + MAJORVER=`echo %{version} | awk 'BEGIN {FS="."}{print $1}'` %cmake . -DFULLVER=%{version} -DMAJORVER=${MAJORVER} diff --git a/src/mv_surveillance.c b/src/mv_surveillance.c new file mode 100644 index 0000000..48aa269 --- /dev/null +++ b/src/mv_surveillance.c @@ -0,0 +1,363 @@ +/** + * Copyright (c) 2015 Samsung Electronics Co., Ltd All Rights Reserved + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#include "mv_surveillance.h" + +#include "mv_surveillance_private.h" +#include "mv_private.h" + +#ifdef MEDIA_VISION_SURVEILLANCE_LICENSE_PORT + +/* Include headers of licensed surveillance module here. */ +#include "mv_surveillance_lic.h" + +#else + +/* Include headers of open surveillance module here. */ +#include "mv_surveillance_open.h" + +#endif /* MEDIA_VISION_SURVEILLANCE_LICENSE_PORT */ + +/** + * @file mv_surveillance.c + * @brief This file contains the porting layer for Media Vision surveillance module. + */ + +static size_t __mv_surveillance_id_counter = 0; + +int mv_surveillance_event_trigger_create( + const char *event_type, + mv_surveillance_event_trigger_h * trigger) +{ + MEDIA_VISION_SUPPORT_CHECK(__mv_face_check_system_info_feature_supported()); + MEDIA_VISION_SUPPORT_CHECK(__mv_image_check_system_info_feature_supported()); + MEDIA_VISION_NULL_ARG_CHECK(event_type); + MEDIA_VISION_NULL_ARG_CHECK(trigger); + MEDIA_VISION_FUNCTION_ENTER(); + + mv_surveillance_event_trigger_s *handle = + (mv_surveillance_event_trigger_s *) malloc( + sizeof(mv_surveillance_event_trigger_s)); + if (NULL == handle) { + LOGE("[%s] malloc fail", __func__); + return MEDIA_VISION_ERROR_OUT_OF_MEMORY; + } + + memset(handle, 0, sizeof(mv_surveillance_event_trigger_s)); + + // default values: + handle->trigger_id = ++__mv_surveillance_id_counter; + handle->event_type = strndup(event_type, 255); + handle->number_of_roi_points = 0; + handle->roi = NULL; + + *trigger = (mv_surveillance_event_trigger_h) handle; + + MEDIA_VISION_FUNCTION_LEAVE(); + return MEDIA_VISION_ERROR_NONE; +} + +int mv_surveillance_event_trigger_destroy( + mv_surveillance_event_trigger_h trigger) +{ + MEDIA_VISION_SUPPORT_CHECK(__mv_face_check_system_info_feature_supported()); + MEDIA_VISION_SUPPORT_CHECK(__mv_image_check_system_info_feature_supported()); + MEDIA_VISION_NULL_ARG_CHECK(trigger); + MEDIA_VISION_FUNCTION_ENTER(); + + mv_surveillance_event_trigger_s *handle = + (mv_surveillance_event_trigger_s *) trigger; + free(handle->event_type); + free(handle->roi); + free((mv_surveillance_event_trigger_s *) trigger); + + MEDIA_VISION_FUNCTION_LEAVE(); + return MEDIA_VISION_ERROR_NONE; +} + +int mv_surveillance_get_event_trigger_type( + mv_surveillance_event_trigger_h trigger, + char **event_type) +{ + MEDIA_VISION_SUPPORT_CHECK(__mv_face_check_system_info_feature_supported()); + MEDIA_VISION_SUPPORT_CHECK(__mv_image_check_system_info_feature_supported()); + MEDIA_VISION_INSTANCE_CHECK(trigger); + MEDIA_VISION_NULL_ARG_CHECK(event_type); + MEDIA_VISION_FUNCTION_ENTER(); + + mv_surveillance_event_trigger_s *handle = + (mv_surveillance_event_trigger_s *)trigger; + *event_type = strndup(handle->event_type, 255); + + MEDIA_VISION_FUNCTION_LEAVE(); + return MEDIA_VISION_ERROR_NONE; +} + +int mv_surveillance_set_event_trigger_roi( + mv_surveillance_event_trigger_h trigger, + int number_of_points, + mv_point_s *roi) +{ + MEDIA_VISION_SUPPORT_CHECK(__mv_face_check_system_info_feature_supported()); + MEDIA_VISION_SUPPORT_CHECK(__mv_image_check_system_info_feature_supported()); + MEDIA_VISION_INSTANCE_CHECK(trigger); + MEDIA_VISION_NULL_ARG_CHECK(roi); + MEDIA_VISION_FUNCTION_ENTER(); + + mv_surveillance_event_trigger_s *handle = + (mv_surveillance_event_trigger_s *)trigger; + + handle->number_of_roi_points = number_of_points; + handle->roi = (mv_point_s*) malloc(sizeof(mv_point_s) * number_of_points); + + if (NULL == handle->roi) { + LOGE("[%s] malloc fail", __func__); + return MEDIA_VISION_ERROR_OUT_OF_MEMORY; + } + + int i = 0; + for (; i < number_of_points; ++i) { + handle->roi[i].x = roi[i].x; + handle->roi[i].y = roi[i].y; + } + + MEDIA_VISION_FUNCTION_LEAVE(); + return MEDIA_VISION_ERROR_NONE; +} + +int mv_surveillance_get_event_trigger_roi( + mv_surveillance_event_trigger_h trigger, + int *number_of_points, + mv_point_s ** roi) +{ + MEDIA_VISION_SUPPORT_CHECK(__mv_face_check_system_info_feature_supported()); + MEDIA_VISION_SUPPORT_CHECK(__mv_image_check_system_info_feature_supported()); + MEDIA_VISION_INSTANCE_CHECK(trigger); + MEDIA_VISION_NULL_ARG_CHECK(number_of_points); + MEDIA_VISION_NULL_ARG_CHECK(roi); + MEDIA_VISION_FUNCTION_ENTER(); + + mv_surveillance_event_trigger_s *handle = + (mv_surveillance_event_trigger_s *) trigger; + + *number_of_points = handle->number_of_roi_points; + if (0 == *number_of_points) + { + MEDIA_VISION_FUNCTION_LEAVE(); + return MEDIA_VISION_ERROR_NONE; + } + *roi = (mv_point_s *) malloc( + sizeof(mv_point_s) * handle->number_of_roi_points); + + int i = 0; + for (; i < handle->number_of_roi_points; ++i) { + (*roi)[i].x = handle->roi[i].x; + (*roi)[i].y = handle->roi[i].y; + } + + MEDIA_VISION_FUNCTION_LEAVE(); + return MEDIA_VISION_ERROR_NONE; +} + +int mv_surveillance_subscribe_event_trigger( + mv_surveillance_event_trigger_h event_trigger, + int video_stream_id, + mv_engine_config_h engine_cfg, + mv_surveillance_event_occurred_cb callback, + void *user_data) +{ + MEDIA_VISION_SUPPORT_CHECK(__mv_face_check_system_info_feature_supported()); + MEDIA_VISION_SUPPORT_CHECK(__mv_image_check_system_info_feature_supported()); + MEDIA_VISION_INSTANCE_CHECK(event_trigger); + MEDIA_VISION_NULL_ARG_CHECK(callback); + MEDIA_VISION_FUNCTION_ENTER(); + +#ifdef MEDIA_VISION_SURVEILLANCE_LICENSE_PORT + + /* Use licensed surveillance functionality here. */ + const int ret = mv_surveillance_subscribe_event_trigger_lic( + event_trigger, + video_stream_id, + engine_cfg, + callback, + user_data); + +#else + + /* Use open surveillance functionality here. */ + const int ret = mv_surveillance_subscribe_event_trigger_open( + event_trigger, + video_stream_id, + engine_cfg, + callback, + user_data); + +#endif /* MEDIA_VISION_SURVEILLANCE_LICENSE_PORT */ + + MEDIA_VISION_FUNCTION_LEAVE(); + return ret; +} + +int mv_surveillance_unsubscribe_event_trigger( + mv_surveillance_event_trigger_h event_trigger, + int video_stream_id) +{ + MEDIA_VISION_SUPPORT_CHECK(__mv_face_check_system_info_feature_supported()); + MEDIA_VISION_SUPPORT_CHECK(__mv_image_check_system_info_feature_supported()); + MEDIA_VISION_INSTANCE_CHECK(event_trigger); + MEDIA_VISION_FUNCTION_ENTER(); + +#ifdef MEDIA_VISION_SURVEILLANCE_LICENSE_PORT + + /* Use licensed surveillance functionality here. */ + const int ret = mv_surveillance_unsubscribe_event_trigger_lic( + event_trigger, + video_stream_id); + +#else + + /* Use open surveillance functionality here. */ + const int ret = mv_surveillance_unsubscribe_event_trigger_open( + event_trigger, + video_stream_id); + +#endif /* MEDIA_VISION_SURVEILLANCE_LICENSE_PORT */ + + MEDIA_VISION_FUNCTION_LEAVE(); + return ret; +} + +int mv_surveillance_push_source( + mv_source_h source, + int video_stream_id) +{ + MEDIA_VISION_SUPPORT_CHECK(__mv_face_check_system_info_feature_supported()); + MEDIA_VISION_SUPPORT_CHECK(__mv_image_check_system_info_feature_supported()); + MEDIA_VISION_INSTANCE_CHECK(source); + MEDIA_VISION_FUNCTION_ENTER(); + +#ifdef MEDIA_VISION_SURVEILLANCE_LICENSE_PORT + + /* Use licensed surveillance functionality here. */ + const int ret = mv_surveillance_push_source_lic(source, video_stream_id); + +#else + + /* Use open surveillance functionality here. */ + const int ret = mv_surveillance_push_source_open(source, video_stream_id); + +#endif /* MEDIA_VISION_SURVEILLANCE_LICENSE_PORT */ + + MEDIA_VISION_FUNCTION_LEAVE(); + return ret; +} + +int mv_surveillance_foreach_supported_event_type( + mv_surveillance_event_type_cb callback, + void *user_data) +{ + MEDIA_VISION_SUPPORT_CHECK(__mv_face_check_system_info_feature_supported()); + MEDIA_VISION_SUPPORT_CHECK(__mv_image_check_system_info_feature_supported()); + MEDIA_VISION_NULL_ARG_CHECK(callback); + MEDIA_VISION_FUNCTION_ENTER(); + +#ifdef MEDIA_VISION_SURVEILLANCE_LICENSE_PORT + + /* Use licensed surveillance functionality here. */ + const int ret = mv_surveillance_foreach_event_type_lic( + callback, + user_data); + +#else + + /* Use open surveillance functionality here. */ + const int ret = mv_surveillance_foreach_event_type_open( + callback, + user_data); + +#endif /* MEDIA_VISION_SURVEILLANCE_LICENSE_PORT */ + + MEDIA_VISION_FUNCTION_LEAVE(); + return ret; +} + +int mv_surveillance_foreach_event_result_name( + const char *event_type, + mv_surveillance_event_result_name_cb callback, + void *user_data) +{ + MEDIA_VISION_SUPPORT_CHECK(__mv_face_check_system_info_feature_supported()); + MEDIA_VISION_SUPPORT_CHECK(__mv_image_check_system_info_feature_supported()); + MEDIA_VISION_NULL_ARG_CHECK(event_type); + MEDIA_VISION_NULL_ARG_CHECK(callback); + MEDIA_VISION_FUNCTION_ENTER(); + +#ifdef MEDIA_VISION_SURVEILLANCE_LICENSE_PORT + + /* Use licensed surveillance functionality here. */ + const int ret = mv_surveillance_foreach_event_result_value_name_lic( + event_type, + callback, + user_data); + +#else + + /* Use open surveillance functionality here. */ + const int ret = mv_surveillance_foreach_event_result_value_name_open( + event_type, + callback, + user_data); + +#endif /* MEDIA_VISION_SURVEILLANCE_LICENSE_PORT */ + + MEDIA_VISION_FUNCTION_LEAVE(); + + return ret; +} + +int mv_surveillance_get_result_value( + mv_surveillance_result_h result, + const char *value_name, + void *value) +{ + MEDIA_VISION_SUPPORT_CHECK(__mv_face_check_system_info_feature_supported()); + MEDIA_VISION_SUPPORT_CHECK(__mv_image_check_system_info_feature_supported()); + MEDIA_VISION_INSTANCE_CHECK(result); + MEDIA_VISION_NULL_ARG_CHECK(value_name); + MEDIA_VISION_NULL_ARG_CHECK(value); + MEDIA_VISION_FUNCTION_ENTER(); + +#ifdef MEDIA_VISION_SURVEILLANCE_LICENSE_PORT + + /* Use licensed surveillance functionality here. */ + const int ret = mv_surveillance_get_result_value_lic( + result, + value_name, + value); + +#else + + /* Use open surveillance functionality here. */ + const int ret = mv_surveillance_get_result_value_open( + result, + value_name, + value); + +#endif /* MEDIA_VISION_SURVEILLANCE_LICENSE_PORT */ + + MEDIA_VISION_FUNCTION_LEAVE(); + return ret; +} diff --git a/test/testsuites/CMakeLists.txt b/test/testsuites/CMakeLists.txt index 16a2eb6..fdb2d20 100644 --- a/test/testsuites/CMakeLists.txt +++ b/test/testsuites/CMakeLists.txt @@ -11,3 +11,4 @@ add_subdirectory(${PROJECT_SOURCE_DIR}/common) add_subdirectory(${PROJECT_SOURCE_DIR}/barcode) add_subdirectory(${PROJECT_SOURCE_DIR}/face) add_subdirectory(${PROJECT_SOURCE_DIR}/image) +add_subdirectory(${PROJECT_SOURCE_DIR}/surveillance) \ No newline at end of file diff --git a/test/testsuites/surveillance/CMakeLists.txt b/test/testsuites/surveillance/CMakeLists.txt new file mode 100644 index 0000000..80ac1ac --- /dev/null +++ b/test/testsuites/surveillance/CMakeLists.txt @@ -0,0 +1,31 @@ +project(mv_surveillance_test_suite) +cmake_minimum_required(VERSION 2.6) + +set_property(DIRECTORY APPEND PROPERTY COMPILE_DEFINITIONS_DEBUG _DEBUG) + +if(NOT SKIP_WARNINGS) + set(CMAKE_CXX_FLAGS "${CMAKE_C_FLAGS} -Wall -Wextra -Werror") +endif() + +set(CMAKE_ARCHIVE_OUTPUT_DIRECTORY ${CMAKE_BINARY_DIR}/${LIB_INSTALL_DIR}) +set(CMAKE_LIBRARY_OUTPUT_DIRECTORY ${CMAKE_BINARY_DIR}/${LIB_INSTALL_DIR}) +set(CMAKE_RUNTIME_OUTPUT_DIRECTORY ${CMAKE_BINARY_DIR}/bin) + +include_directories(${PROJECT_SOURCE_DIR}) +include_directories(${MV_CAPI_MEDIA_VISION_INC_DIR}) +include_directories(${INC_TS_COMMON}) +include_directories(${INC_VIDEO_HELPER}) +include_directories(${INC_IMAGE_HELPER}) + +file(GLOB MV_SURVEILLANCE_TS_INC_LIST "${PROJECT_SOURCE_DIR}/*.h") +file(GLOB MV_SURVEILLANCE_TS_SRC_LIST "${PROJECT_SOURCE_DIR}/*.c") + +add_executable(${PROJECT_NAME} ${MV_SURVEILLANCE_TS_SRC_LIST} + ${MV_SURVEILLANCE_TS_INC_LIST} + ${MV_CAPI_MEDIA_VISION_INC_LIST}) + +target_link_libraries(${PROJECT_NAME} capi-media-vision + mv_testsuite_common + mv_image_helper) + +install(TARGETS ${PROJECT_NAME} DESTINATION ${testsuites_dir}) diff --git a/test/testsuites/surveillance/surveillance_test_suite.c b/test/testsuites/surveillance/surveillance_test_suite.c new file mode 100644 index 0000000..6057121 --- /dev/null +++ b/test/testsuites/surveillance/surveillance_test_suite.c @@ -0,0 +1,1137 @@ +/** + * Copyright (c) 2015 Samsung Electronics Co., Ltd All Rights Reserved + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#include "mv_testsuite_common.h" +#include "image_helper.h" + +#include "mv_log_cfg.h" + +#include "mv_private.h" +#include "mv_surveillance.h" + +#include + +#define MAX_EVENTS_NUMBER 101 + +#define MAX_EVENT_TYPE_LEN 255 + +#define MIN_NUMBER_OF_ROI_POINTS 3 +#define MAX_NUMBER_OF_ROI_POINTS 100 + +#define MIN_ROI_X_COORD 0 +#define MAX_ROI_X_COORD 10000 +#define MIN_ROI_Y_COORD 0 +#define MAX_ROI_Y_COORD 10000 + +/*----------------------------------------------------*/ + +#define PRINT_R(MSG) printf(TEXT_RED MSG "\n" TEXT_RESET) +#define PRINT_Y(MSG) printf(TEXT_YELLOW MSG "\n" TEXT_RESET) +#define PRINT_G(MSG) printf(TEXT_GREEN MSG "\n" TEXT_RESET); + +#define PRINT_E(MSG, ERR) printf(TEXT_RED MSG "\n" TEXT_RESET, ERR) +#define PRINT_W(MSG, WARN) printf(TEXT_YELLOW MSG "\n" TEXT_RESET, WARN) +#define PRINT_S(MSG, RES) printf(TEXT_GREEN MSG "\n" TEXT_RESET, RES) + +/*----------------------------------------------------*/ +/* static */ +static mv_surveillance_event_trigger_h is_subscribed[MAX_EVENTS_NUMBER]; +static int video_streams_ids[MAX_EVENTS_NUMBER]; +static unsigned int trigger_id_cnt = 0; +static const int green_color[] = {0, 255, 0}; +static const int red_color[] = {0, 0, 255}; +static const int blue_color[] = {255, 0, 0}; +static bool save_results_to_image = false; +/*----------------------------------------------------*/ +/*functions*/ + +/* initializes is_subscribed by false */ +void init_is_subscribed(); + +/* prints identificators of subscribed events */ +void print_is_subscribed(); + +/* prints names of all available event types */ +void print_supported_events(); + +/* select event name from all available event types and creates trigger handle */ +int create_trigger_handle_by_event_name(mv_surveillance_event_trigger_h *handle); + +/* subscribes event */ +void subscribe_to_event(); + +/* adds ROI to event */ +void add_roi_to_event(mv_surveillance_event_trigger_h event_trigger); + +/* unsubscribes from event */ +void unsubscribe_from_event(); + +/* unsubscribes from all event */ +void unsubscribe_from_all_events(); + +/* pushes media source to event manager */ +void push_source(); + +/* fills engine configuration for person recognized event */ +bool fill_engine_cfg_person_recognized(mv_engine_config_h engine_cfg); + +/* Turn on (off) saving event results to image file */ +void turn_on_off_saving_to_image(); + +/*----------------------------------------------------*/ +/* callbacks */ + +void detect_person_appeared_cb( + mv_surveillance_event_trigger_h handle, + mv_source_h source, + int video_stream_id, + mv_surveillance_result_h event_result, + void *user_data); + +void person_recognized_cb( + mv_surveillance_event_trigger_h handle, + mv_source_h source, + int video_stream_id, + mv_surveillance_result_h event_result, + void *user_data); + +void movement_detected_cb( + mv_surveillance_event_trigger_h handle, + mv_source_h source, + int video_stream_id, + mv_surveillance_result_h event_result, + void *user_data); + +/*----------------------------------------------------*/ + +int main(void) +{ + LOGI("Surveillance Media Vision Testsuite is launched."); + + PRINT_W("Maximal number of events is %d", MAX_EVENTS_NUMBER - 1); + + init_is_subscribed(); + + const int options[8] = { 1, 2, 3, 4, 5, 6, 7, 8 }; + const char *names[8] = { + "Get list of supported events", + "Get identificators of subscribed events", + "Subscribe to event", + "Unsubscribe from event", + "Unsubscribe from all events", + "Push source", + "Turn on (off) saving event result to image", + "Exit" + }; + + while(1) { + char exit = 'n'; + int sel_opt = show_menu("Select action:", options, names, 8); + switch (sel_opt) { + case 1: /* Get list of supported events */ + print_supported_events(); + break; + case 2: /* Get identificators of subscribed events */ + print_is_subscribed(); + break; + case 3: /* Subscribe to event */ + subscribe_to_event(); + break; + case 4: /* Unsubscribe from event */ + unsubscribe_from_event(); + break; + case 5: /* Unsubscribe from all events */ + unsubscribe_from_all_events(); + break; + case 6: /* Push source */ + push_source(); + break; + case 7: /* Save event results to image */ + turn_on_off_saving_to_image(); + break; + case 8: /* Exit */ + exit = 'y'; + break; + default: + PRINT_R("Invalid option."); + sel_opt = 0; + continue; + } + + if ('y' == exit) { + sel_opt = 0; + const int options_last[2] = { 1, 2 }; + const char *names_last[2] = { "No", "Yes" }; + + while (sel_opt == 0) { + sel_opt = show_menu("Are you sure?", + options_last, names_last, 2); + switch (sel_opt) { + case 1: + exit = 'n'; + break; + case 2: + exit = 'y'; + break; + default: + PRINT_R("Invalid option. Back to the main menu."); + sel_opt = 0; + break; + } + } + + if ('y' == exit) { + unsubscribe_from_all_events(); + break; + } + } + } + + LOGI("Surveillance Media Vision Testsuite is closed"); + + return 0; +} + +void init_is_subscribed() +{ + int i = 0; + for (; i < MAX_EVENTS_NUMBER; ++i) { + is_subscribed[i] = NULL; + video_streams_ids[i] = -1; + } +} + +void print_is_subscribed() +{ + PRINT_Y("List of subscribed events identificators:"); + + bool is_empty = true; + + int i = 0; + for (; i < MAX_EVENTS_NUMBER; ++i) { + if (NULL != is_subscribed[i]) { + printf("%d ", i); + is_empty = false; + } + } + + if (is_empty) + PRINT_Y("List of subscribed events is empty"); +} + +static const char *EVENT_TYPES_NAMES[MAX_EVENT_TYPE_LEN] = { + MV_SURVEILLANCE_EVENT_TYPE_MOVEMENT_DETECTED, + MV_SURVEILLANCE_EVENT_TYPE_PERSON_APPEARED_DISAPPEARED, + MV_SURVEILLANCE_EVENT_TYPE_PERSON_RECOGNIZED +}; + +static const unsigned int NUMBER_OF_TYPES = 3u; + +bool foreach_event_result_value_name_cb(const char *value_name, void *user_data) +{ + if (NULL == value_name) { + PRINT_R("\tError occurred. Value name is NULL"); + return true; + } + PRINT_W("%s", value_name); + return true; +} + +bool foreach_event_type_cb(const char *event_type, void *user_data) +{ + if (NULL == event_type) { + PRINT_R("Error occurred. Event type name is NULL"); + return true; + } + + PRINT_W("%s", event_type); + PRINT_G("\tList of supported event result value names is:"); + + const int error = mv_surveillance_foreach_event_result_name( + event_type, foreach_event_result_value_name_cb, user_data); + if (MEDIA_VISION_ERROR_NONE != error) { + PRINT_E("Error occurred when trying to get value names for " + "event named '%s'", event_type); + return true; + } + return true; +} + +void print_supported_events() +{ + PRINT_G("List of supported events is:"); + + const int error = mv_surveillance_foreach_supported_event_type( + foreach_event_type_cb, NULL); + + if (MEDIA_VISION_ERROR_NONE != error) + PRINT_R("Error occurred when trying to get list of event type names \n"); +} + +int create_trigger_handle_by_event_name( + mv_surveillance_event_trigger_h *handle) +{ + PRINT_G("\nSelect event type:"); + + unsigned int i = 0u; + for (; i < NUMBER_OF_TYPES; ++i) + printf("#%d. %s\n", i, EVENT_TYPES_NAMES[i]); + + unsigned int event_id = 0u; + while (input_size("Input event type (unsigned integer value):", + NUMBER_OF_TYPES - 1, &event_id) == -1) { + PRINT_R("Incorrect input! Try again.\n List of supported events is:"); + + unsigned int i = 0u; + for (; i < NUMBER_OF_TYPES; ++i) + printf("%d\t%s\n", i, EVENT_TYPES_NAMES[i]); + } + + const int error = mv_surveillance_event_trigger_create( + EVENT_TYPES_NAMES[event_id], handle); + if (MEDIA_VISION_ERROR_NONE != error) { + PRINT_E("mv_surveillance_event_trigger_create() error!\n" + "Error code: %i\n", error); + return error; + } + + return MEDIA_VISION_ERROR_NONE; +} + +bool try_destroy_event_trigger(mv_surveillance_event_trigger_h trigger) +{ + const int error = mv_surveillance_event_trigger_destroy(trigger); + if (MEDIA_VISION_ERROR_NONE != error) { + PRINT_E("Error with code %d was occured when try to destroy " + "event trigger.", error); + return false; + } + return true; +} + +void subscribe_to_event() +{ + if (++trigger_id_cnt >= MAX_EVENTS_NUMBER) { + PRINT_R("Maximal value of event trigger id is reached. " + "Subscription impossible"); + return; + } + + mv_surveillance_event_trigger_h event_trigger = NULL; + int error = create_trigger_handle_by_event_name(&event_trigger); + if (MEDIA_VISION_ERROR_NONE != error) { + PRINT_E("Error occurred when creating of event trigger. " + "Error code: %i", error); + try_destroy_event_trigger(event_trigger); + return; + } + + int video_stream_id = 0; + + while (input_int("Input video stream identificator (integer value):", + INT_MIN, + INT_MAX, + &video_stream_id) == -1) + PRINT_R("Incorrect input! Try again."); + + char *event_type = NULL; + error = mv_surveillance_get_event_trigger_type(event_trigger, &event_type); + if (MEDIA_VISION_ERROR_NONE != error) { + PRINT_E("Error occurred when getting of event trigger type. " + "Error code: %i", error); + try_destroy_event_trigger(event_trigger); + return; + } + + if (show_confirm_dialog("Would you like to set ROI (Region Of Interest)?")) + add_roi_to_event(event_trigger); + + if (strncmp(event_type, + MV_SURVEILLANCE_EVENT_TYPE_PERSON_APPEARED_DISAPPEARED, + MAX_EVENT_TYPE_LEN) == 0) { + error = mv_surveillance_subscribe_event_trigger( + event_trigger, + video_stream_id, + NULL, + detect_person_appeared_cb, + NULL); + } else if (strncmp(event_type, + MV_SURVEILLANCE_EVENT_TYPE_PERSON_RECOGNIZED, + MAX_EVENT_TYPE_LEN) == 0) { + PRINT_Y("Please create and save face recognition models\n" + "before subscribing to event. Use mv_face_test_suite."); + + mv_engine_config_h engine_cfg = NULL; + error = mv_create_engine_config(&engine_cfg); + + if (error != MEDIA_VISION_ERROR_NONE) { + PRINT_R("Failed to create engine configuration for event trigger."); + try_destroy_event_trigger(event_trigger); + free(event_type); + return; + } + + const bool is_filled = fill_engine_cfg_person_recognized(engine_cfg); + + if (!is_filled) { + PRINT_R("Failed to fill engine configuration for event trigger."); + try_destroy_event_trigger(event_trigger); + if (mv_destroy_engine_config(engine_cfg) != MEDIA_VISION_ERROR_NONE) + PRINT_E("Failed to destroy engine configuration for event trigger.", + error); + free(event_type); + return; + } + + error = mv_surveillance_subscribe_event_trigger( + event_trigger, + video_stream_id, + engine_cfg, + person_recognized_cb, + NULL); + + if (error != MEDIA_VISION_ERROR_NONE) { + PRINT_E("Subscription failed. Error code: %i.", error); + try_destroy_event_trigger(event_trigger); + if (mv_destroy_engine_config(engine_cfg) != MEDIA_VISION_ERROR_NONE) + PRINT_E("Failed to destroy engine configuration for event trigger.", + error); + free(event_type); + return; + } + + if (mv_destroy_engine_config(engine_cfg) != MEDIA_VISION_ERROR_NONE) + PRINT_E("Failed to destroy engine configuration for event trigger.", + error); + } else if (strncmp(event_type, + MV_SURVEILLANCE_EVENT_TYPE_MOVEMENT_DETECTED, + MAX_EVENT_TYPE_LEN) == 0) { + error = mv_surveillance_subscribe_event_trigger( + event_trigger, + video_stream_id, + NULL, + movement_detected_cb, + NULL); + } + + free(event_type); + + if (MEDIA_VISION_ERROR_NONE != error) { + PRINT_E("Error with code %d was occurred in subscribe event.", error); + try_destroy_event_trigger(event_trigger); + return; + } + + is_subscribed[trigger_id_cnt] = event_trigger; + video_streams_ids[trigger_id_cnt] = video_stream_id; + PRINT_S("Event trigger %i has been successfully subscribed", trigger_id_cnt); +} + +void add_roi_to_event(mv_surveillance_event_trigger_h event_trigger) +{ + int number_of_roi_points = 0; + while (input_int("Input number of ROI points (integer value >2):", + MIN_NUMBER_OF_ROI_POINTS, + MAX_NUMBER_OF_ROI_POINTS, + &number_of_roi_points) == -1) + PRINT_R("Incorrect input! Try again."); + + mv_point_s* roi = (mv_point_s*) malloc(sizeof(mv_point_s) * number_of_roi_points); + + int x = 0; + int y = 0; + + int i = 0; + for (; i < number_of_roi_points; ++i) { + printf("Point %d \n", i + 1); + + while (input_int("Input x (integer value):", + MIN_ROI_X_COORD, + MAX_ROI_X_COORD, + &x) == -1) + PRINT_R("Incorrect input! Try again."); + + while (input_int("Input y (integer value):", + MIN_ROI_Y_COORD, + MAX_ROI_Y_COORD, + &y) == -1) + PRINT_R("Incorrect input! Try again."); + + roi[i].x = x; + roi[i].y = y; + } + + const int error = mv_surveillance_set_event_trigger_roi( + event_trigger, + number_of_roi_points, + roi); + + if (error == MEDIA_VISION_ERROR_NONE) + PRINT_G("ROI was successfully set") + else + PRINT_R("Setting ROI failed. Please try again") ; + + if (roi != NULL) + free(roi); +} + +void unsubscribe_from_event() +{ + int trigger_id = 0; + while (input_int("Input event identificator (1-100):", + 1, + MAX_EVENTS_NUMBER - 1, + &trigger_id) == -1) + PRINT_R("Incorrect input! Try again."); + + mv_surveillance_event_trigger_h event_trigger = is_subscribed[trigger_id]; + if (NULL == event_trigger) { + PRINT_E("Sorry, event trigger with %i identifier wasn't subscribed.", + trigger_id); + return; + } + + const int error = mv_surveillance_unsubscribe_event_trigger( + event_trigger, + video_streams_ids[trigger_id]); + if (error != MEDIA_VISION_ERROR_NONE) { + PRINT_E("Error with code %d was occured in unsubscribe event.", error); + return; + } + + try_destroy_event_trigger(event_trigger); + is_subscribed[trigger_id] = NULL; + video_streams_ids[trigger_id] = -1; + PRINT_S("Event with id %d was successfully unsubscribed", trigger_id); +} + +void unsubscribe_from_all_events() +{ + int error = MEDIA_VISION_ERROR_NONE; + unsigned int trigger_id = 0; + int unsubscribed_number = 0; + for (; trigger_id < MAX_EVENTS_NUMBER; ++trigger_id) { + mv_surveillance_event_trigger_h event_trigger = + is_subscribed[trigger_id]; + if (NULL != event_trigger) { + error = mv_surveillance_unsubscribe_event_trigger( + event_trigger, + video_streams_ids[trigger_id]); + if (error != MEDIA_VISION_ERROR_NONE) { + PRINT_E("Error with code %d was occurred in unsubscribe event.", + error); + continue; + } + ++unsubscribed_number; + + PRINT_S("Event with id %d was successfully unsubscribed", trigger_id); + + try_destroy_event_trigger(event_trigger); + is_subscribed[trigger_id] = NULL; + video_streams_ids[trigger_id] = -1; + } + } + + unsubscribed_number > 0 ? + PRINT_S("%d event(s) was successfully unsubscribed", unsubscribed_number): + PRINT_Y("\nThere are no triggers can be unsubscribed."); +} + +void push_source() +{ + mv_source_h source; + int error = mv_create_source(&source); + if (MEDIA_VISION_ERROR_NONE != error) { + PRINT_E("ERROR: Errors were occurred during source creating!!! Code %i" , + error); + return; + } + + char *path_to_image = NULL; + + while (input_string("Input file name with image to be analyzed:", + 1024, &path_to_image) == -1) + PRINT_R("Incorrect input! Try again."); + + error = load_mv_source_from_file(path_to_image, source); + if (MEDIA_VISION_ERROR_NONE != error) { + PRINT_E("Errors were occurred during source loading, code %i", error); + return; + } + + if (path_to_image != NULL) + free(path_to_image); + + int video_stream_id = 0; + + while (input_int("Input video stream identificator (integer value):", + INT_MIN, + INT_MAX, + &video_stream_id) == -1) + PRINT_R("Incorrect input! Try again."); + + error = mv_surveillance_push_source(source, video_stream_id); + if (MEDIA_VISION_ERROR_NONE != error) { + PRINT_E("Errors were occurred during source pushing, code %i", error); + return; + } + + error = mv_destroy_source(source); + if (MEDIA_VISION_ERROR_NONE != error) { + PRINT_E("Errors were occurred during source destroying, code %i", error); + return; + } + + PRINT_G("Media source was successfully pushed"); +} + +bool fill_engine_cfg_person_recognized(mv_engine_config_h engine_cfg) +{ + char *path_to_model = NULL; + + while (input_string("Input file name with face recognition model:", + 1024, &path_to_model) == -1) + PRINT_R("Incorrect input! Try again."); + + const int error = mv_engine_config_set_string_attribute( + engine_cfg, + MV_SURVEILLANCE_FACE_RECOGNITION_MODEL_FILE_PATH, + path_to_model); + + if (error != MEDIA_VISION_ERROR_NONE) + PRINT_E("Setting path to face recognition model failed, code %i", + error); + else + printf("\nModel path is %s \n", path_to_model); + + if (path_to_model != NULL) + free(path_to_model); + + return true; +} + +void turn_on_off_saving_to_image() +{ + save_results_to_image = !save_results_to_image; + + save_results_to_image ? + PRINT_Y("Save event results to image files ON."): + PRINT_Y("Save event results to image files OFF."); +} + +void detect_person_appeared_cb( + mv_surveillance_event_trigger_h handle, + mv_source_h source, + int video_stream_id, + mv_surveillance_result_h event_result, + void *user_data) +{ + PRINT_G("Person appeared / disappeared event was occured"); + if (save_results_to_image) + PRINT_G("Output image will be saved to /tmp/person_app.jpg.\n" + "Appeared locations - green;\n" + "Tracked locations - blue;\n" + "Disappeared locations - red."); + + unsigned char *out_buffer = NULL; + unsigned int buf_size = 0; + image_data_s image_data = { 0, 0, MEDIA_VISION_COLORSPACE_INVALID }; + + if (save_results_to_image && + (mv_source_get_buffer(source, &out_buffer, &buf_size) || + mv_source_get_width(source, &(image_data.image_width)) || + mv_source_get_height(source, &(image_data.image_height)) || + mv_source_get_colorspace(source, &(image_data.image_colorspace)) || + out_buffer == NULL || + buf_size == 0)) + { + PRINT_R("ERROR: Creating out image is impossible."); + + return; + } + + unsigned char *out_buffer_copy = NULL; + if (save_results_to_image) { + out_buffer_copy = (unsigned char *) malloc(buf_size); + memcpy(out_buffer_copy, out_buffer, buf_size); + } + + int number_of_appeared_persons = 0; + int error = mv_surveillance_get_result_value( + event_result, + MV_SURVEILLANCE_PERSONS_APPEARED_NUMBER, + &number_of_appeared_persons); + + if (error != MEDIA_VISION_ERROR_NONE) { + PRINT_E("Error with code %d was occured in getting number of " + "appeared persons.", error); + if (out_buffer_copy != NULL) + free(out_buffer_copy); + + return; + } + + printf("\nNumber of appeared persons is %d \n", number_of_appeared_persons); + + mv_rectangle_s *appeared_locations = + malloc(sizeof(mv_rectangle_s) * number_of_appeared_persons); + + error = mv_surveillance_get_result_value( + event_result, + MV_SURVEILLANCE_PERSONS_APPEARED_LOCATIONS, + appeared_locations); + + if (error != MEDIA_VISION_ERROR_NONE) { + PRINT_E("Error with code %d was occured in getting locations of " + "appeared persons.", error); + + if (appeared_locations != NULL) + free(appeared_locations); + + if (out_buffer_copy != NULL) + free(out_buffer_copy); + + return; + } + + int i = 0; + for (; i < number_of_appeared_persons; ++i) { + printf("Person #%d location is: x - %d, y - %d, w - %d, h - %d.\n", + i, + appeared_locations[i].point.x, + appeared_locations[i].point.y, + appeared_locations[i].width, + appeared_locations[i].height); + + if (save_results_to_image) + draw_rectangle_on_buffer( + appeared_locations[i].point.x, + appeared_locations[i].point.y, + appeared_locations[i].point.x + appeared_locations[i].width, + appeared_locations[i].point.y + appeared_locations[i].height, + 3, + green_color, + &image_data, + out_buffer_copy); + } + + int number_of_tracked_persons = 0; + error = mv_surveillance_get_result_value( + event_result, + MV_SURVEILLANCE_PERSONS_TRACKED_NUMBER, + &number_of_tracked_persons); + + if (error != MEDIA_VISION_ERROR_NONE) { + PRINT_E("Error with code %d was occured in getting number of " + "tracked persons.", error); + + if (appeared_locations != NULL) + free(appeared_locations); + + if (out_buffer_copy != NULL) + free(out_buffer_copy); + + return; + } + + printf("\nNumber of tracked persons is %d \n", number_of_tracked_persons); + + mv_rectangle_s *tracked_locations = + malloc(sizeof(mv_rectangle_s) * number_of_tracked_persons); + + error = mv_surveillance_get_result_value( + event_result, + MV_SURVEILLANCE_PERSONS_TRACKED_LOCATIONS, + tracked_locations); + + if (error != MEDIA_VISION_ERROR_NONE) { + PRINT_E("Error with code %d was occured in getting locations of " + "tracked persons.", error); + + if (appeared_locations != NULL) + free(appeared_locations); + + if (tracked_locations != NULL) + free(tracked_locations); + + if (out_buffer_copy != NULL) + free(out_buffer_copy); + + return; + } + + for (i = 0; i < number_of_tracked_persons; ++i) { + printf("Person #%d location is: x - %d, y - %d, w - %d, h - %d.\n", + i, + tracked_locations[i].point.x, + tracked_locations[i].point.y, + tracked_locations[i].width, + tracked_locations[i].height); + + if (save_results_to_image) + draw_rectangle_on_buffer( + tracked_locations[i].point.x, + tracked_locations[i].point.y, + tracked_locations[i].point.x + tracked_locations[i].width, + tracked_locations[i].point.y + tracked_locations[i].height, + 3, + blue_color, + &image_data, + out_buffer_copy); + } + + int number_of_disappeared_persons = 0; + error = mv_surveillance_get_result_value( + event_result, + MV_SURVEILLANCE_PERSONS_DISAPPEARED_NUMBER, + &number_of_disappeared_persons); + + if (error != MEDIA_VISION_ERROR_NONE) { + PRINT_E("Error with code %d was occured in getting number of " + "disappeared persons.", error); + + if (appeared_locations != NULL) + free(appeared_locations); + + if (tracked_locations != NULL) + free(tracked_locations); + + if (out_buffer_copy != NULL) + free(out_buffer_copy); + + return; + } + + printf("\nNumber of disappeared persons is %d \n", number_of_disappeared_persons); + + mv_rectangle_s *disappeared_locations = + malloc(sizeof(mv_rectangle_s) * number_of_disappeared_persons); + + error = mv_surveillance_get_result_value( + event_result, + MV_SURVEILLANCE_PERSONS_DISAPPEARED_LOCATIONS, + disappeared_locations); + + if (error != MEDIA_VISION_ERROR_NONE) { + PRINT_E("Error with code %d was occured in getting locations of " + "disappeared persons.", error); + + if (appeared_locations != NULL) + free(appeared_locations); + + if (tracked_locations != NULL) + free(tracked_locations); + + if (disappeared_locations != NULL) + free(disappeared_locations); + + if (out_buffer_copy != NULL) + free(out_buffer_copy); + + return; + } + + for (i = 0; i < number_of_disappeared_persons; ++i) { + printf("Person #%d location is: x - %d, y - %d, w - %d, h - %d.\n", + i, + disappeared_locations[i].point.x, + disappeared_locations[i].point.y, + disappeared_locations[i].width, + disappeared_locations[i].height); + + if (save_results_to_image) + draw_rectangle_on_buffer( + disappeared_locations[i].point.x, + disappeared_locations[i].point.y, + disappeared_locations[i].point.x + disappeared_locations[i].width, + disappeared_locations[i].point.y + disappeared_locations[i].height, + 3, + red_color, + &image_data, + out_buffer_copy); + } + + if (save_results_to_image) + save_image_from_buffer("/tmp/person_app.jpg", out_buffer_copy, &image_data, 100); + + printf("\n"); + + if (appeared_locations != NULL) + free(appeared_locations); + + if (tracked_locations != NULL) + free(tracked_locations); + + if (disappeared_locations != NULL) + free(disappeared_locations); + + if (out_buffer_copy != NULL) + free(out_buffer_copy); +} + +void person_recognized_cb( + mv_surveillance_event_trigger_h handle, + mv_source_h source, + int video_stream_id, + mv_surveillance_result_h event_result, + void *user_data) +{ + PRINT_G("Person recognized event was occurred"); + if (save_results_to_image) + PRINT_G("Output image will be saved to /tmp/person_rec.jpg.\n" + "Person recognized locations - red."); + + int number_of_persons = 0; + int error = mv_surveillance_get_result_value( + event_result, + MV_SURVEILLANCE_PERSONS_RECOGNIZED_NUMBER, + &number_of_persons); + + if (error != MEDIA_VISION_ERROR_NONE) { + PRINT_E("Error with code %d was occured in getting number of persons.", + error); + return; + } + + printf("\nNumber of persons is %d \n\n", number_of_persons); + + mv_rectangle_s *locations = malloc(sizeof(mv_rectangle_s) * number_of_persons); + + error = mv_surveillance_get_result_value( + event_result, + MV_SURVEILLANCE_PERSONS_RECOGNIZED_LOCATIONS, + locations); + + if (error != MEDIA_VISION_ERROR_NONE) { + PRINT_E("Error with code %d was occured in getting locations of persons.", + error); + + if (locations != NULL) + free(locations); + + return; + } + + int *labels = malloc(sizeof(int) * number_of_persons); + + error = mv_surveillance_get_result_value( + event_result, + MV_SURVEILLANCE_PERSONS_RECOGNIZED_LABELS, + labels); + + if (error != MEDIA_VISION_ERROR_NONE) { + PRINT_E("Error with code %d was occured in getting labels of persons.", + error); + + if (locations != NULL) + free(locations); + + if (labels != NULL) + free(labels); + + return; + } + + double *confidences = malloc(sizeof(double) * number_of_persons); + + error = mv_surveillance_get_result_value( + event_result, + MV_SURVEILLANCE_PERSONS_RECOGNIZED_CONFIDENCES, + confidences); + + if (error != MEDIA_VISION_ERROR_NONE) { + PRINT_E("Error with code %d was occured in getting confidences of persons.", + error); + + if (locations != NULL) + free(locations); + + if (labels != NULL) + free(labels); + + if (confidences != NULL) + free(confidences); + + return; + } + + unsigned char *out_buffer = NULL; + unsigned int buf_size = 0; + image_data_s image_data = { 0, 0, MEDIA_VISION_COLORSPACE_INVALID }; + + if (save_results_to_image && + (mv_source_get_buffer(source, &out_buffer, &buf_size) || + mv_source_get_width(source, &(image_data.image_width)) || + mv_source_get_height(source, &(image_data.image_height)) || + mv_source_get_colorspace(source, &(image_data.image_colorspace)) || + out_buffer == NULL || + buf_size == 0)) + { + PRINT_R("ERROR: Creating out image is impossible."); + + return; + } + + unsigned char *out_buffer_copy = NULL; + if (save_results_to_image) { + out_buffer_copy = (unsigned char *) malloc(buf_size); + memcpy(out_buffer_copy, out_buffer, buf_size); + } + + int i = 0; + for (; i < number_of_persons; ++i) { + printf("Person %d:\n", labels[i]); + printf("Location is: x - %d, y - %d, w - %d, h - %d.\n", + locations[i].point.x, + locations[i].point.y, + locations[i].width, + locations[i].height); + printf("Model confidence - %3.2f", confidences[i]); + printf("\n"); + + if (save_results_to_image) + draw_rectangle_on_buffer( + locations[i].point.x, + locations[i].point.y, + locations[i].point.x + locations[i].width, + locations[i].point.y + locations[i].height, + 3, + red_color, + &image_data, + out_buffer_copy); + } + + printf("\n"); + + if (save_results_to_image) + save_image_from_buffer("/tmp/person_rec.jpg", out_buffer_copy, &image_data, 100); + + if (locations != NULL) + free(locations); + + if (labels != NULL) + free(labels); + + if (confidences != NULL) + free(confidences); + + if (out_buffer_copy != NULL) + free(out_buffer_copy); +} + +void movement_detected_cb( + mv_surveillance_event_trigger_h event_trigger, + mv_source_h source, + int video_stream_id, + mv_surveillance_result_h event_result, + void *user_data) +{ + PRINT_G("Movement detected event was occured"); + if (save_results_to_image) + PRINT_G("Output image will be saved to /tmp/move_detect.jpg.\n" + "Movement detected locations - blue."); + + int number_of_movement_regions = 0; + int error = mv_surveillance_get_result_value( + event_result, + MV_SURVEILLANCE_MOVEMENT_NUMBER_OF_REGIONS, + &number_of_movement_regions); + + if (error != MEDIA_VISION_ERROR_NONE) { + PRINT_E("Error with code %d was occured in getting number of " + "movement regions.", error); + + return; + } + + printf("\nNumber of movement regions is %d \n", number_of_movement_regions); + + mv_rectangle_s *movement_regions = + malloc(sizeof(mv_rectangle_s) * number_of_movement_regions); + + error = mv_surveillance_get_result_value( + event_result, + MV_SURVEILLANCE_MOVEMENT_REGIONS, + movement_regions); + + if (error != MEDIA_VISION_ERROR_NONE) { + PRINT_E("Error with code %d was occured in getting movement regions.", + error); + + if (movement_regions != NULL) + free(movement_regions); + + return; + } + + unsigned char *out_buffer = NULL; + unsigned int buf_size = 0; + image_data_s image_data = { 0, 0, MEDIA_VISION_COLORSPACE_INVALID }; + + if (save_results_to_image && + (mv_source_get_buffer(source, &out_buffer, &buf_size) || + mv_source_get_width(source, &(image_data.image_width)) || + mv_source_get_height(source, &(image_data.image_height)) || + mv_source_get_colorspace(source, &(image_data.image_colorspace)) || + out_buffer == NULL || + buf_size == 0)) + { + PRINT_R("ERROR: Creating out image is impossible."); + + if (movement_regions != NULL) + free(movement_regions); + + return; + } + + unsigned char *out_buffer_copy = NULL; + if (save_results_to_image) { + out_buffer_copy = (unsigned char *) malloc(buf_size); + memcpy(out_buffer_copy, out_buffer, buf_size); + } + + int i = 0; + for (; i < number_of_movement_regions; ++i) { + printf("Movement #%d region is: x - %d, y - %d, w - %d, h - %d.\n", + i, + movement_regions[i].point.x, + movement_regions[i].point.y, + movement_regions[i].width, + movement_regions[i].height); + + if (save_results_to_image) + draw_rectangle_on_buffer( + movement_regions[i].point.x, + movement_regions[i].point.y, + movement_regions[i].point.x + movement_regions[i].width, + movement_regions[i].point.y + movement_regions[i].height, + 3, + blue_color, + &image_data, + out_buffer_copy); + } + + printf("\n"); + + if (save_results_to_image) + save_image_from_buffer("/tmp/move_detect.jpg", out_buffer_copy, &image_data, 100); + + if (movement_regions != NULL) + free(movement_regions); + + if (out_buffer_copy != NULL) + free(out_buffer_copy); +}