Recording desktop to file, using openCV - c++

I'm attempting to utilize OpenCV, C++, on my windows 10 system to record the screen as part of a larger program I am writing. I need the ability to record the display and save the recording for later review.
I was able to find this link on stackoverflow
How to capture the desktop in OpenCV (ie. turn a bitmap into a Mat)?
User john ktejik created a function that in essence completed exactly what I am looking to accomplish, short of saving the stream to file.
Now what I have always done in the past was once I've opened a connection to my webcam or a video file, I could simply create a VideoWriter Object and write the individual frames to file. I have attempted to do just that utilizing John's function to act as a video source.
int main (int argc, char **argv)
{
HWND hwndDesktop = GetDesktopWindow ();
int key = 0;
int frame_width = 1920;
int frame_height = 1080;
VideoWriter video ("screenCap.avi", CV_FOURCC ('M', 'J', 'P', 'G'), 15, Size (frame_width, frame_height));
while (key != 27)
{
Mat src = hwnd2mat (hwndDesktop);
video.write (src);
imshow ("Screen Capture", src);
key = waitKey (27);
}
video.release ();
destroyAllWindows ();
return 0;
}
What I'm seeing as the output, is the file labeled "screenCap.avi", however the file is empty of video. The file saves as 16KB storage space.
John's function is on point, as it displays the frames just fine via imshow(), but doesn't seem to allow me to save them.

So over the weekend I played with the software some more. And as I really don't have a firm grasp on it, I figured that there had to be a problem with settings between the screen capture and the file writer.
So I started looking at each of the lines in John's function. I came across
src.create(height, width, CV_8UC4);
It seems that the Mat object is being created as with 4 color channels. Did a bit more digging and I found a couple references that point to Videowriter expecting 3 channels.
So a simple change was to convert the output of Johns function from 4 channels to 3 channels. This fixed the problem and I can now write the frames to file.
int main (int argc, char **argv)
{
HWND hwndDesktop = GetDesktopWindow ();
int key = 0;
int frame_width = 1920;
int frame_height = 1080;
VideoWriter video ("screenCap.avi", CV_FOURCC ('M', 'J', 'P', 'G'), 15, Size
(frame_width, frame_height));
while (key != 27)
{
Mat src = hwnd2mat (hwndDesktop);
Mat dst;
cvtColor (src, dst, COLOR_BGRA2RGB);
video.write (dst);
imshow ("Screen Capture", dst);
key = waitKey (27);
}
video.release ();
destroyAllWindows ();
return 0;
}

Related

Reading h264 or h265 stream with ffmpeg/OpenCV: Which is faster?

I'm using OpenCV with ffmpeg support to read a RTSP stream coming from an IP camera and then to write the frames to a video. The problem is that the frame size is 2816x2816 at 20 fps i.e. there's a lot of data coming in.
I noticed that there was a significant delay in the stream, so I set the buffer size of the cv::VideoCapture object to 1, because I thought that the frames might just get stuck in the buffer instead of being grabbed and processed. This however just caused for frames to be dropped instead.
My next move was to experiment a bit with the frame size/fps and the encoding of the video that I'm writing. All of those things helped to improve the situation, but in the long run I still have to use a frame size of 2816x2816 and support up to 20 fps, so I can't set it lower sadly.
That's where my question comes in: given the fact that the camera stream is going to be either h264 or h265, which one would be read faster by the cv::VideoCapture object? And how should I encode the video I'm writing in order to minimize the time spent decoding/encoding frames?
That's the code I'm using for reference:
using namespace cv;
int main(int argc, char** argv)
{
VideoCapture cap;
cap.set(CAP_PROP_BUFFERSIZE, 1); // internal buffer will now store only 1 frames
if (!cap.open("rtsp://admin:admin#1.1.1.1:554/stream")) {
return -1;
}
VideoWriter videoWr;
Mat frame;
cap >> frame;
//int x264 = cv::VideoWriter::fourcc('x', '2', '6', '4'); //I was trying different options
int x264 = cv::VideoWriter::fourcc('M', 'J', 'P', 'G');
videoWr = cv::VideoWriter("test_video.avi", 0, 0, 20, frame.size(), true);
namedWindow("test", WINDOW_NORMAL);
cv::resizeWindow("test", 1024, 768);
for (;;)
{
cap >> frame;
if (frame.empty()) break; // end of video stream
imshow("test", frame);
if (waitKey(10) == 27) break;
videoWr << frame;
}
return 0;
}

bad quality, when rendering images from camera in qt4

