Camera 2 API 会降低拍摄后的质量
Posted
技术标签:
【中文标题】Camera 2 API 会降低拍摄后的质量【英文标题】:Camera 2 API reduces the quality after capture 【发布时间】:2019-10-06 23:33:54 【问题描述】:我在我的应用程序中使用相机拍摄身份证照片,我有一个矩形叠加层,图像将被裁剪。问题是一旦捕获图像,图像质量就会降低。
我无法弄清楚它到底发生在哪里。在 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;
【问题讨论】:
按照帖子中的建议,不要使用位图使用 Uri。 ***.com/questions/34609275/… 好的,关于如何修改我的代码有什么建议吗?哪些方法需要改变? 【参考方案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);
【讨论】:
我知道有一种方法可以将位图的一部分剪辑到一个新位图中,您可以在其中指定左上角的 x、y 坐标,然后指定宽度和高度,该矩形将是复制到新的位图,根本不改变分辨率,但我手边没有任何代码示例。我做了一些“适合”风格的尺寸调整,如果我有一个非正方形的图像,我会寻找最小的边、高度或宽度,然后让它适合正方形,然后截断较长的尺寸。这可以防止图像被挤压或拉伸以适应。以上是关于Camera 2 API 会降低拍摄后的质量的主要内容,如果未能解决你的问题,请参考以下文章
如何在 Android 中提高 Camera 2 的图像质量?