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

相信很多人都用過相機功能,也開發(fā)過簡單調(diào)度相機功能,但是相機采集功能。是圖像信號輸入的重要來源。

SurfaceView和View的不同之處:


SurfaceView和View對比

相機圖像采樣,需要維持一個比較穩(wěn)定的幀數(shù)來維持圖像實時性,需要頻繁刷新,創(chuàng)建一個子線程來進(jìn)行畫面更新,會被占用主線程效率好很多,而且雙緩沖機制可以在畫面從前臺刷新到后臺時才占用主線程操作,所以選用SurfaceView作繪制是最好的。
而GLSurfaceView是SurfaceView的一個子類,專用于openGL繪制,其運行效率遠(yuǎn)高于SurfaceView是因為使用了GPU參與繪制。
這一節(jié)介紹Android攝像頭采樣,還是采用了SurfaceView來做采樣
1.需要申請相機權(quán)限。

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

2.打開攝像頭,先檢查攝像和前置攝像頭,然后通過攝像頭Id,來返回攝像頭對象。

 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
    }

 /**
     * 相機采樣參數(shù)大小
     */
    fun setOptimalSize(camera:Camera,aspectRatio:Float,maxWidth:Int,maxHeight:Int){
        val parameters= camera.parameters
        //使用自動對焦
        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è)置相機預(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è)置相機圖像角度

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è)置一個SurfaceHolder.CallBack。
有三個必須的方法

   //創(chuàng)建時調(diào)用
   override fun surfaceCreated(holder: SurfaceHolder?) { }
   //當(dāng)surface發(fā)生任何結(jié)構(gòu)性的變化時(格式或者大?。摲椒ň蜁涣⒓凑{(diào)用。
   override fun surfaceChanged(holder: SurfaceHolder?, format: Int, width: Int, height: Int) { }
   //被移除時調(diào)用
   override fun surfaceDestroyed(holder: SurfaceHolder?) {}

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

 fun initOpenGL(surface: Surface, width: Int, height: Int){
        //新開一個之后一個線程的線程池
        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來做紋理裝載
            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ù)覽,并開始自動對焦。

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

6.opengl繪制,先要強制更新紋理圖像,再更新獲取紋理矩陣,然后讓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é),將會介紹native層的opengl繪制代碼。

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

?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請聯(lián)系作者
【社區(qū)內(nèi)容提示】社區(qū)部分內(nèi)容疑似由AI輔助生成,瀏覽時請結(jié)合常識與多方信息審慎甄別。
平臺聲明:文章內(nèi)容(如有圖片或視頻亦包括在內(nèi))由作者上傳并發(fā)布,文章內(nèi)容僅代表作者本人觀點,簡書系信息發(fā)布平臺,僅提供信息存儲服務(wù)。

相關(guān)閱讀更多精彩內(nèi)容

友情鏈接更多精彩內(nèi)容