• 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,  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()
        }
  • 相关阅读:
    举例一个IO多路复用的C/S例子
    简单介绍协程
    生产者消费者模型
    多进程介绍
    有关多线程(同步锁,递归锁,同步对象,信号量)
    threading多线程模块
    开发一个支持多用户在线的FTP程序
    NTP时间服务器与客户端
    EF之增删改查
    返回新插入的数据的主键ID
  • 原文地址:https://www.cnblogs.com/fuyaozhishang/p/9868664.html
Copyright © 2020-2023  润新知