My code:
camera = new RaspiCam_Cv();//raspbery pi library
camera->set(CV_CAP_PROP_FORMAT,CV_8UC1); //this is monochrome 8 bit format
camera->set(CV_CAP_PROP_FRAME_WIDTH, 960);
camera->set(CV_CAP_PROP_FRAME_HEIGHT,720);
while (1){
camera->grab();//for linux
unsigned char* buff = camera->getImageBufferData();
QPixmap pic = QPixmap::fromImage(QImage( buff, camWidth_, camHeight_, camWidth_ * 1, QImage::Format_Indexed8 ));
label->setPixmap(pic);
}
The problem is bad quality! I found out that the problem happens when using QImage, when using openCv Mat, everything is good!
Same thing happens in other Qt based programs, like this one (same bad quality): https://code.google.com/p/qt-opencv-multithreaded/
Here is a pic, where the problem is shown. there is a white page in front of the camera, so if all went as it should, you should see clean gray image.
You are resizing the image using pixmap and label transformations, which are worse than the one of QImage. This is due to pixmap being optimized for display and not for anything else. The pixmap size should be the same of the label to avoid any further resizing.
QImage img =QImage(
buff,
camWidth_,
camHeight_,
camWidth_ * 1,
QImage::Format_Indexed8 ).scaled(label->size());
label->setPixmap(QPixmap::fromImage(img));
This is not an answer, but it's too hard to share code in the comments.
Can you please test this code and tell me whether the result is good or bad?
int main(int argc, char** argv)
{
RaspiCam_Cv *camera = new RaspiCam_Cv();
camera->set(CV_CAP_PROP_FORMAT , CV_8UC1) ;
camera->set(CV_CAP_PROP_FRAME_WIDTH, 960);
camera->set(CV_CAP_PROP_FRAME_HEIGHT,720);
namedWindow("Output",CV_WINDOW_AUTOSIZE);
while (1)
{
Mat frame;
camera.grab();
//camera.retrieve ( frame);
unsigned char* buff = camera->getImageBufferData();
frame = cv::Mat(720, 960, CV_8UC1, buff);
imshow("Output", frame);
if (waitKey(30) == 27)
{ cout << "Exit" << endl; break; }
}
camera->~RaspiCam_Cv();
return 0;
}
Your provided images look like the color depth is only 16 Bit.
For comparison, here's the provided captured image:
and here's the same image, transformed to 16 bit color space in IrfanView (without Floyd-Steinberg-Dithering).
In the comments we found out that the Raspberry Pi Output Buffer was set to 16 Bit. and setting it to 24 Bit helped.
But I can't explain why rendering the image on the pi with OpenCV's cv::imshow produced well looking images on the Monitor/TV...

Why this code doesn't write any data to output file to create movie using opencv

I am trying to create some video from a set of large raw images that I have.
The code is as follow:
int imageWidth=687;
int imageHeight=916;
int fps=3;
int ex=-1;
CvSize size = cvSize(imageWidth,imageHeight);
VideoWriter outputVideo;
outputVideo.open(MovieOutput, ex, fps, size, true);
if(outputVideo.isOpened())
{
cout << "error opening output video";
}
for(int frameNo=0;frameNo<58;frameNo++)
{
ostringstream outfilename;
outfilename << InputDir<< (frameNo+1)<<".jpg";
rawimages.Read(frameNo);
Mat image=rawimages.ToOpencvImage();
imwrite( outfilename.str(), image );
outputVideo <<image;
imshow("Image", image);
if(waitKey(30) >= 0) break;
}
I can see that images are shown on screen and also different jpg are saved on hard disk.
I can see that the output avi is created, but its size is zero.
What is the problem with this code?
some note:
The output size is very big. Can it generate movie with that size?
To summarize the comments: you pass the second parameter to the VideoWriter open command with a value of -1. This is supposed to open a codec selection dialogue in Windows, but as of OpenCV 2.4.5, the dialogue seems to be bugged - it appears, but I couldn't manage to write to a file afterwards.
Selecting a codec directly works fine and makes more sense in my opinion. More info about this command and available codecs can be found here.
outputVideo.open("example.avi", CV_FOURCC('M','J','P','G'), fps, size, true);

OpenCV : convert the pointer in memory to image

I have a grabber which can get the images and show them on the screen with the following code
while((lastPicNr = Fg_getLastPicNumberBlockingEx(fg,lastPicNr+1,0,10,_memoryAllc))<200) {
iPtr=(unsigned char*)Fg_getImagePtrEx(fg,lastPicNr,0,_memoryAllc);
::DrawBuffer(nId,iPtr,lastPicNr,"testing"); }
but I want to use the pointer to the image data and display them with OpenCV, cause I need to do the processing on the pixels. my camera is a CCD mono camera and the depth of the pixels is 8bits. I am new to OpenCV, is there any option in opencv that can get the return of the (unsigned char*)Fg_getImagePtrEx(fg,lastPicNr,0,_memoryAllc); and disply it on the screen? or get the data from the iPtr pointer an allow me to use the image data?
Creating an IplImage from unsigned char* raw_data takes 2 important instructions: cvCreateImageHeader() and cvSetData():
// 1 channel for mono camera, and for RGB would be 3
int channels = 1;
IplImage* cv_image = cvCreateImageHeader(cvSize(width,height), IPL_DEPTH_8U, channels);
if (!cv_image)
{
// print error, failed to allocate image!
}
cvSetData(cv_image, raw_data, cv_image->widthStep);
cvNamedWindow("win1", CV_WINDOW_AUTOSIZE);
cvShowImage("win1", cv_image);
cvWaitKey(10);
// release resources
cvReleaseImageHeader(&cv_image);
cvDestroyWindow("win1");
I haven't tested the code, but the roadmap for the code you are looking for is there.
If you are using C++, I don't understand why your are not doing it the simple way like this:
If your camera is supported, I would do it this way:
cv::VideoCapture capture(0);
if(!capture.isOpened()) {
// print error
return -1;
}
cv::namedWindow("viewer");
cv::Mat frame;
while( true )
{
capture >> frame;
// ... processing here
cv::imshow("viewer", frame);
int c = cv::waitKey(10);
if( (char)c == 'c' ) { break; } // press c to quit
}
I would recommend starting to read the docs and tutorials which you can find here.

