Android Things(三)-傳感器與相機功能

AndroidThings傳感器添加

Android 傳感器框架支持多種傳感器類型來感知周邊環(huán)境數(shù)據(jù)。使用Androidthings提供的傳感器驅(qū)動接口,可以通過Peripheral I/O接口來添加新的傳感器設(shè)備。對于Android系統(tǒng)中已經(jīng)集成的傳感器的數(shù)據(jù)通過SensorManager的接口即可獲取,另外我們的Androidtings應(yīng)用也可以通過創(chuàng)建新的傳感器驅(qū)動的方式來為系統(tǒng)添加一個新類型的傳感器,比如溫感器,血糖檢測器等傳感器。
創(chuàng)建一個新的傳感器驅(qū)動并將其注冊進系統(tǒng)后,系統(tǒng)將會輪詢監(jiān)聽傳感器來周期性的獲取傳感器數(shù)據(jù), 為了響應(yīng)系統(tǒng)對傳感器數(shù)據(jù)的輪訓(xùn)請求, 我們需要實現(xiàn)UserSensorDriver 類并且重寫read()方法,在read方法中返回傳感器數(shù)據(jù).下面我們來詳細(xì)描述添加一個新的傳感器的過程。

首先要添加系統(tǒng)權(quán)限

    <uses-permission android:name="com.google.android.things.permission.MANAGE_SENSOR_DRIVERS" />

然后要實現(xiàn)一個傳感器驅(qū)動,用來讀取傳感器數(shù)據(jù),每次調(diào)用read方法都要返回一個新的包含當(dāng)前傳感器數(shù)據(jù)的UserSensorReading對象。代碼示例如下

UserSensorDriver mDriver = new UserSensorDriver() {
    // Sensor data values
    float x, y, z;
    @Override
    public UserSensorReading read() {
        try {
            // ...讀取傳感器硬件數(shù)據(jù)的步驟此處略過...
            // 將讀取到的傳感器數(shù)據(jù)返回
            return new UserSensorReading(new float[]{x, y, z});
        } (catch Exception e) {
            throw new IOException("Unable to read sensor");
        }
    }
};

創(chuàng)建完傳感器驅(qū)動后,開始為系統(tǒng)添加傳感器,傳感器分為系統(tǒng)已支持的類型和系統(tǒng)還未支持的類型,兩者的添加步驟有區(qū)別。
如果需要添加一個系統(tǒng)支持類型的傳感器設(shè)備:

  1. 使用 UserSensor.Builder 描述傳感器類型,傳感器類型為Android 目前已經(jīng)支持的 Sensor types.
  2. 設(shè)置 sensor 名稱以及驅(qū)動供應(yīng)商名稱
  3. 設(shè)置傳感器數(shù)據(jù)范圍,分辨率,更新頻率,電量需求(如果需要).這些參數(shù)將幫助framework在接到SensorManager的數(shù)據(jù)請求時來選擇最佳的傳感器。
  4. 使用setDriver()方法attach UserSensorDriver

如果要添加一個系統(tǒng)尚未支持的傳感器類型:

  1. 使用setCustomType() 方法,設(shè)置sensor type 為大于等于 TYPE_DEVICE_PRIVATE_BASE的類型值
  2. 設(shè)置傳感器類型名稱,傳感器名稱在系統(tǒng)范圍內(nèi)需要是唯一的
  3. 設(shè)置傳感器數(shù)據(jù)報告模式

代碼示例如下:

// 添加已支持類型的傳感器
UserSensor accelerometer = UserSensor.builder()
        .setName("GroveAccelerometer")
        .setVendor("Seeed")
        .setType(Sensor.TYPE_ACCELEROMETER)
        .setDriver(mDriver)
        .build();
// 添加未支持類型的傳感器
UserSensor custom = UserSensor.builder()
        .setName("MySensor")
        .setVendor("MyCompany")
        .setCustomType(Sensor.TYPE_DEVICE_PRIVATE_BASE,
                "com.example.mysensor",
                Sensor.REPORTING_MODE_CONTINUOUS)
        .setDriver(mDriver)
        .build();

