Compare commits

..

5 Commits

Author SHA1 Message Date
Maurice ONeal
01a11741c7 v1.5
Release candidate for merging to master.
2022-10-15 10:24:28 -04:00
Maurice ONeal
a0ee8e35f7 v1.4.t11
completely removed object detection code because I don't foresee going
back to that model anytime soon. diffs will not reset to 0 instead
decrement and the consecutive pixel diffs are now adjustable via
consec_threshold.

updated README.md for the changes to pixel diff detection.
2022-10-14 14:31:12 -04:00
Maurice ONeal
9ecace7e4b v1.4.t10
optical flow calculations use up a lot of processing power even at the
block level so I decided to take it back out. once again, no objection
detection is going to be used and will fall back to pixel diffs only.
also modified pixel diffs to decrement pixel diffs of no diff is
detected, going test how this works out.
2022-10-14 11:42:59 -04:00
Maurice ONeal
17c136b6bc v1.4.t9
Motion detection was still not scanning the entire block. potential fix
in secDiff(), correcting offsets in the for loops.
2022-10-06 19:41:47 -04:00
Maurice ONeal
4bf3672a39 v1.4.t8
AI object detection via yolov5 didn't work out too well, in fact it was
crashing the detection threads for whatever reason. I could deep dive
why it was crashing but I think the better solution is to bring back
optical flow detection at the block level. the advantage of this over
object detection is the fact that a block doesn't need to have a whole
object in it.
2022-10-04 18:12:32 -04:00
12 changed files with 71 additions and 313 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 src/obj_detect.cpp ) add_executable( mow src/main.cpp src/common.cpp src/mo_detect.cpp )
target_link_libraries( mow ${OpenCV_LIBS} ) target_link_libraries( mow ${OpenCV_LIBS} )

View File

@ -45,20 +45,15 @@ 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.
# #
color_threshold = 8 consec_threshold = 512
# the color levels in each pixel of the camera stream can range from 0-255. # motion is detected by comparing each frame in the camera feed for
# in an ideal world the color differences in between frames should be 0 if # differences in the pixels. this value determine how many consecutive
# there is no motion but must cameras can't do this. the threshold value # pixels need to different or how large the suspect object in motion
# here is used to filter if the pixels are truly different. # needs to be.
# #
block_threshold = 3456 block_threshold = 1024
# this application detects motion by loading frames from the camera and # this value tells the application how many "lines" of pixels need to
# then compare the pixels of each back to back frame for any significant # exceed consec_threshold before being considered motion.
# 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

View File

@ -1,80 +0,0 @@
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

Binary file not shown.

View File

