Android camera2扫描

本文探讨了在Android中使用Camera2进行扫描时如何获取高清图像的问题。针对原Google demo存在的问题,建议避免使用AutoFitTextureView,并给出了调整分辨率的方法。同时,文章提到在特定设备如OPPO手机上,由于Camera2硬件支持等级限制,可能无法实现高清预览和拍照,建议采用低分辨率预览图作为替代方案。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

camera2扫描获取高清图

先看下google提供的demo https://github.com/googlesamples/android-Camera2Basic
扫描获取高清图也是基于这个demo做了一些修改,遇到一些问题,这边讲述下自己的想法,大佬勿喷。

  1. demo中自定义的AutoFitTextureView 我建议是不要用,它是通过手机的屏幕分辨率来选择的分辨率,现在国产手机分辨率有很多不是16:9,4:3的,所以它这套算法下来,会选取比较低的分辨率,就导致预览的时候模糊看不清。
    我个人建议是先选定自己想要的分辨率,(我用的是1080*1920)然后设置使用TextureView设置宽为match_parent,最后动态设置高度为 textureView高度 = textureView宽 * 预览高 / 预览宽

2.Camera2功能支持情况 我做的是扫描软件,同时又要求要高清图,为了保证预览无卡顿,所以要做到拍摄图片时,预览不能暂停,但是发现oppo手机不行,取一张图就卡一次,用户体验非常不好,我也傻到联系他们客服(当然客服跟你讲的都是官方话术)这里要感谢一位大佬 这是他写 https://www.jianshu.com/p/23e8789fbc10 以下是摘抄

查询Camera2功能支持情况
上面说过, 不是所以手机都支持完整的Camera2功能, 现在都2018了, Camera2出来都有4年左右了, 但估计还有些中低端手机使用的HAL1, 使用HAL1就会导致Camera2一些高级功能都没法使用了, 下面讲一下如何查询设备对应Camera2的支持情况.
INFO_SUPPORTED_HARDWARE_LEVEL
硬件层面支持的Camera2功能等级, 主要分为5个等级:

INFO_SUPPORTED_HARDWARE_LEVEL_LEGACY
INFO_SUPPORTED_HARDWARE_LEVEL_LIMITED
INFO_SUPPORTED_HARDWARE_LEVEL_FULL
INFO_SUPPORTED_HARDWARE_LEVEL_3
INFO_SUPPORTED_HARDWARE_LEVEL_EXTERNAL

LEVEL_LEGACY: 向后兼容模式, 如果是此等级, 基本没有额外功能, HAL层大概率就是HAL1(我遇到过的都是)
LEVEL_LIMITED: 有最基本的功能, 还支持一些额外的高级功能, 这些高级功能是LEVEL_FULL的子集
LEVEL_FULL: 支持对每一帧数据进行控制,还支持高速率的图片拍摄
LEVEL_3: 支持YUV后处理和Raw格式图片拍摄, 还支持额外的输出流配置
LEVEL_EXTERNAL: API28中加入的, 应该是外接的摄像头, 功能和LIMITED类似
各个等级从支持的功能多少排序为: LEGACY < LIMITED < FULL < LEVEL_3
获取等级相关代码

private int isHardwareSupported(CameraCharacteristics characteristics) {
        Integer deviceLevel = characteristics.get(CameraCharacteristics.INFO_SUPPORTED_HARDWARE_LEVEL);
        if (deviceLevel == null) {
            Log.e(TAG, "can not get INFO_SUPPORTED_HARDWARE_LEVEL");
            return -1;
        }
        switch (deviceLevel) {
            case CameraCharacteristics.INFO_SUPPORTED_HARDWARE_LEVEL_FULL:
                Log.w(TAG, "hardware supported level:LEVEL_FULL");
                break;
            case CameraCharacteristics.INFO_SUPPORTED_HARDWARE_LEVEL_LEGACY:
                Log.w(TAG, "hardware supported level:LEVEL_LEGACY");
                break;
            case CameraCharacteristics.INFO_SUPPORTED_HARDWARE_LEVEL_3:
                Log.w(TAG, "hardware supported level:LEVEL_3");
                break;
            case CameraCharacteristics.INFO_SUPPORTED_HARDWARE_LEVEL_LIMITED:
                Log.w(TAG, "hardware supported level:LEVEL_LIMITED");
                break;
        }
        return deviceLevel;
    }

. 测试了几款手机 oppo手机的等级就是LEVEL_LEGACY,也就是个five。所以oppo用camera2的话,预览的时候就不要想拿高分辨的图了,也不要拍照,直接获取低分辨率预览图,也是可以使用的。

开始贴代码

