本文使用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();
- ? ? }
- }
以上就是本文的全部内容,希望对大家的学习有所帮助,也希望大家多多支持w3xue。