創(chuàng)建完傳感器后就要將其注冊到系統(tǒng),使用UserDriverManager來 注冊傳感器,將新傳感器連接到系統(tǒng),應(yīng)用就可以可以通過Android傳感器系統(tǒng)服務(wù)來獲取傳感器的數(shù)據(jù)。實例代碼如下

public class SensorDriverService extends Service {

    UserSensor mAccelerometer;

    @Override
    public void onCreate() {
        super.onCreate();
        ...
        UserDriverManager manager = UserDriverManager.getManager();
        // 創(chuàng)建一個新的傳感器
        mAccelerometer = ...;
        // 將傳感器注冊到系統(tǒng)
        manager.registerSensor(mAccelerometer);
    }
}

至此,如何添加一個新的傳感器的步驟我們就描述完了。下面我們介紹如何為Androidthings應(yīng)用添加相機功能,這也是官方提供的一個示例。

Androidthings相機功能添加

首先添加系統(tǒng)權(quán)限:

<uses-permission android:name="android.permission.CAMERA" />

然后將相機連接到開發(fā)板,將相機模塊連接到開發(fā)板上的CS1-2相機接口,如下圖所示(圖片來自于谷歌開發(fā)者網(wǎng)站)

完成上面的準(zhǔn)備工作后,我們正式開始進行圖片獲取相關(guān)的流程,獲取圖片的第一步是需要找到相機并且建立和設(shè)備之間的連接:

  1. 使用CameraManager 系統(tǒng)服務(wù)的getCameraIdList()找到所有可用的相機設(shè)備列表。
  2. 創(chuàng)建一個ImageReader 實例用來處理相機原始數(shù)據(jù)并且生成JPEG編碼格式的圖像數(shù)據(jù)。
  3. 建立和相機設(shè)備的連接,相機被成功打開后將會調(diào)用CameraDevice.StateCallback的onOpened() 回調(diào)方法
    示例代碼如下:
public class CameraSample {
    // 圖像參數(shù) (device-specific)
    private static final int IMAGE_WIDTH  = ...;
    private static final int IMAGE_HEIGHT = ...;
    private static final int MAX_IMAGES   = ...;

    // 用于處理圖像結(jié)果
    private ImageReader mImageReader;
    // 激活相機設(shè)備連接
    private CameraDevice mCameraDevice;
    // 激活圖像捕捉
    private CameraCaptureSession mCaptureSession;

    // Initialize a new camera device connection
    public void initializeCamera(Context context,
                                 Handler backgroundHandler,
                                 ImageReader.OnImageAvailableListener imageAvailableListener) {
        // 獲取可用的相機設(shè)備列表
        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];
        // 初始化圖片處理器 imagereader
        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);
        }
    }

    // 相機相關(guān)回調(diào)
    private final CameraDevice.StateCallback mStateCallback =
            new CameraDevice.StateCallback() {

        @Override
        public void onOpened(CameraDevice cameraDevice) {
            mCameraDevice = cameraDevice;
        }
        ...
    };

    // 關(guān)閉相機資源
    public void shutDown() {
        if (mCameraDevice != null) {
            mCameraDevice.close();
        }
    }
}

上面詳細(xì)描述了相機初始化,建立設(shè)備連接相關(guān)的工作,接下來描述一下如何調(diào)用相機來捕獲圖片

和相機建立連接之后, 需要創(chuàng)建一個相機拍照會話,具體步驟如下:

  1. 使用createCaptureSession() 方法創(chuàng)建一個新的CameraCaptureSession 實例
  2. 傳入和ImageReader建立連接的surface
  3. 創(chuàng)建一個 CameraCaptureSession.StateCallback回調(diào)來監(jiān)控相機會話配置以及激活的狀態(tài) 。示例代碼如下
