Qt:在QCamera上繪圖(QCamera as background)


舉個例子:

背景:
幾年前在某家人臉識別公司工作時,做了幾個演示用的demo(FaceView),其中大量用到了繪圖方面的Qt方法。
核心就是:在攝像頭的視頻流上繪制人臉框、性別、年齡等等。好在當時是使用openCV來捕獲攝像頭,后面在繪圖事件中刷新,所以實際寫起來起來非常簡單。

這次在MMI中也遇到了這個問題,開發時考慮到對相機的要求不高,能看就行,於是采用了QCamera->QCameraViewfinder一系列操作。
就在快要驗收的時候,甲方爸爸提出:不要使用一大塊取景器,最好像手機識別那樣的圓框。

嘗試解決:
網上的一些方法,繼承QCameraViewfinder之后重寫paintEvent()。實際debug時,發現paintEvent只有窗體被show出來的時候調用了一次,沒有update,更別說畫圖了。

解決:
后來在某個issue下看到有人提到QAbstractVideoSurface,於是去大概了解了一下。
QCamera初始化完成后,可以用setViewfinder()方法來設置數據流往哪走。
void setViewfinder(QVideoWidget *viewfinder);
void setViewfinder(QGraphicsVideoItem *viewfinder);
void setViewfinder(QAbstractVideoSurface *surface);

這里提到的就是第三種方法,追到QAbstractVideoSurface類中,結合Qt手冊可以看到:
virtual bool present(const QVideoFrame &frame) = 0;
[pure virtual] bool QAbstractVideoSurface::present(const QVideoFrame &frame)
Presents a video frame.Returns true if the frame was presented, and false if an error occurred.
說白了就是把幀丟出來,給我們自己處理。

這樣一來,整條線就打通了,從取數據->繪畫->渲染。

實現:
要實現上述功能,首先需要對數據進行處理。
1.新建類VideoSurface,繼承自QAbstractVideoSurface,可以把它理解成數據源。繼承后,supportedPixelFormats(...)、present(...)這兩個純虛函數實現一下,同時別忘記加上信號,以方便把我們的數據扔出去給其他控件使用;
2.新建類ViewFinder,繼承自QLabel,可以把它理解成取景器的QWidget。選擇用QLabel是因為比較容易把圖片放上來(setPixmap),重寫它的paintEvent()。注意:重寫的繪圖事件中,一定要先調用QLabel::paintEvent(event),否則外面觸發的update都不會被渲染;
3.在原本的相機類中,用VideoSurface取代QCameraViewfinder(注意:這時候VideoSurface不是QWidget了,要將ViewFinder當做Widget,設定父窗口、父窗口等)。在這里定義一個槽,用來接收步驟1中的信號,獲取圖片;
4.使用setPixmap方法,給ViewFinder加上相機的背景。調用此方法時,會進入ViewFinder的繪圖事件,在步驟2所說的QLabel::paintEvent(event)后,添加畫圖的工序。

上述步驟完成后,即可大功告成。

我繪制的是一張png圖片,把這張圖片換成切好的人臉識別框,就可以實現甲方爸爸的需求了。當然,也可以自己用蒙版畫出來一個圓。
具體的實現代碼,見https://github.com/zhujingran/PaintOnQCamera


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM