I\'m trying to find a fast way to take screenshots (as in 30 fps or over) for use with opencv in c++
All the information I\'ve found online either involved windows.h or
Based on @abc's answer I came up with the following, using MIT's shared memory extension for X.
@abc's example runs at 120-180 fps and uses ~40% of a Titan X (Maxwell). The following runs at 15000 fps and uses 80% of a Titan X. (Assuming you don't sync. If you do sync, then the framerate drops to @abc's framerate!)
Comment out the break;
statement inside the inner loop too run it nonstop.
Version using @abc's class:
// g++ screena.cpp -o screena -lX11 -lXext -Ofast -mfpmath=both -march=native -m64 -funroll-loops -mavx2 `pkg-config opencv --cflags --libs` && ./screena
#include
#include
#include
#include
#include
#include // This includes most headers!
#include
#define FPS(start) (CLOCKS_PER_SEC / (clock()-start))
struct ScreenShot{
ScreenShot(uint x, uint y, uint width, uint height):
x(x), y(y), width(width), height(height){
display = XOpenDisplay(nullptr);
root = DefaultRootWindow(display);
XGetWindowAttributes(display, root, &window_attributes);
screen = window_attributes.screen;
ximg = XShmCreateImage(display, DefaultVisualOfScreen(screen), DefaultDepthOfScreen(screen), ZPixmap, NULL, &shminfo, width, height);
shminfo.shmid = shmget(IPC_PRIVATE, ximg->bytes_per_line * ximg->height, IPC_CREAT|0777);
shminfo.shmaddr = ximg->data = (char*)shmat(shminfo.shmid, 0, 0);
shminfo.readOnly = False;
if(shminfo.shmid < 0)
puts("Fatal shminfo error!");;
Status s1 = XShmAttach(display, &shminfo);
printf("XShmAttach() %s\n", s1 ? "success!" : "failure!");
init = true;
}
void operator() (cv::Mat& cv_img){
if(init)
init = false;
XShmGetImage(display, root, ximg, 0, 0, 0x00ffffff);
cv_img = cv::Mat(height, width, CV_8UC4, ximg->data);
}
~ScreenShot(){
if(!init)
XDestroyImage(ximg);
XShmDetach(display, &shminfo);
shmdt(shminfo.shmaddr);
XCloseDisplay(display);
}
Display* display;
Window root;
XWindowAttributes window_attributes;
Screen* screen;
XImage* ximg;
XShmSegmentInfo shminfo;
int x, y, width, height;
bool init;
};
int main(){
ScreenShot screen(0, 0, 1920, 1080);
cv::Mat img;
for(uint i;; ++i){
double start = clock();
screen(img);
if(!(i & 0b111111))
printf("fps %4.f spf %.4f\n", FPS(start), 1 / FPS(start));
break;
}
cv::imshow("img", img);
cv::waitKey(0);
}
"Naked" version:
// g++ xshm2.c -o xshm2 -lX11 -lXext `$cv`-Ofast -mfpmath=both -march=native -m64 -funroll-loops -mavx2 && ./xshm2
#include
#include
#include
#include
#include
#include // This includes most headers!
#include
#define FPS(start) (CLOCKS_PER_SEC / (clock()-start))
// Using one monitor DOESN'T improve performance! Querying a smaller subset of the screen DOES
const uint WIDTH = 1920>>0;
const uint HEIGHT = 1080>>0;
// -------------------------------------------------------
int main(){
Display* display = XOpenDisplay(NULL);
Window root = DefaultRootWindow(display); // Macro to return the root window! It's a simple uint32
XWindowAttributes window_attributes;
XGetWindowAttributes(display, root, &window_attributes);
Screen* screen = window_attributes.screen;
XShmSegmentInfo shminfo;
XImage* ximg = XShmCreateImage(display, DefaultVisualOfScreen(screen), DefaultDepthOfScreen(screen), ZPixmap, NULL, &shminfo, WIDTH, HEIGHT);
shminfo.shmid = shmget(IPC_PRIVATE, ximg->bytes_per_line * ximg->height, IPC_CREAT|0777);
shminfo.shmaddr = ximg->data = (char*)shmat(shminfo.shmid, 0, 0);
shminfo.readOnly = False;
if(shminfo.shmid < 0)
puts("Fatal shminfo error!");;
Status s1 = XShmAttach(display, &shminfo);
printf("XShmAttach() %s\n", s1 ? "success!" : "failure!");
cv::Mat img;
for(int i; ; i++){
double start = clock();
XShmGetImage(display, root, ximg, 0, 0, 0x00ffffff);
img = cv::Mat(HEIGHT, WIDTH, CV_8UC4, ximg->data);
if(!(i & 0b111111))
printf("fps %4.f spf %.4f\n", FPS(start), 1 / FPS(start));
break;
}
cv::imshow("img", img);
cv::waitKey(0);
XShmDetach(display, &shminfo);
XDestroyImage(ximg);
shmdt(shminfo.shmaddr);
XCloseDisplay(display);
puts("Exit success!");
}