使用 Camera2 API 的照片真的很暗

Pictures with Camera2 API are really dark

我正在处理 Android,我正在尝试在不显示任何预览的情况下拍摄照片。我试图通过制作 class 来简化流程。它正在工作,但所有图片都非常暗。 这是我的 class :

public class Cam {
private Context context;
private CameraManager manager;
private CameraDevice camera;
private CameraCaptureSession session;
private ImageReader reader;
public static String FRONT="-1";
public static String BACK="-1";
private boolean available=true;
private String filepath;

private static final String NO_CAM = "No camera found on device!";
private static final String ERR_CONFIGURE = "Failed configuring session";
private static final String ERR_OPEN = "Can't open the camera";
private static final String CAM_DISCONNECT = "Camera disconnected";
private static final String FILE_EXIST = "File already exist";

private static final SparseIntArray ORIENTATIONS = new SparseIntArray();
static {
    ORIENTATIONS.append(Surface.ROTATION_0, 90);
    ORIENTATIONS.append(Surface.ROTATION_90, 0);
    ORIENTATIONS.append(Surface.ROTATION_180, 270);
    ORIENTATIONS.append(Surface.ROTATION_270, 180);
}

public Cam(Context context) throws CameraAccessException {
    this.context = context;
    this.manager = (CameraManager) context.getSystemService(Context.CAMERA_SERVICE);
    String ids[] = manager.getCameraIdList();
    if(ids.length==2){
        BACK=ids[0];
        FRONT=ids[1];
    }
    else if(ids.length==1){
        BACK=ids[0];
    }
    else{
        available=false;
        throw new CameraAccessException(-1, NO_CAM);
    }
}

public void takePicture(String camId, String filepath) throws CameraAccessException {
    if(available){
        this.filepath=filepath;
        StreamConfigurationMap map = manager.getCameraCharacteristics(camId).get(CameraCharacteristics.SCALER_STREAM_CONFIGURATION_MAP);
        Size largest = Collections.max(Arrays.asList(map.getOutputSizes(ImageFormat.JPEG)), new CompareSizesByArea());
        reader=ImageReader.newInstance(largest.getWidth(), largest.getHeight(), ImageFormat.JPEG, 1);
        reader.setOnImageAvailableListener(imageListener, null);
        manager.openCamera(camId, cameraStateCallback, null);
    }
    else
        throwError(NO_CAM);
}

private CameraDevice.StateCallback cameraStateCallback = new CameraDevice.StateCallback() {
    @Override
    public void onOpened(CameraDevice camera) {
        Cam.this.camera=camera;
        try {
            camera.createCaptureSession(Collections.singletonList(reader.getSurface()), sessionStateCallback, null);
        } catch (CameraAccessException e) {
            throwError(e.getMessage());
        }
    }

    @Override
    public void onDisconnected(CameraDevice camera) {
        throwError(CAM_DISCONNECT);
    }

    @Override
    public void onError(CameraDevice camera, int error) {
        throwError(ERR_OPEN);
    }
};

private CameraCaptureSession.StateCallback sessionStateCallback = new CameraCaptureSession.StateCallback() {
    @Override
    public void onConfigured(CameraCaptureSession session) {
        Cam.this.session=session;
        try {
            CaptureRequest.Builder request = camera.createCaptureRequest(CameraDevice.TEMPLATE_STILL_CAPTURE);
            request.set(CaptureRequest.CONTROL_AF_MODE, CaptureRequest.CONTROL_AF_MODE_CONTINUOUS_PICTURE);
            request.addTarget(reader.getSurface());
            int rotation = ((WindowManager)context.getSystemService(Context.WINDOW_SERVICE)).getDefaultDisplay().getRotation();
            request.set(CaptureRequest.JPEG_ORIENTATION, ORIENTATIONS.get(rotation));
            session.capture(request.build(), captureCallback, null);
        } catch (CameraAccessException e) {
            throwError(e.getMessage());
        }
    }

    @Override
    public void onConfigureFailed(CameraCaptureSession session) {
        throwError(ERR_CONFIGURE);
    }
};

private CameraCaptureSession.CaptureCallback captureCallback = new CameraCaptureSession.CaptureCallback() {
    @Override
    public void onCaptureFailed(CameraCaptureSession session, CaptureRequest request, CaptureFailure failure) {
        super.onCaptureFailed(session, request, failure);
        throwError(failure.toString());
    }
};

private ImageReader.OnImageAvailableListener imageListener = new ImageReader.OnImageAvailableListener() {
    @Override
    public void onImageAvailable(ImageReader reader) {
        Image image = reader.acquireLatestImage();
        try {
            File file = saveImage(image);
            // Send file via a listener
            closeCamera();
        } catch (IOException e) {
            throwError(e.getMessage());
        }
        reader.close();
    }
};

private File saveImage(Image image) throws IOException {
    File file = new File(filepath);
    if (file.exists()) {
        throwError(FILE_EXIST);
        return null;
    }
    else {
        ByteBuffer buffer = image.getPlanes()[0].getBuffer();
        byte[] bytes = new byte[buffer.remaining()];
        buffer.get(bytes);
        FileOutputStream output = new FileOutputStream(file);
        output.write(bytes);
        image.close();
        output.close();
        return file;
    }
}

static class CompareSizesByArea implements Comparator<Size> {
    @Override
    public int compare(Size lhs, Size rhs) {
        return Long.signum((long) lhs.getWidth() * lhs.getHeight() - (long) rhs.getWidth() * rhs.getHeight());
    }
}

private void closeCamera(){
    if(session!=null) {session.close();}
    if(reader!=null) {reader.close();}
    if(camera!=null) {camera.close();}
}

然后我在 Activity 中调用 Cam 对象:

Cam cam = new Cam(MainActivity.this);
cam.takePicture(Cam.BACK, "/sdcard/pic.jpg");

当图片可用时,侦听器会阻止 MainActivity,但我删除了代码以清除一点。

我不知道我做错了什么,图片真的很暗。也许是一面旗帜或其他东西......任何帮助将不胜感激。

编辑: 工作 class:https://github.com/omaflak/Android-Camera2-Library/blob/master/ezcam/src/main/java/me/aflak/ezcam/EZCam.java

示例:https://github.com/omaflak/Android-Camera2-Library/blob/master/app/src/main/java/me/aflak/libraries/MainActivity.java

或许你可以尝试开启自动曝光模式和自动白平衡:

request.set(CaptureRequest.CONTROL_AE_MODE, CaptureRequest.CONTROL_AE_MODE_ON);
request.set(CaptureRequest.CONTROL_AWB_MODE, CaptureRequest.CONTROL_AWB_MODE_AUTO);

希望对您有所帮助:)

