简体   繁体   English

如何在拍摄照片时通过Java获取Android手机摄像头的曝光补偿等级?

[英]How to get Exposure Compensation level from Android phone camera, via Java, when picture is taken?

How to get AutoExposureCompensation level (brightness) from Android phone when the picture is taken ? 如何在拍摄照片时从Android手机获取AutoExposureCompensation等级(亮度)?

I can take a picture. 我可以拍张照。 I can access the Parameters of the Camera, including the Exposure Compensation (always zero when I check), but I need to get the AE Compensation level at the moment the picture is taken, not before and not afterward. 我可以访问摄像机的参数,包括曝光补偿(当我检查时总是为零),但我需要在拍摄照片时获得AE补偿级别,而不是之前和之后。

Background: I want all pictures, taken at a certain time, to use the same AE Compensation level the pictures are taken. 背景:我希望在特定时间拍摄的所有照片都使用相同的AE补偿等级拍摄照片。 I don't want those hundreds of adjustments to the exposure level, or the white balance, that Android cameras typically do. 我不希望Android相机通常会对曝光级别或白平衡进行数百次调整。 I want to get once, and set for all the succeeding photos, the same settings. 我想获得一次,并为所有后续照片设置相同的设置。

I have tried using "intents" for pictures, OpenCV, fragments, etc. I can't seem to get the AE compensation setting with any of these. 我已经尝试过使用“意图”来拍照,OpenCV,碎片等。我似乎无法使用其中任何一种获得AE补偿设置。 Here's the latest code I've tried, starting with the an extended version of JavaCameraView: 这是我尝试过的最新代码,从JavaCameraView的扩展版本开始:

import org.opencv.android.JavaCameraView;
import android.content.Context;
import android.hardware.Camera;
import android.hardware.Camera.PictureCallback;
import android.hardware.Camera.Size;
import android.util.AttributeSet;
import android.util.Log;
@SuppressWarnings("deprecation")
public class NewJavaCameraView extends JavaCameraView implements PictureCallback {

public int getExposureCompensation(){
    return mCamera.getParameters().getExposureCompensation();
}
 @SuppressWarnings("deprecation")
 public void takePicture(final String fileName) {
    Log.i(TAG, "Taking picture");
    this.mPictureFileName = fileName;

    Camera.Parameters params = mCamera.getParameters();
    int exposureComp = params.getExposureCompensation();
    mCamera.setPreviewCallback(null);

    // PictureCallback is implemented by the current class
    int otherexposureComp =this.getExposureCompensation();
    mCamera.takePicture(null, null, this);
}

 @SuppressWarnings("deprecation")
 @Override
 public void onPictureTaken(byte[] data, Camera camera) {

    Camera.Parameters params = mCamera.getParameters();
    int exposureComp = params.getExposureCompensation();
    int otherexposureComp =this.getExposureCompensation();
    mCamera.startPreview();
    mCamera.setPreviewCallback(this);

    // Write the image in a file (in jpeg format)
    try {
        FileOutputStream fos = new FileOutputStream(mPictureFileName);

        fos.write(data);
        fos.close();

    } catch (java.io.IOException e) {
        Log.e("Picture", "photoCallback", e);
    }
}

Here's some of the code from the Android View that's using the abovementioned class: 以下是Android View中使用上述类的一些代码:

public class DiscPhoto extends Activity implements CvCameraViewListener2, OnTouchListener {
 private static final String TAG = "OCVSample::Activity";
 private NewJavaCameraView mOpenCvCameraView;
 private List<Size> mResolutionList;

