首页 > 解决方案 > Camera 2 API 会降低拍摄后的质量

问题描述

我在我的应用程序中使用相机来拍摄身份证照片,我有一个矩形叠加层,图像将被裁剪。问题是一旦捕获图像,图像质量就会降低。

我无法弄清楚它到底发生在哪里。在 cutImage 方法中,我正在剪切图像,但我认为我没有对那里的图像分辨率做任何事情。

任何人都可以建议质量可能下降的地方。

当用户点击拍照时调用 takePicture。拍摄照片后,会出现一个“使用图片”按钮,即调用 usePicture 时。

cutImage 方法用于根据预览裁剪图像。

关于如何阻止决议下降的任何建议都将非常有帮助

protected void takePicture() {
    Log.e(TAG, "takePicture started");
    if(null == cameraDevice) {
        Log.e(TAG, "cameraDevice is null");
        return;
    }
    try {
        ImageReader reader = ImageReader.newInstance(textureViewWidth, textureViewHeight, ImageFormat.JPEG, 1);
        List<Surface> outputSurfaces = new ArrayList<Surface>(2);
        outputSurfaces.add(reader.getSurface());
        outputSurfaces.add(new Surface(textureView.getSurfaceTexture()));
        final CaptureRequest.Builder captureBuilder = cameraDevice.createCaptureRequest(CameraDevice.TEMPLATE_STILL_CAPTURE);
        captureBuilder.addTarget(reader.getSurface());
        captureBuilder.set(CaptureRequest.CONTROL_MODE, CameraMetadata.CONTROL_MODE_AUTO);
        // Orientation
        int rotation = getActivity().getWindowManager().getDefaultDisplay().getRotation();
        captureBuilder.set(CaptureRequest.JPEG_ORIENTATION, ORIENTATIONS.get(rotation));
        ImageReader.OnImageAvailableListener readerListener = new ImageReader.OnImageAvailableListener() {
            @Override
            public void onImageAvailable(ImageReader reader) {
                Image image = null;
                try {
                    image = reader.acquireLatestImage();
                    ByteBuffer buffer = image.getPlanes()[0].getBuffer();
                    byte[] bytes = new byte[buffer.capacity()];
                    buffer.get(bytes);
                    takenPictureBytes = bytes;
                    Log.d(TAG, "takenPictureBytes length - " + takenPictureBytes.length);

                } catch (Exception e) {
                    Log.d(TAG, " onImageAvailable exception ");
                    e.printStackTrace();
                } finally {
                    if (image != null) {
                        Log.d(TAG, " image closing");
                        image.close();
                    }
                }
            }
        };
        reader.setOnImageAvailableListener(readerListener, mBackgroundHandler);
        final CameraCaptureSession.CaptureCallback captureListener = new CameraCaptureSession.CaptureCallback() {
            @Override
            public void onCaptureCompleted(CameraCaptureSession session, CaptureRequest request, TotalCaptureResult result) {
                super.onCaptureCompleted(session, request, result);
                Log.d(TAG, "takePicture - camera capture session");
                switchPanels(true);
                progress.setVisibility(View.GONE);
            }
        };
        cameraDevice.createCaptureSession(outputSurfaces, new CameraCaptureSession.StateCallback() {
            @Override
            public void onConfigured(CameraCaptureSession session) {
                try {
                    session.capture(captureBuilder.build(), captureListener, mBackgroundHandler);
                } catch (CameraAccessException e) {
                    Log.d(TAG, "takePicture - onConfigured- camera access exception ");
                    e.printStackTrace();
                }
            }
            @Override
            public void onConfigureFailed(CameraCaptureSession session) {
                Log.d(TAG, "takePicture - onConfigureFailed");

            }
        }, mBackgroundHandler);
    } catch (CameraAccessException e) {
        Log.d(TAG, "takePicture - CameraAccessException ");
        e.printStackTrace();
    }
}

