Merge pull request #2334 from HydrogenSulfate/cp_android_demo
[cherry pick]Add shitu android demo2pull/2336/head
|
@ -0,0 +1,183 @@
|
|||
<?xml version="1.0" encoding="UTF-8"?>
|
||||
<module external.linked.project.id=":app" external.linked.project.path="$MODULE_DIR$" external.root.project.path="$MODULE_DIR$/.." external.system.id="GRADLE" type="JAVA_MODULE" version="4">
|
||||
<component name="FacetManager">
|
||||
<facet type="android-gradle" name="Android-Gradle">
|
||||
<configuration>
|
||||
<option name="GRADLE_PROJECT_PATH" value=":app" />
|
||||
</configuration>
|
||||
</facet>
|
||||
<facet type="native-android-gradle" name="Native-Android-Gradle">
|
||||
<configuration>
|
||||
<option name="SELECTED_BUILD_VARIANT" value="debug-arm64-v8a" />
|
||||
</configuration>
|
||||
</facet>
|
||||
<facet type="android" name="Android">
|
||||
<configuration>
|
||||
<option name="SELECTED_BUILD_VARIANT" value="debug" />
|
||||
<option name="ASSEMBLE_TASK_NAME" value="assembleDebug" />
|
||||
<option name="COMPILE_JAVA_TASK_NAME" value="compileDebugSources" />
|
||||
<afterSyncTasks>
|
||||
<task>generateDebugSources</task>
|
||||
</afterSyncTasks>
|
||||
<option name="ALLOW_USER_CONFIGURATION" value="false" />
|
||||
<option name="MANIFEST_FILE_RELATIVE_PATH" value="/src/main/AndroidManifest.xml" />
|
||||
<option name="RES_FOLDER_RELATIVE_PATH" value="/src/main/res" />
|
||||
<option name="RES_FOLDERS_RELATIVE_PATH" value="file://$MODULE_DIR$/src/main/res" />
|
||||
<option name="TEST_RES_FOLDERS_RELATIVE_PATH" value="" />
|
||||
<option name="ASSETS_FOLDER_RELATIVE_PATH" value="/src/main/assets" />
|
||||
</configuration>
|
||||
</facet>
|
||||
</component>
|
||||
<component name="NewModuleRootManager" LANGUAGE_LEVEL="JDK_1_7">
|
||||
<output url="file://$MODULE_DIR$/build/intermediates/javac/debug/compileDebugJavaWithJavac/classes" />
|
||||
<output-test url="file://$MODULE_DIR$/build/intermediates/javac/debugUnitTest/compileDebugUnitTestJavaWithJavac/classes" />
|
||||
<exclude-output />
|
||||
<content url="file://$MODULE_DIR$">
|
||||
<sourceFolder url="file://$MODULE_DIR$/src/main/cpp" isTestSource="false" />
|
||||
<sourceFolder url="file://$MODULE_DIR$/build/generated/source/apt/debug" isTestSource="false" generated="true" />
|
||||
<sourceFolder url="file://$MODULE_DIR$/build/generated/aidl_source_output_dir/debug/compileDebugAidl/out" isTestSource="false" generated="true" />
|
||||
<sourceFolder url="file://$MODULE_DIR$/build/generated/source/buildConfig/debug" isTestSource="false" generated="true" />
|
||||
<sourceFolder url="file://$MODULE_DIR$/build/generated/renderscript_source_output_dir/debug/compileDebugRenderscript/out" isTestSource="false" generated="true" />
|
||||
<sourceFolder url="file://$MODULE_DIR$/build/generated/res/rs/debug" type="java-resource" />
|
||||
<sourceFolder url="file://$MODULE_DIR$/build/generated/res/resValues/debug" type="java-resource" />
|
||||
<sourceFolder url="file://$MODULE_DIR$/build/generated/source/apt/androidTest/debug" isTestSource="true" generated="true" />
|
||||
<sourceFolder url="file://$MODULE_DIR$/build/generated/aidl_source_output_dir/debugAndroidTest/compileDebugAndroidTestAidl/out" isTestSource="true" generated="true" />
|
||||
<sourceFolder url="file://$MODULE_DIR$/build/generated/source/buildConfig/androidTest/debug" isTestSource="true" generated="true" />
|
||||
<sourceFolder url="file://$MODULE_DIR$/build/generated/renderscript_source_output_dir/debugAndroidTest/compileDebugAndroidTestRenderscript/out" isTestSource="true" generated="true" />
|
||||
<sourceFolder url="file://$MODULE_DIR$/build/generated/res/rs/androidTest/debug" type="java-test-resource" />
|
||||
<sourceFolder url="file://$MODULE_DIR$/build/generated/res/resValues/androidTest/debug" type="java-test-resource" />
|
||||
<sourceFolder url="file://$MODULE_DIR$/build/generated/source/apt/test/debug" isTestSource="true" generated="true" />
|
||||
<sourceFolder url="file://$MODULE_DIR$/src/debug/res" type="java-resource" />
|
||||
<sourceFolder url="file://$MODULE_DIR$/src/debug/resources" type="java-resource" />
|
||||
<sourceFolder url="file://$MODULE_DIR$/src/debug/assets" type="java-resource" />
|
||||
<sourceFolder url="file://$MODULE_DIR$/src/debug/aidl" isTestSource="false" />
|
||||
<sourceFolder url="file://$MODULE_DIR$/src/debug/java" isTestSource="false" />
|
||||
<sourceFolder url="file://$MODULE_DIR$/src/debug/jni" isTestSource="false" />
|
||||
<sourceFolder url="file://$MODULE_DIR$/src/debug/rs" isTestSource="false" />
|
||||
<sourceFolder url="file://$MODULE_DIR$/src/debug/shaders" isTestSource="false" />
|
||||
<sourceFolder url="file://$MODULE_DIR$/src/androidTestDebug/res" type="java-test-resource" />
|
||||
<sourceFolder url="file://$MODULE_DIR$/src/androidTestDebug/resources" type="java-test-resource" />
|
||||
<sourceFolder url="file://$MODULE_DIR$/src/androidTestDebug/assets" type="java-test-resource" />
|
||||
<sourceFolder url="file://$MODULE_DIR$/src/androidTestDebug/aidl" isTestSource="true" />
|
||||
<sourceFolder url="file://$MODULE_DIR$/src/androidTestDebug/java" isTestSource="true" />
|
||||
<sourceFolder url="file://$MODULE_DIR$/src/androidTestDebug/jni" isTestSource="true" />
|
||||
<sourceFolder url="file://$MODULE_DIR$/src/androidTestDebug/rs" isTestSource="true" />
|
||||
<sourceFolder url="file://$MODULE_DIR$/src/androidTestDebug/shaders" isTestSource="true" />
|
||||
<sourceFolder url="file://$MODULE_DIR$/src/testDebug/res" type="java-test-resource" />
|
||||
<sourceFolder url="file://$MODULE_DIR$/src/testDebug/resources" type="java-test-resource" />
|
||||
<sourceFolder url="file://$MODULE_DIR$/src/testDebug/assets" type="java-test-resource" />
|
||||
<sourceFolder url="file://$MODULE_DIR$/src/testDebug/aidl" isTestSource="true" />
|
||||
<sourceFolder url="file://$MODULE_DIR$/src/testDebug/java" isTestSource="true" />
|
||||
<sourceFolder url="file://$MODULE_DIR$/src/testDebug/jni" isTestSource="true" />
|
||||
<sourceFolder url="file://$MODULE_DIR$/src/testDebug/rs" isTestSource="true" />
|
||||
<sourceFolder url="file://$MODULE_DIR$/src/testDebug/shaders" isTestSource="true" />
|
||||
<sourceFolder url="file://$MODULE_DIR$/src/main/res" type="java-resource" />
|
||||
<sourceFolder url="file://$MODULE_DIR$/src/main/resources" type="java-resource" />
|
||||
<sourceFolder url="file://$MODULE_DIR$/src/main/assets" type="java-resource" />
|
||||
<sourceFolder url="file://$MODULE_DIR$/src/main/aidl" isTestSource="false" />
|
||||
<sourceFolder url="file://$MODULE_DIR$/src/main/java" isTestSource="false" />
|
||||
<sourceFolder url="file://$MODULE_DIR$/src/main/jni" isTestSource="false" />
|
||||
<sourceFolder url="file://$MODULE_DIR$/src/main/rs" isTestSource="false" />
|
||||
<sourceFolder url="file://$MODULE_DIR$/src/main/shaders" isTestSource="false" />
|
||||
<sourceFolder url="file://$MODULE_DIR$/src/androidTest/res" type="java-test-resource" />
|
||||
<sourceFolder url="file://$MODULE_DIR$/src/androidTest/resources" type="java-test-resource" />
|
||||
<sourceFolder url="file://$MODULE_DIR$/src/androidTest/assets" type="java-test-resource" />
|
||||
<sourceFolder url="file://$MODULE_DIR$/src/androidTest/aidl" isTestSource="true" />
|
||||
<sourceFolder url="file://$MODULE_DIR$/src/androidTest/java" isTestSource="true" />
|
||||
<sourceFolder url="file://$MODULE_DIR$/src/androidTest/jni" isTestSource="true" />
|
||||
<sourceFolder url="file://$MODULE_DIR$/src/androidTest/rs" isTestSource="true" />
|
||||
<sourceFolder url="file://$MODULE_DIR$/src/androidTest/shaders" isTestSource="true" />
|
||||
<sourceFolder url="file://$MODULE_DIR$/src/test/res" type="java-test-resource" />
|
||||
<sourceFolder url="file://$MODULE_DIR$/src/test/resources" type="java-test-resource" />
|
||||
<sourceFolder url="file://$MODULE_DIR$/src/test/assets" type="java-test-resource" />
|
||||
<sourceFolder url="file://$MODULE_DIR$/src/test/aidl" isTestSource="true" />
|
||||
<sourceFolder url="file://$MODULE_DIR$/src/test/java" isTestSource="true" />
|
||||
<sourceFolder url="file://$MODULE_DIR$/src/test/jni" isTestSource="true" />
|
||||
<sourceFolder url="file://$MODULE_DIR$/src/test/rs" isTestSource="true" />
|
||||
<sourceFolder url="file://$MODULE_DIR$/src/test/shaders" isTestSource="true" />
|
||||
<excludeFolder url="file://$MODULE_DIR$/.externalNativeBuild" />
|
||||
<excludeFolder url="file://$MODULE_DIR$/build/generated/not_namespaced_r_class_sources" />
|
||||
<excludeFolder url="file://$MODULE_DIR$/build/generated/source/r" />
|
||||
<excludeFolder url="file://$MODULE_DIR$/build/intermediates/annotation_processor_list" />
|
||||
<excludeFolder url="file://$MODULE_DIR$/build/intermediates/apk_list" />
|
||||
<excludeFolder url="file://$MODULE_DIR$/build/intermediates/blame" />
|
||||
<excludeFolder url="file://$MODULE_DIR$/build/intermediates/bundle_manifest" />
|
||||
<excludeFolder url="file://$MODULE_DIR$/build/intermediates/check_manifest_result" />
|
||||
<excludeFolder url="file://$MODULE_DIR$/build/intermediates/cmake" />
|
||||
<excludeFolder url="file://$MODULE_DIR$/build/intermediates/compatible_screen_manifest" />
|
||||
<excludeFolder url="file://$MODULE_DIR$/build/intermediates/dex" />
|
||||
<excludeFolder url="file://$MODULE_DIR$/build/intermediates/duplicate_classes_check" />
|
||||
<excludeFolder url="file://$MODULE_DIR$/build/intermediates/external_libs_dex" />
|
||||
<excludeFolder url="file://$MODULE_DIR$/build/intermediates/incremental" />
|
||||
<excludeFolder url="file://$MODULE_DIR$/build/intermediates/instant_app_manifest" />
|
||||
<excludeFolder url="file://$MODULE_DIR$/build/intermediates/instant_run_merged_manifests" />
|
||||
<excludeFolder url="file://$MODULE_DIR$/build/intermediates/javac" />
|
||||
<excludeFolder url="file://$MODULE_DIR$/build/intermediates/lint_jar" />
|
||||
<excludeFolder url="file://$MODULE_DIR$/build/intermediates/merged_assets" />
|
||||
<excludeFolder url="file://$MODULE_DIR$/build/intermediates/merged_jni_libs" />
|
||||
<excludeFolder url="file://$MODULE_DIR$/build/intermediates/merged_manifests" />
|
||||
<excludeFolder url="file://$MODULE_DIR$/build/intermediates/merged_shaders" />
|
||||
<excludeFolder url="file://$MODULE_DIR$/build/intermediates/metadata_feature_manifest" />
|
||||
<excludeFolder url="file://$MODULE_DIR$/build/intermediates/prebuild" />
|
||||
<excludeFolder url="file://$MODULE_DIR$/build/intermediates/processed_res" />
|
||||
<excludeFolder url="file://$MODULE_DIR$/build/intermediates/res" />
|
||||
<excludeFolder url="file://$MODULE_DIR$/build/intermediates/shader_assets" />
|
||||
<excludeFolder url="file://$MODULE_DIR$/build/intermediates/signing_config" />
|
||||
<excludeFolder url="file://$MODULE_DIR$/build/intermediates/symbols" />
|
||||
<excludeFolder url="file://$MODULE_DIR$/build/intermediates/transforms" />
|
||||
<excludeFolder url="file://$MODULE_DIR$/build/intermediates/validate_signing_config" />
|
||||
<excludeFolder url="file://$MODULE_DIR$/build/outputs" />
|
||||
<excludeFolder url="file://$MODULE_DIR$/build/tmp" />
|
||||
</content>
|
||||
<orderEntry type="jdk" jdkName="Android API 28 Platform" jdkType="Android SDK" />
|
||||
<orderEntry type="sourceFolder" forTests="false" />
|
||||
<orderEntry type="library" name="Gradle: com.android.support:design:28.0.0@aar" level="project" />
|
||||
<orderEntry type="library" name="Gradle: com.android.support:support-fragment:28.0.0@aar" level="project" />
|
||||
<orderEntry type="library" name="Gradle: com.android.support:localbroadcastmanager:28.0.0@aar" level="project" />
|
||||
<orderEntry type="library" name="Gradle: com.android.support:documentfile:28.0.0@aar" level="project" />
|
||||
<orderEntry type="library" name="Gradle: com.android.support:animated-vector-drawable:28.0.0@aar" level="project" />
|
||||
<orderEntry type="library" name="Gradle: com.android.support:slidingpanelayout:28.0.0@aar" level="project" />
|
||||
<orderEntry type="library" name="Gradle: android.arch.lifecycle:viewmodel:1.1.1@aar" level="project" />
|
||||
<orderEntry type="library" name="Gradle: com.android.support:appcompat-v7:28.0.0@aar" level="project" />
|
||||
<orderEntry type="library" scope="TEST" name="Gradle: com.android.support.test.espresso:espresso-idling-resource:3.0.2@aar" level="project" />
|
||||
<orderEntry type="library" name="Gradle: com.android.support:loader:28.0.0@aar" level="project" />
|
||||
<orderEntry type="library" name="Gradle: android.arch.core:runtime:1.1.1@aar" level="project" />
|
||||
<orderEntry type="library" scope="TEST" name="Gradle: com.android.support.test:runner:1.0.2@aar" level="project" />
|
||||
<orderEntry type="library" name="Gradle: android.arch.lifecycle:livedata-core:1.1.1@aar" level="project" />
|
||||
<orderEntry type="library" name="Gradle: com.android.support:collections:28.0.0@jar" level="project" />
|
||||
<orderEntry type="library" scope="TEST" name="Gradle: com.squareup:javawriter:2.1.1@jar" level="project" />
|
||||
<orderEntry type="library" name="Gradle: com.android.support:cursoradapter:28.0.0@aar" level="project" />
|
||||
<orderEntry type="library" name="Gradle: android.arch.lifecycle:runtime:1.1.1@aar" level="project" />
|
||||
<orderEntry type="library" scope="TEST" name="Gradle: com.android.support.test.espresso:espresso-core:3.0.2@aar" level="project" />
|
||||
<orderEntry type="library" name="Gradle: com.android.support:support-compat:28.0.0@aar" level="project" />
|
||||
<orderEntry type="library" name="Gradle: com.android.support:support-core-ui:28.0.0@aar" level="project" />
|
||||
<orderEntry type="library" name="Gradle: com.android.support:cardview-v7:28.0.0@aar" level="project" />
|
||||
<orderEntry type="library" name="Gradle: com.android.support:asynclayoutinflater:28.0.0@aar" level="project" />
|
||||
<orderEntry type="library" name="Gradle: com.android.support.constraint:constraint-layout:1.1.3@aar" level="project" />
|
||||
<orderEntry type="library" name="Gradle: com.android.support:print:28.0.0@aar" level="project" />
|
||||
<orderEntry type="library" scope="TEST" name="Gradle: com.google.code.findbugs:jsr305:2.0.1@jar" level="project" />
|
||||
<orderEntry type="library" scope="TEST" name="Gradle: javax.inject:javax.inject:1@jar" level="project" />
|
||||
<orderEntry type="library" name="Gradle: android.arch.core:common:1.1.1@jar" level="project" />
|
||||
<orderEntry type="library" name="Gradle: com.android.support:versionedparcelable:28.0.0@aar" level="project" />
|
||||
<orderEntry type="library" scope="TEST" name="Gradle: junit:junit:4.12@jar" level="project" />
|
||||
<orderEntry type="library" scope="TEST" name="Gradle: org.hamcrest:hamcrest-core:1.3@jar" level="project" />
|
||||
<orderEntry type="library" name="Gradle: com.android.support:viewpager:28.0.0@aar" level="project" />
|
||||
<orderEntry type="library" name="Gradle: com.android.support:support-core-utils:28.0.0@aar" level="project" />
|
||||
<orderEntry type="library" name="Gradle: com.android.support:recyclerview-v7:28.0.0@aar" level="project" />
|
||||
<orderEntry type="library" name="Gradle: com.android.support:support-vector-drawable:28.0.0@aar" level="project" />
|
||||
<orderEntry type="library" scope="TEST" name="Gradle: com.android.support.test:monitor:1.0.2@aar" level="project" />
|
||||
<orderEntry type="library" scope="TEST" name="Gradle: org.hamcrest:hamcrest-library:1.3@jar" level="project" />
|
||||
<orderEntry type="library" scope="TEST" name="Gradle: org.hamcrest:hamcrest-integration:1.3@jar" level="project" />
|
||||
<orderEntry type="library" name="Gradle: com.android.support:support-annotations:28.0.0@jar" level="project" />
|
||||
<orderEntry type="library" name="Gradle: android.arch.lifecycle:common:1.1.1@jar" level="project" />
|
||||
<orderEntry type="library" name="Gradle: com.android.support:interpolator:28.0.0@aar" level="project" />
|
||||
<orderEntry type="library" name="Gradle: com.android.support:transition:28.0.0@aar" level="project" />
|
||||
<orderEntry type="library" name="Gradle: android.arch.lifecycle:livedata:1.1.1@aar" level="project" />
|
||||
<orderEntry type="library" name="Gradle: com.android.support:drawerlayout:28.0.0@aar" level="project" />
|
||||
<orderEntry type="library" name="Gradle: com.android.support:coordinatorlayout:28.0.0@aar" level="project" />
|
||||
<orderEntry type="library" name="Gradle: com.android.support:customview:28.0.0@aar" level="project" />
|
||||
<orderEntry type="library" name="Gradle: com.android.support:swiperefreshlayout:28.0.0@aar" level="project" />
|
||||
<orderEntry type="library" name="Gradle: com.android.support.constraint:constraint-layout-solver:1.1.3@jar" level="project" />
|
||||
<orderEntry type="library" scope="TEST" name="Gradle: net.sf.kxml:kxml2:2.3.0@jar" level="project" />
|
||||
</component>
|
||||
</module>
|
|
@ -0,0 +1,93 @@
|
|||
import java.security.MessageDigest
|
||||
|
||||
apply plugin: 'com.android.application'
|
||||
|
||||
android {
|
||||
compileSdkVersion 30
|
||||
defaultConfig {
|
||||
applicationId "com.baidu.paddle.lite.demo.pp_shitu"
|
||||
minSdkVersion 15
|
||||
targetSdkVersion 30
|
||||
versionCode 1
|
||||
versionName "1.0"
|
||||
testInstrumentationRunner "android.support.test.runner.AndroidJUnitRunner"
|
||||
externalNativeBuild {
|
||||
cmake {
|
||||
arguments '-DANDROID_PLATFORM=android-23', '-DANDROID_STL=c++_shared', "-DANDROID_TOOLCHAIN="
|
||||
abiFilters 'arm64-v8a'
|
||||
cppFlags "-std=c++11"
|
||||
}
|
||||
}
|
||||
}
|
||||
buildTypes {
|
||||
release {
|
||||
minifyEnabled false
|
||||
proguardFiles getDefaultProguardFile('proguard-android-optimize.txt'), 'proguard-rules.pro'
|
||||
}
|
||||
}
|
||||
externalNativeBuild {
|
||||
cmake {
|
||||
path "src/main/cpp/CMakeLists.txt"
|
||||
version "3.18.1"
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
dependencies {
|
||||
implementation fileTree(include: ['*.jar'], dir: 'libs')
|
||||
implementation 'com.android.support:appcompat-v7:28.0.0'
|
||||
implementation 'com.android.support.constraint:constraint-layout:1.1.3'
|
||||
implementation 'com.android.support:design:28.0.0'
|
||||
testImplementation 'junit:junit:4.12'
|
||||
androidTestImplementation 'com.android.support.test:runner:1.0.2'
|
||||
androidTestImplementation 'com.android.support.test.espresso:espresso-core:3.0.2'
|
||||
}
|
||||
tasks.withType(JavaCompile) {
|
||||
options.encoding = "UTF-8"
|
||||
}
|
||||
def archives = [
|
||||
[
|
||||
'src' : 'https://paddlelite-demo.bj.bcebos.com/libs/android/paddle_lite_libs_v2_10_gpu.tar.gz',
|
||||
'dest': 'PaddleLite'
|
||||
],
|
||||
[
|
||||
'src' : 'https://paddlelite-demo.bj.bcebos.com/libs/android/opencv-4.2.0-android-sdk.tar.gz',
|
||||
'dest': 'OpenCV'
|
||||
],
|
||||
[
|
||||
'src' : 'https://paddlelite-demo.bj.bcebos.com/demo/PP_shitu/models/ppshitu_lite_models_v1.0.tar.gz',
|
||||
'dest' : 'src/main/assets/models'
|
||||
]
|
||||
]
|
||||
|
||||
task downloadAndExtractArchives(type: DefaultTask) {
|
||||
doFirst {
|
||||
println "Downloading and extracting archives including libs and models"
|
||||
}
|
||||
doLast {
|
||||
// Prepare cache folder for archives
|
||||
String cachePath = "cache"
|
||||
if (!file("${cachePath}").exists()) {
|
||||
mkdir "${cachePath}"
|
||||
}
|
||||
archives.eachWithIndex { archive, index ->
|
||||
MessageDigest messageDigest = MessageDigest.getInstance('MD5')
|
||||
messageDigest.update(archive.src.bytes)
|
||||
String cacheName = new BigInteger(1, messageDigest.digest()).toString(32)
|
||||
// Download the target archive if not exists
|
||||
boolean copyFiles = !file("${archive.dest}").exists()
|
||||
if (!file("${cachePath}/${cacheName}.tar.gz").exists()) {
|
||||
ant.get(src: archive.src, dest: file("${cachePath}/${cacheName}.tar.gz"))
|
||||
copyFiles = true; // force to copy files from the latest archive files
|
||||
}
|
||||
// Extract the target archive if its dest path does not exists
|
||||
if (copyFiles) {
|
||||
copy {
|
||||
from tarTree("${cachePath}/${cacheName}.tar.gz")
|
||||
into "${archive.dest}"
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
preBuild.dependsOn downloadAndExtractArchives
|
|
@ -0,0 +1,21 @@
|
|||
# Add project specific ProGuard rules here.
|
||||
# You can control the set of applied configuration files using the
|
||||
# proguardFiles setting in build.gradle.
|
||||
#
|
||||
# For more details, see
|
||||
# http://developer.android.com/guide/developing/tools/proguard.html
|
||||
|
||||
# If your project uses WebView with JS, uncomment the following
|
||||
# and specify the fully qualified class name to the JavaScript interface
|
||||
# class:
|
||||
#-keepclassmembers class fqcn.of.javascript.interface.for.webview {
|
||||
# public *;
|
||||
#}
|
||||
|
||||
# Uncomment this to preserve the line number information for
|
||||
# debugging stack traces.
|
||||
#-keepattributes SourceFile,LineNumberTable
|
||||
|
||||
# If you keep the line number information, uncomment this to
|
||||
# hide the original source file name.
|
||||
#-renamesourcefileattribute SourceFile
|
|
@ -0,0 +1,26 @@
|
|||
package com.baidu.paddle.lite.demo.pp_shitu;
|
||||
|
||||
import android.content.Context;
|
||||
import android.support.test.InstrumentationRegistry;
|
||||
import android.support.test.runner.AndroidJUnit4;
|
||||
|
||||
import org.junit.Test;
|
||||
import org.junit.runner.RunWith;
|
||||
|
||||
import static org.junit.Assert.*;
|
||||
|
||||
/**
|
||||
* Instrumented test, which will execute on an Android device.
|
||||
*
|
||||
* @see <a href="http://d.android.com/tools/testing">Testing documentation</a>
|
||||
*/
|
||||
@RunWith(AndroidJUnit4.class)
|
||||
public class ExampleInstrumentedTest {
|
||||
@Test
|
||||
public void useAppContext() {
|
||||
// Context of the app under test.
|
||||
Context appContext = InstrumentationRegistry.getTargetContext();
|
||||
|
||||
assertEquals("com.baidu.paddle.lite.demo", appContext.getPackageName());
|
||||
}
|
||||
}
|
|
@ -0,0 +1,34 @@
|
|||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<manifest xmlns:android="http://schemas.android.com/apk/res/android"
|
||||
xmlns:tools="http://schemas.android.com/tools"
|
||||
package="com.baidu.paddle.lite.demo.pp_shitu">
|
||||
|
||||
<uses-permission android:name="android.permission.WRITE_EXTERNAL_STORAGE"
|
||||
tools:ignore="ScopedStorage" />
|
||||
<uses-permission android:name="android.permission.READ_EXTERNAL_STORAGE"/>
|
||||
<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"/>
|
||||
|
||||
<application
|
||||
android:allowBackup="true"
|
||||
android:icon="@mipmap/ic_launcher"
|
||||
android:label="@string/app_name"
|
||||
android:roundIcon="@mipmap/ic_launcher_round"
|
||||
android:supportsRtl="true"
|
||||
android:theme="@style/AppTheme">
|
||||
<activity android:name="com.baidu.paddle.lite.demo.pp_shitu.MainActivity"
|
||||
android:exported="true">
|
||||
<intent-filter>
|
||||
<action android:name="android.intent.action.MAIN"/>
|
||||
|
||||
<category android:name="android.intent.category.LAUNCHER"/>
|
||||
</intent-filter>
|
||||
</activity>
|
||||
<activity
|
||||
android:name="com.baidu.paddle.lite.demo.pp_shitu.SettingsActivity"
|
||||
android:label="Settings">
|
||||
</activity>
|
||||
</application>
|
||||
|
||||
</manifest>
|
After Width: | Height: | Size: 346 KiB |
|
@ -0,0 +1 @@
|
|||
put `*.index` and `*.txt` here. such as `original.index` and `original.txt`
|
|
@ -0,0 +1 @@
|
|||
put `*.nb` inference model file there. such as `general_PPLCNetV2_base_quant_v1.0_lite.nb` and `mainbody_PPLCNet_x2_5_640_quant_v1.0_lite.nb`
|
|
@ -0,0 +1,105 @@
|
|||
# For more information about using CMake with Android Studio, read the
|
||||
# documentation: https://d.android.com/studio/projects/add-native-code.html
|
||||
|
||||
# Sets the minimum version of CMake required to build the native library.
|
||||
#project(ShituDemo)
|
||||
|
||||
cmake_minimum_required(VERSION 3.4.1)
|
||||
|
||||
# Creates and names a library, sets it as either STATIC or SHARED, and provides
|
||||
# the relative paths to its source code. You can define multiple libraries, and
|
||||
# CMake builds them for you. Gradle automatically packages shared libraries with
|
||||
# your APK.
|
||||
|
||||
set(PaddleLite_DIR "${CMAKE_CURRENT_SOURCE_DIR}/../../../PaddleLite")
|
||||
include_directories(${PaddleLite_DIR}/cxx/include)
|
||||
|
||||
set(OpenCV_DIR "${CMAKE_CURRENT_SOURCE_DIR}/../../../OpenCV/sdk/native/jni")
|
||||
find_package(OpenCV REQUIRED)
|
||||
message(STATUS "OpenCV libraries: ${OpenCV_LIBS}")
|
||||
include_directories(${OpenCV_INCLUDE_DIRS})
|
||||
|
||||
#set(PaddleLite_DIR "${CMAKE_CURRENT_SOURCE_DIR}/../../../PaddleLite")
|
||||
|
||||
include_directories(${CMAKE_CURRENT_SOURCE_DIR}/../../../PaddleLite/src/main/cpp/include/faiss)
|
||||
set(target faiss)
|
||||
|
||||
set(CMAKE_CXX_FLAGS
|
||||
"${CMAKE_CXX_FLAGS} -ffast-math -Ofast -Os -DNDEBUG -fexceptions -fomit-frame-pointer -fno-asynchronous-unwind-tables -fno-unwind-tables"
|
||||
)
|
||||
set(CMAKE_CXX_FLAGS
|
||||
"${CMAKE_CXX_FLAGS} -fvisibility=hidden -fvisibility-inlines-hidden -fdata-sections -ffunction-sections"
|
||||
)
|
||||
set(CMAKE_SHARED_LINKER_FLAGS
|
||||
"${CMAKE_SHARED_LINKER_FLAGS} -Wl,--gc-sections -Wl,-z,nocopyreloc")
|
||||
|
||||
add_library(
|
||||
# Sets the name of the library.
|
||||
Native
|
||||
# Sets the library as a shared library.
|
||||
SHARED
|
||||
# Provides a relative path to your source file(s).
|
||||
Native.cc Pipeline.cc Utils.cc ObjectDetector.cc FeatureExtractor.cc VectorSearch.cc)
|
||||
|
||||
find_library(
|
||||
# Sets the name of the path variable.
|
||||
log-lib
|
||||
# Specifies the name of the NDK library that you want CMake to locate.
|
||||
log)
|
||||
|
||||
add_library(
|
||||
# Sets the name of the library.
|
||||
paddle_light_api_shared
|
||||
# Sets the library as a shared library.
|
||||
SHARED
|
||||
# Provides a relative path to your source file(s).
|
||||
IMPORTED)
|
||||
|
||||
add_library(
|
||||
# Sets the name of the library.
|
||||
faiss
|
||||
STATIC
|
||||
IMPORTED)
|
||||
|
||||
set_target_properties(
|
||||
# Specifies the target library.
|
||||
paddle_light_api_shared
|
||||
# Specifies the parameter you want to define.
|
||||
PROPERTIES
|
||||
IMPORTED_LOCATION
|
||||
${PaddleLite_DIR}/cxx/libs/${ANDROID_ABI}/libpaddle_light_api_shared.so
|
||||
# Provides the path to the library you want to import.
|
||||
)
|
||||
|
||||
# if there libfaiss.a not exist, will download it automatically
|
||||
IF(NOT ${CMAKE_CURRENT_SOURCE_DIR}/../../../src/main/jniLibs/arm64-v8a/libfaiss.a)
|
||||
message(STATUS "Downloading ${OCI_LIB_ZIP_NAME} to ${CMAKE_CURRENT_SOURCE_DIR}/../../../src/main/jniLibs/arm64-v8a/")
|
||||
FILE(DOWNLOAD https://paddle-imagenet-models-name.bj.bcebos.com/demos/lib/libfaiss.a
|
||||
${CMAKE_CURRENT_SOURCE_DIR}/../../../src/main/jniLibs/arm64-v8a/libfaiss.a
|
||||
TIMEOUT ${DOWNLOAD_OCI_LIB_TIMEOUT}
|
||||
STATUS ERR
|
||||
SHOW_PROGRESS)
|
||||
ENDIF()
|
||||
|
||||
set_target_properties(
|
||||
faiss
|
||||
PROPERTIES
|
||||
IMPORTED_LOCATION
|
||||
${CMAKE_CURRENT_SOURCE_DIR}/../../../src/main/jniLibs/arm64-v8a/libfaiss.a
|
||||
)
|
||||
|
||||
# Specifies libraries CMake should link to your target library. You can link
|
||||
# multiple libraries, such as libraries you define in this build script,
|
||||
# prebuilt third-party libraries, or system libraries.
|
||||
|
||||
target_link_libraries(
|
||||
# Specifies the target library.
|
||||
Native
|
||||
paddle_light_api_shared
|
||||
jnigraphics
|
||||
${OpenCV_LIBS}
|
||||
GLESv2
|
||||
EGL
|
||||
${log-lib}
|
||||
faiss
|
||||
)
|
|
@ -0,0 +1,98 @@
|
|||
// Copyright (c) 2022 PaddlePaddle Authors. All Rights Reserved.
|
||||
//
|
||||
// Licensed under the Apache License, Version 2.0 (the "License");
|
||||
// you may not use this file except in compliance with the License.
|
||||
// You may obtain a copy of the License at
|
||||
//
|
||||
// http://www.apache.org/licenses/LICENSE-2.0
|
||||
//
|
||||
// Unless required by applicable law or agreed to in writing, software
|
||||
// distributed under the License is distributed on an "AS IS" BASIS,
|
||||
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
// See the License for the specific language governing permissions and
|
||||
// limitations under the License.
|
||||
|
||||
#include "FeatureExtractor.h" // NOLINT
|
||||
#include <utility> // NOLINT
|
||||
|
||||
void FeatureExtract::RunRecModel(const cv::Mat &img, double &cost_time,
|
||||
std::vector<float> &feature) {
|
||||
// Read img
|
||||
cv::Mat img_fp;
|
||||
ResizeImage(img, img_fp);
|
||||
NormalizeImage(&img_fp, this->mean_, this->std_, this->scale_);
|
||||
std::vector<float> input(1 * 3 * img_fp.rows * img_fp.cols, 0.0f);
|
||||
Permute(&img_fp, input.data());
|
||||
auto pre_cost0 = GetCurrentUS();
|
||||
|
||||
// Prepare input data from image
|
||||
std::unique_ptr<Tensor> input_tensor(
|
||||
std::move(this->predictor_->GetInput(0)));
|
||||
input_tensor->Resize({1, 3, this->size, this->size});
|
||||
auto *data0 = input_tensor->mutable_data<float>();
|
||||
|
||||
for (int i = 0; i < input.size(); ++i) {
|
||||
data0[i] = input[i];
|
||||
}
|
||||
auto start = std::chrono::system_clock::now();
|
||||
// Run predictor
|
||||
this->predictor_->Run();
|
||||
|
||||
// Get output and post process
|
||||
std::unique_ptr<const Tensor> output_tensor(
|
||||
std::move(this->predictor_->GetOutput(0)));
|
||||
auto end = std::chrono::system_clock::now();
|
||||
auto duration =
|
||||
std::chrono::duration_cast<std::chrono::microseconds>(end - start);
|
||||
cost_time = double(duration.count()) *
|
||||
std::chrono::microseconds::period::num /
|
||||
std::chrono::microseconds::period::den;
|
||||
|
||||
// do postprocess
|
||||
int output_size = 1;
|
||||
for (auto dim : output_tensor->shape()) {
|
||||
output_size *= dim;
|
||||
}
|
||||
feature.resize(output_size);
|
||||
output_tensor->CopyToCpu(feature.data());
|
||||
|
||||
// postprocess include sqrt or binarize.
|
||||
FeatureNorm(feature);
|
||||
}
|
||||
|
||||
void FeatureExtract::FeatureNorm(std::vector<float> &feature) {
|
||||
float feature_sqrt = std::sqrt(std::inner_product(
|
||||
feature.begin(), feature.end(), feature.begin(), 0.0f));
|
||||
for (int i = 0; i < feature.size(); ++i) {
|
||||
feature[i] /= feature_sqrt;
|
||||
}
|
||||
}
|
||||
|
||||
void FeatureExtract::Permute(const cv::Mat *im, float *data) {
|
||||
int rh = im->rows;
|
||||
int rw = im->cols;
|
||||
int rc = im->channels();
|
||||
for (int i = 0; i < rc; ++i) {
|
||||
cv::extractChannel(*im, cv::Mat(rh, rw, CV_32FC1, data + i * rh * rw), i);
|
||||
}
|
||||
}
|
||||
|
||||
void FeatureExtract::ResizeImage(const cv::Mat &img, cv::Mat &resize_img) {
|
||||
cv::resize(img, resize_img, cv::Size(this->size, this->size));
|
||||
}
|
||||
|
||||
void FeatureExtract::NormalizeImage(cv::Mat *im, const std::vector<float> &mean,
|
||||
const std::vector<float> &std,
|
||||
float scale) {
|
||||
(*im).convertTo(*im, CV_32FC3, scale);
|
||||
for (int h = 0; h < im->rows; h++) {
|
||||
for (int w = 0; w < im->cols; w++) {
|
||||
im->at<cv::Vec3f>(h, w)[0] =
|
||||
(im->at<cv::Vec3f>(h, w)[0] - mean[0]) / std[0];
|
||||
im->at<cv::Vec3f>(h, w)[1] =
|
||||
(im->at<cv::Vec3f>(h, w)[1] - mean[1]) / std[1];
|
||||
im->at<cv::Vec3f>(h, w)[2] =
|
||||
(im->at<cv::Vec3f>(h, w)[2] - mean[2]) / std[2];
|
||||
}
|
||||
}
|
||||
}
|
|
@ -0,0 +1,62 @@
|
|||
// Copyright (c) 2022 PaddlePaddle Authors. All Rights Reserved.
|
||||
//
|
||||
// Licensed under the Apache License, Version 2.0 (the "License");
|
||||
// you may not use this file except in compliance with the License.
|
||||
// You may obtain a copy of the License at
|
||||
//
|
||||
// http://www.apache.org/licenses/LICENSE-2.0
|
||||
//
|
||||
// Unless required by applicable law or agreed to in writing, software
|
||||
// distributed under the License is distributed on an "AS IS" BASIS,
|
||||
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
// See the License for the specific language governing permissions and
|
||||
// limitations under the License.
|
||||
|
||||
#pragma once
|
||||
|
||||
#include "Utils.h" // NOLINT
|
||||
#include "numeric"
|
||||
#include "paddle_api.h" // NOLINT
|
||||
#include <arm_neon.h> // NOLINT
|
||||
#include <fstream> // NOLINT
|
||||
#include <iostream> // NOLINT
|
||||
#include <math.h> // NOLINT
|
||||
#include <memory> // NOLINT
|
||||
#include <opencv2/opencv.hpp> // NOLINT
|
||||
#include <stdlib.h> // NOLINT
|
||||
#include <string> // NOLINT
|
||||
#include <sys/time.h> // NOLINT
|
||||
#include <vector> // NOLINT
|
||||
|
||||
using namespace paddle::lite_api; // NOLINT
|
||||
using namespace std; // NOLINT
|
||||
|
||||
class FeatureExtract {
|
||||
public: // NOLINT
|
||||
explicit FeatureExtract(std::string model_path, std::vector<int> input_shape,
|
||||
int cpu_nums, std::string cpu_power) {
|
||||
MobileConfig config;
|
||||
config.set_threads(cpu_nums);
|
||||
config.set_power_mode(ParsePowerMode(cpu_power));
|
||||
config.set_model_from_file(model_path);
|
||||
this->predictor_ = CreatePaddlePredictor<MobileConfig>(config);
|
||||
}
|
||||
|
||||
void RunRecModel(const cv::Mat &img, double &cost_time,
|
||||
std::vector<float> &feature); // NOLINT
|
||||
void FeatureNorm(std::vector<float> &feature);
|
||||
|
||||
void ResizeImage(const cv::Mat &img, cv::Mat &resize_img);
|
||||
|
||||
void Permute(const cv::Mat *im, float *data);
|
||||
|
||||
void NormalizeImage(cv::Mat *im, const std::vector<float> &mean,
|
||||
const std::vector<float> &std, float scale);
|
||||
|
||||
private: // NOLINT
|
||||
std::shared_ptr<PaddlePredictor> predictor_;
|
||||
std::vector<float> mean_ = {0.485f, 0.456f, 0.406f};
|
||||
std::vector<float> std_ = {0.229f, 0.224f, 0.225f};
|
||||
double scale_ = 0.00392157; // 1/255.0
|
||||
int size = 224;
|
||||
};
|
|
@ -0,0 +1,212 @@
|
|||
// Copyright (c) 2019 PaddlePaddle Authors. All Rights Reserved.
|
||||
//
|
||||
// Licensed under the Apache License, Version 2.0 (the "License");
|
||||
// you may not use this file except in compliance with the License.
|
||||
// You may obtain a copy of the License at
|
||||
//
|
||||
// http://www.apache.org/licenses/LICENSE-2.0
|
||||
//
|
||||
// Unless required by applicable law or agreed to in writing, software
|
||||
// distributed under the License is distributed on an "AS IS" BASIS,
|
||||
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
// See the License for the specific language governing permissions and
|
||||
// limitations under the License.
|
||||
|
||||
#include "Native.h"
|
||||
#include "Pipeline.h"
|
||||
#include <android/bitmap.h>
|
||||
|
||||
#ifdef __cplusplus
|
||||
extern "C" {
|
||||
#endif
|
||||
/*
|
||||
* Class: Java_com_baidu_paddle_lite_demo_pp_1shitu_Native
|
||||
* Method: nativeInit
|
||||
* Signature:
|
||||
* (Ljava/lang/String;Ljava/lang/String;ILjava/lang/String;II[F[FF)J
|
||||
*/
|
||||
JNIEXPORT jlong JNICALL
|
||||
Java_com_baidu_paddle_lite_demo_pp_1shitu_Native_nativeInit(
|
||||
JNIEnv *env, jclass thiz, jstring jDetModelDir, jstring jRecModelDir,
|
||||
jstring jLabelPath, jstring jIndexPath, jlongArray jDetInputShape,
|
||||
jlongArray jRecInputShape, jint cpuThreadNum, jint WarmUp, jint Repeats,
|
||||
jint topk, jboolean jaddGallery, jstring cpu_power) {
|
||||
std::string det_model_path = jstring_to_cpp_string(env, jDetModelDir);
|
||||
std::string rec_model_path = jstring_to_cpp_string(env, jRecModelDir);
|
||||
std::string label_path = jstring_to_cpp_string(env, jLabelPath);
|
||||
std::string index_path = jstring_to_cpp_string(env, jIndexPath);
|
||||
bool add_gallery = jaddGallery;
|
||||
const std::string cpu_mode = jstring_to_cpp_string(env, cpu_power);
|
||||
std::vector<int64_t> det_input_shape =
|
||||
jlongarray_to_int64_vector(env, jDetInputShape);
|
||||
std::vector<int64_t> rec_input_shape =
|
||||
jlongarray_to_int64_vector(env, jRecInputShape);
|
||||
std::vector<int> det_input_shape_int;
|
||||
std::vector<int> rec_input_shape_int;
|
||||
for (auto &tmp : det_input_shape)
|
||||
det_input_shape_int.emplace_back(static_cast<int>(tmp));
|
||||
for (auto &tmp : rec_input_shape)
|
||||
rec_input_shape_int.emplace_back(static_cast<int>(tmp));
|
||||
return reinterpret_cast<jlong>(
|
||||
new PipeLine(det_model_path, rec_model_path, label_path, index_path,
|
||||
det_input_shape_int, rec_input_shape_int, cpuThreadNum,
|
||||
WarmUp, Repeats, topk, add_gallery, cpu_mode));
|
||||
}
|
||||
|
||||
/*
|
||||
* Class: Java_com_baidu_paddle_lite_demo_pp_1shitu_Native
|
||||
* Method: nativeRelease
|
||||
* Signature: (J)Z
|
||||
*/
|
||||
JNIEXPORT jboolean JNICALL
|
||||
Java_com_baidu_paddle_lite_demo_pp_1shitu_Native_nativeRelease(JNIEnv *env,
|
||||
jclass thiz,
|
||||
jlong ctx) {
|
||||
if (ctx == 0) {
|
||||
return JNI_FALSE;
|
||||
}
|
||||
auto *pipeline = reinterpret_cast<PipeLine *>(ctx);
|
||||
delete pipeline;
|
||||
return JNI_TRUE;
|
||||
}
|
||||
|
||||
JNIEXPORT jboolean JNICALL
|
||||
Java_com_baidu_paddle_lite_demo_pp_1shitu_Native_nativesetAddGallery(
|
||||
JNIEnv *env, jclass thiz, jlong ctx, jboolean flag) {
|
||||
if (ctx == 0) {
|
||||
return JNI_FALSE;
|
||||
}
|
||||
auto *pipeline = reinterpret_cast<PipeLine *>(ctx);
|
||||
pipeline->set_add_gallery(flag);
|
||||
return JNI_TRUE;
|
||||
}
|
||||
|
||||
JNIEXPORT jboolean JNICALL
|
||||
Java_com_baidu_paddle_lite_demo_pp_1shitu_Native_nativeclearGallery(JNIEnv *env,
|
||||
jclass thiz,
|
||||
jlong ctx) {
|
||||
if (ctx == 0) {
|
||||
return JNI_FALSE;
|
||||
}
|
||||
auto *pipeline = reinterpret_cast<PipeLine *>(ctx);
|
||||
pipeline->ClearFeature();
|
||||
return JNI_TRUE;
|
||||
}
|
||||
|
||||
/*
|
||||
* Class: Java_com_baidu_paddle_lite_demo_pp_1shitu_Native
|
||||
* Method: nativeProcess
|
||||
* Signature: (JIIIILjava/lang/String;)Z
|
||||
*/
|
||||
JNIEXPORT jstring JNICALL
|
||||
Java_com_baidu_paddle_lite_demo_pp_1shitu_Native_nativeProcess(
|
||||
JNIEnv *env, jclass thiz, jlong ctx, jobject jARGB8888ImageBitmap,
|
||||
jstring jlabel_name) {
|
||||
if (ctx == 0) {
|
||||
return JNI_FALSE;
|
||||
}
|
||||
|
||||
// Convert the android bitmap(ARGB8888) to the OpenCV RGBA image. Actually,
|
||||
// the data layout of AGRB8888 is R, G, B, A, it's the same as CV RGBA image,
|
||||
// so it is unnecessary to do the conversion of color format, check
|
||||
// https://developer.android.com/reference/android/graphics/Bitmap.Config#ARGB_8888
|
||||
// to get the more details about Bitmap.Config.ARGB8888
|
||||
auto t = GetCurrentTime();
|
||||
void *bitmapPixels;
|
||||
AndroidBitmapInfo bitmapInfo;
|
||||
if (AndroidBitmap_getInfo(env, jARGB8888ImageBitmap, &bitmapInfo) < 0) {
|
||||
LOGE("Invoke AndroidBitmap_getInfo() failed!");
|
||||
return JNI_FALSE;
|
||||
}
|
||||
if (bitmapInfo.format != ANDROID_BITMAP_FORMAT_RGBA_8888) {
|
||||
LOGE("Only Bitmap.Config.ARGB8888 color format is supported!");
|
||||
return JNI_FALSE;
|
||||
}
|
||||
if (AndroidBitmap_lockPixels(env, jARGB8888ImageBitmap, &bitmapPixels) < 0) {
|
||||
LOGE("Invoke AndroidBitmap_lockPixels() failed!");
|
||||
return JNI_FALSE;
|
||||
}
|
||||
cv::Mat bmpImage(bitmapInfo.height, bitmapInfo.width, CV_8UC4, bitmapPixels);
|
||||
cv::Mat rgbaImage;
|
||||
std::string label_name = jstring_to_cpp_string(env, jlabel_name);
|
||||
bmpImage.copyTo(rgbaImage);
|
||||
if (AndroidBitmap_unlockPixels(env, jARGB8888ImageBitmap) < 0) {
|
||||
LOGE("Invoke AndroidBitmap_unlockPixels() failed!");
|
||||
return JNI_FALSE;
|
||||
}
|
||||
LOGD("Read from bitmap costs %f ms", GetElapsedTime(t));
|
||||
|
||||
auto *pipeline = reinterpret_cast<PipeLine *>(ctx);
|
||||
|
||||
std::vector<cv::Mat> input_mat;
|
||||
std::vector<ObjectResult> out_object;
|
||||
cv::Mat rgb_input;
|
||||
cv::cvtColor(rgbaImage, rgb_input, cv::COLOR_RGBA2RGB);
|
||||
input_mat.emplace_back(rgb_input);
|
||||
std::string res_str = pipeline->run(input_mat, out_object, 1, label_name);
|
||||
bool modified = res_str.empty();
|
||||
if (!modified) {
|
||||
cv::Mat res_img;
|
||||
cv::cvtColor(input_mat[0], res_img, cv::COLOR_RGB2RGBA);
|
||||
// Convert the OpenCV RGBA image to the android bitmap(ARGB8888)
|
||||
if (res_img.type() != CV_8UC4) {
|
||||
LOGE("Only CV_8UC4 color format is supported!");
|
||||
return JNI_FALSE;
|
||||
}
|
||||
t = GetCurrentTime();
|
||||
if (AndroidBitmap_lockPixels(env, jARGB8888ImageBitmap, &bitmapPixels) <
|
||||
0) {
|
||||
LOGE("Invoke AndroidBitmap_lockPixels() failed!");
|
||||
return JNI_FALSE;
|
||||
}
|
||||
cv::Mat bmpImage(bitmapInfo.height, bitmapInfo.width, CV_8UC4,
|
||||
bitmapPixels);
|
||||
res_img.copyTo(bmpImage);
|
||||
if (AndroidBitmap_unlockPixels(env, jARGB8888ImageBitmap) < 0) {
|
||||
LOGE("Invoke AndroidBitmap_unlockPixels() failed!");
|
||||
return JNI_FALSE;
|
||||
}
|
||||
LOGD("Write to bitmap costs %f ms", GetElapsedTime(t));
|
||||
}
|
||||
return cpp_string_to_jstring(env, res_str);
|
||||
}
|
||||
|
||||
#ifdef __cplusplus
|
||||
}
|
||||
#endif
|
||||
|
||||
extern "C" JNIEXPORT jboolean JNICALL
|
||||
Java_com_baidu_paddle_lite_demo_pp_1shitu_Native_nativesaveIndex(
|
||||
JNIEnv *env, jclass clazz, jlong ctx, jstring jsave_file_name) {
|
||||
// TODO: implement nativesaveIndex()
|
||||
if (ctx == 0) {
|
||||
return JNI_FALSE;
|
||||
}
|
||||
auto *pipeline = reinterpret_cast<PipeLine *>(ctx);
|
||||
std::string save_file_name = jstring_to_cpp_string(env, jsave_file_name);
|
||||
pipeline->SaveIndex(save_file_name);
|
||||
return JNI_TRUE;
|
||||
}
|
||||
extern "C" JNIEXPORT jboolean JNICALL
|
||||
Java_com_baidu_paddle_lite_demo_pp_1shitu_Native_nativeloadIndex(
|
||||
JNIEnv *env, jclass clazz, jlong ctx, jstring jload_file_name) {
|
||||
// TODO: implement nativeloadIndex()
|
||||
if (ctx == 0) {
|
||||
return JNI_FALSE;
|
||||
}
|
||||
auto *pipeline = reinterpret_cast<PipeLine *>(ctx);
|
||||
std::string load_file_name = jstring_to_cpp_string(env, jload_file_name);
|
||||
bool load_flag = pipeline->LoadIndex(load_file_name);
|
||||
return JNI_TRUE && load_flag;
|
||||
}
|
||||
extern "C" JNIEXPORT jstring JNICALL
|
||||
Java_com_baidu_paddle_lite_demo_pp_1shitu_Native_nativegetClassname(
|
||||
JNIEnv *env, jclass clazz, jlong ctx) {
|
||||
// TODO: implement nativegetClassname()
|
||||
if (ctx == 0) {
|
||||
return JNI_FALSE;
|
||||
}
|
||||
auto *pipeline = reinterpret_cast<PipeLine *>(ctx);
|
||||
std::string class_name_content = pipeline->GetLabelList();
|
||||
return cpp_string_to_jstring(env, class_name_content);
|
||||
}
|
|
@ -0,0 +1,147 @@
|
|||
// Copyright (c) 2022 PaddlePaddle Authors. All Rights Reserved.
|
||||
//
|
||||
// Licensed under the Apache License, Version 2.0 (the "License");
|
||||
// you may not use this file except in compliance with the License.
|
||||
// You may obtain a copy of the License at
|
||||
//
|
||||
// http://www.apache.org/licenses/LICENSE-2.0
|
||||
//
|
||||
// Unless required by applicable law or agreed to in writing, software
|
||||
// distributed under the License is distributed on an "AS IS" BASIS,
|
||||
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
// See the License for the specific language governing permissions and
|
||||
// limitations under the License.
|
||||
|
||||
#pragma once
|
||||
|
||||
#include <jni.h>
|
||||
#include <string>
|
||||
#include <vector>
|
||||
|
||||
inline std::string jstring_to_cpp_string(JNIEnv *env, jstring jstr) {
|
||||
// In java, a unicode char will be encoded using 2 bytes (utf16).
|
||||
// so jstring will contain characters utf16. std::string in c++ is
|
||||
// essentially a string of bytes, not characters, so if we want to
|
||||
// pass jstring from JNI to c++, we have convert utf16 to bytes.
|
||||
if (!jstr) {
|
||||
return "";
|
||||
}
|
||||
const jclass stringClass = env->GetObjectClass(jstr);
|
||||
const jmethodID getBytes =
|
||||
env->GetMethodID(stringClass, "getBytes", "(Ljava/lang/String;)[B");
|
||||
const jbyteArray stringJbytes = (jbyteArray)env->CallObjectMethod(
|
||||
jstr, getBytes, env->NewStringUTF("UTF-8"));
|
||||
|
||||
size_t length = (size_t)env->GetArrayLength(stringJbytes);
|
||||
jbyte *pBytes = env->GetByteArrayElements(stringJbytes, NULL);
|
||||
|
||||
std::string ret = std::string(reinterpret_cast<char *>(pBytes), length);
|
||||
env->ReleaseByteArrayElements(stringJbytes, pBytes, JNI_ABORT);
|
||||
|
||||
env->DeleteLocalRef(stringJbytes);
|
||||
env->DeleteLocalRef(stringClass);
|
||||
return ret;
|
||||
}
|
||||
|
||||
inline jstring cpp_string_to_jstring(JNIEnv *env, std::string str) {
|
||||
auto *data = str.c_str();
|
||||
jclass strClass = env->FindClass("java/lang/String");
|
||||
jmethodID strClassInitMethodID =
|
||||
env->GetMethodID(strClass, "<init>", "([BLjava/lang/String;)V");
|
||||
|
||||
jbyteArray bytes = env->NewByteArray(strlen(data));
|
||||
env->SetByteArrayRegion(bytes, 0, strlen(data),
|
||||
reinterpret_cast<const jbyte *>(data));
|
||||
|
||||
jstring encoding = env->NewStringUTF("UTF-8");
|
||||
jstring res = (jstring)(
|
||||
env->NewObject(strClass, strClassInitMethodID, bytes, encoding));
|
||||
|
||||
env->DeleteLocalRef(strClass);
|
||||
env->DeleteLocalRef(encoding);
|
||||
env->DeleteLocalRef(bytes);
|
||||
|
||||
return res;
|
||||
}
|
||||
|
||||
inline jobject cpp_string_to_jobect(JNIEnv *env, std::string str) {
|
||||
auto *data = str.c_str();
|
||||
jclass strClass = env->FindClass("java/lang/String");
|
||||
jmethodID strClassInitMethodID =
|
||||
env->GetMethodID(strClass, "<init>", "([BLjava/lang/String;)V");
|
||||
|
||||
jbyteArray bytes = env->NewByteArray(strlen(data));
|
||||
env->SetByteArrayRegion(bytes, 0, strlen(data),
|
||||
reinterpret_cast<const jbyte *>(data));
|
||||
|
||||
jstring encoding = env->NewStringUTF("UTF-8");
|
||||
jobject res = env->NewObject(strClass, strClassInitMethodID, bytes, encoding);
|
||||
|
||||
env->DeleteLocalRef(strClass);
|
||||
env->DeleteLocalRef(encoding);
|
||||
env->DeleteLocalRef(bytes);
|
||||
|
||||
return res;
|
||||
}
|
||||
|
||||
inline jfloatArray cpp_array_to_jfloatarray(JNIEnv *env, const float *buf,
|
||||
int64_t len) {
|
||||
jfloatArray result = env->NewFloatArray(len);
|
||||
env->SetFloatArrayRegion(result, 0, len, buf);
|
||||
return result;
|
||||
}
|
||||
|
||||
inline jintArray cpp_array_to_jintarray(JNIEnv *env, const int *buf,
|
||||
int64_t len) {
|
||||
jintArray result = env->NewIntArray(len);
|
||||
env->SetIntArrayRegion(result, 0, len, buf);
|
||||
return result;
|
||||
}
|
||||
|
||||
inline jbyteArray cpp_array_to_jbytearray(JNIEnv *env, const int8_t *buf,
|
||||
int64_t len) {
|
||||
jbyteArray result = env->NewByteArray(len);
|
||||
env->SetByteArrayRegion(result, 0, len, buf);
|
||||
return result;
|
||||
}
|
||||
|
||||
inline jobjectArray
|
||||
cpp_array_to_jobjectarray(JNIEnv *env, const std::string *buf, int64_t len) {
|
||||
jclass jclz = env->FindClass("java/lang/String");
|
||||
jobjectArray result = env->NewObjectArray(len, jclz, NULL);
|
||||
for (int i = 0; i < len; i++) {
|
||||
jobject job = cpp_string_to_jobect(env, buf[i]);
|
||||
env->SetObjectArrayElement(result, i, job);
|
||||
}
|
||||
return result;
|
||||
}
|
||||
|
||||
inline jlongArray int64_vector_to_jlongarray(JNIEnv *env,
|
||||
const std::vector<int64_t> &vec) {
|
||||
jlongArray result = env->NewLongArray(vec.size());
|
||||
jlong *buf = new jlong[vec.size()];
|
||||
for (size_t i = 0; i < vec.size(); ++i) {
|
||||
buf[i] = (jlong)vec[i];
|
||||
}
|
||||
env->SetLongArrayRegion(result, 0, vec.size(), buf);
|
||||
delete[] buf;
|
||||
return result;
|
||||
}
|
||||
|
||||
inline std::vector<int64_t> jlongarray_to_int64_vector(JNIEnv *env,
|
||||
jlongArray data) {
|
||||
int data_size = env->GetArrayLength(data);
|
||||
jlong *data_ptr = env->GetLongArrayElements(data, nullptr);
|
||||
std::vector<int64_t> data_vec(data_ptr, data_ptr + data_size);
|
||||
env->ReleaseLongArrayElements(data, data_ptr, 0);
|
||||
return data_vec;
|
||||
}
|
||||
|
||||
inline std::vector<float> jfloatarray_to_float_vector(JNIEnv *env,
|
||||
jfloatArray data) {
|
||||
int data_size = env->GetArrayLength(data);
|
||||
jfloat *data_ptr = env->GetFloatArrayElements(data, nullptr);
|
||||
std::vector<float> data_vec(data_ptr, data_ptr + data_size);
|
||||
env->ReleaseFloatArrayElements(data, data_ptr, 0);
|
||||
return data_vec;
|
||||
}
|
|
@ -0,0 +1,324 @@
|
|||
// Copyright (c) 2022 PaddlePaddle Authors. All Rights Reserved.
|
||||
//
|
||||
// Licensed under the Apache License, Version 2.0 (the "License");
|
||||
// you may not use this file except in compliance with the License.
|
||||
// You may obtain a copy of the License at
|
||||
//
|
||||
// http://www.apache.org/licenses/LICENSE-2.0
|
||||
//
|
||||
// Unless required by applicable law or agreed to in writing, software
|
||||
// distributed under the License is distributed on an "AS IS" BASIS,
|
||||
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
// See the License for the specific language governing permissions and
|
||||
// limitations under the License.
|
||||
|
||||
#include "ObjectDetector.h" // NOLINT
|
||||
#include <iomanip> // NOLINT
|
||||
#include <sstream> // NOLINT
|
||||
#include <unistd.h>
|
||||
#include <vector> // NOLINT
|
||||
|
||||
// PicoDet decode
|
||||
ObjectResult DisPred2Bbox(const float *dfl_det, int label, float score, int x,
|
||||
int y, int stride, std::vector<float> im_shape,
|
||||
int reg_max) {
|
||||
float ct_x = (x + 0.5) * stride;
|
||||
float ct_y = (y + 0.5) * stride;
|
||||
std::vector<float> dis_pred;
|
||||
dis_pred.resize(4);
|
||||
for (int i = 0; i < 4; i++) {
|
||||
float dis = 0;
|
||||
float *dis_after_sm = new float[reg_max + 1];
|
||||
activation_function_softmax(dfl_det + i * (reg_max + 1), dis_after_sm,
|
||||
reg_max + 1);
|
||||
for (int j = 0; j < reg_max + 1; j++) {
|
||||
dis += j * dis_after_sm[j];
|
||||
}
|
||||
dis *= stride;
|
||||
dis_pred[i] = dis;
|
||||
delete[] dis_after_sm;
|
||||
}
|
||||
int xmin = static_cast<int>(std::max(ct_x - dis_pred[0], .0f));
|
||||
int ymin = static_cast<int>(std::max(ct_y - dis_pred[1], .0f));
|
||||
int xmax = static_cast<int>(std::min(ct_x + dis_pred[2], im_shape[0]));
|
||||
int ymax = static_cast<int>(std::min(ct_y + dis_pred[3], im_shape[1]));
|
||||
ObjectResult result_item;
|
||||
result_item.rect = {xmin, ymin, xmax, ymax};
|
||||
result_item.class_id = label;
|
||||
result_item.confidence = score;
|
||||
return result_item;
|
||||
}
|
||||
|
||||
void PicoDetPostProcess(std::vector<ObjectResult> *results,
|
||||
std::vector<const float *> outs,
|
||||
std::vector<int> fpn_stride,
|
||||
std::vector<float> im_shape,
|
||||
std::vector<float> scale_factor, float score_threshold,
|
||||
float nms_threshold, int num_class, int reg_max) {
|
||||
std::vector<std::vector<ObjectResult>> bbox_results;
|
||||
bbox_results.resize(num_class);
|
||||
int in_h = im_shape[0], in_w = im_shape[1];
|
||||
for (int i = 0; i < fpn_stride.size(); ++i) {
|
||||
int feature_h = ceil(in_h * 1.f / fpn_stride[i]);
|
||||
int feature_w = ceil(in_w * 1.f / fpn_stride[i]);
|
||||
for (int idx = 0; idx < feature_h * feature_w; idx++) {
|
||||
const float *scores = outs[i] + (idx * num_class);
|
||||
int row = idx / feature_w;
|
||||
int col = idx % feature_w;
|
||||
float score = 0;
|
||||
int cur_label = 0;
|
||||
for (int label = 0; label < num_class; label++) {
|
||||
if (scores[label] > score) {
|
||||
score = scores[label];
|
||||
cur_label = label;
|
||||
}
|
||||
}
|
||||
if (score > score_threshold) {
|
||||
const float *bbox_pred =
|
||||
outs[i + fpn_stride.size()] + (idx * 4 * (reg_max + 1));
|
||||
bbox_results[cur_label].push_back(
|
||||
DisPred2Bbox(bbox_pred, cur_label, score, col, row, fpn_stride[i],
|
||||
im_shape, reg_max));
|
||||
}
|
||||
}
|
||||
}
|
||||
for (int i = 0; i < bbox_results.size(); i++) {
|
||||
nms(&bbox_results[i], nms_threshold);
|
||||
for (auto box : bbox_results[i]) {
|
||||
box.rect[0] = box.rect[0] / scale_factor[1];
|
||||
box.rect[2] = box.rect[2] / scale_factor[1];
|
||||
box.rect[1] = box.rect[1] / scale_factor[0];
|
||||
box.rect[3] = box.rect[3] / scale_factor[0];
|
||||
results->push_back(box);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// ***************************** member Function ***************************
|
||||
// Load Model and create model predictor
|
||||
void ObjectDetector::LoadModel(const std::string &model_file, int num_theads,
|
||||
std::string cpu_power) {
|
||||
MobileConfig config;
|
||||
config.set_threads(num_theads);
|
||||
config.set_model_from_file(model_file);
|
||||
config.set_power_mode(ParsePowerMode(cpu_power));
|
||||
if (access(model_file.c_str(), 0) != 0) {
|
||||
LOGD("File not exist!");
|
||||
}
|
||||
predictor_ = CreatePaddlePredictor<MobileConfig>(config);
|
||||
}
|
||||
|
||||
void ObjectDetector::Preprocess(const cv::Mat &ori_im) {
|
||||
// Clone the image : keep the original mat for postprocess
|
||||
cv::Mat im = ori_im.clone();
|
||||
for (auto &op_process : ops_) {
|
||||
op_process(&im, &inputs_, pre_param_);
|
||||
}
|
||||
}
|
||||
|
||||
void ObjectDetector::Postprocess(const std::vector<cv::Mat> mats,
|
||||
std::vector<ObjectResult> *result,
|
||||
std::vector<int> bbox_num,
|
||||
bool is_rbox = false) {
|
||||
result->clear();
|
||||
int start_idx = 0;
|
||||
for (int im_id = 0; im_id < mats.size(); im_id++) {
|
||||
cv::Mat raw_mat = mats[im_id];
|
||||
int rh = 1;
|
||||
int rw = 1;
|
||||
if (arch_ == "Face") {
|
||||
rh = raw_mat.rows;
|
||||
rw = raw_mat.cols;
|
||||
}
|
||||
for (int j = start_idx; j < start_idx + bbox_num[im_id]; j++) {
|
||||
if (is_rbox) {
|
||||
// Class id
|
||||
int class_id = static_cast<int>(round(output_data_[0 + j * 10]));
|
||||
// Confidence score
|
||||
float score = output_data_[1 + j * 10];
|
||||
int x1 = (output_data_[2 + j * 10] * rw);
|
||||
int y1 = (output_data_[3 + j * 10] * rh);
|
||||
int x2 = (output_data_[4 + j * 10] * rw);
|
||||
int y2 = (output_data_[5 + j * 10] * rh);
|
||||
int x3 = (output_data_[6 + j * 10] * rw);
|
||||
int y3 = (output_data_[7 + j * 10] * rh);
|
||||
int x4 = (output_data_[8 + j * 10] * rw);
|
||||
int y4 = (output_data_[9 + j * 10] * rh);
|
||||
ObjectResult result_item;
|
||||
result_item.rect = {x1, y1, x2, y2, x3, y3, x4, y4};
|
||||
result_item.class_id = class_id;
|
||||
result_item.confidence = score;
|
||||
result->push_back(result_item);
|
||||
} else {
|
||||
// Class id
|
||||
int class_id = static_cast<int>(round(output_data_[0 + j * 6]));
|
||||
// Confidence score
|
||||
float score = output_data_[1 + j * 6];
|
||||
int xmin = (output_data_[2 + j * 6] * rw);
|
||||
int ymin = (output_data_[3 + j * 6] * rh);
|
||||
int xmax = (output_data_[4 + j * 6] * rw);
|
||||
int ymax = (output_data_[5 + j * 6] * rh);
|
||||
int wd = xmax - xmin;
|
||||
int hd = ymax - ymin;
|
||||
|
||||
ObjectResult result_item;
|
||||
result_item.rect = {xmin, ymin, xmax, ymax};
|
||||
result_item.class_id = class_id;
|
||||
result_item.confidence = score;
|
||||
result->push_back(result_item);
|
||||
}
|
||||
}
|
||||
start_idx += bbox_num[im_id];
|
||||
}
|
||||
}
|
||||
|
||||
void ObjectDetector::Predict(const std::vector<cv::Mat> &imgs, const int warmup,
|
||||
const int repeats,
|
||||
std::vector<ObjectResult> *result,
|
||||
std::vector<int> *bbox_num,
|
||||
std::vector<double> *times) {
|
||||
auto preprocess_start = std::chrono::steady_clock::now();
|
||||
int batch_size = imgs.size();
|
||||
|
||||
// in_data_batch
|
||||
std::vector<float> in_data_all;
|
||||
std::vector<float> im_shape_all(batch_size * 2);
|
||||
std::vector<float> scale_factor_all(batch_size * 2);
|
||||
// Preprocess image
|
||||
for (int bs_idx = 0; bs_idx < batch_size; bs_idx++) {
|
||||
cv::Mat im = imgs.at(bs_idx);
|
||||
Preprocess(im);
|
||||
im_shape_all[bs_idx * 2] = inputs_.im_shape_[0];
|
||||
im_shape_all[bs_idx * 2 + 1] = inputs_.im_shape_[1];
|
||||
|
||||
scale_factor_all[bs_idx * 2] = inputs_.scale_factor_[0];
|
||||
scale_factor_all[bs_idx * 2 + 1] = inputs_.scale_factor_[1];
|
||||
|
||||
// TODO: reduce cost time
|
||||
in_data_all.insert(in_data_all.end(), inputs_.im_data_.begin(),
|
||||
inputs_.im_data_.end());
|
||||
}
|
||||
auto preprocess_end = std::chrono::steady_clock::now();
|
||||
std::vector<const float *> output_data_list_;
|
||||
// Prepare input tensor
|
||||
|
||||
auto input_names = predictor_->GetInputNames();
|
||||
for (const auto &tensor_name : input_names) {
|
||||
auto in_tensor = predictor_->GetInputByName(tensor_name);
|
||||
if (tensor_name == "image") {
|
||||
int rh = inputs_.in_net_shape_[0];
|
||||
int rw = inputs_.in_net_shape_[1];
|
||||
in_tensor->Resize({batch_size, 3, rh, rw});
|
||||
auto *inptr = in_tensor->mutable_data<float>();
|
||||
std::copy_n(in_data_all.data(), in_data_all.size(), inptr);
|
||||
} else if (tensor_name == "im_shape") {
|
||||
in_tensor->Resize({batch_size, 2});
|
||||
auto *inptr = in_tensor->mutable_data<float>();
|
||||
std::copy_n(im_shape_all.data(), im_shape_all.size(), inptr);
|
||||
} else if (tensor_name == "scale_factor") {
|
||||
in_tensor->Resize({batch_size, 2});
|
||||
auto *inptr = in_tensor->mutable_data<float>();
|
||||
std::copy_n(scale_factor_all.data(), scale_factor_all.size(), inptr);
|
||||
}
|
||||
}
|
||||
|
||||
// Run predictor
|
||||
// warmup
|
||||
for (int i = 0; i < warmup; i++) {
|
||||
predictor_->Run();
|
||||
// Get output tensor
|
||||
auto output_names = predictor_->GetOutputNames();
|
||||
if (arch_ == "PicoDet") {
|
||||
for (int j = 0; j < output_names.size(); j++) {
|
||||
auto output_tensor = predictor_->GetTensor(output_names[j]);
|
||||
const float *outptr = output_tensor->data<float>();
|
||||
std::vector<int64_t> output_shape = output_tensor->shape();
|
||||
output_data_list_.push_back(outptr);
|
||||
}
|
||||
} else {
|
||||
auto out_tensor = predictor_->GetTensor(output_names[0]);
|
||||
auto out_bbox_num = predictor_->GetTensor(output_names[1]);
|
||||
}
|
||||
}
|
||||
|
||||
bool is_rbox = false;
|
||||
auto inference_start = std::chrono::steady_clock::now();
|
||||
for (int i = 0; i < repeats; i++) {
|
||||
predictor_->Run();
|
||||
}
|
||||
auto inference_end = std::chrono::steady_clock::now();
|
||||
auto postprocess_start = std::chrono::steady_clock::now();
|
||||
|
||||
// Get output tensor
|
||||
output_data_list_.clear();
|
||||
int num_class = 1;
|
||||
int reg_max = 7;
|
||||
auto output_names = predictor_->GetOutputNames();
|
||||
// TODO: Unified model output.
|
||||
if (arch_ == "PicoDet") {
|
||||
for (int i = 0; i < output_names.size(); i++) {
|
||||
auto output_tensor = predictor_->GetTensor(output_names[i]);
|
||||
const float *outptr = output_tensor->data<float>();
|
||||
std::vector<int64_t> output_shape = output_tensor->shape();
|
||||
if (i == 0) {
|
||||
num_class = output_shape[2];
|
||||
}
|
||||
if (i == fpn_stride_.size()) {
|
||||
reg_max = output_shape[2] / 4 - 1;
|
||||
}
|
||||
output_data_list_.push_back(outptr);
|
||||
}
|
||||
} else {
|
||||
auto output_tensor = predictor_->GetTensor(output_names[0]);
|
||||
auto output_shape = output_tensor->shape();
|
||||
auto out_bbox_num = predictor_->GetTensor(output_names[1]);
|
||||
auto out_bbox_num_shape = out_bbox_num->shape();
|
||||
// Calculate output length
|
||||
int output_size = 1;
|
||||
for (int j = 0; j < output_shape.size(); ++j) {
|
||||
output_size *= output_shape[j];
|
||||
}
|
||||
is_rbox = output_shape[output_shape.size() - 1] % 10 == 0;
|
||||
|
||||
if (output_size < 6) {
|
||||
std::cerr << "[WARNING] No object detected." << std::endl;
|
||||
}
|
||||
output_data_.resize(output_size);
|
||||
std::copy_n(output_tensor->mutable_data<float>(), output_size,
|
||||
output_data_.data());
|
||||
|
||||
int out_bbox_num_size = 1;
|
||||
for (int j = 0; j < out_bbox_num_shape.size(); ++j) {
|
||||
out_bbox_num_size *= out_bbox_num_shape[j];
|
||||
}
|
||||
out_bbox_num_data_.resize(out_bbox_num_size);
|
||||
std::copy_n(out_bbox_num->mutable_data<int>(), out_bbox_num_size,
|
||||
out_bbox_num_data_.data());
|
||||
}
|
||||
// Postprocessing result
|
||||
|
||||
result->clear();
|
||||
if (arch_ == "PicoDet") {
|
||||
PicoDetPostProcess(result, output_data_list_, fpn_stride_,
|
||||
inputs_.im_shape_, inputs_.scale_factor_,
|
||||
score_threshold_, nms_threshold_, num_class, reg_max);
|
||||
bbox_num->push_back(result->size());
|
||||
} else {
|
||||
Postprocess(imgs, result, out_bbox_num_data_, is_rbox);
|
||||
bbox_num->clear();
|
||||
for (int k = 0; k < out_bbox_num_data_.size(); k++) {
|
||||
int tmp = out_bbox_num_data_[k];
|
||||
bbox_num->push_back(tmp);
|
||||
}
|
||||
}
|
||||
auto postprocess_end = std::chrono::steady_clock::now();
|
||||
|
||||
std::chrono::duration<float> preprocess_diff =
|
||||
preprocess_end - preprocess_start;
|
||||
times->push_back(double(preprocess_diff.count() * 1000));
|
||||
std::chrono::duration<float> inference_diff = inference_end - inference_start;
|
||||
times->push_back(double(inference_diff.count() / repeats * 1000));
|
||||
std::chrono::duration<float> postprocess_diff =
|
||||
postprocess_end - postprocess_start;
|
||||
times->push_back(double(postprocess_diff.count() * 1000));
|
||||
}
|
|
@ -0,0 +1,254 @@
|
|||
// Copyright (c) 2021 PaddlePaddle Authors. All Rights Reserved.
|
||||
//
|
||||
// Licensed under the Apache License, Version 2.0 (the "License");
|
||||
// you may not use this file except in compliance with the License.
|
||||
// You may obtain a copy of the License at
|
||||
//
|
||||
// http://www.apache.org/licenses/LICENSE-2.0
|
||||
//
|
||||
// Unless required by applicable law or agreed to in writing, software
|
||||
// distributed under the License is distributed on an "AS IS" BASIS,
|
||||
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
// See the License for the specific language governing permissions and
|
||||
// limitations under the License.
|
||||
|
||||
#pragma once
|
||||
|
||||
#include "Utils.h" // NOLINT
|
||||
#include "paddle_api.h" // NOLINT
|
||||
#include <algorithm> // NOLINT
|
||||
#include <functional> // NOLINT
|
||||
#include <iostream> // NOLINT
|
||||
#include <memory> // NOLINT
|
||||
#include <opencv2/core/core.hpp> // NOLINT
|
||||
#include <opencv2/highgui/highgui.hpp> // NOLINT
|
||||
#include <opencv2/imgproc/imgproc.hpp> // NOLINT
|
||||
#include <stdio.h> // NOLINT
|
||||
#include <string> // NOLINT
|
||||
#include <unordered_map> // NOLINT
|
||||
#include <utility> // NOLINT
|
||||
#include <vector> // NOLINT
|
||||
|
||||
using namespace paddle::lite_api; // NOLINT
|
||||
|
||||
struct ObjectPreprocessParam {
|
||||
// Normalisze
|
||||
std::vector<float> mean;
|
||||
std::vector<float> std;
|
||||
bool is_scale;
|
||||
// resize
|
||||
int interp;
|
||||
bool keep_ratio;
|
||||
std::vector<int> target_size;
|
||||
// Pad
|
||||
int stride;
|
||||
// TopDownEvalAffine
|
||||
std::vector<int> trainsize;
|
||||
};
|
||||
|
||||
using PreprocessFunc = std::function<void(cv::Mat *im, ImageBlob *data,
|
||||
ObjectPreprocessParam item)>;
|
||||
|
||||
// PreProcess Function
|
||||
inline void InitInfo(cv::Mat *im, ImageBlob *data, ObjectPreprocessParam item) {
|
||||
data->im_shape_ = {static_cast<float>(im->rows),
|
||||
static_cast<float>(im->cols)};
|
||||
data->scale_factor_ = {1., 1.};
|
||||
data->in_net_shape_ = {static_cast<float>(im->rows),
|
||||
static_cast<float>(im->cols)};
|
||||
}
|
||||
|
||||
inline void NormalizeImage(cv::Mat *im, ImageBlob *data,
|
||||
ObjectPreprocessParam item) {
|
||||
std::vector<float> mean;
|
||||
std::vector<float> scale;
|
||||
bool is_scale;
|
||||
for (auto tmp : item.mean) {
|
||||
mean.emplace_back(tmp);
|
||||
}
|
||||
for (auto tmp : item.std) {
|
||||
scale.emplace_back(tmp);
|
||||
}
|
||||
is_scale = item.is_scale;
|
||||
double e = 1.0;
|
||||
if (is_scale) {
|
||||
e *= 1. / 255.0;
|
||||
}
|
||||
(*im).convertTo(*im, CV_32FC3, e);
|
||||
for (int h = 0; h < im->rows; h++) {
|
||||
for (int w = 0; w < im->cols; w++) {
|
||||
im->at<cv::Vec3f>(h, w)[0] =
|
||||
(im->at<cv::Vec3f>(h, w)[0] - mean[0]) / scale[0];
|
||||
im->at<cv::Vec3f>(h, w)[1] =
|
||||
(im->at<cv::Vec3f>(h, w)[1] - mean[1]) / scale[1];
|
||||
im->at<cv::Vec3f>(h, w)[2] =
|
||||
(im->at<cv::Vec3f>(h, w)[2] - mean[2]) / scale[2];
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
inline void Permute(cv::Mat *im, ImageBlob *data, ObjectPreprocessParam item) {
|
||||
(*im).convertTo(*im, CV_32FC3);
|
||||
int rh = im->rows;
|
||||
int rw = im->cols;
|
||||
int rc = im->channels();
|
||||
(data->im_data_).resize(rc * rh * rw);
|
||||
float *base = (data->im_data_).data();
|
||||
for (int i = 0; i < rc; ++i) {
|
||||
cv::extractChannel(*im, cv::Mat(rh, rw, CV_32FC1, base + i * rh * rw), i);
|
||||
}
|
||||
}
|
||||
|
||||
inline void Resize(cv::Mat *im, ImageBlob *data, ObjectPreprocessParam item) {
|
||||
std::vector<int> target_size;
|
||||
int interp = item.interp;
|
||||
bool keep_ratio = item.keep_ratio;
|
||||
for (auto tmp : item.target_size) {
|
||||
target_size.emplace_back(tmp);
|
||||
}
|
||||
std::pair<float, float> resize_scale;
|
||||
int origin_w = im->cols;
|
||||
int origin_h = im->rows;
|
||||
if (keep_ratio) {
|
||||
int im_size_max = std::max(origin_w, origin_h);
|
||||
int im_size_min = std::min(origin_w, origin_h);
|
||||
int target_size_max =
|
||||
*std::max_element(target_size.begin(), target_size.end());
|
||||
int target_size_min =
|
||||
*std::min_element(target_size.begin(), target_size.end());
|
||||
float scale_min =
|
||||
static_cast<float>(target_size_min) / static_cast<float>(im_size_min);
|
||||
float scale_max =
|
||||
static_cast<float>(target_size_max) / static_cast<float>(im_size_max);
|
||||
float scale_ratio = std::min(scale_min, scale_max);
|
||||
resize_scale = {scale_ratio, scale_ratio};
|
||||
} else {
|
||||
resize_scale.first =
|
||||
static_cast<float>(target_size[1]) / static_cast<float>(origin_w);
|
||||
resize_scale.second =
|
||||
static_cast<float>(target_size[0]) / static_cast<float>(origin_h);
|
||||
}
|
||||
data->im_shape_ = {static_cast<float>(im->cols * resize_scale.first),
|
||||
static_cast<float>(im->rows * resize_scale.second)};
|
||||
data->in_net_shape_ = {static_cast<float>(im->cols * resize_scale.first),
|
||||
static_cast<float>(im->rows * resize_scale.second)};
|
||||
cv::resize(*im, *im, cv::Size(), resize_scale.first, resize_scale.second,
|
||||
interp);
|
||||
data->im_shape_ = {
|
||||
static_cast<float>(im->rows), static_cast<float>(im->cols),
|
||||
};
|
||||
data->scale_factor_ = {
|
||||
resize_scale.second, resize_scale.first,
|
||||
};
|
||||
}
|
||||
|
||||
inline void PadStride(cv::Mat *im, ImageBlob *data,
|
||||
ObjectPreprocessParam item) {
|
||||
int stride = item.stride;
|
||||
if (stride <= 0) {
|
||||
return;
|
||||
}
|
||||
int rc = im->channels();
|
||||
int rh = im->rows;
|
||||
int rw = im->cols;
|
||||
int nh = (rh / stride) * stride + (rh % stride != 0) * stride;
|
||||
int nw = (rw / stride) * stride + (rw % stride != 0) * stride;
|
||||
cv::copyMakeBorder(*im, *im, 0, nh - rh, 0, nw - rw, cv::BORDER_CONSTANT,
|
||||
cv::Scalar(0));
|
||||
data->in_net_shape_ = {
|
||||
static_cast<float>(im->rows), static_cast<float>(im->cols),
|
||||
};
|
||||
}
|
||||
|
||||
inline void TopDownEvalAffine(cv::Mat *im, ImageBlob *data,
|
||||
ObjectPreprocessParam item) {
|
||||
int interp = 1;
|
||||
std::vector<int> trainsize;
|
||||
for (auto tmp : item.trainsize) {
|
||||
trainsize.emplace_back(tmp);
|
||||
}
|
||||
cv::resize(*im, *im, cv::Size(trainsize[0], trainsize[1]), 0, 0, interp);
|
||||
// todo: Simd::ResizeBilinear();
|
||||
data->in_net_shape_ = {
|
||||
static_cast<float>(trainsize[1]), static_cast<float>(trainsize[0]),
|
||||
};
|
||||
}
|
||||
|
||||
class ObjectDetector {
|
||||
public: // NOLINT
|
||||
explicit ObjectDetector(const std::string &model_dir,
|
||||
std::vector<int> det_input_shape,
|
||||
const int &cpu_threads, std::string cpu_power,
|
||||
const int &batch_size = 1) {
|
||||
// global
|
||||
fpn_stride_ = std::vector<int>({8, 16, 32, 64});
|
||||
// Init preprocess param
|
||||
// Normalisze
|
||||
pre_param_.mean = std::vector<float>({0.485, 0.456, 0.406});
|
||||
pre_param_.std = std::vector<float>({0.229, 0.224, 0.225});
|
||||
pre_param_.is_scale = true;
|
||||
// resize
|
||||
pre_param_.interp = 2;
|
||||
pre_param_.keep_ratio = false;
|
||||
pre_param_.target_size =
|
||||
std::vector<int>({det_input_shape[2], det_input_shape[3]});
|
||||
// Pad
|
||||
pre_param_.stride = 0;
|
||||
// TopDownEvalAffine
|
||||
pre_param_.trainsize =
|
||||
std::vector<int>({det_input_shape[2], det_input_shape[3]});
|
||||
// op
|
||||
preprocess_op_func_ = std::vector<std::string>(
|
||||
{"DetResize", "DetNormalizeImage", "DetPermute"});
|
||||
// init postprocess param
|
||||
arch_ = "PicoDet";
|
||||
nms_threshold_ = 0.5f;
|
||||
score_threshold_ = 0.3f;
|
||||
op_map_["InitInfo"] = (PreprocessFunc)InitInfo;
|
||||
op_map_["DetNormalizeImage"] = (PreprocessFunc)NormalizeImage;
|
||||
op_map_["DetPermute"] = (PreprocessFunc)Permute;
|
||||
op_map_["DetResize"] = (PreprocessFunc)Resize;
|
||||
op_map_["DetPadStride"] = (PreprocessFunc)PadStride;
|
||||
op_map_["DetTopDownEvalAffine"] = (PreprocessFunc)TopDownEvalAffine;
|
||||
for (auto op_name : preprocess_op_func_) {
|
||||
ops_.emplace_back(op_map_[op_name]);
|
||||
}
|
||||
LoadModel(model_dir, cpu_threads, cpu_power);
|
||||
}
|
||||
|
||||
// Load Paddle inference model
|
||||
void LoadModel(const std::string &model_file, int num_theads,
|
||||
std::string cpu_power);
|
||||
|
||||
// Run predictor
|
||||
void Predict(const std::vector<cv::Mat> &imgs, const int warmup = 0,
|
||||
const int repeats = 1,
|
||||
std::vector<ObjectResult> *result = nullptr,
|
||||
std::vector<int> *bbox_num = nullptr,
|
||||
std::vector<double> *times = nullptr);
|
||||
|
||||
private: // NOLINT
|
||||
// Preprocess image and copy data to input buffer
|
||||
void Preprocess(const cv::Mat &image_mat);
|
||||
|
||||
// Postprocess result
|
||||
void Postprocess(const std::vector<cv::Mat> mats,
|
||||
std::vector<ObjectResult> *result, std::vector<int> bbox_num,
|
||||
bool is_rbox);
|
||||
|
||||
std::shared_ptr<PaddlePredictor> predictor_;
|
||||
|
||||
ObjectPreprocessParam pre_param_;
|
||||
std::vector<PreprocessFunc> ops_;
|
||||
std::vector<std::string> preprocess_op_func_;
|
||||
|
||||
ImageBlob inputs_;
|
||||
std::vector<float> output_data_;
|
||||
std::vector<int> out_bbox_num_data_;
|
||||
float nms_threshold_;
|
||||
std::unordered_map<std::string, PreprocessFunc> op_map_;
|
||||
std::string arch_;
|
||||
float score_threshold_;
|
||||
|
||||
std::vector<int> fpn_stride_;
|
||||
};
|
|
@ -0,0 +1,305 @@
|
|||
// Copyright (c) 2022 PaddlePaddle Authors. All Rights Reserved.
|
||||
//
|
||||
// Licensed under the Apache License, Version 2.0 (the "License");
|
||||
// you may not use this file except in compliance with the License.
|
||||
// You may obtain a copy of the License at
|
||||
//
|
||||
// http://www.apache.org/licenses/LICENSE-2.0
|
||||
//
|
||||
// Unless required by applicable law or agreed to in writing, software
|
||||
// distributed under the License is distributed on an "AS IS" BASIS,
|
||||
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
// See the License for the specific language governing permissions and
|
||||
// limitations under the License.
|
||||
|
||||
#include "Pipeline.h"
|
||||
#include "FeatureExtractor.h"
|
||||
#include "ObjectDetector.h"
|
||||
#include "VectorSearch.h"
|
||||
#include <algorithm>
|
||||
#include <functional>
|
||||
#include <sys/stat.h>
|
||||
#include <unistd.h>
|
||||
#include <utility>
|
||||
|
||||
void PrintResult(std::vector<ObjectResult> &det_result,
|
||||
const std::shared_ptr<VectorSearch> &vector_search,
|
||||
SearchResult &search_result) {
|
||||
for (int i = 0; i < std::min((int)det_result.size(), 1); ++i) {
|
||||
int t = i;
|
||||
LOGD("\tresult%d: bbox[%d, %d, %d, %d], score: %f, label: %s\n", i,
|
||||
det_result[t].rect[0], det_result[t].rect[1], det_result[t].rect[2],
|
||||
det_result[t].rect[3], det_result[t].confidence,
|
||||
vector_search->GetLabel(search_result.I[search_result.return_k * t])
|
||||
.c_str());
|
||||
}
|
||||
}
|
||||
|
||||
void VisualResult(cv::Mat &img, std::vector<ObjectResult> &results) { // NOLINT
|
||||
for (int i = 0; i < 1; i++) {
|
||||
int w = results[i].rect[2] - results[i].rect[0];
|
||||
int h = results[i].rect[3] - results[i].rect[1];
|
||||
cv::Rect roi = cv::Rect(results[i].rect[0], results[i].rect[1], w, h);
|
||||
cv::rectangle(img, roi, cv::Scalar(41, 50, 255), 3);
|
||||
}
|
||||
}
|
||||
|
||||
PipeLine::PipeLine(std::string det_model_path, std::string rec_model_path,
|
||||
std::string label_path, std::string index_path,
|
||||
std::vector<int> det_input_shape,
|
||||
std::vector<int> rec_input_shape, int cpu_num_threads,
|
||||
int warm_up, int repeats, int topk, bool add_gallery,
|
||||
std::string cpu_power) {
|
||||
det_model_path_ = det_model_path;
|
||||
rec_model_path_ = rec_model_path;
|
||||
label_path_ = label_path;
|
||||
index_path_ = index_path;
|
||||
det_input_shape_ = det_input_shape;
|
||||
rec_input_shape_ = rec_input_shape;
|
||||
cpu_num_threads_ = cpu_num_threads;
|
||||
add_gallery_flag = add_gallery;
|
||||
|
||||
max_det_num_ = topk;
|
||||
cpu_pow_ = cpu_power;
|
||||
det_model_path_ =
|
||||
det_model_path_ + "/mainbody_PPLCNet_x2_5_640_quant_v1.0_lite.nb";
|
||||
rec_model_path_ =
|
||||
rec_model_path_ + "/general_PPLCNetV2_base_quant_v1.0_lite.nb";
|
||||
|
||||
// create object detector
|
||||
det_ = std::make_shared<ObjectDetector>(det_model_path_, det_input_shape_,
|
||||
cpu_num_threads_, cpu_pow_);
|
||||
|
||||
// create rec model
|
||||
rec_ = std::make_shared<FeatureExtract>(rec_model_path_, rec_input_shape_,
|
||||
cpu_num_threads_, cpu_pow_);
|
||||
// create vector search
|
||||
searcher_ = std::make_shared<VectorSearch>(label_path_, index_path_, 5, 0.5);
|
||||
}
|
||||
|
||||
std::string PipeLine::run(std::vector<cv::Mat> &batch_imgs, // NOLINT
|
||||
std::vector<ObjectResult> &det_result, // NOLINT
|
||||
int batch_size, const std::string &label_name) {
|
||||
std::fill(times_.begin(), times_.end(), 0);
|
||||
|
||||
// if (!this->add_gallery_flag)
|
||||
// {
|
||||
DetPredictImage(batch_imgs, &det_result, batch_size, det_,
|
||||
max_det_num_); // det_result获取[l,d,r,u]
|
||||
// }
|
||||
// add the whole image for recognition to improve recall
|
||||
|
||||
ObjectResult result_whole_img = {
|
||||
{0, 0, batch_imgs[0].cols, batch_imgs[0].rows}, 0, 1.0};
|
||||
det_result.push_back(result_whole_img); // 加入整图的坐标,提升召回率
|
||||
// get rec result
|
||||
for (int j = 0; j < det_result.size(); ++j) {
|
||||
double rec_time = 0.0; // .rect:vector = {l, d, r, u}
|
||||
vector<float> feature;
|
||||
int w = det_result[j].rect[2] - det_result[j].rect[0];
|
||||
int h = det_result[j].rect[3] - det_result[j].rect[1];
|
||||
cv::Rect rect(det_result[j].rect[0], det_result[j].rect[1], w, h);
|
||||
cv::Mat crop_img = batch_imgs[0](rect);
|
||||
rec_->RunRecModel(crop_img, rec_time, feature);
|
||||
if (this->add_gallery_flag) {
|
||||
this->searcher_->AddFeature(feature.data(), label_name);
|
||||
} else {
|
||||
features.insert(features.end(), feature.begin(),
|
||||
feature.end()); //每次插入一个512的向量
|
||||
}
|
||||
}
|
||||
if (this->add_gallery_flag) {
|
||||
VisualResult(batch_imgs[0], det_result);
|
||||
det_result.clear();
|
||||
features.clear();
|
||||
indices.clear();
|
||||
std::string res = std::to_string(times_[1] + times_[4]) + "\n";
|
||||
return res;
|
||||
}
|
||||
// do vectore search
|
||||
SearchResult search_result = searcher_->Search(
|
||||
features.data(),
|
||||
det_result
|
||||
.size()); // 一次搜索多个向量(展平在features里),共det_result.size()个
|
||||
// for (int j = 0; j < det_result.size(); ++j)
|
||||
for (int j = 0; j < 1; ++j) // 对于每个检测框,只把
|
||||
{
|
||||
det_result[j].confidence =
|
||||
search_result.return_k * j < search_result.D.size()
|
||||
? search_result.D[search_result.return_k * j]
|
||||
: 0.0f;
|
||||
for (int k = 0; k < this->max_index_num_; ++k) {
|
||||
std::size_t tidx =
|
||||
min<std::size_t>((std::size_t)(search_result.return_k * j + k),
|
||||
search_result.D.size() - 1);
|
||||
|
||||
std::string _class_name = searcher_->GetLabel(search_result.I[tidx]);
|
||||
int _index = (int)(search_result.I[tidx]);
|
||||
float _dist = search_result.D[tidx];
|
||||
if (_dist > 1e5 || _dist < -1e5) {
|
||||
_dist = 0.0;
|
||||
}
|
||||
|
||||
det_result[j].rec_result.push_back({_class_name, _index, _dist});
|
||||
}
|
||||
}
|
||||
// sort(det_result.begin(), det_result.end(), [](const ObjectResult &a,
|
||||
// const ObjectResult &b){
|
||||
// if (a.rec_result.empty() and b.rec_result.empty())
|
||||
// {
|
||||
// return 0;
|
||||
// }
|
||||
// else if (a.rec_result.empty() and !b.rec_result.empty())
|
||||
// {
|
||||
// return 0;
|
||||
// }
|
||||
// else if (!a.rec_result.empty() and b.rec_result.empty())
|
||||
// {
|
||||
// return 1;
|
||||
// }
|
||||
// else
|
||||
// {
|
||||
// return (int)(a.rec_result[0].score > b.rec_result[0].score);
|
||||
// }
|
||||
// });
|
||||
NMSBoxes(det_result, searcher_->GetThreshold(), this->rec_nms_thresold_,
|
||||
indices);
|
||||
VisualResult(batch_imgs[0], det_result);
|
||||
LOGD("================== result summary =========================");
|
||||
PrintResult(det_result, searcher_, search_result);
|
||||
|
||||
// results
|
||||
std::string res;
|
||||
res += std::to_string(times_[1] + times_[4]) + "\n";
|
||||
for (int i = 0; i < 1; i++) {
|
||||
res.append(det_result[i].rec_result[0].class_name + ", " +
|
||||
std::to_string((int)(det_result[i].rec_result[0].score * 1000) *
|
||||
1.0 / 1000) +
|
||||
"\n");
|
||||
}
|
||||
det_result.clear();
|
||||
features.clear();
|
||||
indices.clear();
|
||||
return res;
|
||||
}
|
||||
|
||||
void PipeLine::DetPredictImage(const std::vector<cv::Mat> batch_imgs,
|
||||
std::vector<ObjectResult> *im_result,
|
||||
const int batch_size_det,
|
||||
std::shared_ptr<ObjectDetector> det,
|
||||
const int max_det_num) {
|
||||
int steps = ceil(float(batch_imgs.size()) / batch_size_det);
|
||||
for (int idx = 0; idx < steps; idx++) {
|
||||
int left_image_cnt = (int)batch_imgs.size() - idx * batch_size_det;
|
||||
if (left_image_cnt > batch_size_det) {
|
||||
left_image_cnt = batch_size_det;
|
||||
}
|
||||
// Store all detected result
|
||||
std::vector<ObjectResult> result;
|
||||
std::vector<int> bbox_num;
|
||||
std::vector<double> det_times;
|
||||
|
||||
// bool is_rbox = false;
|
||||
det->Predict(batch_imgs, 0, 1, &result, &bbox_num, &det_times);
|
||||
int item_start_idx = 0;
|
||||
for (int i = 0; i < left_image_cnt; i++) {
|
||||
cv::Mat im = batch_imgs[i];
|
||||
int detect_num = 0;
|
||||
for (int j = 0; j < min(bbox_num[i], max_det_num); j++) {
|
||||
ObjectResult item = result[item_start_idx + j];
|
||||
if (item.class_id == -1) {
|
||||
continue;
|
||||
}
|
||||
detect_num += 1;
|
||||
im_result->push_back(item);
|
||||
}
|
||||
item_start_idx = item_start_idx + bbox_num[i];
|
||||
}
|
||||
times_[0] += det_times[0];
|
||||
times_[1] += det_times[1];
|
||||
times_[2] += det_times[2];
|
||||
}
|
||||
}
|
||||
|
||||
template <typename T>
|
||||
static inline bool SortScorePairDescend(const std::pair<float, T> &pair1,
|
||||
const std::pair<float, T> &pair2) {
|
||||
return pair1.first > pair2.first;
|
||||
}
|
||||
|
||||
inline void
|
||||
GetMaxScoreIndex(const std::vector<ObjectResult> &det_result,
|
||||
const float threshold,
|
||||
std::vector<std::pair<float, int>> &score_index_vec) {
|
||||
// Generate index score pairs.
|
||||
for (size_t i = 0; i < det_result.size(); ++i) {
|
||||
if (det_result[i].confidence > threshold) {
|
||||
score_index_vec.push_back(std::make_pair(det_result[i].confidence, i));
|
||||
}
|
||||
}
|
||||
|
||||
// Sort the score pair according to the scores in descending order
|
||||
std::stable_sort(score_index_vec.begin(), score_index_vec.end(),
|
||||
SortScorePairDescend<int>);
|
||||
}
|
||||
|
||||
float RectOverlap(const ObjectResult &a, const ObjectResult &b) {
|
||||
float Aa = (a.rect[2] - a.rect[0] + 1) * (a.rect[3] - a.rect[1] + 1);
|
||||
float Ab = (b.rect[2] - b.rect[0] + 1) * (b.rect[3] - b.rect[1] + 1);
|
||||
|
||||
int iou_w = max(min(a.rect[2], b.rect[2]) - max(a.rect[0], b.rect[0]) + 1, 0);
|
||||
int iou_h = max(min(a.rect[3], b.rect[3]) - max(a.rect[1], b.rect[1]) + 1, 0);
|
||||
float Aab = iou_w * iou_h;
|
||||
return Aab / (Aa + Ab - Aab);
|
||||
}
|
||||
|
||||
void PipeLine::NMSBoxes(const std::vector<ObjectResult> &det_result,
|
||||
const float score_threshold, const float nms_threshold,
|
||||
std::vector<int> &indices) {
|
||||
// Get top_k scores (with corresponding indices).
|
||||
std::vector<std::pair<float, int>> score_index_vec;
|
||||
GetMaxScoreIndex(det_result, score_threshold, score_index_vec);
|
||||
|
||||
// Do nms
|
||||
indices.clear();
|
||||
for (size_t i = 0; i < score_index_vec.size(); ++i) {
|
||||
const int idx = score_index_vec[i].second;
|
||||
bool keep = true;
|
||||
for (int k = 0; k < (int)indices.size() && keep; ++k) {
|
||||
const int kept_idx = indices[k];
|
||||
float overlap = RectOverlap(det_result[idx], det_result[kept_idx]);
|
||||
keep = overlap <= nms_threshold;
|
||||
}
|
||||
if (keep)
|
||||
indices.push_back(idx);
|
||||
}
|
||||
}
|
||||
|
||||
void PipeLine::set_add_gallery(const bool &flag) {
|
||||
this->add_gallery_flag = flag;
|
||||
}
|
||||
|
||||
void PipeLine::ClearFeature() { this->searcher_->ClearFeature(); }
|
||||
|
||||
void PipeLine::SaveIndex(const string &save_file_name) {
|
||||
this->searcher_->SaveIndex(save_file_name);
|
||||
}
|
||||
|
||||
bool PipeLine::LoadIndex(const string &save_file_name) {
|
||||
return this->searcher_->LoadFromSaveFileName(save_file_name);
|
||||
}
|
||||
|
||||
string PipeLine::GetLabelList() {
|
||||
std::vector<std::string> class_name_list = this->searcher_->GetLabelList();
|
||||
string ret;
|
||||
ret += "共";
|
||||
ret += std::to_string(class_name_list.size());
|
||||
ret += "类";
|
||||
ret += "\n";
|
||||
ret += "====================\n";
|
||||
for (const auto &str : class_name_list) {
|
||||
ret += str;
|
||||
ret += "\n";
|
||||
}
|
||||
return ret;
|
||||
}
|
|
@ -0,0 +1,90 @@
|
|||
// Copyright (c) 2022 PaddlePaddle Authors. All Rights Reserved.
|
||||
//
|
||||
// Licensed under the Apache License, Version 2.0 (the "License");
|
||||
// you may not use this file except in compliance with the License.
|
||||
// You may obtain a copy of the License at
|
||||
//
|
||||
// http://www.apache.org/licenses/LICENSE-2.0
|
||||
//
|
||||
// Unless required by applicable law or agreed to in writing, software
|
||||
// distributed under the License is distributed on an "AS IS" BASIS,
|
||||
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
// See the License for the specific language governing permissions and
|
||||
// limitations under the License.
|
||||
|
||||
#pragma once
|
||||
|
||||
#include "FeatureExtractor.h" // NOLINT
|
||||
#include "ObjectDetector.h" // NOLINT
|
||||
#include "Utils.h"
|
||||
#include "VectorSearch.h"
|
||||
#include "paddle_api.h" // NOLINT
|
||||
#include <EGL/egl.h> // NOLINT
|
||||
#include <GLES2/gl2.h> // NOLINT
|
||||
#include <memory> // NOLINT
|
||||
#include <opencv2/core.hpp> // NOLINT
|
||||
#include <opencv2/highgui/highgui.hpp> // NOLINT
|
||||
#include <opencv2/imgcodecs.hpp> // NOLINT
|
||||
#include <opencv2/imgproc.hpp> // NOLINT
|
||||
#include <string> // NOLINT
|
||||
#include <vector> // NOLINT
|
||||
|
||||
class PipeLine {
|
||||
public: // NOLINT
|
||||
explicit PipeLine(std::string det_model_path, std::string rec_model_path,
|
||||
std::string label_path, std::string index_path,
|
||||
std::vector<int> det_input_shape,
|
||||
std::vector<int> rec_input_shape, int cpu_num_threads,
|
||||
int warm_up, int repeats, int topk, bool add_gallery,
|
||||
std::string cpu_power);
|
||||
|
||||
std::string run(std::vector<cv::Mat> &batch_imgs, // NOLINT
|
||||
std::vector<ObjectResult> &det_result, // NOLINT
|
||||
int batch_size, const std::string &label_name = "");
|
||||
|
||||
void set_add_gallery(const bool &flag);
|
||||
|
||||
void ClearFeature();
|
||||
|
||||
void SaveIndex(const string &save_file_name);
|
||||
|
||||
bool LoadIndex(const string &load_file_name);
|
||||
|
||||
string GetLabelList();
|
||||
|
||||
private: // NOLINT
|
||||
std::string det_model_path_;
|
||||
std::string rec_model_path_;
|
||||
std::string label_path_;
|
||||
std::string index_path_;
|
||||
std::vector<int> det_input_shape_;
|
||||
std::vector<int> rec_input_shape_;
|
||||
int cpu_num_threads_;
|
||||
bool add_gallery_flag;
|
||||
std::string cpu_pow_;
|
||||
// 实例化检测类
|
||||
std::shared_ptr<ObjectDetector> det_;
|
||||
|
||||
// 实例化特征提取(rec)类
|
||||
std::shared_ptr<FeatureExtract> rec_;
|
||||
|
||||
// 实例化特征检索类
|
||||
std::shared_ptr<VectorSearch> searcher_;
|
||||
|
||||
int max_det_num_ = 3;
|
||||
int max_index_num_ = 5;
|
||||
float rec_nms_thresold_ = 0.05f;
|
||||
std::vector<float> features;
|
||||
std::vector<int> indices;
|
||||
std::vector<double> times_{0, 0, 0, 0, 0, 0, 0};
|
||||
|
||||
void DetPredictImage(const std::vector<cv::Mat> batch_imgs,
|
||||
std::vector<ObjectResult> *im_result,
|
||||
const int batch_size_det,
|
||||
std::shared_ptr<ObjectDetector> det,
|
||||
const int max_det_num = 3);
|
||||
|
||||
void NMSBoxes(const std::vector<ObjectResult> &det_result,
|
||||
const float score_threshold, const float nms_threshold,
|
||||
std::vector<int> &indices);
|
||||
};
|
|
@ -0,0 +1,3 @@
|
|||
download [faiss1.5.3.tar.gz](https://paddle-inference-dist.bj.bcebos.com/faiss1.5.3.tar.gz) and unzip it, put `include` directory here, file structure like below:
|
||||
|
||||
main/cpp/include/faiss/*.h
|
|
@ -0,0 +1,183 @@
|
|||
// Copyright (c) 2019 PaddlePaddle Authors. All Rights Reserved.
|
||||
//
|
||||
// Licensed under the Apache License, Version 2.0 (the "License");
|
||||
// you may not use this file except in compliance with the License.
|
||||
// You may obtain a copy of the License at
|
||||
//
|
||||
// http://www.apache.org/licenses/LICENSE-2.0
|
||||
//
|
||||
// Unless required by applicable law or agreed to in writing, software
|
||||
// distributed under the License is distributed on an "AS IS" BASIS,
|
||||
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
// See the License for the specific language governing permissions and
|
||||
// limitations under the License.
|
||||
|
||||
#include "Utils.h" // NOLINT
|
||||
#include <algorithm> // NOLINT
|
||||
#include <arm_neon.h> // NOLINT
|
||||
|
||||
int64_t ShapeProduction(const std::vector<int64_t> &shape) {
|
||||
int64_t res = 1;
|
||||
for (auto i : shape)
|
||||
res *= i;
|
||||
return res;
|
||||
}
|
||||
|
||||
void NHWC3ToNC3HW(const float *src, float *dst, const float *mean,
|
||||
const float *std, int width, int height) {
|
||||
int size = height * width;
|
||||
float32x4_t vmean0 = vdupq_n_f32(mean ? mean[0] : 0.0f);
|
||||
float32x4_t vmean1 = vdupq_n_f32(mean ? mean[1] : 0.0f);
|
||||
float32x4_t vmean2 = vdupq_n_f32(mean ? mean[2] : 0.0f);
|
||||
float32x4_t vscale0 = vdupq_n_f32(std ? (1.0f / std[0]) : 1.0f);
|
||||
float32x4_t vscale1 = vdupq_n_f32(std ? (1.0f / std[1]) : 1.0f);
|
||||
float32x4_t vscale2 = vdupq_n_f32(std ? (1.0f / std[2]) : 1.0f);
|
||||
float *dst_c0 = dst;
|
||||
float *dst_c1 = dst + size;
|
||||
float *dst_c2 = dst + size * 2;
|
||||
int i = 0;
|
||||
for (; i < size - 3; i += 4) {
|
||||
float32x4x3_t vin3 = vld3q_f32(src);
|
||||
float32x4_t vsub0 = vsubq_f32(vin3.val[0], vmean0);
|
||||
float32x4_t vsub1 = vsubq_f32(vin3.val[1], vmean1);
|
||||
float32x4_t vsub2 = vsubq_f32(vin3.val[2], vmean2);
|
||||
float32x4_t vs0 = vmulq_f32(vsub0, vscale0);
|
||||
float32x4_t vs1 = vmulq_f32(vsub1, vscale1);
|
||||
float32x4_t vs2 = vmulq_f32(vsub2, vscale2);
|
||||
vst1q_f32(dst_c0, vs0);
|
||||
vst1q_f32(dst_c1, vs1);
|
||||
vst1q_f32(dst_c2, vs2);
|
||||
src += 12;
|
||||
dst_c0 += 4;
|
||||
dst_c1 += 4;
|
||||
dst_c2 += 4;
|
||||
}
|
||||
for (; i < size; i++) {
|
||||
*(dst_c0++) = (*(src++) - mean[0]) / std[0];
|
||||
*(dst_c1++) = (*(src++) - mean[1]) / std[1];
|
||||
*(dst_c2++) = (*(src++) - mean[2]) / std[2];
|
||||
}
|
||||
}
|
||||
|
||||
void NHWC1ToNC1HW(const float *src, float *dst, const float *mean,
|
||||
const float *std, int width, int height) {
|
||||
int size = height * width;
|
||||
float32x4_t vmean = vdupq_n_f32(mean ? mean[0] : 0.0f);
|
||||
float32x4_t vscale = vdupq_n_f32(std ? (1.0f / std[0]) : 1.0f);
|
||||
int i = 0;
|
||||
for (; i < size - 3; i += 4) {
|
||||
float32x4_t vin = vld1q_f32(src);
|
||||
float32x4_t vsub = vsubq_f32(vin, vmean);
|
||||
float32x4_t vs = vmulq_f32(vsub, vscale);
|
||||
vst1q_f32(dst, vs);
|
||||
src += 4;
|
||||
dst += 4;
|
||||
}
|
||||
for (; i < size; i++) {
|
||||
*(dst++) = (*(src++) - mean[0]) / std[0];
|
||||
}
|
||||
}
|
||||
|
||||
void nms(std::vector<ObjectResult> *input_boxes, float nms_threshold,
|
||||
bool rec_nms) {
|
||||
if (!rec_nms) {
|
||||
std::sort(input_boxes->begin(), input_boxes->end(),
|
||||
[](ObjectResult a, ObjectResult b) {
|
||||
return a.confidence > b.confidence;
|
||||
});
|
||||
} else {
|
||||
std::sort(input_boxes->begin(), input_boxes->end(),
|
||||
[](ObjectResult a, ObjectResult b) {
|
||||
return a.rec_result[0].score > b.rec_result[0].score;
|
||||
});
|
||||
}
|
||||
std::vector<float> vArea(input_boxes->size());
|
||||
for (int i = 0; i < input_boxes->size(); ++i) {
|
||||
vArea[i] = (input_boxes->at(i).rect[2] - input_boxes->at(i).rect[0] + 1) *
|
||||
(input_boxes->at(i).rect[3] - input_boxes->at(i).rect[1] + 1);
|
||||
}
|
||||
for (int i = 0; i < input_boxes->size(); ++i) {
|
||||
for (int j = i + 1; j < input_boxes->size();) {
|
||||
float xx1 =
|
||||
std::max(input_boxes->at(i).rect[0], input_boxes->at(j).rect[0]);
|
||||
float yy1 =
|
||||
std::max(input_boxes->at(i).rect[1], input_boxes->at(j).rect[1]);
|
||||
float xx2 =
|
||||
std::min(input_boxes->at(i).rect[2], input_boxes->at(j).rect[2]);
|
||||
float yy2 =
|
||||
std::min(input_boxes->at(i).rect[3], input_boxes->at(j).rect[3]);
|
||||
float w = std::max(0.f, xx2 - xx1 + 1);
|
||||
float h = std::max(0.f, yy2 - yy1 + 1);
|
||||
float inter = w * h;
|
||||
float ovr = inter / (vArea[i] + vArea[j] - inter);
|
||||
if (ovr >= nms_threshold) {
|
||||
input_boxes->erase(input_boxes->begin() + j);
|
||||
vArea.erase(vArea.begin() + j);
|
||||
} else {
|
||||
j++;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// fill tensor with mean and scale
|
||||
// and trans layout: nhwc -> nchw, neon speed up
|
||||
void neon_mean_scale(const float *din, float *dout, int size, float *mean,
|
||||
float *scale) {
|
||||
float32x4_t vmean0 = vdupq_n_f32(mean[0]);
|
||||
float32x4_t vmean1 = vdupq_n_f32(mean[1]);
|
||||
float32x4_t vmean2 = vdupq_n_f32(mean[2]);
|
||||
float32x4_t vscale0 = vdupq_n_f32(1.f / scale[0]);
|
||||
float32x4_t vscale1 = vdupq_n_f32(1.f / scale[1]);
|
||||
float32x4_t vscale2 = vdupq_n_f32(1.f / scale[2]);
|
||||
|
||||
float *dout_c0 = dout;
|
||||
float *dout_c1 = dout + size;
|
||||
float *dout_c2 = dout + size * 2;
|
||||
|
||||
int i = 0;
|
||||
for (; i < size - 3; i += 4) {
|
||||
float32x4x3_t vin3 = vld3q_f32(din);
|
||||
float32x4_t vsub0 = vsubq_f32(vin3.val[0], vmean0);
|
||||
float32x4_t vsub1 = vsubq_f32(vin3.val[1], vmean1);
|
||||
float32x4_t vsub2 = vsubq_f32(vin3.val[2], vmean2);
|
||||
float32x4_t vs0 = vmulq_f32(vsub0, vscale0);
|
||||
float32x4_t vs1 = vmulq_f32(vsub1, vscale1);
|
||||
float32x4_t vs2 = vmulq_f32(vsub2, vscale2);
|
||||
vst1q_f32(dout_c0, vs0);
|
||||
vst1q_f32(dout_c1, vs1);
|
||||
vst1q_f32(dout_c2, vs2);
|
||||
|
||||
din += 12;
|
||||
dout_c0 += 4;
|
||||
dout_c1 += 4;
|
||||
dout_c2 += 4;
|
||||
}
|
||||
for (; i < size; i++) {
|
||||
*(dout_c0++) = (*(din++) - mean[0]) / scale[0];
|
||||
*(dout_c0++) = (*(din++) - mean[1]) / scale[1];
|
||||
*(dout_c0++) = (*(din++) - mean[2]) / scale[2];
|
||||
}
|
||||
}
|
||||
|
||||
float fast_exp(float x) {
|
||||
union {
|
||||
uint32_t i;
|
||||
float f;
|
||||
} v{};
|
||||
v.i = (1 << 23) * (1.4426950409 * x + 126.93490512f);
|
||||
return v.f;
|
||||
}
|
||||
|
||||
void activation_function_softmax(const float *src, float *dst, int length) {
|
||||
const float alpha = *std::max_element(src, src + length);
|
||||
float denominator{0.f};
|
||||
|
||||
for (int i = 0; i < length; ++i) {
|
||||
dst[i] = fast_exp(src[i] - alpha);
|
||||
denominator += dst[i];
|
||||
}
|
||||
for (int i = 0; i < length; ++i) {
|
||||
dst[i] /= denominator;
|
||||
}
|
||||
}
|
|
@ -0,0 +1,143 @@
|
|||
// Copyright (c) 2019 PaddlePaddle Authors. All Rights Reserved.
|
||||
//
|
||||
// Licensed under the Apache License, Version 2.0 (the "License");
|
||||
// you may not use this file except in compliance with the License.
|
||||
// You may obtain a copy of the License at
|
||||
//
|
||||
// http://www.apache.org/licenses/LICENSE-2.0
|
||||
//
|
||||
// Unless required by applicable law or agreed to in writing, software
|
||||
// distributed under the License is distributed on an "AS IS" BASIS,
|
||||
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
// See the License for the specific language governing permissions and
|
||||
// limitations under the License.
|
||||
|
||||
#pragma once
|
||||
|
||||
#include "paddle_api.h" // NOLINT
|
||||
#include <android/log.h> // NOLINT
|
||||
#include <fstream> // NOLINT
|
||||
#include <string> // NOLINT
|
||||
#include <vector> // NOLINT
|
||||
|
||||
#define TAG "JNI"
|
||||
#define LOGD(...) __android_log_print(ANDROID_LOG_DEBUG, TAG, __VA_ARGS__)
|
||||
#define LOGI(...) __android_log_print(ANDROID_LOG_INFO, TAG, __VA_ARGS__)
|
||||
#define LOGW(...) __android_log_print(ANDROID_LOG_WARN, TAG, __VA_ARGS__)
|
||||
#define LOGE(...) __android_log_print(ANDROID_LOG_ERROR, TAG, __VA_ARGS__)
|
||||
#define LOGF(...) __android_log_print(ANDROID_LOG_FATAL, TAG, __VA_ARGS__)
|
||||
|
||||
#define MIN(a, b) (((a) < (b)) ? (a) : (b))
|
||||
#define MAX(a, b) (((a) > (b)) ? (a) : (b))
|
||||
|
||||
int64_t ShapeProduction(const std::vector<int64_t> &shape);
|
||||
|
||||
template <typename T>
|
||||
bool ReadFile(const std::string &path, std::vector<T> *data) {
|
||||
std::ifstream file(path, std::ifstream::binary);
|
||||
if (file) {
|
||||
file.seekg(0, file.end);
|
||||
int size = file.tellg();
|
||||
LOGD("file size=%lld\n", size);
|
||||
data->resize(size / sizeof(T));
|
||||
file.seekg(0, file.beg);
|
||||
file.read(reinterpret_cast<char *>(data->data()), size);
|
||||
file.close();
|
||||
return true;
|
||||
} else {
|
||||
LOGE("Can't read file from %s\n", path.c_str());
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
template <typename T>
|
||||
bool WriteFile(const std::string &path, const std::vector<T> &data) {
|
||||
std::ofstream file{path, std::ios::binary};
|
||||
if (!file.is_open()) {
|
||||
LOGE("Can't write file to %s\n", path.c_str());
|
||||
return false;
|
||||
}
|
||||
file.write(reinterpret_cast<const char *>(data.data()),
|
||||
data.size() * sizeof(T));
|
||||
file.close();
|
||||
return true;
|
||||
}
|
||||
|
||||
inline int64_t GetCurrentTime() {
|
||||
struct timeval time;
|
||||
gettimeofday(&time, NULL);
|
||||
return 1000000LL * (int64_t)time.tv_sec + (int64_t)time.tv_usec;
|
||||
}
|
||||
|
||||
inline double GetElapsedTime(int64_t time) {
|
||||
return (GetCurrentTime() - time) / 1000.0f;
|
||||
}
|
||||
|
||||
inline paddle::lite_api::PowerMode ParsePowerMode(std::string mode) {
|
||||
if (mode == "LITE_POWER_HIGH") {
|
||||
return paddle::lite_api::LITE_POWER_HIGH;
|
||||
} else if (mode == "LITE_POWER_LOW") {
|
||||
return paddle::lite_api::LITE_POWER_LOW;
|
||||
} else if (mode == "LITE_POWER_FULL") {
|
||||
return paddle::lite_api::LITE_POWER_FULL;
|
||||
} else if (mode == "LITE_POWER_RAND_HIGH") {
|
||||
return paddle::lite_api::LITE_POWER_RAND_HIGH;
|
||||
} else if (mode == "LITE_POWER_RAND_LOW") {
|
||||
return paddle::lite_api::LITE_POWER_RAND_LOW;
|
||||
}
|
||||
return paddle::lite_api::LITE_POWER_NO_BIND;
|
||||
}
|
||||
|
||||
void NHWC3ToNC3HW(const float *src, float *dst, const float *mean,
|
||||
const float *std, int width, int height);
|
||||
|
||||
void NHWC1ToNC1HW(const float *src, float *dst, const float *mean,
|
||||
const float *std, int width, int height);
|
||||
|
||||
// Recognise Result
|
||||
struct RectResult {
|
||||
std::string class_name;
|
||||
int class_id;
|
||||
float score;
|
||||
};
|
||||
|
||||
// Object Detection Result
|
||||
struct ObjectResult {
|
||||
// Rectangle coordinates of detected object: left, right, top, down
|
||||
std::vector<int> rect;
|
||||
// Class id of detected object
|
||||
int class_id;
|
||||
// Confidence of detected object
|
||||
float confidence;
|
||||
// RecModel result
|
||||
std::vector<RectResult> rec_result;
|
||||
};
|
||||
|
||||
// Object for storing all preprocessed data
|
||||
class ImageBlob {
|
||||
public: // NOLINT
|
||||
// image width and height
|
||||
std::vector<float> im_shape_;
|
||||
// Buffer for image data after preprocessing
|
||||
std::vector<float> im_data_;
|
||||
// in net data shape(after pad)
|
||||
std::vector<float> in_net_shape_;
|
||||
// Evaluation image width and height
|
||||
// std::vector<float> eval_im_size_f_;
|
||||
// Scale factor for image size to origin image size
|
||||
std::vector<float> scale_factor_;
|
||||
};
|
||||
|
||||
void nms(std::vector<ObjectResult> *input_boxes, float nms_threshold,
|
||||
bool rec_nms = false);
|
||||
|
||||
void neon_mean_scale(const float *din, float *dout, int size, float *mean,
|
||||
float *scale);
|
||||
|
||||
void activation_function_softmax(const float *src, float *dst, int length);
|
||||
|
||||
inline double GetCurrentUS() {
|
||||
struct timeval time;
|
||||
gettimeofday(&time, NULL);
|
||||
return 1e+6 * time.tv_sec + time.tv_usec;
|
||||
}
|
|
@ -0,0 +1,166 @@
|
|||
// Copyright (c) 2022 PaddlePaddle Authors. All Rights Reserved.
|
||||
//
|
||||
// Licensed under the Apache License, Version 2.0 (the "License");
|
||||
// you may not use this file except in compliance with the License.
|
||||
// You may obtain a copy of the License at
|
||||
//
|
||||
// http://www.apache.org/licenses/LICENSE-2.0
|
||||
//
|
||||
// Unless required by applicable law or agreed to in writing, software
|
||||
// distributed under the License is distributed on an "AS IS" BASIS,
|
||||
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
// See the License for the specific language governing permissions and
|
||||
// limitations under the License.
|
||||
|
||||
#include "VectorSearch.h"
|
||||
#include "Utils.h"
|
||||
#include <cstdio>
|
||||
#include <fstream>
|
||||
#include <iostream>
|
||||
#include <regex>
|
||||
#include <sys/stat.h>
|
||||
#include <unistd.h>
|
||||
|
||||
void VectorSearch::LoadIndexFile() {
|
||||
//"/storage/emulated/0/Android/data/com.baidu.paddle.lite.demo.pp_shitu/files/index/vector.index"
|
||||
std::string file_path = this->index_path;
|
||||
const char *fname = file_path.c_str();
|
||||
this->index = faiss::read_index(fname, 0);
|
||||
}
|
||||
|
||||
// load id_map.txt
|
||||
void VectorSearch::LoadIdMap() {
|
||||
std::string file_path = this->label_path;
|
||||
std::ifstream in(file_path);
|
||||
std::string line;
|
||||
std::vector<std::string> m_vec;
|
||||
if (in) {
|
||||
while (getline(in, line)) {
|
||||
std::regex ws_re("\\s+");
|
||||
std::vector<std::string> v(
|
||||
std::sregex_token_iterator(line.begin(), line.end(), ws_re, -1),
|
||||
std::sregex_token_iterator());
|
||||
if (v.size() != 2) {
|
||||
std::cout << "The number of element for each line in : " << file_path
|
||||
<< "must be 2, exit the program..." << std::endl;
|
||||
exit(1);
|
||||
} else
|
||||
this->id_map.insert(std::pair<long int, std::string>(
|
||||
std::stol(v[0], nullptr, 10), v[1]));
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// doing search
|
||||
const SearchResult &VectorSearch::Search(float *feature, int query_number) {
|
||||
this->D.resize(this->return_k * query_number);
|
||||
this->I.resize(this->return_k * query_number);
|
||||
this->index->search(query_number, feature, return_k, D.data(), I.data());
|
||||
this->sr.return_k = this->return_k;
|
||||
this->sr.D = this->D;
|
||||
this->sr.I = this->I;
|
||||
return this->sr;
|
||||
}
|
||||
|
||||
std::string VectorSearch::GetLabel(faiss::Index::idx_t ind) {
|
||||
if (this->id_map.count(ind)) {
|
||||
return this->id_map[ind];
|
||||
} else {
|
||||
return "None";
|
||||
}
|
||||
}
|
||||
|
||||
int VectorSearch::AddFeature(float *feature, const std::string &label) {
|
||||
this->index->add(1, feature);
|
||||
int id = (int)(this->id_map.size());
|
||||
if (!label.empty()) {
|
||||
this->id_map.insert(std::pair<long int, std::string>(id, label));
|
||||
} else {
|
||||
this->id_map.insert(
|
||||
std::pair<long int, std::string>(id, std::to_string(id)));
|
||||
}
|
||||
return (int)(this->index->ntotal);
|
||||
}
|
||||
|
||||
void VectorSearch::SaveIndex(const std::string &save_file_name) {
|
||||
// save_file_name 为无后缀的文件名字,如 vector、vector_new 等
|
||||
std::string file_path_index, file_path_labelmap;
|
||||
if (save_file_name.empty()) {
|
||||
file_path_index = this->index_path;
|
||||
file_path_labelmap = this->label_path;
|
||||
} else {
|
||||
int begin_pos = (int)this->index_path.find_last_of('/') + 1;
|
||||
int end_pos = (int)this->index_path.find_last_of('.');
|
||||
int replace_len = end_pos - begin_pos;
|
||||
file_path_index =
|
||||
this->index_path.replace(begin_pos, replace_len, save_file_name);
|
||||
|
||||
begin_pos = (int)this->label_path.find_last_of('/') + 1;
|
||||
end_pos = (int)this->label_path.find_last_of('.');
|
||||
replace_len = end_pos - begin_pos;
|
||||
file_path_labelmap =
|
||||
this->label_path.replace(begin_pos, replace_len, save_file_name);
|
||||
}
|
||||
// save index
|
||||
faiss::write_index(this->index, file_path_index.c_str());
|
||||
LOGD("index file saved at [%s]", file_path_index.c_str());
|
||||
|
||||
// save label_map
|
||||
std::ofstream out(file_path_labelmap);
|
||||
std::map<long int, std::string>::iterator iter;
|
||||
for (iter = this->id_map.begin(); iter != this->id_map.end(); iter++) {
|
||||
std::string content = std::to_string(iter->first) + " " + iter->second;
|
||||
out.write(content.c_str(), (int)content.size());
|
||||
out << std::endl;
|
||||
}
|
||||
out.close();
|
||||
}
|
||||
|
||||
void VectorSearch::ClearFeature() {
|
||||
this->index->reset();
|
||||
this->id_map.clear();
|
||||
LOGD("=========================features cleard");
|
||||
}
|
||||
|
||||
const float &VectorSearch::GetThreshold() const { return this->score_thres; }
|
||||
|
||||
bool file_exist(const std::string &file_name) {
|
||||
return access(file_name.c_str(), F_OK) != -1;
|
||||
}
|
||||
|
||||
bool VectorSearch::LoadFromSaveFileName(const std::string &load_file_name) {
|
||||
std::string origin_label_path = GetLabelPath();
|
||||
int begin_pos = (int)origin_label_path.find_last_of('/') + 1;
|
||||
int end_pos = (int)origin_label_path.find_last_of('.');
|
||||
int replace_len = end_pos - begin_pos;
|
||||
std::string new_label_path =
|
||||
origin_label_path.replace(begin_pos, replace_len, load_file_name);
|
||||
|
||||
std::string origin_index_path = GetIndexPath();
|
||||
begin_pos = (int)origin_index_path.find_last_of('/') + 1;
|
||||
end_pos = (int)origin_index_path.find_last_of('.');
|
||||
replace_len = end_pos - begin_pos;
|
||||
std::string new_index_path =
|
||||
origin_index_path.replace(begin_pos, replace_len, load_file_name);
|
||||
|
||||
if (!file_exist(new_label_path) || !file_exist(new_index_path)) {
|
||||
return false;
|
||||
}
|
||||
this->label_path = new_label_path;
|
||||
this->id_map.clear();
|
||||
LoadIdMap();
|
||||
|
||||
this->index_path = new_index_path;
|
||||
LoadIndexFile();
|
||||
return true;
|
||||
}
|
||||
|
||||
std::vector<std::string> VectorSearch::GetLabelList() const {
|
||||
std::vector<std::string> tmp;
|
||||
for (const auto &it : this->id_map) {
|
||||
tmp.emplace_back(it.second);
|
||||
}
|
||||
std::sort(tmp.begin(), tmp.end());
|
||||
tmp.erase(unique(tmp.begin(), tmp.end()), tmp.end());
|
||||
return tmp;
|
||||
}
|
|
@ -0,0 +1,90 @@
|
|||
// Copyright (c) 2020 PaddlePaddle Authors. All Rights Reserved.
|
||||
//
|
||||
// Licensed under the Apache License, Version 2.0 (the "License");
|
||||
// you may not use this file except in compliance with the License.
|
||||
// You may obtain a copy of the License at
|
||||
//
|
||||
// http://www.apache.org/licenses/LICENSE-2.0
|
||||
//
|
||||
// Unless required by applicable law or agreed to in writing, software
|
||||
// distributed under the License is distributed on an "AS IS" BASIS,
|
||||
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
// See the License for the specific language governing permissions and
|
||||
// limitations under the License.
|
||||
|
||||
#pragma once
|
||||
#ifdef WIN32
|
||||
#define OS_PATH_SEP "\\"
|
||||
#else
|
||||
#define OS_PATH_SEP "/"
|
||||
#endif
|
||||
|
||||
#include "include/faiss/Index.h"
|
||||
#include "include/faiss/index_io.h"
|
||||
#include <cstring>
|
||||
#include <map>
|
||||
#include <vector>
|
||||
|
||||
struct SearchResult {
|
||||
std::vector<faiss::Index::idx_t> I;
|
||||
std::vector<float> D;
|
||||
int return_k;
|
||||
};
|
||||
|
||||
class VectorSearch {
|
||||
public:
|
||||
explicit VectorSearch(const std::string &label_path,
|
||||
const std::string &index_path, const int &return_k = 5,
|
||||
const float &score_thres = 0.5) {
|
||||
// // IndexProcess
|
||||
this->label_path = label_path;
|
||||
this->index_path = index_path;
|
||||
this->return_k = return_k;
|
||||
this->score_thres = score_thres;
|
||||
|
||||
LoadIdMap();
|
||||
LoadIndexFile();
|
||||
this->I.resize(this->return_k * this->max_query_number);
|
||||
this->D.resize(this->return_k * this->max_query_number);
|
||||
printf("faiss index load success!\n");
|
||||
};
|
||||
|
||||
void LoadIdMap();
|
||||
|
||||
bool LoadFromSaveFileName(const std::string &load_file_name);
|
||||
|
||||
void LoadIndexFile();
|
||||
|
||||
int AddFeature(float *feature, const std::string &label = "");
|
||||
|
||||
const SearchResult &Search(float *feature, int query_number);
|
||||
|
||||
const int GetIndexLength() { return this->index->ntotal; }
|
||||
|
||||
void SaveIndex(const std::string &save_path = "");
|
||||
|
||||
std::string GetIndexPath() { return this->index_path; }
|
||||
|
||||
std::string GetLabelPath() { return this->label_path; }
|
||||
|
||||
std::string GetLabel(faiss::Index::idx_t ind);
|
||||
|
||||
void ClearFeature();
|
||||
|
||||
const float &GetThreshold() const;
|
||||
|
||||
std::vector<std::string> GetLabelList() const;
|
||||
|
||||
private:
|
||||
std::string index_path;
|
||||
std::string label_path;
|
||||
int return_k = 5;
|
||||
float score_thres = 0.5;
|
||||
|
||||
std::map<long int, std::string> id_map;
|
||||
faiss::Index *index;
|
||||
int max_query_number = 6;
|
||||
std::vector<float> D;
|
||||
std::vector<faiss::Index::idx_t> I;
|
||||
SearchResult sr;
|
||||
};
|
|
@ -0,0 +1,127 @@
|
|||
/*
|
||||
* Copyright (C) 2014 The Android Open Source Project
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
package com.baidu.paddle.lite.demo.common;
|
||||
|
||||
import android.content.res.Configuration;
|
||||
import android.os.Bundle;
|
||||
import android.preference.PreferenceActivity;
|
||||
import android.support.annotation.LayoutRes;
|
||||
import android.support.annotation.Nullable;
|
||||
import android.support.v7.app.ActionBar;
|
||||
import android.support.v7.app.AppCompatDelegate;
|
||||
import android.support.v7.widget.Toolbar;
|
||||
import android.view.MenuInflater;
|
||||
import android.view.View;
|
||||
import android.view.ViewGroup;
|
||||
|
||||
/**
|
||||
* A {@link PreferenceActivity} which implements and proxies the necessary calls
|
||||
* to be used with AppCompat.
|
||||
* <p>
|
||||
* This technique can be used with an {@link android.app.Activity} class, not just
|
||||
* {@link PreferenceActivity}.
|
||||
*/
|
||||
public abstract class AppCompatPreferenceActivity extends PreferenceActivity {
|
||||
private AppCompatDelegate mDelegate;
|
||||
|
||||
@Override
|
||||
protected void onCreate(Bundle savedInstanceState) {
|
||||
getDelegate().installViewFactory();
|
||||
getDelegate().onCreate(savedInstanceState);
|
||||
super.onCreate(savedInstanceState);
|
||||
}
|
||||
|
||||
@Override
|
||||
protected void onPostCreate(Bundle savedInstanceState) {
|
||||
super.onPostCreate(savedInstanceState);
|
||||
getDelegate().onPostCreate(savedInstanceState);
|
||||
}
|
||||
|
||||
public ActionBar getSupportActionBar() {
|
||||
return getDelegate().getSupportActionBar();
|
||||
}
|
||||
|
||||
public void setSupportActionBar(@Nullable Toolbar toolbar) {
|
||||
getDelegate().setSupportActionBar(toolbar);
|
||||
}
|
||||
|
||||
@Override
|
||||
public MenuInflater getMenuInflater() {
|
||||
return getDelegate().getMenuInflater();
|
||||
}
|
||||
|
||||
@Override
|
||||
public void setContentView(@LayoutRes int layoutResID) {
|
||||
getDelegate().setContentView(layoutResID);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void setContentView(View view) {
|
||||
getDelegate().setContentView(view);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void setContentView(View view, ViewGroup.LayoutParams params) {
|
||||
getDelegate().setContentView(view, params);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void addContentView(View view, ViewGroup.LayoutParams params) {
|
||||
getDelegate().addContentView(view, params);
|
||||
}
|
||||
|
||||
@Override
|
||||
protected void onPostResume() {
|
||||
super.onPostResume();
|
||||
getDelegate().onPostResume();
|
||||
}
|
||||
|
||||
@Override
|
||||
protected void onTitleChanged(CharSequence title, int color) {
|
||||
super.onTitleChanged(title, color);
|
||||
getDelegate().setTitle(title);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onConfigurationChanged(Configuration newConfig) {
|
||||
super.onConfigurationChanged(newConfig);
|
||||
getDelegate().onConfigurationChanged(newConfig);
|
||||
}
|
||||
|
||||
@Override
|
||||
protected void onStop() {
|
||||
super.onStop();
|
||||
getDelegate().onStop();
|
||||
}
|
||||
|
||||
@Override
|
||||
protected void onDestroy() {
|
||||
super.onDestroy();
|
||||
getDelegate().onDestroy();
|
||||
}
|
||||
|
||||
public void invalidateOptionsMenu() {
|
||||
getDelegate().invalidateOptionsMenu();
|
||||
}
|
||||
|
||||
private AppCompatDelegate getDelegate() {
|
||||
if (mDelegate == null) {
|
||||
mDelegate = AppCompatDelegate.create(this, null);
|
||||
}
|
||||
return mDelegate;
|
||||
}
|
||||
}
|
|
@ -0,0 +1,259 @@
|
|||
/*
|
||||
* Copyright (C) 2014 The Android Open Source Project
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
package com.baidu.paddle.lite.demo.common;
|
||||
|
||||
import android.content.Context;
|
||||
import android.content.res.AssetManager;
|
||||
import android.content.pm.PackageManager;
|
||||
import android.content.res.Resources;
|
||||
import android.hardware.Camera;
|
||||
import android.opengl.GLES20;
|
||||
import android.os.Environment;
|
||||
import android.util.Log;
|
||||
import android.view.Surface;
|
||||
import android.view.WindowManager;
|
||||
|
||||
import java.io.*;
|
||||
import java.util.List;
|
||||
|
||||
public class Utils {
|
||||
private static final String TAG = Utils.class.getSimpleName();
|
||||
|
||||
public static void RecursiveCreateDirectories(String fileDir) {
|
||||
String[] fileDirs = fileDir.split("\\/");
|
||||
String topPath = "";
|
||||
for (int i = 0; i < fileDirs.length; i++) {
|
||||
topPath += "/" + fileDirs[i];
|
||||
File file = new File(topPath);
|
||||
if (file.exists()) {
|
||||
continue;
|
||||
} else {
|
||||
file.mkdir();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
public static void copyFileFromAssets(Context appCtx, String srcPath, String dstPath) {
|
||||
if (srcPath.isEmpty() || dstPath.isEmpty()) {
|
||||
return;
|
||||
}
|
||||
String dstDir = dstPath.substring(0, dstPath.lastIndexOf('/'));
|
||||
if (dstDir.length() > 0) {
|
||||
RecursiveCreateDirectories(dstDir);
|
||||
}
|
||||
InputStream is = null;
|
||||
OutputStream os = null;
|
||||
if (new File(dstPath).exists())
|
||||
{
|
||||
return ;
|
||||
}
|
||||
try {
|
||||
is = new BufferedInputStream(appCtx.getAssets().open(srcPath));
|
||||
os = new BufferedOutputStream(new FileOutputStream(new File(dstPath)));
|
||||
byte[] buffer = new byte[1024];
|
||||
int length = 0;
|
||||
while ((length = is.read(buffer)) != -1) {
|
||||
os.write(buffer, 0, length);
|
||||
}
|
||||
} catch (FileNotFoundException e) {
|
||||
e.printStackTrace();
|
||||
} catch (IOException e) {
|
||||
e.printStackTrace();
|
||||
} finally {
|
||||
try {
|
||||
os.close();
|
||||
is.close();
|
||||
} catch (IOException e) {
|
||||
e.printStackTrace();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
public static void copyDirectoryFromAssets(Context appCtx, String srcDir, String dstDir) {
|
||||
if (srcDir.isEmpty() || dstDir.isEmpty()) {
|
||||
return;
|
||||
}
|
||||
try {
|
||||
if (!new File(dstDir).exists()) {
|
||||
new File(dstDir).mkdirs();
|
||||
}
|
||||
for (String fileName : appCtx.getAssets().list(srcDir)) {
|
||||
String srcSubPath = srcDir + File.separator + fileName;
|
||||
String dstSubPath = dstDir + File.separator + fileName;
|
||||
if (new File(srcSubPath).isDirectory()) {
|
||||
copyDirectoryFromAssets(appCtx, srcSubPath, dstSubPath);
|
||||
} else {
|
||||
copyFileFromAssets(appCtx, srcSubPath, dstSubPath);
|
||||
}
|
||||
}
|
||||
} catch (Exception e) {
|
||||
e.printStackTrace();
|
||||
}
|
||||
}
|
||||
|
||||
public static float[] parseFloatsFromString(String string, String delimiter) {
|
||||
String[] pieces = string.trim().toLowerCase().split(delimiter);
|
||||
float[] floats = new float[pieces.length];
|
||||
for (int i = 0; i < pieces.length; i++) {
|
||||
floats[i] = Float.parseFloat(pieces[i].trim());
|
||||
}
|
||||
return floats;
|
||||
}
|
||||
|
||||
public static long[] parseLongsFromString(String string, String delimiter) {
|
||||
String[] pieces = string.trim().toLowerCase().split(delimiter);
|
||||
long[] longs = new long[pieces.length];
|
||||
for (int i = 0; i < pieces.length; i++) {
|
||||
longs[i] = Long.parseLong(pieces[i].trim());
|
||||
}
|
||||
return longs;
|
||||
}
|
||||
|
||||
public static String getSDCardDirectory() {
|
||||
return Environment.getExternalStorageDirectory().getAbsolutePath();
|
||||
}
|
||||
|
||||
public static String getDCIMDirectory() {
|
||||
return Environment.getExternalStoragePublicDirectory(Environment.DIRECTORY_DCIM).getAbsolutePath();
|
||||
}
|
||||
|
||||
public static Camera.Size getOptimalPreviewSize(List<Camera.Size> sizes, int w, int h) {
|
||||
final double ASPECT_TOLERANCE = 0.1;
|
||||
double targetRatio = (double) w / h;
|
||||
if (sizes == null) return null;
|
||||
|
||||
Camera.Size optimalSize = null;
|
||||
double minDiff = Double.MAX_VALUE;
|
||||
|
||||
int targetHeight = h;
|
||||
|
||||
// Try to find an size match aspect ratio and size
|
||||
for (Camera.Size size : sizes) {
|
||||
double ratio = (double) size.width / size.height;
|
||||
if (Math.abs(ratio - targetRatio) > ASPECT_TOLERANCE) continue;
|
||||
if (Math.abs(size.height - targetHeight) < minDiff) {
|
||||
optimalSize = size;
|
||||
minDiff = Math.abs(size.height - targetHeight);
|
||||
}
|
||||
}
|
||||
|
||||
// Cannot find the one match the aspect ratio, ignore the requirement
|
||||
if (optimalSize == null) {
|
||||
minDiff = Double.MAX_VALUE;
|
||||
for (Camera.Size size : sizes) {
|
||||
if (Math.abs(size.height - targetHeight) < minDiff) {
|
||||
optimalSize = size;
|
||||
minDiff = Math.abs(size.height - targetHeight);
|
||||
}
|
||||
}
|
||||
}
|
||||
return optimalSize;
|
||||
}
|
||||
|
||||
public static int getScreenWidth() {
|
||||
return Resources.getSystem().getDisplayMetrics().widthPixels;
|
||||
}
|
||||
|
||||
public static int getScreenHeight() {
|
||||
return Resources.getSystem().getDisplayMetrics().heightPixels;
|
||||
}
|
||||
|
||||
public static int getCameraDisplayOrientation(Context context, int cameraId) {
|
||||
android.hardware.Camera.CameraInfo info = new android.hardware.Camera.CameraInfo();
|
||||
android.hardware.Camera.getCameraInfo(cameraId, info);
|
||||
WindowManager wm = (WindowManager) context.getSystemService(Context.WINDOW_SERVICE);
|
||||
int rotation = wm.getDefaultDisplay().getRotation();
|
||||
int degrees = 0;
|
||||
switch (rotation) {
|
||||
case Surface.ROTATION_0:
|
||||
degrees = 0;
|
||||
break;
|
||||
case Surface.ROTATION_90:
|
||||
degrees = 90;
|
||||
break;
|
||||
case Surface.ROTATION_180:
|
||||
degrees = 180;
|
||||
break;
|
||||
case Surface.ROTATION_270:
|
||||
degrees = 270;
|
||||
break;
|
||||
}
|
||||
int result;
|
||||
if (info.facing == Camera.CameraInfo.CAMERA_FACING_FRONT) {
|
||||
result = (info.orientation + degrees) % 360;
|
||||
result = (360 - result) % 360; // compensate the mirror
|
||||
} else {
|
||||
// back-facing
|
||||
result = (info.orientation - degrees + 360) % 360;
|
||||
}
|
||||
return result;
|
||||
}
|
||||
|
||||
public static int createShaderProgram(String vss, String fss) {
|
||||
int vshader = GLES20.glCreateShader(GLES20.GL_VERTEX_SHADER);
|
||||
GLES20.glShaderSource(vshader, vss);
|
||||
GLES20.glCompileShader(vshader);
|
||||
int[] status = new int[1];
|
||||
GLES20.glGetShaderiv(vshader, GLES20.GL_COMPILE_STATUS, status, 0);
|
||||
if (status[0] == 0) {
|
||||
Log.e(TAG, GLES20.glGetShaderInfoLog(vshader));
|
||||
GLES20.glDeleteShader(vshader);
|
||||
vshader = 0;
|
||||
return 0;
|
||||
}
|
||||
|
||||
int fshader = GLES20.glCreateShader(GLES20.GL_FRAGMENT_SHADER);
|
||||
GLES20.glShaderSource(fshader, fss);
|
||||
GLES20.glCompileShader(fshader);
|
||||
GLES20.glGetShaderiv(fshader, GLES20.GL_COMPILE_STATUS, status, 0);
|
||||
if (status[0] == 0) {
|
||||
Log.e(TAG, GLES20.glGetShaderInfoLog(fshader));
|
||||
GLES20.glDeleteShader(vshader);
|
||||
GLES20.glDeleteShader(fshader);
|
||||
fshader = 0;
|
||||
return 0;
|
||||
}
|
||||
|
||||
int program = GLES20.glCreateProgram();
|
||||
GLES20.glAttachShader(program, vshader);
|
||||
GLES20.glAttachShader(program, fshader);
|
||||
GLES20.glLinkProgram(program);
|
||||
GLES20.glDeleteShader(vshader);
|
||||
GLES20.glDeleteShader(fshader);
|
||||
GLES20.glGetProgramiv(program, GLES20.GL_LINK_STATUS, status, 0);
|
||||
if (status[0] == 0) {
|
||||
Log.e(TAG, GLES20.glGetProgramInfoLog(program));
|
||||
program = 0;
|
||||
return 0;
|
||||
}
|
||||
GLES20.glValidateProgram(program);
|
||||
GLES20.glGetProgramiv(program, GLES20.GL_VALIDATE_STATUS, status, 0);
|
||||
if (status[0] == 0) {
|
||||
Log.e(TAG, GLES20.glGetProgramInfoLog(program));
|
||||
GLES20.glDeleteProgram(program);
|
||||
program = 0;
|
||||
return 0;
|
||||
}
|
||||
|
||||
return program;
|
||||
}
|
||||
|
||||
public static boolean isSupportedNPU() {
|
||||
String hardware = android.os.Build.HARDWARE;
|
||||
return hardware.equalsIgnoreCase("kirin810") || hardware.equalsIgnoreCase("kirin990");
|
||||
}
|
||||
}
|
|
@ -0,0 +1,701 @@
|
|||
/*
|
||||
* Copyright (C) 2014 The Android Open Source Project
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
package com.baidu.paddle.lite.demo.pp_shitu;
|
||||
|
||||
import android.Manifest;
|
||||
import android.annotation.SuppressLint;
|
||||
import android.app.AlertDialog;
|
||||
import android.app.ProgressDialog;
|
||||
import android.content.ContentResolver;
|
||||
import android.content.Context;
|
||||
import android.content.Intent;
|
||||
import android.content.SharedPreferences;
|
||||
import android.content.pm.ApplicationInfo;
|
||||
import android.content.pm.PackageManager;
|
||||
import android.database.Cursor;
|
||||
import android.graphics.Bitmap;
|
||||
import android.graphics.BitmapFactory;
|
||||
import android.graphics.Typeface;
|
||||
import android.net.Uri;
|
||||
import android.os.Bundle;
|
||||
import android.os.Handler;
|
||||
import android.os.HandlerThread;
|
||||
import android.os.Message;
|
||||
import android.preference.PreferenceManager;
|
||||
import android.provider.MediaStore;
|
||||
import android.support.annotation.NonNull;
|
||||
import android.support.v4.app.ActivityCompat;
|
||||
import android.support.v4.content.ContextCompat;
|
||||
import android.support.v7.app.AppCompatActivity;
|
||||
import android.text.SpannableString;
|
||||
import android.text.Spanned;
|
||||
import android.text.style.StyleSpan;
|
||||
import android.util.Log;
|
||||
import android.view.Menu;
|
||||
import android.view.MenuInflater;
|
||||
import android.view.MenuItem;
|
||||
import android.view.View;
|
||||
import android.widget.Button;
|
||||
import android.widget.EditText;
|
||||
import android.widget.ImageView;
|
||||
import android.widget.TextView;
|
||||
import android.widget.Toast;
|
||||
|
||||
import com.baidu.paddle.lite.demo.common.Utils;
|
||||
|
||||
import java.io.File;
|
||||
import java.io.IOException;
|
||||
import java.io.InputStream;
|
||||
|
||||
public class MainActivity extends AppCompatActivity {
|
||||
private static final String TAG = MainActivity.class.getSimpleName();
|
||||
public static final int OPEN_QUERY_PHOTO_REQUEST_CODE = 0;
|
||||
public static final int TAKE_QUERY_PHOTO_REQUEST_CODE = 1;
|
||||
public static final int OPEN_GALLERY_PHOTO_REQUEST_CODE = 4;
|
||||
public static final int TAKE_GALLERY_PHOTO_REQUEST_CODE = 5;
|
||||
public static final int CLEAR_FEATURE_REQUEST_CODE = 6;
|
||||
public static final int REQUEST_LOAD_MODEL = 0;
|
||||
public static final int REQUEST_RUN_MODEL = 1;
|
||||
public static final int RESPONSE_LOAD_MODEL_SUCCESSED = 0;
|
||||
public static final int RESPONSE_LOAD_MODEL_FAILED = 1;
|
||||
public static final int RESPONSE_RUN_MODEL_SUCCESSED = 2;
|
||||
public static final int RESPONSE_RUN_MODEL_FAILED = 3;
|
||||
|
||||
protected ProgressDialog pbLoadModel = null;
|
||||
protected ProgressDialog pbRunModel = null;
|
||||
protected Handler receiver = null; // Receive messages from worker thread
|
||||
protected Handler sender = null; // Send command to worker thread
|
||||
protected HandlerThread worker = null; // Worker thread to load&run model
|
||||
|
||||
// UI components of image classification
|
||||
protected ImageView ivInputImage;
|
||||
protected TextView tvTop1Result;
|
||||
protected TextView tvInferenceTime;
|
||||
protected TextView tvSimilarity;
|
||||
protected TextView tvIndexName;
|
||||
protected TextView tv_description;
|
||||
|
||||
//protected Switch mSwitch;
|
||||
|
||||
// Model settings of image classification
|
||||
protected String modelPath = "";
|
||||
protected String labelPath = "";
|
||||
protected String indexPath = "";
|
||||
|
||||
protected String imagePath = "";
|
||||
protected String DetModelPath = "";
|
||||
protected String RecModelPath = "";
|
||||
protected int cpuThreadNum = 1;
|
||||
protected EditText label_name;
|
||||
protected Button label_botton;
|
||||
protected Button cancel_botton;
|
||||
protected boolean add_gallery = false;
|
||||
protected int topk = 3;
|
||||
protected String cpuMode = "";
|
||||
protected long[] detinputShape = new long[]{};
|
||||
protected long[] recinputShape = new long[]{};
|
||||
protected boolean useGpu = false;
|
||||
protected Native predictor = new Native();
|
||||
|
||||
ImageView mImage_add_query;
|
||||
ImageView mImage_take_query;
|
||||
ImageView mImage_add_gallery;
|
||||
ImageView mImage_take_gallery;
|
||||
ImageView mImage_save;
|
||||
ImageView mShow_index;
|
||||
View help;
|
||||
View reset;
|
||||
|
||||
@SuppressLint("HandlerLeak")
|
||||
@Override
|
||||
protected void onCreate(Bundle savedInstanceState) {
|
||||
super.onCreate(savedInstanceState);
|
||||
setContentView(R.layout.activity_main);
|
||||
|
||||
// Clear all setting items to avoid app crashing due to the incorrect settings
|
||||
SharedPreferences sharedPreferences = PreferenceManager.getDefaultSharedPreferences(this);
|
||||
SharedPreferences.Editor editor = sharedPreferences.edit();
|
||||
editor.clear();
|
||||
editor.apply();
|
||||
|
||||
// Prepare the worker thread for mode loading and inference
|
||||
receiver = new Handler() {
|
||||
@Override
|
||||
public void handleMessage(Message msg) {
|
||||
switch (msg.what) {
|
||||
case RESPONSE_LOAD_MODEL_SUCCESSED:
|
||||
pbLoadModel.dismiss();
|
||||
onLoadModelSuccessed();
|
||||
break;
|
||||
case RESPONSE_LOAD_MODEL_FAILED:
|
||||
pbLoadModel.dismiss();
|
||||
Toast.makeText(MainActivity.this, "Load model failed!", Toast.LENGTH_SHORT).show();
|
||||
onLoadModelFailed();
|
||||
break;
|
||||
case RESPONSE_RUN_MODEL_SUCCESSED:
|
||||
pbRunModel.dismiss();
|
||||
onRunModelSuccessed();
|
||||
break;
|
||||
case RESPONSE_RUN_MODEL_FAILED:
|
||||
pbRunModel.dismiss();
|
||||
Toast.makeText(MainActivity.this, "Run model failed!", Toast.LENGTH_SHORT).show();
|
||||
onRunModelFailed();
|
||||
break;
|
||||
default:
|
||||
break;
|
||||
}
|
||||
}
|
||||
};
|
||||
worker = new HandlerThread("Predictor Worker");
|
||||
worker.start();
|
||||
sender = new Handler(worker.getLooper()) {
|
||||
public void handleMessage(Message msg) {
|
||||
switch (msg.what) {
|
||||
case REQUEST_LOAD_MODEL:
|
||||
// Load model and reload test image
|
||||
if (onLoadModel()) {
|
||||
receiver.sendEmptyMessage(RESPONSE_LOAD_MODEL_SUCCESSED);
|
||||
} else {
|
||||
receiver.sendEmptyMessage(RESPONSE_LOAD_MODEL_FAILED);
|
||||
}
|
||||
break;
|
||||
case REQUEST_RUN_MODEL:
|
||||
// Run model if model is loaded
|
||||
if (onRunModel()) {
|
||||
receiver.sendEmptyMessage(RESPONSE_RUN_MODEL_SUCCESSED);
|
||||
} else {
|
||||
receiver.sendEmptyMessage(RESPONSE_RUN_MODEL_FAILED);
|
||||
}
|
||||
break;
|
||||
default:
|
||||
break;
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
// Setup the UI components
|
||||
ivInputImage = findViewById(R.id.iv_input_image);
|
||||
tvTop1Result = findViewById(R.id.tv_top1_result);
|
||||
tvInferenceTime = findViewById(R.id.tv_inference_time);
|
||||
tvSimilarity = findViewById(R.id.similairy);
|
||||
tvIndexName = findViewById(R.id.index_name);
|
||||
tv_description = findViewById(R.id.description);
|
||||
|
||||
// 启动时隐藏输入label的输入框和确定按钮
|
||||
label_name = findViewById(R.id.label_name);
|
||||
label_name.setVisibility(View.INVISIBLE);
|
||||
label_botton = findViewById(R.id.label_botton);
|
||||
label_botton.setVisibility(View.INVISIBLE);
|
||||
cancel_botton = findViewById(R.id.cancel_botton);
|
||||
cancel_botton.setVisibility(View.INVISIBLE);
|
||||
|
||||
mImage_add_query = (ImageView) findViewById(R.id.add_query);
|
||||
mImage_take_query = (ImageView) findViewById(R.id.take_query);
|
||||
mImage_add_gallery = (ImageView) findViewById(R.id.add_gallery);
|
||||
mImage_take_gallery = (ImageView) findViewById(R.id.take_gallery);
|
||||
mImage_save = (ImageView) findViewById(R.id.save);
|
||||
mShow_index = (ImageView) findViewById(R.id.show_index);
|
||||
|
||||
File fileindex = new File(getExternalFilesDir(null) + "/" + "index/latest.index");
|
||||
if (fileindex.exists()) {
|
||||
this.indexPath = "index/latest.index";
|
||||
} else {
|
||||
this.indexPath = "index/original.index";
|
||||
}
|
||||
File filelabel = new File(getExternalFilesDir(null) + "/" + "index/latest.txt");
|
||||
if (filelabel.exists()) {
|
||||
this.labelPath = "index/latest.txt";
|
||||
} else {
|
||||
this.labelPath = "index/original.txt";
|
||||
}
|
||||
mImage_add_query.setOnClickListener(v -> {
|
||||
if (requestAllPermissions()) {
|
||||
label_name.setVisibility(View.INVISIBLE);
|
||||
label_botton.setVisibility(View.INVISIBLE);
|
||||
cancel_botton.setVisibility(View.INVISIBLE);
|
||||
openQueryPhoto();
|
||||
} else {
|
||||
Toast.makeText(MainActivity.this, "请开启相机和读写文件权限", Toast.LENGTH_SHORT).show();
|
||||
}
|
||||
});
|
||||
mImage_take_query.setOnClickListener(v -> {
|
||||
if (requestAllPermissions()) {
|
||||
label_name.setVisibility(View.INVISIBLE);
|
||||
label_botton.setVisibility(View.INVISIBLE);
|
||||
cancel_botton.setVisibility(View.INVISIBLE);
|
||||
takeQueryPhoto();
|
||||
} else {
|
||||
Toast.makeText(MainActivity.this, "请开启相机和读写文件权限", Toast.LENGTH_SHORT).show();
|
||||
}
|
||||
});
|
||||
mImage_add_gallery.setOnClickListener(v -> {
|
||||
if (requestAllPermissions()) {
|
||||
label_name.setText("");
|
||||
label_name.setVisibility(View.INVISIBLE);
|
||||
label_botton.setVisibility(View.INVISIBLE);
|
||||
cancel_botton.setVisibility(View.INVISIBLE);
|
||||
openGalleryPhoto();
|
||||
} else {
|
||||
Toast.makeText(MainActivity.this, "请开启相机和读写文件权限", Toast.LENGTH_SHORT).show();
|
||||
}
|
||||
});
|
||||
mImage_take_gallery.setOnClickListener(v -> {
|
||||
if (requestAllPermissions()) {
|
||||
label_name.setVisibility(View.INVISIBLE);
|
||||
label_botton.setVisibility(View.INVISIBLE);
|
||||
cancel_botton.setVisibility(View.INVISIBLE);
|
||||
takeGalleryPhoto();
|
||||
} else {
|
||||
Toast.makeText(MainActivity.this, "请开启相机和读写文件权限", Toast.LENGTH_SHORT).show();
|
||||
}
|
||||
});
|
||||
mImage_save.setOnClickListener(v -> {
|
||||
if (requestAllPermissions()) {
|
||||
label_name.setVisibility(View.INVISIBLE);
|
||||
label_botton.setVisibility(View.INVISIBLE);
|
||||
cancel_botton.setVisibility(View.INVISIBLE);
|
||||
saveIndex();
|
||||
} else {
|
||||
Toast.makeText(MainActivity.this, "请开启相机和读写文件权限", Toast.LENGTH_SHORT).show();
|
||||
}
|
||||
});
|
||||
|
||||
mShow_index.setOnClickListener(view -> {
|
||||
// String fullpath = getExternalFilesDir(null) + "/" + labelPath;
|
||||
if (requestAllPermissions()) {
|
||||
String labelfile_content = predictor.getClassname();
|
||||
AlertDialog alertDialog = new AlertDialog.Builder(MainActivity.this)
|
||||
//标题
|
||||
.setTitle(labelPath)
|
||||
//内容
|
||||
.setMessage(labelfile_content)
|
||||
//图标
|
||||
.setIcon(R.mipmap.ic_launcher)
|
||||
.setPositiveButton("确认", null)
|
||||
.create();
|
||||
alertDialog.show();
|
||||
} else {
|
||||
Toast.makeText(MainActivity.this, "请开启相机和读写文件权限", Toast.LENGTH_SHORT).show();
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
|
||||
@Override
|
||||
protected void onResume() {
|
||||
super.onResume();
|
||||
SharedPreferences sharedPreferences = PreferenceManager.getDefaultSharedPreferences(this);
|
||||
boolean settingsChanged;
|
||||
String model_path = sharedPreferences.getString(getString(R.string.MODEL_PATH_KEY),
|
||||
getString(R.string.MODEL_PATH_DEFAULT));
|
||||
String label_path = sharedPreferences.getString(getString(R.string.LABEL_PATH_KEY),
|
||||
getString(R.string.LABEL_PATH_DEFAULT));
|
||||
String index_path = sharedPreferences.getString(getString(R.string.INDEX_PATH_KEY),
|
||||
getString(R.string.INDEX_PATH_DEFAULT));
|
||||
File fileindex = new File(getExternalFilesDir(null) + "/" + "index/latest.index");
|
||||
if (fileindex.exists()) {
|
||||
this.indexPath = "index/latest.index";
|
||||
} else {
|
||||
this.indexPath = "index/original.index";
|
||||
}
|
||||
File filelabel = new File(getExternalFilesDir(null) + "/" + "index/latest.txt");
|
||||
if (filelabel.exists()) {
|
||||
this.labelPath = "index/latest.txt";
|
||||
} else {
|
||||
this.labelPath = "index/original.txt";
|
||||
}
|
||||
|
||||
String image_path = "images/demo.jpg";
|
||||
settingsChanged = !model_path.equalsIgnoreCase(modelPath);
|
||||
settingsChanged |= !image_path.equalsIgnoreCase(imagePath);
|
||||
int cpu_thread_num = Integer.parseInt("4");
|
||||
settingsChanged |= cpu_thread_num != cpuThreadNum;
|
||||
long[] det_input_shape =
|
||||
Utils.parseLongsFromString("1,3,640,640", ",");
|
||||
long[] rec_input_shape =
|
||||
Utils.parseLongsFromString("1,3,224,224", ",");
|
||||
String cpu_power_mode = "LITE_POWER_HIGH";
|
||||
settingsChanged |= !cpu_power_mode.equalsIgnoreCase(cpuMode);
|
||||
int top_k = Integer.parseInt("3");
|
||||
settingsChanged |= top_k != topk;
|
||||
settingsChanged |= det_input_shape.length != detinputShape.length;
|
||||
settingsChanged |= rec_input_shape.length != recinputShape.length;
|
||||
if (!settingsChanged) {
|
||||
for (int i = 0; i < det_input_shape.length; i++) {
|
||||
settingsChanged |= det_input_shape[i] != detinputShape[i];
|
||||
}
|
||||
for (int i = 0; i < rec_input_shape.length; i++) {
|
||||
settingsChanged |= rec_input_shape[i] != recinputShape[i];
|
||||
}
|
||||
}
|
||||
if (settingsChanged || useGpu) {
|
||||
modelPath = model_path;
|
||||
imagePath = image_path;
|
||||
cpuThreadNum = cpu_thread_num;
|
||||
detinputShape = det_input_shape;
|
||||
recinputShape = rec_input_shape;
|
||||
DetModelPath = modelPath;
|
||||
RecModelPath = modelPath;
|
||||
topk = top_k;
|
||||
cpuMode = cpu_power_mode;
|
||||
loadModel();
|
||||
}
|
||||
}
|
||||
|
||||
public void loadModel() {
|
||||
pbLoadModel = ProgressDialog.show(this, "", "Loading model...", false, false);
|
||||
sender.sendEmptyMessage(REQUEST_LOAD_MODEL);
|
||||
}
|
||||
|
||||
public void runModel() {
|
||||
pbRunModel = ProgressDialog.show(this, "", "Running model...", false, false);
|
||||
sender.sendEmptyMessage(REQUEST_RUN_MODEL);
|
||||
}
|
||||
|
||||
|
||||
public boolean onLoadModel() {
|
||||
Context context = getBaseContext();
|
||||
ApplicationInfo info = context.getApplicationInfo();
|
||||
int debug_mode = info.flags & ApplicationInfo.FLAG_DEBUGGABLE;
|
||||
// push model to sdcard
|
||||
String realDetModelDir = getExternalFilesDir(null) + "/" + DetModelPath;
|
||||
if (debug_mode != 0) {
|
||||
Utils.copyDirectoryFromAssets(this, DetModelPath, realDetModelDir);
|
||||
}
|
||||
String realRecModelDir = getExternalFilesDir(null) + "/" + RecModelPath;
|
||||
if (debug_mode != 0) {
|
||||
Utils.copyDirectoryFromAssets(this, RecModelPath, realRecModelDir);
|
||||
}
|
||||
// push label to sdcard
|
||||
String realLabelPath = getExternalFilesDir(null) + "/" + labelPath;
|
||||
if (debug_mode != 0) {
|
||||
Utils.copyFileFromAssets(this, labelPath, realLabelPath);
|
||||
}
|
||||
String realIndexDir = getExternalFilesDir(null) + "/" + indexPath;
|
||||
if (debug_mode != 0) {
|
||||
Utils.copyFileFromAssets(this, indexPath, realIndexDir);
|
||||
}
|
||||
return predictor.init(realDetModelDir, realRecModelDir, realLabelPath, realIndexDir,
|
||||
detinputShape, recinputShape, cpuThreadNum, 0, 1, topk, add_gallery, cpuMode);
|
||||
}
|
||||
|
||||
public boolean onRunModel() {
|
||||
return predictor.isLoaded() && predictor.process();
|
||||
}
|
||||
|
||||
public void onLoadModelSuccessed() {
|
||||
// Load test image from path and run model
|
||||
try {
|
||||
if (imagePath.isEmpty()) {
|
||||
return;
|
||||
}
|
||||
Bitmap image;
|
||||
// Read test image file from custom path if the first character of mode path is '/', otherwise read test
|
||||
// image file from assets
|
||||
if (imagePath.charAt(0) != '/') {
|
||||
InputStream imageStream = getAssets().open(imagePath);
|
||||
image = BitmapFactory.decodeStream(imageStream);
|
||||
} else {
|
||||
if (!new File(imagePath).exists()) {
|
||||
return;
|
||||
}
|
||||
image = BitmapFactory.decodeFile(imagePath);
|
||||
}
|
||||
if (image != null && predictor.isLoaded()) {
|
||||
predictor.setInputImage(image);
|
||||
runModel();
|
||||
}
|
||||
} catch (IOException e) {
|
||||
e.printStackTrace();
|
||||
}
|
||||
}
|
||||
|
||||
public void onLoadModelFailed() {
|
||||
}
|
||||
|
||||
@SuppressLint("SetTextI18n")
|
||||
public void onRunModelSuccessed() {
|
||||
// Obtain results and update UI
|
||||
tvInferenceTime.setText("预测时间:" + predictor.inferenceTime() + " ms");
|
||||
Bitmap inputImage = predictor.inputImage();
|
||||
if (inputImage != null) {
|
||||
ivInputImage.setImageBitmap(inputImage);
|
||||
}
|
||||
String res = predictor.top1Result();
|
||||
if (res.contains(",")) {
|
||||
String[] res_split = res.split(",");
|
||||
tvTop1Result.setText("类别:" + res_split[0].trim());
|
||||
res_split[1] = res_split[1].trim().substring(0, 5);
|
||||
tvSimilarity.setText("相似度:" + res_split[1]);
|
||||
tvIndexName.setText("检索库名称:" + indexPath.split("/")[1].split("\\.")[0]);
|
||||
}
|
||||
}
|
||||
|
||||
public void onRunModelFailed() {
|
||||
}
|
||||
|
||||
public void onImageChanged(Bitmap image) {
|
||||
// Rerun model if users pick test image from gallery or camera
|
||||
if (image != null && predictor.isLoaded()) {
|
||||
label_name.setVisibility(View.INVISIBLE);
|
||||
label_botton.setVisibility(View.INVISIBLE);
|
||||
cancel_botton.setVisibility(View.INVISIBLE);
|
||||
predictor.setAddGallery(0);
|
||||
predictor.setInputImage(image);
|
||||
runModel();
|
||||
tv_description.setText("【待识别图片】");
|
||||
}
|
||||
}
|
||||
|
||||
@SuppressLint("SetTextI18n")
|
||||
public void onAddGallery(Bitmap image) {
|
||||
if (image != null && predictor.isLoaded()) {
|
||||
ivInputImage.setImageBitmap(image);
|
||||
tv_description.setText("【待加库图片】");
|
||||
predictor.setAddGallery(1);
|
||||
predictor.setInputImage(image);
|
||||
runModel();
|
||||
label_name.setVisibility(View.VISIBLE);
|
||||
label_name.setHint("image label name");
|
||||
label_name.setText("");
|
||||
label_botton.setVisibility(View.VISIBLE);
|
||||
cancel_botton.setVisibility(View.VISIBLE);
|
||||
tvTop1Result.setText("类别:");
|
||||
tvSimilarity.setText("相似度:");
|
||||
tvInferenceTime.setText("预测时间:" + predictor.inferenceTime() + " ms");
|
||||
cancel_botton.setOnClickListener(view -> {
|
||||
predictor.setAddGallery(0);
|
||||
label_name.setVisibility(View.INVISIBLE);
|
||||
label_botton.setVisibility(View.INVISIBLE);
|
||||
cancel_botton.setVisibility(View.INVISIBLE);
|
||||
tvTop1Result.setText("类别:");
|
||||
tvSimilarity.setText("相似度:");
|
||||
tvInferenceTime.setText("预测时间:");
|
||||
});
|
||||
label_botton.setOnClickListener(view -> {
|
||||
predictor.setAddGallery(2);
|
||||
predictor.setLabelName(label_name.getText().toString());
|
||||
runModel();
|
||||
label_name.setVisibility(View.INVISIBLE);
|
||||
label_botton.setVisibility(View.INVISIBLE);
|
||||
cancel_botton.setVisibility(View.INVISIBLE);
|
||||
label_name.setText("");
|
||||
tvTop1Result.setText("类别:");
|
||||
tvSimilarity.setText("相似度:");
|
||||
tvInferenceTime.setText("预测时间:" + predictor.inferenceTime() + " ms");
|
||||
Toast.makeText(MainActivity.this, "已添加至检索库", Toast.LENGTH_SHORT).show();
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
public void onSettingsClicked() {
|
||||
startActivity(new Intent(MainActivity.this, SettingsActivity.class));
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean onCreateOptionsMenu(Menu menu) {
|
||||
MenuInflater inflater = getMenuInflater();
|
||||
inflater.inflate(R.menu.menu_action_options, menu);
|
||||
help = findViewById(R.id.help);
|
||||
reset = findViewById(R.id.reset);
|
||||
return true;
|
||||
}
|
||||
|
||||
|
||||
public boolean onPrepareOptionsMenu(Menu menu) {
|
||||
boolean isLoaded = predictor.isLoaded();
|
||||
return super.onPrepareOptionsMenu(menu);
|
||||
}
|
||||
|
||||
@SuppressLint("NonConstantResourceId")
|
||||
@Override
|
||||
public boolean onOptionsItemSelected(MenuItem item) {
|
||||
switch (item.getItemId()) {
|
||||
case android.R.id.home:
|
||||
finish();
|
||||
break;
|
||||
case R.id.reset:
|
||||
File filelabel = new File(getExternalFilesDir(null) + "/" + labelPath);
|
||||
if (filelabel.exists()) {
|
||||
filelabel.delete();
|
||||
}
|
||||
File fileindex = new File(getExternalFilesDir(null) + "/" + indexPath);
|
||||
if (fileindex.exists()) {
|
||||
fileindex.delete();
|
||||
}
|
||||
labelPath = getString(R.string.LABEL_PATH_DEFAULT);
|
||||
indexPath = getString(R.string.INDEX_PATH_DEFAULT);
|
||||
predictor.loadIndex("original");
|
||||
tvIndexName.setText("检索库名称:" + indexPath.split("/")[1].split("\\.")[0]);
|
||||
Toast.makeText(this, "检索库已初始化为original", Toast.LENGTH_SHORT).show();
|
||||
break;
|
||||
case R.id.help:
|
||||
if (requestAllPermissions()) {
|
||||
// Make sure we have SDCard r&w permissions to load model from SDCard
|
||||
String help_content = "【功能说明】\n本APP基于PaddleClas图像分类开发套件中的通用图像识别系统PP-ShiTu开发,支持对拍照/本地上传的图片进行识别。\n【默认检索库说明】\n · 默认内置检索库名为:original,主要包含常见饮料类别共计196种,可通过 [类别查询] 查看已有类别信息。\n" +
|
||||
" · 可根据实际需求,通过拍照/上传本地图像补充检索库,以提高识别准确率或增加可识别类别。\n注意:修改检索库后需进行保存,否则重启APP后将重置为初始库original。\n";
|
||||
SpannableString span = new SpannableString(help_content);
|
||||
int idx = help_content.indexOf("【功能说明】");
|
||||
span.setSpan(new StyleSpan(Typeface.BOLD), idx, idx + "【功能说明】".length(), Spanned.SPAN_EXCLUSIVE_EXCLUSIVE);
|
||||
idx = help_content.indexOf("【默认检索库说明】");
|
||||
span.setSpan(new StyleSpan(Typeface.BOLD), idx, idx + "【默认检索库说明】".length(), Spanned.SPAN_EXCLUSIVE_EXCLUSIVE);
|
||||
idx = help_content.indexOf("注意:");
|
||||
span.setSpan(new StyleSpan(Typeface.BOLD), idx, idx + "注意:".length(), Spanned.SPAN_EXCLUSIVE_EXCLUSIVE);
|
||||
AlertDialog alertDialog = new AlertDialog.Builder(MainActivity.this)
|
||||
//标题
|
||||
.setTitle("使用说明")
|
||||
//内容
|
||||
.setMessage(span)
|
||||
//图标
|
||||
.setIcon(R.mipmap.ic_launcher)
|
||||
.setPositiveButton("确认", null)
|
||||
.create();
|
||||
alertDialog.show();
|
||||
}
|
||||
break;
|
||||
case R.id.settings:
|
||||
if (requestAllPermissions()) {
|
||||
// Make sure we have SDCard r&w permissions to load model from SDCard
|
||||
onSettingsClicked();
|
||||
}
|
||||
break;
|
||||
}
|
||||
return super.onOptionsItemSelected(item);
|
||||
}
|
||||
|
||||
|
||||
@Override
|
||||
public void onRequestPermissionsResult(int requestCode, @NonNull String[] permissions,
|
||||
@NonNull int[] grantResults) {
|
||||
super.onRequestPermissionsResult(requestCode, permissions, grantResults);
|
||||
if (grantResults[0] != PackageManager.PERMISSION_GRANTED || grantResults[1] != PackageManager.PERMISSION_GRANTED) {
|
||||
Toast.makeText(this, "Permission Denied", Toast.LENGTH_SHORT).show();
|
||||
}
|
||||
}
|
||||
|
||||
private boolean requestAllPermissions() {
|
||||
if (ContextCompat.checkSelfPermission(this, Manifest.permission.WRITE_EXTERNAL_STORAGE) != PackageManager.PERMISSION_GRANTED
|
||||
|| ContextCompat.checkSelfPermission(this, Manifest.permission.CAMERA) != PackageManager.PERMISSION_GRANTED) {
|
||||
ActivityCompat.requestPermissions(this, new String[]{Manifest.permission.WRITE_EXTERNAL_STORAGE, Manifest.permission.CAMERA}, 0);
|
||||
return false;
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
private void openQueryPhoto() {
|
||||
Intent intent = new Intent(Intent.ACTION_PICK, null); // 选择数据
|
||||
intent.setDataAndType(MediaStore.Images.Media.EXTERNAL_CONTENT_URI, "image/*");
|
||||
startActivityForResult(intent, OPEN_QUERY_PHOTO_REQUEST_CODE);
|
||||
}
|
||||
|
||||
@SuppressLint("QueryPermissionsNeeded")
|
||||
private void takeQueryPhoto() {
|
||||
Intent takePhotoIntent = new Intent(MediaStore.ACTION_IMAGE_CAPTURE);
|
||||
startActivityForResult(takePhotoIntent, TAKE_QUERY_PHOTO_REQUEST_CODE);
|
||||
}
|
||||
|
||||
private void openGalleryPhoto() {
|
||||
// 增加现有图片到库中---主逻辑代码
|
||||
Intent intent = new Intent(Intent.ACTION_PICK, null); // 选择数据
|
||||
intent.setDataAndType(MediaStore.Images.Media.EXTERNAL_CONTENT_URI, "image/*");
|
||||
startActivityForResult(intent, OPEN_GALLERY_PHOTO_REQUEST_CODE);
|
||||
}
|
||||
|
||||
@SuppressLint("QueryPermissionsNeeded")
|
||||
private void takeGalleryPhoto() {
|
||||
// 直接拍一张图片到库中---主逻辑代码
|
||||
Intent intent = new Intent(MediaStore.ACTION_IMAGE_CAPTURE);
|
||||
startActivityForResult(intent, TAKE_GALLERY_PHOTO_REQUEST_CODE);
|
||||
}
|
||||
|
||||
private void clearIndex() {
|
||||
// 清空index主逻辑代码
|
||||
predictor.clearFeature();
|
||||
}
|
||||
|
||||
@SuppressLint("SetTextI18n")
|
||||
private void saveIndex() {
|
||||
label_name.setText("latest");
|
||||
indexPath = "index/latest.index";
|
||||
labelPath = "index/latest.txt";
|
||||
predictor.saveIndex(label_name.getText().toString());
|
||||
tvIndexName.setText("检索库名称:" + indexPath.split("/")[1].split("\\.")[0]);
|
||||
Toast.makeText(MainActivity.this, "检索库保存并更新为latest", Toast.LENGTH_SHORT).show();
|
||||
}
|
||||
|
||||
|
||||
@Override
|
||||
protected void onActivityResult(int requestCode, int resultCode, Intent data) {
|
||||
super.onActivityResult(requestCode, resultCode, data);
|
||||
if (resultCode == RESULT_OK && data != null) {
|
||||
switch (requestCode) {
|
||||
case OPEN_QUERY_PHOTO_REQUEST_CODE:
|
||||
try {
|
||||
ContentResolver resolver = getContentResolver();
|
||||
Uri uri = data.getData();
|
||||
Bitmap image = MediaStore.Images.Media.getBitmap(resolver, uri);
|
||||
String[] proj = {MediaStore.Images.Media.DATA};
|
||||
Cursor cursor = managedQuery(uri, proj, null, null, null);
|
||||
cursor.moveToFirst();
|
||||
onImageChanged(image);
|
||||
} catch (IOException e) {
|
||||
Log.e(TAG, e.toString());
|
||||
}
|
||||
break;
|
||||
case OPEN_GALLERY_PHOTO_REQUEST_CODE:
|
||||
try {
|
||||
|
||||
ContentResolver resolver = getContentResolver();
|
||||
Uri uri = data.getData();
|
||||
Bitmap image = MediaStore.Images.Media.getBitmap(resolver, uri);
|
||||
String[] proj = {MediaStore.Images.Media.DATA};
|
||||
Cursor cursor = managedQuery(uri, proj, null, null, null);
|
||||
cursor.moveToFirst();
|
||||
onAddGallery(image);
|
||||
} catch (IOException e) {
|
||||
Log.e(TAG, e.toString());
|
||||
}
|
||||
break;
|
||||
case TAKE_GALLERY_PHOTO_REQUEST_CODE:
|
||||
Bundle gextras = data.getExtras();
|
||||
Bitmap gimage = (Bitmap) gextras.get("data");
|
||||
onAddGallery(gimage);
|
||||
break;
|
||||
case TAKE_QUERY_PHOTO_REQUEST_CODE:
|
||||
Bundle extras = data.getExtras();
|
||||
Bitmap image = (Bitmap) extras.get("data");
|
||||
onImageChanged(image);
|
||||
break;
|
||||
case CLEAR_FEATURE_REQUEST_CODE:
|
||||
clearIndex();
|
||||
break;
|
||||
default:
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
protected void onDestroy() {
|
||||
if (predictor != null) {
|
||||
predictor.release();
|
||||
}
|
||||
worker.quit();
|
||||
super.onDestroy();
|
||||
}
|
||||
}
|
|
@ -0,0 +1,186 @@
|
|||
/*
|
||||
* Copyright (C) 2014 The Android Open Source Project
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
package com.baidu.paddle.lite.demo.pp_shitu;
|
||||
|
||||
import android.graphics.Bitmap;
|
||||
import android.util.Log;
|
||||
|
||||
import java.util.Arrays;
|
||||
|
||||
public class Native {
|
||||
static {
|
||||
System.loadLibrary("Native");
|
||||
}
|
||||
|
||||
protected Bitmap inputImage = null;
|
||||
protected long[] detinputShape = new long[]{1, 3, 640, 640};
|
||||
protected long[] recinputShape = new long[]{1, 3, 224, 224};
|
||||
protected float inferenceTime = 0;
|
||||
protected String top1Result = "";
|
||||
protected int topk = 1;
|
||||
protected boolean addGallery = false;
|
||||
protected String label_name = "";
|
||||
protected boolean clearFeature = false;
|
||||
private long ctx = 0;
|
||||
|
||||
public boolean init(String DetModelDir,
|
||||
String RecModelDir,
|
||||
String labelPath,
|
||||
String IndexDir,
|
||||
long[] DetInputShape,
|
||||
long[] RecInputShape,
|
||||
int cpuThreadNum,
|
||||
int WarmUp,
|
||||
int Repeat,
|
||||
int topk,
|
||||
boolean add_gallery,
|
||||
String cpuMode) {
|
||||
if (DetInputShape.length != 4) {
|
||||
Log.i("Paddle-lite", "Size of input shape should be: 4");
|
||||
return false;
|
||||
}
|
||||
if (DetInputShape[0] != 1) {
|
||||
Log.i("Paddle-lite", "Only one batch is supported in the image classification demo, you can use any batch size in " +
|
||||
"your Apps!");
|
||||
return false;
|
||||
}
|
||||
if (DetInputShape[1] != 1 && DetInputShape[1] != 3) {
|
||||
Log.i("Paddle-lite", "Only one/three channels are supported in the image classification demo, you can use any " +
|
||||
"channel size in your Apps!");
|
||||
return false;
|
||||
}
|
||||
this.detinputShape = DetInputShape;
|
||||
this.recinputShape = RecInputShape;
|
||||
this.topk = topk;
|
||||
this.addGallery = add_gallery;
|
||||
ctx = nativeInit(
|
||||
DetModelDir,
|
||||
RecModelDir,
|
||||
labelPath,
|
||||
IndexDir,
|
||||
DetInputShape,
|
||||
RecInputShape,
|
||||
cpuThreadNum,
|
||||
WarmUp,
|
||||
Repeat,
|
||||
topk,
|
||||
add_gallery,
|
||||
cpuMode);
|
||||
return ctx != 0;
|
||||
}
|
||||
|
||||
public boolean release() {
|
||||
if (ctx == 0) {
|
||||
return false;
|
||||
}
|
||||
return nativeRelease(ctx);
|
||||
}
|
||||
|
||||
public void setAddGallery(int flag) {
|
||||
nativesetAddGallery(ctx, flag);
|
||||
}
|
||||
|
||||
public void saveIndex(String save_file_name) {
|
||||
nativesaveIndex(ctx, save_file_name);
|
||||
}
|
||||
|
||||
public boolean loadIndex(String load_file_name) {
|
||||
return nativeloadIndex(ctx, load_file_name);
|
||||
}
|
||||
|
||||
public void clearFeature() {
|
||||
boolean ret = nativeclearGallery(ctx);
|
||||
}
|
||||
|
||||
public void setLabelName(String label_name) {
|
||||
this.label_name = label_name;
|
||||
}
|
||||
|
||||
public String getClassname() {
|
||||
return nativegetClassname(ctx);
|
||||
}
|
||||
|
||||
public boolean process() {
|
||||
if (ctx == 0) {
|
||||
return false;
|
||||
}
|
||||
// ARGB8888 bitmap is only supported in native, other color formats can be added by yourself.
|
||||
String[] res = nativeProcess(ctx, this.inputImage, label_name).split("\n");
|
||||
if (res.length >= 1) {
|
||||
if (!Arrays.toString(res).contains("success")) {
|
||||
inferenceTime = Float.parseFloat(res[0]);
|
||||
if (res.length >= 2) {
|
||||
top1Result = res[1];
|
||||
} else {
|
||||
top1Result = "";
|
||||
}
|
||||
}
|
||||
}
|
||||
return (res.length > 0);
|
||||
}
|
||||
|
||||
public boolean isLoaded() {
|
||||
return ctx != 0;
|
||||
}
|
||||
|
||||
public void setInputImage(Bitmap image) {
|
||||
if (image == null) {
|
||||
return;
|
||||
}
|
||||
this.inputImage = image.copy(Bitmap.Config.ARGB_8888, true);
|
||||
}
|
||||
|
||||
public float inferenceTime() {
|
||||
inferenceTime = (float) (Math.round(inferenceTime * 100)) / 100;
|
||||
return inferenceTime;
|
||||
}
|
||||
|
||||
public Bitmap inputImage() {
|
||||
return inputImage;
|
||||
}
|
||||
|
||||
public String top1Result() {
|
||||
return top1Result;
|
||||
}
|
||||
|
||||
public static native long nativeInit(String DetModelDir,
|
||||
String RecModelDir,
|
||||
String labelPath,
|
||||
String IndexDir,
|
||||
long[] DetInputShape,
|
||||
long[] RecInputShape,
|
||||
int cpuThreadNum,
|
||||
int WarmUp,
|
||||
int Repeat,
|
||||
int topk,
|
||||
boolean addGallery,
|
||||
String cpuMode);
|
||||
|
||||
public static native boolean nativeRelease(long ctx);
|
||||
|
||||
public static native boolean nativesetAddGallery(long ctx, int flag);
|
||||
|
||||
public static native boolean nativeclearGallery(long ctx);
|
||||
|
||||
public static native String nativeProcess(long ctx, Bitmap ARGB888ImageBitmap, String label_name);
|
||||
|
||||
public static native boolean nativesaveIndex(long ctx, String save_file_name);
|
||||
|
||||
public static native boolean nativeloadIndex(long ctx, String load_file_name);
|
||||
|
||||
public static native String nativegetClassname(long ctx);
|
||||
}
|
|
@ -0,0 +1,150 @@
|
|||
/*
|
||||
* Copyright (C) 2014 The Android Open Source Project
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
package com.baidu.paddle.lite.demo.pp_shitu;
|
||||
|
||||
import android.app.AlertDialog;
|
||||
import android.content.SharedPreferences;
|
||||
import android.os.Bundle;
|
||||
import android.preference.ListPreference;
|
||||
import android.support.v7.app.ActionBar;
|
||||
import android.view.View;
|
||||
import android.widget.Button;
|
||||
import android.widget.ListView;
|
||||
import android.widget.Toast;
|
||||
|
||||
import com.baidu.paddle.lite.demo.common.AppCompatPreferenceActivity;
|
||||
|
||||
import java.io.BufferedReader;
|
||||
import java.io.File;
|
||||
import java.io.FileInputStream;
|
||||
import java.io.InputStream;
|
||||
import java.io.InputStreamReader;
|
||||
import java.util.ArrayList;
|
||||
import java.util.List;
|
||||
import java.util.Objects;
|
||||
|
||||
public class SettingsActivity extends AppCompatPreferenceActivity implements SharedPreferences.OnSharedPreferenceChangeListener {
|
||||
ListPreference lpChoosePreInstalledModel = null;
|
||||
ListPreference lpLabelPath = null;
|
||||
ListPreference lpIndexPath = null;
|
||||
|
||||
|
||||
List<String> preInstalledModelPaths = null;
|
||||
List<String> preInstalledLabelPaths = null;
|
||||
List<String> preInstalledIndexDirs = null;
|
||||
List<String> preInstalledImagePaths = null;
|
||||
|
||||
|
||||
@Override
|
||||
public void onCreate(Bundle savedInstanceState) {
|
||||
super.onCreate(savedInstanceState);
|
||||
addPreferencesFromResource(R.xml.settings);
|
||||
ActionBar supportActionBar = getSupportActionBar();
|
||||
if (supportActionBar != null) {
|
||||
supportActionBar.setDisplayHomeAsUpEnabled(true);
|
||||
}
|
||||
|
||||
|
||||
// Initialized pre-installed models
|
||||
preInstalledModelPaths = new ArrayList<>();
|
||||
preInstalledLabelPaths = new ArrayList<>();
|
||||
preInstalledIndexDirs = new ArrayList<>();
|
||||
preInstalledImagePaths = new ArrayList<>();
|
||||
|
||||
// Add mobilenet_v1_for_cpu
|
||||
preInstalledModelPaths.add(getString(R.string.MODEL_PATH_DEFAULT));
|
||||
preInstalledLabelPaths.add(getString(R.string.LABEL_PATH_DEFAULT));
|
||||
preInstalledIndexDirs.add(getString(R.string.INDEX_PATH_DEFAULT));
|
||||
preInstalledImagePaths.add(getString(R.string.IMAGE_PATH_DEFAULT));
|
||||
|
||||
// Setup UI components
|
||||
lpChoosePreInstalledModel =
|
||||
(ListPreference) findPreference(getString(R.string.CHOOSE_PRE_INSTALLED_MODEL_KEY));
|
||||
String[] preInstalledModelNames = new String[preInstalledModelPaths.size()];
|
||||
for (int i = 0; i < preInstalledModelPaths.size(); i++) {
|
||||
preInstalledModelNames[i] =
|
||||
preInstalledModelPaths.get(i).substring(preInstalledModelPaths.get(i).lastIndexOf("/") + 1);
|
||||
}
|
||||
lpChoosePreInstalledModel.setEntries(preInstalledModelNames);
|
||||
lpChoosePreInstalledModel.setEntryValues(preInstalledModelPaths.toArray(new String[preInstalledModelPaths.size()]));
|
||||
|
||||
lpLabelPath = (ListPreference) findPreference(getString(R.string.LABEL_PATH_KEY));
|
||||
String label_dir = getExternalFilesDir(null) + "/index/";
|
||||
File dir = new File(label_dir);
|
||||
String[] files = dir.list();
|
||||
ArrayList<String> files_ = new ArrayList<>();
|
||||
for (int i = 0; i < Objects.requireNonNull(files).length; i++) {
|
||||
if (!files[i].endsWith(".txt")) {
|
||||
continue;
|
||||
}
|
||||
files_.add("index/" + files[i]);
|
||||
files[i] = label_dir + files[i];
|
||||
}
|
||||
lpLabelPath.setEntries(files_.toArray(new String[files_.size()]));
|
||||
lpLabelPath.setEntryValues(files_.toArray(new String[files_.size()]));
|
||||
|
||||
lpIndexPath = (ListPreference) findPreference(getString(R.string.INDEX_PATH_KEY));
|
||||
String index_dir = getExternalFilesDir(null) + "/index/";
|
||||
dir = new File(index_dir);
|
||||
files = dir.list();
|
||||
files_ = new ArrayList<>();
|
||||
for (int i = 0; i < Objects.requireNonNull(files).length; i++) {
|
||||
if (!files[i].endsWith(".index")) {
|
||||
continue;
|
||||
}
|
||||
files_.add("index/" + files[i]);
|
||||
files[i] = index_dir + files[i];
|
||||
}
|
||||
lpIndexPath.setEntries(files_.toArray(new String[files_.size()]));
|
||||
lpIndexPath.setEntryValues(files_.toArray(new String[files_.size()]));
|
||||
}
|
||||
|
||||
private void reloadPreferenceAndUpdateUI() {
|
||||
SharedPreferences sharedPreferences = getPreferenceScreen().getSharedPreferences();
|
||||
String modelPath = "models";
|
||||
int modelIdx = lpChoosePreInstalledModel.findIndexOfValue(modelPath);
|
||||
if (modelIdx >= 0 && modelIdx < preInstalledModelPaths.size()) {
|
||||
lpChoosePreInstalledModel.setSummary(modelPath);
|
||||
}
|
||||
|
||||
String labelPath = sharedPreferences.getString(getString(R.string.LABEL_PATH_KEY),
|
||||
getString(R.string.LABEL_PATH_DEFAULT));
|
||||
String indexPath = sharedPreferences.getString(getString(R.string.INDEX_PATH_KEY),
|
||||
getString(R.string.INDEX_PATH_DEFAULT));
|
||||
|
||||
lpLabelPath.setSummary(labelPath);
|
||||
lpIndexPath.setSummary(indexPath);
|
||||
}
|
||||
|
||||
@Override
|
||||
protected void onResume() {
|
||||
super.onResume();
|
||||
getPreferenceScreen().getSharedPreferences().registerOnSharedPreferenceChangeListener(this);
|
||||
reloadPreferenceAndUpdateUI();
|
||||
}
|
||||
|
||||
@Override
|
||||
protected void onPause() {
|
||||
super.onPause();
|
||||
getPreferenceScreen().getSharedPreferences().unregisterOnSharedPreferenceChangeListener(this);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onSharedPreferenceChanged(SharedPreferences sharedPreferences, String key) {
|
||||
reloadPreferenceAndUpdateUI();
|
||||
}
|
||||
}
|
|
@ -0,0 +1 @@
|
|||
put `libfaiss.a` in this folder
|
|
@ -0,0 +1,34 @@
|
|||
<vector xmlns:android="http://schemas.android.com/apk/res/android"
|
||||
xmlns:aapt="http://schemas.android.com/aapt"
|
||||
android:width="108dp"
|
||||
android:height="108dp"
|
||||
android:viewportWidth="108"
|
||||
android:viewportHeight="108">
|
||||
<path
|
||||
android:fillType="evenOdd"
|
||||
android:pathData="M32,64C32,64 38.39,52.99 44.13,50.95C51.37,48.37 70.14,49.57 70.14,49.57L108.26,87.69L108,109.01L75.97,107.97L32,64Z"
|
||||
android:strokeWidth="1"
|
||||
android:strokeColor="#00000000">
|
||||
<aapt:attr name="android:fillColor">
|
||||
<gradient
|
||||
android:endX="78.5885"
|
||||
android:endY="90.9159"
|
||||
android:startX="48.7653"
|
||||
android:startY="61.0927"
|
||||
android:type="linear">
|
||||
<item
|
||||
android:color="#44000000"
|
||||
android:offset="0.0" />
|
||||
<item
|
||||
android:color="#00000000"
|
||||
android:offset="1.0" />
|
||||
</gradient>
|
||||
</aapt:attr>
|
||||
</path>
|
||||
<path
|
||||
android:fillColor="#FFFFFF"
|
||||
android:fillType="nonZero"
|
||||
android:pathData="M66.94,46.02L66.94,46.02C72.44,50.07 76,56.61 76,64L32,64C32,56.61 35.56,50.11 40.98,46.06L36.18,41.19C35.45,40.45 35.45,39.3 36.18,38.56C36.91,37.81 38.05,37.81 38.78,38.56L44.25,44.05C47.18,42.57 50.48,41.71 54,41.71C57.48,41.71 60.78,42.57 63.68,44.05L69.11,38.56C69.84,37.81 70.98,37.81 71.71,38.56C72.44,39.3 72.44,40.45 71.71,41.19L66.94,46.02ZM62.94,56.92C64.08,56.92 65,56.01 65,54.88C65,53.76 64.08,52.85 62.94,52.85C61.8,52.85 60.88,53.76 60.88,54.88C60.88,56.01 61.8,56.92 62.94,56.92ZM45.06,56.92C46.2,56.92 47.13,56.01 47.13,54.88C47.13,53.76 46.2,52.85 45.06,52.85C43.92,52.85 43,53.76 43,54.88C43,56.01 43.92,56.92 45.06,56.92Z"
|
||||
android:strokeWidth="1"
|
||||
android:strokeColor="#00000000" />
|
||||
</vector>
|
After Width: | Height: | Size: 1.5 KiB |
After Width: | Height: | Size: 9.8 KiB |
After Width: | Height: | Size: 28 KiB |
|
@ -0,0 +1,6 @@
|
|||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<selector xmlns:android="http://schemas.android.com/apk/res/android">
|
||||
<item android:state_pressed="true" android:drawable="@drawable/btn_settings_pressed"/>
|
||||
<item android:drawable="@drawable/btn_settings_default"/>
|
||||
</selector>
|
||||
|
|
@ -0,0 +1,13 @@
|
|||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<vector xmlns:android="http://schemas.android.com/apk/res/android"
|
||||
android:width="24dp"
|
||||
android:height="24dp"
|
||||
android:viewportWidth="24"
|
||||
android:viewportHeight="24">
|
||||
<path
|
||||
android:fillColor="#FFFFFF"
|
||||
android:fillType="evenOdd"
|
||||
android:pathData="M10.4696,3.5L9.8539,5.2714C9.7605,5.5401 9.5784,5.7688 9.3375,5.9199L8.0854,6.7054C7.8421,6.8581 7.5537,6.9223 7.2686,6.8872L5.3518,6.6517L3.9124,9.0649L4.9862,10.3888C5.1668,10.6114 5.2654,10.8894 5.2654,11.1762L5.2654,12.9051C5.2654,13.2057 5.157,13.4963 4.9602,13.7235L3.9165,14.9283L5.3573,17.4236L7.264,17.1741C7.5472,17.137 7.8344,17.198 8.0781,17.3469L9.401,18.1555C9.655,18.3107 9.8452,18.5515 9.9375,18.8345L10.4806,20.5L13.5194,20.5L14.0625,18.8345C14.1548,18.5515 14.345,18.3107 14.599,18.1555L15.9219,17.3469C16.1656,17.198 16.4528,17.137 16.736,17.1741L18.6427,17.4236L20.0835,14.9283L19.0398,13.7235C18.843,13.4963 18.7346,13.2057 18.7346,12.9051L18.7346,11.1762C18.7346,10.8894 18.8332,10.6114 19.0138,10.3888L20.0876,9.0649L18.6482,6.6517L16.7314,6.8872C16.4463,6.9223 16.1579,6.8581 15.9146,6.7054L14.6629,5.9202C14.4221,5.7691 14.2399,5.5404 14.1466,5.2718L13.5305,3.5L10.4696,3.5ZM8.4659,4.696L9.1111,2.8396C9.2858,2.3369 9.7596,2 10.2918,2L13.7083,2C14.2404,2 14.7142,2.3369 14.8889,2.8395L15.5345,4.6962L16.6366,5.3876L18.6269,5.143C19.1184,5.0826 19.5993,5.318 19.8529,5.7433L21.4653,8.4465C21.7339,8.8968 21.6928,9.4669 21.3625,9.8742L20.2346,11.2648L20.2346,12.8118L21.3338,14.0807C21.6826,14.4833 21.7379,15.0628 21.4715,15.5241L19.8583,18.3182C19.6057,18.7557 19.1145,18.9982 18.6136,18.9326L16.6288,18.6728L15.46,19.3872L14.8893,21.1375C14.7216,21.6519 14.2419,22 13.7009,22L10.2991,22C9.7581,22 9.2784,21.6519 9.1107,21.1375L8.54,19.3872L7.3712,18.6728L5.3864,18.9326C4.8855,18.9982 4.3943,18.7557 4.1417,18.3182L2.5285,15.5241C2.2621,15.0628 2.3174,14.4833 2.6662,14.0807L3.7654,12.8118L3.7654,11.2648L2.6375,9.8742C2.3072,9.4669 2.2661,8.8968 2.5347,8.4465L4.1471,5.7433C4.4007,5.318 4.8816,5.0826 5.3731,5.143L7.3634,5.3876L8.4659,4.696ZM12,15.75C9.9289,15.75 8.25,14.0711 8.25,12C8.25,9.9289 9.9289,8.25 12,8.25C14.0711,8.25 15.75,9.9289 15.75,12C15.75,14.0711 14.0711,15.75 12,15.75ZM12,14.75C13.5188,14.75 14.75,13.5188 14.75,12C14.75,10.4812 13.5188,9.25 12,9.25C10.4812,9.25 9.25,10.4812 9.25,12C9.25,13.5188 10.4812,14.75 12,14.75Z"
|
||||
android:strokeWidth="0"
|
||||
android:strokeColor="#00000000" />
|
||||
</vector>
|
|
@ -0,0 +1,13 @@
|
|||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<vector xmlns:android="http://schemas.android.com/apk/res/android"
|
||||
android:width="24dp"
|
||||
android:height="24dp"
|
||||
android:viewportWidth="24"
|
||||
android:viewportHeight="24">
|
||||
<path
|
||||
android:fillColor="#bcbcbc"
|
||||
android:fillType="evenOdd"
|
||||
android:pathData="M10.4696,3.5L9.8539,5.2714C9.7605,5.5401 9.5784,5.7688 9.3375,5.9199L8.0854,6.7054C7.8421,6.8581 7.5537,6.9223 7.2686,6.8872L5.3518,6.6517L3.9124,9.0649L4.9862,10.3888C5.1668,10.6114 5.2654,10.8894 5.2654,11.1762L5.2654,12.9051C5.2654,13.2057 5.157,13.4963 4.9602,13.7235L3.9165,14.9283L5.3573,17.4236L7.264,17.1741C7.5472,17.137 7.8344,17.198 8.0781,17.3469L9.401,18.1555C9.655,18.3107 9.8452,18.5515 9.9375,18.8345L10.4806,20.5L13.5194,20.5L14.0625,18.8345C14.1548,18.5515 14.345,18.3107 14.599,18.1555L15.9219,17.3469C16.1656,17.198 16.4528,17.137 16.736,17.1741L18.6427,17.4236L20.0835,14.9283L19.0398,13.7235C18.843,13.4963 18.7346,13.2057 18.7346,12.9051L18.7346,11.1762C18.7346,10.8894 18.8332,10.6114 19.0138,10.3888L20.0876,9.0649L18.6482,6.6517L16.7314,6.8872C16.4463,6.9223 16.1579,6.8581 15.9146,6.7054L14.6629,5.9202C14.4221,5.7691 14.2399,5.5404 14.1466,5.2718L13.5305,3.5L10.4696,3.5ZM8.4659,4.696L9.1111,2.8396C9.2858,2.3369 9.7596,2 10.2918,2L13.7083,2C14.2404,2 14.7142,2.3369 14.8889,2.8395L15.5345,4.6962L16.6366,5.3876L18.6269,5.143C19.1184,5.0826 19.5993,5.318 19.8529,5.7433L21.4653,8.4465C21.7339,8.8968 21.6928,9.4669 21.3625,9.8742L20.2346,11.2648L20.2346,12.8118L21.3338,14.0807C21.6826,14.4833 21.7379,15.0628 21.4715,15.5241L19.8583,18.3182C19.6057,18.7557 19.1145,18.9982 18.6136,18.9326L16.6288,18.6728L15.46,19.3872L14.8893,21.1375C14.7216,21.6519 14.2419,22 13.7009,22L10.2991,22C9.7581,22 9.2784,21.6519 9.1107,21.1375L8.54,19.3872L7.3712,18.6728L5.3864,18.9326C4.8855,18.9982 4.3943,18.7557 4.1417,18.3182L2.5285,15.5241C2.2621,15.0628 2.3174,14.4833 2.6662,14.0807L3.7654,12.8118L3.7654,11.2648L2.6375,9.8742C2.3072,9.4669 2.2661,8.8968 2.5347,8.4465L4.1471,5.7433C4.4007,5.318 4.8816,5.0826 5.3731,5.143L7.3634,5.3876L8.4659,4.696ZM12,15.75C9.9289,15.75 8.25,14.0711 8.25,12C8.25,9.9289 9.9289,8.25 12,8.25C14.0711,8.25 15.75,9.9289 15.75,12C15.75,14.0711 14.0711,15.75 12,15.75ZM12,14.75C13.5188,14.75 14.75,13.5188 14.75,12C14.75,10.4812 13.5188,9.25 12,9.25C10.4812,9.25 9.25,10.4812 9.25,12C9.25,13.5188 10.4812,14.75 12,14.75Z"
|
||||
android:strokeWidth="0"
|
||||
android:strokeColor="#00000000" />
|
||||
</vector>
|
|
@ -0,0 +1,5 @@
|
|||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<selector xmlns:android="http://schemas.android.com/apk/res/android">
|
||||
<item android:state_pressed="true" android:drawable="@drawable/btn_shutter_pressed"/>
|
||||
<item android:drawable="@drawable/btn_shutter_default"/>
|
||||
</selector>
|
|
@ -0,0 +1,17 @@
|
|||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<vector xmlns:android="http://schemas.android.com/apk/res/android"
|
||||
android:height="201dp"
|
||||
android:width="201dp"
|
||||
android:viewportWidth="201"
|
||||
android:viewportHeight="201">
|
||||
<path
|
||||
android:fillColor="#ffffff"
|
||||
android:pathData="M 100.5 0 C 156.004617358 0 201 44.995382642 201 100.5 C 201 156.004617358 156.004617358 201 100.5 201 C 44.995382642 201 0 156.004617358 0 100.5 C 0 44.995382642 44.995382642 0 100.5 0 Z"
|
||||
android:strokeWidth="1"
|
||||
android:fillType="evenOdd"/>
|
||||
<path
|
||||
android:fillColor="#dfe7eb"
|
||||
android:pathData="M 100.5 17.2285714 C 146.489540112 17.2285714 183.7714286 54.5104598876 183.7714286 100.5 C 183.7714286 146.489540112 146.489540112 183.7714286 100.5 183.7714286 C 54.5104598876 183.7714286 17.2285714 146.489540112 17.2285714 100.5 C 17.2285714 54.5104598876 54.5104598876 17.2285714 100.5 17.2285714 Z"
|
||||
android:strokeWidth="1"
|
||||
android:fillType="evenOdd"/>
|
||||
</vector>
|
|
@ -0,0 +1,17 @@
|
|||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<vector xmlns:android="http://schemas.android.com/apk/res/android"
|
||||
android:height="201dp"
|
||||
android:width="201dp"
|
||||
android:viewportWidth="201"
|
||||
android:viewportHeight="201">
|
||||
<path
|
||||
android:fillColor="#ffffff"
|
||||
android:pathData="M 100.5 0 C 156.004617358 0 201 44.995382642 201 100.5 C 201 156.004617358 156.004617358 201 100.5 201 C 44.995382642 201 0 156.004617358 0 100.5 C 0 44.995382642 44.995382642 0 100.5 0 Z"
|
||||
android:strokeWidth="1"
|
||||
android:fillType="evenOdd"/>
|
||||
<path
|
||||
android:fillColor="#cfd7db"
|
||||
android:pathData="M 100.5 17.2285714 C 146.489540112 17.2285714 183.7714286 54.5104598876 183.7714286 100.5 C 183.7714286 146.489540112 146.489540112 183.7714286 100.5 183.7714286 C 54.5104598876 183.7714286 17.2285714 146.489540112 17.2285714 100.5 C 17.2285714 54.5104598876 54.5104598876 17.2285714 100.5 17.2285714 Z"
|
||||
android:strokeWidth="1"
|
||||
android:fillType="evenOdd"/>
|
||||
</vector>
|
|
@ -0,0 +1,5 @@
|
|||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<selector xmlns:android="http://schemas.android.com/apk/res/android">
|
||||
<item android:state_pressed="true" android:drawable="@drawable/btn_switch_pressed"/>
|
||||
<item android:drawable="@drawable/btn_switch_default"/>
|
||||
</selector>
|
|
@ -0,0 +1,170 @@
|
|||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<vector xmlns:android="http://schemas.android.com/apk/res/android"
|
||||
android:width="108dp"
|
||||
android:height="108dp"
|
||||
android:viewportWidth="108"
|
||||
android:viewportHeight="108">
|
||||
<path
|
||||
android:fillColor="#008577"
|
||||
android:pathData="M0,0h108v108h-108z" />
|
||||
<path
|
||||
android:fillColor="#00000000"
|
||||
android:pathData="M9,0L9,108"
|
||||
android:strokeWidth="0.8"
|
||||
android:strokeColor="#33FFFFFF" />
|
||||
<path
|
||||
android:fillColor="#00000000"
|
||||
android:pathData="M19,0L19,108"
|
||||
android:strokeWidth="0.8"
|
||||
android:strokeColor="#33FFFFFF" />
|
||||
<path
|
||||
android:fillColor="#00000000"
|
||||
android:pathData="M29,0L29,108"
|
||||
android:strokeWidth="0.8"
|
||||
android:strokeColor="#33FFFFFF" />
|
||||
<path
|
||||
android:fillColor="#00000000"
|
||||
android:pathData="M39,0L39,108"
|
||||
android:strokeWidth="0.8"
|
||||
android:strokeColor="#33FFFFFF" />
|
||||
<path
|
||||
android:fillColor="#00000000"
|
||||
android:pathData="M49,0L49,108"
|
||||
android:strokeWidth="0.8"
|
||||
android:strokeColor="#33FFFFFF" />
|
||||
<path
|
||||
android:fillColor="#00000000"
|
||||
android:pathData="M59,0L59,108"
|
||||
android:strokeWidth="0.8"
|
||||
android:strokeColor="#33FFFFFF" />
|
||||
<path
|
||||
android:fillColor="#00000000"
|
||||
android:pathData="M69,0L69,108"
|
||||
android:strokeWidth="0.8"
|
||||
android:strokeColor="#33FFFFFF" />
|
||||
<path
|
||||
android:fillColor="#00000000"
|
||||
android:pathData="M79,0L79,108"
|
||||
android:strokeWidth="0.8"
|
||||
android:strokeColor="#33FFFFFF" />
|
||||
<path
|
||||
android:fillColor="#00000000"
|
||||
android:pathData="M89,0L89,108"
|
||||
android:strokeWidth="0.8"
|
||||
android:strokeColor="#33FFFFFF" />
|
||||
<path
|
||||
android:fillColor="#00000000"
|
||||
android:pathData="M99,0L99,108"
|
||||
android:strokeWidth="0.8"
|
||||
android:strokeColor="#33FFFFFF" />
|
||||
<path
|
||||
android:fillColor="#00000000"
|
||||
android:pathData="M0,9L108,9"
|
||||
android:strokeWidth="0.8"
|
||||
android:strokeColor="#33FFFFFF" />
|
||||
<path
|
||||
android:fillColor="#00000000"
|
||||
android:pathData="M0,19L108,19"
|
||||
android:strokeWidth="0.8"
|
||||
android:strokeColor="#33FFFFFF" />
|
||||
<path
|
||||
android:fillColor="#00000000"
|
||||
android:pathData="M0,29L108,29"
|
||||
android:strokeWidth="0.8"
|
||||
android:strokeColor="#33FFFFFF" />
|
||||
<path
|
||||
android:fillColor="#00000000"
|
||||
android:pathData="M0,39L108,39"
|
||||
android:strokeWidth="0.8"
|
||||
android:strokeColor="#33FFFFFF" />
|
||||
<path
|
||||
android:fillColor="#00000000"
|
||||
android:pathData="M0,49L108,49"
|
||||
android:strokeWidth="0.8"
|
||||
android:strokeColor="#33FFFFFF" />
|
||||
<path
|
||||
android:fillColor="#00000000"
|
||||
android:pathData="M0,59L108,59"
|
||||
android:strokeWidth="0.8"
|
||||
android:strokeColor="#33FFFFFF" />
|
||||
<path
|
||||
android:fillColor="#00000000"
|
||||
android:pathData="M0,69L108,69"
|
||||
android:strokeWidth="0.8"
|
||||
android:strokeColor="#33FFFFFF" />
|
||||
<path
|
||||
android:fillColor="#00000000"
|
||||
android:pathData="M0,79L108,79"
|
||||
android:strokeWidth="0.8"
|
||||
android:strokeColor="#33FFFFFF" />
|
||||
<path
|
||||
android:fillColor="#00000000"
|
||||
android:pathData="M0,89L108,89"
|
||||
android:strokeWidth="0.8"
|
||||
android:strokeColor="#33FFFFFF" />
|
||||
<path
|
||||
android:fillColor="#00000000"
|
||||
android:pathData="M0,99L108,99"
|
||||
android:strokeWidth="0.8"
|
||||
android:strokeColor="#33FFFFFF" />
|
||||
<path
|
||||
android:fillColor="#00000000"
|
||||
android:pathData="M19,29L89,29"
|
||||
android:strokeWidth="0.8"
|
||||
android:strokeColor="#33FFFFFF" />
|
||||
<path
|
||||
android:fillColor="#00000000"
|
||||
android:pathData="M19,39L89,39"
|
||||
android:strokeWidth="0.8"
|
||||
android:strokeColor="#33FFFFFF" />
|
||||
<path
|
||||
android:fillColor="#00000000"
|
||||
android:pathData="M19,49L89,49"
|
||||
android:strokeWidth="0.8"
|
||||
android:strokeColor="#33FFFFFF" />
|
||||
<path
|
||||
android:fillColor="#00000000"
|
||||
android:pathData="M19,59L89,59"
|
||||
android:strokeWidth="0.8"
|
||||
android:strokeColor="#33FFFFFF" />
|
||||
<path
|
||||
android:fillColor="#00000000"
|
||||
android:pathData="M19,69L89,69"
|
||||
android:strokeWidth="0.8"
|
||||
android:strokeColor="#33FFFFFF" />
|
||||
<path
|
||||
android:fillColor="#00000000"
|
||||
android:pathData="M19,79L89,79"
|
||||
android:strokeWidth="0.8"
|
||||
android:strokeColor="#33FFFFFF" />
|
||||
<path
|
||||
android:fillColor="#00000000"
|
||||
android:pathData="M29,19L29,89"
|
||||
android:strokeWidth="0.8"
|
||||
android:strokeColor="#33FFFFFF" />
|
||||
<path
|
||||
android:fillColor="#00000000"
|
||||
android:pathData="M39,19L39,89"
|
||||
android:strokeWidth="0.8"
|
||||
android:strokeColor="#33FFFFFF" />
|
||||
<path
|
||||
android:fillColor="#00000000"
|
||||
android:pathData="M49,19L49,89"
|
||||
android:strokeWidth="0.8"
|
||||
android:strokeColor="#33FFFFFF" />
|
||||
<path
|
||||
android:fillColor="#00000000"
|
||||
android:pathData="M59,19L59,89"
|
||||
android:strokeWidth="0.8"
|
||||
android:strokeColor="#33FFFFFF" />
|
||||
<path
|
||||
android:fillColor="#00000000"
|
||||
android:pathData="M69,19L69,89"
|
||||
android:strokeWidth="0.8"
|
||||
android:strokeColor="#33FFFFFF" />
|
||||
<path
|
||||
android:fillColor="#00000000"
|
||||
android:pathData="M79,19L79,89"
|
||||
android:strokeWidth="0.8"
|
||||
android:strokeColor="#33FFFFFF" />
|
||||
</vector>
|
After Width: | Height: | Size: 4.7 KiB |
After Width: | Height: | Size: 4.7 KiB |
After Width: | Height: | Size: 3.7 KiB |
After Width: | Height: | Size: 7.6 KiB |
|
@ -0,0 +1,99 @@
|
|||
<?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"
|
||||
android:keepScreenOn="true"
|
||||
tools:context=".MainActivity">
|
||||
|
||||
<RelativeLayout
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="match_parent"
|
||||
android:background="@color/colorWindow">
|
||||
|
||||
<com.baidu.paddle.lite.demo.common.CameraSurfaceView
|
||||
android:id="@+id/sv_preview"
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="match_parent"
|
||||
android:layout_centerInParent="true" />
|
||||
|
||||
<RelativeLayout
|
||||
android:layout_width="@dimen/top_bar_height"
|
||||
android:layout_height="match_parent"
|
||||
android:layout_alignParentLeft="true"
|
||||
android:background="@color/colorTopBar">
|
||||
|
||||
<TextView
|
||||
android:id="@+id/tv_status"
|
||||
android:layout_width="wrap_content"
|
||||
android:layout_height="wrap_content"
|
||||
android:layout_alignParentBottom="true"
|
||||
android:layout_centerHorizontal="true"
|
||||
android:layout_marginLeft="@dimen/top_bar_left_right_margin"
|
||||
android:layout_marginBottom="@dimen/top_bar_left_right_margin"
|
||||
android:textColor="@color/colorText"
|
||||
android:gravity="center"
|
||||
android:textSize="@dimen/small_font_size" />
|
||||
|
||||
</RelativeLayout>
|
||||
|
||||
<LinearLayout
|
||||
android:layout_width="wrap_content"
|
||||
android:layout_height="match_parent"
|
||||
android:layout_alignParentRight="true"
|
||||
android:background="@color/colorBottomBar"
|
||||
android:orientation="horizontal">
|
||||
|
||||
<LinearLayout
|
||||
android:layout_width="@dimen/bottom_bar_top_margin"
|
||||
android:layout_height="match_parent"
|
||||
android:orientation="horizontal"></LinearLayout>
|
||||
|
||||
<RelativeLayout
|
||||
android:layout_width="@dimen/large_button_height"
|
||||
android:layout_height="match_parent">
|
||||
|
||||
<ImageButton
|
||||
android:id="@+id/btn_switch"
|
||||
android:layout_width="@dimen/small_button_width"
|
||||
android:layout_height="@dimen/small_button_height"
|
||||
android:layout_alignParentBottom="true"
|
||||
android:layout_centerHorizontal="true"
|
||||
android:layout_marginTop="@dimen/bottom_bar_left_right_margin"
|
||||
android:layout_marginBottom="@dimen/bottom_bar_left_right_margin"
|
||||
android:background="#00000000"
|
||||
android:scaleType="fitXY"
|
||||
android:src="@drawable/btn_switch" />
|
||||
|
||||
<ImageButton
|
||||
android:id="@+id/btn_shutter"
|
||||
android:layout_width="@dimen/large_button_width"
|
||||
android:layout_height="@dimen/large_button_height"
|
||||
android:layout_centerInParent="true"
|
||||
android:background="@null"
|
||||
android:focusable="true"
|
||||
android:focusableInTouchMode="true"
|
||||
android:scaleType="fitXY"
|
||||
android:src="@drawable/btn_shutter" />
|
||||
|
||||
<ImageButton
|
||||
android:id="@+id/btn_settings"
|
||||
android:layout_width="@dimen/small_button_width"
|
||||
android:layout_height="@dimen/small_button_width"
|
||||
android:layout_alignParentTop="true"
|
||||
android:layout_centerHorizontal="true"
|
||||
android:layout_marginTop="@dimen/bottom_bar_left_right_margin"
|
||||
android:background="@null"
|
||||
android:scaleType="fitXY"
|
||||
android:src="@drawable/btn_settings" />
|
||||
|
||||
</RelativeLayout>
|
||||
|
||||
<LinearLayout
|
||||
android:layout_width="@dimen/bottom_bar_bottom_margin"
|
||||
android:layout_height="match_parent"
|
||||
android:orientation="horizontal"></LinearLayout>
|
||||
</LinearLayout>
|
||||
</RelativeLayout>
|
||||
</android.support.constraint.ConstraintLayout>
|
|
@ -0,0 +1,257 @@
|
|||
<LinearLayout 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"
|
||||
android:orientation="vertical">
|
||||
<!-- 顶部按钮栏-->
|
||||
<LinearLayout
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="110dp"
|
||||
android:layout_marginTop="0dp"
|
||||
android:background="@drawable/white">
|
||||
<LinearLayout
|
||||
android:layout_weight="1"
|
||||
android:id="@+id/kt"
|
||||
android:layout_width="wrap_content"
|
||||
android:layout_height="wrap_content"
|
||||
android:orientation="vertical"
|
||||
android:layout_marginLeft="0dp"
|
||||
android:layout_marginStart="0dp">
|
||||
<ImageView
|
||||
android:id="@+id/show_index"
|
||||
android:layout_width="wrap_content"
|
||||
android:layout_height="wrap_content"
|
||||
android:background="@mipmap/leibiechaxun_100"
|
||||
android:layout_gravity="center">
|
||||
</ImageView>
|
||||
<TextView
|
||||
android:layout_width="fill_parent"
|
||||
android:layout_height="wrap_content"
|
||||
android:text="类别查询"
|
||||
android:textAlignment="center"
|
||||
android:textSize="15sp"
|
||||
android:gravity="center_horizontal">
|
||||
</TextView>
|
||||
</LinearLayout>
|
||||
<LinearLayout
|
||||
android:layout_weight="1"
|
||||
android:layout_width="wrap_content"
|
||||
android:layout_height="wrap_content"
|
||||
android:orientation="vertical">
|
||||
<ImageView
|
||||
android:id="@+id/take_gallery"
|
||||
android:layout_width="wrap_content"
|
||||
android:layout_height="wrap_content"
|
||||
android:layout_gravity="center"
|
||||
android:background="@mipmap/paizhaoshangchuan_100"
|
||||
android:maxWidth="150dp">
|
||||
</ImageView>
|
||||
<TextView
|
||||
android:layout_width="fill_parent"
|
||||
android:layout_height="wrap_content"
|
||||
android:text="拍照加库"
|
||||
android:textAlignment="center"
|
||||
android:textSize="15sp"
|
||||
android:gravity="center_horizontal">
|
||||
</TextView>
|
||||
</LinearLayout>
|
||||
<LinearLayout
|
||||
android:layout_weight="1"
|
||||
android:layout_width="wrap_content"
|
||||
android:layout_height="wrap_content"
|
||||
android:orientation="vertical">
|
||||
<ImageView
|
||||
android:id="@+id/add_gallery"
|
||||
android:layout_width="wrap_content"
|
||||
android:layout_height="wrap_content"
|
||||
android:layout_gravity="center"
|
||||
android:background="@mipmap/bendishangchuan_100">
|
||||
</ImageView>
|
||||
<TextView
|
||||
android:layout_width="fill_parent"
|
||||
android:layout_height="wrap_content"
|
||||
android:text="本地图片加库"
|
||||
android:textAlignment="center"
|
||||
android:textSize="15sp"
|
||||
android:gravity="center_horizontal">
|
||||
</TextView>
|
||||
</LinearLayout>
|
||||
<LinearLayout
|
||||
android:layout_width="wrap_content"
|
||||
android:layout_height="wrap_content"
|
||||
android:orientation="vertical"
|
||||
android:layout_weight="1">
|
||||
<ImageView
|
||||
android:id="@+id/save"
|
||||
android:layout_width="wrap_content"
|
||||
android:layout_height="wrap_content"
|
||||
android:background="@mipmap/baocunxiugai_100"
|
||||
android:layout_gravity="center">
|
||||
</ImageView>
|
||||
<TextView
|
||||
android:layout_width="fill_parent"
|
||||
android:layout_height="wrap_content"
|
||||
android:text="保存库"
|
||||
android:textAlignment="center"
|
||||
android:textSize="15sp"
|
||||
android:gravity="center_horizontal">
|
||||
</TextView>
|
||||
</LinearLayout>
|
||||
</LinearLayout>
|
||||
<!-- 图片展示栏-->
|
||||
<LinearLayout
|
||||
android:layout_width="fill_parent"
|
||||
android:layout_height="0dp"
|
||||
android:layout_gravity="center_vertical"
|
||||
android:layout_weight="1.4"
|
||||
android:gravity="center_vertical"
|
||||
android:orientation="vertical">
|
||||
<TextView
|
||||
android:id="@+id/description"
|
||||
android:layout_width="fill_parent"
|
||||
android:layout_height="wrap_content"
|
||||
android:text="【待识别图片】">
|
||||
</TextView>
|
||||
<ImageView
|
||||
android:id="@+id/iv_input_image"
|
||||
android:layout_width="wrap_content"
|
||||
android:layout_height="wrap_content"
|
||||
android:background="@drawable/white">
|
||||
</ImageView>
|
||||
</LinearLayout>
|
||||
<LinearLayout
|
||||
android:layout_width="fill_parent"
|
||||
android:layout_height="wrap_content">
|
||||
<EditText
|
||||
android:id="@+id/label_name"
|
||||
android:layout_weight="1"
|
||||
android:layout_width="wrap_content"
|
||||
android:layout_height="wrap_content"
|
||||
android:hint="label name"
|
||||
android:textColorHint="#546E7A" />
|
||||
|
||||
<Button
|
||||
android:id="@+id/label_botton"
|
||||
android:layout_width="0dp"
|
||||
android:layout_height="wrap_content"
|
||||
android:layout_weight="1"
|
||||
android:gravity="center"
|
||||
android:text="确定" />
|
||||
|
||||
<Button
|
||||
android:id="@+id/cancel_botton"
|
||||
android:layout_weight="1"
|
||||
android:layout_width="0dp"
|
||||
android:layout_height="wrap_content"
|
||||
|
||||
android:text="取消" />
|
||||
</LinearLayout>
|
||||
|
||||
<!-- 识别结果输出栏-->
|
||||
<LinearLayout
|
||||
android:layout_width="fill_parent"
|
||||
android:layout_height="wrap_content">
|
||||
<LinearLayout
|
||||
android:layout_width="fill_parent"
|
||||
android:layout_height="wrap_content"
|
||||
android:orientation="vertical">
|
||||
<TextView
|
||||
android:layout_width="fill_parent"
|
||||
android:layout_height="wrap_content"
|
||||
android:text="【识别结果】">
|
||||
</TextView>
|
||||
<LinearLayout
|
||||
android:layout_width="fill_parent"
|
||||
android:layout_height="wrap_content"
|
||||
android:orientation="horizontal">
|
||||
<TextView
|
||||
android:id="@+id/tv_top1_result"
|
||||
android:layout_width="0dp"
|
||||
android:layout_height="wrap_content"
|
||||
android:layout_weight="1"
|
||||
android:text="类别:">
|
||||
</TextView>
|
||||
<TextView
|
||||
android:id="@+id/similairy"
|
||||
android:layout_width="0dp"
|
||||
android:layout_weight="1"
|
||||
android:layout_height="wrap_content"
|
||||
android:text="相似度:">
|
||||
</TextView>
|
||||
</LinearLayout>
|
||||
<LinearLayout
|
||||
android:layout_width="fill_parent"
|
||||
android:layout_height="wrap_content"
|
||||
android:orientation="horizontal">
|
||||
<TextView
|
||||
android:id="@+id/index_name"
|
||||
android:layout_width="0dp"
|
||||
android:layout_height="wrap_content"
|
||||
android:layout_weight="1"
|
||||
android:text="检索库名称:">
|
||||
</TextView>
|
||||
<TextView
|
||||
android:id="@+id/tv_inference_time"
|
||||
android:layout_width="0dp"
|
||||
android:layout_weight="1"
|
||||
android:layout_height="wrap_content"
|
||||
android:text="预测时间:">
|
||||
</TextView>
|
||||
</LinearLayout>
|
||||
</LinearLayout>
|
||||
</LinearLayout>
|
||||
<!-- 底部按纽栏-->
|
||||
<LinearLayout
|
||||
android:layout_width="fill_parent"
|
||||
android:layout_height="0dp"
|
||||
android:gravity="bottom"
|
||||
android:layout_weight="0.4"
|
||||
android:layout_gravity="center_horizontal">
|
||||
<LinearLayout
|
||||
android:layout_width="wrap_content"
|
||||
android:layout_height="wrap_content"
|
||||
android:orientation="vertical"
|
||||
android:layout_weight="1">
|
||||
<ImageView
|
||||
android:id="@+id/take_query"
|
||||
android:layout_width="wrap_content"
|
||||
android:layout_height="wrap_content"
|
||||
android:background="@mipmap/paizhaoshibie_100"
|
||||
android:layout_gravity="center">
|
||||
</ImageView>
|
||||
<TextView
|
||||
android:layout_width="fill_parent"
|
||||
android:layout_height="wrap_content"
|
||||
android:text="拍照识别"
|
||||
android:textAlignment="center"
|
||||
android:textSize="15sp"
|
||||
android:gravity="center_horizontal">
|
||||
</TextView>
|
||||
</LinearLayout>
|
||||
|
||||
<LinearLayout
|
||||
android:layout_width="wrap_content"
|
||||
android:layout_height="wrap_content"
|
||||
android:orientation="vertical"
|
||||
android:layout_weight="1">
|
||||
<ImageView
|
||||
android:id="@+id/add_query"
|
||||
android:layout_width="wrap_content"
|
||||
android:layout_height="wrap_content"
|
||||
android:background="@mipmap/bendishibie_100"
|
||||
android:layout_gravity="center">
|
||||
</ImageView>
|
||||
<TextView
|
||||
android:layout_width="fill_parent"
|
||||
android:layout_height="wrap_content"
|
||||
android:text="本地识别"
|
||||
android:textAlignment="center"
|
||||
android:textSize="15sp"
|
||||
android:gravity="center_horizontal">
|
||||
</TextView>
|
||||
</LinearLayout>
|
||||
|
||||
</LinearLayout>
|
||||
</LinearLayout>
|
|
@ -0,0 +1,20 @@
|
|||
<menu xmlns:android="http://schemas.android.com/apk/res/android"
|
||||
xmlns:app="http://schemas.android.com/apk/res-auto">
|
||||
<group>
|
||||
<item
|
||||
android:id="@+id/reset"
|
||||
android:icon="@drawable/reset"
|
||||
android:title="初始化检索库"
|
||||
app:showAsAction="always"/>
|
||||
<item
|
||||
android:id="@+id/help"
|
||||
android:icon="@drawable/shuoming"
|
||||
android:title="settings"
|
||||
app:showAsAction="ifRoom"/>
|
||||
<item
|
||||
android:id="@+id/settings"
|
||||
android:icon="@drawable/shezhi"
|
||||
android:title="reset"
|
||||
app:showAsAction="always"/>
|
||||
</group>
|
||||
</menu>
|
|
@ -0,0 +1,5 @@
|
|||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<adaptive-icon xmlns:android="http://schemas.android.com/apk/res/android">
|
||||
<background android:drawable="@drawable/ic_launcher_background" />
|
||||
<foreground android:drawable="@drawable/ic_launcher_foreground" />
|
||||
</adaptive-icon>
|
|
@ -0,0 +1,5 @@
|
|||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<adaptive-icon xmlns:android="http://schemas.android.com/apk/res/android">
|
||||
<background android:drawable="@drawable/ic_launcher_background" />
|
||||
<foreground android:drawable="@drawable/ic_launcher_foreground" />
|
||||
</adaptive-icon>
|
After Width: | Height: | Size: 1.9 KiB |
After Width: | Height: | Size: 2.8 KiB |
After Width: | Height: | Size: 1013 B |
After Width: | Height: | Size: 1.5 KiB |
After Width: | Height: | Size: 3.0 KiB |
After Width: | Height: | Size: 3.1 KiB |
After Width: | Height: | Size: 1.8 KiB |
After Width: | Height: | Size: 2.7 KiB |
After Width: | Height: | Size: 4.3 KiB |
After Width: | Height: | Size: 3.0 KiB |
After Width: | Height: | Size: 3.6 KiB |
After Width: | Height: | Size: 4.0 KiB |
After Width: | Height: | Size: 28 KiB |
After Width: | Height: | Size: 5.0 KiB |
After Width: | Height: | Size: 7.9 KiB |
After Width: | Height: | Size: 8.3 KiB |
After Width: | Height: | Size: 12 KiB |
|
@ -0,0 +1,48 @@
|
|||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<resources>
|
||||
<string-array name="label_path_entries">
|
||||
<item>index_id_map.txt</item>
|
||||
</string-array>
|
||||
<string-array name="label_path_values">
|
||||
<item name="android:button">index/index_id_map.txt</item>
|
||||
</string-array>
|
||||
<string-array name="index_path_entries">
|
||||
<item>vector.index</item>
|
||||
</string-array>
|
||||
<string-array name="index_path_values">
|
||||
<item>index/vector.index</item>
|
||||
</string-array>
|
||||
<!-- <string-array name="cpu_thread_num_entries">-->
|
||||
<!-- <item>1 threads</item>-->
|
||||
<!-- <item>2 threads</item>-->
|
||||
<!-- <item>4 threads</item>-->
|
||||
<!-- <item>8 threads</item>-->
|
||||
<!-- </string-array>-->
|
||||
<!-- <string-array name="cpu_thread_num_values">-->
|
||||
<!-- <item>1</item>-->
|
||||
<!-- <item>2</item>-->
|
||||
<!-- <item>4</item>-->
|
||||
<!-- <item>8</item>-->
|
||||
<!-- </string-array>-->
|
||||
<!-- <string-array name="cpu_power_mode_entries">-->
|
||||
<!-- <item>HIGH(only big cores)</item>-->
|
||||
<!-- <item>LOW(only LITTLE cores)</item>-->
|
||||
<!-- <item>FULL(all cores)</item>-->
|
||||
<!-- <item>NO_BIND(depends on system)</item>-->
|
||||
<!-- <item>RAND_HIGH</item>-->
|
||||
<!-- <item>RAND_LOW</item>-->
|
||||
<!-- </string-array>-->
|
||||
<!-- <string-array name="cpu_power_mode_values">-->
|
||||
<!-- <item>LITE_POWER_HIGH</item>-->
|
||||
<!-- <item>LITE_POWER_LOW</item>-->
|
||||
<!-- <item>LITE_POWER_FULL</item>-->
|
||||
<!-- <item>LITE_POWER_NO_BIND</item>-->
|
||||
<!-- <item>LITE_POWER_RAND_HIGH</item>-->
|
||||
<!-- <item>LITE_POWER_RAND_LOW</item>-->
|
||||
<!-- </string-array>-->
|
||||
<!-- <string-array name="input_topk_values">-->
|
||||
<!-- <item>1</item>-->
|
||||
<!-- <item>2</item>-->
|
||||
<!-- <item>3</item>-->
|
||||
<!-- </string-array>-->
|
||||
</resources>
|
|
@ -0,0 +1,10 @@
|
|||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<resources>
|
||||
<color name="colorPrimary">#192EE3</color>
|
||||
<color name="colorPrimaryDark">#000000</color>
|
||||
<color name="colorAccent">#D81B60</color>
|
||||
<color name="colorWindow">#FF000000</color>
|
||||
<color name="colorTopBar">#00000000</color>
|
||||
<color name="colorBottomBar">#00000000</color>
|
||||
<color name="colorText">#FFFFFFFF</color>
|
||||
</resources>
|
|
@ -0,0 +1,17 @@
|
|||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<resources>
|
||||
<dimen name="bottom_bar_top_margin">26dp</dimen>
|
||||
<dimen name="bottom_bar_bottom_margin">36dp</dimen>
|
||||
<dimen name="bottom_bar_left_right_margin">34dp</dimen>
|
||||
<dimen name="top_bar_height">60dp</dimen>
|
||||
<dimen name="top_bar_left_right_margin">16dp</dimen>
|
||||
<dimen name="large_button_width">67dp</dimen>
|
||||
<dimen name="large_button_height">67dp</dimen>
|
||||
<dimen name="medium_button_width">56dp</dimen>
|
||||
<dimen name="medium_button_height">56dp</dimen>
|
||||
<dimen name="small_button_width">46dp</dimen>
|
||||
<dimen name="small_button_height">46dp</dimen>
|
||||
<dimen name="large_font_size">32dp</dimen>
|
||||
<dimen name="medium_font_size">24dp</dimen>
|
||||
<dimen name="small_font_size">16dp</dimen>
|
||||
</resources>
|
|
@ -0,0 +1,23 @@
|
|||
<resources>
|
||||
<string name="app_name">PP-ShiTu</string>
|
||||
<string name="CHOOSE_PRE_INSTALLED_MODEL_KEY">CHOOSE_PRE_INSTALLED_MODEL_KEY</string>
|
||||
<!-- <string name="ENABLE_CUSTOM_SETTINGS_KEY">ENABLE_CUSTOM_SETTINGS_KEY</string>-->
|
||||
<string name="MODEL_PATH_KEY">MODEL_PATH_KEY</string>
|
||||
<string name="LABEL_PATH_KEY">LABEL_PATH_KEY</string>
|
||||
<string name="INDEX_PATH_KEY">INDEX_DIR_KEY</string>
|
||||
<!-- <string name="IMAGE_PATH_KEY">IMAGE_PATH_KEY</string>-->
|
||||
<!-- <string name="CPU_THREAD_NUM_KEY">CPU_THREAD_NUM_KEY</string>-->
|
||||
<!-- <string name="CPU_POWER_MODE_KEY">CPU_POWER_MODE_KEY</string>-->
|
||||
<!-- <string name="INPUT_TOPK_KEY">INPUT_TOPK_KEY</string>-->
|
||||
<!-- <string name="DET_INPUT_SHAPE_KEY">DET_INPUT_SHAPE_KEY</string>-->
|
||||
<!-- <string name="REC_INPUT_SHAPE_KEY">REC_INPUT_SHAPE_KEY</string>-->
|
||||
<string name="MODEL_PATH_DEFAULT">models</string>
|
||||
<string name="LABEL_PATH_DEFAULT">index/id_map.txt</string>
|
||||
<string name="INDEX_PATH_DEFAULT">index/vector.index</string>
|
||||
<string name="IMAGE_PATH_DEFAULT">images/demo.jpg</string>
|
||||
<string name="CPU_THREAD_NUM_DEFAULT">4</string>
|
||||
<string name="CPU_POWER_MODE_DEFAULT">LITE_POWER_HIGH</string>
|
||||
<string name="INPUT_TOPK_DEFAULT">3</string>
|
||||
<string name="DET_INPUT_SHAPE_DEFAULT">1,3,640,640</string>
|
||||
<string name="REC_INPUT_SHAPE_DEFAULT">1,3,224,224</string>
|
||||
</resources>
|
|
@ -0,0 +1,25 @@
|
|||
<resources>
|
||||
|
||||
<!-- Base application theme. -->
|
||||
<style name="AppTheme" parent="Theme.AppCompat.Light.DarkActionBar">
|
||||
<!-- Customize your theme here. -->
|
||||
<item name="colorPrimary">@color/colorPrimary</item>
|
||||
<item name="colorPrimaryDark">@color/colorPrimaryDark</item>
|
||||
<item name="colorAccent">@color/colorAccent</item>
|
||||
<item name="actionOverflowMenuStyle">@style/OverflowMenuStyle</item>
|
||||
</style>
|
||||
|
||||
<style name="OverflowMenuStyle" parent="Widget.AppCompat.Light.PopupMenu.Overflow">
|
||||
<item name="overlapAnchor">false</item>
|
||||
</style>
|
||||
|
||||
<style name="AppTheme.NoActionBar">
|
||||
<item name="windowActionBar">false</item>
|
||||
<item name="windowNoTitle">true</item>
|
||||
</style>
|
||||
|
||||
<style name="AppTheme.AppBarOverlay" parent="ThemeOverlay.AppCompat.Dark.ActionBar"/>
|
||||
|
||||
<style name="AppTheme.PopupOverlay" parent="ThemeOverlay.AppCompat.Light"/>
|
||||
|
||||
</resources>
|
|
@ -0,0 +1,31 @@
|
|||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<PreferenceScreen xmlns:android="http://schemas.android.com/apk/res/android">
|
||||
<PreferenceCategory android:title="Model Settings">
|
||||
<ListPreference
|
||||
android:defaultValue="@string/MODEL_PATH_DEFAULT"
|
||||
android:key="@string/CHOOSE_PRE_INSTALLED_MODEL_KEY"
|
||||
android:negativeButtonText="@null"
|
||||
android:positiveButtonText="@null"
|
||||
android:title="Choose pre-installed models" />
|
||||
|
||||
<ListPreference
|
||||
android:defaultValue="@string/LABEL_PATH_DEFAULT"
|
||||
android:entries="@array/label_path_entries"
|
||||
android:entryValues="@array/label_path_values"
|
||||
android:key="@string/LABEL_PATH_KEY"
|
||||
android:negativeButtonText="@null"
|
||||
android:positiveButtonText="@null"
|
||||
android:title="Label Path" />
|
||||
|
||||
<ListPreference
|
||||
android:defaultValue="@string/INDEX_PATH_DEFAULT"
|
||||
android:entries="@array/index_path_entries"
|
||||
android:entryValues="@array/index_path_values"
|
||||
android:key="@string/INDEX_PATH_KEY"
|
||||
android:negativeButtonText="@null"
|
||||
android:positiveButtonText="@null"
|
||||
android:title="Index Path" />
|
||||
|
||||
</PreferenceCategory>
|
||||
</PreferenceScreen>
|
||||
|
|
@ -0,0 +1,17 @@
|
|||
package com.baidu.paddle.lite.demo.pp_shitu;
|
||||
|
||||
import org.junit.Test;
|
||||
|
||||
import static org.junit.Assert.*;
|
||||
|
||||
/**
|
||||
* Example local unit test, which will execute on the development machine (host).
|
||||
*
|
||||
* @see <a href="http://d.android.com/tools/testing">Testing documentation</a>
|
||||
*/
|
||||
public class ExampleUnitTest {
|
||||
@Test
|
||||
public void addition_isCorrect() {
|
||||
assertEquals(4, 2 + 2);
|
||||
}
|
||||
}
|
|
@ -0,0 +1,27 @@
|
|||
// Top-level build file where you can add configuration options common to all sub-projects/modules.
|
||||
|
||||
buildscript {
|
||||
repositories {
|
||||
google()
|
||||
jcenter()
|
||||
|
||||
}
|
||||
dependencies {
|
||||
classpath 'com.android.tools.build:gradle:4.1.0'
|
||||
|
||||
// NOTE: Do not place your application dependencies here; they belong
|
||||
// in the individual module build.gradle files
|
||||
}
|
||||
}
|
||||
|
||||
allprojects {
|
||||
repositories {
|
||||
google()
|
||||
jcenter()
|
||||
|
||||
}
|
||||
}
|
||||
|
||||
task clean(type: Delete) {
|
||||
delete rootProject.buildDir
|
||||
}
|
|
@ -0,0 +1,119 @@
|
|||
# PP-ShiTu android demo
|
||||
|
||||
本项目是一个基于 PaddleClas PP-ShiTu 和 [Paddle-Lite-Demo](https://github.com/PaddlePaddle/Paddle-Lite-Demo) 的安卓轻量级图像识别APP
|
||||
|
||||
本工程Android部分的代码基于 [Paddle-Lite-Demo](https://github.com/PaddlePaddle/Paddle-Lite-Demo) 开发,识别过程中使用的检测与识别模型基于 PaddleDetection 和 PaddleClas
|
||||
详细信息可以查看对应模型文档 [主体检测模型](../../../docs/zh_CN/training/PP-ShiTu/mainbody_detection.md) 与 [特征提取模型](../../../docs/zh_CN/training/PP-ShiTu/feature_extraction.md) 以及 [faiss检索库](../../../docs/zh_CN/deployment/PP-ShiTu/vector_search.md)
|
||||
|
||||
|
||||
## APP安装体验
|
||||
|
||||
- 点击链接下载:[下载链接](https://paddle-imagenet-models-name.bj.bcebos.com/demos/PP-ShiTu.apk)
|
||||
- 扫描下方二维码下载:
|
||||
<div align="center">
|
||||
<img src="../../../docs/images/quick_start/android_demo/PPShiTu_qrcode.png" width=20% height=20%>
|
||||
</div>
|
||||
|
||||
## Demo开发介绍
|
||||
本Demo使用的集成开发环境是 [AndroidStudio](https://developer.android.google.cn/studio)
|
||||
|
||||
### 克隆本项目
|
||||
首先使用git工具,将本项目克隆到本地
|
||||
|
||||
### 文件准备
|
||||
下述步骤均以 **ppshituv2_lite_models_v1.0.tar** 模型压缩包为例,如需使用非量化的模型包 ppshituv2_lite_models_fp32_v1.0.tar ,则按照类似步骤进行操作,过程中使用非量化的识别模型生成检索库,同时按照 [FAQ-Q2](#faq) 将文件名替换成非量化的模型文件名即可。
|
||||
|
||||
1. 下载我们准备好的模型压缩包:
|
||||
|
||||
| model | download_link | size |
|
||||
| :--------- | :------------------------------------------------ | :-- |
|
||||
| 量化模型 | [ppshituv2_lite_models_v1.0.tar](https://paddle-imagenet-models-name.bj.bcebos.com/dygraph/lite/ppshituv2_lite_models_v1.0.tar) | 13.8MB |
|
||||
| fp32模型 | [ppshituv2_lite_models_fp32_v1.0.tar](https://paddle-imagenet-models-name.bj.bcebos.com/dygraph/lite/ppshituv2_lite_models_fp32_v1.0.tar) | 44.8MB |
|
||||
**注:** 量化模型虽然减少了体积但也略微降低了精度,用户可以根据实际需要下载量化或非量化版本的模型文件;上述表格中的“size”指主体检测+特征提取`*.nb`文件的大小总和,不包括压缩包内额外提供的方便用户导出检索库的普通推理模型大小;
|
||||
|
||||
|
||||
下载 [ppshituv2_lite_models_v1.0.tar](https://paddle-imagenet-models-name.bj.bcebos.com/dygraph/lite/ppshituv2_lite_models_v1.0.tar) 并解压出来,将其中2个 `*.nb` 格式的Lite模型文件放置到 [shitu_android_demo/app/src/main/assets/models](../app/src/main/assets/models) 目录下。以 PP-ShiTuV2 的主体检测和特征提取模型为例,模型最终的文件组织结构如下所示
|
||||
```log
|
||||
models
|
||||
├── general_PPLCNetV2_base_quant_v1.0_lite.nb
|
||||
└── mainbody_PPLCNet_x2_5_640_quant_v1.0_lite.nb
|
||||
```
|
||||
|
||||
2. 下载 [faiss1.5.3.tar.gz](https://paddle-inference-dist.bj.bcebos.com/faiss1.5.3.tar.gz) 检索库文件并解压,里面包含了编译好的 `libfaiss.a` 文件和对应的头文件,将 `include` 文件夹放置在 `main/cpp` 目录下,将 `libs` 文件夹下的 `arm64-v8a` 和 `armeabi-v7a` 放置在 `main/jniLibs` 文件夹下。
|
||||
|
||||
3. 以 `drink_dataset_v2.0` 检索库为例,依次执行以下命令,生成检索库 `vector.index` 文件与pkl格式的标签库 `id_map.pkl` 文件
|
||||
|
||||
```shell
|
||||
# 进入工作目录
|
||||
cd PaddleClas/deploy
|
||||
|
||||
# 下载数据集
|
||||
wget -nc https://paddle-imagenet-models-name.bj.bcebos.com/dygraph/rec/data/drink_dataset_v2.0.tar
|
||||
tar -xf drink_dataset_v2.0.tar
|
||||
|
||||
# 下载lite模型
|
||||
cd models
|
||||
wget -nc https://paddle-imagenet-models-name.bj.bcebos.com/dygraph/lite/ppshituv2_lite_models_v1.0.tar
|
||||
tar -xf ppshituv2_lite_models_v1.0.tar
|
||||
cd ../
|
||||
|
||||
# 构建检索库与标签库(如使用非量化模型,则需将Global.rec_inference_model_dir后的路径改为非量化推理模型)
|
||||
python3.7 python/build_gallery.py -c configs/inference_drink.yaml -o Global.rec_inference_model_dir=./models/ppshituv2_lite_models_v1.0/general_PPLCNetV2_base_quant_v1.0_lite_inference_model -o IndexProcess.index_method=Flat -o Global.android_demo=True
|
||||
```
|
||||
4. 然后将 `configs/inference_drink.yaml` 文件中的 `IndexProcess.index_dir` 字段后的路径改为 `"../drink_dataset_v2.0/index"`,再执行以下命令,得到转换成txt格式的标签库
|
||||
|
||||
```shell
|
||||
cd ./lite_shitu
|
||||
python3.7 transform_id_map.py -c ../configs/inference_drink.yaml
|
||||
```
|
||||
|
||||
5. 最后,将上述步骤生成的 `vector.index` 与 `id_map.txt` 重命名为 `original.index` 与 `original.txt` ,放置到 [shitu_android_demo/app/src/main/assets/index](../app/src/main/assets/index) 文件夹下。
|
||||
|
||||
### 打开Demo
|
||||
1. 首先下载并安装AndroidStudio集成开发环境(IDE)
|
||||
2. 打开安装好的AndroidStudio,依次点击标题栏左侧的"File"→"Open"→找到并选择 `shitu_android_demo` 文件夹,点击"ok",等待IDE加载处理完毕,如下图所示
|
||||
<div align="center">
|
||||
<img src="./images/step1.png" width="800">
|
||||
</div>
|
||||
|
||||
4. 然后将手机通过USB数据线连接到调试用的电脑上,并且在手机上**选择允许USB调试模式**。以华为手机为例,连接电脑后选择"传输文件",并且在"开发人员选项"中开启"USB调试"选项,如下图所示
|
||||
<div align="center">
|
||||
<img src="./images/step2.1.png" width="400">
|
||||
<img src="./images/step2.2.png" width="400">
|
||||
</div>
|
||||
|
||||
**注: 这一步非常重要,否则可能Android Studio无法检测并安装APP到手机上**
|
||||
5. 连接完毕后应该能在Android Studio界面右上角显示出手机型号,如下图所示
|
||||

|
||||
|
||||
6. 然后点击手机型号右侧第一个绿色箭头,会自动进行交叉编译并安装APP到手机上,在手机上确认安装即可开始使用、调试APP。开发过程中想即时使用当前代码生成的APP,则仍旧点击手机型号右侧的第一个按钮(开发过程中绿色箭头的按钮图标可能会变成其它形状,但仍然可以点击它),在手机上确认安装最新APK文件,重复这一过程直至开发完成即可。开发完毕后,如需导出APK文件并分享给他人安装,请自行上网查找对应教程。
|
||||
**注: 过程中AndroidStudio可能会提示需要安装一系列的环境包,包括JRE、Android NDK等,请按照提示安装,否则可能无法编译安装APP到手机上。**
|
||||
|
||||
### 二次开发
|
||||
本项目的功能实现主要由一些的代码文件完成,如下所示
|
||||
```log
|
||||
main
|
||||
├── assets/ # 资源文件夹,用于放置界面图标、模型文件以及检索库
|
||||
├── cpp/ # CPP代码文件夹,其下的include文件夹配合libfaiss.a用来调用faiss检索API,其余代码文件负责控制C++层的代码逻辑
|
||||
└── include/
|
||||
└── faiss/ # faiss 检索库头文件,无需改动
|
||||
├── java/ # JAVA代码文件夹,内部的代码文件负责控制JAVA层的代码逻辑
|
||||
├── jniLibs/
|
||||
├── arm64-v8a/ # 用于存放第三方SDK文件所需的库,如存放libfaiss.a文件
|
||||
└── armeabi-v7a/ # 用于存放第三方SDK文件所需的库,如存放libfaiss.a文件
|
||||
├── docs/ # 本项目文档与文档图片存放目录
|
||||
├── ...
|
||||
└── ...
|
||||
```
|
||||
开发者可以针对以上涉及的具体代码文件进行修改与二次开发
|
||||
|
||||
|
||||
### FAQ
|
||||
Q1: 点击绿色箭头后,交叉编译过程中报“source-7不支持lambda匿名表达式“的错误
|
||||
A1: 将光标移动到报错的代码上,按 `Alt+Enter` 键,选择替换语言支持到source-8,等待IDE同步更新完毕,再重新点击绿色箭头即可。
|
||||
|
||||
Q2: 想更换其他检测或识别模型
|
||||
A2: 首先将自己准备的模型放置到 [shitu_android_demo/app/src/main/assets/models](../app/src/main/assets/models) 目录下,然后对用替换 [Pipeline.cc](../app/src/main/cpp/Pipeline.cc#L64-L65) 文件中检测和识别模型的路径,重新点击绿色三角箭头交叉编译安装即可。
|
||||
|
||||
Q3: 开发过程中APP进行某些操作会闪退
|
||||
A3: 一般闪退是由于APP代码存在一些BUG导致程序运行出现错误,可以重点关注Android Studio下方的"Run"窗口,闪退时报错信息会显示在里面,方便快速定位问题代码行。
|
After Width: | Height: | Size: 18 KiB |
After Width: | Height: | Size: 30 KiB |
After Width: | Height: | Size: 140 KiB |
After Width: | Height: | Size: 4.1 KiB |
|
@ -0,0 +1,15 @@
|
|||
# Project-wide Gradle settings.
|
||||
# IDE (e.g. Android Studio) users:
|
||||
# Gradle settings configured through the IDE *will override*
|
||||
# any settings specified in this file.
|
||||
# For more details on how to configure your build environment visit
|
||||
# http://www.gradle.org/docs/current/userguide/build_environment.html
|
||||
# Specifies the JVM arguments used for the daemon process.
|
||||
# The setting is particularly useful for tweaking memory settings.
|
||||
org.gradle.jvmargs=-Xmx1536m
|
||||
# When configured, Gradle will run in incubating parallel mode.
|
||||
# This option should only be used with decoupled projects. More details, visit
|
||||
# http://www.gradle.org/docs/current/userguide/multi_project_builds.html#sec:decoupled_projects
|
||||
# org.gradle.parallel=true
|
||||
|
||||
|
|
@ -0,0 +1,6 @@
|
|||
#Thu Aug 22 15:05:37 CST 2019
|
||||
distributionBase=GRADLE_USER_HOME
|
||||
distributionPath=wrapper/dists
|
||||
zipStoreBase=GRADLE_USER_HOME
|
||||
zipStorePath=wrapper/dists
|
||||
distributionUrl=https\://services.gradle.org/distributions/gradle-7.0-all.zip
|
|
@ -0,0 +1,172 @@
|
|||
#!/usr/bin/env sh
|
||||
|
||||
##############################################################################
|
||||
##
|
||||
## Gradle start up script for UN*X
|
||||
##
|
||||
##############################################################################
|
||||
|
||||
# Attempt to set APP_HOME
|
||||
# Resolve links: $0 may be a link
|
||||
PRG="$0"
|
||||
# Need this for relative symlinks.
|
||||
while [ -h "$PRG" ] ; do
|
||||
ls=`ls -ld "$PRG"`
|
||||
link=`expr "$ls" : '.*-> \(.*\)$'`
|
||||
if expr "$link" : '/.*' > /dev/null; then
|
||||
PRG="$link"
|
||||
else
|
||||
PRG=`dirname "$PRG"`"/$link"
|
||||
fi
|
||||
done
|
||||
SAVED="`pwd`"
|
||||
cd "`dirname \"$PRG\"`/" >/dev/null
|
||||
APP_HOME="`pwd -P`"
|
||||
cd "$SAVED" >/dev/null
|
||||
|
||||
APP_NAME="Gradle"
|
||||
APP_BASE_NAME=`basename "$0"`
|
||||
|
||||
# Add default JVM options here. You can also use JAVA_OPTS and GRADLE_OPTS to pass JVM options to this script.
|
||||
DEFAULT_JVM_OPTS=""
|
||||
|
||||
# Use the maximum available, or set MAX_FD != -1 to use that value.
|
||||
MAX_FD="maximum"
|
||||
|
||||
warn () {
|
||||
echo "$*"
|
||||
}
|
||||
|
||||
die () {
|
||||
echo
|
||||
echo "$*"
|
||||
echo
|
||||
exit 1
|
||||
}
|
||||
|
||||
# OS specific support (must be 'true' or 'false').
|
||||
cygwin=false
|
||||
msys=false
|
||||
darwin=false
|
||||
nonstop=false
|
||||
case "`uname`" in
|
||||
CYGWIN* )
|
||||
cygwin=true
|
||||
;;
|
||||
Darwin* )
|
||||
darwin=true
|
||||
;;
|
||||
MINGW* )
|
||||
msys=true
|
||||
;;
|
||||
NONSTOP* )
|
||||
nonstop=true
|
||||
;;
|
||||
esac
|
||||
|
||||
CLASSPATH=$APP_HOME/gradle/wrapper/gradle-wrapper.jar
|
||||
|
||||
# Determine the Java command to use to start the JVM.
|
||||
if [ -n "$JAVA_HOME" ] ; then
|
||||
if [ -x "$JAVA_HOME/jre/sh/java" ] ; then
|
||||
# IBM's JDK on AIX uses strange locations for the executables
|
||||
JAVACMD="$JAVA_HOME/jre/sh/java"
|
||||
else
|
||||
JAVACMD="$JAVA_HOME/bin/java"
|
||||
fi
|
||||
if [ ! -x "$JAVACMD" ] ; then
|
||||
die "ERROR: JAVA_HOME is set to an invalid directory: $JAVA_HOME
|
||||
|
||||
Please set the JAVA_HOME variable in your environment to match the
|
||||
location of your Java installation."
|
||||
fi
|
||||
else
|
||||
JAVACMD="java"
|
||||
which java >/dev/null 2>&1 || die "ERROR: JAVA_HOME is not set and no 'java' command could be found in your PATH.
|
||||
|
||||
Please set the JAVA_HOME variable in your environment to match the
|
||||
location of your Java installation."
|
||||
fi
|
||||
|
||||
# Increase the maximum file descriptors if we can.
|
||||
if [ "$cygwin" = "false" -a "$darwin" = "false" -a "$nonstop" = "false" ] ; then
|
||||
MAX_FD_LIMIT=`ulimit -H -n`
|
||||
if [ $? -eq 0 ] ; then
|
||||
if [ "$MAX_FD" = "maximum" -o "$MAX_FD" = "max" ] ; then
|
||||
MAX_FD="$MAX_FD_LIMIT"
|
||||
fi
|
||||
ulimit -n $MAX_FD
|
||||
if [ $? -ne 0 ] ; then
|
||||
warn "Could not set maximum file descriptor limit: $MAX_FD"
|
||||
fi
|
||||
else
|
||||
warn "Could not query maximum file descriptor limit: $MAX_FD_LIMIT"
|
||||
fi
|
||||
fi
|
||||
|
||||
# For Darwin, add options to specify how the application appears in the dock
|
||||
if $darwin; then
|
||||
GRADLE_OPTS="$GRADLE_OPTS \"-Xdock:name=$APP_NAME\" \"-Xdock:icon=$APP_HOME/media/gradle.icns\""
|
||||
fi
|
||||
|
||||
# For Cygwin, switch paths to Windows format before running java
|
||||
if $cygwin ; then
|
||||
APP_HOME=`cygpath --path --mixed "$APP_HOME"`
|
||||
CLASSPATH=`cygpath --path --mixed "$CLASSPATH"`
|
||||
JAVACMD=`cygpath --unix "$JAVACMD"`
|
||||
|
||||
# We build the pattern for arguments to be converted via cygpath
|
||||
ROOTDIRSRAW=`find -L / -maxdepth 1 -mindepth 1 -type d 2>/dev/null`
|
||||
SEP=""
|
||||
for dir in $ROOTDIRSRAW ; do
|
||||
ROOTDIRS="$ROOTDIRS$SEP$dir"
|
||||
SEP="|"
|
||||
done
|
||||
OURCYGPATTERN="(^($ROOTDIRS))"
|
||||
# Add a user-defined pattern to the cygpath arguments
|
||||
if [ "$GRADLE_CYGPATTERN" != "" ] ; then
|
||||
OURCYGPATTERN="$OURCYGPATTERN|($GRADLE_CYGPATTERN)"
|
||||
fi
|
||||
# Now convert the arguments - kludge to limit ourselves to /bin/sh
|
||||
i=0
|
||||
for arg in "$@" ; do
|
||||
CHECK=`echo "$arg"|egrep -c "$OURCYGPATTERN" -`
|
||||
CHECK2=`echo "$arg"|egrep -c "^-"` ### Determine if an option
|
||||
|
||||
if [ $CHECK -ne 0 ] && [ $CHECK2 -eq 0 ] ; then ### Added a condition
|
||||
eval `echo args$i`=`cygpath --path --ignore --mixed "$arg"`
|
||||
else
|
||||
eval `echo args$i`="\"$arg\""
|
||||
fi
|
||||
i=$((i+1))
|
||||
done
|
||||
case $i in
|
||||
(0) set -- ;;
|
||||
(1) set -- "$args0" ;;
|
||||
(2) set -- "$args0" "$args1" ;;
|
||||
(3) set -- "$args0" "$args1" "$args2" ;;
|
||||
(4) set -- "$args0" "$args1" "$args2" "$args3" ;;
|
||||
(5) set -- "$args0" "$args1" "$args2" "$args3" "$args4" ;;
|
||||
(6) set -- "$args0" "$args1" "$args2" "$args3" "$args4" "$args5" ;;
|
||||
(7) set -- "$args0" "$args1" "$args2" "$args3" "$args4" "$args5" "$args6" ;;
|
||||
(8) set -- "$args0" "$args1" "$args2" "$args3" "$args4" "$args5" "$args6" "$args7" ;;
|
||||
(9) set -- "$args0" "$args1" "$args2" "$args3" "$args4" "$args5" "$args6" "$args7" "$args8" ;;
|
||||
esac
|
||||
fi
|
||||
|
||||
# Escape application args
|
||||
save () {
|
||||
for i do printf %s\\n "$i" | sed "s/'/'\\\\''/g;1s/^/'/;\$s/\$/' \\\\/" ; done
|
||||
echo " "
|
||||
}
|
||||
APP_ARGS=$(save "$@")
|
||||
|
||||
# Collect all arguments for the java command, following the shell quoting and substitution rules
|
||||
eval set -- $DEFAULT_JVM_OPTS $JAVA_OPTS $GRADLE_OPTS "\"-Dorg.gradle.appname=$APP_BASE_NAME\"" -classpath "\"$CLASSPATH\"" org.gradle.wrapper.GradleWrapperMain "$APP_ARGS"
|
||||
|
||||
# by default we should be in the correct project dir, but when run from Finder on Mac, the cwd is wrong
|
||||
if [ "$(uname)" = "Darwin" ] && [ "$HOME" = "$PWD" ]; then
|
||||
cd "$(dirname "$0")"
|
||||
fi
|
||||
|
||||
exec "$JAVACMD" "$@"
|
|
@ -0,0 +1,84 @@
|
|||
@if "%DEBUG%" == "" @echo off
|
||||
@rem ##########################################################################
|
||||
@rem
|
||||
@rem Gradle startup script for Windows
|
||||
@rem
|
||||
@rem ##########################################################################
|
||||
|
||||
@rem Set local scope for the variables with windows NT shell
|
||||
if "%OS%"=="Windows_NT" setlocal
|
||||
|
||||
set DIRNAME=%~dp0
|
||||
if "%DIRNAME%" == "" set DIRNAME=.
|
||||
set APP_BASE_NAME=%~n0
|
||||
set APP_HOME=%DIRNAME%
|
||||
|
||||
@rem Add default JVM options here. You can also use JAVA_OPTS and GRADLE_OPTS to pass JVM options to this script.
|
||||
set DEFAULT_JVM_OPTS=
|
||||
|
||||
@rem Find java.exe
|
||||
if defined JAVA_HOME goto findJavaFromJavaHome
|
||||
|
||||
set JAVA_EXE=java.exe
|
||||
%JAVA_EXE% -version >NUL 2>&1
|
||||
if "%ERRORLEVEL%" == "0" goto init
|
||||
|
||||
echo.
|
||||
echo ERROR: JAVA_HOME is not set and no 'java' command could be found in your PATH.
|
||||
echo.
|
||||
echo Please set the JAVA_HOME variable in your environment to match the
|
||||
echo location of your Java installation.
|
||||
|
||||
goto fail
|
||||
|
||||
:findJavaFromJavaHome
|
||||
set JAVA_HOME=%JAVA_HOME:"=%
|
||||
set JAVA_EXE=%JAVA_HOME%/bin/java.exe
|
||||
|
||||
if exist "%JAVA_EXE%" goto init
|
||||
|
||||
echo.
|
||||
echo ERROR: JAVA_HOME is set to an invalid directory: %JAVA_HOME%
|
||||
echo.
|
||||
echo Please set the JAVA_HOME variable in your environment to match the
|
||||
echo location of your Java installation.
|
||||
|
||||
goto fail
|
||||
|
||||
:init
|
||||
@rem Get command-line arguments, handling Windows variants
|
||||
|
||||
if not "%OS%" == "Windows_NT" goto win9xME_args
|
||||
|
||||
:win9xME_args
|
||||
@rem Slurp the command line arguments.
|
||||
set CMD_LINE_ARGS=
|
||||
set _SKIP=2
|
||||
|
||||
:win9xME_args_slurp
|
||||
if "x%~1" == "x" goto execute
|
||||
|
||||
set CMD_LINE_ARGS=%*
|
||||
|
||||
:execute
|
||||
@rem Setup the command line
|
||||
|
||||
set CLASSPATH=%APP_HOME%\gradle\wrapper\gradle-wrapper.jar
|
||||
|
||||
@rem Execute Gradle
|
||||
"%JAVA_EXE%" %DEFAULT_JVM_OPTS% %JAVA_OPTS% %GRADLE_OPTS% "-Dorg.gradle.appname=%APP_BASE_NAME%" -classpath "%CLASSPATH%" org.gradle.wrapper.GradleWrapperMain %CMD_LINE_ARGS%
|
||||
|
||||
:end
|
||||
@rem End local scope for the variables with windows NT shell
|
||||
if "%ERRORLEVEL%"=="0" goto mainEnd
|
||||
|
||||
:fail
|
||||
rem Set variable GRADLE_EXIT_CONSOLE if you need the _script_ return code instead of
|
||||
rem the _cmd.exe /c_ return code!
|
||||
if not "" == "%GRADLE_EXIT_CONSOLE%" exit 1
|
||||
exit /b 1
|
||||
|
||||
:mainEnd
|
||||
if "%OS%"=="Windows_NT" endlocal
|
||||
|
||||
:omega
|
|
@ -0,0 +1,8 @@
|
|||
## This file must *NOT* be checked into Version Control Systems,
|
||||
# as it contains information specific to your local configuration.
|
||||
#
|
||||
# Location of the SDK. This is only used by Gradle.
|
||||
# For customization when using a Version Control System, please read the
|
||||
# header note.
|
||||
#Mon Sep 19 19:05:12 CST 2022
|
||||
sdk.dir=C\:\\Users\\hesensen\\AppData\\Local\\Android\\Sdk
|
|
@ -0,0 +1 @@
|
|||
include ':app'
|
Before Width: | Height: | Size: 767 KiB After Width: | Height: | Size: 522 KiB |
Before Width: | Height: | Size: 2.6 KiB After Width: | Height: | Size: 8.7 KiB |
|
@ -57,7 +57,7 @@
|
|||
|
||||
在选择好要入库的图片之后,首先会通过检测模型进行主体检测,得到图像中的物体的区域,然后将这块区域裁剪出来输入到识别模型中,得到对应的特征向量,再与用户输入的图像标签一起加入到检索库中。
|
||||
|
||||
**温馨提示:** 使用安卓demo管理类别主要用于功能体验,如果您有较为重要的数据要生成检索库,推荐使用[检索库管理工具](https://github.com/PaddlePaddle/PaddleClas/blob/release/2.5/docs/zh_CN/inference_deployment/shitu_gallery_manager.md)
|
||||
**温馨提示:** 使用安卓demo管理类别主要用于功能体验,如果您有较为重要的数据要生成检索库,推荐使用 [检索库管理工具](../deployment/PP-ShiTu/gallery_manager.md)
|
||||
|
||||
#### (3) 保存检索库
|
||||
点击上方的“保存修改”按钮<img src="../../images/quick_start/android_demo/baocunxiugai_100.png" width="25" height="25"/>,即可将当前库以 `latest` 的库名保存下来。
|
||||
|
|