如果您发送给相机的唯一捕获请求是最终图片请求,这不足为奇。

相机自动曝光、对焦和白平衡例程通常需要一两秒的流缓冲才能收敛到良好的效果。

虽然您不需要在屏幕上绘制预览,但这里最简单的方法是首先 运行 重复请求一两秒钟,然后触发 JPEG 捕获。 您可以只流式传输 JPEG 捕获,但 JPEG 捕获速度很慢,因此您需要更长的时间来收敛(此外,与典型的预览相比,相机实现更可能存在重复 JPEG 捕获和获得良好曝光的错误) .

因此,创建一个带有随机纹理 ID 参数的虚拟 SurfaceTexture:

private SurfaceTexture mDummyPreview = new SurfaceTexture(1);
private Surface mDummySurface = new Surface(mDummyPreview);

并在您的会话配置中包含 Surface。配置会话后,创建一个以虚拟预览为目标的预览请求,并在 N 个捕获结果进来后,提交您想要的 JPEG 的捕获请求。您可能想尝试使用 N,但大概 ~30 帧就足够了。

请注意,您还没有处理:

  • 锁定自动对焦以确保您的 JPEG 图像清晰
  • 运行 AE预捕捉允许闪光测光,如果你想允许闪光使用
  • 通过某种方式让用户知道他们将捕获什么,因为没有预览,他们不能很好地将设备对准任何东西。

