QT+OpenCV实现录屏功能
程序员文章站
2022-06-16 15:10:12
本文使用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(); } }
以上就是本文的全部内容,希望对大家的学习有所帮助,也希望大家多多支持。
上一篇: 摄影技巧:夏天玩水照怎么拍好看?