Android Camera Preview在SurfaceView上变形了

时间:2019-08-30 15:48:57

标签: android android-camera2

我已经实施了相机预览活动:

package ai.my.mysdktest

import android.annotation.SuppressLint
import android.app.Activity
import android.content.Intent
import android.content.pm.PackageManager
import android.net.Uri
import androidx.appcompat.app.AppCompatActivity
import android.os.Bundle
import androidx.core.app.ActivityCompat
import androidx.core.content.ContextCompat
import android.Manifest
import android.content.Context
import android.graphics.ImageFormat
import android.graphics.ImageFormat.YUV_420_888
import android.graphics.PointF
import android.graphics.Rect
import android.hardware.camera2.*
import android.hardware.camera2.CameraDevice.TEMPLATE_PREVIEW
import android.media.ImageReader
import android.provider.Settings
import android.widget.Toast
import android.os.Handler
import android.util.Log
import android.hardware.camera2.CaptureRequest
import android.util.Size
import android.view.*

import java.nio.ByteBuffer

import ai.my.mysdk.CameraPermissionHelper
import android.graphics.Camera
import kotlinx.android.synthetic.main.activity_camera2.*






class CameraActivity2 : AppCompatActivity() {

    private val TAG: String = CameraActivity::class.java.simpleName

    lateinit var camera: CameraDevice
    private lateinit var cameraManager: CameraManager
    lateinit var cameraCharacteristics: CameraCharacteristics
    lateinit var captureSession: CameraCaptureSession
    lateinit var previewRequestBuilder: CaptureRequest.Builder
    lateinit var captureCallback: CameraCaptureSession.StateCallback

    //Zooming
    var fingerSpacing: Float = 0F
    var zoomLevel: Float = 1F
    private var maximumZoomLevel: Float = 1F
    private var zoom: Rect = Rect()


    lateinit var imageReader: ImageReader
    //lateinit var interpreter: Interpreter
    lateinit var imgData: ByteBuffer


    lateinit var previewSurface: Surface
    lateinit var recordingSurface: Surface



    lateinit var previewSize: Size
    lateinit var decodeSize: Size

    override fun onCreate(savedInstanceState: Bundle?) {

        super.onCreate(savedInstanceState)

        requestWindowFeature(Window.FEATURE_NO_TITLE)

        getWindow().setFlags(WindowManager.LayoutParams.FLAG_FULLSCREEN,
            WindowManager.LayoutParams.FLAG_FULLSCREEN
        )

        getWindow().addFlags(WindowManager.LayoutParams.FLAG_KEEP_SCREEN_ON)

        setContentView(R.layout.activity_camera)

        cameraManager = getSystemService(Context.CAMERA_SERVICE) as CameraManager


       surfaceView.holder.addCallback(surfaceReadyCallback)

        if (!CameraPermissionHelper.hasCameraPermission(this)) {
            CameraPermissionHelper.requestCameraPermission(this)
            return
        }


    }

    override fun onRequestPermissionsResult(requestCode: Int, permissions: Array<out String>, grantResults: IntArray) {
        Log.d(TAG, "")
        if (!CameraPermissionHelper.hasCameraPermission(this)) {
            Toast.makeText(this, "Camera permission is needed to run this application", Toast.LENGTH_LONG)
                .show()
            if (!CameraPermissionHelper.shouldShowRequestPermissionRationale(this)) {
                // Permission denied with checking "Do not ask again".
                CameraPermissionHelper.launchPermissionSettings(this)
            }
            finish()
        }

        recreate()
    }


