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;
}

person BRDroid    schedule 20.05.2019    source источник


Ответы (1)


Вы меняете размер/разрешение растрового изображения в этом коде:

            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);
person Michael Dougan    schedule 20.05.2019
comment
Привет, Майкл, у меня есть квадратное наложение, поэтому в методе вырезания изображения я вырезаю изображение до этого размера. Можно ли как-то вырезать изображение, но не менять разрешение? - person BRDroid; 21.05.2019
comment
Я знаю, что есть способ вырезать часть растрового изображения в новое растровое изображение, где вы должны указать координаты x, y верхнего левого угла, а затем ширину и высоту, и этот прямоугольник будет скопирован в новое растровое изображение без изменения разрешение вообще, но у меня нет под рукой примера кода. Я выполнил некоторое изменение размера в стиле «Подгонка», где, если у меня есть неквадратное изображение, я ищу наименьшую сторону, высоту или ширину, затем подгоняю его под квадрат, а затем обрезаю более длинное измерение. Это предотвратит сжатие или растягивание изображения. - person Michael Dougan; 21.05.2019