@ -1,6 +1,3 @@
#!/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,18 +140,6 @@ 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;
@ -181,21 +169,6 @@ 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());
@ -215,20 +188,15 @@ bool rdConf(shared_t *share)
share->postCmd.clear(); share->postCmd.clear();
share->buffDir.clear(); share->buffDir.clear();
share->colorThresh = 5; share->consecThresh = 512;
share->secs = 60; share->secs = 60;
share->blockX = 32; share->blockX = 32;
share->blockY = 32; share->blockY = 32;
share->blockThresh = 900; share->blockThresh = 1024;
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
{ {
@ -239,7 +207,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("color_threshold = ", line, &share->colorThresh); rdLine("consec_threshold = ", line, &share->consecThresh);
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);
@ -266,6 +234,9 @@ 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;
} }
@ -336,10 +307,10 @@ string parseForParam(const string &arg, int argc, char** argv, bool argOnly)
void statOut(shared_t *share) void statOut(shared_t *share)
{ {
createDirTree(cleanDir(share->buffDir)); system(string("touch " + cleanDir(share->buffDir) + "/stat").c_str());
auto path = string(cleanDir(share->buffDir) + "/stat"); auto path = string(cleanDir(share->buffDir) + "/stat");
auto fd = open(path.c_str(), O_WRONLY); auto fd = open(path.c_str(), fstream::out | fstream::trunc);
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,36 +37,29 @@ using namespace std;
using namespace std::filesystem; using namespace std::filesystem;
#define BUF_SZ 10 #define BUF_SZ 10
#define APP_VER "1.4.t7" #define APP_VER "1.5"
struct shared_t struct shared_t
{ {
vector<string> classNames; string stat;
dnn::Net network; string recordUrl;
string stat; string outDir;
string recordUrl; string postCmd;
string outDir; string conf;
string postCmd; string buffDir;
string conf; string vidExt;
string buffDir; bool init;
string concatTxtTmp; bool recLoopWait;
string concatShTmp; bool skipCmd;
string createShTmp; int consecThresh;
string vidExt; int secs;
bool init; int blockThresh;
bool recLoopWait; int blockX;
bool skipCmd; int blockY;
int tmpId; int maxDays;
int colorThresh; int retCode;
int secs;
int blockThresh;
int blockX;
int blockY;
int maxDays;
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);
@ -84,6 +77,5 @@ 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,7 +11,6 @@
// 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)
{ {
@ -23,25 +22,15 @@ 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, &blockArea, &blockImg, share)) if (moDetect(fullPath, share))
{ {
if (objectInImage(blockImg, blockArea, share)) share->skipCmd = true;
{
share->skipCmd = true;
wrOut(fullPath, share); wrOut(fullPath, share);
}
else
{
remove(fullPath.c_str());
}
} }
else else
{ {
@ -108,7 +97,6 @@ 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,33 +14,30 @@
bool pixDiff(const uchar &pixA, const uchar &pixB, shared_t *share) bool pixDiff(const uchar &pixA, const uchar &pixB, shared_t *share)
{ {
auto diff = 0; if (pixA > pixB) return true;
if (pixB > pixA) return true;
if (pixA > pixB) diff = pixA - pixB; return false;
if (pixB > pixA) diff = pixB - pixA;
if (diff < share->colorThresh)
{
diff = 0;
}
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) 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)
{ {
auto diff = 0;
auto pnts = 0; auto pnts = 0;
for (auto y = rowOffs; y < rows; y++) for (auto y = rowOffs; y < (rowOffs + rows); y++)
{ {
for (auto x = colOffs; x < cols; x++) for (auto x = colOffs; x < (colOffs + 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)) if (pixDiff(pixA, pixB, share)) pnts += 1;
else pnts = 0;
if (pnts >= share->consecThresh)
{ {
pnts += 1; diff += 1;
} }
} }
} }
@ -51,27 +48,28 @@ void secDiff(const Mat &imgA, const Mat &imgB, int rows, int cols, int rowOffs,
res.y = rowOffs; res.y = rowOffs;
res.xSize = cols; res.xSize = cols;
res.ySize = rows; res.ySize = rows;
res.pixDiff = pnts; res.pixDiff = diff;
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, Rect *block, shared_t *share) bool imgDiff(const Mat &prev, const Mat &next, 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) for (auto y = 0; y < prev.rows; y += share->blockY, id += 1)
{ {
threads.push_back(thread(secDiff, prev, next, share->blockY, share->blockX, y, x, &results, &secMutex, share)); threads.push_back(thread(secDiff, prev, next, id, share->blockY, share->blockX, y, x, &results, &secMutex, share));
} }
} }
@ -91,12 +89,12 @@ bool imgDiff(const Mat &prev, const Mat &next, Rect *block, 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;
share->stat += string("block_thread:") if (diff > 0)
+ " x=" + to_string(x) {
+ " y=" + to_string(y) share->stat += string("block_thread:") + " id=" + to_string(id) + " diff=" + to_string(diff) + "\n";
+ " 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))
{ {
@ -105,25 +103,10 @@ bool imgDiff(const Mat &prev, const Mat &next, Rect *block, shared_t *share)
} }
} }
if (maxPixDiff >= share->blockThresh) return 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;
}
return moInBlock;
} }
bool moDetect(const string &buffFile, Rect *block, Mat *img, shared_t *share) bool moDetect(const string &buffFile, shared_t *share)
{ {
auto mod = false; auto mod = false;
@ -134,15 +117,10 @@ bool moDetect(const string &buffFile, Rect *block, Mat *img, shared_t *share)
Mat prev; Mat prev;
Mat next; Mat next;
share->stat += "motion_detection-- clip_file - " + buffFile + "\n"; while (capPair(prev, next, capture, share))
for (auto i = 0; capPair(prev, next, capture, share); ++i)
{ {
share->stat += "frame_pair-- " + to_string(i) + "\n"; if (imgDiff(toGray(prev), toGray(next), share))
if (imgDiff(toGray(prev), toGray(next), block, share))
{ {
*img = next;
mod = true; break; mod = true; break;
} }
} }

View File

@ -17,6 +17,7 @@
struct sec_t struct sec_t
{ {
int id;
int x; int x;
int y; int y;
int xSize; int xSize;
@ -24,9 +25,9 @@ struct sec_t
int pixDiff; int pixDiff;
}; };
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); 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);
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, Rect *block, shared_t *share); bool imgDiff(const Mat &prev, const Mat &next, shared_t *share);
bool moDetect(const string &buffFile, Rect *block, Mat *img, shared_t *share); bool moDetect(const string &buffFile, shared_t *share);
#endif // MO_DETECT_H #endif // MO_DETECT_H

View File

@ -1,64 +0,0 @@
// 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;
}

View File

@ -1,20 +0,0 @@
#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