 private BaseLoaderCallback mLoaderCallback = new BaseLoaderCallback(this) {
    @Override
    public void onManagerConnected(int status) {
        switch (status) {
            case LoaderCallbackInterface.SUCCESS:
            {
                Log.i(TAG, "OpenCV loaded successfully");
                mOpenCvCameraView.enableView();
                mOpenCvCameraView.setOnTouchListener(DiscPhoto.this);
            } break;
            default:
            {
                super.onManagerConnected(status);
            } break;
        }
    }
};

public DiscPhoto() {
    Log.i(TAG, "Instantiated new " + this.getClass());
}

/** Called when the activity is first created. */
@Override
public void onCreate(Bundle savedInstanceState) {
    Log.i(TAG, "called onCreate");
    super.onCreate(savedInstanceState);
    getWindow().addFlags(WindowManager.LayoutParams.FLAG_KEEP_SCREEN_ON);

    setContentView(R.layout.activity_disc_photo);

    mOpenCvCameraView = (NewJavaCameraView) findViewById(R.id.discPhotoPage);
    mOpenCvCameraView.setVisibility(SurfaceView.VISIBLE);
    mOpenCvCameraView.setCvCameraViewListener(this);
}

@SuppressLint("SimpleDateFormat")
@Override
public boolean onTouch(View v, MotionEvent event) {
    Log.i(TAG,"onTouch event");
    SimpleDateFormat sdf = new SimpleDateFormat("yyyy-MM-dd_HH-mm-ss");
    String currentDateandTime = sdf.format(new Date());
    String fileName = Environment.getExternalStorageDirectory().getPath() +
            "/sample_picture_" + currentDateandTime + ".jpg";
    mOpenCvCameraView.takePicture(fileName);
    Toast.makeText(this, fileName + " saved", Toast.LENGTH_SHORT).show();
    return false;
}

I think camera2 APIs ( https://developer.android.com/reference/android/hardware/camera2/CaptureRequest.html ) will suffice your need. 我认为camera2 API( https://developer.android.com/reference/android/hardware/camera2/CaptureRequest.html )足以满足您的需求。

Source: https://developer.android.com/reference/android/hardware/camera2/CaptureRequest.html#CONTROL_AE_LOCK 资料来源: https//developer.android.com/reference/android/hardware/camera2/CaptureRequest.html#CONTROL_AE_LOCK

Since the camera device has a pipeline of in-flight requests, the settings that get locked do not necessarily correspond to the settings that were present in the latest capture result received from the camera device, since additional captures and AE updates may have occurred even before the result was sent out. 由于相机设备具有飞行中请求的管道,因此锁定的设置不一定对应于从相机设备接收的最新捕获结果中存在的设置,因为甚至在之前可能已经发生了额外的捕获和AE更新。结果发出去了。 If an application is switching between automatic and manual control and wishes to eliminate any flicker during the switch, the following procedure is recommended: 如果应用程序在自动和手动控制之间切换,并希望在切换期间消除任何闪烁,建议采用以下步骤:

  1. Starting in auto-AE mode: 从自动AE模式开始:
  2. Lock AE 锁定AE
  3. Wait for the first result to be output that has the AE locked 等待输出AE锁定的第一个结果
  4. Copy exposure settings from that result into a request, set the request to manual AE 将该结果的曝光设置复制到请求中,将请求设置为手动AE
  5. Submit the capture request, proceed to run manual AE as desired. 提交捕获请求,继续根据需要运行手动AE。

Also as per the description of AE mode (same source) 另外根据AE模式的描述(相同来源)

When set to any of the ON modes, the values chosen by the camera device auto-exposure routine for the overridden fields for a given capture will be available in its CaptureResult. 当设置为任何ON模式时,摄像机设备自动曝光例程为给定捕获的被覆盖字段选择的值将在其CaptureResult中可用。

So once you make the first CaptureRequest, you can use the TotalCaptureResult from following callback: 因此,一旦您完成第一个CaptureRequest,您可以使用以下回调中的TotalCaptureResult

void onCaptureCompleted (CameraCaptureSession session, 
                CaptureRequest request, 
                TotalCaptureResult result)
{
       int aecompensationlevel = result.get(CaptureResult.CONTROL_AE_EXPOSURE_COMPENSATION)
}

OK, the question came up how to set in concrete the exposure, sensor sensitivity and other variables when writing code to control an Android camera. 好吧,问题出现了如何在编写代码来控制Android摄像头时具体设置曝光,传感器灵敏度和其他变量。 This will only work in Lollipop or later. 这只适用于Lollipop或更高版本。 There's to much code to post, but I'll try to put in the highlights 要发布很多代码,但我会尝试加入亮点

In short, I use a TextureView (AutoFitTextureView) with a CameraManager. 简而言之,我使用带有CameraManager的TextureView(AutoFitTextureView)。 When I open the camera, I call a void function called createPreviewSessions() 当我打开相机时,我调用一个名为createPreviewSessions()的void函数

  void openCamera() {
    try {
        mManager.openCamera(mCameraId, new CameraDevice.StateCallback() {
            @Override
            public void onOpened(CameraDevice camera) {
                createPreviewSession();
            }
        }

private void createPreviewSession() {
    try {
        SurfaceTexture texture = mTextureView.getSurfaceTexture();
        assert texture != null;
        texture.setDefaultBufferSize(mPreviewSize.getWidth(), mPreviewSize.getHeight());
        final List<Surface> surfaceList = new ArrayList<>();
        Surface surface = mImageReader.getSurface();
        surfaceList.add(surface);

        mCamera.createCaptureSession(surfaceList, new CameraCaptureSession.StateCallback() {
            @Override
            public void onConfigured(CameraCaptureSession session) {
                mSession = session;

                CaptureRequest request = createRequest(surfaceList, milliSecond, sensorSetting); //module variables
            } ...
       } ...
}
private CaptureRequest createRequest(List<Surface> surfaces, int milliSeconds, int sensorSetting) {
    Log.v("createRequest","here");
    try {
        CaptureRequest.Builder builder = mCamera.createCaptureRequest(CameraDevice.TEMPLATE_PREVIEW);
        builder.set(CaptureRequest.CONTROL_EFFECT_MODE, CameraMetadata.CONTROL_EFFECT_MODE_OFF);
        for (Surface surface : surfaces)
            builder.addTarget(surface);
        int exposureTime = milliSeconds * (milliSecondFactor); //billionth
        CaptureRequestSettings.SetRequestBuilder(builder,CONTROL_AWB_MODE_DAYLIGHT);

        builder.set(CaptureRequest.SENSOR_EXPOSURE_TIME, Long.valueOf(exposureTime));       //set hard values based on settings caught when photo taken
        builder.set(CaptureRequest.SENSOR_SENSITIVITY, Integer.valueOf(sensorSetting));     //same thing
        builder.set(CaptureRequest.CONTROL_AF_MODE, CaptureRequest.CONTROL_AF_MODE_CONTINUOUS_PICTURE);
        builder.set(CaptureRequest.CONTROL_AWB_MODE, CaptureRequest.CONTROL_AWB_MODE_OFF);  //CaptureRequest.CONTROL_AWB_MODE_OFF); //off here just like video mode
        builder.set(CaptureRequest.CONTROL_AE_MODE, CaptureRequest.CONTROL_AE_MODE_OFF);    //off ... don't want auto exposure

        return builder.build();
    } catch (CameraAccessException e) {
        Log.e("CaptureRequest", "CameraAccessException: " +e.getMessage());
    } catch (Exception e) {
        Log.e("CaptureRequest", "Regular Exception: " +e.getMessage());
    }
    Log.v("createRequest","shouldn't get here");
    return null;
}

OK, I'm going to answer this myself for whoever finds a similar problem. 好的,我会自己回答这个问题,因为谁发现了类似的问题。

Manish got close with his answer, but even when used in the onCaptureSession event, only 0 (zero) is returned by CONTROL_AE_EXPOSURE_COMPENSATION, which is of no use; Manish与他的回答很接近,但即使在onCaptureSession事件中使用,CONTROL_AE_EXPOSURE_COMPENSATION也只返回0(零),这是没用的; that's just the default starting value for the camera. 这只是相机的默认起始值​​。

However, the CameraCaptureSession.CaptureCallback (onCaptureSession event) does let you get the values from SENSOR_EXPOSURE_TIME and SENSOR_SENSITIVITY to create a work around to the auto exposure problem of working with Android cameras. 但是,CameraCaptureSession.CaptureCallback(onCaptureSession事件)确实允许您从SENSOR_EXPOSURE_TIME和SENSOR_SENSITIVITY获取值,以创建解决使用Android摄像头的自动曝光问题。

Below is a snippet of the code I used: 以下是我使用的代码片段:

private void captureStillPicture() {
    try {
          ...
        CameraCaptureSession.CaptureCallback CaptureCallback
                = new CameraCaptureSession.CaptureCallback() {

            @Override
            public void onCaptureCompleted(@NonNull CameraCaptureSession session,
                                           @NonNull CaptureRequest request,
                                           @NonNull TotalCaptureResult result) {

                long sensorTime= result.get(CaptureResult.SENSOR_EXPOSURE_TIME);
                long sensorSensitivity = result.get(CaptureResult.SENSOR_SENSITIVITY);

                int ONE_SECOND = 1000000000; //1 billion nanoseconds
                int MICRO_SECOND = 1000000;
                int MILLI_SECOND = 1000;
                String exposureText = "";
                if (sensorTime > ONE_SECOND) {
                    exposureText = String.format("%.2f s", sensorTime / 1e9);
                } else if (sensorTime > MILLI_SECOND) {
                    exposureText = String.format("%.2f ms", sensorTime / 1e6);
                } else if (sensorTime > MICRO_SECOND) {
                    exposureText = String.format("%.2f us", sensorTime / 1e3);
                } else {
                    exposureText = String.format("%d ns", sensorTime);
                }

                int aecompensationlevel=result.get(CaptureResult.CONTROL_AE_EXPOSURE_COMPENSATION); //only returns zero
                showToast("Saved: " + mFile +" | " +exposureText );
                Log.d(TAG, mFile.toString());

            }
        };

Here's a picture of the results from my debugger: 这是我调试器的结果图:

在此输入图像描述

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM