Skip to content
Snippets Groups Projects
Commit 523e162a authored by Leonardo de Lima Gaspar's avatar Leonardo de Lima Gaspar
Browse files

Implemented frame change detection, and timestamp generation based on that...

Implemented frame change detection, and timestamp generation based on that output. Current parameters seem to work decently well, but still significant gaps from ground truth comparison.
parent 187c7219
No related branches found
No related tags found
No related merge requests found
......@@ -3,6 +3,9 @@
#include <iostream>
#include <stdlib.h>
#include <filesystem>
#include <fstream>
#include <chrono>
#include <math.h>
#include <opencv2/opencv.hpp>
#include <opencv2/bgsegm.hpp>
......@@ -10,7 +13,11 @@
using namespace std;
using namespace cv;
double percentChangeThreshold = 0.00390625;
// 2^-11
double percentChangeThreshold = 0.00048828125;
// Defines change between frames as XOR bitwise operation between pixel pairs mapped to same position,
// and returns true for when the ratio of the sum of those changes is sufficiently large.
bool detectChange(Mat firstFrame, Mat secFrame) {
Mat xorMapMat = Mat::zeros(firstFrame.rows, firstFrame.cols, CV_8U);
......@@ -23,8 +30,7 @@ bool detectChange(Mat firstFrame, Mat secFrame) {
{
for(int j = 0; j < firstFrame.cols; j++)
{
int pos = i*firstFrame.rows + j;
//xorMapArr[pos] = firstFrameArr[pos] ^ secFrameArr[pos];
int pos = i*j + j;
sum += firstFrameArr[pos] ^ secFrameArr[pos];
}
}
......@@ -45,8 +51,14 @@ int main(int argc, char** argv) {
return -1;
}*/
// Currently using Myggbukta-[2021-05-21_10-47-06]-716 as test.mp4.
VideoCapture decoder("test.mp4");
auto start = chrono::steady_clock::now();
int detectionsPerSec = 3;
// Amount of extra time each detection interval will be padded with.
double paddingTime_s = 1;
/* Parameters on CNT background subtractor:
minPixelStability number of frames with same pixel color to consider stable
......@@ -54,11 +66,10 @@ int main(int argc, char** argv) {
maxPixelStability maximum allowed credit for a pixel in history
isParallel determines if we're parallelizing the algorithm
*/
Ptr<BackgroundSubtractor> p_bgSub = bgsegm::createBackgroundSubtractorCNT(CAP_PROP_FPS/targetFPS, true, CAP_PROP_FPS*4/targetFPS, true);
Ptr<BackgroundSubtractor> p_bgSub = bgsegm::createBackgroundSubtractorCNT(detectionsPerSec, true, 4*detectionsPerSec, true);
int detectionsPerSec = 2;
double paddingTime_s = 1;
// Pre-allocation of variables
vector<bool> motionVec;
double simFPS = decoder.get(CAP_PROP_FPS) / detectionsPerSec;
int simFrameTime = decoder.get(CAP_PROP_FRAME_COUNT) / simFPS;
......@@ -77,28 +88,68 @@ int main(int argc, char** argv) {
bool isFirstFrame = true;
bool keyPressed = false;
bool gotFrame, gotNextFrame, isMoving;
bool gotFrame, gotNextFrame;
while (gotFrame = decoder.read(frame)) {
// Skips some amount of "real" frames to reach desired "simulated fps".
for(int i = 0; i < simFPS - 1; i++) {
decoder.grab();
}
gotNextFrame = decoder.read(nextFrame);
// Applies bg segmentation, median filter to reduce noise, and pushes the change detection result to output vector.
// The frame count might be inaccurate at the moment, such that real frame 24 at time 1s, might be declared as being at time 0.9s. Needs testing.
if (gotFrame && gotNextFrame && !keyPressed) {
p_bgSub->apply(frame, binaryFrame, -1);
p_bgSub->apply(nextFrame, binaryNextFrame, -1);
medianBlur(binaryFrame, medianBlurFrame, 5);
medianBlur(binaryNextFrame, medianBlurNextFrame, 5);
// Code runs 30min input file in ~9 minutes. Reducing this median blur value from 7 -> 5, yields run time of ~2 minutes, but more noisy output.
medianBlur(binaryFrame, medianBlurFrame, 7);
medianBlur(binaryNextFrame, medianBlurNextFrame, 7);
motionVec.push_back(detectChange(medianBlurFrame, medianBlurNextFrame));
imshow("Test", medianBlurFrame);
keyPressed = (waitKey(33) >= 0)? true : false;
} else { break; }
}
destroyAllWindows();
decoder.release();
// Detection interval padding logic; essentially applies 1D dilation (morphological operation).
int framesPaddedEachSide = round((paddingTime_s/2)*detectionsPerSec + 0.5);
vector<bool> dilated = motionVec;
for(int i=0; i<motionVec.size(); i++) {
if (motionVec[i] == 1) {
for(int k = -framesPaddedEachSide; k <= framesPaddedEachSide; k++) {
dilated[i+k] = 1;
}
}
}
// Timestamp generator logic; converts frame index to real time in input file. As mentioned above, the math might be inaccurate.
double timePerSimFrame_s = 1/(double)detectionsPerSec;
double runStart_s, runEnd_s;
for(int i=0; i<dilated.size(); i++) {
runStart_s = i*timePerSimFrame_s;
int runCounter = 0;
bool nextIsWithinBounds = i+runCounter < dilated.size();
while(nextIsWithinBounds) {
if(dilated[i+runCounter] == 1) { runCounter++; }
else { break; }
nextIsWithinBounds = i+runCounter < dilated.size();
}
runEnd_s = runStart_s + timePerSimFrame_s*runCounter;
i += runCounter;
if (runCounter>0) {
int startMin = floor(runStart_s/60);
int endMin = floor(runEnd_s/60);
cout << startMin << ":" << floor(runStart_s-(startMin*60)) << "," << endMin << ":" << floor(runEnd_s-(endMin*60)) << endl;
}
}
auto end = chrono::steady_clock::now();
auto elapsed = chrono::duration_cast<chrono::seconds>(end - start).count();
int minutes = floor(elapsed/60);
int seconds = floor(elapsed-(minutes*60));
cout << "Elapsed(mm:ss)=" << minutes << ":" << seconds << endl;
return 0;
}
0% Loading or .
You are about to add 0 people to the discussion. Proceed with caution.
Please register or to comment