Skip to content

Commit

Permalink
Merge pull request #2 from michaeltroger/feature/update_dependencies
Browse files Browse the repository at this point in the history
Update dependencies
  • Loading branch information
michaeltroger authored Feb 25, 2024
2 parents bd40c1f + fcc8e4f commit 3e97794
Show file tree
Hide file tree
Showing 917 changed files with 796 additions and 719,719 deletions.
1 change: 0 additions & 1 deletion .gitattributes

This file was deleted.

14 changes: 6 additions & 8 deletions .github/workflows/android.yml
Original file line number Diff line number Diff line change
Expand Up @@ -12,19 +12,17 @@ jobs:
runs-on: ubuntu-latest

steps:
- uses: actions/checkout@v2
- name: set up JDK 11
uses: actions/setup-java@v2
- uses: actions/checkout@v4
- name: set up JDK
uses: actions/setup-java@v4
with:
java-version: '11'
distribution: 'adopt'
java-version: '21'
distribution: 'temurin'
cache: gradle

- name: Grant execute permission for gradlew
run: chmod +x gradlew
- name: Build with Gradle
run: ./gradlew assembleDebug
- uses: actions/upload-artifact@v2
- uses: actions/upload-artifact@v4
with:
name: APK
path: app/build/outputs/apk/debug/
13 changes: 5 additions & 8 deletions .github/workflows/release_build.yml
Original file line number Diff line number Diff line change
Expand Up @@ -12,18 +12,15 @@ jobs:
runs-on: ubuntu-latest

steps:
- uses: actions/checkout@v2
- uses: actions/checkout@v4

- name: set up JDK 11
uses: actions/setup-java@v2
- name: set up JDK
uses: actions/setup-java@v4
with:
java-version: '11'
distribution: 'adopt'
java-version: '21'
distribution: 'temurin'
cache: gradle

- name: grant execute permission for gradlew
run: chmod +x gradlew

- name: Build APK with Gradle
run: ./gradlew assembleDebug

Expand Down
695 changes: 674 additions & 21 deletions LICENSE

Large diffs are not rendered by default.

