Skip to content
Permalink
master
Switch branches/tags

Name already in use

A tag already exists with the provided branch name. Many Git commands accept both tag and branch names, so creating this branch may cause unexpected behavior. Are you sure you want to create this branch?
Go to file
 
 
Cannot retrieve contributors at this time
#include "carCounter.h"
using namespace cv;
// db functions
static int callback(void *NotUsed, int argc, char **argv, char **azColName){
int i;
for(i=0; i<argc; i++){
printf("%s = %s\n", azColName[i], argv[i] ? argv[i] : "NULL");
}
printf("\n");
return 0;
}
// PRIVATE METHODS
void carCounter::openDB(int expID,char dataBase[200]){
char * sql;
sql = new char[200];
char * num;
num = new char[15];
char * params;
params = new char[500];
char *zErrMsg = 0;
/* Open database */
rc = sqlite3_open(dataBase, &db);
if( rc ){
fprintf(stderr, "Can't open database: %s\n", sqlite3_errmsg(db));
exit(0);
}else{
fprintf(stderr, "Opened database successfully\n");
}
/* Create table if it doesn't exist. */
strcpy (sql,"CREATE TABLE IF NOT EXISTS CarTable_exp");
params = "(Video TEXT NOT NULL," \
"Date TEXT NOT NULL," \
"SecInVid TEXT NOT NULL," \
"ExpID INTEGER NOT NULL," \
"CarID INTEGER NOT NULL," \
"Count TEXT NOT NULL," \
"Frames TEXT NOT NULL," \
"ULX TEXT NOT NULL,"\
"ULY TEXT NOT NULL,"\
"LRX TEXT NOT NULL,"\
"LRY TEXT NOT NULL,"\
"Area TEXT NOT NULL,"\
"Path TEXT NOT NULL);";
itoa(expID,num,10);
strncat (sql,num,15);
strncat (sql,params,500);
/* Execute SQL statement */
bool continueTrying = true;
while(continueTrying){
rc = sqlite3_exec(db, sql, callback, 0, &zErrMsg);
switch(rc){
case SQLITE_BUSY:
sqlite3_sleep(10);
break;
case SQLITE_OK:
continueTrying = false;
break;
}
}
rc = sqlite3_exec(db, sql, callback, 0, &zErrMsg);
if( rc != SQLITE_OK ){
fprintf(stderr, "SQL error: %s\n", zErrMsg);
sqlite3_free(zErrMsg);
}else{
fprintf(stdout, "Table created successfully\n");
}
}
void carCounter::closeDB(){
sqlite3_close(db);
}
void carCounter::exportCarsToDB(int expID,char fileName[100],char saveImgTo[200],std::deque<Object> &carArchive,int fps) {
int index = 0;
char *zErrMsg = 0;
char * sql;
sql = new char[10000];
char * value0;
value0 = new char[100];
char * value1;
value1 = new char[100];
char * value2;
value2 = new char[15];
char * value3;
value3 = new char[15];
char * value4;
value4 = new char[15];
char * value5;
value5 = new char[15];
char * value6;
value6 = new char[1000];
char * value7;
value7 = new char[1000];
char * value8;
value8 = new char[1000];
char * value9;
value9 = new char[1000];
char * value10;
value10 = new char[1000];
char * value11;
value11 = new char[1000];
char * value12;
value12 = new char[1000];
char * temp;
temp = new char[15];
// File path
strcpy (value0,"'");
strncat (value0,fileName,100);
strncat (value0,"'",1);
// Time and Date
time_t now = time(0);
tm* localtm = localtime(&now);
tm* gmtm = gmtime(&now);
strcpy (value1,"'");
strncat (value1,asctime(gmtm),100);
strncat (value1,"'",1);
while(true){
while(index < carArchive.size()){
// Archive Last Frame Time
int last_frame = carArchive[index].frames[carArchive[index].frames.size()-1];
double secsInVideo = (double) (last_frame)/(double) (fps);
sprintf(value2, "%f", secsInVideo);
// Expirement Id
itoa (expID,value3,10);
// Archive Car Id
itoa (carArchive[index].id,value4,10);
// Archive Car Count
itoa (carArchive[index].count,value5,10);
// Archive Frames
strcpy (value6,"'");
for(int i=1; i < carArchive[index].frames.size(); i++){
itoa (carArchive[index].frames[i],temp,10);
if(i != carArchive[index].frames.size()-1){
strncat (temp," ",1);
strncat (value6,temp,15);
} else {
strncat (temp,"'",1);
strncat(value6,temp,15);
}
}
// Archive ulX
strcpy (value7,"'");
for(int i=1; i < carArchive[index].ulx.size(); i++){
itoa (carArchive[index].ulx[i],temp,10);
if(i != carArchive[index].ulx.size()-1){
strncat (temp," ",1);
strncat (value7,temp,15);
} else {
strncat (temp,"'",1);
strncat(value7,temp,15);
}
}
// Archive ulY
strcpy (value8,"'");
for(int i=1; i < carArchive[index].uly.size(); i++){
itoa (carArchive[index].uly[i],temp,10);
if(i != carArchive[index].uly.size()-1){
strncat (temp," ",1);
strncat (value8,temp,15);
} else {
strncat (temp,"'",1);
strncat(value8,temp,15);
}
}
// Archive lrX
strcpy (value9,"'");
for(int i=1; i < carArchive[index].lrx.size(); i++){
itoa (carArchive[index].lrx[i],temp,10);
if(i != carArchive[index].lrx.size()-1){
strncat (temp," ",1);
strncat (value9,temp,15);
} else {
strncat (temp,"'",1);
strncat(value9,temp,15);
}
}
// Archive lrY
strcpy (value10,"'");
for(int i=1; i < carArchive[index].lry.size(); i++){
itoa (carArchive[index].lry[i],temp,10);
if(i != carArchive[index].lry.size()-1){
strncat (temp," ",1);
strncat (value10,temp,15);
} else {
strncat (temp,"'",1);
strncat(value10,temp,15);
}
}
// Archive Area
strcpy (value11,"'");
for(int i=1; i < carArchive[index].area.size(); i++){
itoa (carArchive[index].area[i],temp,10);
if(i != carArchive[index].area.size()-1){
strncat (temp," ",1);
strncat (value11,temp,15);
} else {
strncat (temp,"'",1);
strncat(value11,temp,15);
}
}
// Save image locally & save directory
strcpy (value12,saveImgTo);
strncat (value12,"exp",15);
strncat (value12,value3,15);
strncat (value12,"_",15);
strncat (value12,"id",15);
strncat (value12,value4,15);
strncat (value12,".jpg",15);
String value12_S = String(value12);
imwrite(value12_S, carArchive[index].img );
/* Create SQL statement */
strcpy(sql,"INSERT INTO CarTable_exp");
strncat(sql,value3,15);
strncat(sql,"(Video,Date,SecInVid,expID,CarID,Count,Frames,ULX,ULY,LRX,LRY,Area,Path) VALUES (",100);
strncat(sql,value0,100);
strncat(sql,",",1);
strncat(sql,value1,100);
strncat(sql,",",1);
strncat(sql,value2,15);
strncat(sql,",",1);
strncat(sql,value3,15);
strncat(sql,",",1);
strncat(sql,value4,15);
strncat(sql,",",1);
strncat(sql,value5,15);
strncat(sql,",",1);
strncat(sql,value6,1000);
strncat(sql,",",1);
strncat(sql,value7,1000);
strncat(sql,",",1);
strncat(sql,value8,1000);
strncat(sql,",",1);
strncat(sql,value9,1000);
strncat(sql,",",1);
strncat(sql,value10,1000);
strncat(sql,",",1);
strncat(sql,value11,1000);
strncat(sql,",",1);
strncat(sql,"'",1);
strncat(sql,value12,100);
strncat(sql,"'",1);
strncat(sql,");",3);
/* Execute SQL statement */
bool continueTrying = true;
while(continueTrying){
rc = sqlite3_exec(db, sql, callback, 0, &zErrMsg);
switch(rc){
case SQLITE_BUSY:
sqlite3_sleep(10);
break;
case SQLITE_OK:
continueTrying = false;
break;
}
}
if( rc != SQLITE_OK ){
fprintf(stderr, "SQL error: %s\n",zErrMsg);
sqlite3_free(zErrMsg);
} else {
fprintf(stdout, "Records created successfully\n");
fprintf(stdout, "----------------------------\n");
fprintf(stdout, "Record idx:%d\n",index);
fprintf(stdout, "Record Video:%s\n",value0);
fprintf(stdout, "Record Video Date:%s\n",value1);
fprintf(stdout, "Record Seconds into Video:%s\n",value2);
fprintf(stdout, "Record EID:%s\n",value3);
fprintf(stdout, "Record CID:%s\n",value4);
fprintf(stdout, "Record count:%s\n",value5);
fprintf(stdout, "Record frames:%s\n",value6);
fprintf(stdout, "Record ULX:%s\n",value7);
fprintf(stdout, "Record ULY:%s\n",value8);
fprintf(stdout, "Record LRX:%s\n",value9);
fprintf(stdout, "Record LRY:%s\n",value10);
fprintf(stdout, "Record Area:%s\n",value11);
fprintf(stdout, "Path of image:%s\n",value12);
}
index++;
}
}
}
void carCounter::detectCarBoxForNight(cv::Mat &subtract,cv::Rect &boundRect,int minObjectSize){
cv::vector<cv::vector<cv::Point>> endRegionContours;
cv::vector<cv::Vec4i> endRegionHierarchy;
findContours(subtract, endRegionContours, endRegionHierarchy, CV_RETR_EXTERNAL, CV_CHAIN_APPROX_NONE, cv::Point(0, 0));
cv::vector<cv::vector<cv::Point>> endRegionContoursPoly(endRegionContours.size());
cv::vector<cv::Rect> endRegionboundRect(endRegionContours.size());
cv::vector<cv::Point2f> endRegionCenter(endRegionContours.size());
cv::vector<float> endRegionRadius(endRegionContours.size());
int j = obtainBoundBoxes(endRegionContours,endRegionContoursPoly,endRegionboundRect,endRegionCenter,endRegionRadius,2000);
int xUL = std::numeric_limits<int>::max();
int yUL = std::numeric_limits<int>::max();
int xLR = 0;
int yLR = 0;
for( int i = 0; i<endRegionboundRect.size(); i=endRegionHierarchy[i][0]){
if(endRegionboundRect[i].x < xUL && endRegionboundRect[i].x != 0)
xUL = endRegionboundRect[i].x;
if(endRegionboundRect[i].y < yUL && endRegionboundRect[i].y != 0)
yUL = endRegionboundRect[i].y;
if(endRegionboundRect[i].x+endRegionboundRect[i].width > xLR)
xLR = endRegionboundRect[i].x +endRegionboundRect[i].width;
if(endRegionboundRect[i].y+endRegionboundRect[i].height > yLR)
yLR = endRegionboundRect[i].y+endRegionboundRect[i].height;
}
boundRect = Rect(cv::Point(xUL,yUL),cv::Point(xLR,yLR));
/* test code
cv::rectangle(frame,cv::Point(xUL,yUL),cv::Point(xLR,yLR),CV_RGB(255,0,255),2,8,0);
for( int i = 0; i<endRegionboundRect.size(); i=endRegionHierarchy[i][0] ){
frame.copyTo(test);
cv::putText(frame, std::to_string(i), cv::Point(endRegionboundRect[i].x,endRegionboundRect[i].y), FONT_HERSHEY_PLAIN, 1.0, CV_RGB(255,255,0), 2.0);
cv::rectangle(frame,endRegionboundRect[i].tl(),endRegionboundRect[i].br(),CV_RGB(255,255,0),2,8,0);
}
imshow("Test",test);
*/
}
void carCounter::removeShadow(cv::Mat &subtract,cv::Mat3b &subtractrgb){
subtractrgb = subtract;
for (Mat3b::iterator it =subtractrgb.begin(); it != subtractrgb.end(); it++) {
if(*it != Vec3b(255, 255, 255)) {
*it = Vec3b(0, 0, 0);
}
}
}
void carCounter::cleanNoise(cv::Mat &mask, cv::Mat &extraMask,cv::Mat3b &maskrgb, cv::Mat3b &extraMaskrgb){
cv::erode(mask,mask,cv::Mat(),Point(-1, -1), 1, 1, 1);
cv::dilate(mask,mask,cv::Mat(),Point(-1, -1), 3, 1, 1);
cv::medianBlur(mask,mask,21);
removeShadow(mask,maskrgb);
cv::resize(maskrgb, maskrgb, cv::Size(frame.size().width, frame.size().height));
cv::cvtColor(maskrgb,mask,CV_RGB2GRAY);
if(extraMask.dims > 0){
cv::erode(extraMask,extraMask,cv::Mat(),Point(-1, -1), 1, 1, 1);
cv::dilate(extraMask,extraMask,cv::Mat(),Point(-1, -1), 3, 1, 1);
cv::medianBlur(extraMask,extraMask,21);
removeShadow(extraMask,extraMaskrgb);
cv::resize(extraMaskrgb, extraMaskrgb, cv::Size(frame.size().width, frame.size().height));
cv::cvtColor(extraMaskrgb,extraMask,CV_RGB2GRAY);
}
}
int carCounter::obtainBoundBoxes(cv::vector<cv::vector<cv::Point>> &contours,cv::vector<cv::vector<cv::Point>> &contours_poly,
cv::vector<cv::Rect> &boundRect,
cv::vector<cv::Point2f> &center,cv::vector<float> &radius, int minObjectSize){
int j = 0;
for(int i = 0; i < contours.size(); i++){
approxPolyDP(cv::Mat(contours[i]), contours_poly[i], 3, true);
cv::minEnclosingCircle((Mat)contours_poly[i], center[j], radius[j]);
if(cv::boundingRect(cv::Mat(contours_poly[i])).area() > minObjectSize){
boundRect[j] = cv::boundingRect(cv::Mat(contours_poly[i]));
contours[j] = contours[i];
cv::minEnclosingCircle((Mat)contours_poly[i], center[j], radius[j]);
j++;
}
}
return j;
}
void carCounter::createFrame(Frame &data, cv::vector<cv::vector<cv::Point>> &contours,
cv::vector<cv::vector<cv::Point>> &contours_poly,
cv::vector<cv::Rect> &boundRect,
cv::vector<cv::Point2f> &center,
cv::vector<float> &radius, int minObjectSize){
int j = obtainBoundBoxes(contours,contours_poly,boundRect,center,radius,minObjectSize);
contours.resize(j);
boundRect.resize(j);
center.resize(j);
radius.resize(j);
data.contour = std::vector<cv::vector<cv::Point>>(contours);
data.boundRect = std::vector<cv::Rect>(boundRect);
data.center = std::vector<cv::Point2f>(center);
data.radius = std::vector<float>(radius);
data.speed = std::vector<double>(j);
data.speedX = std::vector<double>(j);
data.speedY = std::vector<double>(j);
data.start_loc = std::vector<Point2f>(j);
data.velocity = std::vector<double>(j);
data.velocityX = std::vector<double>(j);
data.velocityY = std::vector<double>(j);
data.distance = std::vector<double>(j);
data.distanceX = std::vector<double>(j);
data.distanceY = std::vector<double>(j);
data.time = std::vector<int>(j);
data.id = std::vector<int>(j);
data.tIdx = std::vector<int>(j);
data.p = std::vector<bool>(j);
data.checked = std::vector<bool>(j);
data.pTraj = std::vector<std::vector<cv::Point2f>>(j);
}
vector<Mat> carCounter::calcHistogram(Mat& img,Mat& mask){
int bins = 256; // number of bins
int nc = img.channels(); // number of channels
vector<Mat> hist(nc); // histogram arrays
// Initalize histogram arrays
for (int i = 0; i < hist.size(); i++)
hist[i] = Mat::zeros(1, bins, CV_32SC1);
// Calculate the histogram of the image
for (int i = 0; i < img.rows; i++){
for (int j = 0; j < img.cols; j++){
for (int k = 0; k < nc; k++){
if(mask.at<Vec3b>(i,j) == Vec3b(255, 255, 255)){
uchar val = nc == 1 ? img.at<uchar>(i,j) : img.at<Vec3b>(i,j)[k];
if(val != 0)
hist[k].at<int>(val) += 1;
}
}
}
}
// For each histogram arrays, obtain the maximum (peak) value
// Needed to normalize the display later
int hmax[3] = {0,0,0};
for (int i = 0; i < nc; i++){
for (int j = 0; j < bins-1; j++)
hmax[i] = hist[i].at<int>(j) > hmax[i] ? hist[i].at<int>(j) : hmax[i];
}
const char* wname[3] = { "blue", "green", "red" };
Scalar colors[3] = { Scalar(255,0,0), Scalar(0,255,0), Scalar(0,0,255) };
vector<Mat> canvas(nc);
return hist;
}
double carCounter::findStd(vector<Mat> &hist, int channel){
int bins = 255;
double total = 0;
double avg = 0;
double var = 0;
double deviation = 0;
for(int i = 0; i < bins-1; i++){
total=total+hist[channel].at<int>(i);
}
for(int i = 0; i < bins-1; i++){
avg=avg+(hist[channel].at<int>(i)/total)*i;
}
for(int i = 0; i < bins-1; i++){
var=var+(hist[channel].at<int>(i)/total)*pow((i-avg),2.0);
}
deviation = pow(var,0.5);
return deviation;
}
double carCounter::histogramCalcAndFindStd(cv::Mat &frame,cv::Mat &background,cv::Mat& subtract,std::deque<Frame> &buffer, int boundBoxNum){
cv::Mat croppedFrame;
cv::Mat croppedSubtract;
cv::Mat croppedBackground;
cv::Mat croppedDifference;
frame(buffer[0].boundRect[boundBoxNum]).copyTo(croppedFrame);
background(buffer[0].boundRect[boundBoxNum]).copyTo(croppedBackground);
subtract(buffer[0].boundRect[boundBoxNum]).copyTo(croppedSubtract);
croppedDifference = croppedFrame - croppedBackground;
vector<Mat> hist = calcHistogram(croppedDifference,croppedSubtract);
double std = findStd(hist,0);
return std;
}
bool carCounter::safetyChecks(cv::Mat &frame,cv::Mat &background,cv::Mat &subtract,std::deque<Frame> &buffer,int boundBoxNum, double thresh){
double std = histogramCalcAndFindStd(frame,background,subtract,buffer,boundBoxNum);
if (std < thresh && !nightTime)
return false;
else
return true;
}
// region monitor functions
void carCounter::startRegionMonitor(cv::Mat &frame, cv::Mat &background, cv::Mat &subtract,
std::deque<Frame> &buffer,std::deque<Object> &transit,
cv::vector<cv::Point> startRegion,int horizontalBandWidth){
std::deque<Object> temp;
for(int i = 0; i < buffer[0].p.size(); i++){
bool safetyPass = safetyChecks(frame,background,subtract,buffer,i,10);
if(pointPolygonTest(startRegion, buffer[0].center[i], false) == 1
&& safetyPass == true){
if(!buffer[0].checked[i]){
buffer[0].start_loc[i] = buffer[0].center[i];
buffer[0].time[i] = 0;
}
Object obj;
obj.last_seen = frame_counter;
obj.id = buffer[0].id[i];
obj.tIdx = buffer[0].tIdx[i];
obj.checked = buffer[0].checked[i];
obj.start_loc = buffer[0].center[i];
obj.center_when_last_seen = buffer[0].center[i];
obj.velocity_when_last_seen = buffer[0].velocity[i];
obj.velocityX_when_last_seen = buffer[0].velocityX[i];
obj.velocityY_when_last_seen = buffer[0].velocityY[i];
obj.speed_when_last_seen = buffer[0].speed[i];
obj.speedX_when_last_seen = buffer[0].speedX[i];
obj.speedY_when_last_seen = buffer[0].speedY[i];
obj.distance_when_last_seen = buffer[0].distance[i];
obj.distanceX_when_last_seen = buffer[0].distanceX[i];
obj.distanceY_when_last_seen = buffer[0].distanceY[i];
obj.time_when_last_seen = buffer[0].time[i];
obj.contour_before_disappearing = buffer[0].contour[i];
obj.boundbox_before_disappearing = buffer[0].boundRect[i];
/* First time object is seen so no path can be predicted yet based
* on it's previous trajectory. For correctness sake we place a
* "trajectory" that only indicates current positon of the object. */
std::vector<cv::Point2f> aVector;
aVector.push_back(obj.start_loc);
obj.trajectories_before_disappearing.push_back(aVector);
obj.area.push_back(buffer[0].boundRect[i].area());
obj.trajectory.push_back(buffer[0].center[i]);
obj.frames.push_back(frame_counter);
obj.bIdx = i;
temp.push_front(obj);
}
}
if(nightTime){
for(int x = temp.size()-1; x > -1; x--){
for(int y = temp.size()-1; y > -1; y--){
if(x != y &&
temp.at(x).center_when_last_seen.y < temp.at(y).center_when_last_seen.y+horizontalBandWidth &&
temp.at(x).center_when_last_seen.y > temp.at(y).center_when_last_seen.y-horizontalBandWidth &&
!temp.at(x).checked){
if(temp.at(y).checked && !temp.at(y).hasSibling){
temp.at(x).checked = true;
temp.at(x).headlight = true;
temp.at(x).hasSibling = true;
temp.at(x).siblingID = temp.at(y).id;
temp.at(x).id = object_counter+1;
buffer[0].id[temp.at(x).bIdx] = temp.at(x).id;
buffer[0].checked[temp.at(x).bIdx] = temp.at(x).checked;
transit.push_front(temp.at(x));
for(int i = 0; i < transit.size(); i++)
transit[i].tIdx = i;
temp.at(y).hasSibling = true;
temp.at(y).siblingID = temp.at(x).id;
transit[temp.at(y).tIdx+1].hasSibling = temp.at(y).hasSibling;
transit[temp.at(y).tIdx+1].siblingID = temp.at(y).siblingID;
object_counter++;
} else if(!temp.at(y).checked && !temp.at(y).hasSibling) {
temp.at(x).checked = true;
temp.at(x).headlight = true;
temp.at(x).hasSibling = true;
temp.at(x).id = object_counter+1;
buffer[0].id[temp.at(x).bIdx] = temp.at(x).id;
buffer[0].checked[temp.at(x).bIdx] = temp.at(x).checked;
object_counter++;
temp.at(y).checked = true;
temp.at(y).headlight = true;
temp.at(y).hasSibling = true;
temp.at(y).id = object_counter+1;
buffer[0].id[temp.at(y).bIdx] = temp.at(y).id;
buffer[0].checked[temp.at(x).bIdx] = temp.at(x).checked;
object_counter++;
temp.at(x).siblingID = temp.at(y).id;
temp.at(y).siblingID = temp.at(x).id;
transit.push_front(temp.at(x));
transit.push_front(temp.at(y));
for(int i = 0; i < transit.size(); i++)
transit[i].tIdx = i;
}
}
}
if(!temp.at(x).checked){
temp.at(x).checked = true;
temp.at(x).headlight = true;
temp.at(x).hasSibling = false;
temp.at(x).id = object_counter+1;
buffer[0].id[temp.at(x).bIdx] = temp.at(x).id;
buffer[0].checked[temp.at(x).bIdx] = temp.at(x).checked;
transit.push_front(temp.at(x));
for(int i = 0; i < transit.size(); i++)
transit[i].tIdx = i;
object_counter++;
}
}
} else {
for(int x = temp.size()-1; x > -1; x--){
if(!temp.at(x).checked){
temp.at(x).checked = true;
temp.at(x).headlight = false;
temp.at(x).hasSibling = false;
temp.at(x).id = object_counter+1;
buffer[0].id[temp.at(x).bIdx] = temp.at(x).id;
buffer[0].checked[temp.at(x).bIdx] = temp.at(x).checked;
transit.push_front(temp.at(x));
for(int i = 0; i < transit.size(); i++)
transit[i].tIdx = i;
object_counter++;
}
}
}
}
void carCounter::endRegionMonitor(cv::Mat &frame,cv::Mat &background,cv::Mat &subtract,cv::Mat &subtract2, int minObjectSize,
int &car_counter,
std::deque<Frame> &buffer,std::deque<Object> &transit, std::deque<Object> &carArchive,
cv::vector<cv::Point> endRegion){
for(int i = 0; i < buffer[0].center.size(); i++){
bool safetyPass = safetyChecks(frame,background,subtract,buffer, i,10);
if(pointPolygonTest(endRegion, buffer[0].center[i], false) == 1 && safetyPass == true){
int sib = NULL;
bool found = false;
for(int j = 0; j < transit.size(); j++){
if(buffer[0].id[i] == transit[j].id){
found = true;
if(nightTime && transit[j].hasSibling)
sib = transit[j].siblingID;
}
}
if(found == true){
while(buffer[0].id[i] != transit[transit.size()-1].id)
transit.pop_back();
if(buffer[0].id[i] == transit[transit.size()-1].id){
car_counter++;
if(!nightTime){
cv::Mat croppedBox;
frame(buffer[0].boundRect[i]).copyTo(croppedBox);
transit[transit.size()-1].img = croppedBox;
transit[transit.size()-1].count = car_counter;
carArchive.push_back(transit[transit.size()-1]);
} else {
detectCarBoxForNight(subtract2,buffer[0].boundRect[i],minObjectSize);
cv::Mat croppedBox;
frame(buffer[0].boundRect[i]).copyTo(croppedBox);
transit[transit.size()-1].img = croppedBox;
transit[transit.size()-1].count = car_counter;
carArchive.push_back(transit[transit.size()-1]);
}
transit.pop_back();
}
if(nightTime && sib != NULL){
while(sib != transit[transit.size()-1].id && transit.size() != 0)
transit.pop_back();
if(sib == transit[transit.size()-1].id)
transit.pop_back();
}
}
}
}
}
void carCounter::checkMissing(int &frame_counter,std::deque<Frame> &buffer,std::deque<Object> &transit){
// Keep track of missing objects currently in transit.
for(int i = 0; i < transit.size(); i++)
{
bool found = false;
for(int j = 0; j < buffer[0].id.size(); j++){
if(buffer[0].id[j] == transit[i].id){
found = true;
transit[i].miss = false;
transit[i].last_seen = frame_counter;
transit[i].trajectories_before_disappearing.push_back(buffer[0].pTraj[j]);
transit[i].time_when_last_seen =buffer[0].time[j];
transit[i].center_when_last_seen = buffer[0].center[j];
transit[i].velocity_when_last_seen = buffer[0].velocity[j];
transit[i].velocityX_when_last_seen = buffer[0].velocityX[j];
transit[i].velocityY_when_last_seen = buffer[0].velocityY[j];
transit[i].speed_when_last_seen = buffer[0].speed[j];
transit[i].speedX_when_last_seen = buffer[0].speedX[j];
transit[i].speedY_when_last_seen = buffer[0].speedY[j];
transit[i].distance_when_last_seen = buffer[0].distance[j];
transit[i].distanceX_when_last_seen = buffer[0].distanceX[j];
transit[i].distanceY_when_last_seen = buffer[0].distanceY[j];
transit[i].contour_before_disappearing = buffer[0].contour[j];
transit[i].boundbox_before_disappearing = buffer[0].boundRect[j];
transit[i].area.push_back(buffer[0].boundRect[j].area());
transit[i].trajectory.push_back(buffer[0].center[i]);
transit[i].frames.push_back(frame_counter);
transit[i].ulx.push_back(buffer[0].boundRect[j].x);
transit[i].uly.push_back(buffer[0].boundRect[j].y);
transit[i].lrx.push_back(buffer[0].boundRect[j].x+buffer[0].boundRect[j].width);
transit[i].lry.push_back(buffer[0].boundRect[j].y+buffer[0].boundRect[j].height);
break;
}
}
if(!found){
transit[i].miss = true;
}
}
}
// Persistency checking using Transit Ledger.
void carCounter::checkTransitLedgerUsingMomentAndCenter(int &frame_counter,std::deque<Frame> &buffer,std::deque<Object> &transit, int expectedDist)
{
for(int i = 0; i < buffer[0].boundRect.size(); i++){
if(buffer[0].p[i] != true){
/* The loop below this comment is what controls how we examine the transit ledger.
We can either search front to end or end to front.*/
for(int j = transit.size()-1; j > -1; j--){
/* For Future: Matching is currently disabled. Need to figure out how to choose threshold for similarity.
Another direction to consider is matching over RGB image rather than contour. cv::MatchShape(...) > ???value???*/
if(cv::matchShapes(buffer[0].contour[i], transit[j].contour_before_disappearing,CV_CONTOURS_MATCH_I2,0) || nightTime){
for(int t = transit[j].trajectories_before_disappearing.size()-1; t >= 0; t--){
for(int m = transit[j].trajectories_before_disappearing[t].size()-1; m >= 0; m--){
if(((buffer[0].center[i].x > transit[j].trajectories_before_disappearing[t].at(m).x-expectedDist)
&& (transit[j].trajectories_before_disappearing[t].at(m).x+expectedDist > buffer[0].center[i].x))
&& ((buffer[0].center[i].y > transit[j].trajectories_before_disappearing[t].at(m).y-expectedDist)
&& (transit[j].trajectories_before_disappearing[t].at(m).y+expectedDist > buffer[0].center[i].y))){
buffer[0].start_loc[i] = transit[j].start_loc;
buffer[0].id[i] = transit[j].id;
int n_time = transit[j].time_when_last_seen + (frame_counter - transit[j].last_seen);
buffer[0].time[i] = n_time;
// Instantanous Metrics
buffer[0].velocity[i] = sqrt(pow(buffer[0].center[i].x - transit[j].center_when_last_seen.x,2.0)
+ pow(buffer[0].center[i].y - transit[j].center_when_last_seen.y,2.0))/(frame_counter - transit[j].last_seen);
buffer[0].velocityX[i] = (buffer[0].center[i].x - transit[j].center_when_last_seen.x)/(frame_counter - transit[j].last_seen);
buffer[0].velocityY[i] = (buffer[0].center[i].y - transit[j].center_when_last_seen.y)/(frame_counter - transit[j].last_seen);
// Average Metrics
buffer[0].distance[i] = sqrt(pow(buffer[0].center[i].x - transit[j].start_loc.x,2.0)
+ pow(buffer[0].center[i].y - transit[j].start_loc.y,2.0));
buffer[0].distanceX[i] = buffer[0].center[i].x-transit[j].start_loc.x;
buffer[0].distanceY[i] = buffer[0].center[i].y-transit[j].start_loc.y;
buffer[0].speed[i] = buffer[0].distance[i]/buffer[0].time[i];
buffer[0].speedX[i] = buffer[0].distanceX[i]/buffer[0].time[i];
buffer[0].speedY[i] = buffer[0].distanceY[i]/buffer[0].time[i];
// Transit Ledger index
buffer[0].tIdx[i] = transit[j].tIdx;
// Checked Flag
buffer[0].checked[i] = transit[j].checked;
// Presistance Flag
buffer[0].p[i] = true;
int k = 0;
// Fill predicted trajectory vector
while(k < 30) {
Point2f fp(buffer[0].center[i].x+(buffer[0].speedX[i])*k, buffer[0].center[i].y+(buffer[0].speedY[i])*k);
buffer[0].pTraj[i].push_back(fp);
k++;
}
break;
}
}
if(buffer[0].p[i] == true)
break;
}
if(buffer[0].p[i] == true)
break;
}
}
}
}
}
void carCounter::persistenceCheck(int &frame_counter,int type,std::deque<Frame> &buffer, std::deque<Object> &transit,int expectedDist){
if(type == 0)
checkTransitLedgerUsingMomentAndCenter(frame_counter,buffer,transit,expectedDist);
else
assert(type < 1 && type >= 0);
// Additional methods or combination of methods can be used to check for persistence between frames.
}
void carCounter::selectBackgroundSubtraction(bool useMOG2, bool nightTime,
cv::Mat &resizeF,cv::Mat &mask,
int nmixtures, double backgroundratio, bool detectShadows){
if(!useMOG2 && !nightTime){
bgsAdaptiveMedian->process(resizeF, mask, background);
}else if(useMOG2 && !nightTime){
MOG2.set("nmixtures", nmixtures);
MOG2.set("backgroundRatio", backgroundratio);
MOG2.set("detectShadows", detectShadows);
MOG2(resizeF, mask,.005);
MOG2.getBackgroundImage(background);
}else{
// Nighttime case, switch to Frame Difference.
bgsFrameDifference->process(resizeF, mask, background);
}
}
void carCounter::drawResult(int &car_counter, int &object_counter, cv::Mat &frame, cv::vector<cv::Vec4i> &hierarchy, std::deque<Frame> &buffer,std::deque<Object> &transit,
bool showLastCar, bool boundBoxesOn, bool predictionOn, bool latestPathsOn, bool displayTransitLedger, bool displayFocusRegions, int showPathofId,
cv::vector<cv::Point> startRegion,cv::vector<cv::Point> endRegion)
{
// draw bounding boxes.
if(boundBoxesOn){
for( int i = 0; i<buffer[0].boundRect.size(); i=hierarchy[i][0] ){
if(buffer[0].id[i] > 0){
cv::Scalar color = cv::Scalar(255,0,0);
cv::rectangle(frame,buffer[0].boundRect[i].tl(),buffer[0].boundRect[i].br(),color,2,8,0);
cv::putText(frame, std::to_string(i), cv::Point(buffer[0].boundRect[i].x,buffer[0].boundRect[i].y), FONT_HERSHEY_PLAIN, 1.0, CV_RGB(0,255,0), 2.0);
cv::putText(frame, "gid="+std::to_string(buffer[0].id[i])+
"/d="+std::to_string(buffer[0].distance[i])+
"/t="+std::to_string(buffer[0].time[i])+
"/s="+std::to_string(buffer[0].speed[i])+
"/c=("+std::to_string(buffer[0].start_loc[i].x)+
+","+std::to_string(buffer[0].start_loc[i].y)+")"
,cv::Point(buffer[0].center[i].x,buffer[0].center[i].y), FONT_HERSHEY_PLAIN, 1.0, CV_RGB(255,0,0), 2.0);
}else{
cv::Scalar color = cv::Scalar(255,0,255);
cv::rectangle(frame,buffer[0].boundRect[i].tl(),buffer[0].boundRect[i].br(),color,2,8,0);
cv::putText(frame, std::to_string(i), cv::Point(buffer[0].boundRect[i].x,buffer[0].boundRect[i].y), FONT_HERSHEY_PLAIN, 1.0, CV_RGB(0,255,0), 2.0);
cv::putText(frame, "gid="+std::to_string(buffer[0].id[i])+
"/d="+std::to_string(buffer[0].distance[i])+
"/t="+std::to_string(buffer[0].time[i])+
"/s="+std::to_string(buffer[0].speed[i])+
"/c=("+std::to_string(buffer[0].start_loc[i].x)+
+","+std::to_string(buffer[0].start_loc[i].y)+")"
,cv::Point(buffer[0].center[i].x,buffer[0].center[i].y), FONT_HERSHEY_PLAIN, 1.0, CV_RGB(255,0,0), 2.0);
}
}
}
// draw predicted trajectory.
if(predictionOn){
for( int i = 0; i<buffer[0].boundRect.size(); i=hierarchy[i][0] ){
if(buffer[0].id[i] > 0){
cv::Scalar color = cv::Scalar(255,0,255);
if( buffer[0].pTraj[i].size() > 2){
for(int j = 0; j < buffer[0].pTraj[i].size()-1; j++){
line(frame, buffer[0].pTraj[i].at(j), buffer[0].pTraj[i].at(j+1), color, 20, 8, 0);
}
}
for(int j = 0; j < buffer[0].pTraj[i].size(); j++){
circle(frame, buffer[0].pTraj[i].at(j), 1, cv::Scalar(255,255,255), 3, 8, 0);
}
}
}
}
// draw recent trajectories.
if(latestPathsOn){
for(int i = 0; i<buffer.size(); i++){
for( int j = 0; j<buffer[i].boundRect.size(); j=hierarchy[j][0] ){
if(buffer[i].id[j] > 0){
cv::Scalar color = cv::Scalar(255,0,0);
circle(frame, buffer[i].center[j], 1, color, 3, 8, 0);
}
}
}
}
// draw older trajectories from archive.
if(showPathofId != SHOW_PATH_OF_CAR_X_OFF){
int display_path_of_id = showPathofId;
if(carArchive.size() > display_path_of_id){
for( int j = 0; j<carArchive[display_path_of_id].trajectory.size(); j++ ){
cv::Scalar color = cv::Scalar(0,0,255);
circle(frame, carArchive[display_path_of_id].trajectory[j], 1, color, 3, 8, 0);
}
}
}
// display the last discovered car in a box.
if(showLastCar && carArchive.size() != 0){
String s = "Last_Car_";
std::string r = s + std::to_string(id);
imshow(r,carArchive[carArchive.size()-1].img);
}
// transit ledger.
if(displayTransitLedger){
putText(frame, "object counter="+std::to_string(object_counter),
cv::Point(25,25), FONT_HERSHEY_PLAIN, 1.0, CV_RGB(255,0,0), 2.0);
putText(frame, "car counter="+std::to_string(car_counter),
cv::Point(25,45), FONT_HERSHEY_PLAIN, 1.0, CV_RGB(255,0,0), 2.0);
putText(frame, "In Transit: ",
cv::Point(25,65), FONT_HERSHEY_PLAIN, 1.0, CV_RGB(255,0,0), 2.0);
for(int i = 0; i < transit.size(); i++)
putText(frame, std::to_string(transit[i].id) +" | "+std::to_string(transit[i].miss),
cv::Point(25,85+i*20), FONT_HERSHEY_PLAIN, 1.0, CV_RGB(255,0,0), 2.0);
}
//display start and end regions
if(displayFocusRegions){
cv::vector<cv::vector<cv::Point>> regions;
regions.push_back(startRegion);
regions.push_back(endRegion);
drawContours(frame, regions, -1, CV_RGB(255,0,0), 1, 8);
}
}
// PUBLIC METHODS
/* constructor */
carCounter::carCounter(int expID,bool night) {
id = expID;
nightTime = night;
object_counter = 0;
frame_counter = 0;
car_counter = 0;
}
/* make the counter start counting */
void carCounter::run(int bufferSize, int minObjectSizeDay,int minObjectSizeNight, int skip, int learningTime,
char fileName[100], char saveImgTo[200], char dataBase[200],
int fps,
int expectedDist, int horizontalBandWidth,
bool online, const cv::Point* ppt, const cv::Point* ppt2,int* npt,int* npt2,
bool useMOG2, int nmixtures, double backgroundratio, bool detectShadows,
bool showLastCar, bool boundBoxesOn, bool predictionOn, bool latestPathsOn,
bool displayTransitLedger, bool displayFocusRegions, int showPathofId,
int displayType,
cv::vector<cv::Point> startRegion,cv::vector<cv::Point> endRegion){
// 0 is the id of video device.0 if you have only one camera.
cv::VideoCapture stream(fileName);
bgsAdaptiveMedian = new DPAdaptiveMedianBGS;
bgsFrameDifference = new FrameDifferenceBGS;
openDB(id,dataBase);
// Background archiving process.
std::thread DBthread(&carCounter::exportCarsToDB,*this,id,fileName,saveImgTo,std::ref(carArchive),fps);
DBthread.detach();
// Unconditional loop (keeps grabbing frames from video).
while (true) {
frame_counter++;
if(frame_counter % skip == 0){
// OBTAIN FRAME
if(!(stream.read(frame)))
break;
// create windows of correct size.
cv::resize(frame, resizeF, cv::Size(frame.size().width, frame.size().height));
// set focal overlays.
fillPoly(resizeF,&ppt,npt,1,Scalar( 0, 0, 0 ),8);
fillPoly(resizeF,&ppt2,npt2,1,Scalar( 0, 0, 0 ),8);
// BACKGROUND SUBTRACTION
selectBackgroundSubtraction(useMOG2,nightTime,resizeF,mask,nmixtures,backgroundratio,detectShadows);
mask.copyTo(extraMask); // if daytime extraMask and mask will store background subracted foreground,
// if nighttime mask will store threshold foreground
// extraMask will store the background subtracted foreground.
// THRESHOLDING
if(nightTime){
cv::cvtColor(resizeF,resizeFgray,COLOR_RGB2GRAY);
threshold(resizeFgray,mask,240.0,255.0,THRESH_BINARY);
}
// CLEAN MASK
// clear out noise using erode and dilate, also remove shadows if they are detected.
cleanNoise(mask,extraMask,maskrgb,extraMaskrgb);
if(frame_counter > learningTime){
// OBJECT DETECTION
findContours(mask, contours, hierarchy, CV_RETR_EXTERNAL, CV_CHAIN_APPROX_NONE, cv::Point(0, 0));
// Approximate contours to polygons + get bounding rects and circles.
cv::vector<cv::vector<cv::Point>> contours_poly(contours.size());
cv::vector<cv::Rect> boundRect(contours.size());
cv::vector<cv::Point2f> center(contours.size());
cv::vector<float> radius(contours.size());
Frame data;
int minObjectSize;
if(nightTime)
minObjectSize = minObjectSizeNight;
else
minObjectSize = minObjectSizeDay;
// initialize the frame object data.
carCounter::createFrame(data, contours,
contours_poly,
boundRect,
center,
radius, minObjectSize);
// OBJECT TRACKING
// buffer frame data (last X frames are placed in a queue).
// buffer flood.
if(buffer.size() < bufferSize){
buffer.push_front(data);
// persistence checks only make sense if you have more than one frame.
if(buffer.size() > 1){
persistenceCheck(frame_counter,0,buffer,transit,expectedDist);
startRegionMonitor(frame,background,maskrgb,buffer,transit,startRegion,horizontalBandWidth);
checkMissing(frame_counter,buffer,transit);
endRegionMonitor(frame,background,maskrgb,extraMask,minObjectSizeDay,car_counter,buffer,transit,carArchive,endRegion);
frameArchive.push_front(buffer[0]);
}
}
// full buffer.
else{
buffer.push_front(data);
buffer.pop_back();
persistenceCheck(frame_counter,0,buffer,transit,expectedDist);
startRegionMonitor(frame,background,maskrgb,buffer,transit,startRegion,horizontalBandWidth);
checkMissing(frame_counter,buffer,transit);
endRegionMonitor(frame,background,maskrgb,extraMask,minObjectSizeDay,car_counter,buffer,transit,carArchive,endRegion);
frameArchive.push_back(buffer[0]);
}
// VISUALIZATION/CMD OUTPUT
// if online mode is on a window is produced otherwise runs through console with no visualization.
if(online){
if(displayType == USE_BACKGROUND)
background.copyTo(display);
else if(displayType == USE_ORGINAL_FRAME)
frame.copyTo(display);
else if(displayType == USE_RESIZED_WITH_OVERLAYS_FRAME)
resizeF.copyTo(display);
else if(displayType == USE_SUBTRACTED)
maskrgb.copyTo(display);
drawResult(car_counter,object_counter,display,hierarchy,buffer,transit,
showLastCar,boundBoxesOn, predictionOn, latestPathsOn,
displayTransitLedger, displayFocusRegions, showPathofId,
startRegion,endRegion);
// display original with bounding boxes.
String s = "Display_";
std::string r = s + std::to_string(id);
imshow(r, display);
}
}
if (cv::waitKey(30) >= 0)
break;
}
}
closeDB();
}