[OpenGL]未來(lái)視覺(jué)1-Android攝像頭采集基礎(chǔ)

相信很多人都用過(guò)相機(jī)功能昌阿,也開發(fā)過(guò)簡(jiǎn)單調(diào)度相機(jī)功能殖告,但是相機(jī)采集功能烘挫。是圖像信號(hào)輸入的重要來(lái)源诀艰。

SurfaceView和View的不同之處:


SurfaceView和View對(duì)比

相機(jī)圖像采樣,需要維持一個(gè)比較穩(wěn)定的幀數(shù)來(lái)維持圖像實(shí)時(shí)性饮六,需要頻繁刷新其垄,創(chuàng)建一個(gè)子線程來(lái)進(jìn)行畫面更新,會(huì)被占用主線程效率好很多卤橄,而且雙緩沖機(jī)制可以在畫面從前臺(tái)刷新到后臺(tái)時(shí)才占用主線程操作绿满,所以選用SurfaceView作繪制是最好的。
而GLSurfaceView是SurfaceView的一個(gè)子類窟扑,專用于openGL繪制喇颁,其運(yùn)行效率遠(yuǎn)高于SurfaceView是因?yàn)槭褂昧薌PU參與繪制漏健。
這一節(jié)介紹Android攝像頭采樣,還是采用了SurfaceView來(lái)做采樣
1.需要申請(qǐng)相機(jī)權(quán)限橘霎。

    <uses-permission android:name="android.permission.CAMERA" />

2.打開攝像頭蔫浆,先檢查攝像和前置攝像頭,然后通過(guò)攝像頭Id茎毁,來(lái)返回?cái)z像頭對(duì)象克懊。

 fun openCamera(cameraId:Int):Camera?{
        if (!haveFeature(PackageManager.FEATURE_CAMERA)){
            Log.e(TAG,"no camera!")
            return null
        }

        if (cameraId == Camera.CameraInfo.CAMERA_FACING_FRONT && !haveFeature(PackageManager.FEATURE_CAMERA_FRONT)){
            Log.e(TAG,"no front camera!")
            return null
        }

        val camera = Camera.open(cameraId)
        if (camera == null){
            Log.e(TAG, "openCamera failed")
            return null
        }

        return camera
    }

3.設(shè)置畫面比例

/**
     * 獲取最大的圖片大小
     */
    fun getLargePictureSize(camera: Camera?): Camera.Size? {
        if (camera != null) {
            //獲取可選比例
            val sizes = camera.parameters.supportedPictureSizes
            var temp: Camera.Size = sizes[0]
            for (i in 1 until sizes.size) {
                val scale = sizes[i].height.toFloat() / sizes[i].width
                if (temp.width < sizes[i].width && scale < 0.6f && scale > 0.5f)
                    temp = sizes[i]
            }
            return temp
        }
        return null
    }

    /**
     * 獲取最大的預(yù)覽大小
     */
    fun getLargePreviewSize(camera: Camera?): Camera.Size? {
        if (camera != null) {
            //獲取可選比例
            val sizes = camera.parameters.supportedPreviewSizes
            var temp: Camera.Size = sizes[0]
            for (i in 1 until sizes.size) {
                if (temp.width < sizes[i].width)
                    temp = sizes[i]
            }
            return temp
        }
        return null
    }

 /**
     * 相機(jī)采樣參數(shù)大小
     */
    fun setOptimalSize(camera:Camera,aspectRatio:Float,maxWidth:Int,maxHeight:Int){
        val parameters= camera.parameters
        //使用自動(dòng)對(duì)焦
        if (parameters.supportedFocusModes.contains(
                        Camera.Parameters.FOCUS_MODE_CONTINUOUS_PICTURE)) {
            parameters.focusMode = Camera.Parameters.FOCUS_MODE_CONTINUOUS_PICTURE
        }
        val size = getLargePreviewSize(camera)
        size?.let {
            //設(shè)置相機(jī)預(yù)覽大小
            parameters.setPreviewSize(it.width,it.height)
            Log.d(TAG, "input max: (" + maxWidth + ", " + maxHeight + "), output size: ("
                    + it.width + ", " + it.height + ")")
        }

        val pictureSize = getLargePictureSize(camera)
        pictureSize?.let {
            //圖片參數(shù)
            parameters.setPictureSize(it.width,it.height)
            Log.d(TAG, "picture max: (" + maxWidth + ", " + maxHeight + "), output size: ("
                    + it.width + ", " + it.height + ")")
        }

        camera.parameters = parameters
    }

3.設(shè)置相機(jī)圖像角度

