OpenCV自帶的JavaCameraView豎屏狀態(tài)下會旋轉(zhuǎn)90°顯示解決方案

昨天剛解決完OpenCV Manager的問題,今天就馬不停蹄的開始寫人臉追蹤仙畦。結(jié)合《深入OpenCV Android應(yīng)用開發(fā)》這本書加上官方給出的demo输涕,成功寫出了第一版的人臉追蹤效果(這里就不貼具體的代碼的,大家可以根據(jù)官方的demo——face-detection去試著實(shí)現(xiàn))慨畸,雖然能實(shí)現(xiàn)追蹤的效果莱坎,但是此時(shí)的頁面只有在設(shè)置橫屏?xí)r才是正常的,并且使用時(shí)需要把手機(jī)橫過來才有效果寸士,這和大家平時(shí)的使用習(xí)慣顯然是不符的檐什。于是我開始試著讓他能豎向顯示以及識別......

1. 首先試著把輸出的圖像豎起來

首先我們把頁面的android:screenOrientation="landscape"從水平改成豎向,此時(shí)的效果是顯示的內(nèi)容向左旋轉(zhuǎn)90°弱卡,然后顯示的區(qū)域是一個(gè)正方形乃正。接著我們開始試著讓圖像豎起來——
如果大家按照demo試著實(shí)現(xiàn)的話想必會讓頁面實(shí)現(xiàn)CameraBridgeViewBase.CvCameraViewListener2這個(gè)接口,此接口中需要實(shí)現(xiàn)

public Mat onCameraFrame(CvCameraViewFrame inputFrame);

這個(gè)方法婶博。根據(jù)其描述以及自己的猜測瓮具,我們可以知道這個(gè)方法是在攝像頭傳幀時(shí)被調(diào)用到。我們可以在這個(gè)方法中去處理所獲得到的幀凡人,尋找人臉的操作自然也就在這里進(jìn)行了名党。所以我們這尋找人臉的操作之前,先要將圖像旋轉(zhuǎn)回來挠轴。

        Mat gray = inputFrame.gray();
        rgb = inputFrame.rgba();
        // 旋轉(zhuǎn)輸入幀
        if (isFrontCamera) {
            Core.rotate(rgb, rgb, Core.ROTATE_90_COUNTERCLOCKWISE);
            Core.rotate(gray, gray, Core.ROTATE_90_COUNTERCLOCKWISE);
            Core.flip(rgb, rgb, 1);
            Core.flip(gray, gray, 1);
        } else {
            Core.rotate(rgb, rgb, Core.ROTATE_90_CLOCKWISE);
            Core.rotate(gray, gray, Core.ROTATE_90_CLOCKWISE);
        }

這里的rgb既是我們得到的彩色圖像传睹,gray既是灰度化之后的圖像。這里我們根據(jù)是否是前置攝像頭忠荞,對幀所對應(yīng)的矩陣進(jìn)行旋轉(zhuǎn)蒋歌,其中后置攝像頭需要將矩陣順時(shí)針旋轉(zhuǎn)90°得到豎向的圖像帅掘,而前置攝像頭則需要逆時(shí)針旋轉(zhuǎn)90°,同時(shí)進(jìn)行一次鏡像翻轉(zhuǎn)才能得到想要的圖像堂油。
PS:由于矩陣的乘法不滿足交換律修档,所以此處前置攝像頭的旋轉(zhuǎn)與翻轉(zhuǎn)順序不可更改!府框!對矩陣不太了解的小伙伴可以參考此處吱窝。
至此我們已經(jīng)將圖像豎起來了,同時(shí)也能對人臉進(jìn)行追蹤了迫靖,但是此時(shí)顯示的圖像依舊是一個(gè)以手機(jī)寬為邊長的正方形院峡。

2. 將預(yù)覽視圖擴(kuò)大到正常比例

經(jīng)過一番尋根問底,我發(fā)現(xiàn)決定幀大小的代碼是JavaCameraView里initializeCamera中的這一行

/* Select the size that fits surface considering maximum size allowed */
Size frameSize = calculateCameraFrameSize(sizes, new JavaCameraSizeAccessor(), width, height);