OpenCV : How to display webcam capture in windows form application?

generally we display webcam or video motion in opencv windows with :
CvCapture* capture = cvCreateCameraCapture(0);
cvNamedWindow( "title", CV_WINDOW_AUTOSIZE );
cvMoveWindow("title",x,y);
while(1)
{
frame = cvQueryFrame( capture );
if( !frame )
{
break;
}
cvShowImage( "title", frame );
char c = cvWaitKey(33);
if( c == 27 )
{
break;
}
}
i tried to use pictureBox that is successful to display image in windows form with this :
pictureBox1->Image = gcnew System::Drawing::Bitmap( image->width,image->height,image->widthStep,System::Drawing::Imaging::PixelFormat::Undefined, ( System::IntPtr ) image-> imageData);
but when im trying to display captured image from video it wont works, here is the source :
CvCapture* capture = cvCreateCameraCapture(0);
while(1)
{
frame = cvQueryFrame( capture );
if( !frame )
{
break;
}
pictureBox1->Image = gcnew System::Drawing::Bitmap( frame->width,frame->height,frame->widthStep,System::Drawing::Imaging::PixelFormat::Undefined, ( System::IntPtr ) frame-> imageData);
char c = cvWaitKey(33);
if( c == 27 )
{
break;
}
}
is there anyway to use windows form instead opencv windows to show video or webcam?
or is there something wrong with my code?
thanks for your help.. :)
Piece of advice : use VideoInput instead of CvCapture (CvCapture is a part of highgui a library that is not intended for production use, but just for quick testing). Yes the VideoInput homepage looks strange, but the library is quite worthwhile.
Here is a quick sample for the usage of VideoInput (extracted from the VideoInput.h file):
//create a videoInput object
videoInput VI;
//Prints out a list of available devices and returns num of devices found
int numDevices = VI.listDevices();
int device1 = 0; //this could be any deviceID that shows up in listDevices
int device2 = 1; //this could be any deviceID that shows up in listDevices
//if you want to capture at a different frame rate (default is 30)
//specify it here, you are not guaranteed to get this fps though.
//VI.setIdealFramerate(dev, 60);
//setup the first device - there are a number of options:
VI.setupDevice(device1); //setup the first device with the default settings
//VI.setupDevice(device1, VI_COMPOSITE); //or setup device with specific connection type
//VI.setupDevice(device1, 320, 240); //or setup device with specified video size
//VI.setupDevice(device1, 320, 240, VI_COMPOSITE); //or setup device with video size and connection type
//VI.setFormat(device1, VI_NTSC_M); //if your card doesn't remember what format it should be
//call this with the appropriate format listed above
//NOTE: must be called after setupDevice!
//optionally setup a second (or third, fourth ...) device - same options as above
VI.setupDevice(device2);
//As requested width and height can not always be accomodated
//make sure to check the size once the device is setup
int width = VI.getWidth(device1);
int height = VI.getHeight(device1);
int size = VI.getSize(device1);
unsigned char * yourBuffer1 = new unsigned char[size];
unsigned char * yourBuffer2 = new unsigned char[size];
//to get the data from the device first check if the data is new
if(VI.isFrameNew(device1)){
VI.getPixels(device1, yourBuffer1, false, false); //fills pixels as a BGR (for openCV) unsigned char array - no flipping
VI.getPixels(device1, yourBuffer2, true, true); //fills pixels as a RGB (for openGL) unsigned char array - flipping!
}
//same applies to device2 etc
//to get a settings dialog for the device
VI.showSettingsWindow(device1);
//Shut down devices properly
VI.stopDevice(device1);
VI.stopDevice(device2);
The pixel format should be known when you capture images from a camera, it's very likely the format of 24-bit BGR. System::Drawing::Imaging::PixelFormat::Format24bppRgb will be the closest format but you might get weird color display. A re-arrange of the color component will solve this problem.
Actually, there are .net version opencv library available here:
http://code.google.com/p/opencvdotnet/
and here:
http://www.emgu.com/wiki/index.php/Main_Page
Hope it helps!
I don't know if you will like this, but you could use OpenGL to show the video stream in other windows than the ones provided with opencv. (Capture the frame and display it on a rectangle.. or something like that..)
Another option you might want to consider is using emgu. This is a .Net wrapper for opencv with winforms controlls.