QT+OpenCV實現(xiàn)錄屏功能
本文使用QT+opencv來實現(xiàn)對指定窗體畫面錄制,并保存為avi文件。
(1)獲取窗體界面
QScreen類有一個grabWindow函數(shù),可以用來獲取窗體的畫面,這個函數(shù)使用很簡單,就是傳入窗體句柄和要截取的坐標(biāo)。但是這個函數(shù)有一個缺陷,它是通過截取桌面畫面的方式,而不是通過
窗體獲取界面,所以當(dāng)你的窗體被其他窗體遮擋時,就無法截取完整的窗體界面,如果你是要錄制整個桌面畫面,那用這個函數(shù)就可以了,下面的方法調(diào)用GDI函數(shù)來實現(xiàn),即使窗體被遮擋時仍然能夠獲取到完整界面,但是窗體最小化時也一樣無法獲取。
/* * ? ?函數(shù)功能:獲取窗體指定窗體圖像 * ? 參 ? ? ?數(shù):hd:窗體句柄 * ? ? ? ? ? ? ? ? pm:保存獲取到的圖片 * ? ? ? ? ? ? ? ? x:截取的起始x坐標(biāo), * ? ? ? ? ? ? ? ? y:截取的起始y坐標(biāo), * ? ? ? ? ? ? ? ? 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; ? ? ? ? ? ? ? ? ? ?//內(nèi)存緩沖設(shè)備環(huán)境 ? ? HBITMAP hbmMem,hbmOld; ? ? ? ?//內(nèi)存緩沖設(shè)備環(huán)境中的位圖 ? ? 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); ? ?//創(chuàng)建內(nèi)存兼容設(shè)備環(huán)境 ? ? //創(chuàng)建內(nèi)存兼容位圖 ? ? hbmMem=CreateCompatibleBitmap(hDC,pInfo_Header->biWidth,pInfo_Header->biHeight); ? ? hbmOld=(HBITMAP)SelectObject(hMemDC,hbmMem); ? ? //將內(nèi)存設(shè)備環(huán)境中的內(nèi)容繪制到物理設(shè)備環(huán)境 ? 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); ? ? // 獲得數(shù)據(jù)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; } ? ? /* * ? ?函數(shù)功能:將bitmap轉(zhuǎn)為QPixmap */ bool BitmapToPixmap(HBITMAP hBitmap, QPixmap &pm) { ? ? HDC ? ? hDC; ? ? //設(shè)備描述表 ? ? int ? ? iBits; ? ? //當(dāng)前顯示分辨率下每個像素所占字節(jié)數(shù) ? ? WORD ? ?wBitCount; ? ? //位圖中每個像素所占字節(jié)數(shù) ? ? //定義調(diào)色板大小, 位圖中像素字節(jié)大小 , ?位圖文件大小 , 寫入文件字節(jié)數(shù) ? ? DWORD ? ? ? ? ? dwPaletteSize=0,dwBmBitsSize,dwDIBSize; ? ? BITMAP ? ? ? ? ?Bitmap; ? ? //位圖屬性結(jié)構(gòu) ? ? BITMAPFILEHEADER ? bmfHdr; ? ? //位圖文件頭結(jié)構(gòu) ? ? BITMAPINFOHEADER ? bi; ? ? //位圖信息頭結(jié)構(gòu) ? ? LPBITMAPINFOHEADER lpbi; ? ? //指向位圖信息頭結(jié)構(gòu) ? ? HANDLE ? ? ? ? ?hDib, hPal; ? ? HPALETTE ? ? hOldPal=NULL; ? ? //定義文件,分配內(nèi)存句柄,調(diào)色板句柄 ? ? //計算位圖文件每個像素所占字節(jié)數(shù) ? ? 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; ? ? //計算調(diào)色板大小 ? ? if (wBitCount <= 8) ? ? ? ? dwPaletteSize=(1<<wBitCount)*sizeof(RGBQUAD); ? ? //設(shè)置位圖信息頭結(jié)構(gòu) ? ? 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; ? ? //為位圖內(nèi)容分配內(nèi)存 ? ? hDib ?= GlobalAlloc(GHND,dwBmBitsSize+dwPaletteSize+sizeof(BITMAPINFOHEADER)); ? ? lpbi = (LPBITMAPINFOHEADER)GlobalLock(hDib); ? ? *lpbi = bi; ? ? // 處理調(diào)色板 ? ? hPal = GetStockObject(DEFAULT_PALETTE); ? ? if (hPal) ? ? { ? ? ? ? hDC = ::GetDC(NULL); ? ? ? ? hOldPal=SelectPalette(hDC,(HPALETTE)hPal,FALSE); ? ? ? ? RealizePalette(hDC); ? ? } ? ? // 獲取該調(diào)色板下新的像素值 ? ? GetDIBits(hDC,hBitmap,0,(UINT)Bitmap.bmHeight,(LPSTR)lpbi+sizeof(BITMAPINFOHEADER)+dwPaletteSize, (BITMAPINFO *)lpbi,DIB_RGB_COLORS); ? ? //恢復(fù)調(diào)色板 ? ? if (hOldPal) ? ? { ? ? ? ? SelectPalette(hDC, hOldPal, TRUE); ? ? ? ? RealizePalette(hDC); ? ? ? ? ::ReleaseDC(NULL, hDC); ? ? } ? ? // 設(shè)置位圖文件頭 ? ? 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)); ? ? // 寫入位圖文件其余內(nèi)容 ? ? 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)) ? ? { ? ? ? ? ? ? //轉(zhuǎn)成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(); ? ? } }
以上就是本文的全部內(nèi)容,希望對大家的學(xué)習(xí)有所幫助,也希望大家多多支持腳本之家。
相關(guān)文章
C++多重繼承引發(fā)的重復(fù)調(diào)用問題與解決方法
這篇文章主要介紹了C++多重繼承引發(fā)的重復(fù)調(diào)用問題與解決方法,結(jié)合具體實例形式分析了C++多重調(diào)用中的重復(fù)調(diào)用問題及相應(yīng)的解決方法,需要的朋友可以參考下2018-05-05詳解C++中的內(nèi)存同步模式(memory order)
這篇文章主要介紹了C++中的內(nèi)存同步模式,文中通過示例代碼介紹的非常詳細(xì),對大家的學(xué)習(xí)或者工作具有一定的參考學(xué)習(xí)價值,需要的朋友們下面隨著小編來一起學(xué)習(xí)學(xué)習(xí)吧2019-04-04C++實現(xiàn)LeetCode(211.添加和查找單詞-數(shù)據(jù)結(jié)構(gòu)設(shè)計)
這篇文章主要介紹了C++實現(xiàn)LeetCode(211.添加和查找單詞-數(shù)據(jù)結(jié)構(gòu)設(shè)計),本篇文章通過簡要的案例,講解了該項技術(shù)的了解與使用,以下就是詳細(xì)內(nèi)容,需要的朋友可以參考下2021-08-08Qt數(shù)據(jù)庫應(yīng)用之超級自定義委托
Qt中需要用到自定義委托的情形很多,比如提供下拉框選擇,進(jìn)度條展示下載進(jìn)度啥的,默認(rèn)的單元格是沒有這些效果的,需要自己單獨用委托的形式來展示。本文將為大家介紹Qt中如何進(jìn)行超級自定義委托,需要的可以參考一下2022-03-03