    override fun onTouchEvent(event: MotionEvent): Boolean {
        try {
            val rect = cameraCharacteristics.get(CameraCharacteristics.SENSOR_INFO_ACTIVE_ARRAY_SIZE)
                ?: return false
            val currentFingerSpacing: Float

            if (event.pointerCount == 2) { //Multi touch.
                currentFingerSpacing = event.getFingerSpacing()
                var delta = 0.05f //Control this value to control the zooming sensibility
                if (fingerSpacing != 0F) {
                    if (currentFingerSpacing > fingerSpacing) { //Don't over zoom-in
                        if (maximumZoomLevel - zoomLevel <= delta) {
                            delta = maximumZoomLevel - zoomLevel
                        }
                        zoomLevel += delta
                    } else if (currentFingerSpacing < fingerSpacing) { //Don't over zoom-out
                        if (zoomLevel - delta < 1f) {
                            delta = zoomLevel - 1f
                        }
                        zoomLevel -= delta
                    }
                    val ratio = 1.toFloat() / zoomLevel //This ratio is the ratio of cropped Rect to Camera's original(Maximum) Rect
                    //croppedWidth and croppedHeight are the pixels cropped away, not pixels after cropped
                    val croppedWidth = rect.width() - Math.round(rect.width() * ratio)
                    val croppedHeight = rect.height() - Math.round(rect.height() * ratio)
                    //Finally, zoom represents the zoomed visible area
                    zoom = Rect(croppedWidth / 2, croppedHeight / 2,
                        rect.width() - croppedWidth / 2, rect.height() - croppedHeight / 2)
                    previewRequestBuilder.set(CaptureRequest.SCALER_CROP_REGION, zoom)
                }
                fingerSpacing = currentFingerSpacing
            } else { //Single touch point, needs to return true in order to detect one more touch point
                return true
            }

            captureSession.setRepeatingRequest(previewRequestBuilder.build(), object: CameraCaptureSession.CaptureCallback() {}, Handler { true })

            return true
        } catch (e: Exception) {
            //Error handling up to you
            return true
        }

    }

    private fun MotionEvent.getFingerSpacing(): Float {
        val x = getX(0) - getX(1)
        val y = getY(0) - getY(1)
        return Math.sqrt((x * x + y * y).toDouble()).toFloat()
    }



    @SuppressLint("MissingPermission")
    private fun startCameraSession() {

        with (cameraManager) {

            if (cameraIdList.isEmpty()) {
                //toast("You need a camera to use this app.")
                return
            }

            val firstCamera = cameraIdList[0]

            openCamera(firstCamera, object: CameraDevice.StateCallback() {
                override fun onDisconnected(p0: CameraDevice) {
                    Log.d(TAG, "Camera is Disconnected")
                }


                override fun onError(p0: CameraDevice, p1: Int) {
                    Log.d(TAG, "Camera Error: $p1")
                }

                override fun onOpened(cam: CameraDevice) {
                    camera = cam
                    cameraCharacteristics = getCameraCharacteristics(camera.id)
                    val configMap = cameraCharacteristics[CameraCharacteristics.SCALER_STREAM_CONFIGURATION_MAP]

                    cameraCharacteristics.get(CameraCharacteristics.SCALER_AVAILABLE_MAX_DIGITAL_ZOOM)?.let {
                        maximumZoomLevel = it * 10
                    }



                    if (configMap == null) {
                        //toast("Could not configure your camera for use with this application.")
                        Log.d(TAG,"No config map for camera: ${camera.id}")
                        return
                    }

                    val yuvSizes = configMap.getOutputSizes(ImageFormat.YUV_420_888)

                    if (yuvSizes.isEmpty()) {
                        //toast("Could not configure your camera for use with this application.")
                        Log.d(TAG,"No sizes found for format YUV")
                        return
                    }

                    /* Beautiful preview */
                    previewSize = yuvSizes.first()
                    decodeSize = yuvSizes.first()

                    val displayRotation = windowManager.defaultDisplay.rotation
                    val swappedDimensions = areDimensionsSwapped(displayRotation = displayRotation)

                    val rotatedPreviewWidth = if (swappedDimensions) previewSize.height else previewSize.width
                    val rotatedPreviewHeight = if (swappedDimensions) previewSize.width else previewSize.height



                    surfaceView.holder.setSizeFromLayout()// setFixedSize(rotatedPreviewWidth, rotatedPreviewHeight)

                    // Configure Image Reader
                    imageReader = ImageReader.newInstance(rotatedPreviewWidth, rotatedPreviewHeight, YUV_420_888, 10)



                    imageReader.setOnImageAvailableListener(
                        { imageReader ->

                            val image = imageReader.acquireLatestImage()
                            Log.d(TAG, "Image Available: H (${image?.height}), W (${image?.width})")
                            Log.d(TAG, "Surface Size: H (${surfaceView.height}), W (${surfaceView.width})")
                            image?.close()

                        }, Handler {true})

                    previewSurface = surfaceView.holder.surface
                    recordingSurface = imageReader.surface


                    captureCallback = object : CameraCaptureSession.StateCallback() {
                        override fun onConfigureFailed(session: CameraCaptureSession) { }

                        override fun onConfigured(session: CameraCaptureSession) {

                            previewRequestBuilder = camera.createCaptureRequest(TEMPLATE_PREVIEW).apply {
                                addTarget(recordingSurface)
                                addTarget(previewSurface)
                            }

                            session.setRepeatingRequest(previewRequestBuilder.build(), object: CameraCaptureSession.CaptureCallback() {}, Handler { true })

                            captureSession = session
                        }
                    }

                    camera.createCaptureSession(mutableListOf(previewSurface, recordingSurface), captureCallback, Handler { true })

                    //val hasSeenFTX = sharedPreferences.getBoolean(SHARED_PREFERENCES_FTX_KEY, false)
                    //if (!hasSeenFTX) {
                    //    showFirstTooltip()
                    //}
                }
            }, Handler { true })
        }
    }