fun setDisplayOritation(activity: Activity, camera: Camera, cameraId: Int) {
        //獲取window的角度
        val rotation = activity.windowManager.defaultDisplay.rotation
        var degress = 0
        when (rotation) {
            Surface.ROTATION_0 -> degress = 0
            Surface.ROTATION_90 -> degress = 90
            Surface.ROTATION_180 -> degress = 180
            Surface.ROTATION_270 -> degress = 270
        }

        val info = Camera.CameraInfo()
        Camera.getCameraInfo(cameraId, info)
        var result: Int
        //前置攝像頭
        if (info.facing == Camera.CameraInfo.CAMERA_FACING_FRONT) {
            result = (info.orientation + degress) % 360
            result = (360 - result) % 360  // compensate the mirror
        } else {//后置攝像頭
            result = (info.orientation - degress + 360) % 360 // back-facing
        }
        Log.d(TAG, "window rotation: $degress, camera oritation: $result")
        camera.setDisplayOrientation(result)
    }

4.設(shè)置完攝像頭參數(shù)后,需要設(shè)置一個(gè)SurfaceHolder.CallBack七蜘。
有三個(gè)必須的方法

   //創(chuàng)建時(shí)調(diào)用
   override fun surfaceCreated(holder: SurfaceHolder?) { }
   //當(dāng)surface發(fā)生任何結(jié)構(gòu)性的變化時(shí)(格式或者大刑犯取),該方法就會(huì)被立即調(diào)用橡卤。
   override fun surfaceChanged(holder: SurfaceHolder?, format: Int, width: Int, height: Int) { }
   //被移除時(shí)調(diào)用
   override fun surfaceDestroyed(holder: SurfaceHolder?) {}

這里創(chuàng)建相機(jī)的調(diào)用surfaceCreated扮念,之后會(huì)立刻調(diào)用一次surfaceChanged
這里在surfaceChanged上調(diào)用openGL的init操作

 fun initOpenGL(surface: Surface, width: Int, height: Int){
        //新開一個(gè)之后一個(gè)線程的線程池
        mExecutor.execute {
            //獲取紋理id
            val textureId = OpenGLJniLib.magicBaseInit(surface,width,height,BaseApplication.context.assets)
            
           if (textureId < 0){
                Log.e(TAG, "surfaceCreated init OpenGL ES failed!")
                return@execute
            }
            //需要使用surfaceTexture來(lái)做紋理裝載
            mSurfaceTexture = SurfaceTexture(textureId)
            //添加紋理變化回調(diào)
            mSurfaceTexture?.setOnFrameAvailableListener { drawOpenGL() }
            try {
                //把攝像頭采樣關(guān)聯(lián)到紋理
                mCamera?.setPreviewTexture(mSurfaceTexture)
                //開始攝像頭采樣
                doStartPreview()
            }catch (e:IOException){
                Log.e(TAG,e.localizedMessage)
                releaseOpenGL()
            }
        }
    }

5.開始預(yù)覽,并開始自動(dòng)對(duì)焦碧库。

    fun doStartPreview(){
        mCamera?.startPreview()
        cameraFocus(width/2.0f,height/2.0f)
    }

6.opengl繪制柜与,先要強(qiáng)制更新紋理圖像,再更新獲取紋理矩陣嵌灰,然后讓opengl繪制

    fun drawOpenGL(){
        mExecutor.execute {
            mSurfaceTexture?.updateTexImage()
            mSurfaceTexture?.getTransformMatrix(mMatrix)
            OpenGLJniLib.magicBaseDraw(mMatrix)
        }
    }

7.在destroySurfaceView的是否釋放資源

    fun releaseOpenGL(){
        mExecutor.execute {
            mSurfaceTexture?.release()
            mSurfaceTexture=null
            OpenGLJniLib.magicBaseRelease()
        }
    }

介紹了Camera采樣配置和弄匕,surfaceTexture紋理獲取了和加載,下一節(jié)沽瞭,將會(huì)介紹native層的opengl繪制代碼迁匠。

近來(lái)在寫一個(gè)有趣的項(xiàng)目,大家熟知攝像頭繪制和opengl的人應(yīng)該有看過(guò)MagicCamera這個(gè)Android opengl2.0的開源工程驹溃,但是已經(jīng)很多年沒(méi)人維護(hù)了城丧,這邊正在將其重構(gòu)為opengl3.0的的版本。命名為MagicCamera3以供大家學(xué)習(xí)豌鹤,現(xiàn)在還在重構(gòu)當(dāng)中亡哄,致敬作者,也希望可以有機(jī)會(huì)和作者多交流布疙,如果有認(rèn)識(shí)的可以告知我一聲蚊惯,謝謝。
開源地址:MagicCamera3灵临,這節(jié)的用例在CameraActivity中