9 changes: 5 additions & 4 deletions README.md
Original file line number Diff line number Diff line change
@@ -1,8 +1,10 @@
# Augmented Reality Template Matching for >= Android 4.0
# Augmented Reality Template Matching using OpenCV 4 for Android
## Approach: Feature Matching - Brute-Force Matching with ORB Descriptors
[![Android CI](https://github.com/michaeltroger/feature-matching-android/actions/workflows/android.yml/badge.svg)](https://github.com/michaeltroger/feature-matching-android/actions/workflows/android.yml)

Attention: This app was created in 2016. I was a beginner to Android development and Computer Vision back then. So don't expect a perfect code please. In 2021 I updated the project to build with the latest Android Studio (2020.3.1), updated most dependencies and converted it to Kotlin, while the business logic remained unchanged.
Attention: This app was created in 2016. I was a beginner to Android development and Computer Vision back then. So don't expect a perfect code please. Over the years I updated the dependencies and converted it to Kotlin, while the business logic remained unchanged.

Note: Originally I targeted min SDK 15 (Android 4), more architectures ("mips", "mips64", "armeabi") and OpenCV 3 with this project. Nowadays the repo uses newer versions. If you need to support older devices, then you can look back in the repo's Git history (app version 1.1 / Git tag 2)

<img src="/screenshots/demo.gif" alt="" width="800"/>
Copyright of the logo: The Coca-Cola Company
Expand All @@ -12,9 +14,8 @@ Copyright of the logo: The Coca-Cola Company
* More computer vision projects at https://michaeltroger.com/computervision/

### How do I get set up? ###
* IDE: Android Studio (tested with 2020.3.1)
* IDE: Android Studio (tested with 2023.3.1)
* Android SDK
* Dependencies: OpenCV 3 library (included) [License](/opencv-3-4-15/LICENSE)
* Template image location: res/drawable (chooseable in MainActivity)

### Template image ###
Expand Down
36 changes: 14 additions & 22 deletions app/build.gradle
Original file line number Diff line number Diff line change
Expand Up @@ -2,46 +2,38 @@ apply plugin: 'com.android.application'
apply plugin: 'kotlin-android'

android {
compileSdkVersion 30
buildToolsVersion "30.0.3"
compileSdkVersion 34
namespace "com.michaeltroger.featurematching"

defaultConfig {
applicationId "com.michaeltroger.featurematching"
minSdkVersion 15
targetSdkVersion 30
versionCode 2
versionName "1.1"
minSdkVersion 21
targetSdkVersion 34
versionCode 3
versionName "1.2"
}
buildTypes {
release {
minifyEnabled false
proguardFiles getDefaultProguardFile('proguard-android.txt'), 'proguard-rules.pro'
}
}
compileOptions {
sourceCompatibility = JavaVersion.VERSION_11
targetCompatibility = JavaVersion.VERSION_11
}
kotlinOptions {
jvmTarget = "11"
}
splits {
abi {
enable true
reset()
include "x86", "x86_64", "mips", "mips64", "armeabi", "armeabi-v7a", "arm64-v8a"
include "x86", "x86_64", "armeabi-v7a", "arm64-v8a"
universalApk true
}
}
}

kotlin.jvmToolchain(java_version)

dependencies {
implementation 'androidx.activity:activity-ktx:1.3.1'
implementation 'androidx.appcompat:appcompat:1.3.1'
implementation project(':opencv-3-4-15')
implementation 'androidx.core:core-ktx:1.6.0'
implementation "org.jetbrains.kotlin:kotlin-stdlib-jdk7:$kotlin_version"
implementation 'androidx.core:core-ktx:1.12.0'
implementation "org.jetbrains.kotlin:kotlin-stdlib:$kotlin_version"
implementation 'androidx.activity:activity-ktx:1.8.2'
implementation 'androidx.appcompat:appcompat:1.6.1'
implementation 'org.opencv:opencv:4.9.0'
}
repositories {
mavenCentral()
}
13 changes: 7 additions & 6 deletions app/src/main/AndroidManifest.xml
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
<?xml version="1.0" encoding="utf-8"?>
<manifest xmlns:android="http://schemas.android.com/apk/res/android"
package="com.michaeltroger.featurematching">
<manifest xmlns:tools="http://schemas.android.com/tools"
xmlns:android="http://schemas.android.com/apk/res/android">
<uses-permission android:name="android.permission.CAMERA"/>
<uses-feature android:name="android.hardware.camera" />
<uses-feature android:name="android.hardware.camera.autofocus" android:required="false"/>
Expand All @@ -10,10 +10,11 @@
android:icon="@mipmap/ic_launcher"
android:allowBackup="true"
android:label="@string/app_name"
android:theme="@android:style/Theme.Light.NoTitleBar" >
<activity android:name="com.michaeltroger.featurematching.MainActivity"
android:label="@string/app_name"
android:screenOrientation="landscape">
android:theme="@style/AppTheme.NoActionBar" >
<activity android:name=".MainActivity"
android:screenOrientation="landscape"
android:exported="true"
tools:ignore="DiscouragedApi">
<intent-filter>
<action android:name="android.intent.action.MAIN" />
<category android:name="android.intent.category.LAUNCHER" />
Expand Down
154 changes: 66 additions & 88 deletions app/src/main/java/com/michaeltroger/featurematching/MainActivity.kt
Original file line number Diff line number Diff line change
@@ -1,11 +1,8 @@
package com.michaeltroger.featurematching

import android.Manifest
import android.app.Activity
import android.content.pm.PackageManager
import org.opencv.android.CameraBridgeViewBase.CvCameraViewListener2
import org.opencv.features2d.FeatureDetector
import org.opencv.features2d.DescriptorExtractor
import org.opencv.features2d.DescriptorMatcher
import org.opencv.imgcodecs.Imgcodecs
import org.opencv.imgproc.Imgproc
Expand All @@ -21,21 +18,17 @@ import org.opencv.android.*
import org.opencv.android.CameraBridgeViewBase.CvCameraViewFrame
import org.opencv.calib3d.Calib3d
import org.opencv.core.*
import org.opencv.features2d.ORB
import java.io.IOException
import java.util.ArrayList

class MainActivity : ComponentActivity(), CvCameraViewListener2 {
private var mOpenCvCameraView: CameraBridgeViewBase? = null

/**
* The ORB feature detector
* ORB feature detector * ORB descriptor extractor
*/
private var fd: FeatureDetector? = null

/**
* the ORB descriptor extractor
*/
private var de: DescriptorExtractor? = null
private var de: ORB? = null

/**
* the BRUTEFORCE HAMMINGLUT descriptor matcher
Expand Down Expand Up @@ -108,6 +101,12 @@ class MainActivity : ComponentActivity(), CvCameraViewListener2 {
*/
private var intCornersCamera: MatOfPoint? = null


/**
* Mask for the ORB detector
*/
private var mask: Mat? = null

private val requestPermissionLauncher =
registerForActivityResult(
ActivityResultContracts.RequestPermission()
Expand All @@ -129,67 +128,12 @@ class MainActivity : ComponentActivity(), CvCameraViewListener2 {
}

private fun onPermissionGranted() {
mLoaderCallback.onManagerConnected(LoaderCallbackInterface.SUCCESS)
}

private val mLoaderCallback: BaseLoaderCallback = object : BaseLoaderCallback(this) {
override fun onManagerConnected(status: Int) {
when (status) {
SUCCESS -> {
Log.i(TAG, "OpenCV loaded successfully")
mOpenCvCameraView!!.enableView()
fd = FeatureDetector.create(FeatureDetector.ORB)
de = DescriptorExtractor.create(DescriptorExtractor.ORB)
dm = DescriptorMatcher.create(DescriptorMatcher.BRUTEFORCE_HAMMINGLUT)
keyPointsTemplate = MatOfKeyPoint()
keyPointsCamera = MatOfKeyPoint()
descriptorsCamera = Mat()
descriptorsTemplate = Mat()
matches = MatOfDMatch()

// load the specified image from file system in bgr color
var bgr: Mat? = null
try {
bgr = Utils.loadResource(
applicationContext,
TEMPLATE_IMAGE,
Imgcodecs.CV_LOAD_IMAGE_COLOR
)
} catch (e: IOException) {
e.printStackTrace()
}

// convert the loaded image to gray-scale and color
templGray = Mat()
templColor = Mat()
Imgproc.cvtColor(bgr, templGray, Imgproc.COLOR_BGR2GRAY)
Imgproc.cvtColor(bgr, templColor, Imgproc.COLOR_BGR2RGBA)

// pre-calculate features and descriptors of template image
fd!!.detect(templGray, keyPointsTemplate)
de!!.compute(templGray, keyPointsTemplate, descriptorsTemplate)

// set the corners of the template image
cornersTemplate = Mat(4, 1, CvType.CV_32FC2)
cornersTemplate!!.put(0, 0, 0.0, 0.0)
cornersTemplate!!.put(1, 0, templGray!!.cols().toDouble(), 0.0)
cornersTemplate!!.put(
2,
0,
templGray!!.cols().toDouble(),
templGray!!.rows().toDouble()
)
cornersTemplate!!.put(3, 0, 0.0, templGray!!.rows().toDouble())

// init matrices who hold detected corners later
cornersCamera = Mat(4, 1, CvType.CV_32FC2)
intCornersCamera = MatOfPoint()
}
else -> {
super.onManagerConnected(status)
}
}
if (FIXED_FRAME_SIZE) {
mOpenCvCameraView!!.setMaxFrameSize(FRAME_SIZE_WIDTH, FRAME_SIZE_HEIGHT)
}
mOpenCvCameraView!!.visibility = SurfaceView.VISIBLE
mOpenCvCameraView!!.setCvCameraViewListener(this)
mOpenCvCameraView!!.setCameraPermissionGranted()
}

public override fun onCreate(savedInstanceState: Bundle?) {
Expand All @@ -199,11 +143,8 @@ class MainActivity : ComponentActivity(), CvCameraViewListener2 {
setContentView(R.layout.activity_main)
mOpenCvCameraView =
findViewById<View>(R.id.tutorial1_activity_java_surface_view) as CameraBridgeViewBase
if (FIXED_FRAME_SIZE) {
mOpenCvCameraView!!.setMaxFrameSize(FRAME_SIZE_WIDTH, FRAME_SIZE_HEIGHT)
}
mOpenCvCameraView!!.visibility = SurfaceView.VISIBLE
mOpenCvCameraView!!.setCvCameraViewListener(this)

checkPermissonAndInitialize()
}

public override fun onPause() {
Expand All @@ -213,13 +154,55 @@ class MainActivity : ComponentActivity(), CvCameraViewListener2 {

public override fun onResume() {
super.onResume()
if (!OpenCVLoader.initDebug()) {
Log.d(TAG, "Internal OpenCV library not found. Using OpenCV Manager for initialization")
OpenCVLoader.initAsync(OpenCVLoader.OPENCV_VERSION_3_0_0, this, mLoaderCallback)
} else {
Log.d(TAG, "OpenCV library found inside package. Using it!")
checkPermissonAndInitialize()
OpenCVLoader.initLocal()

Log.i(TAG, "OpenCV loaded successfully")
mOpenCvCameraView!!.enableView()
de = ORB.create()
dm = DescriptorMatcher.create(DescriptorMatcher.BRUTEFORCE_HAMMINGLUT)
keyPointsTemplate = MatOfKeyPoint()
keyPointsCamera = MatOfKeyPoint()
descriptorsCamera = Mat()
descriptorsTemplate = Mat()
mask = Mat()
matches = MatOfDMatch()

// load the specified image from file system in bgr color
var bgr: Mat? = null
try {
bgr = Utils.loadResource(
applicationContext,
TEMPLATE_IMAGE,
Imgcodecs.IMREAD_COLOR
)
} catch (e: IOException) {
e.printStackTrace()
}

// convert the loaded image to gray-scale and color
templGray = Mat()
templColor = Mat()
Imgproc.cvtColor(bgr, templGray, Imgproc.COLOR_BGR2GRAY)
Imgproc.cvtColor(bgr, templColor, Imgproc.COLOR_BGR2RGBA)

// pre-calculate features and descriptors of template image
de!!.detectAndCompute(templGray, mask, keyPointsTemplate, descriptorsTemplate)

// set the corners of the template image
cornersTemplate = Mat(4, 1, CvType.CV_32FC2)
cornersTemplate!!.put(0, 0, 0.0, 0.0)
cornersTemplate!!.put(1, 0, templGray!!.cols().toDouble(), 0.0)
cornersTemplate!!.put(
2,
0,
templGray!!.cols().toDouble(),
templGray!!.rows().toDouble()
)
cornersTemplate!!.put(3, 0, 0.0, templGray!!.rows().toDouble())

// init matrices who hold detected corners later
cornersCamera = Mat(4, 1, CvType.CV_32FC2)
intCornersCamera = MatOfPoint()
}

public override fun onDestroy() {
Expand All @@ -243,12 +226,7 @@ class MainActivity : ComponentActivity(), CvCameraViewListener2 {
* responsible for keypoint matching - searches for template images within scene
*/
private fun keypointMatching() {
fd!!.detect(mRgb, keyPointsCamera)
if (keyPointsCamera!!.empty()) {
Log.e(TAG, "no keypoints in camera scene")
return
}
de!!.compute(mRgb, keyPointsCamera, descriptorsCamera)
de!!.detectAndCompute(mRgb, mask, keyPointsCamera, descriptorsCamera)
if (descriptorsCamera!!.empty()) {
Log.e(TAG, "no descriptors in camera scene")
return
Expand Down Expand Up @@ -334,7 +312,7 @@ class MainActivity : ComponentActivity(), CvCameraViewListener2 {
/**
* The template image to use
*/
private const val TEMPLATE_IMAGE = R.drawable.coca_cola
private val TEMPLATE_IMAGE = R.drawable.coca_cola

/**
* frame size width
Expand Down
10 changes: 10 additions & 0 deletions app/src/main/res/values/styles.xml
Original file line number Diff line number Diff line change
@@ -0,0 +1,10 @@
<?xml version="1.0" encoding="utf-8"?>
<resources>

<style name="AppTheme.NoActionBar" parent="Theme.AppCompat.Light.DarkActionBar">
<item name="windowActionBar">false</item>
<item name="windowNoTitle">true</item>
<item name="android:windowFullscreen">true</item>
</style>

</resources>
Loading

0 comments on commit 3e97794

Please sign in to comment.