From fce8b9d4a8cd79ac755641bb63a62213dc0344bd Mon Sep 17 00:00:00 2001 From: MrDave Date: Sat, 19 Oct 2019 19:32:04 -0600 Subject: [PATCH] Add secondary detection --- configure.ac | 71 ++++-- src/Makefile.am | 2 +- src/alg_sec.cpp | 569 ++++++++++++++++++++++++++++++++++++++++++++ src/alg_sec.hpp | 57 +++++ src/conf.cpp | 45 +++- src/conf.hpp | 10 + src/conf_edit.cpp | 156 ++++++++++++ src/motion.hpp | 2 + src/motion_loop.cpp | 6 + 9 files changed, 899 insertions(+), 19 deletions(-) create mode 100644 src/alg_sec.cpp create mode 100644 src/alg_sec.hpp diff --git a/configure.ac b/configure.ac index 4b1d3957..6e1c0d83 100644 --- a/configure.ac +++ b/configure.ac @@ -250,6 +250,45 @@ AS_IF([test "${FFMPEG}" = "no"], [ export PKG_CONFIG_PATH ] ) +############################################################################## +### OpenCV support +############################################################################## +OPENCV_VER="--" +AC_ARG_WITH([opencv], + AS_HELP_STRING([--with-opencv[=DIR]],[Build with OpenCV support]), + [OPENCV=$withval], + [OPENCV="yes"] +) + +AS_IF([test "${OPENCV}" = "no"], [ + AC_MSG_CHECKING(for OpenCV support) + AC_MSG_RESULT(skipped) + ],[ + TEMP_PATH=$PKG_CONFIG_PATH + AC_MSG_CHECKING(OpenCV pkg-config path) + AS_IF([test "${OPENCV}" != "yes"], [ + PKG_CONFIG_PATH=${OPENCV}/lib/pkgconfig:$PKG_CONFIG_PATH + OPENCV="yes" + ] + ) + export PKG_CONFIG_PATH + AC_MSG_RESULT($PKG_CONFIG_PATH) + + AC_MSG_CHECKING(for OpenCV) + AS_IF([pkg-config opencv], [ + TEMP_CPPFLAGS="$TEMP_CPPFLAGS "`pkg-config --cflags opencv` + TEMP_LIBS="$TEMP_LIBS "`pkg-config --libs opencv` + OPENCV_VER=`pkg-config --modversion opencv` + AC_DEFINE([HAVE_OPENCV], [1], [Define to 1 if OpenCV is around]) + ],[ + OPENCV="no" + ] + ) + AC_MSG_RESULT([$OPENCV]) + PKG_CONFIG_PATH=$TEMP_PATH + export PKG_CONFIG_PATH + ] +) ############################################################################## ### Check for MariaDB - Optional @@ -421,7 +460,7 @@ AC_ARG_WITH([developer-flags], [DEVELOPER_FLAGS=no]) AS_IF([test "${DEVELOPER_FLAGS}" = "yes"], [ - TEMP_CPPFLAGS="$TEMP_CPPFLAGS -W -Werror -Wall -Wextra -Wformat -Wshadow -Wpointer-arith -Wwrite-strings -Waggregate-return -Winline -Wredundant-decls -Wno-long-long -ggdb -g3" + TEMP_CPPFLAGS="$TEMP_CPPFLAGS -W -Werror -Wall -Wextra -Wformat -Wshadow -Wpointer-arith -Wwrite-strings -Winline -Wredundant-decls -Wno-long-long -ggdb -g3" ] ) @@ -463,20 +502,22 @@ echo echo "LDFLAGS: $LDFLAGS" echo echo -echo "OS : $host_os" -echo "pthread_np : $PTHREAD_NP" -echo "pthread_setname_np : $PTHREAD_SETNAME_NP" -echo "pthread_getname_np : $PTHREAD_GETNAME_NP" -echo "XSI error : $XSI_STRERROR" -echo "webp support : $WEBP" -echo "V4L2 support : $V4L2" -echo "MMAL support : $MMAL" -echo "FFmpeg support : $FFMPEG" -echo "libavformat version : $FFMPEG_VER" -echo "SQLite3 support : $SQLITE3" -echo "MYSQL support : $MYSQL" -echo "PostgreSQL support : $PGSQL" -echo "MariaDB support : $MARIADB" +echo "OS : $host_os" +echo "pthread_np : $PTHREAD_NP" +echo "pthread_setname_np : $PTHREAD_SETNAME_NP" +echo "pthread_getname_np : $PTHREAD_GETNAME_NP" +echo "XSI error : $XSI_STRERROR" +echo "webp support : $WEBP" +echo "V4L2 support : $V4L2" +echo "MMAL support : $MMAL" +echo "FFmpeg support : $FFMPEG" +echo " libavformat version : $FFMPEG_VER" +echo "OpenCV : $OPENCV" +echo " version : $OPENCV_VER" +echo "SQLite3 support : $SQLITE3" +echo "MYSQL support : $MYSQL" +echo "PostgreSQL support : $PGSQL" +echo "MariaDB support : $MARIADB" echo echo "Install prefix: $prefix" echo diff --git a/src/Makefile.am b/src/Makefile.am index 5d7a2a7b..1efc764d 100644 --- a/src/Makefile.am +++ b/src/Makefile.am @@ -8,7 +8,7 @@ LIBS = @LIBINTL@ @LIBS@ bin_PROGRAMS = motion -motion_SOURCES = motion.cpp motion_loop.cpp logger.cpp conf.cpp conf_edit.cpp util.cpp alg.cpp \ +motion_SOURCES = motion.cpp motion_loop.cpp logger.cpp conf.cpp conf_edit.cpp util.cpp alg.cpp alg_sec.cpp\ video_v4l2.cpp video_common.cpp video_loopback.cpp netcam.cpp jpegutils.cpp exif.cpp \ rotate.cpp draw.cpp event.cpp movie.cpp picture.cpp dbse.cpp track.cpp \ webu.cpp webu_html.cpp webu_stream.cpp webu_text.cpp mmalcam.cpp $(MMAL_SRC) diff --git a/src/alg_sec.cpp b/src/alg_sec.cpp new file mode 100644 index 00000000..009067ab --- /dev/null +++ b/src/alg_sec.cpp @@ -0,0 +1,569 @@ +/* alg_sec.cpp + * + * Algorithms for Secondary Detection + * Detect changes in a video stream using alternative methods. + * This software is distributed under the GNU public license version 2 + * See also the file 'COPYING'. + * + */ +#include "motion.hpp" +#include "util.hpp" +#include "logger.hpp" +#include "alg_sec.hpp" +#include +#include +#include +#include + +#ifdef HAVE_OPENCV + +#include "opencv2/objdetect.hpp" +#include "opencv2/highgui.hpp" +#include "opencv2/imgproc.hpp" + +using namespace cv; + +static void algsec_img_show(ctx_cam *cam, Mat &mat_src + , std::vector &src_pos, std::vector &src_weights + , std::string algmethod, float min_weight ){ + + std::vector fltr_pos; + std::vector fltr_weights; + std::string testdir; + std::size_t indx0, indx1; + bool isdetected; + char wstr[10]; + + testdir = cam->conf.target_dir; + + imwrite(testdir + "/src_" + algmethod + ".jpg", mat_src); + + isdetected = false; + for (indx0=0; indx0 min_weight)){ + fltr_pos.push_back(r); + fltr_weights.push_back(w); + isdetected = true; + } + } + + if (isdetected){ + for (indx0=0; indx0current_image->location.minx-32; + roi.y = cam->current_image->location.miny-32; + roi.width = cam->current_image->location.width+64; + roi.height = cam->current_image->location.height+64; + /* + MOTION_LOG(ERR, TYPE_ALL, NO_ERRNO + ,"bbx %d y %d %dx%d %dx%d " + ,roi.x, roi.y, roi.width, roi.height + ,cam->current_image->location.width + ,cam->current_image->location.height); + */ + if (roi.x < 0) { + roi.x = 0; + roi.width =roi.width-16; + } + if (roi.y < 0){ + roi.y = 0; + roi.height = roi.height-16; + } + if ((roi.x + roi.width) > cam->imgs.width) { + roi.width = cam->imgs.width - roi.x; + } + if ((roi.y + roi.height) > cam->imgs.height) { + roi.height = cam->imgs.height - roi.y; + } + + mat_dst = mat_src(roi); + /* + MOTION_LOG(ERR, TYPE_ALL, NO_ERRNO + ,"x %d y %d %dx%d %dx%d " + ,roi.x, roi.y, roi.width, roi.height + ,cam->current_image->location.width + ,cam->current_image->location.height); + */ + +} + +static void algsec_detect_yolo(ctx_cam *cam, ctx_algsec_model &algmdl){ + + (void)cam; + (void)algmdl; + +} + +static void algsec_detect_hog(ctx_cam *cam, ctx_algsec_model &algmdl){ + + std::vector detect_weights; + std::vector detect_pos; + HOGDescriptor hog; + Mat mat_dst; + + try { + if (algmdl.parms[0][0] == "color"){ + /* AFAIK, the detector uses grey so users shouldn't really use this*/ + Mat mat_src = Mat(cam->imgs.height*3/2, cam->imgs.width + , CV_8UC1, (void*)cam->algsec->image_norm); + cvtColor(mat_src, mat_dst, COLOR_YUV2RGB_YV12); + + } else if (algmdl.parms[0][0] == "full"){ + mat_dst = Mat(cam->imgs.height, cam->imgs.width + , CV_8UC1, (void*)cam->algsec->image_norm); + + } else { + /*Discard really small and large images */ + if ((cam->current_image->location.width < 64) || + (cam->current_image->location.height < 64) || + ((cam->current_image->location.width/cam->imgs.width) > 0.7) || + ((cam->current_image->location.height/cam->imgs.height) > 0.7)) return; + + Mat mat_src = Mat(cam->imgs.height, cam->imgs.width + , CV_8UC1, (void*)cam->algsec->image_norm); + algsec_img_roi(cam, mat_src, mat_dst); + + } + + equalizeHist(mat_dst, mat_dst); + + hog.setSVMDetector(HOGDescriptor::getDefaultPeopleDetector()); + + hog.detectMultiScale(mat_dst, detect_pos, detect_weights, 0 + ,Size(algmdl.parms_int[0][2], algmdl.parms_int[0][2]) + ,Size(algmdl.parms_int[0][3], algmdl.parms_int[0][3]) + ,algmdl.parms_float[0][4] + ,algmdl.parms_int[0][5] + ,false); + + algsec_img_show(cam, mat_dst, detect_pos, detect_weights, "hog" + ,algmdl.parms_float[0][1]); + + } + catch ( cv::Exception& e ) { + const char* err_msg = e.what(); + MOTION_LOG(ERR, TYPE_ALL, NO_ERRNO, _("Error %s"),err_msg); + MOTION_LOG(ERR, TYPE_ALL, NO_ERRNO, _("Disabling secondary detection")); + algmdl.method = 0; + } +} + +static void algsec_detect_haar(ctx_cam *cam, ctx_algsec_model &algmdl){ + + std::vector detect_weights; + std::vector detect_pos; + std::vector levels; + + try { + Mat mat_src(cam->imgs.height, cam->imgs.width + , CV_8UC1, (void*)cam->algsec->image_norm); + + equalizeHist(mat_src, mat_src); + + algmdl.haar_cascade.detectMultiScale( + mat_src, detect_pos, levels, detect_weights, 1.1, 1, 0, Size(), Size(), true); + + algsec_img_show(cam, mat_src, detect_pos, detect_weights, "haar", 0.6); + } + catch ( cv::Exception& e ) { + const char* err_msg = e.what(); + MOTION_LOG(ERR, TYPE_ALL, NO_ERRNO, _("Error %s"),err_msg); + MOTION_LOG(ERR, TYPE_ALL, NO_ERRNO, _("Disabling secondary detection")); + algmdl.method = 0; + } +} + +static void algsec_load_yolo(ctx_algsec_model &algsec){ + /* Placeholder for implementation of yolo classifier */ + (void)algsec; + +} + +static void algsec_load_haar(ctx_algsec_model &algsec){ + + /* If loading fails, reset the method to invalidate detection */ + try { + if (!algsec.haar_cascade.load(algsec.modelfile)){ + /* Loading failed, reset method*/ + algsec.method = 0; + MOTION_LOG(ERR, TYPE_ALL, NO_ERRNO, _("Failed loading model %s") + ,algsec.modelfile.c_str()); + }; + } + catch ( cv::Exception& e ) { + const char* err_msg = e.what(); + MOTION_LOG(ERR, TYPE_ALL, NO_ERRNO, _("Error %s"),err_msg); + MOTION_LOG(ERR, TYPE_ALL, NO_ERRNO, _("Failed loading model %s") + ,algsec.modelfile.c_str()); + algsec.method = 0; + } +} + +static void algsec_parms_hog(ctx_algsec_model &model){ + /* These conversions put the applicable parms into int or float variables + * This is done once here upon parsing the parms so that we do not have to + * do the conversion upon every single image being processed and we can directly + * reference the converted parameter + */ + int indx0, indx1; + + for (indx0=0;indx0<3;indx0++){ + for (indx1=0;indx1<10;indx1++){ + model.parms_int[indx0][indx1] = 0; + model.parms_float[indx0][indx1] = 0; + if (model.parms[indx0][indx1] != ""){ + model.parms_int[indx0][indx1] = std::atoi(model.parms[indx0][indx1].c_str()); + model.parms_float[indx0][indx1] = std::atof(model.parms[indx0][indx1].c_str()); + } + } + /*parm[0] is color/full/area */ + if (model.parms_float[indx0][1] <= 0) model.parms_int[indx0][1] = 0.6; /*Min weigtht */ + if (model.parms_int[indx0][2] <= 0) model.parms_int[indx0][2] = 8; /* */ + if (model.parms_int[indx0][3] <= 0) model.parms_int[indx0][3] = 32; /* */ + if (model.parms_float[indx0][4] <= 0.01) model.parms_float[indx0][4] = 1.05; /* */ + if (model.parms_int[indx0][5] <= 0) model.parms_int[indx0][5] = 2; /* */ + + } + +} + +/**Parse parm based upon colons*/ +static void algsec_parms_parse_detail( + std::string &vin, ctx_algsec_model &model,int indx0) { + + std::size_t st_colon, en_colon; + std::string tmp; + int indx1; + + for (indx1=0;indx1<10;indx1++){ + if (model.parms[indx0][indx1] != "") model.parms[indx0][indx1].clear(); + } + + if (vin == "") return; + + en_colon = -1; + for (indx1=0;indx1<10;indx1++){ + st_colon = en_colon+1; + en_colon = vin.find(':',st_colon); + tmp = vin.substr(st_colon,en_colon-st_colon); + model.parms[indx0][indx1] = tmp; + if (en_colon == std::string::npos) return; + } + + return; +} + +/**Parse parms based upon commas */ +static void algsec_parms_parse(ctx_cam *cam){ + /* Parse out the comma separated parms + * exampe source config line: human:75:25,car:60:30,dog:90:70 + */ + int indx1, indx2; + std::size_t st_comma, en_comma; + std::string tmp; + + for (indx1=0;indx1<3;indx1++){ + if (cam->algsec->models[indx1].config != ""){ + indx2=0; + st_comma = 0; + en_comma = cam->algsec->models[indx1].config.find(',', st_comma); + while ((en_comma != std::string::npos) && (indx2 < 2)){ + tmp = cam->algsec->models[indx1].config.substr(st_comma, en_comma - st_comma); + algsec_parms_parse_detail(tmp, cam->algsec->models[indx1],indx2); + st_comma = en_comma + 1; + en_comma = cam->algsec->models[indx1].config.find(',', st_comma); + indx2++; + } + tmp = cam->algsec->models[indx1].config.substr( + st_comma, cam->algsec->models[indx1].config.length()); + algsec_parms_parse_detail(tmp, cam->algsec->models[indx1], indx2); + } + switch (cam->algsec->models[indx1].method) { + case 1: //Haar Method + break; + case 2: //HoG Method + algsec_parms_hog(cam->algsec->models[indx1]); + break; + case 3: //YoLo Method + break; + } + } +} + +/**Load the parms from the config to algsec struct */ +static void algsec_load_parms(ctx_cam *cam){ + + /* Copy the model file names and config into the ctx_cam + * For "simplicity", these are all fixed size arrays for + * both the models and parms. If there is a need later + * they can be changed to vectors + */ + + cam->algsec->height = cam->imgs.height; + cam->algsec->width = cam->imgs.width; + + cam->algsec->frame_interval = cam->conf.secondary_interval; + cam->algsec->frame_cnt = cam->algsec->frame_interval; + cam->algsec->image_norm = (unsigned char*)mymalloc(cam->imgs.size_norm); + cam->algsec->frame_missed = 0; + cam->algsec->too_slow = 0; + cam->algsec->detecting = false; + + /* We need to set the closing to true so that we can + * know whether to shutdown the handler when we deinit + */ + cam->algsec->closing = true; + + if (cam->conf.secondary_method != 0) { + cam->algsec->models[0].method = cam->conf.secondary_method; + if (cam->conf.secondary_model != NULL){ + cam->algsec->models[0].modelfile = cam->conf.secondary_model; + } + if (cam->conf.secondary_config != NULL) { + cam->algsec->models[0].config = cam->conf.secondary_config; + } + } else { + cam->algsec->models[0].method = 0; + } + + if (cam->conf.secondary_method2 != 0) { + cam->algsec->models[1].method = cam->conf.secondary_method2; + if (cam->conf.secondary_model2 != NULL){ + cam->algsec->models[1].modelfile = cam->conf.secondary_model2; + } + if (cam->conf.secondary_config2 != NULL) { + cam->algsec->models[1].config = cam->conf.secondary_config2; + } + } else { + cam->algsec->models[1].method = 0; + } + + if (cam->conf.secondary_method3 != 0) { + cam->algsec->models[2].method = cam->conf.secondary_method3; + if (cam->conf.secondary_model3 != NULL) { + cam->algsec->models[2].modelfile = cam->conf.secondary_model3; + } + if (cam->conf.secondary_config3 != NULL) { + cam->algsec->models[2].config = cam->conf.secondary_config3; + } + } else { + cam->algsec->models[2].method = 0; + } + + algsec_parms_parse(cam); + +} + +/**If possible preload the models and initialize them */ +static int algsec_load_models(ctx_cam *cam){ + + int indx, retcd; + /* Default the return code to no models found. If after looping + * through and loading the models, we have a method, then we set + * the return code to ok (zero). If a model fails to load then + * in that function we reset the method to zero. + */ + retcd = -1; + for (indx=0;indx<3;indx++){ + if (cam->algsec->models[indx].method != 0){ + retcd = 0; + switch (cam->algsec->models[indx].method) { + case 1: //Haar Method + algsec_load_haar(cam->algsec->models[indx]); + break; + case 2: //HoG Method + break; + case 3: //YoLo Method + algsec_load_yolo(cam->algsec->models[indx]); + break; + } + } + } + + return retcd; + +} + +/**Detection thread processing loop */ +static void *algsec_handler(void *arg) { + ctx_cam *cam = (ctx_cam*)arg; + int indx; + long interval; + + MOTION_LOG(INF, TYPE_NETCAM, NO_ERRNO,_("Starting.")); + + cam->algsec->closing = false; + + if (cam->conf.framerate < 2){ + interval = 1000000L / 2; + } else { + interval = 1000000L / cam->conf.framerate; + } + + while (!cam->algsec->closing){ + if (cam->algsec->detecting){ + for (indx=0;indx<3;indx++){ + switch (cam->algsec->models[indx].method) { + case 1: //Haar Method + algsec_detect_haar(cam, cam->algsec->models[indx]); + break; + case 2: //HoG Method + algsec_detect_hog(cam, cam->algsec->models[indx]); + break; + case 3: //YoLo Method + algsec_detect_yolo(cam, cam->algsec->models[indx]); + break; + } + } + cam->algsec->detecting = false; + } else { + SLEEP(0,interval) + } + } + cam->algsec->closing = false; + MOTION_LOG(INF, TYPE_NETCAM, NO_ERRNO,_("Exiting.")); + pthread_exit(NULL); + +} + +/**Start the detection thread*/ +static void algsec_start_handler(ctx_cam *cam){ + + int retcd, indx; + pthread_attr_t handler_attribute; + + pthread_attr_init(&handler_attribute); + pthread_attr_setdetachstate(&handler_attribute, PTHREAD_CREATE_DETACHED); + + retcd = pthread_create(&cam->algsec->threadid, &handler_attribute, &algsec_handler, cam); + if (retcd < 0) { + MOTION_LOG(ALR, TYPE_NETCAM, SHOW_ERRNO + ,_("Error starting algsec handler thread")); + for (indx=0;indx<3;indx++){ + cam->algsec->models[indx].method = 0; + } + } + pthread_attr_destroy(&handler_attribute); + return; + +} + +#endif + +void algsec_init(ctx_cam *cam){ + /* + * This function parses out and initializes the parameters + * associated with the secondary detection algorithm if a + * secondary detection method has been requested. + */ + #ifdef HAVE_OPENCV + int retcd; + + mythreadname_set("cv",cam->threadnr,cam->conf.camera_name); + + cam->algsec = new ctx_algsec; + + algsec_load_parms(cam); + + retcd = algsec_load_models(cam); + + if (retcd == 0) algsec_start_handler(cam); + + mythreadname_set("ml",cam->threadnr,cam->conf.camera_name); + #else + (void)cam; + #endif + +} + +/** Free algsec memory and shutdown thread */ +void algsec_deinit(ctx_cam *cam){ + #ifdef HAVE_OPENCV + int waitcnt = 0; + + if (!cam->algsec->closing) { + cam->algsec->closing = true; + while ((cam->algsec->closing) && (waitcnt <100)){ + SLEEP(0,1000000) + waitcnt++; + } + } + if (cam->algsec->image_norm != nullptr){ + free(cam->algsec->image_norm); + cam->algsec->image_norm = nullptr; + } + if (waitcnt == 100){ + MOTION_LOG(ERR, TYPE_NETCAM, NO_ERRNO + ,_("Graceful shutdown of secondary detector thread failed")); + } + + delete cam->algsec; + + #else + (void)cam; + #endif +} + +void algsec_detect(ctx_cam *cam){ + /*This function runs on the camera thread */ + #ifdef HAVE_OPENCV + if (cam->algsec->frame_cnt > 0) cam->algsec->frame_cnt--; + + if (cam->algsec->frame_cnt == 0){ + + if (cam->algsec->detecting){ + cam->algsec->frame_missed++; + } else { + /*Get any previous detection results */ + + /*Copy in a new image for processing */ + memcpy(cam->algsec->image_norm, cam->current_image->image_norm, cam->imgs.size_norm); + + /*Set the bool to detect on the new image and reset interval */ + cam->algsec->detecting = true; + cam->algsec->frame_cnt = cam->algsec->frame_interval; + if (cam->algsec->frame_missed >10){ + if (cam->algsec->too_slow == 0) { + MOTION_LOG(WRN, TYPE_NETCAM, NO_ERRNO + ,_("Your computer is too slow for these settings.")); + } else if (cam->algsec->too_slow == 10){ + MOTION_LOG(WRN, TYPE_NETCAM, NO_ERRNO + ,_("Missed many frames for secondary detection. Your computer is too slow.")); + } + cam->algsec->too_slow++; + } + cam->algsec->frame_missed = 0; + + } + } + #else + (void)cam; + #endif + +} + + diff --git a/src/alg_sec.hpp b/src/alg_sec.hpp new file mode 100644 index 00000000..df1d14dc --- /dev/null +++ b/src/alg_sec.hpp @@ -0,0 +1,57 @@ + +/* + * alg_sec.hpp + * + * Algorithms for Secondary Detection Header + * Copyright 2019 + * This software is distributed under the GNU public license version 2 + * See also the file 'COPYING'. + * + * + */ +#ifndef _INCLUDE_ALG_SEC_H +#define _INCLUDE_ALG_SEC_H + + +#include +#include +#ifdef HAVE_OPENCV + #include "opencv2/objdetect.hpp" + #include "opencv2/highgui.hpp" + #include "opencv2/imgproc.hpp" +#endif + + +//example source config line: human:75:25,car:60:30:4:15:A:1.012:3,dog:90:70 +struct ctx_algsec_model { + int method; + std::string modelfile; //Source model file + std::string config; //Source config line + std::string parms[3][10]; //Parms as strings + int parms_int[3][10]; //Parms convert to int + float parms_float[3][10]; //Parms converted to float + #ifdef HAVE_OPENCV + cv::CascadeClassifier haar_cascade; //Haar Cascade (if applicable) + #endif +}; + +struct ctx_algsec { + pthread_t threadid; /* thread i.d. for a secondary detection thread (if required). */ + volatile bool closing; + volatile bool detecting; + int frame_interval; + int frame_cnt; + int frame_missed; + int too_slow; + unsigned char *image_norm; + int width; + int height; + struct ctx_algsec_model models[3]; +}; + + +void algsec_detect(ctx_cam *cam); +void algsec_init(ctx_cam *cam); +void algsec_deinit(ctx_cam *cam); + +#endif \ No newline at end of file diff --git a/src/conf.cpp b/src/conf.cpp index 4a08994d..4faaa2f6 100644 --- a/src/conf.cpp +++ b/src/conf.cpp @@ -221,6 +221,46 @@ struct ctx_parm config_parms[] = { "# Enable tuning of the threshold down if possible.", 0,PARM_TYP_BOOL, PARM_CAT_02, WEBUI_LEVEL_LIMITED}, { + "secondary_interval", + "# The interval between secondary detections.", + 0,PARM_TYP_INT, PARM_CAT_02, WEBUI_LEVEL_LIMITED }, + { + "secondary_method", + "# The method to use for secondary detection.", + 0,PARM_TYP_INT, PARM_CAT_02, WEBUI_LEVEL_LIMITED }, + { + "secondary_model", + "# Full path name for the secondary model.", + 0,PARM_TYP_STRING, PARM_CAT_02, WEBUI_LEVEL_LIMITED }, + { + "secondary_config", + "# Configuration parameters for the secondary model.", + 0,PARM_TYP_STRING, PARM_CAT_02, WEBUI_LEVEL_LIMITED }, + { + "secondary_method2", + "# The method to use for secondary detection 2.", + 0,PARM_TYP_INT, PARM_CAT_02, WEBUI_LEVEL_LIMITED }, + { + "secondary_model2", + "# Full path name for the secondary model 2.", + 0,PARM_TYP_STRING, PARM_CAT_02, WEBUI_LEVEL_LIMITED }, + { + "secondary_config2", + "# Configuration parameters for the secondary model 2.", + 0,PARM_TYP_STRING, PARM_CAT_02, WEBUI_LEVEL_LIMITED }, + { + "secondary_method3", + "# The method to use for secondary detection 3.", + 0,PARM_TYP_INT, PARM_CAT_02, WEBUI_LEVEL_LIMITED }, + { + "secondary_model3", + "# Full path name for the secondary model 3.", + 0,PARM_TYP_STRING, PARM_CAT_02, WEBUI_LEVEL_LIMITED }, + { + "secondary_config3", + "# Configuration parameters for the secondary model 3.", + 0,PARM_TYP_STRING, PARM_CAT_02, WEBUI_LEVEL_LIMITED }, + { "noise_level", "# Noise threshold for the motion detection.", 0,PARM_TYP_INT, PARM_CAT_02, WEBUI_LEVEL_LIMITED }, @@ -914,7 +954,7 @@ static void conf_parm_camera(struct ctx_motapp *motapp, char *str) { /* Index starts at zero (+1) plus another for our new camera(+2)*/ motapp->cam_list = (struct ctx_cam **)myrealloc( motapp->cam_list, sizeof(struct ctx_cam *) * (indx_cams + 2), "config_camera"); - motapp->cam_list[indx_cams] = (struct ctx_cam *)mymalloc(sizeof(struct ctx_cam)); + motapp->cam_list[indx_cams] = new ctx_cam; motapp->cam_list[indx_cams + 1] = NULL; conf_edit_dflt_cam(motapp->cam_list[indx_cams]); @@ -1257,8 +1297,7 @@ void conf_init_cams(struct ctx_motapp *motapp){ int retcd; motapp->cam_list = (struct ctx_cam**)calloc(sizeof(struct ctx_cam *), 2); - motapp->cam_list[0] = (struct ctx_cam *)mymalloc(sizeof(struct ctx_cam)); - memset(motapp->cam_list[0], 0, sizeof(struct ctx_cam)); + motapp->cam_list[0] = new ctx_cam; motapp->cam_list[1] = NULL; motapp->cam_list[0]->motapp = motapp; diff --git a/src/conf.hpp b/src/conf.hpp index d43526c6..e3b2c3b7 100644 --- a/src/conf.hpp +++ b/src/conf.hpp @@ -71,6 +71,16 @@ int threshold_sdevy; int threshold_sdevxy; int threshold_tune; + int secondary_interval; + int secondary_method; + char *secondary_model; + char *secondary_config; + int secondary_method2; + char *secondary_model2; + char *secondary_config2; + int secondary_method3; + char *secondary_model3; + char *secondary_config3; int noise_level; int noise_tune; char *despeckle_filter; diff --git a/src/conf_edit.cpp b/src/conf_edit.cpp index c3840e25..536091a7 100644 --- a/src/conf_edit.cpp +++ b/src/conf_edit.cpp @@ -892,6 +892,152 @@ static void conf_edit_threshold_tune(struct ctx_cam *cam, char *arg1, enum PARM_ return; MOTION_LOG(DBG, TYPE_ALL, NO_ERRNO,"%s:%s","threshold_tune",_("threshold_tune")); } +static void conf_edit_secondary_interval(struct ctx_cam *cam, char *arg1, enum PARM_ACT pact) { + int parm_in; + if (pact == PARM_ACT_DFLT){ + cam->conf.secondary_interval = 0; + } else if (pact == PARM_ACT_SET){ + parm_in = atoi(arg1); + if ((parm_in < 0) ) { + MOTION_LOG(NTC, TYPE_ALL, NO_ERRNO, _("Invalid secondary_interval %d"),parm_in); + } else { + cam->conf.secondary_interval = parm_in; + } + } else if (pact == PARM_ACT_GET){ + conf_edit_get_int(cam->conf.secondary_interval, arg1); + } + return; + MOTION_LOG(DBG, TYPE_ALL, NO_ERRNO,"%s:%s","secondary_interval",_("secondary_interval")); +} +static void conf_edit_secondary_method(struct ctx_cam *cam, char *arg1, enum PARM_ACT pact) { + int parm_in; + if (pact == PARM_ACT_DFLT){ + cam->conf.secondary_method = 0; + } else if (pact == PARM_ACT_SET){ + parm_in = atoi(arg1); + if ((parm_in < 0) ) { + MOTION_LOG(NTC, TYPE_ALL, NO_ERRNO, _("Invalid secondary_method %d"),parm_in); + } else { + cam->conf.secondary_method = parm_in; + } + } else if (pact == PARM_ACT_GET){ + conf_edit_get_int(cam->conf.secondary_method, arg1); + } + return; + MOTION_LOG(DBG, TYPE_ALL, NO_ERRNO,"%s:%s","secondary_method",_("secondary_method")); +} +static void conf_edit_secondary_model(struct ctx_cam *cam, char *arg1, enum PARM_ACT pact) { + if (pact == PARM_ACT_DFLT) { + conf_edit_set_string(&cam->conf.secondary_model,NULL); + } else if (pact == PARM_ACT_FREE){ + conf_edit_set_string(&cam->conf.secondary_model,NULL); + } else if (pact == PARM_ACT_SET){ + conf_edit_set_string(&cam->conf.secondary_model,arg1); + } else if (pact == PARM_ACT_GET){ + conf_edit_get_string(cam->conf.secondary_model,arg1); + } + return; + MOTION_LOG(DBG, TYPE_ALL, NO_ERRNO,"%s:%s","secondary_model",_("secondary_model")); +} +static void conf_edit_secondary_config(struct ctx_cam *cam, char *arg1, enum PARM_ACT pact) { + if (pact == PARM_ACT_DFLT) { + conf_edit_set_string(&cam->conf.secondary_config,NULL); + } else if (pact == PARM_ACT_FREE){ + conf_edit_set_string(&cam->conf.secondary_config,NULL); + } else if (pact == PARM_ACT_SET){ + conf_edit_set_string(&cam->conf.secondary_config,arg1); + } else if (pact == PARM_ACT_GET){ + conf_edit_get_string(cam->conf.secondary_config,arg1); + } + return; + MOTION_LOG(DBG, TYPE_ALL, NO_ERRNO,"%s:%s","secondary_config",_("secondary_config")); +} +static void conf_edit_secondary_method2(struct ctx_cam *cam, char *arg1, enum PARM_ACT pact) { + int parm_in; + if (pact == PARM_ACT_DFLT){ + cam->conf.secondary_method2 = 0; + } else if (pact == PARM_ACT_SET){ + parm_in = atoi(arg1); + if ((parm_in < 0) ) { + MOTION_LOG(NTC, TYPE_ALL, NO_ERRNO, _("Invalid secondary_method2 %d"),parm_in); + } else { + cam->conf.secondary_method2 = parm_in; + } + } else if (pact == PARM_ACT_GET){ + conf_edit_get_int(cam->conf.secondary_method2, arg1); + } + return; + MOTION_LOG(DBG, TYPE_ALL, NO_ERRNO,"%s:%s","secondary_method2",_("secondary_method2")); +} +static void conf_edit_secondary_model2(struct ctx_cam *cam, char *arg1, enum PARM_ACT pact) { + if (pact == PARM_ACT_DFLT) { + conf_edit_set_string(&cam->conf.secondary_model2,NULL); + } else if (pact == PARM_ACT_FREE){ + conf_edit_set_string(&cam->conf.secondary_model2,NULL); + } else if (pact == PARM_ACT_SET){ + conf_edit_set_string(&cam->conf.secondary_model2,arg1); + } else if (pact == PARM_ACT_GET){ + conf_edit_get_string(cam->conf.secondary_model2,arg1); + } + return; + MOTION_LOG(DBG, TYPE_ALL, NO_ERRNO,"%s:%s","secondary_model2",_("secondary_model2")); +} +static void conf_edit_secondary_config2(struct ctx_cam *cam, char *arg1, enum PARM_ACT pact) { + if (pact == PARM_ACT_DFLT) { + conf_edit_set_string(&cam->conf.secondary_config2,NULL); + } else if (pact == PARM_ACT_FREE){ + conf_edit_set_string(&cam->conf.secondary_config2,NULL); + } else if (pact == PARM_ACT_SET){ + conf_edit_set_string(&cam->conf.secondary_config2,arg1); + } else if (pact == PARM_ACT_GET){ + conf_edit_get_string(cam->conf.secondary_config2,arg1); + } + return; + MOTION_LOG(DBG, TYPE_ALL, NO_ERRNO,"%s:%s","secondary_config2",_("secondary_config2")); +} +static void conf_edit_secondary_method3(struct ctx_cam *cam, char *arg1, enum PARM_ACT pact) { + int parm_in; + if (pact == PARM_ACT_DFLT){ + cam->conf.secondary_method3 = 0; + } else if (pact == PARM_ACT_SET){ + parm_in = atoi(arg1); + if ((parm_in < 0) ) { + MOTION_LOG(NTC, TYPE_ALL, NO_ERRNO, _("Invalid secondary_method3 %d"),parm_in); + } else { + cam->conf.secondary_method3 = parm_in; + } + } else if (pact == PARM_ACT_GET){ + conf_edit_get_int(cam->conf.secondary_method3, arg1); + } + return; + MOTION_LOG(DBG, TYPE_ALL, NO_ERRNO,"%s:%s","secondary_method3",_("secondary_method3")); +} +static void conf_edit_secondary_model3(struct ctx_cam *cam, char *arg1, enum PARM_ACT pact) { + if (pact == PARM_ACT_DFLT) { + conf_edit_set_string(&cam->conf.secondary_model3,NULL); + } else if (pact == PARM_ACT_FREE){ + conf_edit_set_string(&cam->conf.secondary_model3,NULL); + } else if (pact == PARM_ACT_SET){ + conf_edit_set_string(&cam->conf.secondary_model3,arg1); + } else if (pact == PARM_ACT_GET){ + conf_edit_get_string(cam->conf.secondary_model3,arg1); + } + return; + MOTION_LOG(DBG, TYPE_ALL, NO_ERRNO,"%s:%s","secondary_model3",_("secondary_model3")); +} +static void conf_edit_secondary_config3(struct ctx_cam *cam, char *arg1, enum PARM_ACT pact) { + if (pact == PARM_ACT_DFLT) { + conf_edit_set_string(&cam->conf.secondary_config3,NULL); + } else if (pact == PARM_ACT_FREE){ + conf_edit_set_string(&cam->conf.secondary_config3,NULL); + } else if (pact == PARM_ACT_SET){ + conf_edit_set_string(&cam->conf.secondary_config3,arg1); + } else if (pact == PARM_ACT_GET){ + conf_edit_get_string(cam->conf.secondary_config3,arg1); + } + return; + MOTION_LOG(DBG, TYPE_ALL, NO_ERRNO,"%s:%s","secondary_config3",_("secondary_config3")); +} static void conf_edit_noise_level(struct ctx_cam *cam, char *arg1, enum PARM_ACT pact) { int parm_in; if (pact == PARM_ACT_DFLT){ @@ -2393,6 +2539,16 @@ static void conf_edit_cat02(struct ctx_cam *cam, const char *cmd, char *arg1, en } else if (mystreq(cmd,"threshold_sdevy")){ conf_edit_threshold_sdevy(cam, arg1, pact); } else if (mystreq(cmd,"threshold_sdevxy")){ conf_edit_threshold_sdevxy(cam, arg1, pact); } else if (mystreq(cmd,"threshold_tune")){ conf_edit_threshold_tune(cam, arg1, pact); + } else if (mystreq(cmd,"secondary_interval")){ conf_edit_secondary_interval(cam, arg1, pact); + } else if (mystreq(cmd,"secondary_method")){ conf_edit_secondary_method(cam, arg1, pact); + } else if (mystreq(cmd,"secondary_model")){ conf_edit_secondary_model(cam, arg1, pact); + } else if (mystreq(cmd,"secondary_config")){ conf_edit_secondary_config(cam, arg1, pact); + } else if (mystreq(cmd,"secondary_method2")){ conf_edit_secondary_method2(cam, arg1, pact); + } else if (mystreq(cmd,"secondary_model2")){ conf_edit_secondary_model2(cam, arg1, pact); + } else if (mystreq(cmd,"secondary_config2")){ conf_edit_secondary_config2(cam, arg1, pact); + } else if (mystreq(cmd,"secondary_method3")){ conf_edit_secondary_method3(cam, arg1, pact); + } else if (mystreq(cmd,"secondary_model3")){ conf_edit_secondary_model3(cam, arg1, pact); + } else if (mystreq(cmd,"secondary_config3")){ conf_edit_secondary_config3(cam, arg1, pact); } else if (mystreq(cmd,"noise_level")){ conf_edit_noise_level(cam, arg1, pact); } else if (mystreq(cmd,"noise_tune")){ conf_edit_noise_tune(cam, arg1, pact); } else if (mystreq(cmd,"despeckle_filter")){ conf_edit_despeckle_filter(cam, arg1, pact); diff --git a/src/motion.hpp b/src/motion.hpp index 8b2a6ae6..4fd0d692 100644 --- a/src/motion.hpp +++ b/src/motion.hpp @@ -57,6 +57,7 @@ struct ctx_dbse; struct ctx_mmalcam; struct ctx_movie; struct ctx_netcam; +struct ctx_algsec; #include "conf.hpp" #include "track.hpp" @@ -316,6 +317,7 @@ struct ctx_cam { struct ctx_netcam *netcam_high; /* this structure contains the context for high resolution RTSP connection */ struct ctx_vdev *vdev; /* Structure for v4l2 device information */ struct ctx_image_data *current_image; /* Pointer to a structure where the image, diffs etc is stored */ + ctx_algsec *algsec; struct ctx_rotate *rotate_data; /* rotation data is thread-specific */ struct ctx_dbse *dbse; struct ctx_movie *movie_norm; diff --git a/src/motion_loop.cpp b/src/motion_loop.cpp index bc8fcf84..a8082e5b 100644 --- a/src/motion_loop.cpp +++ b/src/motion_loop.cpp @@ -32,6 +32,7 @@ #include "conf.hpp" #include "conf_edit.hpp" #include "alg.hpp" +#include "alg_sec.hpp" #include "track.hpp" #include "event.hpp" #include "picture.hpp" @@ -450,6 +451,8 @@ static int mlp_init(struct ctx_cam *cam) { webu_stream_init(cam); + algsec_init(cam); + rotate_init(cam); draw_init_scale(cam); @@ -515,6 +518,8 @@ void mlp_cleanup(struct ctx_cam *cam) { webu_stream_deinit(cam); + algsec_deinit(cam); + if (cam->video_dev >= 0) { MOTION_LOG(INF, TYPE_ALL, NO_ERRNO, _("Calling vid_close() from mlp_cleanup")); vid_close(cam); @@ -1192,6 +1197,7 @@ static void mlp_actions(struct ctx_cam *cam){ if (cam->current_image->flags & IMAGE_SAVE) cam->lasttime = cam->current_image->imgts.tv_sec; + if (cam->detecting_motion) algsec_detect(cam); mlp_areadetect(cam);