AF 锁定和预捕获触发序列包含在此处的 Camera2Basic 示例中:https://github.com/googlesamples/android-Camera2Basic,因此您可以看看它们的作用。

就我而言,只是配置 FPS 对我有帮助。并且不要忘记将其放入 CaptureRequest.Builder 进行预览,也不要忘记将其放入 CaptureRequest.Builder 捕获生成器。照常 FPS 10 或 15 帧足以用于照片和预览。

捕获生成器

// This is the CaptureRequest.Builder that we use to take a picture.
final CaptureRequest.Builder captureBuilder = mCameraDevice.createCaptureRequest(CameraDevice.TEMPLATE_STILL_CAPTURE);
...
setupFPS(captureBuilder);

预览生成器:

// We set up a CaptureRequest.Builder with the output Surface.
mPreviewRequestBuilder = mCameraDevice.createCaptureRequest(CameraDevice.TEMPLATE_PREVIEW);
mPreviewRequestBuilder.addTarget(surface);
                ...
// set FPS rate
setupFPS(mPreviewRequestBuilder);

哪里设置FPS:

private void setupFPS(CaptureRequest.Builder builder){
        if(fpsRange != null) {
            builder.set(CaptureRequest.CONTROL_AE_TARGET_FPS_RANGE, fpsRange);
        }
}

FPS 初始化:

CameraCharacteristics characteristics = manager.getCameraCharacteristics(cameraId);
try {
                    Range<Integer>[] ranges = characteristics.get(CameraCharacteristics.CONTROL_AE_AVAILABLE_TARGET_FPS_RANGES);
                    if(ranges != null) {
                        for (Range<Integer> range : ranges) {
                            int upper = range.getUpper();
                            Log.i(TAG, "[FPS Range Available]:" + range);
                            if (upper >= 10) {
                                if (fpsRange == null || upper < fpsRange.getUpper()) {
                                    fpsRange = range;
                                }
                            }
                        }
                    }
                } catch (Exception e) {
                    e.printStackTrace();
                }
                Log.i(TAG, "[FPS Range] is:" + fpsRange);

我知道这是旧的,但我 运行 陷入了类似的问题。与其他偶然发现此问题的人分享对我有用的东西。在这里尝试了各种答案都没有成功。

将 CONTROL_AE_MODE 设置为 CONTROL_AE_MODE_ON(如某些建议的那样)也没有解决它(您认为它会)。

为我解决的问题是将 CONTROL_CAPTURE_INTENT 设置为 CONTROL_CAPTURE_INTENT_VIDEO_RECORD。

request.set(CaptureRequest.CONTROL_CAPTURE_INTENT, CaptureRequest.CONTROL_CAPTURE_INTENT_VIDEO_RECORD);

添加这条线并构建后,相机上的自动曝光按预期启用,相机自动调整。

有关详细信息,请参阅 https://developer.android.com/reference/android/hardware/camera2/CameraMetadata。我将其用作发现可用选项的指南。

过去两天我一直面临图像暗淡的问题,现在我找到了解决方案。 您需要像下面这样设置 CaptureRequest
我试图在 camera2 API.

中设置亮度
final CaptureRequest.Builder captureBuilder = mCameraDevice.createCaptureRequest(CameraDevice.TEMPLATE_PREVIEW);
            
captureBuilder.set(CaptureRequest.CONTROL_AE_MODE, CaptureRequest.CONTROL_AE_MODE_ON);
            
captureBuilder.set(CaptureRequest.CONTROL_AE_LOCK, true);
            
captureBuilder.set(CaptureRequest.CONTROL_AE_EXPOSURE_COMPENSATION, 10);


captureBuilder.addTarget(imageReader.getSurface());