簡體   English   中英

如何在拍攝照片時通過Java獲取Android手機攝像頭的曝光補償等級?

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

如何在拍攝照片時從Android手機獲取AutoExposureCompensation等級(亮度)?

我可以拍張照。 我可以訪問攝像機的參數,包括曝光補償(當我檢查時總是為零),但我需要在拍攝照片時獲得AE補償級別,而不是之前和之后。

背景:我希望在特定時間拍攝的所有照片都使用相同的AE補償等級拍攝照片。 我不希望Android相機通常會對曝光級別或白平衡進行數百次調整。 我想獲得一次,並為所有后續照片設置相同的設置。

我已經嘗試過使用“意圖”來拍照,OpenCV,碎片等。我似乎無法使用其中任何一種獲得AE補償設置。 這是我嘗試過的最新代碼,從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);
    }
}

以下是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;
}

我認為camera2 API( https://developer.android.com/reference/android/hardware/camera2/CaptureRequest.html )足以滿足您的需求。

資料來源: https//developer.android.com/reference/android/hardware/camera2/CaptureRequest.html#CONTROL_AE_LOCK

由於相機設備具有飛行中請求的管道,因此鎖定的設置不一定對應於從相機設備接收的最新捕獲結果中存在的設置,因為甚至在之前可能已經發生了額外的捕獲和AE更新。結果發出去了。 如果應用程序在自動和手動控制之間切換,並希望在切換期間消除任何閃爍,建議采用以下步驟:

  1. 從自動AE模式開始:
  2. 鎖定AE
  3. 等待輸出AE鎖定的第一個結果
  4. 將該結果的曝光設置復制到請求中,將請求設置為手動AE
  5. 提交捕獲請求,繼續根據需要運行手動AE。

另外根據AE模式的描述(相同來源)

當設置為任何ON模式時,攝像機設備自動曝光例程為給定捕獲的被覆蓋字段選擇的值將在其CaptureResult中可用。

因此,一旦您完成第一個CaptureRequest,您可以使用以下回調中的TotalCaptureResult

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

好吧,問題出現了如何在編寫代碼來控制Android攝像頭時具體設置曝光,傳感器靈敏度和其他變量。 這只適用於Lollipop或更高版本。 要發布很多代碼,但我會嘗試加入亮點

簡而言之,我使用帶有CameraManager的TextureView(AutoFitTextureView)。 當我打開相機時,我調用一個名為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;
}

好的,我會自己回答這個問題,因為誰發現了類似的問題。

Manish與他的回答很接近,但即使在onCaptureSession事件中使用,CONTROL_AE_EXPOSURE_COMPENSATION也只返回0(零),這是沒用的; 這只是相機的默認起始值​​。

但是,CameraCaptureSession.CaptureCallback(onCaptureSession事件)確實允許您從SENSOR_EXPOSURE_TIME和SENSOR_SENSITIVITY獲取值,以創建解決使用Android攝像頭的自動曝光問題。

以下是我使用的代碼片段:

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

            }
        };

這是我調試器的結果圖:

在此輸入圖像描述

暫無
暫無

聲明:本站的技術帖子網頁,遵循CC BY-SA 4.0協議,如果您需要轉載,請注明本站網址或者原文地址。任何問題請咨詢:yoyou2525@163.com.

 
粵ICP備18138465號  © 2020-2024 STACKOOM.COM