?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請(qǐng)聯(lián)系作者
  • 序言:七十年代末拣挪,一起剝皮案震驚了整個(gè)濱河市,隨后出現(xiàn)的幾起案子俱诸,更是在濱河造成了極大的恐慌菠劝,老刑警劉巖,帶你破解...
    沈念sama閱讀 218,386評(píng)論 6 506
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件,死亡現(xiàn)場(chǎng)離奇詭異赶诊,居然都是意外死亡笼平,警方通過(guò)查閱死者的電腦和手機(jī),發(fā)現(xiàn)死者居然都...
    沈念sama閱讀 93,142評(píng)論 3 394
  • 文/潘曉璐 我一進(jìn)店門舔痪,熙熙樓的掌柜王于貴愁眉苦臉地迎上來(lái)寓调,“玉大人,你說(shuō)我怎么就攤上這事锄码《嵊ⅲ” “怎么了?”我有些...
    開封第一講書人閱讀 164,704評(píng)論 0 353
  • 文/不壞的土叔 我叫張陵滋捶,是天一觀的道長(zhǎng)痛悯。 經(jīng)常有香客問(wèn)我,道長(zhǎng)重窟,這世上最難降的妖魔是什么载萌? 我笑而不...
    開封第一講書人閱讀 58,702評(píng)論 1 294
  • 正文 為了忘掉前任,我火速辦了婚禮巡扇,結(jié)果婚禮上扭仁,老公的妹妹穿的比我還像新娘。我一直安慰自己厅翔,他們只是感情好乖坠,可當(dāng)我...
    茶點(diǎn)故事閱讀 67,716評(píng)論 6 392
  • 文/花漫 我一把揭開白布。 她就那樣靜靜地躺著刀闷,像睡著了一般熊泵。 火紅的嫁衣襯著肌膚如雪。 梳的紋絲不亂的頭發(fā)上涩赢,一...
    開封第一講書人閱讀 51,573評(píng)論 1 305
  • 那天戈次,我揣著相機(jī)與錄音轩勘,去河邊找鬼筒扒。 笑死,一個(gè)胖子當(dāng)著我的面吹牛绊寻,可吹牛的內(nèi)容都是我干的花墩。 我是一名探鬼主播,決...
    沈念sama閱讀 40,314評(píng)論 3 418
  • 文/蒼蘭香墨 我猛地睜開眼澄步,長(zhǎng)吁一口氣:“原來(lái)是場(chǎng)噩夢(mèng)啊……” “哼冰蘑!你這毒婦竟也來(lái)了?” 一聲冷哼從身側(cè)響起村缸,我...
    開封第一講書人閱讀 39,230評(píng)論 0 276
  • 序言:老撾萬(wàn)榮一對(duì)情侶失蹤祠肥,失蹤者是張志新(化名)和其女友劉穎,沒(méi)想到半個(gè)月后梯皿,有當(dāng)?shù)厝嗽跇淞掷锇l(fā)現(xiàn)了一具尸體仇箱,經(jīng)...
    沈念sama閱讀 45,680評(píng)論 1 314
  • 正文 獨(dú)居荒郊野嶺守林人離奇死亡县恕,尸身上長(zhǎng)有42處帶血的膿包…… 初始之章·張勛 以下內(nèi)容為張勛視角 年9月15日...
    茶點(diǎn)故事閱讀 37,873評(píng)論 3 336
  • 正文 我和宋清朗相戀三年,在試婚紗的時(shí)候發(fā)現(xiàn)自己被綠了剂桥。 大學(xué)時(shí)的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片忠烛。...
    茶點(diǎn)故事閱讀 39,991評(píng)論 1 348
  • 序言:一個(gè)原本活蹦亂跳的男人離奇死亡,死狀恐怖权逗,靈堂內(nèi)的尸體忽然破棺而出美尸,到底是詐尸還是另有隱情,我是刑警寧澤斟薇,帶...
    沈念sama閱讀 35,706評(píng)論 5 346
  • 正文 年R本政府宣布师坎,位于F島的核電站,受9級(jí)特大地震影響奔垦,放射性物質(zhì)發(fā)生泄漏屹耐。R本人自食惡果不足惜,卻給世界環(huán)境...
    茶點(diǎn)故事閱讀 41,329評(píng)論 3 330
  • 文/蒙蒙 一椿猎、第九天 我趴在偏房一處隱蔽的房頂上張望惶岭。 院中可真熱鬧,春花似錦犯眠、人聲如沸按灶。這莊子的主人今日做“春日...
    開封第一講書人閱讀 31,910評(píng)論 0 22
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽(yáng)鸯旁。三九已至,卻和暖如春量蕊,著一層夾襖步出監(jiān)牢的瞬間铺罢,已是汗流浹背。 一陣腳步聲響...
    開封第一講書人閱讀 33,038評(píng)論 1 270
  • 我被黑心中介騙來(lái)泰國(guó)打工残炮, 沒(méi)想到剛下飛機(jī)就差點(diǎn)兒被人妖公主榨干…… 1. 我叫王不留韭赘,地道東北人。 一個(gè)月前我還...
    沈念sama閱讀 48,158評(píng)論 3 370
  • 正文 我出身青樓势就,卻偏偏與公主長(zhǎng)得像泉瞻,于是被迫代替她去往敵國(guó)和親。 傳聞我的和親對(duì)象是個(gè)殘疾皇子苞冯,可洞房花燭夜當(dāng)晚...
    茶點(diǎn)故事閱讀 44,941評(píng)論 2 355

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