    private fun areDimensionsSwapped(displayRotation: Int): Boolean {
        var swappedDimensions = false
        when (displayRotation) {
            Surface.ROTATION_0, Surface.ROTATION_180 -> {
                if (cameraCharacteristics.get(CameraCharacteristics.SENSOR_ORIENTATION) == 90 || cameraCharacteristics.get(CameraCharacteristics.SENSOR_ORIENTATION) == 270) {
                    swappedDimensions = true
                }
            }
            Surface.ROTATION_90, Surface.ROTATION_270 -> {
                if (cameraCharacteristics.get(CameraCharacteristics.SENSOR_ORIENTATION) == 0 || cameraCharacteristics.get(CameraCharacteristics.SENSOR_ORIENTATION) == 180) {
                    swappedDimensions = true
                }
            }
            else -> {
                Log.d(TAG, "Display rotation is invalid: $displayRotation")
            }
        }
        return swappedDimensions
    }

    private val surfaceReadyCallback = object: SurfaceHolder.Callback {
        override fun surfaceChanged(p0: SurfaceHolder?, p1: Int, p2: Int, p3: Int) {
            Log.d(TAG, "Surface Changed ${p0?.surface.toString()}")
        }
        override fun surfaceDestroyed(p0: SurfaceHolder?) {
            Log.d(TAG, "Surface Destroyed ${p0?.surface.toString()}")
        }

        override fun surfaceCreated(p0: SurfaceHolder?) {
            startCameraSession()
        }
    }

    /**
     * Decode image to [PixelArray]
     */
    /*
    private val decodeImageToPixels = imageReader. .OnImageAvailableListener()  { imageReader ->

        val image  = imageReader.acquireLatestImage()
        image?.close()
        Log.i(TAG, "Image Available: ${image.timestamp}")
    }*/


}

该活动具有以下布局:

<?xml version="1.0" encoding="utf-8"?>



<FrameLayout xmlns:android="http://schemas.android.com/apk/res/android"
    xmlns:tools="http://schemas.android.com/tools"
    android:id="@+id/container"
    android:layout_width="match_parent"
    android:layout_height="match_parent"
    android:background="#000"
    tools:context=".CameraActivity2" >

    <SurfaceView
        android:id="@+id/surfaceView"
        android:layout_width="match_parent"
        android:layout_height="match_parent"
       />

</FrameLayout>

图像按预期显示在屏幕上,但是存在大量失真。当手机为人像时,图像会拉伸,然后随着手机旋转而被压缩。

由于调试日志,我可以看到预览大小与surfaceView的大小不符。

Image Available: H (1080), W (1920)
Surface Size: H (2094), W (1080)

如何使预览图像与Surface显示器匹配并停止失真?

1 个答案:

答案 0 :(得分:0)