Camera 预览变形问题解决

  最近开发一款自定义相机采集照片的demo,花了一个上午开发了一个在测试机上功能正常的apk连同测试机一起交付(需求方反馈没有Android设备),然而晚上被喊去说是在华为畅玩某型号上预览会变形,拍到的图片边界都移位了,只要加个班处理一下机型适配的问题。根据开发经验,防止预览图像变形的终极奥义就是保持照相机硬件支持的某个size(每个特定的手机都有其支持的camera分辨率)、屏幕宽高比、界面上SurfaceView的宽高比尽量一致,同时保持拍照图与预览图分辨率一致,拍出来的照片就和预览看到的相同。

  下面给出实现代码:

  

    private fun getOptimalPreviewSize(sizes: List<Camera.Size>?, w: Int, h: Int): Camera.Size? {
        val ASPECT_TOLERANCE = 0.1
        val targetRatio = h.toDouble() / w

        if (sizes == null) return null

        var optimalSize: Camera.Size? = null
        var minDiff = java.lang.Double.MAX_VALUE

        for (size in sizes) {
            val ratio = size.width.toDouble() / size.height
            if (Math.abs(ratio - targetRatio) > ASPECT_TOLERANCE) continue
            if (Math.abs(size.height - h) < minDiff) {
                optimalSize = size
                minDiff = Math.abs(size.height - h).toDouble()
            }
        }

        if (optimalSize == null) {
            minDiff = java.lang.Double.MAX_VALUE
            for (size in sizes) {
                if (Math.abs(size.height - h) < minDiff) {
                    optimalSize = size
                    minDiff = Math.abs(size.height - h).toDouble()
                }
            }
        }
        return optimalSize
    }

其中第一个参数sizes是camera支持的预览size列表,第二个是View的宽,第三个是View的高,由于我这里的surfaceview是全屏的,所以传入的宽和高可以是屏幕的宽和高

下面给出完整代码:

    
    override fun onCreate(savedInstanceState: Bundle?) {
        super.onCreate(savedInstanceState)
        setContentView(R.layout.activity_camera)
        screenWidth = ScreenUtil.getWindowWidth(this)
        screenHeight = ScreenUtil.getWindowHeigh(this)
        camera = Camera.open(Camera.CameraInfo.CAMERA_FACING_BACK)
        surface.holder.addCallback(this)
    }
    
    override fun surfaceChanged(holder: SurfaceHolder?, format: Int, width: Int, height: Int) {
        camera?.setPreviewDisplay(holder)
        camera?.startPreview()
    }

    override fun surfaceDestroyed(holder: SurfaceHolder?) {
    }


    override fun surfaceCreated(holder: SurfaceHolder?) {
        if (camera == null) {
            camera = Camera.open(Camera.CameraInfo.CAMERA_FACING_BACK)
        }
        val parameters = camera?.parameters
        var sizes = parameters!!.supportedPreviewSizes
        var preSize = getOptimalPreviewSize(sizes, screenWidth, screenHeight)
        parameters.setPreviewSize(preSize!!.width, preSize.height)
        //重新给定surfaceView宽高
        surface.resize(preSize.width, preSize.height)
        if (parameters.supportedFocusModes!!.contains(Camera.Parameters.FOCUS_MODE_CONTINUOUS_VIDEO)) {
            parameters.focusMode = Camera.Parameters.FOCUS_MODE_CONTINUOUS_VIDEO
        }
        parameters.sceneMode = Camera.Parameters.SCENE_MODE_AUTO
        //保持picturesize与presize一致
        parameters.setPictureSize(preSize.width, preSize.height)
        parameters.previewFormat = ImageFormat.NV21
        camera?.parameters = parameters
        camera?.setPreviewDisplay(holder)
        camera?.startPreview()
    }
    
    
    private fun getOptimalPreviewSize(sizes: List<Camera.Size>?, w: Int, h: Int): Camera.Size? {
        val ASPECT_TOLERANCE = 0.1
        val targetRatio = h.toDouble() / w

        if (sizes == null) return null

        var optimalSize: Camera.Size? = null
        var minDiff = java.lang.Double.MAX_VALUE

        for (size in sizes) {
            val ratio = size.width.toDouble() / size.height
            if (Math.abs(ratio - targetRatio) > ASPECT_TOLERANCE) continue
            if (Math.abs(size.height - h) < minDiff) {
                optimalSize = size
                minDiff = Math.abs(size.height - h).toDouble()
            }
        }

        if (optimalSize == null) {
            minDiff = java.lang.Double.MAX_VALUE
            for (size in sizes) {
                if (Math.abs(size.height - h) < minDiff) {
                    optimalSize = size
                    minDiff = Math.abs(size.height - h).toDouble()
                }
            }
        }
        return optimalSize
    }

    
    override fun onWindowFocusChanged(hasFocus: Boolean) {
        val decorView = this@CameraActivity.window.decorView
        decorView.systemUiVisibility = (View.SYSTEM_UI_FLAG_LAYOUT_STABLE or View.SYSTEM_UI_FLAG_LAYOUT_HIDE_NAVIGATION
                or View.SYSTEM_UI_FLAG_LAYOUT_FULLSCREEN
                or View.SYSTEM_UI_FLAG_HIDE_NAVIGATION
                or View.SYSTEM_UI_FLAG_FULLSCREEN
                or View.SYSTEM_UI_FLAG_IMMERSIVE_STICKY)
    }

    override fun onPause() {
        super.onPause()
        camera?.stopPreview()
    }

    override fun onDestroy() {
        super.onDestroy()
        camera?.setPreviewCallback(null)
        camera?.release()
    }

 

posted @ 2018-10-29 08:58  你要  阅读(2918)  评论(0编辑  收藏  举报