相机预览很好,但前置相机产生的照片非常暗
Camera preview is fine but front camera produces very dark photos
我使用摄像头 1 API 构建了一个自定义摄像头,但由于某种原因,它产生了非常暗的照片(仅在前置摄像头上,后置摄像头完美运行美好的)。相机预览以正确的亮度显示相机应有的样子 - 只有当图像被捕获并解码为位图时,它才会看起来 真的 暗。
我疯狂地用谷歌搜索了一段时间,发现这个问题被报告了好几次,但找不到有效的解决方案。我使用的设备是三星 J5。
相机预览:
class CameraPreview extends SurfaceView implements SurfaceHolder.Callback {
private static final String CAMERA = "CAMERA";
private static Camera mCamera;
private final CameraActivity cameraActivity;
private final SurfaceHolder mHolder;
public CameraPreview(Camera camera, CameraActivity cameraActivity) {
super(cameraActivity);
this.cameraActivity = cameraActivity;
mCamera = camera;
mHolder = getHolder();
mHolder.addCallback(this);
mHolder.setType(SurfaceHolder.SURFACE_TYPE_PUSH_BUFFERS);
}
public void setCameraDisplayOrientation(int cameraId) {
Camera.CameraInfo info = new android.hardware.Camera.CameraInfo();
Camera.getCameraInfo(cameraId, info);
final int rotation = cameraActivity.getWindowManager().getDefaultDisplay().getRotation();
int degrees = 0;
switch (rotation) {
case Surface.ROTATION_0:
degrees = 0;
break;
case Surface.ROTATION_90:
degrees = 90;
break;
case Surface.ROTATION_180:
degrees = 180;
break;
case Surface.ROTATION_270:
degrees = 270;
break;
}
int result;
if (info.facing == cameraId) {
result = (info.orientation + degrees) % 360;
result = (360 - result) % 360;
} else {
result = (info.orientation - degrees + 360) % 360;
}
mCamera.setDisplayOrientation(result);
}
@Override
public void surfaceCreated(SurfaceHolder holder) {
try {
mCamera.setPreviewDisplay(holder);
mCamera.startPreview();
cameraActivity.isSafeToTakePicture(true);
Camera.Parameters params = mCamera.getParameters();
// my attempt at preventing darkness
params.setExposureCompensation(params.getMaxExposureCompensation());
if(params.isAutoExposureLockSupported()) {
params.setAutoExposureLock(false);
}
mCamera.setParameters(params);
} catch (IOException e) {
Log.d(CAMERA, "An error occured when setting up the camera preview " + e.getMessage());
}
}
@Override
public void surfaceDestroyed(SurfaceHolder holder) {
mCamera.stopPreview();
}
@Override
public void surfaceChanged(SurfaceHolder holder, int format, int w, int h) {
}
}
在我的 CameraPictureCallback
上(拍摄图像时),我将字节发送到此方法,该方法将字节解码为位图,将其放入一个包中并将其传递给下一个片段:
public void openFragmentWithBitmap(byte[] bytes) {
final BitmapFactory.Options scalingOptions = new BitmapFactory.Options();
final Bitmap bitmap = BitmapFactory.decodeByteArray(bytes, 0, bytes.length, scalingOptions);
final Bundle bundle = new Bundle();
bundle.putParcelable(SELFIE, bitmap);
mCamera.stopPreview();
final FragmentTransaction ft = getSupportFragmentManager().beginTransaction();
final Fragment startChainFragment = new StartChainFragment();
startChainFragment.setArguments(bundle);
ft.setCustomAnimations(R.anim.slide_up, R.anim.slide_down)
.replace(R.id.rlPlaceholder, startChainFragment, StartChainFragment.TAG)
.addToBackStack(null)
.commit();
}
我是不是漏掉了什么技巧?在我的 surfaceCreated() 中,我将曝光补偿设置为最大,但这似乎没有效果。感谢任何帮助。
编辑:
事实证明,添加延迟并没有什么不同,所以这是结果(相机预览与实际拍摄的图像):
相机预览:
拍摄的图像:
点击捕获按钮时调用mCamera.takePicture(null, null, pictureCallback)
捕获图像(回调只是将字节传输到上述方法)。
因为我使用自定义相机并面临许多与不同设备屏幕尺寸等相关的问题,这是唯一对我有用的东西所以我建议使用以下库,它可以帮助您解决问题。它也有很多选择。
经过所有的血汗和泪水,我找到了解决这个问题的方法。我注意到预览图片和最终图片看起来分辨率不同(您可以看到预览图片中的瓶子比捕获的图片宽)。所以我试图让它们相同或尽可能接近。我在 surfaceCreated()
中的 startPreview()
之后调用此方法:
private void loadCameraParameters() {
final Camera.Parameters camParams = mCamera.getParameters();
final Camera.Size previewSize = getOptimalPreviewSize(camParams.getSupportedPreviewSizes(), screenWidth, screenHeight);
camParams.setPreviewSize(previewSize.width, previewSize.height);
final Camera.Size pictureSize = getOptimalPreviewSize(camParams.getSupportedPictureSizes(), screenWidth, screenHeight);
camParams.setPictureSize(pictureSize.width, pictureSize.height);
mCamera.setParameters(camParams);
}
其中 getOptimalPreviewSize()
是:
private Camera.Size getOptimalPreviewSize(List<Size> sizes, int targetWidth, int targetHeight) {
final double aspectTolerance = 0.05;
final double targetRatio = (double) targetWidth/targetHeight;
if (sizes == null) {
return null;
}
Camera.Size optimalSize = null;
double minDiff = Double.MAX_VALUE;
for (Camera.Size size : sizes) {
final double ratio = (double) size.width / size.height;
if (Math.abs(ratio - targetRatio) > aspectTolerance) continue;
if (Math.abs(size.height - targetHeight) < minDiff) {
optimalSize = size;
minDiff = Math.abs(size.height - targetHeight);
}
}
if (optimalSize == null) {
minDiff = Double.MAX_VALUE;
for (Camera.Size size : sizes) {
if (Math.abs(size.height - targetHeight) < minDiff) {
optimalSize = size;
minDiff = Math.abs(size.height - targetHeight);
}
}
}
return optimalSize;
}
现在我的图片和预览在我的两台设备上都完美匹配,具有相同的亮度 和 分辨率。
我来晚了一点,但我遇到了同样的问题,它已通过非常小的更新得到修复
parameters.setPreviewSize(getWidth(),getHeight());
parameters.setPictureSize(getWidth(),getHeight());
parameters.setAutoWhiteBalanceLock(false); // the main life-saver lock ever :)
camera.setParameters(parameters);
width
和 hight
可以是任何值,但在您的代码中启用了自动白平衡锁定。
我使用摄像头 1 API 构建了一个自定义摄像头,但由于某种原因,它产生了非常暗的照片(仅在前置摄像头上,后置摄像头完美运行美好的)。相机预览以正确的亮度显示相机应有的样子 - 只有当图像被捕获并解码为位图时,它才会看起来 真的 暗。 我疯狂地用谷歌搜索了一段时间,发现这个问题被报告了好几次,但找不到有效的解决方案。我使用的设备是三星 J5。
相机预览:
class CameraPreview extends SurfaceView implements SurfaceHolder.Callback {
private static final String CAMERA = "CAMERA";
private static Camera mCamera;
private final CameraActivity cameraActivity;
private final SurfaceHolder mHolder;
public CameraPreview(Camera camera, CameraActivity cameraActivity) {
super(cameraActivity);
this.cameraActivity = cameraActivity;
mCamera = camera;
mHolder = getHolder();
mHolder.addCallback(this);
mHolder.setType(SurfaceHolder.SURFACE_TYPE_PUSH_BUFFERS);
}
public void setCameraDisplayOrientation(int cameraId) {
Camera.CameraInfo info = new android.hardware.Camera.CameraInfo();
Camera.getCameraInfo(cameraId, info);
final int rotation = cameraActivity.getWindowManager().getDefaultDisplay().getRotation();
int degrees = 0;
switch (rotation) {
case Surface.ROTATION_0:
degrees = 0;
break;
case Surface.ROTATION_90:
degrees = 90;
break;
case Surface.ROTATION_180:
degrees = 180;
break;
case Surface.ROTATION_270:
degrees = 270;
break;
}
int result;
if (info.facing == cameraId) {
result = (info.orientation + degrees) % 360;
result = (360 - result) % 360;
} else {
result = (info.orientation - degrees + 360) % 360;
}
mCamera.setDisplayOrientation(result);
}
@Override
public void surfaceCreated(SurfaceHolder holder) {
try {
mCamera.setPreviewDisplay(holder);
mCamera.startPreview();
cameraActivity.isSafeToTakePicture(true);
Camera.Parameters params = mCamera.getParameters();
// my attempt at preventing darkness
params.setExposureCompensation(params.getMaxExposureCompensation());
if(params.isAutoExposureLockSupported()) {
params.setAutoExposureLock(false);
}
mCamera.setParameters(params);
} catch (IOException e) {
Log.d(CAMERA, "An error occured when setting up the camera preview " + e.getMessage());
}
}
@Override
public void surfaceDestroyed(SurfaceHolder holder) {
mCamera.stopPreview();
}
@Override
public void surfaceChanged(SurfaceHolder holder, int format, int w, int h) {
}
}
在我的 CameraPictureCallback
上(拍摄图像时),我将字节发送到此方法,该方法将字节解码为位图,将其放入一个包中并将其传递给下一个片段:
public void openFragmentWithBitmap(byte[] bytes) {
final BitmapFactory.Options scalingOptions = new BitmapFactory.Options();
final Bitmap bitmap = BitmapFactory.decodeByteArray(bytes, 0, bytes.length, scalingOptions);
final Bundle bundle = new Bundle();
bundle.putParcelable(SELFIE, bitmap);
mCamera.stopPreview();
final FragmentTransaction ft = getSupportFragmentManager().beginTransaction();
final Fragment startChainFragment = new StartChainFragment();
startChainFragment.setArguments(bundle);
ft.setCustomAnimations(R.anim.slide_up, R.anim.slide_down)
.replace(R.id.rlPlaceholder, startChainFragment, StartChainFragment.TAG)
.addToBackStack(null)
.commit();
}
我是不是漏掉了什么技巧?在我的 surfaceCreated() 中,我将曝光补偿设置为最大,但这似乎没有效果。感谢任何帮助。
编辑: 事实证明,添加延迟并没有什么不同,所以这是结果(相机预览与实际拍摄的图像):
相机预览:
拍摄的图像:
点击捕获按钮时调用mCamera.takePicture(null, null, pictureCallback)
捕获图像(回调只是将字节传输到上述方法)。
因为我使用自定义相机并面临许多与不同设备屏幕尺寸等相关的问题,这是唯一对我有用的东西所以我建议使用以下库,它可以帮助您解决问题。它也有很多选择。
经过所有的血汗和泪水,我找到了解决这个问题的方法。我注意到预览图片和最终图片看起来分辨率不同(您可以看到预览图片中的瓶子比捕获的图片宽)。所以我试图让它们相同或尽可能接近。我在 surfaceCreated()
中的 startPreview()
之后调用此方法:
private void loadCameraParameters() {
final Camera.Parameters camParams = mCamera.getParameters();
final Camera.Size previewSize = getOptimalPreviewSize(camParams.getSupportedPreviewSizes(), screenWidth, screenHeight);
camParams.setPreviewSize(previewSize.width, previewSize.height);
final Camera.Size pictureSize = getOptimalPreviewSize(camParams.getSupportedPictureSizes(), screenWidth, screenHeight);
camParams.setPictureSize(pictureSize.width, pictureSize.height);
mCamera.setParameters(camParams);
}
其中 getOptimalPreviewSize()
是:
private Camera.Size getOptimalPreviewSize(List<Size> sizes, int targetWidth, int targetHeight) {
final double aspectTolerance = 0.05;
final double targetRatio = (double) targetWidth/targetHeight;
if (sizes == null) {
return null;
}
Camera.Size optimalSize = null;
double minDiff = Double.MAX_VALUE;
for (Camera.Size size : sizes) {
final double ratio = (double) size.width / size.height;
if (Math.abs(ratio - targetRatio) > aspectTolerance) continue;
if (Math.abs(size.height - targetHeight) < minDiff) {
optimalSize = size;
minDiff = Math.abs(size.height - targetHeight);
}
}
if (optimalSize == null) {
minDiff = Double.MAX_VALUE;
for (Camera.Size size : sizes) {
if (Math.abs(size.height - targetHeight) < minDiff) {
optimalSize = size;
minDiff = Math.abs(size.height - targetHeight);
}
}
}
return optimalSize;
}
现在我的图片和预览在我的两台设备上都完美匹配,具有相同的亮度 和 分辨率。
我来晚了一点,但我遇到了同样的问题,它已通过非常小的更新得到修复
parameters.setPreviewSize(getWidth(),getHeight());
parameters.setPictureSize(getWidth(),getHeight());
parameters.setAutoWhiteBalanceLock(false); // the main life-saver lock ever :)
camera.setParameters(parameters);
width
和 hight
可以是任何值,但在您的代码中启用了自动白平衡锁定。