Using time in OpenCV for frame processes and other tasks - c++

I want to count the vehicles from a video. After frame differencing I got a gray scale image or kind of binary image. I have defined a Region of Interest to work on a specific area of the frames, the values of the pixels of the vehicles passing through the Region of Interest are higher than 0 or even higher than 40 or 50 because they are white.
My idea is that when a certain number of pixels in a specific interval of time (say 1-2 seconds) are white then there must be a vehicle passing so I will increment the counter.
What I want is, to check whether there are still white pixels coming or not after a 1-2 seconds. If there are no white pixels coming it means that the vehicle has passed and the next vehicle is gonna come, in this way the counter must be incremented.
One method that came to my mind is to count the frames of the video and store it in a variable called No_of_frames. Then using that variable I think I can estimate the time passed. If the value of the variable No_of_frames is greater then lets say 20, it means that nearly 1 second has passed, if my videos frame rate is 25-30 fps.
I am using Qt Creator with windows 7 and OpenCV 2.3.1
My code is something like:
for(int i=0; i<matFrame.rows; i++)
{
for(int j=0;j<matFrame.cols;j++)
if (matFrame.at<uchar>(i,j)>100)//values of pixels greater than 100
//will be considered as white.
{
whitePixels++;
}
if ()// here I want to use time. The 'if' statement must be like:
//if (total_no._of_whitepixels>100 && no_white_pixel_came_after 2secs)
//which means that a vehicle has just passed so increment the counter.
{
counter++;
}
}
Any other idea for counting the vehicles, better than mine, will be most welcomed. Thanks in advance.
For background segmentation I am using the following algorithm but it is very slow, I don't know why. The whole code is as follows:
// opencv2/video/background_segm.hpp OPENCV header file must be included.
IplImage* tmp_frame = NULL;
CvCapture* cap = NULL;
bool update_bg_model = true;
Mat element = getStructuringElement( 0, Size( 2,2 ),Point() );
Mat eroded_frame;
Mat before_erode;
if( argc > 2 )
cap = cvCaptureFromCAM(0);
else
// cap = cvCreateFileCapture( "C:\\4.avi" );
cap = cvCreateFileCapture( "C:\\traffic2.mp4" );
if( !cap )
{
printf("can not open camera or video file\n");
return -1;
}
tmp_frame = cvQueryFrame(cap);
if(!tmp_frame)
{
printf("can not read data from the video source\n");
return -1;
}
cvNamedWindow("BackGround", 1);
cvNamedWindow("ForeGround", 1);
CvBGStatModel* bg_model = 0;
for( int fr = 1;tmp_frame; tmp_frame = cvQueryFrame(cap), fr++ )
{
if(!bg_model)
{
//create BG model
bg_model = cvCreateGaussianBGModel( tmp_frame );
// bg_model = cvCreateFGDStatModel( temp );
continue;
}
double t = (double)cvGetTickCount();
cvUpdateBGStatModel( tmp_frame, bg_model, update_bg_model ? -1 : 0 );
t = (double)cvGetTickCount() - t;
printf( "%d. %.1f\n", fr, t/(cvGetTickFrequency()*1000.) );
before_erode= bg_model->foreground;
cv::erode((Mat)bg_model->background, (Mat)bg_model->foreground, element );
//eroded_frame=bg_model->foreground;
//frame=(IplImage *)erode_frame.data;
cvShowImage("BackGround", bg_model->background);
cvShowImage("ForeGround", bg_model->foreground);
// cvShowImage("ForeGround", bg_model->foreground);
char k = cvWaitKey(5);
if( k == 27 ) break;
if( k == ' ' )
{
update_bg_model = !update_bg_model;
if(update_bg_model)
printf("Background update is on\n");
else
printf("Background update is off\n");
}
}
cvReleaseBGStatModel( &bg_model );
cvReleaseCapture(&cap);
return 0;

A great deal of research has been done on vehicle tracking and counting. The approach you describe appears to be quite fragile, and is unlikely to be robust or accurate. The main issue is using a count of pixels above a certain threshold, without regard for their spatial connectivity or temporal relation.
Frame differencing can be useful for separating a moving object from its background, provided the object of interest is the only (or largest) moving object.
What you really need is to first identify the object of interest, segment it from the background, and track it over time using an adaptive filter (such as a Kalman filter). Have a look at the OpenCV video reference. OpenCV provides background subtraction and object segmentation to do all the required steps.
I suggest you read up on OpenCV - Learning OpenCV is a great read. And also on more general computer vision algorithms and theory - http://homepages.inf.ed.ac.uk/rbf/CVonline/books.htm has a good list.