public class CameraSample {
    ...
    public void takePicture() {
        // 為了捕獲靜態(tài)圖像,首先創(chuàng)建一個圖像捕捉會話
        try {
            mCameraDevice.createCaptureSession(
                    Collections.singletonList(mImageReader.getSurface()),
                    mSessionCallback,
                    null);
        } catch (CameraAccessException cae) {
            Log.d(TAG, "access exception while preparing pic", cae);
        }
    }

    // 通過回調(diào)來監(jiān)控會話狀態(tài)
    private CameraCaptureSession.StateCallback mSessionCallback =
            new CameraCaptureSession.StateCallback() {

            @Override
            public void onConfigured(CameraCaptureSession cameraCaptureSession) {
                // 會話已經(jīng)準(zhǔn)備好,則可以開始拍照.
                mCaptureSession = cameraCaptureSession;
                triggerImageCapture();
            }
            @Override
            public void onConfigureFailed(CameraCaptureSession cameraCaptureSession) {
                Log.w(TAG, "Failed to configure camera");
            }
    };
}

接下來進入到圖片捕捉階段 :

  1. 初始化一個新的 CaptureRequest ,如果靜態(tài)圖片使用TEMPLATE_STILL_CAPTURE 參數(shù).
  2. 設(shè)置其他相機拍照參數(shù), 比如 auto-focus ,auto-exposure等
  3. 調(diào)用CameraCaptureSession的capture方法來啟動拍照請求,拍照完成后,關(guān)閉capture session。通過CameraCaptureSession.CaptureCallback回調(diào)來處理拍照結(jié)果。具體示例代碼如下:
public class CameraSample {
    // 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");
                }
            }
        };
}

最后是對獲取到的原始圖像數(shù)據(jù)的處理:

  1. 通過ImageReader 的onImageAvailable 方法獲取最新的圖像數(shù)據(jù) .
  2. 通過getBuffer()方法來獲取JPEG編碼格式的數(shù)據(jù)。代碼實例如下:
    // 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(final byte[] imageBytes) {
        if (imageBytes != null) {
            // ...process the captured image...
        }
    }

這樣就完成了通過相機設(shè)備來獲取靜態(tài)圖像數(shù)據(jù)的整個流程。
參考資料:https://developer.android.google.cn/things/sdk/drivers/sensors.html#describing_the_sensor

最后編輯于
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請聯(lián)系作者
平臺聲明:文章內(nèi)容(如有圖片或視頻亦包括在內(nèi))由作者上傳并發(fā)布,文章內(nèi)容僅代表作者本人觀點,簡書系信息發(fā)布平臺,僅提供信息存儲服務(wù)。

推薦閱讀更多精彩內(nèi)容

  • Spring Cloud為開發(fā)人員提供了快速構(gòu)建分布式系統(tǒng)中一些常見模式的工具(例如配置管理,服務(wù)發(fā)現(xiàn),斷路器,智...
    卡卡羅2017閱讀 134,886評論 18 139
  • Android 自定義View的各種姿勢1 Activity的顯示之ViewRootImpl詳解 Activity...
    passiontim閱讀 173,132評論 25 708
  • 國家電網(wǎng)公司企業(yè)標(biāo)準(zhǔn)(Q/GDW)- 面向?qū)ο蟮挠秒娦畔?shù)據(jù)交換協(xié)議 - 報批稿:20170802 前言: 排版 ...
    庭說閱讀 11,132評論 6 13
  • Android傳感器定義 Android 傳感器相關(guān)術(shù)語微機電傳感器(MEMS)MEMS 通常制作在規(guī)格很小的硅芯...
    Jannonx閱讀 4,413評論 0 1
  • 【清】顧嗣協(xié) 《雜興》 駿馬能歷險,犁田不如牛. 堅車能載重,渡河不如舟. 舍才(長)以就短,智高難為謀. 生才貴...
    劉偉書法_沈陽閱讀 661評論 1 11