【发布时间】:2014-09-19 05:20:44
【问题描述】:
我正在尝试寻找一种快速的方法来截取屏幕截图(如 30 fps 或更高),以便在 c++ 中与 opencv 一起使用
我在网上找到的所有信息要么涉及 windows.h,要么太慢。
有人可以提供一些代码来实现这一点,或者至少为我指明正确的方向,以便我自己解决这个问题吗?
【问题讨论】:
标签: c++ linux performance opencv screenshot
我正在尝试寻找一种快速的方法来截取屏幕截图(如 30 fps 或更高),以便在 c++ 中与 opencv 一起使用
我在网上找到的所有信息要么涉及 windows.h,要么太慢。
有人可以提供一些代码来实现这一点,或者至少为我指明正确的方向,以便我自己解决这个问题吗?
【问题讨论】:
标签: c++ linux performance opencv screenshot
您可以使用它来将屏幕截图转换为原始像素结构。将它与 Width & Height & BitsPerPixel 一起传递给 OpenCV,你应该会很好。
#include <X11/Xlib.h>
#include <X11/Xutil.h>
#include <cstdint>
#include <cstring>
#include <vector>
void ImageFromDisplay(std::vector<uint8_t>& Pixels, int& Width, int& Height, int& BitsPerPixel)
{
Display* display = XOpenDisplay(nullptr);
Window root = DefaultRootWindow(display);
XWindowAttributes attributes = {0};
XGetWindowAttributes(display, root, &attributes);
Width = attributes.width;
Height = attributes.height;
XImage* img = XGetImage(display, root, 0, 0 , Width, Height, AllPlanes, ZPixmap);
BitsPerPixel = img->bits_per_pixel;
Pixels.resize(Width * Height * 4);
memcpy(&Pixels[0], img->data, Pixels.size());
XDestroyImage(img);
XCloseDisplay(display);
}
然后将其与OpenCV 一起使用,您可以这样做:
int main()
{
int Width = 0;
int Height = 0;
int Bpp = 0;
std::vector<std::uint8_t> Pixels;
ImageFromDisplay(Pixels, Width, Height, Bpp);
if (Width && Height)
{
Mat img = Mat(Height, Width, Bpp > 24 ? CV_8UC4 : CV_8UC3, &Pixels[0]); //Mat(Size(Height, Width), Bpp > 24 ? CV_8UC4 : CV_8UC3, &Pixels[0]);
namedWindow("WindowTitle", WINDOW_AUTOSIZE);
imshow("Display window", img);
waitKey(0);
}
return 0;
}
【讨论】:
memcpy 替换为对像素图像的 OpenCV api 的调用,或者您需要像素的图像..
g++ main.cpp -std=c++0x -o app 'pkg-config --cflags --libs opencv x11'
ImageFromDisplay 中使用XDestroyImage(img); 而不是XFree(img);。
根据@abc 的回答,我想出了以下内容,使用 MIT 的 X 共享内存扩展。
@abc 的示例以 120-180 fps 的速度运行,并使用了大约 40% 的 Titan X (Maxwell)。以下以 15000 fps 运行并使用 Titan X 的 80%。(假设您不同步。如果您进行同步,那么帧率下降到@abc 的帧率!)
注释掉内部循环中的break; 语句,也可以不间断地运行它。
使用@abc 类的版本:
// g++ screena.cpp -o screena -lX11 -lXext -Ofast -mfpmath=both -march=native -m64 -funroll-loops -mavx2 `pkg-config opencv --cflags --libs` && ./screena
#include <X11/Xlib.h>
#include <X11/Xutil.h>
#include <X11/extensions/XShm.h>
#include <sys/ipc.h>
#include <sys/shm.h>
#include <opencv2/opencv.hpp> // This includes most headers!
#include <time.h>
#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);
}
“裸”版:
// g++ xshm2.c -o xshm2 -lX11 -lXext `$cv`-Ofast -mfpmath=both -march=native -m64 -funroll-loops -mavx2 && ./xshm2
#include <X11/Xlib.h>
#include <X11/Xutil.h>
#include <X11/extensions/XShm.h>
#include <sys/ipc.h>
#include <sys/shm.h>
#include <opencv2/opencv.hpp> // This includes most headers!
#include <time.h>
#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!");
}
【讨论】:
clock!你可能是对的 :) 它快了多少?
我使用 Brandon 的回答制作了一个更快的基于仿函数的屏幕截图代码:
#include <opencv2/opencv.hpp>
#include <X11/Xlib.h>
#include <X11/Xutil.h>
class ScreenShot
{
Display* display;
Window root;
int x,y,width,height;
XImage* img{nullptr};
public:
ScreenShot(int x, int y, int width, int height):
x(x),
y(y),
width(width),
height(height)
{
display = XOpenDisplay(nullptr);
root = DefaultRootWindow(display);
}
void operator() (cv::Mat& cvImg)
{
if(img != nullptr)
XDestroyImage(img);
img = XGetImage(display, root, x, y, width, height, AllPlanes, ZPixmap);
cvImg = cv::Mat(height, width, CV_8UC4, img->data);
}
~ScreenShot()
{
if(img != nullptr)
XDestroyImage(img);
XCloseDisplay(display);
}
};
下面是你如何在循环中使用它(按q退出):
int main()
{
ScreenShot screen(0,0,1920,1080);
cv::Mat img;
while(true)
{
screen(img);
cv::imshow("img", img);
char k = cv::waitKey(1);
if (k == 'q')
break;
}
}
这在我的机器上快了大约 39%。
【讨论】:
#include <X11/Xlib.h>
#include <X11/Xutil.h>
#include <opencv2/opencv.hpp>
#include <opencv2/highgui.hpp>
#include <cstdint>
#include <cstring>
#include <vector>
using namespace cv;
struct ScreenShot
{
ScreenShot(int x, int y, int width, int height):
x(x),
y(y),
width(width),
height(height)
{
display = XOpenDisplay(nullptr);
root = DefaultRootWindow(display);
init = true;
}
void operator() (Mat& cvImg)
{
if(init == true)
init = false;
else
XDestroyImage(img);
img = XGetImage(display, root, x, y, width, height, AllPlanes, ZPixmap);
cvImg = Mat(height, width, CV_8UC4, img->data);
}
~ScreenShot()
{
if(init == false)
XDestroyImage(img);
XCloseDisplay(display);
}
Display* display;
Window root;
int x,y,width,height;
XImage* img;
bool init;
};
int main(int, char**)
{
for(;;){
ScreenShot screen(0,0,1366,768);
Mat img;
screen(img);
imshow("img", img);
if(waitKey(30) >= 0) break;
}
return 0;
}
根据@abc 发布的答案,我使用了以下代码。
另外我是用Visual Studio Code和g++编译的:
g ++ -std=c++0x main.cpp -lX11 'pkg-config --cflags opencv' 'pkg-config --libs opencv' -o main
Xlib.h 和 Xutil.h (ubuntu 14.04) 的路径:/usr/include/X11
【讨论】: