diff --git a/CMakeLists.txt b/CMakeLists.txt
index 213eb8d..0e38807 100644
--- a/CMakeLists.txt
+++ b/CMakeLists.txt
@@ -40,7 +40,9 @@ project(Kerberos)
# ---------------------------
# XCode system library
- link_directories("/usr/local/lib")
+ if(${CMAKE_SYSTEM_NAME} MATCHES "Darwin")
+ link_directories("/usr/local/lib")
+ endif()
# ---------
# Package
diff --git a/cmake/External-OpenCV.cmake b/cmake/External-OpenCV.cmake
index 1dfd7f4..01d63ed 100644
--- a/cmake/External-OpenCV.cmake
+++ b/cmake/External-OpenCV.cmake
@@ -18,14 +18,14 @@ ExternalProject_Add(opencv
-DBUILD_opencv_core=ON
-DBUILD_opencv_imgproc=ON
-DBUILD_opencv_highgui=ON
- -DBUILD_opencv_video=OFF
+ -DBUILD_opencv_video=ON
-DBUILD_opencv_apps=OFF
- -DBUILD_opencv_flann=OFF
+ -DBUILD_opencv_flann=ON
-DBUILD_opencv_gpu=OFF
- -DBUILD_opencv_ml=OFF
+ -DBUILD_opencv_ml=ON
-DBUILD_opencv_legacy=OFF
-DBUILD_opencv_calib3d=OFF
- -DBUILD_opencv_features2d=OFF
+ -DBUILD_opencv_features2d=ON
-DBUILD_opencv_java=OFF
-DBUILD_opencv_objdetect=OFF
-DBUILD_opencv_photo=OFF
@@ -38,6 +38,7 @@ ExternalProject_Add(opencv
-DBUILD_SHARED_LIBS:BOOL=OFF
-DBUILD_TESTS:BOOL=OFF
-DBUILD_PERF_TESTS:BOOL=OFF
+ -DBUILD_opencv_contrib=ON
-DCMAKE_BUILD_TYPE:STRING=Release
-DWITH_FFMPEG:BOOL=ON
-DWITH_IPP:BOOL=OFF
@@ -56,7 +57,7 @@ else()
set(OPENCV_LIBRARY_DIR ${CMAKE_BINARY_DIR}/thirdparty/x86/vc12/lib)
endif()
-set(OPENCV_LIBRARIES opencv_imgproc opencv_core opencv_highgui opencv_videoio opencv_imgcodecs)
+set(OPENCV_LIBRARIES opencv_imgproc opencv_core opencv_highgui opencv_video opencv_videoio opencv_imgcodecs opencv_features2d)
include_directories(${OPENCV_INCLUDE_DIR})
link_directories(${OPENCV_LIBRARY_DIR})
diff --git a/config/algorithm.xml b/config/algorithm.xml
index 5411f96..a82128c 100755
--- a/config/algorithm.xml
+++ b/config/algorithm.xml
@@ -6,4 +6,14 @@
15
+
+ true
+ 50
+ 5
+ 1
+ 5
+ 7
+ 20
+
+
diff --git a/config/capture.xml b/config/capture.xml
index 097fd09..a43725a 100755
--- a/config/capture.xml
+++ b/config/capture.xml
@@ -18,10 +18,18 @@
+ 1280
+ 720
+ 500
+ 0
+
+
+
800
640
+ 0
500
0
-
+
diff --git a/config/config.xml b/config/config.xml
index ca72bd3..77fb86f 100755
--- a/config/config.xml
+++ b/config/config.xml
@@ -6,6 +6,7 @@
false
Europe-Brussels
RaspiCamera
+ Mjpg
Enabled
DifferentialCollins
Hull
diff --git a/config/heuristic.xml b/config/heuristic.xml
index d8fd2d2..1ec5807 100755
--- a/config/heuristic.xml
+++ b/config/heuristic.xml
@@ -6,5 +6,15 @@
2
1000
+
+
+ 5
+ 90
+ 1400
+ true
+ 20
+ 1000
+ 100,100|100,200|200,100|200,200
+
diff --git a/config/io.xml b/config/io.xml
index c2f38f4..c93db02 100644
--- a/config/io.xml
+++ b/config/io.xml
@@ -4,6 +4,8 @@
timestamp_microseconds_instanceName_regionCoordinates_numberOfChanges_token.jpg
/etc/opt/kerberosio/capture/
+ false
+ white
17
diff --git a/config/stream.xml b/config/stream.xml
new file mode 100644
index 0000000..aa7b503
--- /dev/null
+++ b/config/stream.xml
@@ -0,0 +1,10 @@
+
+
+
+
+ true
+ 8889
+ 75
+
+
+
diff --git a/docker/Dockerfile b/docker/Dockerfile
index cecfa50..c578906 100644
--- a/docker/Dockerfile
+++ b/docker/Dockerfile
@@ -1,4 +1,4 @@
-FROM phusion/baseimage:latest
+FROM phusion/baseimage:0.9.16
MAINTAINER "Cédric Verstraeten"
diff --git a/include/kerberos/Globals.h b/include/kerberos/Globals.h
index 5b4d246..a90cb93 100644
--- a/include/kerberos/Globals.h
+++ b/include/kerberos/Globals.h
@@ -14,7 +14,7 @@
#ifndef __Version_H_INCLUDED__ // if Version.h hasn't been included yet...
#define __Version_H_INCLUDED__ // #define this so the compiler knows it has been included
- #define VERSION "2.0.0"
+ #define VERSION "2.1.0"
#define HADES "https://hades.kerberos.io"
#define SYMBOL_DIRECTORY "/etc/opt/kerberosio/symbols/"
#define CONFIGURATION_PATH "/etc/opt/kerberosio/config/config.xml"
diff --git a/include/kerberos/Kerberos.h b/include/kerberos/Kerberos.h
index 7cf28f1..465fee3 100644
--- a/include/kerberos/Kerberos.h
+++ b/include/kerberos/Kerberos.h
@@ -35,7 +35,8 @@ namespace kerberos
void configure(const std::string & configuration);
void configureCapture(StringMap & settings);
void configureCloud(StringMap & settings);
- void startStreamThread();
+ void configureStream(StringMap & settings);
+ void startStreamThread();
void stopStreamThread();
void startIOThread();
void stopIOThread();
diff --git a/include/kerberos/capture/Image.h b/include/kerberos/capture/Image.h
index 621eaf5..255e86f 100644
--- a/include/kerberos/capture/Image.h
+++ b/include/kerberos/capture/Image.h
@@ -53,6 +53,7 @@ namespace kerberos
void difference(const Image & image, Image & result);
void bitwiseAnd(const Image & image, Image & result);
void erode(const Image & kernel);
+ void dilate(const Image & kernel);
void threshold(const int threshold);
int brightness();
static cv::Mat createKernel(int width, int height)
diff --git a/include/kerberos/capture/Stream.h b/include/kerberos/capture/Stream.h
index da5babf..04ca79f 100644
--- a/include/kerberos/capture/Stream.h
+++ b/include/kerberos/capture/Stream.h
@@ -25,6 +25,7 @@
#include
#include
#include
+#include
#define PORT unsigned short
#define SOCKET int
#define HOSTENT struct hostent
@@ -42,8 +43,10 @@ namespace kerberos
std::vector clients;
SOCKET sock;
fd_set master;
- int timeout; // master sock timeout, shutdown after timeout millis.
- int quality; // jpeg compression [1..100]
+ bool m_enabled;
+ int m_streamPort;
+ int m_timeout; // master sock timeout, shutdown after timeout millis.
+ int m_quality; // jpeg compression [1..100]
int _write( int sock, char *s, int len )
{
@@ -57,10 +60,9 @@ namespace kerberos
public:
- Stream(int port = 0) : sock(INVALID_SOCKET), timeout(10), quality(50)
+ Stream() : sock(INVALID_SOCKET), m_timeout(10), m_quality(70)
{
FD_ZERO( &master );
- if (port) open(port);
}
~Stream()
@@ -68,8 +70,9 @@ namespace kerberos
release();
}
+ void configureStream(StringMap & settings);
bool release();
- bool open(int port);
+ bool open();
bool isOpened();
bool connect();
void write(Image image);
diff --git a/include/kerberos/capture/VideoCapture.h b/include/kerberos/capture/VideoCapture.h
new file mode 100644
index 0000000..88b36c0
--- /dev/null
+++ b/include/kerberos/capture/VideoCapture.h
@@ -0,0 +1,63 @@
+//
+// Class: VideoCapture
+// Description: Class that plays a video file.
+// Created: 17/05/2016
+// Author: Cédric Verstraeten
+// Mail: hello@cedric.ws
+// Website: www.cedric.ws
+//
+// The copyright to the computer program(s) herein
+// is the property of Cédric Verstraeten, Belgium.
+// The program(s) may be used and/or copied .
+//
+/////////////////////////////////////////////////////
+
+#ifndef __VideoCapture_H_INCLUDED__ // if VideoCapture.h hasn't been included yet...
+#define __VideoCapture_H_INCLUDED__ // #define this so the compiler knows it has been included
+
+#include "capture/Capture.h"
+#include "Executor.h"
+
+namespace kerberos
+{
+ char VideoCaptureName[] = "VideoCapture";
+ class VideoCapture : public CaptureCreator
+ {
+ private:
+ cv::VideoCapture * m_video;
+ std::string m_path;
+
+ public:
+ VideoCapture()
+ {
+ try
+ {
+ m_video = new cv::VideoCapture();
+ }
+ catch(cv::Exception & ex)
+ {
+ throw OpenCVException(ex.msg.c_str());
+ }
+ }
+
+ VideoCapture(int width, int height);
+ virtual ~VideoCapture(){};
+ void setup(StringMap & settings);
+ void setImageSize(int width, int height);
+ void setRotation(int angle){Capture::setRotation(angle);}
+ void setDelay(int msec){Capture::setDelay(msec);}
+ void setPath(std::string path){m_path=path;}
+ std::string getPath(){return m_path;}
+
+ void grab();
+ Image retrieve();
+ Image * takeImage();
+
+ void open();
+ void close();
+ void update();
+ bool isOpened();
+ };
+}
+
+#endif
\ No newline at end of file
diff --git a/include/kerberos/machinery/algorithm/BackgroundSubtraction.h b/include/kerberos/machinery/algorithm/BackgroundSubtraction.h
new file mode 100644
index 0000000..f5740c4
--- /dev/null
+++ b/include/kerberos/machinery/algorithm/BackgroundSubtraction.h
@@ -0,0 +1,45 @@
+//
+// Class: BackgroundSubtraction
+// Description: An algorithm to detect motion using background subtraction.
+// Created: 17/05/2016
+// Author: Cédric Verstraeten
+// Mail: hello@cedric.ws
+// Website: www.kerberos.io
+//
+// The copyright to the computer program(s) herein
+// is the property of kerberos.io, Belgium.
+// The program(s) may be used and/or copied .
+//
+/////////////////////////////////////////////////////
+
+#ifndef __BackgroundSubtraction_H_INCLUDED__ // if BackgroundSubtraction.h hasn't been included yet...
+#define __BackgroundSubtraction_H_INCLUDED__ // #define this so the compiler knows it has been included
+
+#include "machinery/algorithm/Algorithm.h"
+#include "opencv2/imgcodecs.hpp"
+#include "opencv2/video/video.hpp"
+
+namespace kerberos
+{
+ char BackgroundSubtractionName[] = "BackgroundSubtraction";
+ class BackgroundSubtraction : public AlgorithmCreator
+ {
+ private:
+ int m_threshold;
+ Image m_erodeKernel;
+ Image m_dilateKernel;
+ Image m_backgroud;
+ cv::Ptr m_subtractor;
+
+ public:
+ BackgroundSubtraction(){}
+ void setup(const StringMap & settings);
+
+ void setErodeKernel(int width, int height);
+ void setDilateKernel(int width, int height);
+ void setThreshold(int threshold);
+ void initialize(ImageVector & images);
+ Image evaluate(ImageVector & images, JSON & data);
+ };
+}
+#endif
\ No newline at end of file
diff --git a/include/kerberos/machinery/heuristic/Counter.h b/include/kerberos/machinery/heuristic/Counter.h
new file mode 100644
index 0000000..4b7f2a0
--- /dev/null
+++ b/include/kerberos/machinery/heuristic/Counter.h
@@ -0,0 +1,87 @@
+//
+// Class: Counter
+// Description: The counter heuristic will check if a valid in-out (out-in)
+// happened. The detections in and out are kept in memory.
+// E.g. total people in the building.
+// Created: 28/04/2015
+// Author: Cédric Verstraeten
+// Mail: hello@cedric.ws
+// Website: www.kerberos.io
+//
+// The copyright to the computer program(s) herein
+// is the property of kerberos.io, Belgium.
+// The program(s) may be used and/or copied .
+//
+/////////////////////////////////////////////////////
+
+#ifndef __Counter_H_INCLUDED__ // if Counter.h hasn't been included yet...
+#define __Counter_H_INCLUDED__ // #define this so the compiler knows it has been included
+
+#include "machinery/heuristic/Heuristic.h"
+
+namespace kerberos
+{
+ char CounterName[] = "Counter";
+
+ class Feature
+ {
+ private:
+ int x;
+ int y;
+ int area;
+ int appearance;
+
+ public:
+ Feature(int x, int y, int area, int appearance):x(x), y(y), area(area), appearance(appearance){};
+ int getX(){return x;}
+ int getY(){return y;}
+ int getArea(){return area;}
+ int getAppearance(){return appearance;}
+
+ double distance(Feature f)
+ {
+ return sqrt((f.x - x) * (f.x - x) + (f.y - y) * (f.y - y));
+ }
+
+ double areaDistance(Feature f)
+ {
+ return sqrt((f.area - area) * (f.area - area));
+ }
+
+ void decreaseAppearance()
+ {
+ appearance--;
+ }
+ };
+
+ enum Direction{left, right, top, bottom, parallell};
+
+ class Counter : public HeuristicCreator
+ {
+ private:
+ std::vector m_in;
+ std::vector m_out;
+ int m_minimumChanges;
+ int m_noMotionDelayTime;
+ int m_appearance;
+ int m_maxDistance;
+ int m_minArea;
+ bool m_onlyTrueWhenCounted;
+
+ std::vector > m_features;
+ cv::Mat m_status;
+
+ public:
+ Counter(){}
+ void setup(const StringMap & settings);
+ void setMinimumChanges(int changes){m_minimumChanges=changes;};
+ void setNoMotionDelayTime(int delay){m_noMotionDelayTime=delay;};
+ void setAppearance(int appearance){m_appearance=appearance;};
+ void setMaxDistance(int maxDistance){m_maxDistance=maxDistance;};
+ void setMinArea(int minArea){m_minArea=minArea;};
+ void setOnlyTrueWhenCounted(bool onlyTrueWhenCounted){m_onlyTrueWhenCounted=onlyTrueWhenCounted;};
+ bool intersection(cv::Point2f o1, cv::Point2f p1, cv::Point2f o2, cv::Point2f p2, cv::Point2f &r);
+ bool isValid(const Image & evaluation, const ImageVector & images, JSON & data);
+ };
+}
+#endif
\ No newline at end of file
diff --git a/include/kerberos/machinery/io/IoDisk.h b/include/kerberos/machinery/io/IoDisk.h
index 5af414d..a4a7bc9 100644
--- a/include/kerberos/machinery/io/IoDisk.h
+++ b/include/kerberos/machinery/io/IoDisk.h
@@ -25,16 +25,27 @@ namespace kerberos
private:
std::string m_instanceName;
std::string m_fileFormat;
+ bool m_drawTimestamp;
+ cv::Scalar m_timestampColor;
+ std::string m_timezone;
FileManager m_fileManager;
public:
IoDisk(){};
void setup(const StringMap & settings);
- void setInstanceName(std::string instanceName){m_instanceName=instanceName;};
+ cv::Scalar getColor(const std::string name);
+ bool getDrawTimestamp(){return m_drawTimestamp;};
+ void setDrawTimestamp(bool drawTimestamp){m_drawTimestamp=drawTimestamp;};
+ std::string getTimezone(){return m_timezone;};
+ void setTimezone(std::string timezone){m_timezone=timezone;};
+ cv::Scalar getTimestampColor(){return m_timestampColor;};
+ void setTimestampColor(cv::Scalar timestampColor){m_timestampColor=timestampColor;};
std::string getInstanceName(){return m_instanceName;};
+ void setInstanceName(std::string instanceName){m_instanceName=instanceName;};
std::string getFileFormat(){return m_fileFormat;};
void setFileFormat(std::string fileFormat){m_fileFormat = fileFormat;};
std::string buildPath(std::string pathToImage);
+ void drawDateOnImage(Image & image, std::string timestamp);
bool save(Image & image);
bool save(Image & image, JSON & data);
};
diff --git a/src/CMakeLists.txt b/src/CMakeLists.txt
index 9a1233e..a00c37a 100644
--- a/src/CMakeLists.txt
+++ b/src/CMakeLists.txt
@@ -130,9 +130,9 @@
SET(CPACK_DEBIAN_PACKAGE_MAINTAINER "Cédric Verstraeten")
set(CPACK_PACKAGE_DESCRIPTION_SUMMARY "Kerberos is a low-budget surveillance solution, that uses computer vision algorithms to detect changes, and that can trigger other devices. Kerberos is open source so you, but also other people, can customize the source to your needs and share it with our community. It has a green footprint when deploying on the Raspberry Pi and it's easy to install, you only need to transfer the image to the SD card and you're done.")
- set(CPACK_PACKAGE_VERSION "2.0.0")
+ set(CPACK_PACKAGE_VERSION "2.1.0")
set(CPACK_PACKAGE_VERSION_MAJOR "2")
- set(CPACK_PACKAGE_VERSION_MINOR "0")
+ set(CPACK_PACKAGE_VERSION_MINOR "1")
set(CPACK_PACKAGE_VERSION_PATCH "0")
string(TOLOWER "${CPACK_PACKAGE_NAME}" CPACK_PACKAGE_NAME_LOWERCASE)
diff --git a/src/kerberos/CMakeLists.txt b/src/kerberos/CMakeLists.txt
index d1ea45a..e489da8 100644
--- a/src/kerberos/CMakeLists.txt
+++ b/src/kerberos/CMakeLists.txt
@@ -31,19 +31,22 @@
cloud/GoogleDrive.cpp
capture/USBCamera.cpp
capture/IPCamera.cpp
+ capture/VideoCapture.cpp
machinery/condition/Time.cpp
machinery/condition/Enabled.cpp
machinery/algorithm/DifferentialCollins.cpp
+ machinery/algorithm/BackgroundSubtraction.cpp
machinery/expositor/RectangleExpositor.cpp
machinery/expositor/HullExpositor.cpp
machinery/heuristic/Sequence.cpp
+ machinery/heuristic/Counter.cpp
machinery/io/IoDisk.cpp
machinery/io/IoTCP.cpp
machinery/io/IoWebhook.cpp
)
set(KERBEROS_DEPENDENCIES ${KERBEROS_DEPENDENCIES} opencv restclient)
- set(KERBEROS_LIBRARIES ${KERBEROS_LIBRARIES} ${OPENCV_LIBRARIES} ${RESTCLIENT_LIBRARIES})
+ set(KERBEROS_LIBRARIES ${KERBEROS_LIBRARIES} ${OPENCV_LIBRARIES} ${RESTCLIENT_LIBRARIES} pthread)
# -------------------------------------------
diff --git a/src/kerberos/Kerberos.cpp b/src/kerberos/Kerberos.cpp
index 90ef66d..5347d90 100644
--- a/src/kerberos/Kerberos.cpp
+++ b/src/kerberos/Kerberos.cpp
@@ -9,22 +9,12 @@ namespace kerberos
setParameters(parameters);
- // ----------------
- // Initialize mutex
-
- pthread_mutex_init(&m_streamLock, NULL);
-
// ---------------------
// Initialize kerberos
std::string configuration = (helper::getValueByKey(parameters, "config")) ?: CONFIGURATION_PATH;
configure(configuration);
- // ------------------
- // Open the stream
-
- startStreamThread();
-
// ------------------
// Open the io thread
@@ -95,7 +85,6 @@ namespace kerberos
// Shift images
m_images = capture->shiftImage();
- usleep(250*1000);
}
}
@@ -193,14 +182,20 @@ namespace kerberos
}
// ----------------------------------
- // Configure capture device + thread
+ // Configure capture device + stream
void Kerberos::configureCapture(StringMap & settings)
{
- // ---------------------------
- // Initialize capture device
+ // -----------------------
+ // Stop stream and capture
+
+ if(stream != 0)
+ {
+ LINFO << "Stopping streaming";
+ stopStreamThread();
+ delete stream;
+ }
- pthread_mutex_lock(&m_streamLock);
if(capture != 0)
{
LINFO << "Stopping capture device";
@@ -209,11 +204,20 @@ namespace kerberos
delete capture;
}
+ // ---------------------------
+ // Initialize capture device
+
LINFO << "Starting capture device: " + settings.at("capture");
capture = Factory::getInstance()->create(settings.at("capture"));
capture->setup(settings);
capture->startGrabThread();
- pthread_mutex_unlock(&m_streamLock);
+
+ // ------------------
+ // Initialize stream
+
+ stream = new Stream();
+ stream->configureStream(settings);
+ startStreamThread();
}
// ----------------------------------
@@ -249,7 +253,6 @@ namespace kerberos
{
try
{
- pthread_mutex_lock(&kerberos->m_streamLock);
kerberos->stream->connect();
Image image = kerberos->capture->retrieve();
@@ -258,14 +261,9 @@ namespace kerberos
image.rotate(kerberos->capture->m_angle);
}
kerberos->stream->write(image);
-
- pthread_mutex_unlock(&kerberos->m_streamLock);
- usleep(800*100);
- }
- catch(cv::Exception & ex)
- {
- pthread_mutex_unlock(&kerberos->m_streamLock);
+ usleep(200*1000); // sleep 200ms
}
+ catch(cv::Exception & ex){}
}
}
@@ -274,11 +272,10 @@ namespace kerberos
// ------------------------------------------------
// Start a new thread that streams MJPEG's continuously.
- if(stream == 0)
+ if(stream != 0)
{
- int port = 8888;
- LINFO << "Starting stream on port " + helper::to_string(port);
- stream = new Stream(port);
+ //if stream object just exists try to open configured stream port
+ stream->open();
}
pthread_create(&m_streamThread, NULL, streamContinuously, this);
diff --git a/src/kerberos/capture/Image.cpp b/src/kerberos/capture/Image.cpp
index 83c5cb4..b46016a 100644
--- a/src/kerberos/capture/Image.cpp
+++ b/src/kerberos/capture/Image.cpp
@@ -156,6 +156,18 @@ namespace kerberos
}
}
+ void Image::dilate(const Image & kernel)
+ {
+ try
+ {
+ cv::dilate(m_image, m_image, kernel.m_image);
+ }
+ catch(cv::Exception & ex)
+ {
+ throw OpenCVException(ex.msg.c_str());
+ }
+ }
+
int Image::brightness()
{
try
diff --git a/src/kerberos/capture/RaspiCamera.cpp b/src/kerberos/capture/RaspiCamera.cpp
index f9f64bd..32f146a 100644
--- a/src/kerberos/capture/RaspiCamera.cpp
+++ b/src/kerberos/capture/RaspiCamera.cpp
@@ -115,6 +115,13 @@ namespace kerberos
void RaspiCamera::open()
{
m_camera->open();
+
+ // ----------
+ // sleep a second, to be sure the camera is enabled properly.
+ // when reconfiguring the machinery, the process get blocked, if
+ // the raspicamera isn't enabled properly.
+
+ usleep(1000*1000);
}
void RaspiCamera::close()
diff --git a/src/kerberos/capture/Stream.cpp b/src/kerberos/capture/Stream.cpp
index d6ae413..ba9368a 100644
--- a/src/kerberos/capture/Stream.cpp
+++ b/src/kerberos/capture/Stream.cpp
@@ -2,6 +2,32 @@
namespace kerberos
{
+
+ // ----------------------------------
+ // Configure stream thread settings
+
+ void Stream::configureStream(StringMap & settings)
+ {
+ //read port from settings
+ int enabled = (settings.at("streams.Mjpg.enabled") == "true");
+ int port = std::atoi(settings.at("streams.Mjpg.streamPort").c_str());
+ int quality = std::atoi(settings.at("streams.Mjpg.quality").c_str());
+
+ //use port up to well known ports range
+ if(port >= 1024)
+ {
+ //TODO: here it would be nice to check if port is valid and free
+ m_enabled = enabled;
+ m_streamPort = port;
+ m_quality = quality;
+ }
+ else
+ {
+ LERROR << "Settings: can't use invalid port";
+ //TODO: manage invalid port error
+ }
+ }
+
bool Stream::release()
{
for(int i = 0; i < clients.size(); i++)
@@ -15,41 +41,57 @@ namespace kerberos
if (sock != INVALID_SOCKET)
{
shutdown(sock, 2);
+ close(sock);
}
sock = (INVALID_SOCKET);
+
+ LINFO << "Stream: Succesfully closed streaming";
return false;
}
- bool Stream::open(int port)
+ bool Stream::open()
{
- sock = socket(AF_INET, SOCK_STREAM, IPPROTO_TCP);
-
- int reuse = 1;
- setsockopt(sock, SOL_SOCKET, SO_REUSEADDR, (const char*)&reuse, sizeof(reuse));
-
- SOCKADDR_IN address;
- address.sin_addr.s_addr = INADDR_ANY;
- address.sin_family = AF_INET;
- address.sin_port = htons(port);
-
- while(bind(sock, (SOCKADDR*) &address, sizeof(SOCKADDR_IN)) == SOCKET_ERROR)
+ if(m_enabled)
{
- LERROR << "Stream: couldn't bind sock";
- release();
- usleep(1000*10000);
- sock = ::socket(AF_INET, SOCK_STREAM, IPPROTO_TCP);
- }
-
- while(listen(sock, 2) == SOCKET_ERROR)
- {
- LERROR << "Stream: couldn't listen on sock";
- usleep(1000*10000);
+ sock = socket(AF_INET, SOCK_STREAM, IPPROTO_TCP);
+
+ int reuse = 1;
+ setsockopt(sock, SOL_SOCKET, SO_REUSEADDR, (const char*)&reuse, sizeof(reuse));
+
+ struct timeval timeout;
+ timeout.tv_sec = 3;
+ timeout.tv_usec = 0;
+ setsockopt(sock, SOL_SOCKET, SO_SNDTIMEO, (struct timeval *)&timeout,sizeof(timeout));
+
+ SOCKADDR_IN address;
+ address.sin_addr.s_addr = INADDR_ANY;
+ address.sin_family = AF_INET;
+ address.sin_port = htons(m_streamPort);
+
+ while(bind(sock, (SOCKADDR*) &address, sizeof(SOCKADDR_IN)) == SOCKET_ERROR)
+ {
+ LERROR << "Stream: couldn't bind sock";
+ release();
+ usleep(1000*10000);
+ sock = ::socket(AF_INET, SOCK_STREAM, IPPROTO_TCP);
+ }
+
+ while(listen(sock, 2) == SOCKET_ERROR)
+ {
+ LERROR << "Stream: couldn't listen on sock";
+ usleep(1000*10000);
+ }
+
+ FD_SET(sock, &master);
+
+
+ LINFO << "Stream: Configured stream on port " << helper::to_string(m_streamPort) << " with quality: " << helper::to_string(m_quality);
+
+ return true;
}
-
- FD_SET(sock, &master);
-
- return true;
+
+ return false;
}
bool Stream::isOpened()
@@ -60,7 +102,7 @@ namespace kerberos
bool Stream::connect()
{
fd_set rread = master;
- struct timeval to = {0,timeout};
+ struct timeval to = {0,m_timeout};
SOCKET maxfd = sock+1;
if(select( maxfd, &rread, NULL, NULL, &to ) <= 0)
@@ -69,15 +111,20 @@ namespace kerberos
int addrlen = sizeof(SOCKADDR);
SOCKADDR_IN address = {0};
SOCKET client = accept(sock, (SOCKADDR*)&address, (socklen_t*) &addrlen);
-
+
if (client == SOCKET_ERROR)
{
LERROR << "Stream: couldn't accept connection on sock";
- LINFO << "Stream: reopening master sock";
+ LERROR << "Stream: reopening master sock";
release();
- open(8888);
+ open();
return false;
}
+
+ struct timeval timeout;
+ timeout.tv_sec = 3;
+ timeout.tv_usec = 0;
+ setsockopt(client, SOL_SOCKET, SO_RCVTIMEO, (struct timeval *)&timeout, sizeof(timeout));
maxfd=(maxfd>client?maxfd:client);
FD_SET( client, &master );
@@ -90,6 +137,8 @@ namespace kerberos
"Pragma: no-cache\r\n"
"Content-Type: multipart/x-mixed-replace; boundary=mjpegstream\r\n"
"\r\n",0);
+
+ LINFO << "Stream: opening socket for new client";
clients.push_back(client);
packetsSend[client] = 0;
@@ -112,7 +161,7 @@ namespace kerberos
std::vectoroutbuf;
std::vector params;
params.push_back(cv::IMWRITE_JPEG_QUALITY);
- params.push_back(quality);
+ params.push_back(m_quality);
cv::imencode(".jpg", frame, outbuf, params);
int outlen = outbuf.size();
@@ -154,4 +203,4 @@ namespace kerberos
}
catch(cv::Exception & ex){}
}
-}
\ No newline at end of file
+}
diff --git a/src/kerberos/capture/VideoCapture.cpp b/src/kerberos/capture/VideoCapture.cpp
new file mode 100644
index 0000000..1af0753
--- /dev/null
+++ b/src/kerberos/capture/VideoCapture.cpp
@@ -0,0 +1,154 @@
+#include "capture/VideoCapture.h"
+
+namespace kerberos
+{
+ void VideoCapture::setup(kerberos::StringMap &settings)
+ {
+ int width = std::atoi(settings.at("captures.VideoCapture.frameWidth").c_str());
+ int height = std::atoi(settings.at("captures.VideoCapture.frameHeight").c_str());
+ std::string path = settings.at("captures.VideoCapture.path");
+ int angle = std::atoi(settings.at("captures.VideoCapture.angle").c_str());
+ int delay = std::atoi(settings.at("captures.VideoCapture.delay").c_str());
+
+ // Save width and height in settings
+ Capture::setup(settings, width, height);
+ setImageSize(width, height);
+ setRotation(angle);
+ setDelay(delay);
+ setPath(path);
+
+ // Initialize video
+ open();
+ }
+
+ VideoCapture::VideoCapture(int width, int height)
+ {
+ try
+ {
+ m_video = new cv::VideoCapture();
+ setImageSize(width, height);
+ }
+ catch(cv::Exception & ex)
+ {
+ throw OpenCVException(ex.msg.c_str());
+ }
+ };
+
+ void VideoCapture::grab()
+ {
+ try
+ {
+ pthread_mutex_lock(&m_lock);
+ // A video doesn't need a grabber.
+ // m_video->grab();
+ pthread_mutex_unlock(&m_lock);
+ }
+ catch(cv::Exception & ex)
+ {
+ pthread_mutex_unlock(&m_lock);
+ pthread_mutex_destroy(&m_lock);
+ throw OpenCVException(ex.msg.c_str());
+ }
+ }
+
+ Image VideoCapture::retrieve()
+ {
+ try
+ {
+ Image image;
+ pthread_mutex_lock(&m_lock);
+ m_video->retrieve(image.getImage());
+ pthread_mutex_unlock(&m_lock);
+ return image;
+ }
+ catch(cv::Exception & ex)
+ {
+ pthread_mutex_unlock(&m_lock);
+ pthread_mutex_destroy(&m_lock);
+ throw OpenCVException(ex.msg.c_str());
+ }
+ }
+
+ Image * VideoCapture::takeImage()
+ {
+ // Take image
+ try
+ {
+ // Delay camera for some time..
+ usleep(m_delay*1000);
+ cv::waitKey(10); // this is needed for video files.
+
+ // Get image from camera
+ Image * image = new Image();
+
+ pthread_mutex_lock(&m_lock);
+ m_video->grab();
+ m_video->retrieve(image->getImage());
+ pthread_mutex_unlock(&m_lock);
+
+ // Check if need to rotate the image
+ image->rotate(m_angle);
+
+ return image;
+ }
+ catch(cv::Exception & ex)
+ {
+ pthread_mutex_unlock(&m_lock);
+ throw OpenCVException(ex.msg.c_str());
+ }
+ }
+
+
+ void VideoCapture::setImageSize(int width, int height)
+ {
+ Capture::setImageSize(width, height);
+ try
+ {
+ m_video->set(CV_CAP_PROP_FRAME_WIDTH, m_frameWidth);
+ m_video->set(CV_CAP_PROP_FRAME_HEIGHT, m_frameHeight);
+ }
+ catch(cv::Exception & ex)
+ {
+ throw OpenCVException(ex.msg.c_str());
+ }
+ }
+
+ void VideoCapture::open()
+ {
+ try
+ {
+ if(!isOpened())
+ {
+ m_video->release();
+ m_video->open(getPath());
+ //m_video->set(CV_CAP_PROP_POS_FRAMES, 1850);
+ setImageSize(m_frameWidth, m_frameHeight);
+ }
+ }
+ catch(cv::Exception & ex)
+ {
+ throw OpenCVException(ex.msg.c_str());
+ }
+ }
+
+ void VideoCapture::close()
+ {
+ try
+ {
+ pthread_mutex_unlock(&m_lock);
+ pthread_mutex_destroy(&m_lock);
+ m_video->release();
+ }
+ catch(cv::Exception & ex)
+ {
+ throw OpenCVException(ex.msg.c_str());
+ }
+ }
+
+ void VideoCapture::update(){}
+
+ bool VideoCapture::isOpened()
+ {
+ return m_video->isOpened();
+ }
+}
\ No newline at end of file
diff --git a/src/kerberos/machinery/algorithm/BackgroundSubtraction.cpp b/src/kerberos/machinery/algorithm/BackgroundSubtraction.cpp
new file mode 100644
index 0000000..51fde6f
--- /dev/null
+++ b/src/kerberos/machinery/algorithm/BackgroundSubtraction.cpp
@@ -0,0 +1,68 @@
+#include "machinery/algorithm/BackgroundSubtraction.h"
+
+namespace kerberos
+{
+ void BackgroundSubtraction::setup(const StringMap & settings)
+ {
+ Algorithm::setup(settings);
+ int erode = std::atoi(settings.at("algorithms.BackgroundSubtraction.erode").c_str());
+ int dilate = std::atoi(settings.at("algorithms.BackgroundSubtraction.dilate").c_str());
+ setErodeKernel(erode, erode);
+ setDilateKernel(dilate, dilate);
+
+ m_subtractor = cv::createBackgroundSubtractorMOG2();
+
+ std::string shadows = settings.at("algorithms.BackgroundSubtraction.shadows");
+ int history = std::atoi(settings.at("algorithms.BackgroundSubtraction.history").c_str());
+ int nmixtures = std::atoi(settings.at("algorithms.BackgroundSubtraction.nmixtures").c_str());
+ double ratio = std::atof(settings.at("algorithms.BackgroundSubtraction.ratio").c_str());
+ int threshold = std::atoi(settings.at("algorithms.BackgroundSubtraction.threshold").c_str());
+ m_subtractor->setDetectShadows((shadows == "true"));
+ m_subtractor->setHistory(history);
+ m_subtractor->setNMixtures(nmixtures);
+ m_subtractor->setBackgroundRatio(ratio);
+ m_subtractor->setVarThreshold(threshold);
+ m_subtractor->setVarThresholdGen(threshold);
+ }
+
+ // ---------------------------------------------
+ // Convert all images (except last one) to gray
+
+ void BackgroundSubtraction::initialize(ImageVector & images)
+ {
+ for(int i = 0; i < images.size()-1; i++)
+ {
+ m_subtractor->apply(images[i]->getImage(), m_backgroud.getImage());
+ }
+ }
+
+ Image BackgroundSubtraction::evaluate(ImageVector & images, JSON & data)
+ {
+ // -----------
+ // Calculate
+
+ m_subtractor->apply(images[2]->getImage(), m_backgroud.getImage());
+
+ cv::Mat brackgroundmodel;
+ m_subtractor->getBackgroundImage(brackgroundmodel);
+ m_backgroud.erode(m_erodeKernel);
+ m_backgroud.dilate(m_dilateKernel);
+
+ return m_backgroud;
+ }
+
+ void BackgroundSubtraction::setErodeKernel(int width, int height)
+ {
+ m_erodeKernel.setImage(Image::createKernel(width, height));
+ }
+
+ void BackgroundSubtraction::setDilateKernel(int width, int height)
+ {
+ m_dilateKernel.setImage(Image::createKernel(width, height));
+ }
+
+ void BackgroundSubtraction::setThreshold(int threshold)
+ {
+ m_threshold = threshold;
+ }
+}
\ No newline at end of file
diff --git a/src/kerberos/machinery/heuristic/Counter.cpp b/src/kerberos/machinery/heuristic/Counter.cpp
new file mode 100644
index 0000000..78aaddf
--- /dev/null
+++ b/src/kerberos/machinery/heuristic/Counter.cpp
@@ -0,0 +1,289 @@
+#include "machinery/heuristic/Counter.h"
+
+namespace kerberos
+{
+ void Counter::setup(const StringMap & settings)
+ {
+ std::vector coordinates;
+
+ // Set incoming coordinates
+ helper::tokenize(settings.at("heuristics.Counter.markers"), coordinates, "|");
+ for(int i = 0; i < 2; i++)
+ {
+ std::vector fromAndTo;
+ helper::tokenize(coordinates[i], fromAndTo, ",");
+ int from = std::atoi(fromAndTo[0].c_str());
+ int to = std::atoi(fromAndTo[1].c_str());
+ cv::Point p(from ,to);
+ m_in.push_back(p);
+ }
+
+ // Set outgoing coordinates
+
+ for(int i = 2; i < 4; i++)
+ {
+ std::vector fromAndTo;
+ helper::tokenize(coordinates[i], fromAndTo, ",");
+ int from = std::atoi(fromAndTo[0].c_str());
+ int to = std::atoi(fromAndTo[1].c_str());
+ cv::Point p(from ,to);
+ m_out.push_back(p);
+ }
+
+ setMinimumChanges(std::atoi(settings.at("heuristics.Counter.minimumChanges").c_str()));
+ setNoMotionDelayTime(std::atoi(settings.at("heuristics.Counter.noMotionDelayTime").c_str()));
+ setAppearance(std::atoi(settings.at("heuristics.Counter.appearance").c_str()));
+ setMaxDistance(std::atoi(settings.at("heuristics.Counter.maxDistance").c_str()));
+ setMinArea(std::atoi(settings.at("heuristics.Counter.minArea").c_str()));
+ setOnlyTrueWhenCounted((settings.at("heuristics.Counter.onlyTrueWhenCounted") == "true"));
+ }
+
+ bool Counter::intersection(cv::Point2f o1, cv::Point2f p1, cv::Point2f o2, cv::Point2f p2, cv::Point2f &r)
+ {
+ cv::Point2f x = o2 - o1;
+ cv::Point2f d1 = p1 - o1;
+ cv::Point2f d2 = p2 - o2;
+
+ float cross = d1.x * d2.y - d1.y * d2.x;
+
+ if (std::abs(cross) < 1e-8)
+ {
+ return false;
+ }
+
+ double t1 = (x.x * d2.y - x.y * d2.x)/cross;
+ r = o1 + d1 * t1;
+
+ if((r.x <= MAX(o1.x, p1.x) && r.x >= MIN(o1.x, p1.x) && r.y <= MAX(o1.y, p1.y) && r.y >= MIN(o1.y, p1.y)) &&
+ (r.x <= MAX(o2.x, p2.x) && r.x >= MIN(o2.x, p2.x) && r.y <= MAX(o2.y, p2.y) && r.y >= MIN(o2.y, p2.y)))
+ {
+ return true;
+ }
+
+ return false;
+ }
+
+ bool Counter::isValid(const Image & evaluation, const ImageVector & images, JSON & data)
+ {
+ int numberOfChanges;
+ Rectangle rectangle;
+ numberOfChanges = data["numberOfChanges"].GetInt();
+
+ int incoming = 0;
+ int outgoing = 0;
+
+ kerberos::Image image = evaluation;
+ cv::Mat img = image.getImage();
+ cv::dilate(img, img, cv::getStructuringElement(cv::MORPH_ELLIPSE, cv::Size(25,25)));
+ cv::erode(img, img, cv::getStructuringElement(cv::MORPH_RECT, cv::Size(10,10)));
+
+ cv::Point & outTop = m_out[0];
+ cv::Point & outBottom = m_out[1];
+
+ cv::Point & inTop = m_in[0];
+ cv::Point & inBottom = m_in[1];
+
+ std::vector > contours;
+ std::vector hierarchy;
+ cv::findContours(image.getImage(), contours, hierarchy, CV_RETR_TREE, CV_CHAIN_APPROX_NONE);
+
+ int numberOfContours= 0;
+ for(int i = 0; i < m_features.size(); i++)
+ {
+ int mostRecent = m_features[i].size() - 1;
+ m_features[i][mostRecent].decreaseAppearance();
+ }
+
+ // Find new tracks
+ if(contours.size() > 0)
+ {
+ for( int i = 0; i< contours.size(); i++ )
+ {
+ cv::Moments moments = cv::moments(contours[i]);
+ int x = moments.m10/moments.m00;
+ int y = moments.m01/moments.m00;
+ int area = cv::contourArea(contours[i]);
+
+ if(area < m_minArea) continue;
+
+ Feature current(x, y, area, m_appearance);
+ int best = -1;
+ double bestValue = 99999999;
+ double bestArea = 99999999;
+
+ for(int j = 0; j < m_features.size(); j++)
+ {
+ int mostRecent = m_features[j].size() - 1;
+ double distance = current.distance(m_features[j][mostRecent]);
+ double areaDistance = current.areaDistance(m_features[j][mostRecent]);
+
+ if(distance < m_maxDistance && distance < bestValue + m_maxDistance/2)
+ {
+ if(areaDistance < bestArea)
+ {
+ best = j;
+ bestValue = distance;
+ }
+ }
+ }
+
+ if(best == -1)
+ {
+ std::vector tracking;
+ tracking.push_back(current);
+ m_features.push_back(tracking);
+ }
+ else
+ {
+ m_features[best].push_back(current);
+ }
+
+ numberOfContours++;
+ }
+ }
+
+ // Remove old tracks
+ std::vector >::iterator it = m_features.begin();
+ while(it != m_features.end())
+ {
+ Feature & back = it->back();
+ back.decreaseAppearance();
+
+ if(back.getAppearance() < 0)
+ {
+ it = m_features.erase(it);
+ }
+ else
+ {
+ it++;
+ }
+ }
+
+ // Check existing tracks if they crossed the line
+ it = m_features.begin();
+ while(it != m_features.end())
+ {
+ if(it->size() > 1)
+ {
+ for(int j = 1; j < it->size(); j++)
+ {
+ cv::Point2f prev((*it)[j-1].getX(),(*it)[j-1].getY());
+ cv::Point2f curr((*it)[j].getX(),(*it)[j].getY());
+ }
+
+ // Check if cross line
+ cv::Point2f start((*it)[0].getX(),(*it)[0].getY());
+ cv::Point2f end((*it)[it->size()-1].getX(),(*it)[it->size()-1].getY());
+
+ // Check if interset in line
+ cv::Point2f intersectionPointOutgoing;
+ bool inLine = false;
+ if(intersection(start, end, outTop, outBottom, intersectionPointOutgoing))
+ {
+ inLine = true;
+ }
+ // Check if interset out line
+ cv::Point2f intersectionPointIncoming;
+ bool outLine = false;
+ if(intersection(start, end, inTop, inBottom, intersectionPointIncoming))
+ {
+ outLine = true;
+ }
+
+ // Check if interesected both
+ Direction xDirection = parallell;
+ Direction yDirection = parallell;
+
+ if(inLine && outLine)
+ {
+ // What is the direction (incoming our outgoing?)
+ if(start.x - end.x < 0)
+ {
+ xDirection = right;
+ }
+ else if(start.x - end.x > 0)
+ {
+ xDirection = left;
+ }
+ if(start.y - end.y < 0)
+ {
+ yDirection = bottom;
+ }
+ else if(start.y - end.y > 0)
+ {
+ yDirection = top;
+ }
+
+ // Check which intersection point comes first
+ if(xDirection != parallell)
+ {
+ if(xDirection == left)
+ {
+ // Check which intersection point is most right;
+ if(intersectionPointIncoming.x > intersectionPointOutgoing.x)
+ {
+ incoming++;
+ }
+ else
+ {
+ outgoing++;
+ }
+ }
+ else if(xDirection == right)
+ {
+ // Check which intersection point is most right;
+ if(intersectionPointIncoming.x < intersectionPointOutgoing.x)
+ {
+ incoming++;
+ }
+ else
+ {
+ outgoing++;
+ }
+ }
+ }
+ else
+ {
+
+ }
+
+ it = m_features.erase(it);
+ }
+ else
+ {
+ it++;
+ }
+ }
+ else
+ {
+ it++;
+ }
+ }
+
+ if(numberOfChanges >= m_minimumChanges)
+ {
+ JSON::AllocatorType& allocator = data.GetAllocator();
+ data.AddMember("incoming", incoming, allocator);
+ data.AddMember("outgoing", outgoing, allocator);
+
+ if(m_onlyTrueWhenCounted)
+ {
+ if(incoming > 0 || outgoing > 0)
+ {
+ BINFO << "Counter: in (" << helper::to_string(incoming) << "), out (" << helper::to_string(outgoing) << ")";
+ return true;
+ }
+ }
+ else
+ {
+ return true;
+ }
+ }
+ else
+ {
+ usleep(m_noMotionDelayTime*1000);
+ }
+
+ return false;
+ }
+}
diff --git a/src/kerberos/machinery/io/IoDisk.cpp b/src/kerberos/machinery/io/IoDisk.cpp
index cbf72b0..13d0b46 100644
--- a/src/kerberos/machinery/io/IoDisk.cpp
+++ b/src/kerberos/machinery/io/IoDisk.cpp
@@ -11,6 +11,19 @@ namespace kerberos
std::string instanceName = settings.at("name");
setInstanceName(instanceName);
+
+ // --------------------------
+ // Check if need to draw timestamp
+
+ bool drawTimestamp = (settings.at("ios.Disk.markWithTimestamp") == "true");
+ setDrawTimestamp(drawTimestamp);
+ cv::Scalar color = getColor(settings.at("ios.Disk.timestampColor"));
+ setTimestampColor(color);
+
+ std::string timezone = settings.at("timezone");
+ std::replace(timezone.begin(), timezone.end(), '-', '/');
+ std::replace(timezone.begin(), timezone.end(), '$', '_');
+ setTimezone(timezone);
// -------------------------------------------------------------
// Filemanager is mapped to a directory and is used by an image
@@ -19,6 +32,19 @@ namespace kerberos
setFileFormat(settings.at("ios.Disk.fileFormat"));
m_fileManager.setBaseDirectory(settings.at("ios.Disk.directory"));
}
+
+ cv::Scalar IoDisk::getColor(const std::string name)
+ {
+ std::map m_colors;
+
+ m_colors["white"] = cv::Scalar(255,255,255);
+ m_colors["black"] = cv::Scalar(0,0,0);
+ m_colors["red"] = cv::Scalar(0,0,255);
+ m_colors["green"] = cv::Scalar(0,255,0);
+ m_colors["blue"] = cv::Scalar(255,0,0);
+
+ return m_colors.at(name);
+ }
std::string IoDisk::buildPath(std::string pathToImage)
{
@@ -31,6 +57,29 @@ namespace kerberos
return pathToImage;
}
+ void IoDisk::drawDateOnImage(Image & image, std::string timestamp)
+ {
+ if(m_drawTimestamp)
+ {
+ struct tm tstruct;
+ char buf[80];
+
+ time_t now = std::atoi(timestamp.c_str());
+
+ char * timeformat = "%d-%m-%Y %X";
+ if(m_timezone != "")
+ {
+ setenv("TZ", m_timezone.c_str(), 1);
+ tzset();
+ }
+
+ tstruct = *localtime(&now);
+ strftime(buf, sizeof(buf), timeformat, &tstruct);
+
+ cv::putText(image.getImage(), buf, cv::Point(10,30), cv::FONT_HERSHEY_SIMPLEX, 0.5, getTimestampColor());
+ }
+ }
+
bool IoDisk::save(Image & image)
{
// ----------------------------------------
@@ -46,6 +95,7 @@ namespace kerberos
std::string timestamp = kerberos::helper::getTimestamp();
kerberos::helper::replace(pathToImage, "timestamp", timestamp);
+ drawDateOnImage(image, timestamp);
std::string microseconds = kerberos::helper::getMicroseconds();
std::string size = kerberos::helper::to_string((int)microseconds.length());
@@ -123,8 +173,13 @@ namespace kerberos
path.SetString(pathToImage.c_str(), allocator);
data.AddMember("pathToImage", path, allocator);
- // ---------------------------------------------------------------------
- // Save original version & generate unique timestamp for current image
+ // ------------------
+ // Draw date on image
+
+ drawDateOnImage(image, data["timestamp"].GetString());
+
+ // -------------------------
+ // Save original version
BINFO << "IoDisk: saving image " + pathToImage;
return m_fileManager.save(image, pathToImage);