I am trying to read a video with OpenCV in C++, but when the video is displayed, the framerate is very slow, like 10% of the original framerate.
The whole code is here:
// g++ `pkg-config --cflags --libs opencv` play-video.cpp -o play-video
// ./play-video [video filename]
#include <iostream>
#include <opencv2/core/core.hpp>
#include <opencv2/highgui/highgui.hpp>
using namespace std;
using namespace cv;
int main(int argc, char** argv)
{
// video filename should be given as an argument
if (argc == 1) {
cerr << "Please give the video filename as an argument" << endl;
exit(1);
}
const string videofilename = argv[1];
// we open the video file
VideoCapture capture(videofilename);
if (!capture.isOpened()) {
cerr << "Error when reading video file" << endl;
exit(1);
}
// we compute the frame duration
int FPS = capture.get(CV_CAP_PROP_FPS);
cout << "FPS: " << FPS << endl;
int frameDuration = 1000 / FPS; // frame duration in milliseconds
cout << "frame duration: " << frameDuration << " ms" << endl;
// we read and display the video file, image after image
Mat frame;
namedWindow(videofilename, 1);
while(true)
{
// we grab a new image
capture >> frame;
if(frame.empty())
break;
// we display it
imshow(videofilename, frame);
// press 'q' to quit
char key = waitKey(frameDuration); // waits to display frame
if (key == 'q')
break;
}
// releases and window destroy are automatic in C++ interface
}
I tried with a video from a GoPro Hero 3+, and with a video from my MacBook's webcam, same problem with both videos. Both videos are played without problem by VLC.
Thanks in advance.
Try reducing the waitKey frame wait time. You are effectively waiting for the frame rate time (i.e. 33 ms), plus all the time it takes to grab the frame and display it. This means that if capturing the frame and displaying it takes over 0ms (which it does), you are guaranteed to be waiting for too long. Or if you really want to be accurate, you could time how long that part takes, and wait for the remainder, e.g. something along the lines of:
while(true)
{
auto start_time = std::chrono::high_resolution_clock::now();
capture >> frame;
if(frame.empty())
break;
imshow(videofilename, frame);
auto end_time = std::chrono::high_resolution_clock::now();
int elapsed_time = std::chrono::duration_cast<std::chrono::milliseconds>(end_time - start_time).count();
//make sure we call waitKey with some value > 0
int wait_time = std::max(1, elapsed_time);
char key = waitKey(wait_time); // waits to display frame
if (key == 'q')
break;
}
The whole int wait_time = std::max(1, elapsed_time); line is just to ensure that we wait for at least 1 ms, as OpenCV needs to have a call to waitKey in there to fetch and handle events, and calling waitKey with a value <= 0 tells it to wait infinity for a user input, which we don't want either (in this case)
Related
I'm coding a simple OpenCV example to get webcam frames during 5 seconds (~150 frames if the cam works at ~30fps).
After making some tests, I see that sometimes I get ~140 out of 150 frames (acceptable, I guess), but sometimes ~70. Even worse, sometimes the camera seems to suffer from those frame drops and keep in that state for hours.
To investigate the problem further, I stripped my program until I still have that issue even if I only read frames, but do not write them to disk. I've set a cron job in order to run a 5-seconds capture every minute, and I've been seen things like this:
I think the two first small drops were due to system being busy, but the big, permanent one occurred in the middle of the night. In the morning, I stopped the cron job, touched some things in the code (I can't remember exactly what) and started the test again, to see a gradual recovery followed by a new drop after 2-3 hours:
Since yesterday, I've turned off the computer for several hours, booted it up again and covered the webcam to ensure constant light conditions, but the frame count is still low, and stuck on 70. Also, it's really weird that the drop (70 frames out of 150) goes to exactly half the max frames I've seen in this camera (140 frames out of 150).
The webcam model is Logitech C525. I'm also testing in a Macbook Pro Late 2016 Facetime HD camera and there I see a constant 117 frames out of 150. A colleague of mine also sees frame drops in his laptop. Is there some problem with my code? Could it be thread priority?
// Call the program like this: ./cameraTest pixelWidth pixelHeight fps timeLimit
// timeLimit can be 1 to run a fixed 5-seconds capture, or 0 to wait for 150 frames.
#include "opencv2/opencv.hpp"
#include "iostream"
#include "thread"
#include <unistd.h>
#include <chrono>
#include <ctime>
#include <experimental/filesystem>
using namespace cv;
namespace fs = std::experimental::filesystem;
VideoCapture camera(0);
bool stop = false;
int readFrames = 0;
std::string getTimeStamp()
{
time_t rawtime;
struct tm * timeinfo;
char buffer[80];
time(&rawtime);
timeinfo = localtime(&rawtime);
strftime(buffer,sizeof(buffer),"%Y-%m-%d %H:%M:%S",timeinfo);
std::string timeStamp(buffer);
return timeStamp;
}
void getFrame()
{
Mat frame;
camera >> frame;
// camera.read(frame);
// cv::imwrite("/tmp/frames/frame" + std::to_string(readFrames) + ".jpg", frame);
readFrames++;
}
void getFrames()
{
Mat frame;
while(!stop)
{
camera >> frame;
// cv::imwrite("/tmp/frames/frame" + std::to_string(fc) + ".jpg", frame);
readFrames++;
}
}
int main(int argc, char* argv[])
{
if(argc < 5)
{
std::cout << "Usage: width height fps timeLimit" << std::endl;
return -1;
}
if(!camera.isOpened())
{
std::cout << "Couldn't open camera " << getTimeStamp() << std::endl;
return -1;
}
if (!fs::is_directory("/tmp/frames"))
{
if(system("mkdir -p /tmp/frames") != 0)
{
std::cout << "Error creating /tmp/frames/" << std::endl;
}
}
if (!fs::is_empty("/tmp/frames"))
{
system("exec rm /tmp/frames/*");
}
camera.set(CV_CAP_PROP_FRAME_WIDTH, atoi(argv[1]));
camera.set(CV_CAP_PROP_FRAME_HEIGHT, atoi(argv[2]));
camera.set(CV_CAP_PROP_FPS, atoi(argv[3]));
//camera.set(CV_CAP_PROP_FOURCC, CV_FOURCC('M', 'J', 'P', 'G'));
bool timeLimit(atoi(argv[4]));
std::chrono::steady_clock::time_point begin = std::chrono::steady_clock::now();
int waitSeconds = 5;
if(timeLimit)
{
std::thread tr(getFrames);
usleep(waitSeconds * 1e6);
stop = true;
tr.join();
}
else
{
while(readFrames < 150)
{
getFrame();
}
}
std::chrono::steady_clock::time_point end = std::chrono::steady_clock::now();
std::cout << getTimeStamp() << " " << readFrames << "/" << atoi(argv[3]) * waitSeconds << " "
<< std::chrono::duration_cast<std::chrono::milliseconds>(end - begin).count() << "ms"
<< " " << atoi(argv[1]) << "," << atoi(argv[2]) << "," << atoi(argv[3])
<< std::endl;
return 0;
}
It seems it has to do with low-light conditions. As soon as I uncovered the camara, I saw the frames increase to their expected value. So maybe on poor light conditions, the camera changes some settings or further processes the images, lowering its framerate.
Following my previous question, I want to extract frames from a video. But only 2 seconds at the beginning of the video.
I want to work with raw video as much as I can, that why I don't want to cut the original video and then process it.
Here's my code to extract frame. But this will extract all frame from the video :
#include <opencv2/core/core.hpp>
#include <opencv2/highgui/highgui.hpp>
#include <opencv2/imgproc/imgproc.hpp>
#include <iostream>
#include <string>
#include <sstream>
using namespace cv;
using namespace std;
int c = 0;
string int2str(int &);
int main(int argc, char **argv) {
string s;
VideoCapture cap("test_video.mp4");
if (!cap.isOpened())
{
cout << "Cannot open the video file" << endl;
return -1;
}
double fps = cap.get(CV_CAP_PROP_FPS);
cout << "Frame per seconds : " << fps << endl;
namedWindow("MyVideo", CV_WINDOW_NORMAL);
resizeWindow("MyVideo", 600, 600);
while (1)
{
Mat frame;
Mat Gray_frame;
bool bSuccess = cap.read(frame);
if (!bSuccess)
{
cout << "Cannot read the frame from video file" << endl;
break;
}
s = int2str(c);
//cout<<("%d\n",frame )<<endl;
c++;
imshow("MyVideo", frame);
imwrite("frame" + s + ".jpg", frame);
if (waitKey(30) == 27)
{
cout << "esc key is pressed by user" << endl;
break;
}
}
return 0;
}
string int2str(int &i) {
string s;
stringstream ss(s);
ss << i;
return ss.str();
}
And advice ? Thanks.
It seems like you already know the FPS of the video. So isn't it just a matter of counting how many frames you have extracted and breaking after you reach FPS * 2?
double fps = cap.get(CV_CAP_PROP_FPS);
int framesToExtract = fps * 2;
for (int frames = 0; frames < framesToExtract; ++frames)
{
... // your processing here
}
Also, I looked at your previous question and it seems like you have a misunderstanding of FPS?
FPS = Frames Per Second, this means how many frames there are every second of the video. So let's say your video is 120 FPS. This means there are 120 frames in one second of video, 240 in two seconds and so on.
So (VIDEO_FPS * x) gets you the amount of frames in x seconds of the video.
I'm currently getting the webcam feed of my laptop using VideoCapture cap(0) function and then display it in a Mat frame. What I want to do next is whenever I press a key 'c' for example, it takes the screenshot of the frame and save it into a folder as a JPEG image. However I have no idea on how to do so. Help is much needed, thank you.
I have spent several days searching the internet for the right solution with simple keyboard input. Ther was allways some leg / delay while using cv::waitKey.
The solution i have found is with adding Sleep(5) just after the capturing the frame from webcam.
The below example is a combination of different forum threads.
It works without any leg / delay. Windows OS.
Press "q" to capture and save the frame.
There is a webcam feed always present. You can change the sequence to show the captured frame / image.
PS "tipka" - means "key" on the keyboard.
Regards, Andrej
#include <opencv2/opencv.hpp>
#include <iostream>
#include <stdio.h>
#include <windows.h> // For Sleep
using namespace cv;
using namespace std;
int ct = 0;
char tipka;
char filename[100]; // For filename
int c = 1; // For filename
int main(int, char**)
{
Mat frame;
//--- INITIALIZE VIDEOCAPTURE
VideoCapture cap;
// open the default camera using default API
cap.open(0);
// OR advance usage: select any API backend
int deviceID = 0; // 0 = open default camera
int apiID = cv::CAP_ANY; // 0 = autodetect default API
// open selected camera using selected API
cap.open(deviceID + apiID);
// check if we succeeded
if (!cap.isOpened()) {
cerr << "ERROR! Unable to open camera\n";
return -1;
}
//--- GRAB AND WRITE LOOP
cout << "Start grabbing" << endl
<< "Press a to terminate" << endl;
for (;;)
{
// wait for a new frame from camera and store it into 'frame'
cap.read(frame);
if (frame.empty()) {
cerr << "ERROR! blank frame grabbed\n";
break;
}
Sleep(5); // Sleep is mandatory - for no leg!
// show live and wait for a key with timeout long enough to show images
imshow("CAMERA 1", frame); // Window name
tipka = cv::waitKey(30);
if (tipka == 'q') {
sprintf_s(filename, "C:/Images/Frame_%d.jpg", c); // select your folder - filename is "Frame_n"
cv::waitKey(10);
imshow("CAMERA 1", frame);
imwrite(filename, frame);
cout << "Frame_" << c << endl;
c++;
}
if (tipka == 'a') {
cout << "Terminating..." << endl;
Sleep(2000);
break;
}
}
// the camera will be deinitialized automatically in VideoCapture destructor
return 0;
}
Does anyone know how to get OpenCV to grab all the frames from a video file?
I've been trying to just grab frames from video files (.wmv files, in particular), but on most videos, I end up with "nan" as my framerate and it only gets a single frame from my video and doesn't think there are any more. However, on at least one video, it succeeds and gets the right framerate. I tried to manually set the frame rate, but this did not work.
One thing to note is that it seems like the videos where it doesn't work are very short (~5 seconds long). However, I have not thoroughly tested this theory as I don't have very many videos (only ~10 videos).
The minimum code necessary to reproduce this is below:
int main(int argc, char** argv)
{
VideoCapture capture;
char* video = argv[1];
int flag = arg_parse(argc, argv);
capture.open(video);
//capture.set(CV_CAP_PROP_FPS, 25); // Trying to set frame rate.
std::cout << "frame rate: " << capture.get(CV_CAP_PROP_FPS) << std::endl;
if(!capture.isOpened()) {
fprintf(stderr, "Could not initialize capturing..\n");
return -1;
}
while(true) {
Mat frame;
int i, j, c;
// get a new frame
capture >> frame;
std::cout << "GOT FRAME!" << std::endl;
if(frame.empty()) {
std::cout << "breaking..." << std::endl;
break;
}
}
return 0;
}
Thanks so much!
I am using OpenCV 2.4.8 and Visual Studio 2013 to run the following simple VideoCapture program. The program is intended to capture the video from the webcam and display it.
However, the program works fine only for the FIRST TIME (after signing in windows), and doesn't work second time.
The problem I get on debugging is :
After executing this line - "bool bSuccess = cap.read(frame);" frame variable is still NULL.
#include "stdafx.h"
#include "opencv2/highgui/highgui.hpp"
#include <iostream>
using namespace cv;
using namespace std;
char key;
int main(int argc, char* argv[])
{
VideoCapture cap(0); // open the video camera no. 0
if(!cap.isOpened()) // if not success, exit program
{
cout << "Cannot open the video file" << endl;
return -1;
}
double dWidth = cap.get(CV_CAP_PROP_FRAME_WIDTH); //get the width of frames of the video
double dHeight = cap.get(CV_CAP_PROP_FRAME_HEIGHT); //get the height of frames of the video
cout << "Frame size : " << dWidth << " x " << dHeight << endl;
namedWindow("MyVideo",CV_WINDOW_AUTOSIZE); //create a window called "MyVideo"
Mat frame;
while(1)
{
bool bSuccess = cap.read(frame); // read a new frame from video
if (!bSuccess) //if not success, break loop
{
cout << "Cannot read a frame from video file" << endl;
break;
}
imshow("MyVideo", frame); //show the frame in "MyVideo" window
if(waitKey(30) == 27) //wait for 'esc' key press for 30ms. If 'esc' key is pressed, break loop
{
cout << "esc key is pressed by user" << endl;
break;
}
}
return 0;
}
This happen because the camera is not correctly closed after first instance of program. You should try to close the console by esc button and not by clicking X.
Could you try and read more than a single frame before breaking the loop? This may be similar to this problem, where a corrupted first frame / slow camera set up was the only problem.
Unable to read frames from VideoCapture from secondary webcam with OpenCV