服务器之家:专注于VPS、云服务器配置技术及软件下载分享
分类导航

PHP教程|ASP.NET教程|Java教程|ASP教程|编程技术|正则表达式|C/C++|IOS|C#|Swift|Android|VB|R语言|JavaScript|易语言|vb.net|

服务器之家 - 编程语言 - C/C++ - QT+OpenCV实现录屏功能

QT+OpenCV实现录屏功能

2022-08-30 13:34平凡的编程者 C/C++

这篇文章主要为大家详细介绍了QT+OpenCV实现录屏功能,文中示例代码介绍的非常详细,具有一定的参考价值,感兴趣的小伙伴们可以参考一下

本文使用QT+opencv来实现对指定窗体画面录制,并保存为avi文件。

(1)获取窗体界面

QScreen类有一个grabWindow函数,可以用来获取窗体的画面,这个函数使用很简单,就是传入窗体句柄和要截取的坐标。但是这个函数有一个缺陷,它是通过截取桌面画面的方式,而不是通过

窗体获取界面,所以当你的窗体被其他窗体遮挡时,就无法截取完整的窗体界面,如果你是要录制整个桌面画面,那用这个函数就可以了,下面的方法调用GDI函数来实现,即使窗体被遮挡时仍然能够获取到完整界面,但是窗体最小化时也一样无法获取。

?
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
/*
*    函数功能:获取窗体指定窗体图像
*   参      数:hd:窗体句柄
*                 pm:保存获取到的图片
*                 x:截取的起始x坐标,
*                 y:截取的起始y坐标,
*                 w:截取的宽度
*                 h:截取的高度
*/
bool GetGDIBitmap(HWND hd,QPixmap &pm, int x, int y, int w, int h)
{
    if(hd==NULL)
        return false;
    HDC hDC;
    hDC=GetDCEx(hd,NULL,DCX_PARENTCLIP );
    HDC hMemDC;                    //内存缓冲设备环境
    HBITMAP hbmMem,hbmOld;        //内存缓冲设备环境中的位图
    RECT rc;
    rc.left=x;
    rc.top=y;
    rc.right=x+w;
    rc.bottom=y+h;
    //判断边境值
    RECT clientrc;
    ::GetClientRect(hd,&clientrc);
 
    int xc =0;
    int cx =0;
    int cy =0;
 
    if(rc.bottom>clientrc.bottom || rc.bottom<0)
        rc.bottom=clientrc.bottom;
 
    if(rc.right>clientrc.right || rc.right<0)
        rc.right=clientrc.right;
 
    // 24位图的BITMAPINFO
    BITMAPINFO *pBITMAPINFO = (BITMAPINFO*)malloc(sizeof(BITMAPINFOHEADER));
    memset(pBITMAPINFO, 0, sizeof(BITMAPINFOHEADER));
    BITMAPINFOHEADER *pInfo_Header = (BITMAPINFOHEADER *)pBITMAPINFO;
    pInfo_Header->biSize = sizeof(BITMAPINFOHEADER);
    pInfo_Header->biWidth = rc.right - rc.left;
    pInfo_Header->biHeight = (rc.bottom - rc.top);
    pInfo_Header->biPlanes = 1;
    pInfo_Header->biBitCount = 24;
    pInfo_Header->biCompression = BI_RGB;
 
    hMemDC=CreateCompatibleDC(hDC);    //创建内存兼容设备环境
    //创建内存兼容位图
    hbmMem=CreateCompatibleBitmap(hDC,pInfo_Header->biWidth,pInfo_Header->biHeight);
 
    hbmOld=(HBITMAP)SelectObject(hMemDC,hbmMem);
    //将内存设备环境中的内容绘制到物理设备环境   hDC
    BitBlt(hMemDC,0,0,pInfo_Header->biWidth,pInfo_Header->biHeight,hDC,cx+rc.left,xc+cy+rc.top,CAPTUREBLT|SRCCOPY);
    HBITMAP hBitmap=(HBITMAP)SelectObject(hMemDC,hbmOld);
 
    // 获得数据buf
    DWORD bufSize=(pInfo_Header->biWidth * 3 + 3) / 4 * 4 * pInfo_Header->biHeight;
    BYTE * pBuffer = new BYTE[bufSize];
 
    int aHeight=pInfo_Header->biHeight;
 
    if(::GetDIBits(hMemDC, hBitmap, 0, aHeight, pBuffer,pBITMAPINFO, DIB_RGB_COLORS) == 0)
    {
        return false;
    }
 
    bool bret=BitmapToPixmap(hBitmap,pm);
 
 
    ReleaseDC(hd,hDC);
    //释放资源
    DeleteObject(hbmMem);
    DeleteObject(hbmOld);
    DeleteDC(hMemDC);
    free(pBITMAPINFO);
    ::DeleteObject(hBitmap);
    delete [] pBuffer;
    return bret;
}    
 
