這幾天需要做一個視頻檔案錄製功能。用Qt調用OpenCV代碼搞定。
其中線程相關的過程省略不寫,與CvVideoWriter產生的部分網上有很多,也省略不寫。下面只記錄這個寫入單幀的函數:
第一版:
//ATTENTION: 調用來自另一線程bool ETVideoController::writeVideo(const ETVideoSource &source, QSize size, CvVideoWriter *pWriter){ QImage img(source.getOneFrame()); // pImageWrite與img共用記憶體 IplImage* pImageWrite = cvCreateImageHeader( cvSize(size.width(), size.height()), 8, 3); pImageWrite->imageData = reinterpret_cast<char*>(img.bits()); cvCvtColor(pImageWrite, pImageWrite, CV_RGB2BGR); // 寫入一幀 cvWriteFrame(pWriter, pImageWrite); cvReleaseImageHeader(&pImageWrite); return true;}
這一版本的代碼沒有考慮到輸入的源映像非24位元影像的情況,也沒有考慮到輸入的源映像與視頻大小不一致的情況。所以錄製後的視頻出現了花屏的情況。為改善這一問題,我把代碼改成了如下形式:
第二版:
//ATTENTION: 調用來自另一線程bool ETVideoController::writeVideo(const ETVideoSource &source, QSize size, CvVideoWriter *pWriter){ // 將映像預先轉化成24位 QImage img(source.getOneFrame().convertToFormat(QImage::Format_RGB888)); img = img.scaled(size); // pImageWrite與img共用記憶體 IplImage* pImageWrite = cvCreateImageHeader( cvSize(size.width(), size.height()), 8, 3); pImageWrite->imageData = reinterpret_cast<char*>(img.bits()); cvCvtColor(pImageWrite, pImageWrite, CV_RGB2BGR); // 寫入一幀 cvWriteFrame(pWriter, pImageWrite); cvReleaseImageHeader(&pImageWrite); return true;}
其中cvWriteFrame函數在調用時發生記憶體錯誤。上網各種搜尋,沒有找到有用的答案,初步判斷因為img對象在其複製建構函式、賦值運算子調用的過程中,Qt執行的是淺複製,而對於Qt來說,它會自動判斷在需要深複製的時候再進行一次深複製以使它們看起來像完全不同的對象一樣。Qt這種機制使得OpenCV訪問了非法的記憶體位址,所以造成記憶體出錯。於是,我將代碼改成了最終的版本
第三版:
//ATTENTION: 調用來自另一線程bool ETVideoController::writeVideo(const ETVideoSource &source, QSize size, CvVideoWriter *pWriter){ QImage img = source.getOneFrame(); QImage imgDest(size, QImage::Format_RGB888); // 建立一個新圖片,使用指定的大小和24位格式 { QPainter p(&imgDest); // 使用QPainter描畫圖片 p.drawImage(imgDest.rect(), img); } // 析構 // pImageWrite與img共用記憶體 IplImage* pImageWrite = cvCreateImageHeader( cvSize(size.width(), size.height()), 8, 3); pImageWrite->imageData = reinterpret_cast<char*>(imgDest.bits()); cvCvtColor(pImageWrite, pImageWrite, CV_RGB2BGR); // 寫入一幀 cvWriteFrame(pWriter, pImageWrite); cvReleaseImageHeader(&pImageWrite); return true;}
Painter p外面的大括弧是不可以省略的,因為我們利用在右大括弧}處自動調用p的解構函式的特性。經調試,上述代碼可以正常運行。
(■著作權聲明: 原創內容,轉載僅限非商業用途,並需註明出處: http://blog.csdn.net/LoveASPDotNet)