Normally they just put a small pneumatic pipe across the road (soft pipe semi filled with air). It is attached to a simple counter. Each vehicle passing over the pipe generates two pulses (first front, then rear wheels). The counter records the number of pulses in specified time intervals and divides by 2 to get the approx vehicle count.

Related

My code adds the same frame to a vector while it doesn't when the frame is being rotated

I have an extremely strange situation in which the code adds the same frame to a vector while it doesn't when there is a rotation before the addition. Let me show you :
#include <chrono>
#include <opencv2/opencv.hpp>
#include <vector>
/* Write all the images in a certain directory. All the images with the same name present
in the directory will be overwritten. */
void CameraThread::writeAllFrames(std::vector<cv::Mat> vectorFrame) {
std::string path;
for (size_t i = 0; i < vectorFrame.size(); ++i) {
path = "./Images/image" + std::to_string(i) + ".png";
imwrite(path, vectorFrame.at(i));
}
capturing = 0;
}
int main(){
std::string window_name = "Left Cam";
cv::VideoCapture* videoCapture = new cv::VideoCapture(0);
cv::namedWindow(window_name, CV_WINDOW_NORMAL); //create a window for the camera
std::vector <cv::Mat> capturedFrame; // Vector in which the frames are going to be saved
int i = 0; // Counts how many images are saved
bool capturing = 0;
int amountFramesCaptured = 10;
int periodCapture = 250; // ms
while(1){
bool bSuccess = videoCapture->read(frame); // It captures the frame.
/*The next 2 lines take around 25ms. They turn the frame 90° to the left.*/
cv::transpose(frame, frame);
cv::flip(frame, frame, 0);
if (capturing == 0) {
/* If there is no frame capture, we just display the frames in a window.*/
imshow(window_name, frame);
} else if (capturing == 1) { // We capture the frames here.
capturedFrame.push_back(frame);
Sleep(periodCapture);
++i;
if (i == amountFramesCaptured) {
writeAllFrames(capturedFrame); // Write all frames in a directory.
puts("Frames copied in the directory.");
capturedFrame.clear(); // Clear the vector in case we recapture an other time.
i = 0;
capturing = 0;
}
}
}
return 0;
}
Here, we capture a frame thanks to videoCapture->read(frame). I wanted to rotate the frame so i used the next two lines. Then I tested the capture of the images and it worked well (I know it because I have a motion object in front of the camera). Lastly, I decided not to rotate the frames after some tests because the rotation takes too much resources (around 25 ms) and I needed to synchronize the capture with some blinking LEDs. So I took off the two lines that permitted the rotation and that's when suddenly, the code decided to add the same frame to the vector.
In conclusion, the writing on the hard drive works well when there is a rotation and it doesn't when there isn't (because of the vector). It confuses me so much, tell me if you see something I don't.

Unable to calculate pixel difference with opencv

I am forced to calculate pixel difference pixel by pixel between frames of a video.
I initialize the pixeldifference variable to 0. (assumeall the frames are valid frames in the video)
The problem is lastFrame and frame are always identical which means the code with the cout statement "Pixel count incremented" is never triggered. I know a couple of frames can be identical but i never see that output statement even once. Which leads me to believe the two frames are identical always. Should i do something else? I'd appreciate any guidance. I'm very new to opencv (excus a little bad coding practice inside it was for debugging purposes)
Mat lastFrame;
Mat frame;
capture.read(lastFrame);
capture.read(frame);
while(counter<tofind)
{
for(int cur_row=0_PX;cur_row<max_PX;cur_row++)
{
for(int cur_cols=0;cur_cols<frame.cols;cur_cols++)
{
Vec3b pixels_currentFrame = frame.at<cv::Vec3b>(cur_row, cur_cols);
Vec3b pixels_lastFrame = lastFrame.at<cv::Vec3b>(cur_row, cur_cols);
bCur=int(pixels_currentFrame.val[0]);
gCur=int(pixels_currentFrame.val[1]);
rCur=int(pixels_currentFrame.val[2]);
bPrev=int(pixels_lastFrame.val[0]);
gPrev=int(pixels_lastFrame.val[1]);
rPrev=int(pixels_lastFrame.val[2]);
bDiff=abs(bCur-bPrev);
gDiff=abs(gCur-gPrev);
rDiff=abs(rCur-rPrev);
if (abs(bCur-bPrev) > checkval ||
abs(rCur-rPrev) > checkval ||
abs(gCur-gPrev) > checkval)
{
pixeldifference++;
cout<<"Pixel count incremented"<<endl;
}
}
}
lastFrame=frame;
capture.read(frame);
/*
some other stuff happens here
*/
counter++;
}

