Camera2 контролируемый режим серийной съемки - PullRequest
6 голосов
/ 13 июня 2019

Итак, мы пытаемся создать приложение, которое будет делать фотографии при повороте устройства (примерно через каждые 3 угла). Однако, используя camera2, мы поворачиваем устройство:

  1. Каждый раз, когда снимается фото, предварительный просмотр отстает
  2. После нескольких изображений срабатывает onCaptureFailed с кодом 0

Я все еще пытаюсь выяснить Camera2 (кажется, многие думают, что это супер здорово, поэтому я пытаюсь разобраться с этим, но это довольно сложно). Я скачал пример RAW (https://github.com/googlesamples/android-Camera2Raw) и изменил его, чтобы он работал, как описано выше.

Ранее мы использовали устаревший API камеры, который работает на 100% на Samsung s8 - НО, не работает на старых устройствах, так как скорость съемки изображений была слишком быстрой для обработки. Тем не менее, я видел, что эти старые устройства действительно позволяют делать BURST фотографии - так что, конечно, камера 2 позволяет как-то получить доступ к этой функции?

Просто чтобы прояснить: я не хочу делать серийные снимки, так как это приведет к тому, что будут сделаны снимки, пока угол еще не изменился. Вот код, который мне нужно было изменить:

 // Gets called whenever the sensor gives me a value. calculates if it is time to take the next photo
 private void updateAngle(double angle) {
    mCurrentAngle = angle;
    double photoAngle = angle - mOffset < 0 ? CONST_360 + angle - mOffset : angle - mOffset;

    if (photoAngle < mPrevAngle) {
        tvZAxis.setText("You are going the wrong way");
        Log.w("BROKEN", mPrevAngle + "  " + photoAngle);
        return;
    }

    if (mStart) {
        double diff = photoAngle - mPrevAngle;
        if (diff > 300) {
            tvZAxis.setText("You have gone the wrong way. ");
            return;
        }

        if (diff > MAX_ALLOWED_ANGLE) {
            tvZAxis.setText("You are going too fast, go back a bit");
            return;
        } else if (diff > 1) {

            if (diff >= 3) {
                tvZAxis.setText("Do not go faster");
            }

            if (photoAngle > 356 && diff < 7) {
                photoAngle = CONST_360;
            }
            mPrevAngle = photoAngle;

            int prog = Math.max(0, (int) ((photoAngle / CONST_360) * 100.f));
            mProgress = prog;
            mProgressBar.setProgress(Math.max(0, prog));
            takePicture();
           // We USED to use old camer API:    mCamera.takePicture(null, null, mPicture);
        }
    }
}

И обратный вызов, я просто изменил имена файлов, чтобы можно было различать каждую фотографию под углом:

/**
 * A {@link CameraCaptureSession.CaptureCallback} that handles the still JPEG and RAW capture
 * request.
 */
private final CameraCaptureSession.CaptureCallback mCaptureCallback
        = new CameraCaptureSession.CaptureCallback() {
    @Override
    public void onCaptureStarted(CameraCaptureSession session, CaptureRequest request,
                                 long timestamp, long frameNumber) {
        mPhotoCount++;
        String currentDateTime = generateTimestamp();
        File rawFile = new File(Environment.
                getExternalStoragePublicDirectory(Environment.DIRECTORY_DCIM),
                "RAW_" + mPhotoCount + ".dng");
        File jpegFile = new File(Environment.
                getExternalStoragePublicDirectory(Environment.DIRECTORY_DCIM),
                "JPEG_" + mPhotoCount + ".jpg");

        // Look up the ImageSaverBuilder for this request and update it with the file name
        // based on the capture start time.
        ImageSaver.ImageSaverBuilder jpegBuilder;
        ImageSaver.ImageSaverBuilder rawBuilder;
        int requestId = (int) request.getTag();
        synchronized (mCameraStateLock) {
            jpegBuilder = mJpegResultQueue.get(requestId);
            rawBuilder = mRawResultQueue.get(requestId);
        }

        if (jpegBuilder != null) jpegBuilder.setFile(jpegFile);
        if (rawBuilder != null) rawBuilder.setFile(rawFile);
    }

И, для хорошей меры, вот метод takePicture ().

/**
 * Initiate a still image capture.
 * <p/>
 * This function sends a capture request that initiates a pre-capture sequence in our state
 * machine that waits for auto-focus to finish, ending in a "locked" state where the lens is no
 * longer moving, waits for auto-exposure to choose a good exposure value, and waits for
 * auto-white-balance to converge.
 */
private void takePicture() {
    synchronized (mCameraStateLock) {
        mPendingUserCaptures++;

        // If we already triggered a pre-capture sequence, or are in a state where we cannot
        // do this, return immediately.
        if (mState != STATE_PREVIEW) {
            return;
        }

        try {
            // Trigger an auto-focus run if camera is capable. If the camera is already focused,
            // this should do nothing.
            if (!mNoAFRun) {
                mPreviewRequestBuilder.set(CaptureRequest.CONTROL_AF_TRIGGER,
                        CameraMetadata.CONTROL_AF_TRIGGER_START);
            }

            // If this is not a legacy device, we can also trigger an auto-exposure metering
            // run.
            if (!isLegacyLocked()) {
                // Tell the camera to lock focus.
                mPreviewRequestBuilder.set(CaptureRequest.CONTROL_AE_PRECAPTURE_TRIGGER,
                        CameraMetadata.CONTROL_AE_PRECAPTURE_TRIGGER_START);
            }

            // Update state machine to wait for auto-focus, auto-exposure, and
            // auto-white-balance (aka. "3A") to converge.
            mState = STATE_WAITING_FOR_3A_CONVERGENCE;

            // Start a timer for the pre-capture sequence.
            startTimerLocked();

            // Replace the existing repeating request with one with updated 3A triggers.
            mCaptureSession.capture(mPreviewRequestBuilder.build(), mPreCaptureCallback,
                    mBackgroundHandler);
        } catch (CameraAccessException e) {
            e.printStackTrace();
        }
    }
}

Я чувствую, что упускаю что-то супер очевидное Обновлю пост (и, надеюсь, решение), как я прошел.

РЕДАКТИРОВАТЬ: Хорошо, с тех пор как задал этот вопрос, произошли некоторые важные изменения. В основном это работает сейчас - но, в зависимости от устройства, оно иногда размывается, когда устройство пытается сфокусироваться.

TakePicture был заменен следующим:

 private void captureStillPictureLocked() {
    try {

        if (null == mCameraDevice) {
            return;
        }
        // This is the CaptureRequest.Builder that we use to take a picture.
        final CaptureRequest.Builder captureBuilder =
                mCameraDevice.createCaptureRequest(CameraDevice.TEMPLATE_VIDEO_SNAPSHOT);

        captureBuilder.addTarget(mJpegImageReader.get().getSurface());

        // Use the same AE and AF modes as the preview.
        setup3AControlsLocked(captureBuilder);

        // Set orientation.
        int rotation = getWindowManager().getDefaultDisplay().getRotation();
        captureBuilder.set(CaptureRequest.JPEG_ORIENTATION,
                sensorToDeviceRotation(mCharacteristics, rotation));

        // Set request tag to easily track results in callbacks.
        captureBuilder.setTag(mRequestCounter.getAndIncrement());

        CaptureRequest request = captureBuilder.build();

        // Create an ImageSaverBuilder in which to collect results, and add it to the queue
        // of active requests.
        ImageSaver.ImageSaverBuilder jpegBuilder = new ImageSaver.ImageSaverBuilder(this)
                .setCharacteristics(mCharacteristics);

        mJpegResultQueue.put((int) request.getTag(), jpegBuilder);

        mCaptureSession.capture(request, mCaptureCallback, mBackgroundHandler);

    } catch (CameraAccessException e) {
        e.printStackTrace();
    }
}

и

 private final CameraCaptureSession.CaptureCallback mCaptureCallback
        = new CameraCaptureSession.CaptureCallback() {
    @Override
    public void onCaptureStarted(CameraCaptureSession session, CaptureRequest request,
                                 long timestamp, long frameNumber) {
        mPhotoCount++;
        File jpegFile = new File(Environment.
                getExternalStoragePublicDirectory(Environment.DIRECTORY_DCIM),
                "JPEG_" + mPhotoCount + ".jpg");

        vals.add(jpegFile.getAbsolutePath());

        // Look up the ImageSaverBuilder for this request and update it with the file name
        // based on the capture start time.
        ImageSaver.ImageSaverBuilder jpegBuilder;
        int requestId = (int) request.getTag();
        synchronized (mCameraStateLock) {
            jpegBuilder = mJpegResultQueue.get(requestId);
        }

        if (jpegBuilder != null) jpegBuilder.setFile(jpegFile);
    }

    @Override
    public void onCaptureCompleted(CameraCaptureSession session, CaptureRequest request,
                                   TotalCaptureResult result) {
        int requestId = (int) request.getTag();
        ImageSaver.ImageSaverBuilder jpegBuilder;
        StringBuilder sb = new StringBuilder();

        // Look up the ImageSaverBuilder for this request and update it with the CaptureResult
        synchronized (mCameraStateLock) {
            jpegBuilder = mJpegResultQueue.get(requestId);

            if (jpegBuilder != null) {
                jpegBuilder.setResult(result);
                sb.append("Saving JPEG as: ");
                sb.append(jpegBuilder.getSaveLocation());
            }

            // If we have all the results necessary, save the image to a file in the background.
            handleCompletionLocked(requestId, jpegBuilder, mJpegResultQueue);
        }

        if (mProgress >= 100) {
            Intent returnData = new Intent();
            returnData.putExtra("photoAmount", mPhotoCount);
            returnData.putExtra("paths", vals);
            setResult(RESULT_OK,returnData);
            finish();
        }
    }

    @Override
    public void onCaptureFailed(CameraCaptureSession session, CaptureRequest request,
                                CaptureFailure failure) {
        int requestId = (int) request.getTag();
        synchronized (mCameraStateLock) {
            mJpegResultQueue.remove(requestId);
        }
    }

};

Честно говоря, мне пришлось какое-то время отложить это на задний план. Обновлю вопрос, как только я вернусь к этому (потому что я забыл, что все делает :(), надеюсь, к тому времени я действительно выяснил это сам и могу просто опубликовать решение!

...