接著我們看這里計(jì)算幀大小的方法

    protected Size calculateCameraFrameSize(List<?> supportedSizes, ListItemAccessor accessor, int surfaceWidth, int surfaceHeight) {
        int calcWidth = 0;
        int calcHeight = 0;

        int maxAllowedWidth = (mMaxWidth != MAX_UNSPECIFIED && mMaxWidth < surfaceWidth) ? mMaxWidth : surfaceWidth;
        int maxAllowedHeight = (mMaxHeight != MAX_UNSPECIFIED && mMaxHeight < surfaceHeight) ? mMaxHeight : surfaceHeight;

        for (Object size : supportedSizes) {
            int width = accessor.getWidth(size);
            int height = accessor.getHeight(size);

            if (width <= maxAllowedWidth && height <= maxAllowedHeight) {
                if (height >= calcHeight && width >= calcWidth) {
                    calcWidth = (int) width;
                    calcHeight = (int) height;
                }
            }
        }

        return new Size(calcWidth, calcHeight);
    }

這段代碼首先是計(jì)算了允許的最大寬高系宜,接著遍歷當(dāng)前設(shè)備支持的所有的攝像頭預(yù)覽尺寸照激,在這里的需要注意的是,得到的這些分辨率是類似這樣的:

1920x1080 1280x720 800x480 768x432 720x480 640x480 576x432 480x320

也就是說之所以會出現(xiàn)正方形的預(yù)覽情況盹牧,是因?yàn)閷?shí)際視圖想要的大小(例如1080寬 1920高)與設(shè)備實(shí)際得到的設(shè)備支持的預(yù)覽尺寸的寬高比是不符的俩垃,這也就導(dǎo)致了這里的方法最后得到的尺寸寬高比變成了1:1(也就是主流設(shè)備上的1080x1080)。
因此我們?yōu)榱俗屗衔覀兊膶?shí)際需求汰寓,將此處比較最大值的條件改成

height <= maxAllowedWidth && width <= maxAllowedHeight

這樣最后得到的分辨率既是實(shí)際想要的最大縱向?qū)捀弑?當(dāng)然這里最后得到的數(shù)值依舊是寬>高的口柳,因?yàn)樵趇nitializeCamera的后續(xù)方法里依舊需要將這個(gè)尺寸作為參數(shù)設(shè)置給Camera)。
至此如果運(yùn)行項(xiàng)目會發(fā)現(xiàn)程序會死在CameraBridgeViewBase中deliverAndDrawFrame方法的這一行

Utils.matToBitmap(modified, mCacheBitmap);

這行代碼最后通過jni調(diào)用了OpenCV底層的方法有滑,根據(jù)Log日志反饋的異常跃闹,我們查詢源碼,發(fā)現(xiàn)此處是一個(gè)斷言異常毛好,錯(cuò)誤斷言了此處的矩陣與Bitmap大小一致望艺。我們再來看看此處的Bitmap從何而來莹桅,

protected void AllocateCache() {
    mCacheBitmap = Bitmap.createBitmap(mFrameWidth, mFrameHeight, Bitmap.Config.ARGB_8888);
}

我們可以看到此處的Bitmap是提前就創(chuàng)建好的灾杰,調(diào)用AllocateCache()此方法的地方正是initializeCamera(),也就是說此處Bitmap的大小是在初始化相機(jī)的方法中傳進(jìn)去的睁本,既他的寬是大于高的场靴。而此處的modified矩陣就是我們在上面第一步中旋轉(zhuǎn)好的幀所轉(zhuǎn)化而來的啡莉,也就是說他是一個(gè)縱向的圖像,如圖所示:


實(shí)際示意圖

而Utils.matToBitmap(modified, mCacheBitmap)這個(gè)方法的作用正是把矩陣轉(zhuǎn)化為Bitmap旨剥。

至此相信很多小伙伴就已經(jīng)知道該怎么改了——
我們只需要將Bitmap的初始化方法中的寬高對換即可讓其與矩陣不再沖突

mCacheBitmap = Bitmap.createBitmap(mFrameHeight, mFrameWidth, Bitmap.Config.ARGB_8888);

Ok咧欣,這次我們跑起來就能看到豎向的并且是最大比例的預(yù)覽圖了。


預(yù)覽圖

由于筆者是一名初學(xué)者轨帜,若文章或代碼里有不合理的地方魄咕,還請各位大佬提出來并給些意見。

