经验首页 前端设计 程序设计 Java相关 移动开发 数据库/运维 软件/图像 大数据/云计算 其他经验
当前位置:技术经验 » 程序设计 » C++ » 查看文章
QT+OpenCV实现录屏功能
来源:jb51  时间:2022/1/19 15:45:28  对本文有异议

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

(1)获取窗体界面

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

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

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

(2)录制画面

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

(3) 播放视频

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

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

 友情链接:直通硅谷  点职佳  北美留学生论坛

本站QQ群:前端 618073944 | Java 606181507 | Python 626812652 | C/C++ 612253063 | 微信 634508462 | 苹果 692586424 | C#/.net 182808419 | PHP 305140648 | 运维 608723728

W3xue 的所有内容仅供测试,对任何法律问题及风险不承担任何责任。通过使用本站内容随之而来的风险与本站无关。
关于我们  |  意见建议  |  捐助我们  |  报错有奖  |  广告合作、友情链接(目前9元/月)请联系QQ:27243702 沸活量
皖ICP备17017327号-2 皖公网安备34020702000426号