Compare commits

..

No commits in common. "01a11741c780113becbea2ce5cec8a27bbe5a1bc" and "42814ab8ca10d4ce098531524f38d7a2df5d24fa" have entirely different histories.

12 changed files with 313 additions and 71 deletions

View File

@ -3,5 +3,5 @@ project( MotionWatch )
find_package( OpenCV REQUIRED ) find_package( OpenCV REQUIRED )
set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -std=c++17 -pthread") set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -std=c++17 -pthread")
include_directories( ${OpenCV_INCLUDE_DIRS} ) include_directories( ${OpenCV_INCLUDE_DIRS} )
add_executable( mow src/main.cpp src/common.cpp src/mo_detect.cpp ) add_executable( mow src/main.cpp src/common.cpp src/mo_detect.cpp src/obj_detect.cpp )
target_link_libraries( mow ${OpenCV_LIBS} ) target_link_libraries( mow ${OpenCV_LIBS} )

View File

@ -45,15 +45,20 @@ buff_dir = /tmp/ramdisk/cam_name
# recommend to use a ramdisk tempfs for this since this directory is used # recommend to use a ramdisk tempfs for this since this directory is used
# for lots of writes. # for lots of writes.
# #
consec_threshold = 512 color_threshold = 8
# motion is detected by comparing each frame in the camera feed for # the color levels in each pixel of the camera stream can range from 0-255.
# differences in the pixels. this value determine how many consecutive # in an ideal world the color differences in between frames should be 0 if
# pixels need to different or how large the suspect object in motion # there is no motion but must cameras can't do this. the threshold value
# needs to be. # here is used to filter if the pixels are truly different.
# #
block_threshold = 1024 block_threshold = 3456
# this value tells the application how many "lines" of pixels need to # this application detects motion by loading frames from the camera and
# exceed consec_threshold before being considered motion. # then compare the pixels of each back to back frame for any significant
# differences between the pixels based on color_threshold. it loads the
# pixels of each frame in blocks. the size of the blocks are adjustable
# below. it counts how many pixels are different in the block and this is
# used to tell if the footage has motion if the different pixel count
# exceeds it.
# #
block_x = 64 block_x = 64
# this is the x coordinate size or horizontal size of a block of pixels # this is the x coordinate size or horizontal size of a block of pixels

80
etc/classes.txt Normal file
View File

@ -0,0 +1,80 @@
person
bicycle
car
motorbike
aeroplane
bus
train
truck
boat
traffic light
fire hydrant
stop sign
parking meter
bench
bird
cat
dog
horse
sheep
cow
elephant
bear
zebra
giraffe
backpack
umbrella
handbag
tie
suitcase
frisbee
skis
snowboard
sports ball
kite
baseball bat
baseball glove
skateboard
surfboard
tennis racket
bottle
wine glass
cup
fork
knife
spoon
bowl
banana
apple
sandwich
orange
broccoli
carrot
hot dog
pizza
donut
cake
chair
sofa
pottedplant
bed
diningtable
toilet
tvmonitor
laptop
mouse
remote
keyboard
cell phone
microwave
oven
toaster
sink
refrigerator
book
clock
vase
scissors
teddy bear
hair drier
toothbrush

BIN
etc/yolov5s.onnx Normal file

Binary file not shown.

View File

@ -1,3 +1,6 @@
#!/bin/sh #!/bin/sh
mkdir -p /etc/mow
cp ./etc/yolov5s.onnx /etc/mow/yolov5s.onnx
cp ./etc/classes.txt /etc/mow/classes.txt
cp ./.build-mow/mow /usr/bin/mow cp ./.build-mow/mow /usr/bin/mow

View File