最后編輯于
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請聯(lián)系作者
  • 序言:七十年代末蚌父,一起剝皮案震驚了整個(gè)濱河市哮兰,隨后出現(xiàn)的幾起案子毛萌,更是在濱河造成了極大的恐慌,老刑警劉巖喝滞,帶你破解...
    沈念sama閱讀 216,919評論 6 502
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件阁将,死亡現(xiàn)場離奇詭異,居然都是意外死亡右遭,警方通過查閱死者的電腦和手機(jī)做盅,發(fā)現(xiàn)死者居然都...
    沈念sama閱讀 92,567評論 3 392
  • 文/潘曉璐 我一進(jìn)店門,熙熙樓的掌柜王于貴愁眉苦臉地迎上來窘哈,“玉大人吹榴,你說我怎么就攤上這事」鐾瘢” “怎么了图筹?”我有些...
    開封第一講書人閱讀 163,316評論 0 353
  • 文/不壞的土叔 我叫張陵,是天一觀的道長满哪。 經(jīng)常有香客問我婿斥,道長,這世上最難降的妖魔是什么哨鸭? 我笑而不...
    開封第一講書人閱讀 58,294評論 1 292
  • 正文 為了忘掉前任,我火速辦了婚禮娇妓,結(jié)果婚禮上像鸡,老公的妹妹穿的比我還像新娘。我一直安慰自己哈恰,他們只是感情好只估,可當(dāng)我...
    茶點(diǎn)故事閱讀 67,318評論 6 390
  • 文/花漫 我一把揭開白布。 她就那樣靜靜地躺著着绷,像睡著了一般蛔钙。 火紅的嫁衣襯著肌膚如雪。 梳的紋絲不亂的頭發(fā)上荠医,一...
    開封第一講書人閱讀 51,245評論 1 299
  • 那天吁脱,我揣著相機(jī)與錄音,去河邊找鬼彬向。 笑死兼贡,一個(gè)胖子當(dāng)著我的面吹牛,可吹牛的內(nèi)容都是我干的娃胆。 我是一名探鬼主播遍希,決...
    沈念sama閱讀 40,120評論 3 418
  • 文/蒼蘭香墨 我猛地睜開眼,長吁一口氣:“原來是場噩夢啊……” “哼里烦!你這毒婦竟也來了凿蒜?” 一聲冷哼從身側(cè)響起禁谦,我...
    開封第一講書人閱讀 38,964評論 0 275
  • 序言:老撾萬榮一對情侶失蹤,失蹤者是張志新(化名)和其女友劉穎废封,沒想到半個(gè)月后州泊,有當(dāng)?shù)厝嗽跇淞掷锇l(fā)現(xiàn)了一具尸體,經(jīng)...
    沈念sama閱讀 45,376評論 1 313
  • 正文 獨(dú)居荒郊野嶺守林人離奇死亡虱饿,尸身上長有42處帶血的膿包…… 初始之章·張勛 以下內(nèi)容為張勛視角 年9月15日...
    茶點(diǎn)故事閱讀 37,592評論 2 333
  • 正文 我和宋清朗相戀三年拥诡,在試婚紗的時(shí)候發(fā)現(xiàn)自己被綠了。 大學(xué)時(shí)的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片氮发。...
    茶點(diǎn)故事閱讀 39,764評論 1 348
  • 序言:一個(gè)原本活蹦亂跳的男人離奇死亡渴肉,死狀恐怖,靈堂內(nèi)的尸體忽然破棺而出爽冕,到底是詐尸還是另有隱情仇祭,我是刑警寧澤,帶...
    沈念sama閱讀 35,460評論 5 344
  • 正文 年R本政府宣布颈畸,位于F島的核電站乌奇,受9級特大地震影響,放射性物質(zhì)發(fā)生泄漏眯娱。R本人自食惡果不足惜礁苗,卻給世界環(huán)境...
    茶點(diǎn)故事閱讀 41,070評論 3 327
  • 文/蒙蒙 一、第九天 我趴在偏房一處隱蔽的房頂上張望徙缴。 院中可真熱鬧试伙,春花似錦、人聲如沸于样。這莊子的主人今日做“春日...
    開封第一講書人閱讀 31,697評論 0 22
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽穿剖。三九已至蚤蔓,卻和暖如春,著一層夾襖步出監(jiān)牢的瞬間糊余,已是汗流浹背秀又。 一陣腳步聲響...
    開封第一講書人閱讀 32,846評論 1 269
  • 我被黑心中介騙來泰國打工, 沒想到剛下飛機(jī)就差點(diǎn)兒被人妖公主榨干…… 1. 我叫王不留啄刹,地道東北人涮坐。 一個(gè)月前我還...
    沈念sama閱讀 47,819評論 2 370
  • 正文 我出身青樓,卻偏偏與公主長得像誓军,于是被迫代替她去往敵國和親袱讹。 傳聞我的和親對象是個(gè)殘疾皇子,可洞房花燭夜當(dāng)晚...
    茶點(diǎn)故事閱讀 44,665評論 2 354

推薦閱讀更多精彩內(nèi)容