私はAndroidカメラからの入力画像を処理してユーザーに表示するAndroid $ ===アプリで作業しています。これはかなり簡単です。カメラでPreviewCallback
を登録しますsetPreviewCallbackWithBuffer
を持つオブジェクト。これは簡単で、古いカメラAPIでスムーズに動作します
public void onPreviewFrame(byte[] data, Camera cam) {
// custom image data processing
}
新しいCamera2 APIを活用するためにアプリを移植しようとしていますが、それをどのように行うべきかわかりません。私は、ビデオを記録できるLプレビューサンプルのCamera2Videoに従いました。ただし、サンプルには画像データの直接転送がないため、画像ピクセルデータをどこで正確に取得し、どのように処理するかはわかりません。
誰かが私を助けたり、PreviewCallback
in Android Lの機能を取得する方法、または表示する前にカメラからのプレビューデータを処理する方法を教えてください画面?(カメラオブジェクトにはプレビューコールバックはありません)
ありがとうございました!
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()
で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()
で、ImageReader
を初期化するときに、形式を_ImageFormat.JPEG
_から_ImageFormat.YUV_420_888
_に変更します。 (PS、よりスムーズな操作のためにプレビューサイズをドロップすることもお勧めします-Camera2の優れた機能の1つ)
_mImageReader = ImageReader.newInstance(largest.getWidth() / 16, largest.getHeight() / 16, ImageFormat.YUV_420_888, 2);
_
最後に、_ImageReader.OnImageAvailableListener
_のonImageAvailable()
メソッドでは、@ Kamalaの提案を使用してください。プレビューを閉じると、数フレーム後にプレビューが停止するためです。
_ @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のキューから最新の画像を取得し、古い画像を削除するためです。この関数は、リアルタイム処理により適しているため、ほとんどのユースケースでacquireNextImage()
を使用することをお勧めします。最大画像バッファは少なくとも_2
_でなければなりません。
そして、処理後に画像をclose()
することを忘れないでください。