/*
*    函数功能:将bitmap转为QPixmap
*/
bool BitmapToPixmap(HBITMAP hBitmap, QPixmap &pm)
{
    HDC     hDC;
    //设备描述表
    int     iBits;
    //当前显示分辨率下每个像素所占字节数
    WORD    wBitCount;
    //位图中每个像素所占字节数
    //定义调色板大小, 位图中像素字节大小 ,  位图文件大小 , 写入文件字节数
    DWORD           dwPaletteSize=0,dwBmBitsSize,dwDIBSize;
    BITMAP          Bitmap;
    //位图属性结构
    BITMAPFILEHEADER   bmfHdr;
    //位图文件头结构
    BITMAPINFOHEADER   bi;
    //位图信息头结构
    LPBITMAPINFOHEADER lpbi;
    //指向位图信息头结构
    HANDLE          hDib, hPal;
    HPALETTE     hOldPal=NULL;
    //定义文件,分配内存句柄,调色板句柄
 
    //计算位图文件每个像素所占字节数
    hDC = CreateDC(L"DISPLAY",NULL,NULL,NULL);
    iBits = GetDeviceCaps(hDC, BITSPIXEL) * GetDeviceCaps(hDC, PLANES);
    DeleteDC(hDC);
    if (iBits <= 1)
        wBitCount = 1;
    else if (iBits <= 4)
        wBitCount = 4;
    else if (iBits <= 8)
        wBitCount = 8;
    else if (iBits <= 24)
        wBitCount = 24;
    else
        wBitCount = 24;
    //计算调色板大小
    if (wBitCount <= 8)
        dwPaletteSize=(1<<wBitCount)*sizeof(RGBQUAD);
 
    //设置位图信息头结构
    GetObject(hBitmap, sizeof(BITMAP), (LPSTR)&Bitmap);
    bi.biSize            = sizeof(BITMAPINFOHEADER);
    bi.biWidth           = Bitmap.bmWidth;
    bi.biHeight          = Bitmap.bmHeight;
    bi.biPlanes          = 1;
    bi.biBitCount         = wBitCount;
    bi.biCompression      = BI_RGB;
    bi.biSizeImage         = 0;
    bi.biXPelsPerMeter     = 0;
    bi.biYPelsPerMeter     = 0;
    bi.biClrUsed           = 0;
    bi.biClrImportant      = 0;
 
    dwBmBitsSize = ((Bitmap.bmWidth*wBitCount+31)/32)*4*Bitmap.bmHeight;
    //为位图内容分配内存
    hDib  = GlobalAlloc(GHND,dwBmBitsSize+dwPaletteSize+sizeof(BITMAPINFOHEADER));
    lpbi = (LPBITMAPINFOHEADER)GlobalLock(hDib);
    *lpbi = bi;
    // 处理调色板
    hPal = GetStockObject(DEFAULT_PALETTE);
    if (hPal)
    {
        hDC = ::GetDC(NULL);
        hOldPal=SelectPalette(hDC,(HPALETTE)hPal,FALSE);
        RealizePalette(hDC);
    }
    // 获取该调色板下新的像素值
    GetDIBits(hDC,hBitmap,0,(UINT)Bitmap.bmHeight,(LPSTR)lpbi+sizeof(BITMAPINFOHEADER)+dwPaletteSize, (BITMAPINFO *)lpbi,DIB_RGB_COLORS);
    //恢复调色板
    if (hOldPal)
    {
        SelectPalette(hDC, hOldPal, TRUE);
        RealizePalette(hDC);
        ::ReleaseDC(NULL, hDC);
    }
    // 设置位图文件头
    bmfHdr.bfType = 0x4D42;  // "BM"
    dwDIBSize=sizeof(BITMAPFILEHEADER)+sizeof(BITMAPINFOHEADER)+dwPaletteSize+dwBmBitsSize;
    bmfHdr.bfSize = dwDIBSize;
    bmfHdr.bfReserved1 = 0;
    bmfHdr.bfReserved2 = 0;
    bmfHdr.bfOffBits = (DWORD)sizeof(BITMAPFILEHEADER)+(DWORD)sizeof(BITMAPINFOHEADER)+dwPaletteSize;
 
    std::vector<uchar>buffer;
    uchar *p=(uchar*)&bmfHdr;
    // 写入位图文件头
    buffer.insert(buffer.end(),p,p+sizeof(BITMAPFILEHEADER));
    // 写入位图文件其余内容
    p=(uchar*)lpbi;
    buffer.insert(buffer.end(),p,p+sizeof(BITMAPINFOHEADER)+dwPaletteSize+dwBmBitsSize);
    //清除
    GlobalUnlock(hDib);
    GlobalFree(hDib);
    pm=QPixmap::fromImage(QImage::fromData(buffer.data(),buffer.size()));
    return true;
}

