Raspberry Pi3 のカメラモジュールをAndroid Things から使用してみます。
■公式
https://developer.android.com/things/training/doorbell/camera-input.html
ほぼ公式の通りですぐに試せます。
■Manifestにパーミッションを追加
普通のAndroidアプリと同様にマニフェストにカメラ許可を追加。
1 |
<uses-permission android:name="android.permission.CAMERA" /> |
1 2 |
<uses-feature android:name="android.hardware.camera" /> <uses-feature android:name="android.hardware.camera2" /> |
■カメラを使う側の処理
今回はカメラ画像をキャプチャしてImageViewに貼り付けるだけにします。
モニターがないと確認できないので接続しておいてください。
カメラ用の作業スレッドを作成
メインスレッドをブロックさせないために作業スレッドを作成する。
androidでのthreadは他にもありますが、ここでは公式のオススメ通りにHandlerThreadを使用する。
1 2 3 4 5 6 7 8 |
mCameraThread = new HandlerThread("CameraBackground"); mCameraThread.start(); mCameraHandler = new Handler(mCameraThread.getLooper()); mCamera = new ATCamera(); if(mCamera != null){ mCamera.initializeCamera(this, mCameraHandler, mOnImageAvailableListener); Log.d("test","camera initialized"); } |
ATCameraというクラスを作成してカメラを制御するようにし、Activityから使用します。
ATCameraクラスの作成は後回しにして、できているつもりで使用する側を先に記載します。
キャプチャ実行
プッシュボタン等のトリガーによりキャプチャを実行する。UARTの設定ができていれば、とりあえずUARTからのコマンド受信をトリガーにするのも楽かも。
1 |
mCamera.takePicture(); |
キャプチャできたデータを受け取って表示する
1 2 3 4 5 6 7 8 9 10 11 12 13 14 |
private ImageReader.OnImageAvailableListener mOnImageAvailableListener = new ImageReader.OnImageAvailableListener() { @Override public void onImageAvailable(ImageReader reader) { // Get the raw image bytes Image image = reader.acquireLatestImage(); ByteBuffer imageBuf = image.getPlanes()[0].getBuffer(); final byte[] imageBytes = new byte[imageBuf.remaining()]; imageBuf.get(imageBytes); image.close(); onPictureTaken(imageBytes); } }; |
initializeCameraに指定するリスナーのonImageAvailable()で画像データが取得できるので、Bitmapに変換してImageViewに貼り付ける。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 |
private void onPictureTaken(final byte[] imageBytes) { Log.d("test","onPictureTaken()"+String.valueOf(imageBytes.length)+" bytes"); if (imageBytes != null) { final Bitmap bmp = BitmapFactory.decodeByteArray(imageBytes, 0, imageBytes.length); if (bmp!=null){ Log.d("test","bmp!=null"); if(imageView==null){ Log.d("test","imageView==null"); } else { new Handler(Looper.getMainLooper()).post(new Runnable() { @Override public void run() { imageView.setImageBitmap(Bitmap.createScaledBitmap(bmp, 640, 480, false)); } }); } } } } |
■カメラ制御用クラスの中身
Camera用クラスを作成
1 2 3 4 5 6 7 8 9 10 11 |
public class ATCamera { private static final int IMAGE_WIDTH = 640; private static final int IMAGE_HEIGHT = 480; private static final int MAX_IMAGES = 1; private ImageReader mImageReader; private CameraDevice mCameraDevice; private CameraCaptureSession mCaptureSession; ・・・ |
カメラ初期化処理
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 |
public void initializeCamera(Context context, Handler backgroundHandler, ImageReader.OnImageAvailableListener imageAvailableListener) { CameraManager manager = (CameraManager) context.getSystemService(CAMERA_SERVICE); String[] camIds = {}; try { camIds = manager.getCameraIdList(); } catch (CameraAccessException e) { Log.d(TAG, "Cam access exception getting IDs", e); } if (camIds.length < 1) { Log.d(TAG, "No cameras found"); return; } String id = camIds[0]; Log.d(TAG, "Using camera id " + id); mImageReader = ImageReader.newInstance(IMAGE_WIDTH, IMAGE_HEIGHT, ImageFormat.JPEG, MAX_IMAGES); mImageReader.setOnImageAvailableListener(imageAvailableListener, backgroundHandler); try { manager.openCamera(id, mStateCallback, backgroundHandler); } catch (CameraAccessException cae) { Log.d(TAG, "Camera access exception", cae); } } |
・CameraManagerというシステムサービスのgetCameraIdList()を使って利用可能なカメラデバイスをリストアップする。(カメラモジュールは1つしか繋いでないので最初のidを使用します。)
・ImageReaderというクラスのインスタンスを使用してカメラ画をjpegにする。非同期処理してくれて処理完了時にOnImageAvailableListenerに教えてくれる。
・openCamera()でカメラへアクセスすると、CameraDevice.StateCallback のonOpened()メソッドでオープンできたかのレポートが得られる。
デバイスのステータス変化をコールバックしてもらう受け皿を作成
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 |
private final CameraDevice.StateCallback mStateCallback = new CameraDevice.StateCallback() { @Override public void onOpened(CameraDevice camera) { Log.d(TAG,"camera onOpened()"); mCameraDevice = camera; } @Override public void onDisconnected(CameraDevice camera) { } @Override public void onError(CameraDevice camera, int error) { } }; |
外部から呼び出してもらう画像キャプチャ開始メソッドを作成
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 |
public void takePicture() { if (mCameraDevice == null) { Log.w(TAG, "Cannot capture image. Camera not initialized."); return; } try { mCameraDevice.createCaptureSession( Collections.singletonList(mImageReader.getSurface()), mSessionCallback, null); } catch (CameraAccessException cae) { Log.d(TAG, "access exception while preparing pic", cae); } } |
Activityから呼び出されるメソッド takePicture( )を作成し、この中でキャプチャセッションというのを作成する。
またその際に指定する、キャプチャセッションのステータス変化をコールバックしてくれる受け皿を作成する。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 |
private CameraCaptureSession.StateCallback mSessionCallback = new CameraCaptureSession.StateCallback() { @Override public void onConfigured(CameraCaptureSession cameraCaptureSession) { // The camera is already closed if (mCameraDevice == null) { Log.d("test","mCameraDevice == null"); return; } // When the session is ready, we start capture. mCaptureSession = cameraCaptureSession; triggerImageCapture(); } @Override public void onConfigureFailed(CameraCaptureSession cameraCaptureSession) { Log.w(TAG, "Failed to configure camera"); } }; |
キャプチャセッション作成が成功するとonConfiguredに入ってくるので、キャプチャを実行(triggerImageCapture() )する。
1 2 3 4 5 6 7 8 9 10 11 12 |
private void triggerImageCapture() { try { final CaptureRequest.Builder captureBuilder = mCameraDevice.createCaptureRequest(CameraDevice.TEMPLATE_STILL_CAPTURE); captureBuilder.addTarget(mImageReader.getSurface()); captureBuilder.set(CaptureRequest.CONTROL_AE_MODE, CaptureRequest.CONTROL_AE_MODE_ON); Log.d(TAG, "Session initialized."); mCaptureSession.capture(captureBuilder.build(), mCaptureCallback, null); } catch (CameraAccessException cae) { Log.d(TAG, "camera capture exception"); } } |
ここではCaptureRequestというのを作成、ターゲットに最初に作成したImageReaderを設定、その他のパラメータも設定。
capture( )を実行すると時に指定する処理結果コールバックのonCaptureCompleted( )が呼び出される。
1 2 3 4 5 6 7 8 9 10 11 12 13 |
private final CameraCaptureSession.CaptureCallback mCaptureCallback = new CameraCaptureSession.CaptureCallback() { @Override public void onCaptureCompleted(CameraCaptureSession session, CaptureRequest request, TotalCaptureResult result) { if (session != null) { session.close(); mCaptureSession = null; Log.d(TAG, "CaptureSession closed"); } } }; |