茨城エンジニアのPython開発日記

茨城のITベンチャー企業ではたらく2年目エンジニア。Pythonで色々なものを作成中。

AIをARに応用するぞ会 → (第8回) ~実装1 カメラを管理するインスタンス取得~


ブログから記事を見つけたい場合はこちら

ブログ地図 - 茨城エンジニアのPython開発日記

こんにちは松原です。
夏休みですが外出できないので暇です。
安くなっていたので、DQ11を買ってやっています。
面白い。

0. 今回の内容

現在スマホアプリを作成しようとしています。
基本設計は済んだので、あとは一つ一つ実装していくだけ。

今回はその第一段階として、
androidカメラにアクセスし、カメラの映像を画面に描画する」
という処理を実装しようとしました。

で、今回進んだのは「androidカメラにアクセス」の部分までですね。
かなり複雑な手順が必要だったため、丁寧に説明していこうと思います。

1. 処理概要

さて、androidカメラにアクセスし、カメラの映像を画面に描画していきます。
最初は下記公式ドキュメントを参考にしようとしました。

developer.android.com

ですがここで紹介されているクラス、よくみるとAndroid 5.0までで推奨されている形。
それ以降は下記クラスを利用しろとあります。

android.hardware.camera2  |  Android デベロッパー  |  Android Developers

全部英語なうえに、日本語だったとしてもかなり難解。
そこで下記サイト等も参考にしながら、なんとかかみ砕いていきました。

AndroidのCamera2 APIを触ってみた - woshidan's blog
Androidアプリにおける最小限のカメラ機能の実装|システム開発のブログ|株式会社ベルソフト

かみ砕いた結果、必要な処理フローがこれ。

f:id:tottorisnow33:20210808161517p:plain
f:id:tottorisnow33:20210808161537p:plain

3つのクラスがややこしくかかわりあうので、並行でそれぞれの状態を記入しておきました。
こう書くと簡単だけど、公式ドキュメントから読み解くの無理では……

1. ソースコード

上記を実装してあるのがこれ。
カメラ利用許可を取る処理の場所がおかしいけど、そのうち直します。

package com.example.myapplication_test;

import androidx.annotation.NonNull;
import androidx.appcompat.app.AppCompatActivity;
import androidx.core.app.ActivityCompat;
import androidx.core.content.ContextCompat;


import android.Manifest;
import android.content.pm.PackageManager;

import android.hardware.camera2.CameraDevice;
import android.hardware.camera2.CameraManager;
import android.hardware.camera2.CameraAccessException;
import android.media.MediaPlayer;
import android.os.Bundle;

import android.widget.Button;
import android.widget.TextView;


import android.content.Context;



public class MainActivity extends AppCompatActivity {

    private TextView textView;
    private boolean buttonTap = false;
    final int CLASS_NUM = 2;
    final int IMG_WIDTH = 224;
    final int IMG_HEIGHT = 224;
    MediaPlayer mp = null;
    Button button1;
    static final int REQUEST_CODE = 1;

    Context globalContext = MainActivity.this;      /* コンテキスト */
    CameraManager manager;                          /* カメラマネージャー */
    CameraDevice mCameraDevice = null;              /* カメラデバイス */
    String[] cameraIdList;                          /* カメラマネージャーから取得するカメラID */

    /* カメラ起動時やエラー時の処理セット */
    private final CameraDevice.StateCallback mStateCallback = new CameraDevice.StateCallback() {
        @Override
        /* この処理によって、カメラ起動時にそのカメラをメンバであるmCameraDeviceで制御できるよう結び付ける */
        public void onOpened(@NonNull CameraDevice cameraDevice) {
            mCameraDevice = cameraDevice;
        }

        @Override
        public void onDisconnected(@NonNull CameraDevice cameraDevice) {
            cameraDevice.close();
            mCameraDevice = null;
        }

        @Override
        public void onError(@NonNull CameraDevice cameraDevice, int error) {
            cameraDevice.close();
            mCameraDevice = null;
        }
    };

    private void setCamera()
    {
        /* カメラマネージャーのインスタンスをcontextから取得 */
        manager = (CameraManager) globalContext.getSystemService(Context.CAMERA_SERVICE);

        /* カメラの利用許可を利用者に求める。挙動が変。場所をのちのち変える予定 */
        ActivityCompat.requestPermissions(this, new String[]{
                Manifest.permission.CAMERA
        }, REQUEST_CODE);

        /* カメラマネージャーから、デバイスのカメラID一覧を取得 */
        {
            try {
                cameraIdList = manager.getCameraIdList();
                System.out.println("------------------success getting cameraIDList");
            } catch (CameraAccessException e) {
                e.printStackTrace();
                System.out.println("------------------cant get cameraIDList");
            }
        }


        /* ユーザーからの許可が下りてる場合にはカメラの取得 */
        try {
            if (ActivityCompat.checkSelfPermission(this, Manifest.permission.CAMERA) != PackageManager.PERMISSION_GRANTED) {
                System.out.println("------------------Permission error");
                return;
            }
            else{
                /* カメラの起動。mStateCallbackを引数に渡していることで、今回起動したカメラの制御をmCameraDeviceで行うように設定している */
                manager.openCamera(cameraIdList[0], mStateCallback, null);
                System.out.println("------------------success opening camera");
            }
        } catch (CameraAccessException e) {
            e.printStackTrace();
            System.out.println("------------------cant open camera");
        }

    }


    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_main);

        /* ボタンを設定 */
        button1 = findViewById(R.id.button);

        /* カメラのインスタンスをmCameraDeviceに代入 */
        setCamera();

    }

}

一応コンソールでは"------------------success opening camera"と出るので、カメラ起動の部分には問題なく入っています。
あとは実際に描画してみて、本当にカメラ取得ができているのか確認ですね。

2. androidの設定

カメラの利用ですが、ソースコードだけ書けばいいという訳ではありません。
下記処理が必要です。

1. マニフェストにカメラの利用許可を記入
2. エミュレータ上で、web camをデバイスのカメラと認識してもらうように設定
3. 最低APIを23に変更

1については、ユーザーやgoogle playにカメラ使うよと伝えるための宣言です。
カメラの利用許可の方法は下記参照。
Camera API  |  Android デベロッパー  |  Android Developers

2についてはエミュレータのデバイスには実際のカメラがないので、ウェブカメラをデバイスのカメラと思ってもらう処置です。
下記参照。
[Android]エミュレータでPCのウェブカメラを使う(Android Studio3.5) | ntの備忘録

3については、API 23以下では使えないクラスなどを使うので、利用できる最低のバージョンをそこに合わせようという話です。
API 21だったかもしれない。下記参照。
Android StudioでAPIレベルを下げる - Qiita

3. あとがき

今回はかなりてこずりました。
でもこういうのを読み解くのが楽しいんだよなあ。

という訳で、次回は取得したカメラからの映像を実際に描画してみます。

それではまた次回。