@ -140,6 +140,18 @@ string genDstFile(const string &dirOut, const char *fmt, const string &ext)
return cleanDir(dirOut) + string("/") + genTimeStr(fmt) + ext; return cleanDir(dirOut) + string("/") + genTimeStr(fmt) + ext;
} }
string genTmpFile(const string &dirOut, const string &ext, shared_t *share)
{
createDirTree(cleanDir(dirOut));
if (share->tmpId == 9999999)
{
share->tmpId = 0;
}
return cleanDir(dirOut) + string("/") + to_string(share->tmpId++) + ext;
}
Mat toGray(const Mat &src) Mat toGray(const Mat &src)
{ {
Mat ret; Mat ret;
@ -169,6 +181,21 @@ void rdLine(const string &param, const string &line, int *value)
} }
} }
vector<string> loadClassList()
{
vector<string> ret;
ifstream ifs("/etc/mow/classes.txt");
string line;
while (getline(ifs, line))
{
ret.push_back(line);
}
return ret;
}
bool rdConf(shared_t *share) bool rdConf(shared_t *share)
{ {
ifstream varFile(share->conf.c_str()); ifstream varFile(share->conf.c_str());
@ -188,15 +215,20 @@ bool rdConf(shared_t *share)
share->postCmd.clear(); share->postCmd.clear();
share->buffDir.clear(); share->buffDir.clear();
share->consecThresh = 512; share->colorThresh = 5;
share->secs = 60; share->secs = 60;
share->blockX = 32; share->blockX = 32;
share->blockY = 32; share->blockY = 32;
share->blockThresh = 1024; share->blockThresh = 900;
share->maxDays = 5; share->maxDays = 5;
share->vidExt = "mp4"; share->vidExt = "mp4";
share->recLoopWait = false; share->recLoopWait = false;
share->skipCmd = false; share->skipCmd = false;
share->network = dnn::readNet("/etc/mow/yolov5s.onnx");
share->classNames = loadClassList();
share->network.setPreferableBackend(dnn::DNN_BACKEND_OPENCV);
share->network.setPreferableTarget(dnn::DNN_TARGET_CPU);
do do
{ {
@ -207,7 +239,7 @@ bool rdConf(shared_t *share)
rdLine("recording_stream = ", line, &share->recordUrl); rdLine("recording_stream = ", line, &share->recordUrl);
rdLine("output_dir = ", line, &share->outDir); rdLine("output_dir = ", line, &share->outDir);
rdLine("post_cmd = ", line, &share->postCmd); rdLine("post_cmd = ", line, &share->postCmd);
rdLine("consec_threshold = ", line, &share->consecThresh); rdLine("color_threshold = ", line, &share->colorThresh);
rdLine("duration = ", line, &share->secs); rdLine("duration = ", line, &share->secs);
rdLine("buff_dir = ", line, &share->buffDir); rdLine("buff_dir = ", line, &share->buffDir);
rdLine("block_x = ", line, &share->blockX); rdLine("block_x = ", line, &share->blockX);
@ -234,9 +266,6 @@ bool rdConf(shared_t *share)
new thread(enforceMaxDays, share); new thread(enforceMaxDays, share);
createDirTree(cleanDir(share->buffDir));
system(string("touch " + cleanDir(share->buffDir) + "/stat").c_str());
share->retCode = 0; share->retCode = 0;
} }
@ -307,10 +336,10 @@ string parseForParam(const string &arg, int argc, char** argv, bool argOnly)
void statOut(shared_t *share) void statOut(shared_t *share)
{ {
system(string("touch " + cleanDir(share->buffDir) + "/stat").c_str()); createDirTree(cleanDir(share->buffDir));
auto path = string(cleanDir(share->buffDir) + "/stat"); auto path = string(cleanDir(share->buffDir) + "/stat");
auto fd = open(path.c_str(), fstream::out | fstream::trunc); auto fd = open(path.c_str(), O_WRONLY);
write(fd, share->stat.c_str(), share->stat.size() + 1); write(fd, share->stat.c_str(), share->stat.size() + 1);
close(fd); close(fd);

View File

@ -37,21 +37,27 @@ using namespace std;
using namespace std::filesystem; using namespace std::filesystem;
#define BUF_SZ 10 #define BUF_SZ 10
#define APP_VER "1.5" #define APP_VER "1.4.t7"
struct shared_t struct shared_t
{ {
vector<string> classNames;
dnn::Net network;
string stat; string stat;
string recordUrl; string recordUrl;
string outDir; string outDir;
string postCmd; string postCmd;
string conf; string conf;
string buffDir; string buffDir;
string concatTxtTmp;
string concatShTmp;
string createShTmp;
string vidExt; string vidExt;
bool init; bool init;
bool recLoopWait; bool recLoopWait;
bool skipCmd; bool skipCmd;
int consecThresh; int tmpId;
int colorThresh;
int secs; int secs;
int blockThresh; int blockThresh;
int blockX; int blockX;
@ -60,6 +66,7 @@ struct shared_t
int retCode; int retCode;
}; };
string genTmpFile(const string &dirOut, const string &ext, shared_t *share);
string genDstFile(const string &dirOut, const char *fmt, const string &ext); string genDstFile(const string &dirOut, const char *fmt, const string &ext);
string genTimeStr(const char *fmt); string genTimeStr(const char *fmt);
string cleanDir(const string &path); string cleanDir(const string &path);
@ -77,5 +84,6 @@ bool rdConf(shared_t *share);
bool capPair(Mat &prev, Mat &next, VideoCapture &capture, shared_t *share); bool capPair(Mat &prev, Mat &next, VideoCapture &capture, shared_t *share);
Mat toGray(const Mat &src); Mat toGray(const Mat &src);
vector<string> lsFilesInDir(const string &path, const string &ext); vector<string> lsFilesInDir(const string &path, const string &ext);
vector<string> loadClassList();
#endif // COMMON_H #endif // COMMON_H

View File

@ -11,6 +11,7 @@
// GNU General Public License for more details. // GNU General Public License for more details.
#include "mo_detect.h" #include "mo_detect.h"
#include "obj_detect.h"
void detectLoop(shared_t *share) void detectLoop(shared_t *share)
{ {
@ -22,11 +23,16 @@ void detectLoop(shared_t *share)
if ((bufFiles.size() >= 2) || (share->recLoopWait && !bufFiles.empty())) if ((bufFiles.size() >= 2) || (share->recLoopWait && !bufFiles.empty()))
{ {
Rect blockArea;
Mat blockImg;
auto fullPath = cleanDir(share->buffDir) + "/" + bufFiles[0]; auto fullPath = cleanDir(share->buffDir) + "/" + bufFiles[0];
share->stat.clear(); share->stat.clear();
if (moDetect(fullPath, share)) if (moDetect(fullPath, &blockArea, &blockImg, share))
{
if (objectInImage(blockImg, blockArea, share))
{ {
share->skipCmd = true; share->skipCmd = true;
@ -36,6 +42,11 @@ void detectLoop(shared_t *share)
{ {
remove(fullPath.c_str()); remove(fullPath.c_str());
} }
}
else
{
remove(fullPath.c_str());
}
statOut(share); statOut(share);
} }
@ -97,6 +108,7 @@ int main(int argc, char** argv)
else else
{ {
sharedRes.retCode = 0; sharedRes.retCode = 0;
sharedRes.tmpId = 0;
sharedRes.recLoopWait = false; sharedRes.recLoopWait = false;
sharedRes.skipCmd = false; sharedRes.skipCmd = false;
sharedRes.init = true; sharedRes.init = true;

View File

@ -14,30 +14,33 @@
bool pixDiff(const uchar &pixA, const uchar &pixB, shared_t *share) bool pixDiff(const uchar &pixA, const uchar &pixB, shared_t *share)
{ {
if (pixA > pixB) return true; auto diff = 0;
if (pixB > pixA) return true;
return false; if (pixA > pixB) diff = pixA - pixB;
if (pixB > pixA) diff = pixB - pixA;
if (diff < share->colorThresh)
{
diff = 0;
} }
void secDiff(const Mat &imgA, const Mat &imgB, int id, int rows, int cols, int rowOffs, int colOffs, vector<sec_t> *results, mutex *secMutex, shared_t *share) return diff != 0;
}
void secDiff(const Mat &imgA, const Mat &imgB, int rows, int cols, int rowOffs, int colOffs, vector<sec_t> *results, mutex *secMutex, shared_t *share)
{ {
auto diff = 0;
auto pnts = 0; auto pnts = 0;
for (auto y = rowOffs; y < (rowOffs + rows); y++) for (auto y = rowOffs; y < rows; y++)
{ {
for (auto x = colOffs; x < (colOffs + cols); x++) for (auto x = colOffs; x < cols; x++)
{ {
auto pixA = imgA.at<uchar>(Point(x, y)); auto pixA = imgA.at<uchar>(Point(x, y));
auto pixB = imgB.at<uchar>(Point(x, y)); auto pixB = imgB.at<uchar>(Point(x, y));
if (pixDiff(pixA, pixB, share)) pnts += 1; if (pixDiff(pixA, pixB, share))
else pnts = 0;
if (pnts >= share->consecThresh)
{ {
diff += 1; pnts += 1;
} }
} }
} }
@ -48,28 +51,27 @@ void secDiff(const Mat &imgA, const Mat &imgB, int id, int rows, int cols, int r
res.y = rowOffs; res.y = rowOffs;
res.xSize = cols; res.xSize = cols;
res.ySize = rows; res.ySize = rows;
res.pixDiff = diff; res.pixDiff = pnts;
res.id = id;
lock_guard<mutex> guard(*secMutex); lock_guard<mutex> guard(*secMutex);
results->push_back(res); results->push_back(res);
} }
bool imgDiff(const Mat &prev, const Mat &next, shared_t *share) bool imgDiff(const Mat &prev, const Mat &next, Rect *block, shared_t *share)
{ {
auto moInBlock = false;
vector<thread> threads; vector<thread> threads;
vector<sec_t> results; vector<sec_t> results;
mutex secMutex; mutex secMutex;
auto id = 0;
for (auto x = 0; x < prev.cols; x += share->blockX) for (auto x = 0; x < prev.cols; x += share->blockX)
{ {
// spawn all of the block motion detection threads. // spawn all of the block motion detection threads.
for (auto y = 0; y < prev.rows; y += share->blockY, id += 1) for (auto y = 0; y < prev.rows; y += share->blockY)
{ {
threads.push_back(thread(secDiff, prev, next, id, share->blockY, share->blockX, y, x, &results, &secMutex, share)); threads.push_back(thread(secDiff, prev, next, share->blockY, share->blockX, y, x, &results, &secMutex, share));
} }
} }
@ -89,12 +91,12 @@ bool imgDiff(const Mat &prev, const Mat &next, shared_t *share)
auto x = results[i].x; auto x = results[i].x;
auto y = results[i].y; auto y = results[i].y;
auto diff = results[i].pixDiff; auto diff = results[i].pixDiff;
auto id = results[i].id;
if (diff > 0) share->stat += string("block_thread:")
{ + " x=" + to_string(x)
share->stat += string("block_thread:") + " id=" + to_string(id) + " diff=" + to_string(diff) + "\n"; + " y=" + to_string(y)
} + " pixdiff=" + to_string(diff)
+ "\n";
if ((results[i].pixDiff >= share->blockThresh) && (results[i].pixDiff > maxPixDiff)) if ((results[i].pixDiff >= share->blockThresh) && (results[i].pixDiff > maxPixDiff))
{ {
@ -103,10 +105,25 @@ bool imgDiff(const Mat &prev, const Mat &next, shared_t *share)
} }
} }
return maxPixDiff >= share->blockThresh; if (maxPixDiff >= share->blockThresh)
{
// return true on this function with the block with the
// high pixDiff value which should be at or exceeds
// the block_threshold set by the conf file.
auto res = results[blockPick];
block->x = res.x;
block->y = res.y;
block->height = res.ySize;
block->width = res.xSize;
moInBlock = true;
} }
bool moDetect(const string &buffFile, shared_t *share) return moInBlock;
}
bool moDetect(const string &buffFile, Rect *block, Mat *img, shared_t *share)
{ {
auto mod = false; auto mod = false;
@ -117,10 +134,15 @@ bool moDetect(const string &buffFile, shared_t *share)
Mat prev; Mat prev;
Mat next; Mat next;
while (capPair(prev, next, capture, share)) share->stat += "motion_detection-- clip_file - " + buffFile + "\n";
for (auto i = 0; capPair(prev, next, capture, share); ++i)
{ {
if (imgDiff(toGray(prev), toGray(next), share)) share->stat += "frame_pair-- " + to_string(i) + "\n";
if (imgDiff(toGray(prev), toGray(next), block, share))
{ {
*img = next;
mod = true; break; mod = true; break;
} }
} }

View File

@ -17,7 +17,6 @@
struct sec_t struct sec_t
{ {
int id;
int x; int x;
int y; int y;
int xSize; int xSize;
@ -25,9 +24,9 @@ struct sec_t
int pixDiff; int pixDiff;
}; };
void secDiff(const Mat &imgA, const Mat &imgB, int id, int rows, int cols, int rowOffs, int colOffs, vector<sec_t> *results, mutex *secMutex, shared_t *share); void secDiff(const Mat &imgA, const Mat &imgB, int rows, int cols, int rowOffs, int colOffs, vector<sec_t> *results, mutex *secMutex, shared_t *share);
bool pixDiff(const uchar &pixA, const uchar &pixB, shared_t *share); bool pixDiff(const uchar &pixA, const uchar &pixB, shared_t *share);
bool imgDiff(const Mat &prev, const Mat &next, shared_t *share); bool imgDiff(const Mat &prev, const Mat &next, Rect *block, shared_t *share);
bool moDetect(const string &buffFile, shared_t *share); bool moDetect(const string &buffFile, Rect *block, Mat *img, shared_t *share);
#endif // MO_DETECT_H #endif // MO_DETECT_H

64
src/obj_detect.cpp Normal file
View File

@ -0,0 +1,64 @@
// This file is part of Motion Watch.
// Motion Watch is free software: you can redistribute it and/or modify
// it under the terms of the GNU General Public License as published by
// the Free Software Foundation, either version 3 of the License, or
// (at your option) any later version.
// Motion Watch is distributed in the hope that it will be useful,
// but WITHOUT ANY WARRANTY; without even the implied warranty of
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
// GNU General Public License for more details.
#include "obj_detect.h"
bool objectInImage(const Mat &src, const Rect &area, shared_t *share)
{
// reference: https://github.com/doleron/yolov5-opencv-cpp-python/blob/main/cpp/yolo.cpp
Mat blockImage(src, area);
Mat blob;
dnn::blobFromImage(blockImage, blob, 1./255., Size(area.width, area.height), cv::Scalar(), true, false);
share->network.setInput(blob);
vector<Mat> outputs;
share->network.forward(outputs, share->network.getUnconnectedOutLayersNames());
float *data = (float *)outputs[0].data;
share->stat += "object_detection---\n";
share->stat += " outputs_size=" + to_string(outputs.size()) + " looking_for_objects...\n";
for (int i = 0; i < 25200; ++i)
{
// confidence level data[4];
if (data[4] >= 0.4)
{
float *classesScores = data + 5;
Mat scores(1, share->classNames.size(), CV_32FC1, classesScores);
Point classId;
double maxClassScore;
minMaxLoc(scores, 0, &maxClassScore, 0, &classId);
share->stat += " potential_object[confidence_level]=" + to_string(data[4]) + "\n";
share->stat += " potential_object[class_score]=" + to_string(maxClassScore) + "\n";
if (maxClassScore > 0.2)
{
share->stat += " object_confirmed\n";
return true;
}
}
data += 85;
}
share->stat += " no_objects_found\n";
return false;
}

20
src/obj_detect.h Normal file
View File

@ -0,0 +1,20 @@
#ifndef OBJ_DETECT_H
#define OBJ_DETECT_H
// This file is part of Motion Watch.
// Motion Watch is free software: you can redistribute it and/or modify
// it under the terms of the GNU General Public License as published by
// the Free Software Foundation, either version 3 of the License, or
// (at your option) any later version.
// Motion Watch is distributed in the hope that it will be useful,
// but WITHOUT ANY WARRANTY; without even the implied warranty of
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
// GNU General Public License for more details.
#include "common.h"
bool objectInImage(const Mat &image, const Rect &area, shared_t *share);
#endif // OBJ_DETECT_H