private void usePicture() {
    Log.d(TAG, "usePicture - started     ");

    if(null != takenPictureBytes ){
        try{
            String imagePath = null;

            Bitmap bitmap = BitmapFactory.decodeByteArray(takenPictureBytes, 0, takenPictureBytes.length);
            int bitmapByteCountUsePic = byteSizeOf(bitmap);
            Matrix matrix = new Matrix();
            matrix.postRotate(90);
            Bitmap rotatedBitmap = Bitmap.createBitmap(bitmap, 0, 0, bitmap.getWidth(), bitmap.getHeight(), matrix, true);

            if (isFrameMode) {
                float withRatio = (float) rotatedBitmap.getWidth() / (float) textureViewWidth;
                float heightRatio = (float) rotatedBitmap.getHeight() / (float) textureViewHeight;

                Bitmap newImage = cutImage(rotatedBitmap, (int) (photoFrameView.getWidth() * withRatio), (int) (photoFrameView.getHeight() * heightRatio), withRatio);
                int bitmapByteCountNewImage = byteSizeOf(newImage);
                imagePath = saveBitmap(newImage);
            } else {
                imagePath = saveBitmap(rotatedBitmap);
            }
            TakePhotoFragment.TakePhotoFragmentEvent takePhotoFragmentEvent = new TakePhotoFragment.TakePhotoFragmentEvent();
            takePhotoFragmentEvent.setImagePath(imagePath);
            // send rxjava
            //pop backstack
            RxBus.getInstance().post(takePhotoFragmentEvent);
            getActivity().getSupportFragmentManager().popBackStack();
        }catch (Exception e){
            Log.d(TAG, "usePicture - exception     ");
            e.printStackTrace();
        }
    }else{
        Log.d(TAG, "usePicture - takenPictureBytes is null");
        DialogUtil.showErrorSnackBar(getView(), R.string.retake_photo );
    }
}

public Bitmap cutImage(final Bitmap bitmap, final int pixepWidth, final int pixelsHeight, float widthRatio) {
    int bitmapByteCountCutImage = byteSizeOf(bitmap);
    Bitmap output = createBitmap(pixepWidth, pixelsHeight, Bitmap.Config.ARGB_8888);
    Bitmap original = bitmap;

    final Paint paint = new Paint();
    Canvas canvas = new Canvas(output);
    int padding = (int) ((float) getResources().getDimensionPixelSize(R.dimen.double_padding) * widthRatio);

    Rect rect = new Rect(padding, (original.getHeight() - pixelsHeight) / 2, padding + pixepWidth, original.getHeight() - (original.getHeight() - pixelsHeight) / 2);

    final RectF cutedRect = new RectF(0, 0, pixepWidth, pixelsHeight);
    paint.setAntiAlias(true);
    canvas.drawARGB(0, 0, 0, 0);
    canvas.drawBitmap(original, rect, cutedRect, paint);
    return output;
}

   private String saveBitmap(Bitmap bitmap) {
    File pictureFileDir = getDir();

    if (!pictureFileDir.exists() && !pictureFileDir.mkdirs()) {
        Toast.makeText(getActivity(), "Can't create directory to save image.", Toast.LENGTH_LONG).show();
        return null;

    }

    SimpleDateFormat dateFormat = new SimpleDateFormat("yyyymmddhhmmssSSS");
    String date = dateFormat.format(new Date());
    String photoFile = "Picture_" + date + ".jpg";

    String filename = pictureFileDir.getPath() + File.separator + photoFile;

    File pictureFile = new File(filename);

    try {
        ByteArrayOutputStream stream = new ByteArrayOutputStream();
        bitmap.compress(Bitmap.CompressFormat.JPEG, 100, stream);

        FileOutputStream fos = new FileOutputStream(pictureFile);
        fos.write(stream.toByteArray());
        fos.close();

        return pictureFile.getAbsolutePath();

    } catch (Exception error) {
        Log.d(TAG, "File" + filename + "not saved: " + error.getMessage());
    }

    return null;
}

标签: androidbitmapandroid-cameraandroid-camera2image-reader

解决方案


您正在更改此代码中的位图大小/分辨率:

            float withRatio = (float) rotatedBitmap.getWidth() / (float) textureViewWidth;
        float heightRatio = (float) rotatedBitmap.getHeight() / (float) textureViewHeight;

        Bitmap newImage = cutImage(rotatedBitmap, (int) (photoFrameView.getWidth() * withRatio), (int) (photoFrameView.getHeight() * heightRatio), withRatio);
        int bitmapByteCountNewImage = byteSizeOf(newImage);
        imagePath = saveBitmap(newImage);

放一个断点,看看新的 heightRatio 和 widthRatio 是什么,以及 photoFrameView.getWidth() * withRatio 的值是什么。我想你会发现它与原始图像相比很小。我不确定您为什么要使用 textureViewWidth/Height 计算比率,您不必这样做。无论您在其中显示图像,都应该能够“填充”而无需更改底层位图的大小,从而失去分辨率。

你可以看看这个方法:

rawBitmap = ((BitmapDrawable)imageToLoad.getDrawable()).getBitmap();
theBitmap = Bitmap.createScaledBitmap(rawBitmap, 285, 313, false);

推荐阅读