この記事について
カメラ画像をネガポジ反転して出力するだけの簡単なAndroidアプリを作ります。
カメラ入力にはOpenCV4を使用します。
カメラ画像を用いて何らかのアプリを作るためのベースとなるプロジェクトとして利用できます。
この記事では、OpenCV Managerは使わずにAPK内にOpenCVライブラリを含む方法で説明します。
追記(2020/07/18) 最近のAndroid Studio (またはAndroid SDK) では、この方法は使えないようです。Camera2 APIかCamera Xを使った方が良いです。 CameraXを使う方法はこちらにまとめました。
https://qiita.com/iwatake2222/items/c0ebe6d84afdef57aab3
開発環境
- Windows 10 64-bit
- Android Studio 3.3.2
- 開発言語: Java
- OpenCV 4.1.0
- 対象とするデバイス: Galaxy S7
事前準備
Android Studioのインストール
adbのあるフォルダにパスを通しておくと、色々と便利かも。(本記事では特に使用しません)
OpenCVのダウンロード
https://github.com/opencv/opencv/releases
からopencv-4.1.0-android-sdk.zip
をダウンロードして、適当な場所に展開しておきます。バージョンは好きなのを選んでください。
以後、展開先のパスを{OpenCV-android-sdk}
として説明を記載します。
自分のプロジェクトとapkにOpenCVを取り込む
新規プロジェクトの作成
- Android Studioを開く
-
Start a new Android Studio project
をクリック -
Empty Activity
を選び、Next - アプリケーション名などを設定する
- 試すだけなら全部デフォルトで良いです
- 今回は、開発言語にJava、Minimum API levelはAPI 21としました
- リリースしたりする予定がある場合は、package nameをちゃんと決めてください
- Android端末をUSBでPCに接続します
- メニューバーの
Run
->Run'App'
を選び、Hello Worldが出力されるデフォルト状態のアプリが、Android端末で起動すればOKです
OpenCVを取り込む
- Import Module
- メニューバーの
File
-> ``New->
Import Module` を選ぶ -
Source directory
に、先ほどダウンロード・展開した、{OpenCV-android-sdk}/sdk
を指定- OpenCV 3.x以前だと
{OpenCV-android-sdk}/sdk/java
を指定?
- OpenCV 3.x以前だと
-
Module name
がデフォルトだとsdk
となり分かりづらいので、適当にopencv
としておきます - その他設定はデフォルトのままで、
Next
、Finish
- メニューバーの
- 依存関係の設定
- メニューバーの
File
->Project Structure
を選ぶ - 左側の
app
を選び、Dependencies
タブを選ぶ - 右上の
+(add)
をクリックし、3: Module dependenciy
を選ぶ -
opencv
を選らんで、OK
- メニューバーの
-
(不要) OpenCVライブラリ(soファイル)のコピー
- 以前だと、OpenCVライブラリ(soファイル)を、自分で
jniLibs
にコピーする必要があったのですが、今では(OpenCV4以降??)不要なようです
- 以前だと、OpenCVライブラリ(soファイル)を、自分で
-
(不要) AndroidManifest, build.gradleの修正
- 以前だと、OpenCVの
AndroidManifest.xml
内で、targetSdkVersion
等が指定されているのを削除したり、build.gradle
内で各種sdkVersion(compileSdkVersion
等)をappとそろえる必要がありました。 - 今では(OpenCV4以降??)不要なようです
- 以前だと、OpenCVの
OpenCVのロード
OpenCVを使用するActivityのコード(プロジェクト生成時にEmpty Activityを選び、何も設定を変えていなかったら、app\src\main\java\com\example\myapplication\MainActivity.java
)を以下のように編集します。
実際には、どちらか片方で十分です。実プロダクトでは、System.loadLibrary
だけを使うと思います。
public class MainActivity extends AppCompatActivity {
static {
System.loadLibrary("opencv_java4"); // 追加
}
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
OpenCVLoader.initDebug(); // 追加
}
}
この状態でRunして、Logcatに以下のようなログが出たらOKです。
2019-05-25 18:50:20.050 29439-29439/com.example.myapplication I/OpenCV/StaticHelper: General configuration for OpenCV 4.1.0 =====================================
2019-05-25 18:50:20.050 29439-29439/com.example.myapplication I/OpenCV/StaticHelper: Version control: 4.1.0
2019-05-25 18:50:20.050 29439-29439/com.example.myapplication I/OpenCV/StaticHelper: Platform:
2019-05-25 18:50:20.050 29439-29439/com.example.myapplication I/OpenCV/StaticHelper: Timestamp: 2019-04-07T19:03:43Z
2019-05-25 18:50:20.050 29439-29439/com.example.myapplication I/OpenCV/StaticHelper: Host: Linux 4.15.0-47-generic x86_64
2019-05-25 18:50:20.050 29439-29439/com.example.myapplication I/OpenCV/StaticHelper: Target: Android 1 aarch64
2019-05-25 18:50:20.050 29439-29439/com.example.myapplication I/OpenCV/StaticHelper: CMake: 3.6.0-rc2
2019-05-25 18:50:20.050 29439-29439/com.example.myapplication I/OpenCV/StaticHelper: CMake generator: Ninja
2019-05-25 18:50:20.050 29439-29439/com.example.myapplication I/OpenCV/StaticHelper: CMake build tool: /opt/android/android-sdk.gradle/cmake/3.6.4111459/bin/ninja
2019-05-25 18:50:20.050 29439-29439/com.example.myapplication I/OpenCV/StaticHelper: Configuration: Release
なんか、Logcatの上の方を見ていると、OpenCV/StaticHelper: OpenCV error: Cannot load info library for OpenCV
こんなエラーが出ているのですが、動作には影響がありませんでした。
カメラライブビュー表示
ここまでで、OpenCVを取り込んだプロジェクトを準備できたので、カメラのライブビューを表示するアプリケーションを作ってみようと思います。
パーミッションの設定
カメラを使うための設定をします
マニフェストの設定
まず、「このアプリケーションはカメラを使うよ」と宣言しておく必要があります。
app/src/main/AndroidManifest.xml
に以下の3行を追加します。追加するのは、<manifest>
の直下です。既にある<application>
と同列に追加します。
<?xml version="1.0" encoding="utf-8"?>
<manifest xmlns:android="http://schemas.android.com/apk/res/android"
package="com.example.myapplication">
<!--↓追加 -->
<uses-permission android:name="android.permission.CAMERA"/>
<uses-feature android:name="android.hardware.camera" android:required="false"/>
<uses-feature android:name="android.hardware.camera.autofocus" android:required="false"/>
<!--↑追加-->
<application
android:allowBackup="true"
省略
</application>
</manifest>
許可をもらう
実は、マニフェストの設定だけでも事足ります。
しかし、これだけだと、ユーザがアプリの管理画面から権限設定をする必要があります。通常のユーザはその画面がどこにあるかも知りません。
なので、アプリの方から自発的に「許可をください」と要求画面を出します。
以下のようなコードで、もしも許可設定されていなかったら、許可要求画面を出すようにします。呼び出し元のコードは後程示します。
public static boolean getPermissionCamera(Activity activity) {
if (ContextCompat.checkSelfPermission(
activity,
android.Manifest.permission.CAMERA)
!= PackageManager.PERMISSION_GRANTED) {
String[] permissions = new String[]{Manifest.permission.CAMERA};
ActivityCompat.requestPermissions(
activity,
permissions,
0);
return false;
} else {
return true;
}
}
CameraViewの配置
レイアウトに、CameraViewを追加します。layout/activity_main.xml
を開き、Textモードで、org.opencv.android.JavaCameraView
を追加します。
<?xml version="1.0" encoding="utf-8"?>
<android.support.constraint.ConstraintLayout xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:app="http://schemas.android.com/apk/res-auto"
xmlns:tools="http://schemas.android.com/tools"
android:layout_width="match_parent"
android:layout_height="match_parent"
tools:context=".MainActivity">
<TextView
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:text="Hello World!"
app:layout_constraintBottom_toBottomOf="parent"
app:layout_constraintLeft_toLeftOf="parent"
app:layout_constraintRight_toRightOf="parent"
app:layout_constraintTop_toTopOf="parent" />
<!--↓追加-->
<org.opencv.android.JavaCameraView
android:id="@+id/camera_view"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:visibility="visible"
tools:layout_editor_absoluteX="0dp"
tools:layout_editor_absoluteY="0dp"
app:show_fps="true"/>
<!--↑追加-->
</android.support.constraint.ConstraintLayout>
コード全文
コード全文を以下に示します。
-
CameraBridgeViewBase.CvCameraViewListener
インターフェイスを継承します。-
MainActivity
に、implements CameraBridgeViewBase.CvCameraViewListener
を追加後、Alt+Enterで適当にimportを追加してもらいます。再度、Alt+Enterで、Implement Methods をして必要な関数を追加してもらいます
-
-
CameraBridgeViewBase
を宣言して、先ほどレイアウトに配置したJavaCameraView
に割り当てます。setCvCameraViewListener
でこのActivityを指定して、先ほど追加したonCameraViewStarted
、onCameraViewStopped
、onCameraFrame
を呼んでもらいます。 -
onCameraFrame
は毎フレーム呼ばれる関数です。引数(inputFrame
)にカメラからの入力画像が入り、戻り値に表示したいMat を設定します。- この
onCameraFrame
はUIスレッドとは別スレッドで動いているので、ここで画像処理等多少重たい処理をやっても大丈夫そうです。Best Practiceかどうかは分かりませんが。 - 今回はネガポジ反転してみました
-
onCameraFrame
での処理が重くなると、描画のfpsも当然遅くなります。これは実装者にとってはうれしいことです。onCameraFrame
内で重い処理をやっている最中に、再度onCameraFrame
関数が呼ばれることはないようです。なので、リエントラントにする必要はなさそうです。
- この
public class MainActivity extends AppCompatActivity implements CameraBridgeViewBase.CvCameraViewListener {
private CameraBridgeViewBase m_cameraView;
static {
System.loadLibrary("opencv_java4");
}
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
getPermissionCamera(this);
// OpenCVLoader.initDebug();
m_cameraView = findViewById(R.id.camera_view);
m_cameraView.setCvCameraViewListener(this);
m_cameraView.enableView();
}
public static boolean getPermissionCamera(Activity activity) {
if (ContextCompat.checkSelfPermission(
activity,
android.Manifest.permission.CAMERA)
!= PackageManager.PERMISSION_GRANTED) {
String[] permissions = new String[]{Manifest.permission.CAMERA};
ActivityCompat.requestPermissions(
activity,
permissions,
0);
return false;
} else {
return true;
}
}
@Override
public void onCameraViewStarted(int width, int height) {
}
@Override
public void onCameraViewStopped() {
}
@Override
public Mat onCameraFrame(Mat inputFrame) {
// ここで何らかの画像処理を行う
// 試しに、ネガポジ反転してみる
Core.bitwise_not(inputFrame, inputFrame);
return inputFrame;
}
}
機種や、デバイスの向き、設定によっては画面が回転していたりする可能性があります。それらの調整をやるとコードと説明が複雑になるので省略しています。
同様に、onDestoryやonResumeでの終了、復帰も省略しています。
おわりに
- AndroidでOpenCV4を使う手順をまとめました
- OpenCV4を使ってカメラプレビューを取得、表示する手順をまとめました