Skip to content

Instantly share code, notes, and snippets.

@lzbgt
Created April 7, 2020 10:50
Show Gist options
  • Save lzbgt/5ea774d5eb014bcdc84c8403ad78a15d to your computer and use it in GitHub Desktop.
Save lzbgt/5ea774d5eb014bcdc84c8403ad78a15d to your computer and use it in GitHub Desktop.
void detectMotion(AVPixelFormat format, AVFrame *pFrame)
{
static bool firstRun = true;
static cv::Mat lastImage;
static double _area = 0;
static double area = 0;
static int frameProcessed = 0;
vector<vector<cv::Point> > cnts;
cv::Mat origin;
avcvhelpers::frame2mat(format, pFrame, origin);
// check region
auto w = origin.size().width;
auto h = origin.size().height;
// auto config
if(detPara.resize == 0) {
detPara.resize = detPara.size*1.0/h;
}
else {
detPara.size = int(h * detPara.resize);
}
if(detPara.resize > 0 && detPara.resize < 1) {
// resize. new w, h caculated
w = int(w * detPara.resize);
h = int(h * detPara.resize);
cv::resize(origin, origin, cv::Size(w,h));
}
else {
detPara.resize = 1;
}
if(detPara.region[0].y == 0 || (detPara.region[0].x == 0 && detPara.region[0].y == 0 && detPara.region[1].x == 1 && detPara.region[1].y == 1)) {
// do nothing
}
else {
// crop
auto x = (int)(w * detPara.region[0].x);
auto y = (int)(h * detPara.region[0].y);
w = w * (detPara.region[1].x - detPara.region[0].x);
h = h * (detPara.region[1].y - detPara.region[0].y);
cv::Rect crop(x,y,w,h);
origin = origin(crop);
}
cv::Mat gray;
cv::cvtColor(origin, gray, cv::COLOR_BGR2GRAY);
// float fent = avcvhelpers::getEntropy(thresh);
cv::GaussianBlur(gray, gray, cv::Size(21, 21), cv::THRESH_BINARY);
if(firstRun) {
// lastImage = cv::Mat::zeros(gray.size(), CV_32FC3);
lastImage.release();
lastImage = gray.clone();
firstRun = false;
area = detPara.area == 0 ? (AREA_1080P * detPara.size * detPara.size * 1.0/1080/1080) : detPara.area;
spdlog::info("{} resize: {}, area: {}, origin area: {}", selfId, detPara.resize, area, detPara.area);
return;
}
// packetTm = chrono::system_clock::now();
packetTm = packetTs;
// TODO: AVG
// cv::accumulateWeighted(gray, lastImage, 0.5);
cv::Mat diff;
cv::absdiff(gray, lastImage, diff);
cv::Mat thresh;
cv::threshold(diff, thresh, detPara.thre, 255, cv::THRESH_BINARY);
//cv::dilate(gray, thresh, cv::Mat(), cv::Point(-1,-1), 2);
cv::findContours(thresh, cnts, cv::RETR_EXTERNAL, cv::CHAIN_APPROX_SIMPLE);
bool hasEvent = false;
static int evtCnt = 0;
int i = 0;
// avoid vage cases
if(area < 5) {
area = 5;
}
for(; i < cnts.size(); i++) {
_area = cv::contourArea(cnts[i]);
if(_area < area) {
// nothing
}
else {
hasEvent = true;
evtCnt++;
//spdlog::info("{} motion detected: idx {}, area: {}", selfId, i, _area);
break;
}
} //end for
lastImage.release();
lastImage = gray.clone();
frameProcessed++;
spdlog::debug("{} contours {} area {}, thresh {} hasEvent {}", selfId, cnts.size(), hasEvent? cv::contourArea(cnts[i]):0, detPara.area, hasEvent);
// business logic for event
// auto dura = chrono::duration_cast<chrono::seconds>(packetTm - evtStartTmLast).count();
long long dura = 0;
if(evtStartTmLast != 0) {
dura = packetTm - evtStartTmLast;
}
else {
evtStartTmLast = packetTm;
return;
}
static long long pktDelt = 0;
if(pktCnt %18 == 0) {
auto tmp = chrono::duration_cast<chrono::seconds>(chrono::system_clock::now().time_since_epoch()).count();
pktDelt = tmp - packetTm;
}
switch(evtState) {
case NONE:
case PRE:
if(dura >= detPara.pre) {
auto thr = frameProcessed/2;
if(evtCnt <= thr) {
spdlog::info("state: NONE->NONE ({}, {}, {}, {}, {}, {}, {})", pktDelt, this->fps, dura, frameProcessed, evtCnt, thr, _area);
}
else {
json p;
evtState = IN;
evtStartTmOrig = packetTm - detPara.pre;
spdlog::info("{} state: NONE->IN ({}, {}, {}, {}, {}, {}, {})", selfId, pktDelt, this->fps, dura, frameProcessed, evtCnt, thr, _area);
makeEvent(EV_MSG_EVENT_MOTION_START, evtStartTmOrig);
}
evtCnt = 0;
frameProcessed = 0;
evtStartTmLast = packetTm;
}
break;
case IN: {
if( packetTm - evtStartTmOrig > 60 *detPara.maxDuration ) {
evtStartTmOrig = packetTm;
makeEvent(EV_MSG_EVENT_MOTION_END, packetTs);
makeEvent(EV_MSG_EVENT_MOTION_START, packetTs);
spdlog::warn("{} event video continued over {} minutes, force segmenting and continue", devSn, detPara.maxDuration);
spdlog::debug("{} state: IN->IN ({}, {})",selfId, dura, evtCnt);
evtCnt = 0;
frameProcessed = 0;
evtStartTmLast = packetTm;
}
else if(dura >= detPara.post/2) {
auto thr = frameProcessed/5;
if(evtCnt <= thr) {
evtState = POST;
spdlog::info("{} state: IN->POST ({}, {}, {}, {}, {}, {}, {})", selfId, pktDelt, this->fps, dura, frameProcessed, evtCnt, thr, _area);
}
else {
spdlog::info("{} state: IN->IN ({}, {}, {}, {}, {}, {}, {})", selfId, pktDelt, this->fps, dura, frameProcessed, evtCnt, thr, _area);
}
evtCnt = 0;
frameProcessed = 0;
evtStartTmLast = packetTm;
}
break;
}
case POST: {
if(dura >= detPara.post/2) {
auto thr = frameProcessed/5;
if(evtCnt <= thr) {
spdlog::info("{} state: POST->NONE ({}, {}, {}, {}, {}, {}, {})",selfId, pktDelt, this->fps, dura, frameProcessed, evtCnt, thr, _area);
evtState = NONE;
makeEvent(EV_MSG_EVENT_MOTION_END, packetTs - detPara.post/2);
}
else {
spdlog::info("{} state: POST->IN ({}, {}, {}, {}, {}, {}, {})",selfId, pktDelt, this->fps, dura, frameProcessed, evtCnt, thr, _area);
evtState = IN;
}
evtCnt = 0;
frameProcessed = 0;
evtStartTmLast = packetTm;
}
break;
}
}
}
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment