使用Android L和Camera2 API处理相机预览图像数据

我正在处理一个Android应用程序,它处理来自摄像机的input图像并将其显示给用户。 这很简单,我用setPreviewCallbackWithBuffer在摄像机对象上注册了一个PreviewCallback 。 这很容易,并与旧的相机API工作顺利

 public void onPreviewFrame(byte[] data, Camera cam) { // custom image data processing } 

我试图移植我的应用程序,以利用新的Camera2 API的优势,我不知道我该怎么做。 我遵循允许录制video的L预览示例中的Camera2Video。 然而,在样本中没有直接的图像数据传输,所以我不知道我到底在哪里得到图像像素数据以及如何处理它。

任何人都可以帮助我或build议如何才能获得Android L的PreviewCallback的function,或者如何才能在相机显示到屏幕之前处理预览数据? (相机对象上没有预览callback)

谢谢!

Solutions Collecting From Web of "使用Android L和Camera2 API处理相机预览图像数据"

由于Camera2 API与当前Camera API截然不同,因此可能会帮助您查看文档。

camera2basic示例是一个很好的起点。 它演示了如何使用Camera2 API并configurationImageReader来获取JPEG图像,并注册ImageReader.OnImageAvailableListener来接收这些图像

要接收预览帧,您需要将ImageReader的曲面添加到setRepeatingRequestCaptureRequest.Builder

此外,你应该设置ImageReader的格式为YUV_420_888 ,这将使您在800万像素30fps(该文件保证在800万像素Nexus 5 YUV_420_888秒)。

结合几个答案到一个更容易消化的答案,因为@ VP的答案,虽然技术上很清楚,但是很难理解,如果这是你第一次从相机到相机2:

使用https://github.com/googlesamples/android-Camera2Basic作为起点,修改以下内容:

createCameraPreviewSession()mImageReader初始化一个Surface

 Surface mImageSurface = mImageReader.getSurface(); 

添加新的表面作为CaptureRequest.Buildervariables的输出目标。 使用Camera2Basic示例,variables将是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); ... 

接下来,在初始化ImageReader时,在setUpCameraOutputs() ,将ImageFormat.JPEG的格式更改为ImageFormat.YUV_420_888 。 (PS,我也build议下降您的预览大小更stream畅的操作 – Camera2的一个不错的function)

 mImageReader = ImageReader.newInstance(largest.getWidth() / 16, largest.getHeight() / 16, ImageFormat.YUV_420_888, 2); 

最后,在ImageReader.OnImageAvailableListener onImageAvailable()方法中,请确保使用@ Kamala的build议,因为如果不closures它,预览将在几帧后停止

  @Override public void onImageAvailable(ImageReader reader) { Log.d(TAG, "I'm an image frame!"); Image image = reader.acquireNextImage(); ... if (image != null) image.close(); } 

在ImageReader.OnImageAvailableListener类中,读取后closures图像,如下所示(这将释放下一个捕获的缓冲区)。 你将不得不处理closuresexception

  Image image = imageReader.acquireNextImage(); ByteBuffer buffer = image.getPlanes()[0].getBuffer(); byte[] bytes = new byte[buffer.remaining()]; buffer.get(bytes); image.close(); 

我需要同样的东西,所以我使用他们的例子,并在相机处于预览状态时向新function添加了一个调用。

 private CameraCaptureSession.CaptureCallback mCaptureCallback = new CameraCaptureSession.CaptureCallback() private void process(CaptureResult result) { switch (mState) { case STATE_PREVIEW: { if (buttonPressed){ savePreviewShot(); } break; } 

savePreviewShot()只是简单的原始savePreviewShot()的循环版本,适用于使用预览模板。

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