Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Camera preview is fine but front camera produces very dark photos

I built a custom camera using the Camera 1 API and for some reason it produces very dark pictures (only on the front camera, the back camera works perfectly fine). The camera preview shows the camera as it should, with the correct brightness - it's only when an image is captured and decoded into a bitmap does it look really dark. I have been frantically googling for a while and have found this problem reported quite a few times but can't find a working solution. The device I'm using is a 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) {
  }
}

On my CameraPictureCallback (when an image is taken), I send the bytes to this method, which decodes the bytes into a bitmap, puts it in a bundle and passes it to the next fragment:

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

Am I missing a trick here? In my surfaceCreated() I set the exposure compensation to the max but this doesn't seem to have an effect. Appreciate any help.

Edit: Turns out adding a delay didn't make a difference, so here are the results (camera preview vs actual image taken):

CameraPreview: enter image description here

Captured image: enter image description here

The image is captured by calling mCamera.takePicture(null, null, pictureCallback) when the capture button is clicked (the callback just transfers the bytes to the above method).

like image 321
eyes enberg Avatar asked Dec 02 '18 21:12

eyes enberg


People also ask

Why is my front camera so dark?

Dark images happen when the shutter speed is too fast or the aperture isn't open enough. Be careful of your camera's automatic settings. Most cameras tend not to pick the right ones by default. If your camera creates an image that is too dark, use EV to bump up the brightness.

Why are my pictures coming out so dark?

If you find your photos to be too dark or too light it's because of incorrect exposure. Exposure is the amount of light that gets into your camera and produces the picture on the image sensor. If a photo is too dark it means it has been underexposed and if it is too light it has been overexposed.

Why does my camera make me look darker?

Camera metering is based on an 18% grey standard. Very light-skinned people tend to have overexposed faces. Darker-skin people have underexposed faces. A simple trick is to slightly underexpose light skin and slightly overexpose darker skin.

Why is my phone camera dark?

This means that the camera application has malfunctioned or the lens is broken. If this is the case, take your smartphone for repair.


1 Answers

After all the blood, sweat and tears I found a solution to this. I noticed that the preview and final picture didn't look like they were the same resolution (you can see the bottle is wider in the preview picture than the captured picture). So I tried to make them the same or as close as possible. I call this method after startPreview() in 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);
}

where getOptimalPreviewSize() is:

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

Now my picture and preview are a perfect match on both of my devices, with the same brightness and resolution.

like image 159
eyes enberg Avatar answered Sep 28 '22 09:09

eyes enberg