添加相机功能

一个聪明的门铃应该捕获一个人(或什么)在门口的图像,允许所有者远程作出是否回答的决定。

因为Android Things 建立在Android框架上,您可以访问Android移动开发人员使用的强大的相机API。在本课程中,您将使用Android相机API访问摄像机外围设备,并捕获图像供以后处理。

连接相机

将支持的相机模块连接到电路板上的CSI-2相机端口。 确保电缆完全插入并均匀平放,然后再关闭 连接器门锁。

""

添加权限和所需功能

  1. 将所需的权限添加到应用的清单文件中:

    <uses-permission android:name="android.permission.CAMERA" />
    
  2. 声明您的应用程序需要相机存在:

    <uses-feature android:name="android.hardware.camera" />
    <uses-feature android:name="android.hardware.camera.autofocus" />
    

设置一个I / O线程

与外围硬件通信将阻塞操作引入到应用程序的流中。为了避免阻塞应用程序的主线程,从而延迟框架事件,创建后台工作线程来处理输入和处理命令。 HandlerThread 非常适用于此目的。

public class DoorbellActivity extends Activity {

    /**
     * A Handler for running tasks in the background.
     */
    private Handler mBackgroundHandler;
    /**
     * An additional thread for running tasks that shouldn't block the UI.
     */
    private HandlerThread mBackgroundThread;

    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);

        startBackgroundThread();
    }

    @Override
    protected void onDestroy() {
        super.onDestroy();

        mBackgroundThread.quitSafely();
    }

    /**
     * Starts a background thread and its Handler.
     */
    private void startBackgroundThread() {
        mBackgroundThread = new HandlerThread("InputThread");
        mBackgroundThread.start();
        mBackgroundHandler = new Handler(mBackgroundThread.getLooper());
    }
}

初始化相机会话

