Обработка данных предварительного просмотра камеры с помощью Android L и Camera2 API
Я работаю над Android-приложением, которое обрабатывает входное изображение с камеры и отображает его пользователю. Это довольно просто, я регистрирую PreviewCallback
объекта камеры с помощью setPreviewCallbackWithBuffer
.
Это легко и работает плавно со старым API-интерфейсом камеры
public void onPreviewFrame(byte[] data, Camera cam) {
// custom image data processing
}
Я пытаюсь выполнить перенос моего приложения, чтобы воспользоваться новым API Camera2, и я не уверен, как именно я должен это сделать. Я последовал за образцом Camera2Video в L Preview, который позволяет записывать видео. Однако в образце нет прямой передачи данных изображения, поэтому я не понимаю, где именно я должен получить данные пикселя изображения и как его обрабатывать.
Может кто-нибудь помочь мне или предложить способ, как можно получить функциональность PreviewCallback
в android L, или как можно обрабатывать данные предварительного просмотра с камеры, прежде чем отображать ее на экране? (нет обратного вызова предварительного просмотра объекта камеры)
Спасибо!
Ответы
Ответ 1
Так как API Camera2
сильно отличается от текущего API Camera
, он может помочь просмотреть документацию.
Хорошей отправной точкой является пример camera2basic
. Он демонстрирует, как использовать API Camera2
и настроить ImageReader
для получения изображений JPEG и регистрации ImageReader.OnImageAvailableListener
для получения этих изображений
Чтобы получить кадры предварительного просмотра, вам нужно добавить свою поверхность ImageReader
в setRepeatingRequest
CaptureRequest.Builder
.
Кроме того, вы должны установить формат ImageReader
на YUV_420_888
, что даст вам 30 кадров в секунду при 8MP (документация гарантирует 30 кадров в секунду при 8MP для Nexus 5).
Ответ 2
Объединяя несколько ответов в более удобоваримый, потому что ответ @VP, в то время как технически ясный, трудно понять, если вы в первый раз переходите от камеры к Camera2:
Используя https://github.com/googlesamples/android-Camera2Basic в качестве отправной точки, измените следующее:
В createCameraPreviewSession()
запустите новый Surface
из mImageReader
Surface mImageSurface = mImageReader.getSurface();
Добавьте эту новую поверхность в качестве выходной цели вашей переменной CaptureRequest.Builder
. Используя образец Camera2Basic, переменная будет mPreviewRequestBuilder
mPreviewRequestBuilder.addTarget(mImageSurface);
Вот фрагмент с новыми строками (см. мои комментарии @AngeloS):
private void createCameraPreviewSession() {
try {
SurfaceTexture texture = mTextureView.getSurfaceTexture();
assert texture != null;
// We configure the size of default buffer to be the size of camera preview we want.
texture.setDefaultBufferSize(mPreviewSize.getWidth(), mPreviewSize.getHeight());
// This is the output Surface we need to start preview.
Surface surface = new Surface(texture);
//@AngeloS - Our new output surface for preview frame data
Surface mImageSurface = mImageReader.getSurface();
// We set up a CaptureRequest.Builder with the output Surface.
mPreviewRequestBuilder = mCameraDevice.createCaptureRequest(CameraDevice.TEMPLATE_PREVIEW);
//@AngeloS - Add the new target to our CaptureRequest.Builder
mPreviewRequestBuilder.addTarget(mImageSurface);
mPreviewRequestBuilder.addTarget(surface);
...
Затем, в setUpCameraOutputs()
, измените формат с ImageFormat.JPEG
на ImageFormat.YUV_420_888
, когда вы запустите свой ImageReader
. (PS, я также рекомендую отказаться от размера предварительного просмотра для более плавной работы - одна приятная функция Camera2)
mImageReader = ImageReader.newInstance(largest.getWidth() / 16, largest.getHeight() / 16, ImageFormat.YUV_420_888, 2);
Наконец, в вашем onImageAvailable()
методе ImageReader.OnImageAvailableListener
обязательно используйте предложение @Kamala, потому что превью остановится после нескольких кадров, если вы не закрываете его
@Override
public void onImageAvailable(ImageReader reader) {
Log.d(TAG, "I'm an image frame!");
Image image = reader.acquireNextImage();
...
if (image != null)
image.close();
}
Ответ 3
В классе ImageReader.OnImageAvailableListener закройте изображение после прочтения, как показано ниже (это освободит буфер для следующего захвата). Вам придется обрабатывать исключение при закрытии
Image image = imageReader.acquireNextImage();
ByteBuffer buffer = image.getPlanes()[0].getBuffer();
byte[] bytes = new byte[buffer.remaining()];
buffer.get(bytes);
image.close();
Ответ 4
Мне нужно было то же самое, поэтому я использовал их пример и добавил вызов новой функции, когда камера находится в состоянии предварительного просмотра.
private CameraCaptureSession.CaptureCallback mCaptureCallback
= new CameraCaptureSession.CaptureCallback()
private void process(CaptureResult result) {
switch (mState) {
case STATE_PREVIEW: {
if (buttonPressed){
savePreviewShot();
}
break;
}
savePreviewShot()
- это просто переработанная версия оригинального captureStillPicture()
, адаптированная для использования шаблона предварительного просмотра.
private void savePreviewShot(){
try {
final Activity activity = getActivity();
if (null == activity || null == mCameraDevice) {
return;
}
// This is the CaptureRequest.Builder that we use to take a picture.
final CaptureRequest.Builder captureBuilder =
mCameraDevice.createCaptureRequest(CameraDevice.TEMPLATE_PREVIEW);
captureBuilder.addTarget(mImageReader.getSurface());
// Orientation
int rotation = activity.getWindowManager().getDefaultDisplay().getRotation();
captureBuilder.set(CaptureRequest.JPEG_ORIENTATION, ORIENTATIONS.get(rotation));
CameraCaptureSession.CaptureCallback CaptureCallback
= new CameraCaptureSession.CaptureCallback() {
@Override
public void onCaptureCompleted(CameraCaptureSession session, CaptureRequest request,
TotalCaptureResult result) {
SimpleDateFormat sdf = new SimpleDateFormat("yyyy-MM-dd_HH:mm:ss:SSS");
Date resultdate = new Date(System.currentTimeMillis());
String mFileName = sdf.format(resultdate);
mFile = new File(getActivity().getExternalFilesDir(null), "pic "+mFileName+" preview.jpg");
Log.i("Saved file", ""+mFile.toString());
unlockFocus();
}
};
mCaptureSession.stopRepeating();
mCaptureSession.capture(captureBuilder.build(), CaptureCallback, null);
} catch (Exception e) {
e.printStackTrace();
}
};