FLIR Oneを使ったアプリを考える前に, FLIR Oneについてもう少し理解を深めてみよう.

まずは, FLIR Oneのドキュメント[1]やサンプルコード[2]をベースに, 基本的な機能(サーマル画像表示, 温度測 - 定など)を実装し, FLIR Oneの基本的な動きを確認してみる.

1. サーマル画像表示
基本的な処理の流れは以下の通り.
flir02
[手順]
1) onCreateメソッド
 - 画像タイプを指定して, FrameProcessorのインスタンスを取得する.
2) onResumeメソッド
 - Device#startDiscoveryメソッドで, FLIR Oneとの接続を開始する.
  注) 例外処理(try-catch)をすること.
3) onStopメソッド
 - Device#stopDiscoveryメソッドで, FLIR Oneからのリスニングを中止し接続を停止する.
  注) stopDescoveryメソッド内で, closeメソッドも実行される.
4) onDeviceConnectedメソッド
 - デバイス情報を保持する.
 - Device#startFrameStreamメソッドで,On ストリーミングを開始する.
5) onDeviceDisconnectedメソッド
 - チューニング状態をUnknownにする.
 - Device#stopFrameStreamメソッドで, ストローミングを停止する.
6) onFrameReceivedメソッド
 - チューニング状態がInProgressでない場合に, FrameProcessor#processFrameメソッドで, フレームデータを処理する.
7) onFrameProcessedメソッド
 - RenderImage#getBitmapメソッドで, ビットマップ画像を取得する.
 - ImageView#setImageBitmapメソッドで, ビットマップ画像をビューにセットし, 画像を表示させる.
  注) UI表示はrunOnUiThread内で行うこと.

[コード]
package com.moonlight_aska.android.vision.flironedemo;

import android.graphics.Bitmap;
import android.os.Bundle;
import android.support.v7.app.AppCompatActivity;
import android.support.v7.widget.Toolbar;
import android.util.Log;
import android.view.Menu;
import android.view.MenuItem;
import android.widget.ImageView;
import android.widget.Toast;

import com.flir.flironesdk.Device;
import com.flir.flironesdk.Frame;
import com.flir.flironesdk.FrameProcessor;
import com.flir.flironesdk.RenderedImage;

import java.util.EnumSet;

public class PreviewActivity extends AppCompatActivity
        implements Device.Delegate, Device.StreamDelegate, FrameProcessor.Delegate {
    private static final String LOG_TAG = PreviewActivity.class.getSimpleName();

    private ImageView mThermalView = null;  // 表示用View
    private volatile Device mDevice = null;  // Flir Oneデバイス
    private FrameProcessor mFrmProcessor = null;  // フレーム処理プロセッサ
    private Device.TuningState mTuningState = Device.TuningState.Unknown;    // チューニング状態
    private Bitmap mThermalBitmap = null;   // 描画用ビットマップ画像

    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_preview);
        Toolbar toolbar = (Toolbar) findViewById(R.id.toolbar);
        setSupportActionBar(toolbar);

        mThermalView = (ImageView)findViewById(R.id.thermal_view_id);
        mThermalView.setRotation(180.0f);  // 表示上下反転
        RenderedImage.ImageType defImageType = RenderedImage.ImageType.BlendedMSXRGBA8888Image;  // Thermal + Visual画像
        mFrmProcessor = new FrameProcessor(this, this, EnumSet.of(defImageType));
    }

    @Override
    public boolean onCreateOptionsMenu(Menu menu) {
        // Inflate the menu; this adds items to the action bar if it is present.
        getMenuInflater().inflate(R.menu.menu_preview, menu);
        return true;
    }

    @Override
    public boolean onOptionsItemSelected(MenuItem item) {
        // Handle action bar item clicks here. The action bar will
        // automatically handle clicks on the Home/Up button, so long
        // as you specify a parent activity in AndroidManifest.xml.
        int id = item.getItemId();

        //noinspection SimplifiableIfStatement
        if (id == R.id.action_settings) {
            return true;
        }

        return super.onOptionsItemSelected(item);
    }

   @Override
    protected void onResume() {
        super.onResume();
        try {
            Log.i(LOG_TAG, "onPesume, starting discovery!");
            Device.startDiscovery(this, this);
        } catch(IllegalStateException e) {
            Log.e(LOG_TAG, "onPesume, startDiscovery() error!");
            e.printStackTrace();
        }
    }

    @Override
    protected void onStop() {
        Log.i(LOG_TAG, "onStop, stopping discovery!");
        Device.stopDiscovery();

        super.onStop();
    }

    @Override
    public void onTuningStateChanged(Device.TuningState tuningState) {

    }

    @Override
    public void onAutomaticTuningChanged(boolean b) {

    }

    @Override
    public void onDeviceConnected(Device device) {
        Log.i(LOG_TAG, "Device connected!");

        mDevice = device;
        mDevice.startFrameStream(this);  // ストリーミング開始
        // Debug
        runOnUiThread(new Runnable() {
            @Override
            public void run() {
                Toast.makeText(getApplicationContext(), "Device connected.", Toast.LENGTH_LONG).show();
            }
        });
    }

    @Override
    public void onDeviceDisconnected(Device device) {
        Log.i(LOG_TAG, "Device disconnected!");

        mTuningState = Device.TuningState.Unknown;
        if (mDevice != null) {
            mDevice.stopFrameStream();
            mDevice = null;
            // Debug
            runOnUiThread(new Runnable() {
                @Override
                public void run() {
                    Toast.makeText(getApplicationContext(), "Device disconnected.", Toast.LENGTH_LONG).show();
                }
            });
        }
    }

    @Override
    public void onFrameReceived(Frame frame) {
        Log.i(LOG_TAG, "Frame received!");

        if (mFrmProcessor != null) {
            if (mTuningState != Device.TuningState.InProgress) {  // チューン中でない
                mFrmProcessor.processFrame(frame);
            }
        }
    }

    @Override
    public void onFrameProcessed(RenderedImage renderedImage) {
        Log.i(LOG_TAG, "Frame processing!");

        if (renderedImage.imageType() == RenderedImage.ImageType.BlendedMSXRGBA8888Image) {
            mThermalBitmap = renderedImage.getBitmap();
            runOnUiThread(new Runnable() {
                @Override
                public void run() {
                    if (mThermalView != null) {
                        mThermalView.setImageBitmap(mThermalBitmap); // 画像表示
                    }
                }
            });
        }
    }
}

[実行結果]
 - Nexus 7(2013) / Android 4.4.4

flir01

一応, サーマル画像を表示できるようになった.
次は, 温度表示を行ってみようと思う.

----
参照URL:
[1] FLIR One SDK Documentation
[2] FLIR One Software Development Kit