Предварительный просмотр камеры в порядке, но передняя камера производит очень темные фотографии
Я создал собственную камеру, используя API-интерфейс Camera 1, и по какой-то причине он создает очень темные изображения (только на передней камере задняя камера работает отлично). Предварительный просмотр камеры показывает камеру как надо, с правильной яркостью - только когда изображение захвачено и декодировано в растровое изображение, оно выглядит очень темным. Я некоторое время лихорадочно гуглял и обнаружил, что об этой проблеме сообщалось довольно много раз, но не могу найти работающее решение. Я использую устройство Samsung J5.
CameraPreview:
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() я установил максимальную компенсацию экспозиции, но, похоже, это не дает никакого эффекта. Ценю любую помощь.
Изменить: Оказывается, добавление задержки не имеет значения, поэтому вот результаты (предварительный просмотр камеры против фактического снимка):
CameraPreview:
Изображение снято по телефону mCamera.takePicture(null, null, pictureCallback)
когда нажата кнопка захвата (обратный вызов просто передает байты вышеописанному методу).
2 ответа
После всей крови, пота и слез я нашел решение этой проблемы. Я заметил, что предварительный просмотр и окончательное изображение не выглядят так, как будто они имеют одинаковое разрешение (вы можете видеть, что бутылка на изображении предварительного просмотра шире, чем на снятом изображении). Поэтому я попытался сделать их одинаковыми или максимально близкими. Я называю этот метод после startPreview()
в surfaceCreated()
:
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
может принимать любое значение, но в вашем коде была включена автоматическая блокировка баланса белого.