(2)录制画面

?
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
bool g_needstop =false;void Record()
{
     RECT rect;
     //获取窗体位置大小
     GetWindowRect(hd,&rect);
     cv::Size frameSize;
     frameSize.width=rect.right-rect.left;
     frameSize.height=rect.bottom-rect.top;   
     cv::VideoWriter VideoWriter;
     if(!VideoWriter.open("d:\\1.avi",CV_FOURCC('M', 'J', 'P', 'G'),40,frameSize))
         return;
    while(!g_needstop)
    {
        QPixmap pm;
        GetGDIBitmap(hd,pm,0,0,frameSize.width,frameSize.height);
        VideoWriter.write(ImageToMat(pm.toImage()));
    }        VideoWriter.release(); 
}
 
Mat ImageToMat(QImage img,QString imgFormat)
{
    if(img.isNull())
        return Mat();
    QByteArray ba;
    QBuffer buffer(&ba);
    buffer.open(QIODevice::WriteOnly);
    img.save(&buffer,imgFormat.toLatin1().data());
    _InputArray arrSrc(ba.data(), ba.size());
    Mat mat = cv::imdecode(arrSrc, CV_LOAD_IMAGE_COLOR);
    return mat;
}

(3) 播放视频

?
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
void Play()
{
    cv::VideoCapture Capture;
    if(!Capture.open("d:\\1.avi"))
          return;
    Mat frame;
    //逐帧读取画面
    while(Capture.read(frame))
    {
            //转成QImage格式用于显示
           QImage img = MatToImage(frame);
           emit Frame(img);
           QThread::msleep(40);
    }
    Capture.release();
    emit PlayFinsh();
}
 