Draw trajectories of objects in a video using annotations! [OpenCV]

I have several videos about 5 minutes each along with annotation data containing information about each object's bounding box coordinates at each frame number.
I am trying to read the videos and draw lines between center of bounding boxes frame by frame (when the current frame number matches the number from the ground truth data). I don't want to do this in a batch process, but every 30 or 60 frames would work for me.
Here is my code:
VideoCapture capture(path_video);
if (!capture.isOpened()){
cout << "Failed to capture frame/Open the file" << "\n";
return 0;
}
bool stop(false);
while(!stop){
capture >> frame;
if (frame.data==NULL) {
break;
}
double rate = capture.get(CV_CAP_PROP_FPS);
int delay = 1000/rate;
frmNum = (capture.get(CV_CAP_PROP_POS_FRAMES));
for (int i=0 ; i<db.size() ; i++){//db is a vector of vector that has annotation data for each object splited in inner vectors , and is sorted ascendingly on start frame number of objects.
if (!db[i].empty()){
for (int j=1 ; j<db[i].size() ; j++){
if(frmNum == db[i][j-1].currFrame){
cv::line(frame, db[i][j-1].pnt, db[i][j].pnt,Scalar(255,0,0),2);
}
else{
break;
}
}
}
}
imshow("Video", frame);
int key = waitKey(delay);
if (key==27){
break;
}
I checked and my if condition becomes true but no line is drawn on the video. I guess I don't see the lines because frames are changing and the drawn lines are cleared by new frames, But I couldnt come up with an alternative way. Thanks for your help.
If you want to draw the annotations on every frame and you don't mind that the information may be "obsolete" in some cases, I would do the following:
have a global image variable called "overlay" which would hold the most up to date (in regards to current frame number) representation of annotations and would have the same size as a single frame from the videostream
maintain an array of indices called "last_object_annotation_idx", which would store for each object the last index of its annotation already seen/used (initially set to -1 for each object)
in each iteration of the main loop, update the "last_object_annotation_idx" array (that is, for each object check if the current frame number matches the "currFrame" field of the next annotation - I am using the information that the array of annotations is sorted)
if there was a change to the array, redraw the overlay image using annotations referenced from "last_object_annotation_idx"
finally, add the overlay image to the frame and display the result.
Also, in the if statement
if(frmNum == db[i][j-1].currFrame){
cv::line(frame, db[i][j-1].pnt, db[i][j].pnt,Scalar(255,0,0),2);
}
else{
break;
}
isn't the "break" kind of wrong? It means you will break out of the loop if the first check fails, so you will not see anything past the first index in that case.

OpenCV Stitcher Class with Overlapping Stationary Cameras

I'm trying to use the OpenCV stitcher class to stitch multiple frames from a stereo setup, in which neither camera moves. I'm getting poor stitching results when running across multiple frames. I've tried a few different ways, which I'll try to explain here.
Using stitcher.stitch( )
Given a stereo pair of views, I ran the following code for some frames (VideoFile is a custom wrapper for the OpenCV VideoCapture object):
VideoFile f1( ... );
VideoFile f2( ... );
cv::Mat output_frame;
cv::Stitcher stitcher = cv::Stitcher::createDefault(true);
for( int i = 0; i < num_frames; i++ ) {
currentFrames.push_back(f1.frame( ));
currentFrames.push_back(f2.frame( ));
stitcher.stitch( currentFrames, output_mat );
// Write output_mat, put it in a named window, etc...
f1.next_frame();
f2.next_frame();
currentFrames.clear();
}
This gave really quite good results on each frame, but since the parameters are estimated each frame put in a video you could see the small differences in stitching where the parameters were slightly different.
Using estimateTransform( ) & composePanorama( )
To get past the problem of the above method, I decided to try estimating the parameters only on the first frame, then use composePanorama( ) to stitch all subsequent frames.
for( int i = 0; i < num_frames; i++ ) {
currentFrames.push_back(f1.frame( ));
currentFrames.push_back(f2.frame( ));
if( ! have_transform ) {
status = stitcher.estimateTransform( currentFrames );
}
status = stitcher.composePanorama(currentFrames, output_frame );
// ... as above
}
Sadly there appears to be a bug (documented here) causing the two views to move apart in a very odd way, as in the images below:
Frame 1:
Frame 2:
...
Frame 8:
Clearly this is useless, but I thought it may be just because of the bug, which basically keeps multiplying the intrinsic parameter matrix by a constant each time composePanorama() is called. So I did a minor patch on the bug, stopping this from happening, but then the stitching results were poor. Patch below (modules/stitching/src/stitcher.cpp), results afterwards:
243 for (size_t i = 0; i < imgs_.size(); ++i)
244 {
245 // Update intrinsics
246 // change following to *=1 to prevent scaling error, but messes up stitching.
247 cameras_[i].focal *= compose_work_aspect;
248 cameras_[i].ppx *= compose_work_aspect;
249 cameras_[i].ppy *= compose_work_aspect;
Results:
Does anyone have a clue how I can fix this problem? Basically I need to work out the transformation once, then use it on the remaining frames (we're talking 30mins of video).
I'm ideally looking for some advice on patching the stitcher class, but I would be willing to try handcoding a different solution. An earlier attempt which involved finding SURF points, correlating them and finding the homography gave fairly poor results compared to the stitcher class, so I'd rather use it if possible.
So in the end, I hacked about with the stitcher.cpp code and got something close to a solution (but not perfect as the stitching seam still moves about a lot so your mileage may vary).
Changes to stitcher.hpp
Added a new function setCameras() at line 136:
void setCameras( std::vector<detail::CameraParams> c ) {
this->cameras_ = c;
}`
Added a new private member variable to keep track of whether this is our first estimation:
bool _not_first;
Changes to stitcher.cpp
In estimateTransform() (line ~100):
this->not_first = 0;
images.getMatVector(imgs_);
// ...
In composePanorama() (line ~227):
// ...
compose_work_aspect = compose_scale / work_scale_;
// Update warped image scale
if( !this->not_first ) {
warped_image_scale_ *= static_cast<float>(compose_work_aspect);
this->not_first = 1;
}
w = warper_->create((float)warped_image_scale_);
// ...
Code calling stitcher object:
So basically, we create a stitcher object, then get the transform on the first frame (storing the camera matrices outside the stitcher class). The stitcher will then break the Intrinsic Matrix somewhere along the line causing the next frame to mess up. So before we process it, we just reset the cameras using the ones we extracted from the class.
Be warned, I had to have some error checking in case the stitcher couldn't produce an estimation with the default settings - you may need to iteratively decrease the confidence threshold using setPanoConfidenceThresh(...) before you get a result.
cv::Stitcher stitcher = cv::Stitcher::createDefault(true);
std::vector<cv::detail::CameraParams> cams;
bool have_transform = false;
for( int i = 0; i < num_frames; i++ ) {
currentFrames.push_back(f1.frame( ));
currentFrames.push_back(f2.frame( ));
if( ! have_transform ) {
status = stitcher.estimateTransform( currentFrames );
have_transform = true;
cams = stitcher.cameras();
// some code to check the status of the stitch and handle errors...
}
stitcher.setCameras( cams );
status = stitcher.composePanorama(currentFrames, output_frame );
// ... Doing stuff with the panorama
}
Please be aware that this is very much a hack of the OpenCV code, which is going to make updating to a newer version a pain. Unfortunately I was short of time so a nasty hack was all I could get round to!

Is there a better way to load in a big animation?

Maybe not really big, but a hundred frames or something. Is the only way to load it in by making an array and loading each image individually?
load_image() is a function I made which loads the images and converts their BPP.
expl[0] = load_image( "explode1.gif" );
expl[1] = load_image( "explode2.gif" );
expl[2] = load_image( "explode3.gif" );
expl[3] = load_image( "explode4.gif" );
...
expl[99] = load_image( "explode100.gif" );
Seems like their should be a better way.. at least I hope.
A common technique is spritesheets, in which a single, large image is divided into a grid of cells, with each cell containing one frame of an animation. Often, all animation frames for any game entity are placed on a single, sometimes huge, sprite sheet.
maybe simplify your loading with a utility function that builds a filename for each iteration of a loop:
LoadAnimation(char* isFileBase, int numFrames)
{
char szFileName[255];
for(int i = 0; i < numFrames; i++)
{
// append the frame number and .gif to the file base to get the filename
sprintf(szFileName, "%s%d.gif", isFileBase, i);
expl[i] = load_image(szFileName);
}
}
Instead of loading as a grid, stack all the frames in one vertical strip (same image). Then you only need to know how many rows per frame and you can set a pointer to the frame row offset. You end up still having contiguous scan lines that can be displayed directly or trivially chewed off into separate images.