捕获摄像机图像的第一步是发现硬件并打开设备连接。要连接到相机设备:

  1. 使用 CameraManager 系统服务发现带有 getCameraIdList() 的可用摄像头设备的列表。
  2. 创建一个 ImageReader 实例来处理原始相机数据,并为您的应用程序生成JPEG编码的图像。读者不同步处理数据,并在图像准备就绪时调用提供的 OnImageAvailableListener
  3. 使用 openCamera() 打开与相应摄像机设备的连接。
  4. CameraDevice.StateCallback 报告通过 onOpened() 回调方法成功打开了摄像头。
  5. 关闭 CameraDevice,当它不用于释放系统资源:
  6. </ol>

    public class DoorbellCamera {
    
        // Camera image parameters (device-specific)
        private static final int IMAGE_WIDTH  = ...;
        private static final int IMAGE_HEIGHT = ...;
        private static final int MAX_IMAGES   = ...;
    
        // Image result processor
        private ImageReader mImageReader;
        // Active camera device connection
        private CameraDevice mCameraDevice;
        // Active camera capture session
        private CameraCaptureSession mCaptureSession;
    
        // Initialize a new camera device connection
        public void initializeCamera(Context context,
                                     Handler backgroundHandler,
                                     ImageReader.OnImageAvailableListener imageListener) {
    
            // Discover the camera instance
            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];
    
            // Initialize image processor
            mImageReader = ImageReader.newInstance(IMAGE_WIDTH, IMAGE_HEIGHT,
                    ImageFormat.JPEG, MAX_IMAGES);
            mImageReader.setOnImageAvailableListener(imageListener, backgroundHandler);
    
            // Open the camera resource
            try {
                manager.openCamera(id, mStateCallback, backgroundHandler);
            } catch (CameraAccessException cae) {
                Log.d(TAG, "Camera access exception", cae);
            }
        }
    
        // Callback handling devices state changes
        private final CameraDevice.StateCallback mStateCallback =
                new CameraDevice.StateCallback() {
    
            @Override
            public void onOpened(CameraDevice cameraDevice) {
                mCameraDevice = cameraDevice;
            }
    
            ...
        };
    
        // Close the camera resources
        public void shutDown() {
            if (mCameraDevice != null) {
                mCameraDevice.close();
            }
        }
    }
    

    触发图像捕获

    一旦相机设备连接处于活动状态,请创建一个 CameraCaptureSession 以便于来自硬件的图像请求。打开捕获会话:

    1. 使用 createCaptureSession() 方法构建一个新的 CameraCaptureSession 实例。
    2. 将方法传递给单个图像请求的潜在目标曲面的列表。对于此示例,传递连接到先前构建的 ImageReader 的表面。
    3. 附加一个 CameraCaptureSession.StateCallback 来报告会话的配置和活动时间。如果一切都成功,回调函数调用 onConfigured()方法。
    4. </ol>

      public class DoorbellCamera {
      
          ...
      
          public void takePicture() {
              if (mCameraDevice == null) {
                  Log.w(TAG, "Cannot capture image. Camera not initialized.");
                  return;
              }
      
              // Here, we create a CameraCaptureSession for capturing still images.
              try {
                  mCameraDevice.createCaptureSession(
                          Collections.singletonList(mImageReader.getSurface()),
                          mSessionCallback,
                          null);
              } catch (CameraAccessException cae) {
                  Log.d(TAG, "access exception while preparing pic", cae);
              }
          }
      
          // Callback handling session state changes
          private final CameraCaptureSession.StateCallback mSessionCallback =
                  new CameraCaptureSession.StateCallback() {
      
              @Override
              public void onConfigured(CameraCaptureSession cameraCaptureSession) {
                  // When the session is ready, we start capture.
                  mCaptureSession = cameraCaptureSession;
                  triggerImageCapture();
              }
      
              @Override
              public void onConfigureFailed(CameraCaptureSession cameraCaptureSession) {
                  Log.w(TAG, "Failed to configure camera");
              }
          };
      }
      

      您的应用程序现在可以使用活动的 CameraCaptureSession 从相机硬件请求图像数据。在捕获会话内开始图像捕获请求:

      1. 使用构建器界面初始化新的 CaptureRequest。要捕获单个静止图像,请使用 TEMPLATE_STILL_CAPTURE 参数。
      2. 指示请求的目标曲面。对于这个例子,这是提供给捕获会话的 ImageReader 表面。
      3. 使用请求构建器设置任何其他捕获参数,如自动对焦和自动曝光。
      4. 使用 capture() 方法在 CameraCaptureSession 上启动捕获请求。
      5. 捕获完成后,关闭活动会话。
      6. </ol>

        public class DoorbellCamera {
        
            // Image result processor
            private ImageReader mImageReader;
            // Active camera device connection
            private CameraDevice mCameraDevice;
            // Active camera capture session
            private CameraCaptureSession mCaptureSession;
        
            ...
        
            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);
        
                    mCaptureSession.capture(captureBuilder.build(), mCaptureCallback, null);
                } catch (CameraAccessException cae) {
                    Log.d(TAG, "camera capture exception");
                }
            }
        
            // Callback handling capture progress events
            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");
                        }
                    }
                };
        }
        

        处理图像结果

        从您的活动中,当按下门铃按钮时,初始化相机并调用 takePicture() 方法。在捕获过程中,相机硬件将图像数据流传输到提供的 ImageReader 曲面,并调用带有结果的 OnImageAvailableListener

        捕获完成后获取图像:

        1. 从提供给 onImageAvailable() 方法的 ImageReader 获取最新的图像。
        2. getBuffer() 方法返回的缓冲区中将JPEG编码图像作为 byte [] 检索:
        3. </ol>

          public class DoorbellActivity extends Activity {
          
              /**
               *  Camera capture device wrapper
               */
              private DoorbellCamera mCamera;
          
              @Override
              protected void onCreate(Bundle savedInstanceState) {
                  super.onCreate(savedInstanceState);
                  ...
          
                  mCamera = DoorbellCamera.getInstance();
                  mCamera.initializeCamera(this, mBackgroundHandler, mOnImageAvailableListener);
              }
          
              @Override
              protected void onDestroy() {
                  super.onDestroy();
                  ...
          
                  mCamera.shutDown();
              }
          
              private Button.OnButtonEventListener mButtonCallback =
                      new Button.OnButtonEventListener() {
                  @Override
                  public void onButtonEvent(Button button, boolean pressed) {
                      if (pressed) {
                          // Doorbell rang!
                          mCamera.takePicture();
                      }
                  }
              };
          
              // Callback to receive captured camera image data
              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);
                  }
              };
          
              private void onPictureTaken(byte[] imageBytes) {
                  if (imageBytes != null) {
                      // ...process the captured image...
                  }
              }
          }
          

          results matching ""

            No results matching ""