-
アプリケーションをビルドするには、静的リソースの代わりにカメラまたはその他のリアルタイムイメージストリームを使用します。
LSDCamera2Controller
カメラ 2 API 経由でのデバイスのカメラの設定およびオープンをカプセル化します。LSDCamera2Preview
また、カメラのプレビューを表示することもできます。
注
アプリケーションは、を使用する前 LSDCamera2Controller
にカメラランタイム権限が付与されていることを確認する責任があります。
4.1. LSDCamera2Controller
およびの基本設定 LSDCamera2Preview
:
class CameraActivity : AppCompatActivity() {
private lateinit var cameraController: LSDCamera2Controller
private val imageListener = object : LSDCamera2ImageListener {
override fun onImageAvailable(image: Image?) {
}
}
override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
setContentView(R.layout.activity_camera)
cameraController = LSDCamera2Controller(applicationContext)
cameraController.initialize(LSDCamera2Config().apply {
this.targetStreamResolution = Size(1280, 960)
})
cameraController.setImageListener(WeakReference(imageListener))
val preview = findViewById<LSDCamera2Preview>(R.id.camera_preview)
preview.initialize(cameraController)
}
override fun onResume() {
super.onResume()
if (checkSelfPermission(Manifest.permission.CAMERA) == PERMISSION_GRANTED) {
cameraController.start()
}
}
override fun onPause() {
super.onPause()
cameraController.stop()
}
}
4.2. レイアウト CameraActivity
:
```xml
<?xml version="1.0" encoding="utf-8"?>
<FrameLayout xmlns:android="http://schemas.android.com/apk/res/android"
android:layout_width="match_parent"
android:layout_height="match_parent">
<com.here.see.livesense.ar_lib.camera2.LSDCamera2Preview
android:id="@+id/camera_preview"
android:layout_width="match_parent"
android:layout_height="match_parent" />
</FrameLayout>
```
-
Live Sense SDK への認識のために渡されるデータを準備します。
public class RecognitionFromCamera implements ImageReader.OnImageAvailableListener {
private HandlerThread detectionThread;
private Handler detectionHandler;
private RoadBasicsModel roadBasicsModel;
private volatile boolean isProcessingFrame = false;
private volatile boolean isModelLoaded = false;
private int previewWidth;
private int previewHeight;
private int sensorOrientation;
private int rotation;
private byte[][] yuvBytes = new byte[3][];
private int[] rgbBytes;
private Bitmap bitmap;
public void init(int previewWidth, int previewHeight, int sensorOrientation) {
this.previewWidth = previewWidth;
this.previewHeight = previewHeight;
this.sensorOrientation = sensorOrientation;
detectionThread = new HandlerThread("detection");
detectionThread.start();
detectionHandler = new Handler(detectionThread.getLooper());
detectionHandler.post(() -> {
try {
RoadBasicsModel.Options rbOptions = new RoadBasicsModel.Options();
rbOptions.setEnableTrafficLightStatus(true);
roadBasicsModel = new RoadBasicsModel(rbOptions);
roadBasicsModel.addClassMinConfidence("pedestrian", 0.45f);
isModelLoaded = true;
} catch (IOException e) {
} catch (AuthorizationException e) {
}
rgbBytes = new int[previewWidth * previewHeight];
bitmap = Bitmap.createBitmap(previewWidth, previewHeight, Bitmap.Config.ARGB_8888);
});
}
public void close() {
if (detectionThread != null) {
detectionThread.quitSafely();
detectionThread = null;
detectionHandler = null;
}
if (roadBasicsModel != null) {
roadBasicsModel.close();
roadBasicsModel = null;
}
isModelLoaded = false;
}
public void setDeviceRotation(int deviceRotation) {
this.rotation = (this.sensorOrientation - deviceRotation + 360) % 360;
}
@Override
public void onImageAvailable(final ImageReader reader) {
final Image image = reader.acquireLatestImage();
if (image == null || !isModelLoaded) {
return;
}
if (isProcessingFrame) {
image.close();
return;
}
isProcessingFrame = true;
final Image.Plane[] planes = image.getPlanes();
final int yRowStride = planes[0].getRowStride();
final int uvRowStride = planes[1].getRowStride();
final int uvPixelStride = planes[1].getPixelStride();
for (int i = 0; i < planes.length; ++i) {
final ByteBuffer buffer = planes[i].getBuffer();
if (yuvBytes[i] == null) {
yuvBytes[i] = new byte[buffer.capacity()];
}
buffer.get(yuvBytes[i]);
}
image.close();
detectionHandler.post(() -> {
ImageUtils.convertYUV420ToARGB8888(
yuvBytes[0],
yuvBytes[1],
yuvBytes[2],
previewWidth,
previewHeight,
yRowStride,
uvRowStride,
uvPixelStride,
rgbBytes);
ImageUtils.argb8888ToBitmap(rgbBytes, previewWidth, previewHeight, bitmap);
List<Recognition> recognitions = roadBasicsModel.recognizeImage(bitmap, rotation, 0.6f);
for (Recognition recognition : recognitions) {
Log.d("RoadBasics", recognition.getTitle() + " at "
+ recognition.getLocation() + " with confidence " + recognition.getConfidence());
}
isProcessingFrame = false;
});
}
}
-
生データをビットマップに変換するヘルパーメソッド。
ImageUtils.argb8888ToBitmap(int[] argb, int width, int height);
ImageUtils.argb8888ToBitmap(int[] argb, int width, int height, Bitmap output);
ImageUtils.argb8888ToBitmap(byte[] argb, int width, int height);
ImageUtils.argb8888ToBitmap(byte[] argb, Bitmap output);
ImageUtils.convertYUV420ToARGB8888(
byte[] yData,
byte[] uData,
byte[] vData,
int width,
int height,
int yRowStride,
int uvRowStride,
int uvPixelStride,
int[] out)
ImageUtils.convertYUV420SPToARGB8888(
byte[] input,
int width,
int height,
int[] output)
-
検出コールを実行します。
public void runDetection() {
Bitmap image = BitmapFactory.decodeResource(this.getResources(), R.drawable.test_image);
List<Recognition> detections = roadBasicsModel.recognizeImage(image, 0, 0.6f);
}
-
バックグラウンドから前面に移動したときにLiveSenseEngine.getInstance().onResume()
メソッドを呼び出して、バックグラウンドで一時停止したサービスを再開することをお勧めします。 LiveSenseEngine.getInstance().onPause()
アプリケーションがバックグラウンドになったときにメソッドを呼び出します。 デバイスのメモリとバッテリの使用量を節約するサービスを停止するのに役立ちます。
@Override
protected void onPause() {
super.onPause();
LiveSenseEngine.getInstance().onPause();
}
@Override
protected void onResume() {
super.onResume();
LiveSenseEngine.getInstance().onResume();
}
-
アプリケーション終了時にリソースを解放します。
@Override
protected void onDestroy() {
super.onDestroy();
roadBasicsModel.close();
}
Live Sense モデルはスレッドセーフではありません。同じスレッドで初期化および実行する必要があります。 同じモデルインスタンスに複数のスレッドを使用すると、予期しない動作が発生する可能性があります。
モデルは並行して実行できますが、各モデルインスタンスは一度に 1 つのイメージのみを処理できます。 前の呼び出しが完了する前にモデルを実行すると、例外が発生します。