public class CameraActivity2 extends BaseActivity
        implements ActivityCompat.OnRequestPermissionsResultCallback {

    private static final SparseIntArray ORIENTATIONS = new SparseIntArray();
    private static final int REQUEST_CAMERA_PERMISSION = 1;
    private static final int TIME_OUT = 1010;//鉴别超时
    private ImageView ivOutJb,iv_tishi,iv_light,iv_biaozhi;
    private ViewfinderView viewfinderView;
  
    static {
        ORIENTATIONS.append(Surface.ROTATION_0, 90);
        ORIENTATIONS.append(Surface.ROTATION_90, 0);
        ORIENTATIONS.append(Surface.ROTATION_180, 270);
        ORIENTATIONS.append(Surface.ROTATION_270, 180);
    }

    /**
     * Tag for the {@link Log}.
     */

    private CameraManager manager;
    /**
     * Camera state: Showing camera preview.
     */
    private static final int STATE_PREVIEW = 0;

    /**
     * Camera state: Waiting for the focus to be locked.
     */
    private static final int STATE_WAITING_LOCK = 1;

    /**
     * Camera state: Waiting for the exposure to be precapture state.
     */
    private static final int STATE_WAITING_PRECAPTURE = 2;

    /**
     * Camera state: Waiting for the exposure state to be something other than precapture.
     */
    private static final int STATE_WAITING_NON_PRECAPTURE = 3;

    /**
     * Camera state: Picture was taken.
     */
    private static final int STATE_PICTURE_TAKEN = 4;

    /**
     * Max preview width that is guaranteed by Camera2 API
     */
    private static final int MAX_PREVIEW_WIDTH = 1920;

    /**
     * Max preview height that is guaranteed by Camera2 API
     */
    private static final int MAX_PREVIEW_HEIGHT = 1080;

    private int picWidth;
    private int picHeight;
    /**
     * {@link TextureView.SurfaceTextureListener} handles several lifecycle events on a
     * {@link TextureView}.
     */
    private final TextureView.SurfaceTextureListener mSurfaceTextureListener
            = new TextureView.SurfaceTextureListener() {

        @Override
        public void onSurfaceTextureAvailable(SurfaceTexture texture, int width, int height) {
            openCamera(width, height);
        }

        @Override
        public void onSurfaceTextureSizeChanged(SurfaceTexture texture, int width, int height) {
            configureTransform(width, height);
        }

        @Override
        public boolean onSurfaceTextureDestroyed(SurfaceTexture texture) {
            return true;
        }

        @Override
        public void onSurfaceTextureUpdated(SurfaceTexture texture) {

        }
    };


    /**
     * ID of the current {@link CameraDevice}.
     */
    private String mCameraId;

    /**
     * An {@link AutoFitTextureView} for camera preview.
     */
    private TextureView mTextureView;

    /**
     * A {@link CameraCaptureSession } for camera preview.
     */
    private CameraCaptureSession mCaptureSession;

    /**
     * A reference to the opened {@link CameraDevice}.
     */
    private CameraDevice mCameraDevice;

    /**
     * 预览
     */
    private Size mPreviewSize;
    
	//JPEG格式下支持拍摄图片的数组集合
    private Size [] pic;
    /**
     * {@link CameraDevice.StateCallback} is called when {@link CameraDevice} changes its state.
     */
    private final CameraDevice.StateCallback mStateCallback = new CameraDevice.StateCallback() {

        @Override
        public void onOpened(@NonNull CameraDevice cameraDevice) {
            // This method is called when the camera is opened.  We start camera preview here.
            mCameraOpenCloseLock.release();
            mCameraDevice = cameraDevice;
            createCameraPreviewSession();
        }

        @Override
        public void onDisconnected(@NonNull CameraDevice cameraDevice) {
            mCameraOpenCloseLock.release();
            cameraDevice.close();
            mCameraDevice = null;
        }

        @Override
        public void onError(@NonNull CameraDevice cameraDevice, int error) {
            mCameraOpenCloseLock.release();
            cameraDevice.close();
            mCameraDevice = null;
            finish();

        }

    };

    /**
     * An additional thread for running tasks that shouldn't block the UI.
     */
    private HandlerThread mBackgroundThread;

    /**
     * A {@link Handler} for running tasks in the background.
     */
    private Handler mBackgroundHandler;

    /**
     * An {@link ImageReader} that handles still image capture.
     */
    private ImageReader mImageReader;

    /**
     * This is the output file for our picture.
     */
    private File mFile;

    /**
     * This a callback object for the {@link ImageReader}. "onImageAvailable" will be called when a
     * still image is ready to be saved.
     */
    private final ImageReader.OnImageAvailableListener mOnImageAvailableListener
            = new ImageReader.OnImageAvailableListener() {

        @Override
        public void onImageAvailable(ImageReader reader) {
                mBackgroundHandler.post(new ImageSaver(reader.acquireLatestImage(), mFile));

        }

    };

    /**
     * {@link CaptureRequest.Builder} for the camera preview
     */
    private CaptureRequest.Builder mPreviewRequestBuilder;

    /**
     * {@link CaptureRequest} generated by {@link #mPreviewRequestBuilder}
     */
    private CaptureRequest mPreviewRequest;

    /**
     * The current state of camera state for taking pictures.
     *
     * @see #mCaptureCallback
     */
    private int mState = STATE_PREVIEW;

    /**
     * A {@link Semaphore} to prevent the app from exiting before closing the camera.
     */
    private Semaphore mCameraOpenCloseLock = new Semaphore(1);

    /**
     * Whether the current camera device supports Flash or not.
     */
    private boolean mFlashSupported;

    /**
     * Orientation of the camera sensor
     */
    private int mSensorOrientation;

    /**
     * A {@link CameraCaptureSession.CaptureCallback} that handles events related to JPEG capture.
     */
    private CameraCaptureSession.CaptureCallback mCaptureCallback
            = new CameraCaptureSession.CaptureCallback() {

        private void process(CaptureResult result) {
            switch (mState) {
                case STATE_PREVIEW: {
                    Log.d("stateinfo","STATE_PREVIEW");
                    // We have nothing to do when the camera preview is working normally.
                    break;
                }
                case STATE_WAITING_LOCK: {
                    Integer afState = result.get(CaptureResult.CONTROL_AF_STATE);
                    if (afState == null) {
				       captureStillPicture();
                    } else if (CaptureResult.CONTROL_AF_STATE_PASSIVE_FOCUSED == afState) {
                            mState = STATE_PREVIEW;
                            captureStillPicture();
                    }
                    break;
                }
            }
        }

        @Override
        public void onCaptureProgressed(@NonNull CameraCaptureSession session,
                                        @NonNull CaptureRequest request,
                                        @NonNull CaptureResult partialResult) {
            process(partialResult);
        }

        @Override
        public void onCaptureCompleted(@NonNull CameraCaptureSession session,
                                       @NonNull CaptureRequest request,
                                       @NonNull TotalCaptureResult result) {
            process(result);
        }

    };

    /**
     * Shows a {@link Toast} on the UI thread.
     *
     * @param text The message to show
     */
    private void showToast(final String text) {

        runOnUiThread(new Runnable() {
            @Override
            public void run() {
                Toast.makeText(CameraActivity2.this, text, Toast.LENGTH_SHORT).show();
            }
        });

    }

	//这边做了修改 选择比较合适的分辨率
    private Size chooseOptimalSize(Size[] choices, int textureViewWidth,
                                          int textureViewHeight, int maxWidth, int maxHeight, Size aspectRatio) {
        List<Size> bigEnough = new ArrayList<>();
        List<Size> bestBigEnough = new ArrayList<>();
        List<Size> notBigEnough = new ArrayList<>();
        List<Size> bestSize = new ArrayList<>();
        //以1080p为基准高于1080p的则视为高分辨率相反则视为低分辨率
        for (Size option : choices) {
            int w = option.getWidth();
            int h = option.getHeight();
            double rate = (double) w/h;
            String  rateStr = String.valueOf(rate);
           if (w > 1920 && h > 1080){
               bigEnough.add(option);
               if (rateStr.startsWith("1.7")){
                    bestBigEnough.add(option);
               }
           }else {
               if (rateStr.startsWith("1.7")){
                  bestSize.add(option);
               }
               notBigEnough.add(option);
           }


        }

        //choose 16:9
        if (bestSize.size() > 0){
            return Collections.max(bestSize,new CompareSizesByArea());
        }else if (notBigEnough.size() > 0){
            return Collections.max(notBigEnough, new CompareSizesByArea());
        }else if (bestBigEnough.size()> 0){
            return Collections.min(bestBigEnough, new CompareSizesByArea());
        }else if (bigEnough.size() > 0){
            return Collections.min(bigEnough, new CompareSizesByArea());
        } else {
            return choices[0];
        }
    }



    @Override
    public void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.capture1);
        AppManager.getAppManager().addActivity(this);
        getWindow().addFlags(WindowManager.LayoutParams.FLAG_KEEP_SCREEN_ON);
        setScreenBrightness(200);
        mTextureView = findViewById(R.id.preview_view);
        viewfinderView = findViewById(R.id.scanner_view);
        mFile = new File(Environment.getExternalStorageDirectory(), "自己取名字" +"Pic.jpg");
        mCurrentState = AppManager.getAppManager().getCurrentLightState();
        if (mCurrentState == 0){
            iv_light.setImageResource(R.drawable.a31);
        }else {
            iv_light.setImageResource(R.drawable.a32);
        }
        Toast.makeText(this, R.string.mingliang, Toast.LENGTH_SHORT).show();
        getScreenInfo();

    }

    /**
     * 开始计时
     */
    private void startTimeCount() {
        time = 0;
        start();
    }

    private int screenWidth;
    private int screenHeight;
    private void getScreenInfo(){
        //获取内屏分辨率(华为手机有bar这个值不会变)
//        WindowManager windowManager = getWindowManager();
//        Display d = windowManager.getDefaultDisplay();
//        DisplayMetrics realDis = new DisplayMetrics();
//        d.getRealMetrics(realDis);
//        screenHeight = realDis.heightPixels;
//        screenWidth = realDis.widthPixels;
        Point screenPoint = Camera2Manager.getCamera2Manager().getScreenPoint();
        screenWidth = screenPoint.x;
        screenHeight = screenPoint.y;
    }

    @Override
    public void onResume() {
        super.onResume();
        Log.d("decodeMessage","onResume");
        startBackgroundThread();
        isF
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值