[英]Camera preview image data processing with Android L and Camera2 API
我正在開發一個Android應用程序,它正在處理來自攝像頭的輸入圖像並將其顯示給用戶。 這很簡單,我使用setPreviewCallbackWithBuffer
在相機對象上注冊了一個PreviewCallback
。 使用舊的相機API,這很容易並且運行順暢
public void onPreviewFrame(byte[] data, Camera cam) {
// custom image data processing
}
我正在嘗試移植我的應用程序以利用新的Camera2 API,我不確定我該怎么做。 我在L預覽示例中使用了Camera2Video,可以錄制視頻。 但是,樣本中沒有直接的圖像數據傳輸,因此我不明白應該在何處獲取圖像像素數據以及如何處理它。
有人可以幫我或建議如何在android L中獲得PreviewCallback
的功能,或者如何在將其顯示到屏幕之前處理相機的預覽數據? (相機對象上沒有預覽回調)
謝謝!
由於Camera2
API與當前的Camera
API非常不同,因此閱讀文檔可能會有所幫助。
一個很好的起點是camera2basic
示例。 它演示了如何使用Camera2
API並配置ImageReader
來獲取JPEG圖像並注冊ImageReader.OnImageAvailableListener
以接收這些圖像
要接收預覽幀,您需要將ImageReader
的表面添加到setRepeatingRequest
的CaptureRequest.Builder
。
此外,您應該將ImageReader
的格式設置為YUV_420_888
,這將在8MP時為您提供30fps(Nexus 5的文檔保證在8MP時為30fps)。
將一些答案組合成一個更容易消化的答案,因為@ VP的答案雖然技術上很清楚,但如果你是第一次從Camera轉到Camera2那么很難理解:
使用https://github.com/googlesamples/android-Camera2Basic作為起點,修改以下內容:
在createCameraPreviewSession()
從mImageReader
初始化一個新的Surface
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()
,在初始化ImageReader
時將格式從ImageFormat.JPEG
更改為ImageFormat.YUV_420_888
。 (PS,我還建議刪除預覽尺寸以使操作更順暢 - Camera2的一個不錯的功能)
mImageReader = ImageReader.newInstance(largest.getWidth() / 16, largest.getHeight() / 16, ImageFormat.YUV_420_888, 2);
最后,在ImageReader.OnImageAvailableListener
onImageAvailable()
方法中,請務必使用onImageAvailable()
的建議,因為如果不關閉預覽,預覽將在幾幀后停止
@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類中,讀取后關閉圖像,如下所示(這將釋放緩沖區以進行下一次捕獲)。 您必須在關閉時處理異常
Image image = imageReader.acquireNextImage();
ByteBuffer buffer = image.getPlanes()[0].getBuffer();
byte[] bytes = new byte[buffer.remaining()];
buffer.get(bytes);
image.close();
我需要相同的東西,所以我使用他們的例子,並在相機處於預覽狀態時添加了對新功能的調用。
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();
}
};
這是更好地初始化ImageReader
,最大圖像緩沖區2
然后用reader.acquireLatestImage()
內onImageAvailable()
因為acquireLatestImage()
將從ImageReader的隊列中獲取最新的Image,然后刪除舊的。 對於大多數用例,建議將此函數用於acquireNextImage()
,因為它更適合實時處理。 請注意,最大圖像緩沖區應至少為2
。
並記得在處理后close()
您的圖像。
聲明:本站的技術帖子網頁,遵循CC BY-SA 4.0協議,如果您需要轉載,請注明本站網址或者原文地址。任何問題請咨詢:yoyou2525@163.com.