QImage MatToImage(Mat mat)
{
    if(mat.type() == CV_8UC1)
    {
        QImage image(mat.cols, mat.rows, QImage::Format_Indexed8);
        // Set the color table (used to translate colour indexes to qRgb values)
        image.setColorCount(256);
        for(int i = 0; i < 256; i++)
        {
            image.setColor(i, qRgb(i, i, i));
        }
        // Copy input Mat
        uchar *pSrc = mat.data;
        for(int row = 0; row < mat.rows; row ++)
        {
            uchar *pDest = image.scanLine(row);
            memcpy(pDest, pSrc, mat.cols);
            pSrc += mat.step;
        }
        return image;
    }
    // 8-bits unsigned, NO. OF CHANNELS = 3
    else if(mat.type() == CV_8UC3)
    {
        // Copy input Mat
        const uchar *pSrc = (const uchar*)mat.data;
        // Create QImage with same dimensions as input Mat
        QImage image(pSrc, mat.cols, mat.rows, mat.step, QImage::Format_RGB888);
        return image.rgbSwapped();
    }
    else if(mat.type() == CV_8UC4)
    {
        qDebug() << "CV_8UC4";
        // Copy input Mat
        const uchar *pSrc = (const uchar*)mat.data;
        // Create QImage with same dimensions as input Mat
        QImage image(pSrc, mat.cols, mat.rows, mat.step, QImage::Format_ARGB32);
        return image.copy();
    }
    else
    {
        qDebug() << "ERROR: Mat could not be converted to QImage.";
        return QImage();
    }
}

以上就是本文的全部内容,希望对大家的学习有所帮助,也希望大家多多支持服务器之家。

原文链接:https://www.cnblogs.com/WushiShengFei/p/11202250.html

延伸 · 阅读

精彩推荐
  • C/C++C语言实现简单弹球游戏

    C语言实现简单弹球游戏

    这篇文章主要为大家详细介绍了C语言实现简单弹球游戏,文中示例代码介绍的非常详细,具有一定的参考价值,感兴趣的小伙伴们可以参考一下...

    繁星璀璨G11122021-08-19
  • C/C++深入解析C++中的动态类型转换与静态类型转换运算符

    深入解析C++中的动态类型转换与静态类型转换运算符

    这篇文章主要介绍了C++中的动态类型转换与静态类型转换运算符,即dynamic_cast与static_cast的用法,需要的朋友可以参考下...

    C++教程网5212021-03-19
  • C/C++C语言实现扫雷程序

    C语言实现扫雷程序

    这篇文章主要为大家详细介绍了C语言实现扫雷程序,文中示例代码介绍的非常详细,具有一定的参考价值,感兴趣的小伙伴们可以参考一下...

    木头i9692021-08-10
  • C/C++C++17中的std::optional的具体使用

    C++17中的std::optional的具体使用

    这篇文章主要介绍了C++17中的std::optional的具体使用,文中通过示例代码介绍的非常详细,对大家的学习或者工作具有一定的参考学习价值,需要的朋友们下...

    hzSomthing8292021-10-19
  • C/C++实例代码分析c++动态分配

    实例代码分析c++动态分配

    这篇文章主要介绍了c++动态分配的的相关资料,文中代码简单易懂,方便大家更好的学习参考,感兴趣的朋友可以了解下...

    晟夏的叶9942021-09-09
  • C/C++C++实现正整数的四则运算表达式

    C++实现正整数的四则运算表达式

    这篇文章主要为大家详细介绍了C++实现正整数的四则运算表达式,文中示例代码介绍的非常详细,具有一定的参考价值,感兴趣的小伙伴们可以参考一下...

    Alex山南水北5192021-09-10
  • C/C++vscode中配置LeetCode插件的教程(愉快刷题)

    vscode中配置LeetCode插件的教程(愉快刷题)

    这篇文章主要介绍了vscode中配置LeetCode插件的教程,本文通过图文并茂的形式给大家介绍的非常详细,对大家的学习或工作具有一定的参考借鉴价值,需要...

    TechFlow20199572021-08-29
  • C/C++Easyx实现窗口自动碰撞的小球

    Easyx实现窗口自动碰撞的小球

    这篇文章主要为大家详细介绍了Easyx实现窗口自动碰撞的小球,文中示例代码介绍的非常详细,具有一定的参考价值,感兴趣的小伙伴们可以参考一下...

    shi_xiaobin6562022-08-20