欢迎您访问程序员文章站本站旨在为大家提供分享程序员计算机编程知识!
您现在的位置是: 首页  >  IT编程

QT+OpenCV实现录屏功能

程序员文章站 2022-03-02 22:53:14
本文使用qt+opencv来实现对指定窗体画面录制,并保存为avi文件。(1)获取窗体界面qscreen类有一个grabwindow函数,可以用来获取窗体的画面,这个函数使用很简单,就是传入窗体句柄和...

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

(1)获取窗体界面

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

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

/*
*    函数功能:获取窗体指定窗体图像
*   参      数: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)录制画面

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) 播放视频

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();
    }
}

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

相关标签: QT OpenCV 录屏