From 6245fdd584502150781997f6a4ddc070cd851051 Mon Sep 17 00:00:00 2001 From: gongdaguo Date: Thu, 29 Oct 2020 15:50:52 +0800 Subject: [PATCH] Adaptation version 1.0.1 And Add HiMindspore Demo --- mindspore/lite/test/models_onnx.cfg | 6 + .../official/lite/Himindspore/.gitignore | 83 ++ .../official/lite/Himindspore/app/.gitignore | 2 + .../lite/Himindspore/app/CMakeLists.txt | 86 ++ .../lite/Himindspore/app/build.gradle | 72 ++ .../lite/Himindspore/app/download.gradle | 115 +++ .../lite/Himindspore/app/proguard-rules.pro | 21 + .../himindspore/ExampleInstrumentedTest.java | 26 + .../app/src/main/AndroidManifest.xml | 58 ++ .../main/cpp/GarbageMindSporeNetnative.cpp | 338 +++++++ .../src/main/cpp/GarbageMindSporeNetnative.h | 21 + .../src/main/cpp/ImageMindSporeNetnative.cpp | 811 +++++++++++++++++ .../src/main/cpp/ImageMindSporeNetnative.h | 21 + .../app/src/main/cpp/MSNetWork.cpp | 52 ++ .../Himindspore/app/src/main/cpp/MSNetWork.h | 59 ++ .../src/main/cpp/ObjectMindSporeNetnative.cpp | 267 ++++++ .../app/src/main/cpp/ssd_util/ssd_util.cpp | 291 ++++++ .../app/src/main/cpp/ssd_util/ssd_util.h | 200 +++++ .../mindspore/himindspore/SplashActivity.java | 90 ++ .../himindspore/camera/CameraPreview.java | 843 ++++++++++++++++++ .../contract/ContractActivity.java | 55 ++ .../himindspore/contract/email/MailInfo.java | 109 +++ .../contract/email/MailSender.java | 202 +++++ .../contract/email/MyAuthenticator.java | 21 + .../contract/email/SendMailUtil.java | 51 ++ .../bean/RecognitionImageBean.java | 46 + .../help/GarbageTrackingMobile.java | 130 +++ .../help/ImageTrackingMobile.java | 129 +++ .../imageclassification/ui/HorTextView.java | 73 ++ .../ui/ImageCameraActivity.java | 224 +++++ .../ui/ImageMainActivity.java | 37 + .../bean/RecognitionObjectBean.java | 159 ++++ .../help/ImageDegreeHelper.java | 185 ++++ .../help/ObjectTrackingMobile.java | 120 +++ .../ui/ObjectCameraActivity.java | 87 ++ .../ui/ObjectDetectionMainActivity.java | 87 ++ .../objectdetection/ui/ObjectRectView.java | 115 +++ .../objectdetection/ui/PhotoActivity.java | 115 +++ .../himindspore/track/TrackListener.java | 4 + .../himindspore/utils/DisplayUtil.java | 47 + .../drawable-v24/ic_launcher_foreground.xml | 30 + .../src/main/res/drawable-xxhdpi/btn_code.png | Bin 0 -> 3435 bytes .../src/main/res/drawable-xxhdpi/btn_help.png | Bin 0 -> 1911 bytes .../main/res/drawable-xxhdpi/btn_image.png | Bin 0 -> 3287 bytes .../main/res/drawable-xxhdpi/btn_object.png | Bin 0 -> 4810 bytes .../app/src/main/res/drawable-xxhdpi/logo.png | Bin 0 -> 76723 bytes .../src/main/res/drawable-xxhdpi/logo2.png | Bin 0 -> 9405 bytes .../app/src/main/res/drawable/btn_code.png | Bin 0 -> 1833 bytes .../app/src/main/res/drawable/btn_help.png | Bin 0 -> 1100 bytes .../app/src/main/res/drawable/btn_image.png | Bin 0 -> 1740 bytes .../app/src/main/res/drawable/btn_object.png | Bin 0 -> 2795 bytes .../res/drawable/ic_launcher_background.xml | 170 ++++ .../src/main/res/layout/activity_contract.xml | 57 ++ .../main/res/layout/activity_image_camera.xml | 50 ++ .../main/res/layout/activity_image_main.xml | 72 ++ .../app/src/main/res/layout/activity_main.xml | 19 + .../res/layout/activity_object_camera.xml | 28 + .../layout/activity_object_detection_main.xml | 71 ++ .../main/res/layout/activity_object_photo.xml | 18 + .../src/main/res/layout/activity_splash.xml | 94 ++ .../main/res/layout/layout_hor_text_view.xml | 43 + .../src/main/res/mipmap-hdpi/ic_launcher.png | Bin 0 -> 6175 bytes .../res/mipmap-hdpi/ic_launcher_round.png | Bin 0 -> 7802 bytes .../src/main/res/mipmap-mdpi/ic_launcher.png | Bin 0 -> 3753 bytes .../res/mipmap-mdpi/ic_launcher_round.png | Bin 0 -> 4532 bytes .../src/main/res/mipmap-xhdpi/ic_launcher.png | Bin 0 -> 8939 bytes .../res/mipmap-xhdpi/ic_launcher_round.png | Bin 0 -> 11041 bytes .../main/res/mipmap-xxhdpi/ic_launcher.png | Bin 0 -> 15459 bytes .../res/mipmap-xxhdpi/ic_launcher_round.png | Bin 0 -> 18764 bytes .../main/res/mipmap-xxxhdpi/ic_launcher.png | Bin 0 -> 22722 bytes .../res/mipmap-xxxhdpi/ic_launcher_round.png | Bin 0 -> 27287 bytes .../app/src/main/res/values/colors.xml | 20 + .../app/src/main/res/values/dimens.xml | 9 + .../app/src/main/res/values/strings.xml | 19 + .../app/src/main/res/values/styles.xml | 11 + .../himindspore/ExampleUnitTest.java | 17 + .../official/lite/Himindspore/build.gradle | 25 + .../lite/Himindspore/gradle.properties | 19 + .../gradle/wrapper/gradle-wrapper.jar | Bin 0 -> 54329 bytes .../gradle/wrapper/gradle-wrapper.properties | 6 + model_zoo/official/lite/Himindspore/gradlew | 172 ++++ .../official/lite/Himindspore/gradlew.bat | 84 ++ .../official/lite/Himindspore/settings.gradle | 2 + .../lite/image_classification/README.en.md | 4 +- .../lite/image_classification/README.md | 2 +- .../image_classification/app/CMakeLists.txt | 9 +- .../image_classification/app/download.gradle | 5 +- .../app/src/main/cpp/MindSporeNetnative.cpp | 3 + .../widget/CameraFragment.java | 4 +- .../app/src/main/res/values/strings.xml | 2 +- .../lite/image_classification/settings.gradle | 2 +- .../lite/object_detection/README.en.md | 6 +- .../official/lite/object_detection/README.md | 4 +- .../lite/object_detection/app/CMakeLists.txt | 10 +- .../lite/object_detection/app/download.gradle | 5 +- .../app/src/main/cpp/MindSporeNetnative.cpp | 4 +- .../hiobject/objectdetect/CameraFragment.java | 4 +- .../app/src/main/res/values/strings.xml | 2 +- .../lite/object_detection/settings.gradle | 2 +- 99 files changed, 6537 insertions(+), 25 deletions(-) create mode 100644 model_zoo/official/lite/Himindspore/.gitignore create mode 100644 model_zoo/official/lite/Himindspore/app/.gitignore create mode 100644 model_zoo/official/lite/Himindspore/app/CMakeLists.txt create mode 100644 model_zoo/official/lite/Himindspore/app/build.gradle create mode 100644 model_zoo/official/lite/Himindspore/app/download.gradle create mode 100644 model_zoo/official/lite/Himindspore/app/proguard-rules.pro create mode 100644 model_zoo/official/lite/Himindspore/app/src/androidTest/java/com/mindspore/himindspore/ExampleInstrumentedTest.java create mode 100644 model_zoo/official/lite/Himindspore/app/src/main/AndroidManifest.xml create mode 100644 model_zoo/official/lite/Himindspore/app/src/main/cpp/GarbageMindSporeNetnative.cpp create mode 100644 model_zoo/official/lite/Himindspore/app/src/main/cpp/GarbageMindSporeNetnative.h create mode 100644 model_zoo/official/lite/Himindspore/app/src/main/cpp/ImageMindSporeNetnative.cpp create mode 100644 model_zoo/official/lite/Himindspore/app/src/main/cpp/ImageMindSporeNetnative.h create mode 100644 model_zoo/official/lite/Himindspore/app/src/main/cpp/MSNetWork.cpp create mode 100644 model_zoo/official/lite/Himindspore/app/src/main/cpp/MSNetWork.h create mode 100644 model_zoo/official/lite/Himindspore/app/src/main/cpp/ObjectMindSporeNetnative.cpp create mode 100644 model_zoo/official/lite/Himindspore/app/src/main/cpp/ssd_util/ssd_util.cpp create mode 100644 model_zoo/official/lite/Himindspore/app/src/main/cpp/ssd_util/ssd_util.h create mode 100644 model_zoo/official/lite/Himindspore/app/src/main/java/com/mindspore/himindspore/SplashActivity.java create mode 100644 model_zoo/official/lite/Himindspore/app/src/main/java/com/mindspore/himindspore/camera/CameraPreview.java create mode 100644 model_zoo/official/lite/Himindspore/app/src/main/java/com/mindspore/himindspore/contract/ContractActivity.java create mode 100644 model_zoo/official/lite/Himindspore/app/src/main/java/com/mindspore/himindspore/contract/email/MailInfo.java create mode 100644 model_zoo/official/lite/Himindspore/app/src/main/java/com/mindspore/himindspore/contract/email/MailSender.java create mode 100644 model_zoo/official/lite/Himindspore/app/src/main/java/com/mindspore/himindspore/contract/email/MyAuthenticator.java create mode 100644 model_zoo/official/lite/Himindspore/app/src/main/java/com/mindspore/himindspore/contract/email/SendMailUtil.java create mode 100644 model_zoo/official/lite/Himindspore/app/src/main/java/com/mindspore/himindspore/imageclassification/bean/RecognitionImageBean.java create mode 100644 model_zoo/official/lite/Himindspore/app/src/main/java/com/mindspore/himindspore/imageclassification/help/GarbageTrackingMobile.java create mode 100644 model_zoo/official/lite/Himindspore/app/src/main/java/com/mindspore/himindspore/imageclassification/help/ImageTrackingMobile.java create mode 100644 model_zoo/official/lite/Himindspore/app/src/main/java/com/mindspore/himindspore/imageclassification/ui/HorTextView.java create mode 100644 model_zoo/official/lite/Himindspore/app/src/main/java/com/mindspore/himindspore/imageclassification/ui/ImageCameraActivity.java create mode 100644 model_zoo/official/lite/Himindspore/app/src/main/java/com/mindspore/himindspore/imageclassification/ui/ImageMainActivity.java create mode 100644 model_zoo/official/lite/Himindspore/app/src/main/java/com/mindspore/himindspore/objectdetection/bean/RecognitionObjectBean.java create mode 100644 model_zoo/official/lite/Himindspore/app/src/main/java/com/mindspore/himindspore/objectdetection/help/ImageDegreeHelper.java create mode 100644 model_zoo/official/lite/Himindspore/app/src/main/java/com/mindspore/himindspore/objectdetection/help/ObjectTrackingMobile.java create mode 100644 model_zoo/official/lite/Himindspore/app/src/main/java/com/mindspore/himindspore/objectdetection/ui/ObjectCameraActivity.java create mode 100644 model_zoo/official/lite/Himindspore/app/src/main/java/com/mindspore/himindspore/objectdetection/ui/ObjectDetectionMainActivity.java create mode 100644 model_zoo/official/lite/Himindspore/app/src/main/java/com/mindspore/himindspore/objectdetection/ui/ObjectRectView.java create mode 100644 model_zoo/official/lite/Himindspore/app/src/main/java/com/mindspore/himindspore/objectdetection/ui/PhotoActivity.java create mode 100644 model_zoo/official/lite/Himindspore/app/src/main/java/com/mindspore/himindspore/track/TrackListener.java create mode 100644 model_zoo/official/lite/Himindspore/app/src/main/java/com/mindspore/himindspore/utils/DisplayUtil.java create mode 100644 model_zoo/official/lite/Himindspore/app/src/main/res/drawable-v24/ic_launcher_foreground.xml create mode 100644 model_zoo/official/lite/Himindspore/app/src/main/res/drawable-xxhdpi/btn_code.png create mode 100644 model_zoo/official/lite/Himindspore/app/src/main/res/drawable-xxhdpi/btn_help.png create mode 100644 model_zoo/official/lite/Himindspore/app/src/main/res/drawable-xxhdpi/btn_image.png create mode 100644 model_zoo/official/lite/Himindspore/app/src/main/res/drawable-xxhdpi/btn_object.png create mode 100644 model_zoo/official/lite/Himindspore/app/src/main/res/drawable-xxhdpi/logo.png create mode 100644 model_zoo/official/lite/Himindspore/app/src/main/res/drawable-xxhdpi/logo2.png create mode 100644 model_zoo/official/lite/Himindspore/app/src/main/res/drawable/btn_code.png create mode 100644 model_zoo/official/lite/Himindspore/app/src/main/res/drawable/btn_help.png create mode 100644 model_zoo/official/lite/Himindspore/app/src/main/res/drawable/btn_image.png create mode 100644 model_zoo/official/lite/Himindspore/app/src/main/res/drawable/btn_object.png create mode 100644 model_zoo/official/lite/Himindspore/app/src/main/res/drawable/ic_launcher_background.xml create mode 100644 model_zoo/official/lite/Himindspore/app/src/main/res/layout/activity_contract.xml create mode 100644 model_zoo/official/lite/Himindspore/app/src/main/res/layout/activity_image_camera.xml create mode 100644 model_zoo/official/lite/Himindspore/app/src/main/res/layout/activity_image_main.xml create mode 100644 model_zoo/official/lite/Himindspore/app/src/main/res/layout/activity_main.xml create mode 100644 model_zoo/official/lite/Himindspore/app/src/main/res/layout/activity_object_camera.xml create mode 100644 model_zoo/official/lite/Himindspore/app/src/main/res/layout/activity_object_detection_main.xml create mode 100644 model_zoo/official/lite/Himindspore/app/src/main/res/layout/activity_object_photo.xml create mode 100644 model_zoo/official/lite/Himindspore/app/src/main/res/layout/activity_splash.xml create mode 100644 model_zoo/official/lite/Himindspore/app/src/main/res/layout/layout_hor_text_view.xml create mode 100644 model_zoo/official/lite/Himindspore/app/src/main/res/mipmap-hdpi/ic_launcher.png create mode 100644 model_zoo/official/lite/Himindspore/app/src/main/res/mipmap-hdpi/ic_launcher_round.png create mode 100644 model_zoo/official/lite/Himindspore/app/src/main/res/mipmap-mdpi/ic_launcher.png create mode 100644 model_zoo/official/lite/Himindspore/app/src/main/res/mipmap-mdpi/ic_launcher_round.png create mode 100644 model_zoo/official/lite/Himindspore/app/src/main/res/mipmap-xhdpi/ic_launcher.png create mode 100644 model_zoo/official/lite/Himindspore/app/src/main/res/mipmap-xhdpi/ic_launcher_round.png create mode 100644 model_zoo/official/lite/Himindspore/app/src/main/res/mipmap-xxhdpi/ic_launcher.png create mode 100644 model_zoo/official/lite/Himindspore/app/src/main/res/mipmap-xxhdpi/ic_launcher_round.png create mode 100644 model_zoo/official/lite/Himindspore/app/src/main/res/mipmap-xxxhdpi/ic_launcher.png create mode 100644 model_zoo/official/lite/Himindspore/app/src/main/res/mipmap-xxxhdpi/ic_launcher_round.png create mode 100644 model_zoo/official/lite/Himindspore/app/src/main/res/values/colors.xml create mode 100644 model_zoo/official/lite/Himindspore/app/src/main/res/values/dimens.xml create mode 100644 model_zoo/official/lite/Himindspore/app/src/main/res/values/strings.xml create mode 100644 model_zoo/official/lite/Himindspore/app/src/main/res/values/styles.xml create mode 100644 model_zoo/official/lite/Himindspore/app/src/test/java/com/mindspore/himindspore/ExampleUnitTest.java create mode 100644 model_zoo/official/lite/Himindspore/build.gradle create mode 100644 model_zoo/official/lite/Himindspore/gradle.properties create mode 100644 model_zoo/official/lite/Himindspore/gradle/wrapper/gradle-wrapper.jar create mode 100644 model_zoo/official/lite/Himindspore/gradle/wrapper/gradle-wrapper.properties create mode 100644 model_zoo/official/lite/Himindspore/gradlew create mode 100644 model_zoo/official/lite/Himindspore/gradlew.bat create mode 100644 model_zoo/official/lite/Himindspore/settings.gradle diff --git a/mindspore/lite/test/models_onnx.cfg b/mindspore/lite/test/models_onnx.cfg index b106a50983..b0355d84a5 100644 --- a/mindspore/lite/test/models_onnx.cfg +++ b/mindspore/lite/test/models_onnx.cfg @@ -7,6 +7,12 @@ efficientnet-lite4-11.onnx mobilenetv2-7.onnx shufflenet-v2-10.onnx squeezenet1.1-7.onnx +densenet-9.onnx +googlenet-9.onnx +inception-v1-9.onnx +inception-v2-9.onnx +#shufflenet-9.onnx +squeezenet1.0-9.onnx ml_face_3d.onnx gts_version-RFB-320_simplified.onnx mnist-8.onnx diff --git a/model_zoo/official/lite/Himindspore/.gitignore b/model_zoo/official/lite/Himindspore/.gitignore new file mode 100644 index 0000000000..693777911f --- /dev/null +++ b/model_zoo/official/lite/Himindspore/.gitignore @@ -0,0 +1,83 @@ +# MindSpore +build/ +app/src/main/cpp/mindspore-lite* +app/src/main/assets/model/ +mindspore/lib +output +*.ir +mindspore/ccsrc/schema/inner/* + +# Cmake files +CMakeFiles/ +cmake_install.cmake +CMakeCache.txt +Makefile +cmake-build-debug + +# Dynamic libraries +*.so +*.so.* +*.dylib + +# Static libraries +*.la +*.lai +*.a +*.lib + +# Protocol buffers +*_pb2.py +*.pb.h +*.pb.cc + +# Object files +*.o + +# Editor +.vscode +.idea/ + +# Cquery +.cquery_cached_index/ +compile_commands.json + +# Ctags and cscope +tags +TAGS +CTAGS +GTAGS +GRTAGS +GSYMS +GPATH +cscope.* + +# Python files +*__pycache__* +.pytest_cache + +# Mac files +*.DS_Store + +# Test results +test_temp_summary_event_file/ +*.dot +*.dat +*.svg +*.perf +*.info +*.ckpt +*.shp +*.pkl +.clangd +mindspore/version.py +mindspore/default_config.py +mindspore/.commit_id +onnx.proto +mindspore/ccsrc/onnx.proto + +# Android +local.properties +.gradle +sdk/build +sdk/.cxx +app/.cxx diff --git a/model_zoo/official/lite/Himindspore/app/.gitignore b/model_zoo/official/lite/Himindspore/app/.gitignore new file mode 100644 index 0000000000..b065306da6 --- /dev/null +++ b/model_zoo/official/lite/Himindspore/app/.gitignore @@ -0,0 +1,2 @@ +/build +/src/main/cpp/mindspore-lite-1.0.0-minddata-arm64-cpu/ diff --git a/model_zoo/official/lite/Himindspore/app/CMakeLists.txt b/model_zoo/official/lite/Himindspore/app/CMakeLists.txt new file mode 100644 index 0000000000..7aebd0a5a1 --- /dev/null +++ b/model_zoo/official/lite/Himindspore/app/CMakeLists.txt @@ -0,0 +1,86 @@ +# 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. + +cmake_minimum_required(VERSION 3.4.1) + +set(CMAKE_VERBOSE_MAKEFILE on) +set(CMAKE_LIBRARY_OUTPUT_DIRECTORY ${CMAKE_SOURCE_DIR}/libs/${ANDROID_ABI}) + +set(MINDSPORELITE_VERSION mindspore-lite-1.0.1-runtime-arm64-cpu) + +# ============== Set MindSpore Dependencies. ============= +include_directories(${CMAKE_SOURCE_DIR}/src/main/cpp) +include_directories(${CMAKE_SOURCE_DIR}/src/main/cpp/${MINDSPORELITE_VERSION}/third_party/flatbuffers/include) +include_directories(${CMAKE_SOURCE_DIR}/src/main/cpp/${MINDSPORELITE_VERSION}) +include_directories(${CMAKE_SOURCE_DIR}/src/main/cpp/${MINDSPORELITE_VERSION}/include) +include_directories(${CMAKE_SOURCE_DIR}/src/main/cpp/${MINDSPORELITE_VERSION}/include/ir/dtype) +include_directories(${CMAKE_SOURCE_DIR}/src/main/cpp/${MINDSPORELITE_VERSION}/include/schema) +include_directories(${CMAKE_SOURCE_DIR}/src/main/cpp/${MINDSPORELITE_VERSION}/minddata/include) + +add_library(mindspore-lite SHARED IMPORTED ) +add_library(minddata-lite SHARED IMPORTED ) +add_library(libmindspore-lite-fp16 SHARED IMPORTED ) + +set_target_properties(mindspore-lite PROPERTIES IMPORTED_LOCATION + ${CMAKE_SOURCE_DIR}/src/main/cpp/${MINDSPORELITE_VERSION}/lib/libmindspore-lite.so) +set_target_properties(minddata-lite PROPERTIES IMPORTED_LOCATION + ${CMAKE_SOURCE_DIR}/src/main/cpp/${MINDSPORELITE_VERSION}/minddata/lib/libminddata-lite.so) +set_target_properties(libmindspore-lite-fp16 PROPERTIES IMPORTED_LOCATION + ${CMAKE_SOURCE_DIR}/src/main/cpp/${MINDSPORELITE_VERSION}/lib/libmindspore-lite-fp16.so) +# --------------- MindSpore Lite set End. -------------------- + + +# 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. + +file(GLOB_RECURSE cpp_src "src/main/cpp/*.cpp" "src/main/cpp/*.h") + +add_library( # Sets the name of the library. + mlkit-label-MS + + # Sets the library as a shared library. + SHARED + + # Provides a relative path to your source file(s). + ${cpp_src}) + + +# Searches for a specified prebuilt library and stores the path as a +# variable. Because CMake includes system libraries in the search path by +# default, you only need to specify the name of the public NDK library +# you want to add. CMake verifies that the library exists before +# completing its build. + +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 ) + + +find_library( jnigraphics-lib jnig·raphics ) + +# 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. +add_definitions(-DMNN_USE_LOGCAT) +target_link_libraries( # Specifies the target library. + mlkit-label-MS + + # --- mindspore --- + minddata-lite + mindspore-lite + libmindspore-lite-fp16 + + # --- other dependencies.--- + -ljnigraphics + android + + # Links the target library to the log library + ${log-lib} + ) \ No newline at end of file diff --git a/model_zoo/official/lite/Himindspore/app/build.gradle b/model_zoo/official/lite/Himindspore/app/build.gradle new file mode 100644 index 0000000000..ffb96845f4 --- /dev/null +++ b/model_zoo/official/lite/Himindspore/app/build.gradle @@ -0,0 +1,72 @@ +apply plugin: 'com.android.application' + +android { + compileSdkVersion 30 + buildToolsVersion "30.0.1" + + defaultConfig { + applicationId "com.mindspore.himindspore" + minSdkVersion 21 + targetSdkVersion 30 + versionCode 1 + versionName "1.0" + + testInstrumentationRunner "androidx.test.runner.AndroidJUnitRunner" + externalNativeBuild { + cmake { + arguments "-DANDROID_STL=c++_shared" + cppFlags "-std=c++17" + } + } + ndk { + abiFilters 'arm64-v8a' + } + } + aaptOptions { + noCompress '.so', 'ms' + } + + buildTypes { + release { + minifyEnabled false + proguardFiles getDefaultProguardFile('proguard-android-optimize.txt'), 'proguard-rules.pro' + } + + customDebugType { + debuggable true + } + } + externalNativeBuild { + cmake { + path file('CMakeLists.txt') + } + } + ndkVersion '21.3.6528147' + + sourceSets{ + main { + jniLibs.srcDirs = ['libs'] + } + } + packagingOptions{ + pickFirst 'lib/arm64-v8a/libmlkit-label-MS.so' + } +} + +// Before gradle build. +// To download some necessary libraries. +apply from:'download.gradle' + + +dependencies { + implementation fileTree(dir: "libs", include: ["*.jar"]) + implementation 'androidx.appcompat:appcompat:1.2.0' + implementation 'androidx.constraintlayout:constraintlayout:2.0.2' + implementation 'androidx.cardview:cardview:1.0.0' + testImplementation 'junit:junit:4.13.1' + androidTestImplementation 'androidx.test.ext:junit:1.1.2' + androidTestImplementation 'androidx.test.espresso:espresso-core:3.3.0' + implementation 'com.sun.mail:android-mail:1.6.5' + implementation 'com.sun.mail:android-activation:1.6.5' + +} diff --git a/model_zoo/official/lite/Himindspore/app/download.gradle b/model_zoo/official/lite/Himindspore/app/download.gradle new file mode 100644 index 0000000000..e320910310 --- /dev/null +++ b/model_zoo/official/lite/Himindspore/app/download.gradle @@ -0,0 +1,115 @@ +/** + * To download necessary library from HuaWei server. + * Including mindspore-lite .so file, minddata-lite .so file and model file. + * The libraries can be downloaded manually. + */ +def targetMindSporeInclude = "src/main/cpp/" +def mindsporeLite_Version = "mindspore-lite-1.0.1-runtime-arm64-cpu" + +def targetModelFile = "src/main/assets/model/mobilenetv2.ms" +def mindSporeLibrary_arm64 = "src/main/cpp/${mindsporeLite_Version}.tar.gz" + +def modelDownloadUrl = "https://download.mindspore.cn/model_zoo/official/lite/mobilenetv2_openimage_lite/mobilenetv2.ms" +//def mindsporeLiteDownloadUrl = "https://ms-release.obs.cn-north-4.myhuaweicloud.com/1.0.0/lite/android_aarch64/${mindsporeLite_Version}.tar.gz" +def mindsporeLiteDownloadUrl = "https://download.mindspore.cn/model_zoo/official/lite/lib/mindspore%20version%201.0.1/${mindsporeLite_Version}.tar.gz" + +def targetObjectModelFile = "src/main/assets/model/ssd.ms" +def targetGarbageModelFile = "src/main/assets/model/garbage_mobilenetv2.ms" + +def modelObjectDownloadUrl = "https://download.mindspore.cn/model_zoo/official/lite/ssd_mobilenetv2_lite/ssd.ms" +def modelGarbageDownloadUrl = "https://download.mindspore.cn/model_zoo/official/lite/garbage_mobilenetv2_lite/garbage_mobilenetv2.ms" + +def cleantargetMindSporeInclude = "src/main/cpp" + +task cleanCmakeCache(type: Delete) { + delete '.cxx/cmake/debug' + delete '.cxx/cmake/release' +} + +task downloadModelFile(type: DownloadUrlTask) { + doFirst { + println "Downloading ${modelDownloadUrl}" + } + sourceUrl = "${modelDownloadUrl}" + target = file("${targetModelFile}") +} + +task downloadObjectModelFile(type: DownloadUrlTask) { + doFirst { + println "Downloading ${modelObjectDownloadUrl}" + } + sourceUrl = "${modelObjectDownloadUrl}" + target = file("${targetObjectModelFile}") +} + +task downloadGarbageModelFile(type: DownloadUrlTask) { + doFirst { + println "Downloading ${modelGarbageDownloadUrl}" + } + sourceUrl = "${modelGarbageDownloadUrl}" + target = file("${targetGarbageModelFile}") +} + +task downloadMindSporeLibrary(type: DownloadUrlTask) { + doFirst { + println "Downloading ${mindsporeLiteDownloadUrl}" + } + sourceUrl = "${mindsporeLiteDownloadUrl}" + target = file("${mindSporeLibrary_arm64}") +} + +task unzipMindSporeInclude(type: Copy, dependsOn: 'downloadMindSporeLibrary') { + doFirst { + println "Unzipping ${mindSporeLibrary_arm64}" + } + from tarTree(resources.gzip("${mindSporeLibrary_arm64}")) + into "${targetMindSporeInclude}" +} + +task cleanUnusedmindsporeFiles(type: Delete, dependsOn: ['unzipMindSporeInclude']) { + delete fileTree("${cleantargetMindSporeInclude}").matching { + include "*.tar.gz" + } +} +/* + * Using preBuild to download mindspore library and model file. + * Run before gradle build. + */ +if (file("src/main/cpp/${mindsporeLite_Version}/lib/libmindspore-lite.so").exists()){ + downloadMindSporeLibrary.enabled = false + unzipMindSporeInclude.enabled = false + cleanUnusedmindsporeFiles.enabled = false +} + +if (file("src/main/assets/model/garbage_mobilenetv2.ms").exists()){ + downloadGarbageModelFile.enabled = false +} + +if (file("src/main/assets/model/mobilenetv2.ms").exists()){ + downloadModelFile.enabled = false +} + +if (file("src/main/assets/model/ssd.ms").exists()){ + downloadObjectModelFile.enabled = false +} + +preBuild.dependsOn cleanCmakeCache +preBuild.dependsOn downloadModelFile +preBuild.dependsOn downloadObjectModelFile +preBuild.dependsOn downloadMindSporeLibrary +preBuild.dependsOn downloadGarbageModelFile +preBuild.dependsOn unzipMindSporeInclude +preBuild.dependsOn cleanUnusedmindsporeFiles + +class DownloadUrlTask extends DefaultTask { + @Input + String sourceUrl + + @OutputFile + File target + + @TaskAction + void download() { + ant.get(src: sourceUrl, dest: target) + } +} diff --git a/model_zoo/official/lite/Himindspore/app/proguard-rules.pro b/model_zoo/official/lite/Himindspore/app/proguard-rules.pro new file mode 100644 index 0000000000..481bb43481 --- /dev/null +++ b/model_zoo/official/lite/Himindspore/app/proguard-rules.pro @@ -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 \ No newline at end of file diff --git a/model_zoo/official/lite/Himindspore/app/src/androidTest/java/com/mindspore/himindspore/ExampleInstrumentedTest.java b/model_zoo/official/lite/Himindspore/app/src/androidTest/java/com/mindspore/himindspore/ExampleInstrumentedTest.java new file mode 100644 index 0000000000..41ef62a812 --- /dev/null +++ b/model_zoo/official/lite/Himindspore/app/src/androidTest/java/com/mindspore/himindspore/ExampleInstrumentedTest.java @@ -0,0 +1,26 @@ +package com.mindspore.himindspore; + +import android.content.Context; + +import androidx.test.platform.app.InstrumentationRegistry; +import androidx.test.ext.junit.runners.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 Testing documentation + */ +@RunWith(AndroidJUnit4.class) +public class ExampleInstrumentedTest { + @Test + public void useAppContext() { + // Context of the app under test. + Context appContext = InstrumentationRegistry.getInstrumentation().getTargetContext(); + assertEquals("com.mindspore.himindspore", appContext.getPackageName()); + } +} \ No newline at end of file diff --git a/model_zoo/official/lite/Himindspore/app/src/main/AndroidManifest.xml b/model_zoo/official/lite/Himindspore/app/src/main/AndroidManifest.xml new file mode 100644 index 0000000000..d8be253874 --- /dev/null +++ b/model_zoo/official/lite/Himindspore/app/src/main/AndroidManifest.xml @@ -0,0 +1,58 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + \ No newline at end of file diff --git a/model_zoo/official/lite/Himindspore/app/src/main/cpp/GarbageMindSporeNetnative.cpp b/model_zoo/official/lite/Himindspore/app/src/main/cpp/GarbageMindSporeNetnative.cpp new file mode 100644 index 0000000000..1e6ae1e761 --- /dev/null +++ b/model_zoo/official/lite/Himindspore/app/src/main/cpp/GarbageMindSporeNetnative.cpp @@ -0,0 +1,338 @@ +/** + * Copyright 2020 Huawei Technologies Co., Ltd + * + * 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 +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include "include/errorcode.h" +#include "include/ms_tensor.h" +#include "GarbageMindSporeNetnative.h" +#include "MSNetWork.h" +#include "lite_cv/lite_mat.h" +#include "lite_cv/image_process.h" + +using mindspore::dataset::LiteMat; +using mindspore::dataset::LPixelType; +using mindspore::dataset::LDataType; +#define MS_PRINT(format, ...) __android_log_print(ANDROID_LOG_INFO, "MSJNI", format, ##__VA_ARGS__) + + +static const int RET_GARBAGE_SORT_SUM = 4; +static const char *labels_name_grbage_sort_map[RET_GARBAGE_SORT_SUM] = {{"可回收物"}, + {"干垃圾"}, + {"有害垃圾"}, + {"湿垃圾"}}; + +static const int RET_GARBAGE_DETAILED_SUM = 26; +static const char *labels_name_grbage_detailed_map[RET_GARBAGE_DETAILED_SUM] = { + {"塑料瓶"}, + {"帽子"}, + {"报纸"}, + {"易拉罐"}, + {"玻璃制品"}, + {"玻璃瓶"}, + {"硬纸板"}, + {"篮球"}, + {"纸张"}, + {"金属制品"}, + {"一次性筷子"}, + {"打火机"}, + {"扫把"}, + {"旧镜子"}, + {"牙刷"}, + {"脏污衣服"}, + {"贝壳"}, + {"陶瓷碗"}, + {"油漆桶"}, + {"电池"}, + {"荧光灯"}, + {"药片胶囊"}, + {"橙皮"}, + {"菜叶"}, + {"蛋壳"}, + {"香蕉皮"}}; + + +char *CreateLocalModelBuffer(JNIEnv *env, jobject modelBuffer) { + jbyte *modelAddr = static_cast(env->GetDirectBufferAddress(modelBuffer)); + int modelLen = static_cast(env->GetDirectBufferCapacity(modelBuffer)); + char *buffer(new char[modelLen]); + memcpy(buffer, modelAddr, modelLen); + return buffer; +} + +/** + * To process the result of mindspore inference. + * @param msOutputs + * @return + */ +std::string GarbageProcessRunnetResult(const int RET_CATEGORY_SUM, const char *const labels_name_map[], + std::unordered_map msOutputs) { + // Get the branch of the model output. + // Use iterators to get map elements. + std::unordered_map::iterator iter; + iter = msOutputs.begin(); + + // The mobilenetv2.ms model output just one branch. + auto outputTensor = iter->second; + + // Get a pointer to the first score. + float *temp_scores = static_cast(outputTensor->MutableData()); + float max = 0.0; + unsigned int maxIndex = 0; + for (unsigned int i = 0; i < RET_CATEGORY_SUM; ++i) { + if (temp_scores[i] > max) { + max = temp_scores[i]; + maxIndex = i; + } + } + + // Score for each category. + // Converted to text information that needs to be displayed in the APP. + std::string categoryScore = ""; + if (maxIndex <= 9) { + categoryScore += labels_name_grbage_sort_map[0]; + categoryScore += ":"; + } else if (maxIndex > 9 && maxIndex <= 17) { + categoryScore += labels_name_grbage_sort_map[1]; + categoryScore += ":"; + } else if (maxIndex > 17 && maxIndex <= 21) { + categoryScore += labels_name_grbage_sort_map[2]; + categoryScore += ":"; + } else if (maxIndex > 21 && maxIndex <= 25) { + categoryScore += labels_name_grbage_sort_map[3]; + categoryScore += ":"; + } + categoryScore += labels_name_map[maxIndex]; + return categoryScore; +} + +bool BitmapToLiteMat(JNIEnv *env, const jobject &srcBitmap, LiteMat *lite_mat) { + bool ret = false; + AndroidBitmapInfo info; + void *pixels = nullptr; + LiteMat &lite_mat_bgr = *lite_mat; + AndroidBitmap_getInfo(env, srcBitmap, &info); + if (info.format != ANDROID_BITMAP_FORMAT_RGBA_8888) { + MS_PRINT("Image Err, Request RGBA"); + return false; + } + AndroidBitmap_lockPixels(env, srcBitmap, &pixels); + if (info.stride == info.width * 4) { + ret = InitFromPixel(reinterpret_cast(pixels), + LPixelType::RGBA2RGB, LDataType::UINT8, + info.width, info.height, lite_mat_bgr); + if (!ret) { + MS_PRINT("Init From RGBA error"); + } + } else { + unsigned char *pixels_ptr = new unsigned char[info.width * info.height * 4]; + unsigned char *ptr = pixels_ptr; + unsigned char *data = reinterpret_cast(pixels); + for (int i = 0; i < info.height; i++) { + memcpy(ptr, data, info.width * 4); + ptr += info.width * 4; + data += info.stride; + } + ret = InitFromPixel(reinterpret_cast(pixels_ptr), + LPixelType::RGBA2RGB, LDataType::UINT8, + info.width, info.height, lite_mat_bgr); + if (!ret) { + MS_PRINT("Init From RGBA error"); + } + delete[] (pixels_ptr); + } + AndroidBitmap_unlockPixels(env, srcBitmap); + return ret; +} + +bool PreProcessImageData(const LiteMat &lite_mat_bgr, LiteMat *lite_norm_mat_ptr) { + bool ret = false; + LiteMat lite_mat_resize; + LiteMat &lite_norm_mat_cut = *lite_norm_mat_ptr; + ret = ResizeBilinear(lite_mat_bgr, lite_mat_resize, 256, 256); + if (!ret) { + MS_PRINT("ResizeBilinear error"); + return false; + } + LiteMat lite_mat_convert_float; + ret = ConvertTo(lite_mat_resize, lite_mat_convert_float, 1.0 / 255.0); + if (!ret) { + MS_PRINT("ConvertTo error"); + return false; + } + LiteMat lite_mat_cut; + ret = Crop(lite_mat_convert_float, lite_mat_cut, 16, 16, 224, 224); + if (!ret) { + MS_PRINT("Crop error"); + return false; + } + std::vector means = {0.485, 0.456, 0.406}; + std::vector stds = {0.229, 0.224, 0.225}; + SubStractMeanNormalize(lite_mat_cut, lite_norm_mat_cut, means, stds); + return true; +} + + +/** + * The Java layer reads the model into MappedByteBuffer or ByteBuffer to load the model. + */ +extern "C" +JNIEXPORT jlong JNICALL +Java_com_mindspore_himindspore_imageclassification_help_GarbageTrackingMobile_loadModel(JNIEnv *env, + jobject thiz, + jobject model_buffer, + jint num_thread) { + if (nullptr == model_buffer) { + MS_PRINT("error, buffer is nullptr!"); + return (jlong) nullptr; + } + jlong bufferLen = env->GetDirectBufferCapacity(model_buffer); + if (0 == bufferLen) { + MS_PRINT("error, bufferLen is 0!"); + return (jlong) nullptr; + } + + char *modelBuffer = CreateLocalModelBuffer(env, model_buffer); + if (modelBuffer == nullptr) { + MS_PRINT("modelBuffer create failed!"); + return (jlong) nullptr; + } + + // To create a mindspore network inference environment. + void **labelEnv = new void *; + MSNetWork *labelNet = new MSNetWork; + *labelEnv = labelNet; + + mindspore::lite::Context *context = new mindspore::lite::Context; + context->thread_num_ = num_thread; + + labelNet->CreateSessionMS(modelBuffer, bufferLen, context); + delete context; + + if (labelNet->session() == nullptr) { + MS_PRINT("MindSpore create session failed!."); + delete labelNet; + delete labelEnv; + return (jlong) nullptr; + } + + if (model_buffer != nullptr) { + env->DeleteLocalRef(model_buffer); + } + + return (jlong) labelEnv; +} + +/** + * After the inference environment is successfully created, + * sending a picture to the model and run inference. + */ +extern "C" JNIEXPORT jstring JNICALL +Java_com_mindspore_himindspore_imageclassification_help_GarbageTrackingMobile_runNet(JNIEnv *env, jclass type, + jlong netEnv, + jobject srcBitmap) { + LiteMat lite_mat_bgr, lite_norm_mat_cut; + + if (!BitmapToLiteMat(env, srcBitmap, &lite_mat_bgr)) { + MS_PRINT("BitmapToLiteMat error"); + return NULL; + } + if (!PreProcessImageData(lite_mat_bgr, &lite_norm_mat_cut)) { + MS_PRINT("PreProcessImageData error"); + return NULL; + } + + ImgDims inputDims; + inputDims.channel = lite_norm_mat_cut.channel_; + inputDims.width = lite_norm_mat_cut.width_; + inputDims.height = lite_norm_mat_cut.height_; + + // Get the mindsore inference environment which created in loadModel(). + void **labelEnv = reinterpret_cast(netEnv); + if (labelEnv == nullptr) { + MS_PRINT("MindSpore error, labelEnv is a nullptr."); + return NULL; + } + MSNetWork *labelNet = static_cast(*labelEnv); + + auto mSession = labelNet->session(); + if (mSession == nullptr) { + MS_PRINT("MindSpore error, Session is a nullptr."); + return NULL; + } + MS_PRINT("MindSpore get session."); + + auto msInputs = mSession->GetInputs(); + if (msInputs.size() == 0) { + MS_PRINT("MindSpore error, msInputs.size() equals 0."); + return NULL; + } + auto inTensor = msInputs.front(); + + float *dataHWC = reinterpret_cast(lite_norm_mat_cut.data_ptr_); + // Copy dataHWC to the model input tensor. + memcpy(inTensor->MutableData(), dataHWC, + inputDims.channel * inputDims.width * inputDims.height * sizeof(float)); + + // After the model and image tensor data is loaded, run inference. + auto status = mSession->RunGraph(); + + if (status != mindspore::lite::RET_OK) { + MS_PRINT("MindSpore run net error."); + return NULL; + } + + /** + * Get the mindspore inference results. + * Return the map of output node name and MindSpore Lite MSTensor. + */ + auto names = mSession->GetOutputTensorNames(); + + std::unordered_map msOutputs; + for (const auto &name : names) { + auto temp_dat = mSession->GetOutputByTensorName(name); + msOutputs.insert(std::pair{name, temp_dat}); + } + + std::string resultStr = GarbageProcessRunnetResult(::RET_GARBAGE_DETAILED_SUM, + ::labels_name_grbage_detailed_map, msOutputs); + + const char *resultCharData = resultStr.c_str(); + return (env)->NewStringUTF(resultCharData); +} + +extern "C" JNIEXPORT jboolean JNICALL +Java_com_mindspore_himindspore_imageclassification_help_GarbageTrackingMobile_unloadModel(JNIEnv *env, + jclass type, + jlong netEnv) { + MS_PRINT("MindSpore release net."); + void **labelEnv = reinterpret_cast(netEnv); + if (labelEnv == nullptr) { + MS_PRINT("MindSpore error, labelEnv is a nullptr."); + } + MSNetWork *labelNet = static_cast(*labelEnv); + + labelNet->ReleaseNets(); + + return (jboolean) true; +} diff --git a/model_zoo/official/lite/Himindspore/app/src/main/cpp/GarbageMindSporeNetnative.h b/model_zoo/official/lite/Himindspore/app/src/main/cpp/GarbageMindSporeNetnative.h new file mode 100644 index 0000000000..322b01a201 --- /dev/null +++ b/model_zoo/official/lite/Himindspore/app/src/main/cpp/GarbageMindSporeNetnative.h @@ -0,0 +1,21 @@ +/** + * Copyright 2020 Huawei Technologies Co., Ltd + * + * 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. + */ + +#ifndef GARBAGE_MINDSPORE_JNI_HMS_DEBUG_MINDSPORENETNATIVE_H +#define GARBAGE_MINDSPORE_JNI_HMS_DEBUG_MINDSPORENETNATIVE_H + + +#endif // MINDSPORE_JNI_HMS_DEBUG_MINDSPORENETNATIVE_H diff --git a/model_zoo/official/lite/Himindspore/app/src/main/cpp/ImageMindSporeNetnative.cpp b/model_zoo/official/lite/Himindspore/app/src/main/cpp/ImageMindSporeNetnative.cpp new file mode 100644 index 0000000000..4928ea752d --- /dev/null +++ b/model_zoo/official/lite/Himindspore/app/src/main/cpp/ImageMindSporeNetnative.cpp @@ -0,0 +1,811 @@ +/** + * Copyright 2020 Huawei Technologies Co., Ltd + * + * 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 +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include "include/errorcode.h" +#include "include/ms_tensor.h" +#include "ImageMindSporeNetnative.h" +#include "MSNetWork.h" +#include "lite_cv/lite_mat.h" +#include "lite_cv/image_process.h" + +using mindspore::dataset::LiteMat; +using mindspore::dataset::LPixelType; +using mindspore::dataset::LDataType; +#define MS_PRINT(format, ...) __android_log_print(ANDROID_LOG_INFO, "MSJNI", format, ##__VA_ARGS__) + + +static const int RET_CATEGORY_SUM = 410; +static const char *labels_name_map[RET_CATEGORY_SUM] = { + {"Herd"}, + {"Safari"}, + {"Bangle"}, + {"Cushion"}, + {"Countertop"}, + {"Prom"}, + {"Branch"}, + {"Sports"}, + {"Sky"}, + {"Community"}, + {"Wheel"}, + {"Cola"}, + {"Tuxedo"}, + {"Flowerpot"}, + {"Team"}, + {"Computer"}, + {"Unicycle"}, + {"Brig"}, + {"Aerospace engineering"}, + {"Scuba diving"}, + {"Goggles"}, + {"Fruit"}, + {"Badminton"}, + {"Horse"}, + {"Sunglasses"}, + {"Fun"}, + {"Prairie"}, + {"Poster"}, + {"Flag"}, + {"Speedboat"}, + {"Eyelash"}, + {"Veil"}, + {"Mobile phone"}, + {"Wheelbarrow"}, + {"Saucer"}, + {"Leather"}, + {"Drawer"}, + {"Paper"}, + {"Pier"}, + {"Waterfowl"}, + {"Tights"}, + {"Rickshaw"}, + {"Vegetable"}, + {"Handrail"}, + {"Ice"}, + {"Metal"}, + {"Flower"}, + {"Wing"}, + {"Silverware"}, + {"Event"}, + {"Skyline"}, + {"Money"}, + {"Comics"}, + {"Handbag"}, + {"Porcelain"}, + {"Rodeo"}, + {"Curtain"}, + {"Tile"}, + {"Human mouth"}, + {"Army"}, + {"Menu"}, + {"Boat"}, + {"Snowboarding"}, + {"Cairn terrier"}, + {"Net"}, + {"Pasteles"}, + {"Cup"}, + {"Rugby"}, + {"Pho"}, + {"Cap"}, + {"Human hair"}, + {"Surfing"}, + {"Loveseat"}, + {"Museum"}, + {"Shipwreck"}, + {"Trunk (Tree)"}, + {"Plush"}, + {"Monochrome"}, + {"Volcano"}, + {"Rock"}, + {"Pillow"}, + {"Presentation"}, + {"Nebula"}, + {"Subwoofer"}, + {"Lake"}, + {"Sledding"}, + {"Bangs"}, + {"Tablecloth"}, + {"Necklace"}, + {"Swimwear"}, + {"Standing"}, + {"Jeans"}, + {"Carnival"}, + {"Softball"}, + {"Centrepiece"}, + {"Skateboarder"}, + {"Cake"}, + {"Dragon"}, + {"Aurora"}, + {"Skiing"}, + {"Bathroom"}, + {"Dog"}, + {"Needlework"}, + {"Umbrella"}, + {"Church"}, + {"Fire"}, + {"Piano"}, + {"Denim"}, + {"Bridle"}, + {"Cabinetry"}, + {"Lipstick"}, + {"Ring"}, + {"Television"}, + {"Roller"}, + {"Seal"}, + {"Concert"}, + {"Product"}, + {"News"}, + {"Fast food"}, + {"Horn (Animal)"}, + {"Tattoo"}, + {"Bird"}, + {"Bridegroom"}, + {"Love"}, + {"Helmet"}, + {"Dinosaur"}, + {"Icing"}, + {"Miniature"}, + {"Tire"}, + {"Toy"}, + {"Icicle"}, + {"Jacket"}, + {"Coffee"}, + {"Mosque"}, + {"Rowing"}, + {"Wetsuit"}, + {"Camping"}, + {"Underwater"}, + {"Christmas"}, + {"Gelato"}, + {"Whiteboard"}, + {"Field"}, + {"Ragdoll"}, + {"Construction"}, + {"Lampshade"}, + {"Palace"}, + {"Meal"}, + {"Factory"}, + {"Cage"}, + {"Clipper (Boat)"}, + {"Gymnastics"}, + {"Turtle"}, + {"Human foot"}, + {"Marriage"}, + {"Web page"}, + {"Human beard"}, + {"Fog"}, + {"Wool"}, + {"Cappuccino"}, + {"Lighthouse"}, + {"Lego"}, + {"Sparkler"}, + {"Sari"}, + {"Model"}, + {"Temple"}, + {"Beanie"}, + {"Building"}, + {"Waterfall"}, + {"Penguin"}, + {"Cave"}, + {"Stadium"}, + {"Smile"}, + {"Human hand"}, + {"Park"}, + {"Desk"}, + {"Shetland sheepdog"}, + {"Bar"}, + {"Eating"}, + {"Neon"}, + {"Dalmatian"}, + {"Crocodile"}, + {"Wakeboarding"}, + {"Longboard"}, + {"Road"}, + {"Race"}, + {"Kitchen"}, + {"Odometer"}, + {"Cliff"}, + {"Fiction"}, + {"School"}, + {"Interaction"}, + {"Bullfighting"}, + {"Boxer"}, + {"Gown"}, + {"Aquarium"}, + {"Superhero"}, + {"Pie"}, + {"Asphalt"}, + {"Surfboard"}, + {"Cheeseburger"}, + {"Screenshot"}, + {"Supper"}, + {"Laugh"}, + {"Lunch"}, + {"Party "}, + {"Glacier"}, + {"Bench"}, + {"Grandparent"}, + {"Sink"}, + {"Pomacentridae"}, + {"Blazer"}, + {"Brick"}, + {"Space"}, + {"Backpacking"}, + {"Stuffed toy"}, + {"Sushi"}, + {"Glitter"}, + {"Bonfire"}, + {"Castle"}, + {"Marathon"}, + {"Pizza"}, + {"Beach"}, + {"Human ear"}, + {"Racing"}, + {"Sitting"}, + {"Iceberg"}, + {"Shelf"}, + {"Vehicle"}, + {"Pop music"}, + {"Playground"}, + {"Clown"}, + {"Car"}, + {"Rein"}, + {"Fur"}, + {"Musician"}, + {"Casino"}, + {"Baby"}, + {"Alcohol"}, + {"Strap"}, + {"Reef"}, + {"Balloon"}, + {"Outerwear"}, + {"Cathedral"}, + {"Competition"}, + {"Joker"}, + {"Blackboard"}, + {"Bunk bed"}, + {"Bear"}, + {"Moon"}, + {"Archery"}, + {"Polo"}, + {"River"}, + {"Fishing"}, + {"Ferris wheel"}, + {"Mortarboard"}, + {"Bracelet"}, + {"Flesh"}, + {"Statue"}, + {"Farm"}, + {"Desert"}, + {"Chain"}, + {"Aircraft"}, + {"Textile"}, + {"Hot dog"}, + {"Knitting"}, + {"Singer"}, + {"Juice"}, + {"Circus"}, + {"Chair"}, + {"Musical instrument"}, + {"Room"}, + {"Crochet"}, + {"Sailboat"}, + {"Newspaper"}, + {"Santa claus"}, + {"Swamp"}, + {"Skyscraper"}, + {"Skin"}, + {"Rocket"}, + {"Aviation"}, + {"Airliner"}, + {"Garden"}, + {"Ruins"}, + {"Storm"}, + {"Glasses"}, + {"Balance"}, + {"Nail (Body part)"}, + {"Rainbow"}, + {"Soil "}, + {"Vacation "}, + {"Moustache"}, + {"Doily"}, + {"Food"}, + {"Bride "}, + {"Cattle"}, + {"Pocket"}, + {"Infrastructure"}, + {"Train"}, + {"Gerbil"}, + {"Fireworks"}, + {"Pet"}, + {"Dam"}, + {"Crew"}, + {"Couch"}, + {"Bathing"}, + {"Quilting"}, + {"Motorcycle"}, + {"Butterfly"}, + {"Sled"}, + {"Watercolor paint"}, + {"Rafting"}, + {"Monument"}, + {"Lightning"}, + {"Sunset"}, + {"Bumper"}, + {"Shoe"}, + {"Waterskiing"}, + {"Sneakers"}, + {"Tower"}, + {"Insect"}, + {"Pool"}, + {"Placemat"}, + {"Airplane"}, + {"Plant"}, + {"Jungle"}, + {"Armrest"}, + {"Duck"}, + {"Dress"}, + {"Tableware"}, + {"Petal"}, + {"Bus"}, + {"Hanukkah"}, + {"Forest"}, + {"Hat"}, + {"Barn"}, + {"Tubing"}, + {"Snorkeling"}, + {"Cool"}, + {"Cookware and bakeware"}, + {"Cycling"}, + {"Swing (Seat)"}, + {"Muscle"}, + {"Cat"}, + {"Skateboard"}, + {"Star"}, + {"Toe"}, + {"Junk"}, + {"Bicycle"}, + {"Bedroom"}, + {"Person"}, + {"Sand"}, + {"Canyon"}, + {"Tie"}, + {"Twig"}, + {"Sphynx"}, + {"Supervillain"}, + {"Nightclub"}, + {"Ranch"}, + {"Pattern"}, + {"Shorts"}, + {"Himalayan"}, + {"Wall"}, + {"Leggings"}, + {"Windsurfing"}, + {"Deejay"}, + {"Dance"}, + {"Van"}, + {"Bento"}, + {"Sleep"}, + {"Wine"}, + {"Picnic"}, + {"Leisure"}, + {"Dune"}, + {"Crowd"}, + {"Kayak"}, + {"Ballroom"}, + {"Selfie"}, + {"Graduation"}, + {"Frigate"}, + {"Mountain"}, + {"Dude"}, + {"Windshield"}, + {"Skiff"}, + {"Class"}, + {"Scarf"}, + {"Bull"}, + {"Soccer"}, + {"Bag"}, + {"Basset hound"}, + {"Tractor"}, + {"Swimming"}, + {"Running"}, + {"Track"}, + {"Helicopter"}, + {"Pitch"}, + {"Clock"}, + {"Song"}, + {"Jersey"}, + {"Stairs"}, + {"Flap"}, + {"Jewellery"}, + {"Bridge"}, + {"Cuisine"}, + {"Bread"}, + {"Caving"}, + {"Shell"}, + {"Wreath"}, + {"Roof"}, + {"Cookie"}, + {"Canoe"}}; + +static float g_thres_map[RET_CATEGORY_SUM] = { + 0.23, 0.03, 0.10, 0.13, 0.03, + 0.10, 0.06, 0.09, 0.09, 0.05, + 0.01, 0.04, 0.01, 0.27, 0.05, + 0.16, 0.01, 0.16, 0.04, 0.13, + 0.09, 0.18, 0.10, 0.65, 0.08, + 0.04, 0.08, 0.01, 0.05, 0.20, + 0.01, 0.16, 0.10, 0.10, 0.10, + 0.02, 0.24, 0.08, 0.10, 0.53, + 0.07, 0.05, 0.07, 0.27, 0.02, + 0.01, 0.71, 0.01, 0.06, 0.06, + 0.03, 0.96, 0.03, 0.94, 0.05, + 0.03, 0.14, 0.09, 0.03, 0.11, + 0.50, 0.16, 0.07, 0.07, 0.06, + 0.07, 0.08, 0.10, 0.29, 0.03, + 0.05, 0.11, 0.03, 0.03, 0.03, + 0.01, 0.11, 0.07, 0.03, 0.49, + 0.12, 0.30, 0.10, 0.15, 0.02, + 0.06, 0.17, 0.01, 0.04, 0.07, + 0.06, 0.02, 0.19, 0.20, 0.14, + 0.35, 0.15, 0.01, 0.10, 0.13, + 0.43, 0.11, 0.12, 0.32, 0.01, + 0.22, 0.51, 0.02, 0.04, 0.14, + 0.04, 0.35, 0.35, 0.01, 0.54, + 0.04, 0.02, 0.03, 0.02, 0.38, + 0.13, 0.19, 0.06, 0.01, 0.02, + 0.06, 0.03, 0.04, 0.01, 0.10, + 0.01, 0.07, 0.07, 0.07, 0.33, + 0.08, 0.04, 0.06, 0.07, 0.07, + 0.11, 0.02, 0.32, 0.48, 0.14, + 0.01, 0.01, 0.04, 0.05, 0.04, + 0.16, 0.50, 0.11, 0.03, 0.04, + 0.02, 0.55, 0.17, 0.13, 0.84, + 0.18, 0.03, 0.16, 0.02, 0.06, + 0.03, 0.11, 0.96, 0.36, 0.68, + 0.02, 0.08, 0.02, 0.01, 0.03, + 0.05, 0.14, 0.09, 0.06, 0.03, + 0.20, 0.15, 0.62, 0.03, 0.10, + 0.08, 0.02, 0.02, 0.06, 0.03, + 0.04, 0.01, 0.10, 0.05, 0.04, + 0.02, 0.07, 0.03, 0.32, 0.11, + 0.03, 0.02, 0.03, 0.01, 0.03, + 0.03, 0.25, 0.20, 0.19, 0.03, + 0.11, 0.03, 0.02, 0.03, 0.15, + 0.14, 0.06, 0.11, 0.03, 0.02, + 0.02, 0.52, 0.03, 0.02, 0.02, + 0.02, 0.09, 0.56, 0.01, 0.22, + 0.01, 0.48, 0.14, 0.10, 0.08, + 0.73, 0.39, 0.09, 0.10, 0.85, + 0.31, 0.03, 0.05, 0.01, 0.01, + 0.01, 0.10, 0.28, 0.02, 0.03, + 0.04, 0.03, 0.07, 0.14, 0.20, + 0.10, 0.01, 0.05, 0.37, 0.12, + 0.04, 0.44, 0.04, 0.26, 0.08, + 0.07, 0.27, 0.10, 0.03, 0.01, + 0.03, 0.16, 0.41, 0.16, 0.34, + 0.04, 0.30, 0.04, 0.05, 0.18, + 0.33, 0.03, 0.21, 0.03, 0.04, + 0.22, 0.01, 0.04, 0.02, 0.01, + 0.06, 0.02, 0.08, 0.87, 0.11, + 0.15, 0.05, 0.14, 0.09, 0.08, + 0.22, 0.09, 0.07, 0.06, 0.06, + 0.05, 0.43, 0.70, 0.03, 0.07, + 0.06, 0.07, 0.14, 0.04, 0.01, + 0.03, 0.05, 0.65, 0.06, 0.04, + 0.23, 0.06, 0.75, 0.10, 0.01, + 0.63, 0.41, 0.09, 0.01, 0.01, + 0.18, 0.10, 0.03, 0.01, 0.05, + 0.13, 0.18, 0.03, 0.23, 0.01, + 0.04, 0.03, 0.38, 0.90, 0.21, + 0.18, 0.10, 0.48, 0.08, 0.46, + 0.03, 0.01, 0.02, 0.03, 0.10, + 0.01, 0.09, 0.01, 0.01, 0.01, + 0.10, 0.41, 0.01, 0.06, 0.75, + 0.08, 0.01, 0.01, 0.08, 0.21, + 0.06, 0.02, 0.05, 0.02, 0.05, + 0.09, 0.12, 0.03, 0.06, 0.11, + 0.03, 0.01, 0.01, 0.06, 0.84, + 0.04, 0.81, 0.39, 0.02, 0.29, + 0.77, 0.07, 0.06, 0.22, 0.23, + 0.23, 0.01, 0.02, 0.13, 0.04, + 0.19, 0.04, 0.08, 0.27, 0.09, + 0.06, 0.01, 0.03, 0.21, 0.04, +}; + +char *ImageCreateLocalModelBuffer(JNIEnv *env, jobject modelBuffer) { + jbyte *modelAddr = static_cast(env->GetDirectBufferAddress(modelBuffer)); + int modelLen = static_cast(env->GetDirectBufferCapacity(modelBuffer)); + char *buffer(new char[modelLen]); + memcpy(buffer, modelAddr, modelLen); + return buffer; +} + +/** + * To process the result of mindspore inference. + * @param msOutputs + * @return + */ +std::string ProcessRunnetResult(const int RET_CATEGORY_SUM, const char *const labels_name_map[], + std::unordered_map msOutputs) { + // Get the branch of the model output. + // Use iterators to get map elements. + std::unordered_map::iterator iter; + iter = msOutputs.begin(); + + // The mobilenetv2.ms model output just one branch. + auto outputTensor = iter->second; + + int tensorNum = outputTensor->ElementsNum(); + MS_PRINT("Number of tensor elements:%d", tensorNum); + + // Get a pointer to the first score. + float *temp_scores = static_cast(outputTensor->MutableData()); + float scores[RET_CATEGORY_SUM]; + for (int i = 0; i < RET_CATEGORY_SUM; ++i) { + scores[i] = temp_scores[i]; + } + + float unifiedThre = 0.5; + float probMax = 1.0; + for (size_t i = 0; i < RET_CATEGORY_SUM; ++i) { + float threshold = g_thres_map[i]; + float tmpProb = scores[i]; + if (tmpProb < threshold) { + tmpProb = tmpProb / threshold * unifiedThre; + } else { + tmpProb = (tmpProb - threshold) / (probMax - threshold) * unifiedThre + unifiedThre; + } + scores[i] = tmpProb; + } + + for (int i = 0; i < RET_CATEGORY_SUM; ++i) { + if (scores[i] > 0.5) { + MS_PRINT("MindSpore scores[%d] : [%f]", i, scores[i]); + } + } + + // Score for each category. + // Converted to text information that needs to be displayed in the APP. + std::string categoryScore = ""; + for (int i = 0; i < RET_CATEGORY_SUM; ++i) { + categoryScore += labels_name_map[i]; + categoryScore += ":"; + std::string score_str = std::to_string(scores[i]); + categoryScore += score_str; + categoryScore += ";"; + } + return categoryScore; +} + +bool ImageBitmapToLiteMat(JNIEnv *env, const jobject &srcBitmap, LiteMat *lite_mat) { + bool ret = false; + AndroidBitmapInfo info; + void *pixels = nullptr; + LiteMat &lite_mat_bgr = *lite_mat; + AndroidBitmap_getInfo(env, srcBitmap, &info); + if (info.format != ANDROID_BITMAP_FORMAT_RGBA_8888) { + MS_PRINT("Image Err, Request RGBA"); + return false; + } + AndroidBitmap_lockPixels(env, srcBitmap, &pixels); + if (info.stride == info.width * 4) { + ret = InitFromPixel(reinterpret_cast(pixels), + LPixelType::RGBA2RGB, LDataType::UINT8, + info.width, info.height, lite_mat_bgr); + if (!ret) { + MS_PRINT("Init From RGBA error"); + } + } else { + unsigned char *pixels_ptr = new unsigned char[info.width * info.height * 4]; + unsigned char *ptr = pixels_ptr; + unsigned char *data = reinterpret_cast(pixels); + for (int i = 0; i < info.height; i++) { + memcpy(ptr, data, info.width * 4); + ptr += info.width * 4; + data += info.stride; + } + ret = InitFromPixel(reinterpret_cast(pixels_ptr), + LPixelType::RGBA2RGB, LDataType::UINT8, + info.width, info.height, lite_mat_bgr); + if (!ret) { + MS_PRINT("Init From RGBA error"); + } + delete[] (pixels_ptr); + } + AndroidBitmap_unlockPixels(env, srcBitmap); + return ret; +} + +bool ImagePreProcessImageData(const LiteMat &lite_mat_bgr, LiteMat *lite_norm_mat_ptr) { + bool ret = false; + LiteMat lite_mat_resize; + LiteMat &lite_norm_mat_cut = *lite_norm_mat_ptr; + ret = ResizeBilinear(lite_mat_bgr, lite_mat_resize, 256, 256); + if (!ret) { + MS_PRINT("ResizeBilinear error"); + return false; + } + LiteMat lite_mat_convert_float; + ret = ConvertTo(lite_mat_resize, lite_mat_convert_float, 1.0 / 255.0); + if (!ret) { + MS_PRINT("ConvertTo error"); + return false; + } + LiteMat lite_mat_cut; + ret = Crop(lite_mat_convert_float, lite_mat_cut, 16, 16, 224, 224); + if (!ret) { + MS_PRINT("Crop error"); + return false; + } + std::vector means = {0.485, 0.456, 0.406}; + std::vector stds = {0.229, 0.224, 0.225}; + SubStractMeanNormalize(lite_mat_cut, lite_norm_mat_cut, means, stds); + return true; +} + + +/** + * The Java layer reads the model into MappedByteBuffer or ByteBuffer to load the model. + */ +extern "C" +JNIEXPORT jlong JNICALL +Java_com_mindspore_himindspore_imageclassification_help_ImageTrackingMobile_loadModel(JNIEnv *env, + jobject thiz, + jobject model_buffer, + jint num_thread) { + if (nullptr == model_buffer) { + MS_PRINT("error, buffer is nullptr!"); + return (jlong) nullptr; + } + jlong bufferLen = env->GetDirectBufferCapacity(model_buffer); + if (0 == bufferLen) { + MS_PRINT("error, bufferLen is 0!"); + return (jlong) nullptr; + } + + char *modelBuffer = ImageCreateLocalModelBuffer(env, model_buffer); + if (modelBuffer == nullptr) { + MS_PRINT("modelBuffer create failed!"); + return (jlong) nullptr; + } + + // To create a mindspore network inference environment. + void **labelEnv = new void *; + MSNetWork *labelNet = new MSNetWork; + *labelEnv = labelNet; + + mindspore::lite::Context *context = new mindspore::lite::Context; + context->thread_num_ = num_thread; + + labelNet->CreateSessionMS(modelBuffer, bufferLen, context); + delete context; + + if (labelNet->session() == nullptr) { + MS_PRINT("MindSpore create session failed!."); + delete labelNet; + delete labelEnv; + return (jlong) nullptr; + } + + if (model_buffer != nullptr) { + env->DeleteLocalRef(model_buffer); + } + + return (jlong) labelEnv; +} + +/** + * After the inference environment is successfully created, + * sending a picture to the model and run inference. + */ +extern "C" JNIEXPORT jstring JNICALL +Java_com_mindspore_himindspore_imageclassification_help_ImageTrackingMobile_runNet(JNIEnv *env, + jclass type, + jlong netEnv, + jobject srcBitmap) { + LiteMat lite_mat_bgr, lite_norm_mat_cut; + + if (!ImageBitmapToLiteMat(env, srcBitmap, &lite_mat_bgr)) { + MS_PRINT("ImageBitmapToLiteMat error"); + return NULL; + } + if (!ImagePreProcessImageData(lite_mat_bgr, &lite_norm_mat_cut)) { + MS_PRINT("ImagePreProcessImageData error"); + return NULL; + } + + ImgDims inputDims; + inputDims.channel = lite_norm_mat_cut.channel_; + inputDims.width = lite_norm_mat_cut.width_; + inputDims.height = lite_norm_mat_cut.height_; + + // Get the mindsore inference environment which created in loadModel(). + void **labelEnv = reinterpret_cast(netEnv); + if (labelEnv == nullptr) { + MS_PRINT("MindSpore error, labelEnv is a nullptr."); + return NULL; + } + MSNetWork *labelNet = static_cast(*labelEnv); + + auto mSession = labelNet->session(); + if (mSession == nullptr) { + MS_PRINT("MindSpore error, Session is a nullptr."); + return NULL; + } + MS_PRINT("MindSpore get session."); + + auto msInputs = mSession->GetInputs(); + if (msInputs.size() == 0) { + MS_PRINT("MindSpore error, msInputs.size() equals 0."); + return NULL; + } + auto inTensor = msInputs.front(); + + float *dataHWC = reinterpret_cast(lite_norm_mat_cut.data_ptr_); + // Copy dataHWC to the model input tensor. + memcpy(inTensor->MutableData(), dataHWC, + inputDims.channel * inputDims.width * inputDims.height * sizeof(float)); + + // After the model and image tensor data is loaded, run inference. + auto status = mSession->RunGraph(); + + if (status != mindspore::lite::RET_OK) { + MS_PRINT("MindSpore run net error."); + return NULL; + } + + /** + * Get the mindspore inference results. + * Return the map of output node name and MindSpore Lite MSTensor. + */ + auto names = mSession->GetOutputTensorNames(); + std::unordered_map msOutputs; + for (const auto &name : names) { + auto temp_dat = mSession->GetOutputByTensorName(name); + msOutputs.insert(std::pair{name, temp_dat}); + } + + std::string resultStr = ProcessRunnetResult(::RET_CATEGORY_SUM, + ::labels_name_map, msOutputs); + + const char *resultCharData = resultStr.c_str(); + return (env)->NewStringUTF(resultCharData); +} + +extern "C" JNIEXPORT jboolean JNICALL +Java_com_mindspore_himindspore_imageclassification_help_ImageTrackingMobile_unloadModel(JNIEnv *env, + jclass type, + jlong netEnv) { + MS_PRINT("MindSpore release net."); + void **labelEnv = reinterpret_cast(netEnv); + if (labelEnv == nullptr) { + MS_PRINT("MindSpore error, labelEnv is a nullptr."); + } + MSNetWork *labelNet = static_cast(*labelEnv); + + labelNet->ReleaseNets(); + + return (jboolean) true; +} diff --git a/model_zoo/official/lite/Himindspore/app/src/main/cpp/ImageMindSporeNetnative.h b/model_zoo/official/lite/Himindspore/app/src/main/cpp/ImageMindSporeNetnative.h new file mode 100644 index 0000000000..688409082c --- /dev/null +++ b/model_zoo/official/lite/Himindspore/app/src/main/cpp/ImageMindSporeNetnative.h @@ -0,0 +1,21 @@ +/** + * Copyright 2020 Huawei Technologies Co., Ltd + * + * 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. + */ + +#ifndef IMAGE_MINDSPORE_JNI_HMS_DEBUG_MINDSPORENETNATIVE_H +#define IMAGE_MINDSPORE_JNI_HMS_DEBUG_MINDSPORENETNATIVE_H + + +#endif // MINDSPORE_JNI_HMS_DEBUG_MINDSPORENETNATIVE_H diff --git a/model_zoo/official/lite/Himindspore/app/src/main/cpp/MSNetWork.cpp b/model_zoo/official/lite/Himindspore/app/src/main/cpp/MSNetWork.cpp new file mode 100644 index 0000000000..dd2c5c60f2 --- /dev/null +++ b/model_zoo/official/lite/Himindspore/app/src/main/cpp/MSNetWork.cpp @@ -0,0 +1,52 @@ +/** + * Copyright 2020 Huawei Technologies Co., Ltd + * + * 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 "MSNetWork.h" +#include +#include +#include + +#define MS_PRINT(format, ...) __android_log_print(ANDROID_LOG_INFO, "MSJNI", format, ##__VA_ARGS__) + +MSNetWork::MSNetWork(void) : session_(nullptr) {} + +MSNetWork::~MSNetWork(void) {} + +void MSNetWork::CreateSessionMS(char *modelBuffer, size_t bufferLen, mindspore::lite::Context *ctx) { + session_ = mindspore::session::LiteSession::CreateSession(ctx); + if (session_ == nullptr) { + MS_PRINT("Create Session failed."); + return; + } + + // Compile model. + auto model = mindspore::lite::Model::Import(modelBuffer, bufferLen); + if (model == nullptr) { + MS_PRINT("Import model failed."); + return; + } + + int ret = session_->CompileGraph(model); + if (ret != mindspore::lite::RET_OK) { + MS_PRINT("CompileGraph failed."); + return; + } +} + +int MSNetWork::ReleaseNets(void) { + delete session_; + return 0; +} diff --git a/model_zoo/official/lite/Himindspore/app/src/main/cpp/MSNetWork.h b/model_zoo/official/lite/Himindspore/app/src/main/cpp/MSNetWork.h new file mode 100644 index 0000000000..a6498b95a6 --- /dev/null +++ b/model_zoo/official/lite/Himindspore/app/src/main/cpp/MSNetWork.h @@ -0,0 +1,59 @@ +/** + * Copyright 2020 Huawei Technologies Co., Ltd + * + * 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. + */ + +#ifndef MSNETWORK_H +#define MSNETWORK_H + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +struct ImgDims { + int channel = 0; + int width = 0; + int height = 0; +}; + +/*struct SessIterm { + std::shared_ptr sess = nullptr; +};*/ + +class MSNetWork { + public: + MSNetWork(); + + ~MSNetWork(); + + void CreateSessionMS(char *modelBuffer, size_t bufferLen, mindspore::lite::Context *ctx); + + int ReleaseNets(void); + + mindspore::session::LiteSession *session() const { return session_; } + private: + mindspore::session::LiteSession *session_; +}; +#endif diff --git a/model_zoo/official/lite/Himindspore/app/src/main/cpp/ObjectMindSporeNetnative.cpp b/model_zoo/official/lite/Himindspore/app/src/main/cpp/ObjectMindSporeNetnative.cpp new file mode 100644 index 0000000000..1798231519 --- /dev/null +++ b/model_zoo/official/lite/Himindspore/app/src/main/cpp/ObjectMindSporeNetnative.cpp @@ -0,0 +1,267 @@ +/** + * Copyright 2020 Huawei Technologies Co., Ltd + * + * 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 +#include +#include +#include +#include +#include +#include +#include +#include "include/errorcode.h" +#include "include/ms_tensor.h" +#include "MSNetWork.h" +#include "ssd_util/ssd_util.h" +#include "lite_cv/lite_mat.h" +#include "lite_cv/image_process.h" + +using mindspore::dataset::LiteMat; +using mindspore::dataset::LPixelType; +using mindspore::dataset::LDataType; +#define MS_PRINT(format, ...) __android_log_print(ANDROID_LOG_INFO, "MSJNI", format, ##__VA_ARGS__) + +bool ObjectBitmapToLiteMat(JNIEnv *env, const jobject &srcBitmap, LiteMat *lite_mat) { + bool ret = false; + AndroidBitmapInfo info; + void *pixels = nullptr; + LiteMat &lite_mat_bgr = *lite_mat; + AndroidBitmap_getInfo(env, srcBitmap, &info); + if (info.format != ANDROID_BITMAP_FORMAT_RGBA_8888) { + MS_PRINT("Image Err, Request RGBA"); + return false; + } + AndroidBitmap_lockPixels(env, srcBitmap, &pixels); + if (info.stride == info.width * 4) { + ret = InitFromPixel(reinterpret_cast(pixels), + LPixelType::RGBA2RGB, LDataType::UINT8, + info.width, info.height, lite_mat_bgr); + if (!ret) { + MS_PRINT("Init From RGBA error"); + } + } else { + unsigned char *pixels_ptr = new unsigned char[info.width * info.height * 4]; + unsigned char *ptr = pixels_ptr; + unsigned char *data = reinterpret_cast(pixels); + for (int i = 0; i < info.height; i++) { + memcpy(ptr, data, info.width * 4); + ptr += info.width * 4; + data += info.stride; + } + ret = InitFromPixel(reinterpret_cast(pixels_ptr), + LPixelType::RGBA2RGB, LDataType::UINT8, + info.width, info.height, lite_mat_bgr); + if (!ret) { + MS_PRINT("Init From RGBA error"); + } + delete[] (pixels_ptr); + } + AndroidBitmap_unlockPixels(env, srcBitmap); + return ret; +} + +bool ObjectPreProcessImageData(const LiteMat &lite_mat_bgr, LiteMat *lite_norm_mat_ptr) { + bool ret = false; + LiteMat lite_mat_resize; + LiteMat &lite_norm_mat_cut = *lite_norm_mat_ptr; + ret = ResizeBilinear(lite_mat_bgr, lite_mat_resize, 300, 300); + if (!ret) { + MS_PRINT("ResizeBilinear error"); + return false; + } + LiteMat lite_mat_convert_float; + ret = ConvertTo(lite_mat_resize, lite_mat_convert_float, 1.0 / 255.0); + if (!ret) { + MS_PRINT("ConvertTo error"); + return false; + } + + std::vector means = {0.485, 0.456, 0.406}; + std::vector stds = {0.229, 0.224, 0.225}; + SubStractMeanNormalize(lite_mat_convert_float, lite_norm_mat_cut, means, stds); + return true; +} + +char *ObjectCreateLocalModelBuffer(JNIEnv *env, jobject modelBuffer) { + jbyte *modelAddr = static_cast(env->GetDirectBufferAddress(modelBuffer)); + int modelLen = static_cast(env->GetDirectBufferCapacity(modelBuffer)); + char *buffer(new char[modelLen]); + memcpy(buffer, modelAddr, modelLen); + return buffer; +} + +/** + * + * @param msOutputs Model output, the mindspore inferencing result. + * @param srcImageWidth The width of the original input image. + * @param srcImageHeight The height of the original input image. + * @return + */ +std::string ProcessRunnetResult(std::unordered_map msOutputs, + int srcImageWidth, int srcImageHeight) { + std::unordered_map::iterator iter; + iter = msOutputs.begin(); + auto branch2_string = iter->first; + auto branch2_tensor = iter->second; + + ++iter; + auto branch1_string = iter->first; + auto branch1_tensor = iter->second; + MS_PRINT("%s %s", branch1_string.c_str(), branch2_string.c_str()); + + // ----------- 接口测试 -------------------------- + float *tmpscores2 = reinterpret_cast(branch1_tensor->MutableData()); + float *tmpdata = reinterpret_cast(branch2_tensor->MutableData()); + + // Using ssd model util to process model branch outputs. + SSDModelUtil ssdUtil(srcImageWidth, srcImageHeight); + + std::string retStr = ssdUtil.getDecodeResult(tmpscores2, tmpdata); + MS_PRINT("retStr %s", retStr.c_str()); + + return retStr; +} + +extern "C" JNIEXPORT jlong JNICALL +Java_com_mindspore_himindspore_objectdetection_help_ObjectTrackingMobile_loadModel(JNIEnv *env, jobject thiz, + jobject assetManager, + jobject buffer, + jint numThread) { + MS_PRINT("MindSpore so version 20200730"); + if (nullptr == buffer) { + MS_PRINT("error, buffer is nullptr!"); + return (jlong) nullptr; + } + jlong bufferLen = env->GetDirectBufferCapacity(buffer); + MS_PRINT("MindSpore get bufferLen:%d", static_cast(bufferLen)); + if (0 == bufferLen) { + MS_PRINT("error, bufferLen is 0!"); + return (jlong) nullptr; + } + + char *modelBuffer = ObjectCreateLocalModelBuffer(env, buffer); + if (modelBuffer == nullptr) { + MS_PRINT("modelBuffer create failed!"); + return (jlong) nullptr; + } + + MS_PRINT("MindSpore loading Model."); + void **labelEnv = new void *; + MSNetWork *labelNet = new MSNetWork; + *labelEnv = labelNet; + + mindspore::lite::Context *context = new mindspore::lite::Context; + context->thread_num_ = numThread; + + labelNet->CreateSessionMS(modelBuffer, bufferLen, context); + delete context; + if (labelNet->session() == nullptr) { + delete labelNet; + delete labelEnv; + MS_PRINT("MindSpore create session failed!."); + return (jlong) nullptr; + } + MS_PRINT("MindSpore create session successfully."); + + if (buffer != nullptr) { + env->DeleteLocalRef(buffer); + } + + if (assetManager != nullptr) { + env->DeleteLocalRef(assetManager); + } + MS_PRINT("ptr released successfully."); + + return (jlong) labelEnv; +} + + +extern "C" JNIEXPORT jstring JNICALL +Java_com_mindspore_himindspore_objectdetection_help_ObjectTrackingMobile_runNet(JNIEnv *env, jobject thiz, + jlong netEnv, + jobject srcBitmap) { + LiteMat lite_mat_bgr, lite_norm_mat_cut; + + if (!ObjectBitmapToLiteMat(env, srcBitmap, &lite_mat_bgr)) { + MS_PRINT("ObjectBitmapToLiteMat error"); + return NULL; + } + int srcImageWidth = lite_mat_bgr.width_; + int srcImageHeight = lite_mat_bgr.height_; + if (!ObjectPreProcessImageData(lite_mat_bgr, &lite_norm_mat_cut)) { + MS_PRINT("ObjectPreProcessImageData error"); + return NULL; + } + + ImgDims inputDims; + inputDims.channel = lite_norm_mat_cut.channel_; + inputDims.width = lite_norm_mat_cut.width_; + inputDims.height = lite_norm_mat_cut.height_; + + // Get the mindsore inference environment which created in loadModel(). + void **labelEnv = reinterpret_cast(netEnv); + if (labelEnv == nullptr) { + MS_PRINT("MindSpore error, labelEnv is a nullptr."); + return NULL; + } + MSNetWork *labelNet = static_cast(*labelEnv); + + auto mSession = labelNet->session(); + if (mSession == nullptr) { + MS_PRINT("MindSpore error, Session is a nullptr."); + return NULL; + } + MS_PRINT("MindSpore get session."); + + auto msInputs = mSession->GetInputs(); + auto inTensor = msInputs.front(); + float *dataHWC = reinterpret_cast(lite_norm_mat_cut.data_ptr_); + // copy input Tensor + memcpy(inTensor->MutableData(), dataHWC, + inputDims.channel * inputDims.width * inputDims.height * sizeof(float)); + MS_PRINT("MindSpore get msInputs."); + + auto status = mSession->RunGraph(); + if (status != mindspore::lite::RET_OK) { + MS_PRINT("MindSpore runnet error."); + return NULL; + } + + auto names = mSession->GetOutputTensorNames(); + std::unordered_map msOutputs; + for (const auto &name : names) { + auto temp_dat = mSession->GetOutputByTensorName(name); + msOutputs.insert(std::pair {name, temp_dat}); + } + std::string retStr = ProcessRunnetResult(msOutputs, srcImageWidth, srcImageHeight); + const char *resultChardata = retStr.c_str(); + + return (env)->NewStringUTF(resultChardata); +} + + +extern "C" +JNIEXPORT jboolean JNICALL +Java_com_mindspore_himindspore_objectdetection_help_ObjectTrackingMobile_unloadModel(JNIEnv *env, + jobject thiz, + jlong netEnv) { + void **labelEnv = reinterpret_cast(netEnv); + MSNetWork *labelNet = static_cast(*labelEnv); + labelNet->ReleaseNets(); + return (jboolean) true; +} + diff --git a/model_zoo/official/lite/Himindspore/app/src/main/cpp/ssd_util/ssd_util.cpp b/model_zoo/official/lite/Himindspore/app/src/main/cpp/ssd_util/ssd_util.cpp new file mode 100644 index 0000000000..e34c7d56b1 --- /dev/null +++ b/model_zoo/official/lite/Himindspore/app/src/main/cpp/ssd_util/ssd_util.cpp @@ -0,0 +1,291 @@ +/** + * Copyright 2020 Huawei Technologies Co., Ltd + * + * 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 +#include +#include "ssd_util/ssd_util.h" + +#define MS_PRINT(format, ...) __android_log_print(ANDROID_LOG_INFO, "MSJNI", format, ##__VA_ARGS__) + +SSDModelUtil::~SSDModelUtil(void) {} + +/** + * SSD model util constructor. + * @param srcImgWidth The width of the original input image. + * @param srcImgHeight The height of the original input image. + */ +SSDModelUtil::SSDModelUtil(int srcImgWidth, int srcImgHeight) { + inputImageWidth = srcImgWidth; + inputImageHeight = srcImgHeight; + + getDefaultBoxes(); // To fill the vectordefaultboxes. +} + + +std::string SSDModelUtil::getDecodeResult(float *branchScores, float *branchBoxData) { + std::string result = ""; + NormalBox tmpBox[1917] = {0}; + float mScores[1917][81] = {0}; + + float outBuff[1917][7] = {0}; + + float scoreWithOneClass[1917] = {0}; + int outBoxNum = 0; + YXBoxes decodedBoxes[1917] = {0}; + + // Copy branch outputs box data to tmpBox. + for (int i = 0; i < 1917; ++i) { + tmpBox[i].y = branchBoxData[i * 4 + 0]; + tmpBox[i].x = branchBoxData[i * 4 + 1]; + tmpBox[i].h = branchBoxData[i * 4 + 2]; + tmpBox[i].w = branchBoxData[i * 4 + 3]; + } + + // Copy branch outputs score to mScores. + for (int i = 0; i < 1917; ++i) { + for (int j = 0; j < 81; ++j) { + mScores[i][j] = branchScores[i * 81 + j]; + } + } + + // NMS processing. + ssd_boxes_decode(tmpBox, decodedBoxes, 0.1, 0.2, 1917); + const float nms_threshold = 0.3; + for (int i = 1; i < 81; i++) { + std::vector in_indexes; + for (int j = 0; j < 1917; j++) { + scoreWithOneClass[j] = mScores[j][i]; + if (mScores[j][i] > g_thres_map[i]) { + in_indexes.push_back(j); + } + } + if (in_indexes.size() == 0) { + continue; + } + + sort(in_indexes.begin(), in_indexes.end(), + [&](int a, int b) { return scoreWithOneClass[a] > scoreWithOneClass[b]; }); + std::vector out_indexes; + + nonMaximumSuppression(decodedBoxes, scoreWithOneClass, in_indexes, &out_indexes, + nms_threshold); + for (int k = 0; k < out_indexes.size(); k++) { + // image id + outBuff[outBoxNum][0] = out_indexes[k]; + // labelid + outBuff[outBoxNum][1] = i; + // scores + outBuff[outBoxNum][2] = scoreWithOneClass[out_indexes[k]]; + outBuff[outBoxNum][3] = + decodedBoxes[out_indexes[k]].xmin * inputImageWidth / 300; + outBuff[outBoxNum][4] = + decodedBoxes[out_indexes[k]].ymin * inputImageHeight / 300; + outBuff[outBoxNum][5] = + decodedBoxes[out_indexes[k]].xmax * inputImageWidth / 300; + outBuff[outBoxNum][6] = + decodedBoxes[out_indexes[k]].ymax * inputImageHeight / 300; + outBoxNum++; + } + } + MS_PRINT("outBoxNum %d", outBoxNum); + + for (int i = 0; i < outBoxNum; ++i) { + std::string tmpid_str = std::to_string(outBuff[i][0]); + result += tmpid_str; + result += "_"; + MS_PRINT("label_classes i %d, outBuff %d", i, (int) outBuff[i][1]); + tmpid_str = label_classes[static_cast(outBuff[i][1])]; + // label id + result += tmpid_str; + result += "_"; + tmpid_str = std::to_string(outBuff[i][2]); + // scores + result += tmpid_str; + result += "_"; + tmpid_str = std::to_string(outBuff[i][3]); + // xmin + result += tmpid_str; + result += "_"; + tmpid_str = std::to_string(outBuff[i][4]); + // ymin + result += tmpid_str; + result += "_"; + tmpid_str = std::to_string(outBuff[i][5]); + // xmax + result += tmpid_str; + result += "_"; + tmpid_str = std::to_string(outBuff[i][6]); + // ymax + result += tmpid_str; + result += ";"; + } + + return result; +} + +void SSDModelUtil::getDefaultBoxes() { + float fk[6] = {0.0, 0.0, 0.0, 0.0, 0.0, 0.0}; + std::vector all_sizes; + struct Product mProductData[19 * 19] = {0}; + + for (int i = 0; i < 6; i++) { + fk[i] = config.model_input_height / config.steps[i]; + } + float scale_rate = + (config.max_scale - config.min_scale) / (sizeof(config.num_default) / sizeof(int) - 1); + float scales[7] = {0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 1.0}; + for (int i = 0; i < sizeof(config.num_default) / sizeof(int); i++) { + scales[i] = config.min_scale + scale_rate * i; + } + + for (int idex = 0; idex < sizeof(config.feature_size) / sizeof(int); idex++) { + float sk1 = scales[idex]; + float sk2 = scales[idex + 1]; + float sk3 = sqrt(sk1 * sk2); + struct WHBox tempWHBox; + + all_sizes.clear(); + + // idex == 0时, len(all_sizes) = 3. + if (idex == 0) { + float w = sk1 * sqrt(2); + float h = sk1 / sqrt(2); + + // all_sizes = [(0.1, 0.1), (w, h), (h, w)] + tempWHBox.boxw = 0.1; + tempWHBox.boxh = 0.1; + all_sizes.push_back(tempWHBox); + + tempWHBox.boxw = w; + tempWHBox.boxh = h; + all_sizes.push_back(tempWHBox); + + tempWHBox.boxw = h; + tempWHBox.boxh = w; + all_sizes.push_back(tempWHBox); + } else { + // len(all_sizes) = 6. + tempWHBox.boxw = sk1; + tempWHBox.boxh = sk1; + all_sizes.push_back(tempWHBox); + + for (int j = 0; j < sizeof(config.aspect_ratios[idex]) / sizeof(int); j++) { + float w = sk1 * sqrt(config.aspect_ratios[idex][j]); + float h = sk1 / sqrt(config.aspect_ratios[idex][j]); + + tempWHBox.boxw = w; + tempWHBox.boxh = h; + all_sizes.push_back(tempWHBox); + + tempWHBox.boxw = h; + tempWHBox.boxh = w; + all_sizes.push_back(tempWHBox); + } + + tempWHBox.boxw = sk3; + tempWHBox.boxh = sk3; + all_sizes.push_back(tempWHBox); + } + + for (int i = 0; i < config.feature_size[idex]; i++) { + for (int j = 0; j < config.feature_size[idex]; j++) { + mProductData[i * config.feature_size[idex] + j].x = i; + mProductData[i * config.feature_size[idex] + j].y = j; + } + } + + int productLen = config.feature_size[idex] * config.feature_size[idex]; + + for (int i = 0; i < productLen; i++) { + for (int j = 0; j < all_sizes.size(); j++) { + struct NormalBox tempBox; + + float cx = (mProductData[i].y + 0.5) / fk[idex]; + float cy = (mProductData[i].x + 0.5) / fk[idex]; + + tempBox.y = cy; + tempBox.x = cx; + tempBox.h = all_sizes[j].boxh; + tempBox.w = all_sizes[j].boxw; + + mDefaultBoxes.push_back(tempBox); + } + } + } +} + + +void SSDModelUtil::ssd_boxes_decode(const NormalBox *boxes, + YXBoxes *const decoded_boxes, const float scale0, + const float scale1, const int count) { + if (mDefaultBoxes.size() == 0) { + MS_PRINT("get default boxes error."); + return; + } + + for (int i = 0; i < count; ++i) { + float cy = boxes[i].y * scale0 * mDefaultBoxes[i].h + mDefaultBoxes[i].y; + float cx = boxes[i].x * scale0 * mDefaultBoxes[i].w + mDefaultBoxes[i].x; + float h = exp(boxes[i].h * scale1) * mDefaultBoxes[i].h; + float w = exp(boxes[i].w * scale1) * mDefaultBoxes[i].w; + decoded_boxes[i].ymin = std::min(1.0f, std::max(0.0f, cy - h / 2)) * config.model_input_height; + decoded_boxes[i].xmin = std::min(1.0f, std::max(0.0f, cx - w / 2)) * config.model_input_width; + decoded_boxes[i].ymax = std::min(1.0f, std::max(0.0f, cy + h / 2)) * config.model_input_height; + decoded_boxes[i].xmax = std::min(1.0f, std::max(0.0f, cx + w / 2)) * config.model_input_width; + } +} + +void SSDModelUtil::nonMaximumSuppression(const YXBoxes *const decoded_boxes, + const float *const scores, + const std::vector &in_indexes, + std::vector *out_indexes_p, const float nmsThreshold, + const int count, const int max_results) { + int nR = 0; + std::vector &out_indexes = *out_indexes_p; + std::vector del(count, false); + for (size_t i = 0; i < in_indexes.size(); i++) { + if (!del[in_indexes[i]]) { + out_indexes.push_back(in_indexes[i]); + if (++nR == max_results) { + break; + } + for (size_t j = i + 1; j < in_indexes.size(); j++) { + const auto boxi = decoded_boxes[in_indexes[i]], boxj = decoded_boxes[in_indexes[j]]; + float a[4] = {boxi.xmin, boxi.ymin, boxi.xmax, boxi.ymax}; + float b[4] = {boxj.xmin, boxj.ymin, boxj.xmax, boxj.ymax}; + if (IOU(a, b) > nmsThreshold) { + del[in_indexes[j]] = true; + } + } + } + } +} + +double SSDModelUtil::IOU(float r1[4], float r2[4]) { + float x1 = std::max(r1[0], r2[0]); + float y1 = std::max(r1[1], r2[1]); + float x2 = std::min(r1[2], r2[2]); + float y2 = std::min(r1[3], r2[3]); + // if max(min) > min(max), there is no intersection + if (x2 - x1 + 1 <= 0 || y2 - y1 + 1 <= 0) + return 0; + double insect_area = (x2 - x1 + 1) * (y2 - y1 + 1); + double union_area = + (r1[2] - r1[0] + 1) * (r1[3] - r1[1] + 1) + (r2[2] - r2[0] + 1) * (r2[3] - r2[1] + 1) - + insect_area; + double iou = insect_area / union_area; + return (iou > 0) ? iou : 0; +} diff --git a/model_zoo/official/lite/Himindspore/app/src/main/cpp/ssd_util/ssd_util.h b/model_zoo/official/lite/Himindspore/app/src/main/cpp/ssd_util/ssd_util.h new file mode 100644 index 0000000000..ad476f622e --- /dev/null +++ b/model_zoo/official/lite/Himindspore/app/src/main/cpp/ssd_util/ssd_util.h @@ -0,0 +1,200 @@ +/** + * Copyright 2020 Huawei Technologies Co., Ltd + * + * 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. + */ + +#ifndef HIMINDSPORE_SSD_UTIL_H +#define HIMINDSPORE_SSD_UTIL_H + +#include +#include + + +class SSDModelUtil { + public: + // Constructor. + SSDModelUtil(int srcImageWidth, int srcImgHeight); + + ~SSDModelUtil(); + + /** + * Return the SSD model post-processing result. + * @param branchScores + * @param branchBoxData + * @return + */ + std::string getDecodeResult(float *branchScores, float *branchBoxData); + + struct NormalBox { + float y; + float x; + float h; + float w; + }; + + struct YXBoxes { + float ymin; + float xmin; + float ymax; + float xmax; + }; + + struct Product { + int x; + int y; + }; + + struct WHBox { + float boxw; + float boxh; + }; + + private: + std::vector mDefaultBoxes; + int inputImageHeight; + int inputImageWidth; + + void getDefaultBoxes(); + + void ssd_boxes_decode(const NormalBox *boxes, + YXBoxes *const decoded_boxes, + const float scale0 = 0.1, const float scale1 = 0.2, + const int count = 1917); + + void nonMaximumSuppression(const YXBoxes *const decoded_boxes, const float *const scores, + const std::vector &in_indexes, std::vector *out_indexes_p, + const float nmsThreshold = 0.6, + const int count = 1917, const int max_results = 100); + + double IOU(float r1[4], float r2[4]); + + // ============= variables =============. + struct network { + int model_input_height = 300; + int model_input_width = 300; + + int num_default[6] = {3, 6, 6, 6, 6, 6}; + int feature_size[6] = {19, 10, 5, 3, 2, 1}; + double min_scale = 0.2; + float max_scale = 0.95; + float steps[6] = {16, 32, 64, 100, 150, 300}; + float prior_scaling[2] = {0.1, 0.2}; + float gamma = 2.0; + float alpha = 0.75; + int aspect_ratios[6][2] = {{2, 0}, + {2, 3}, + {2, 3}, + {2, 3}, + {2, 3}, + {2, 3}}; + } config; + + float g_thres_map[81] = {0, 0.635, 0.627, 0.589, 0.585, 0.648, 0.664, 0.655, + 0.481, 0.529, 0.611, 0.641, 0.774, 0.549, 0.513, 0.652, + 0.552, 0.590, 0.650, 0.575, 0.583, 0.650, 0.656, 0.696, + 0.653, 0.438, 0.515, 0.459, 0.561, 0.545, 0.635, 0.540, + 0.560, 0.721, 0.544, 0.548, 0.511, 0.611, 0.592, 0.542, + 0.512, 0.635, 0.531, 0.437, 0.525, 0.445, 0.484, 0.546, + 0.490, 0.581, 0.566, 0.516, 0.445, 0.541, 0.613, 0.560, + 0.483, 0.509, 0.464, 0.543, 0.538, 0.490, 0.576, 0.617, + 0.577, 0.595, 0.640, 0.585, 0.598, 0.592, 0.514, 0.397, + 0.592, 0.504, 0.548, 0.642, 0.581, 0.497, 0.545, 0.154, + 0.580, + }; + + std::string label_classes[81] = { + {"background"}, + {"human"}, + {"bike"}, + {"automobile"}, + {"motorbike"}, + {"aircraft"}, + {"motorbus"}, + {"train"}, + {"motortruck"}, + {"boat"}, + {"traffic signal"}, + {"fireplug"}, + {"stop sign"}, + {"parking meter"}, + {"seat"}, + {"bird"}, + {"cat"}, + {"dog"}, + {"horse"}, + {"sheep"}, + {"cow"}, + {"elephant"}, + {"bear"}, + {"zebra"}, + {"giraffe"}, + {"knapsack"}, + {"bumbershoot"}, + {"purse"}, + {"neckwear"}, + {"traveling bag"}, + {"frisbee"}, + {"skis"}, + {"snowboard"}, + {"sports ball"}, + {"kite"}, + {"baseball bat"}, + {"baseball glove"}, + {"skateboard"}, + {"surfboard"}, + {"tennis racket"}, + {"bottle"}, + {"wine glass"}, + {"cup"}, + {"fork"}, + {"knife"}, + {"spoon"}, + {"bowl"}, + {"banana"}, + {"apple"}, + {"sandwich"}, + {"orange"}, + {"broccoli"}, + {"carrot"}, + {"hot dog"}, + {"pizza"}, + {"donut"}, + {"cake"}, + {"chair"}, + {"couch"}, + {"houseplant"}, + {"bed"}, + {"dinner table"}, + {"toilet"}, + {"television"}, + {"notebook computer"}, + {"mouse"}, + {"remote"}, + {"keyboard"}, + {"smartphone"}, + {"microwave"}, + {"oven"}, + {"toaster"}, + {"water sink"}, + {"fridge"}, + {"book"}, + {"bell"}, + {"vase"}, + {"shears"}, + {"toy bear"}, + {"hair drier"}, + {"toothbrush"} + }; +}; +#endif diff --git a/model_zoo/official/lite/Himindspore/app/src/main/java/com/mindspore/himindspore/SplashActivity.java b/model_zoo/official/lite/Himindspore/app/src/main/java/com/mindspore/himindspore/SplashActivity.java new file mode 100644 index 0000000000..142bf4bfaf --- /dev/null +++ b/model_zoo/official/lite/Himindspore/app/src/main/java/com/mindspore/himindspore/SplashActivity.java @@ -0,0 +1,90 @@ +package com.mindspore.himindspore; + +import android.Manifest; +import android.content.Intent; +import android.net.Uri; +import android.os.Bundle; +import android.view.View; +import android.widget.Button; + +import androidx.annotation.NonNull; +import androidx.appcompat.app.AppCompatActivity; +import androidx.core.app.ActivityCompat; + +import com.mindspore.himindspore.imageclassification.ui.ImageCameraActivity; +import com.mindspore.himindspore.imageclassification.ui.ImageMainActivity; +import com.mindspore.himindspore.objectdetection.ui.ObjectDetectionMainActivity; + +public class SplashActivity extends AppCompatActivity implements View.OnClickListener { + + private static final int REQUEST_PERMISSION = 1; + + private Button btnImage, btnObject, btnContract,btnAdvice; + private boolean isHasPermssion; + + private static final String CODE_URL ="https://gitee.com/mindspore/mindspore/tree/master/model_zoo/official/lite"; + private static final String HELP_URL ="https://github.com/mindspore-ai/mindspore/issues"; + + @Override + protected void onCreate(Bundle savedInstanceState) { + super.onCreate(savedInstanceState); + setContentView(R.layout.activity_splash); + + btnImage = findViewById(R.id.btn_image); + btnObject = findViewById(R.id.btn_object); + btnContract = findViewById(R.id.btn_contact); + btnAdvice = findViewById(R.id.btn_advice); + + btnImage.setOnClickListener(this); + btnObject.setOnClickListener(this); + btnContract.setOnClickListener(this); + btnAdvice.setOnClickListener(this); + + requestPermissions(); + } + + private void requestPermissions() { + ActivityCompat.requestPermissions(this, + new String[]{Manifest.permission.READ_EXTERNAL_STORAGE, Manifest.permission.WRITE_EXTERNAL_STORAGE, + Manifest.permission.READ_PHONE_STATE, Manifest.permission.CAMERA}, REQUEST_PERMISSION); + } + + /** + * 权限申请结果回调 + */ + @Override + public void onRequestPermissionsResult(int requestCode, @NonNull String[] permissions, @NonNull int[] grantResults) { + if (REQUEST_PERMISSION == requestCode) { + isHasPermssion = true; + } + } + + @Override + public void onClick(View view) { + if (R.id.btn_image == view.getId()) { + if (isHasPermssion) { + startActivity(new Intent(SplashActivity.this, ImageMainActivity.class)); + } else { + requestPermissions(); + } + } else if (R.id.btn_object == view.getId()) { + if (isHasPermssion) { + startActivity(new Intent(SplashActivity.this, ObjectDetectionMainActivity.class)); + } else { + requestPermissions(); + } + } else if (R.id.btn_contact == view.getId()) { + openBrowser(CODE_URL); + }else if (R.id.btn_advice == view.getId()) { + openBrowser(HELP_URL); + } + } + + public void openBrowser(String url) { + Intent intent = new Intent(); + intent.setAction("android.intent.action.VIEW"); + Uri uri = Uri.parse(url.trim()); + intent.setData(uri); + startActivity(intent); + } +} \ No newline at end of file diff --git a/model_zoo/official/lite/Himindspore/app/src/main/java/com/mindspore/himindspore/camera/CameraPreview.java b/model_zoo/official/lite/Himindspore/app/src/main/java/com/mindspore/himindspore/camera/CameraPreview.java new file mode 100644 index 0000000000..bc78853054 --- /dev/null +++ b/model_zoo/official/lite/Himindspore/app/src/main/java/com/mindspore/himindspore/camera/CameraPreview.java @@ -0,0 +1,843 @@ +package com.mindspore.himindspore.camera; + +import android.Manifest; +import android.app.Activity; +import android.content.Context; +import android.content.pm.PackageManager; +import android.content.res.Configuration; +import android.graphics.Bitmap; +import android.graphics.ImageFormat; +import android.graphics.Matrix; +import android.graphics.Point; +import android.graphics.RectF; +import android.graphics.SurfaceTexture; +import android.hardware.camera2.CameraAccessException; +import android.hardware.camera2.CameraCaptureSession; +import android.hardware.camera2.CameraCharacteristics; +import android.hardware.camera2.CameraDevice; +import android.hardware.camera2.CameraManager; +import android.hardware.camera2.CameraMetadata; +import android.hardware.camera2.CaptureRequest; +import android.hardware.camera2.CaptureResult; +import android.hardware.camera2.TotalCaptureResult; +import android.hardware.camera2.params.StreamConfigurationMap; +import android.media.Image; +import android.media.ImageReader; +import android.os.Handler; +import android.os.HandlerThread; +import android.util.AttributeSet; +import android.util.Log; +import android.util.Size; +import android.util.SparseIntArray; +import android.view.Surface; +import android.view.TextureView; +import android.widget.Toast; + +import androidx.annotation.NonNull; +import androidx.annotation.Nullable; +import androidx.core.app.ActivityCompat; + +import com.mindspore.himindspore.imageclassification.help.GarbageTrackingMobile; +import com.mindspore.himindspore.track.TrackListener; +import com.mindspore.himindspore.imageclassification.help.ImageTrackingMobile; +import com.mindspore.himindspore.objectdetection.help.ObjectTrackingMobile; + +import java.io.File; +import java.io.FileOutputStream; +import java.io.IOException; +import java.nio.ByteBuffer; +import java.util.ArrayList; +import java.util.Arrays; +import java.util.Collections; +import java.util.Comparator; +import java.util.List; +import java.util.concurrent.Semaphore; +import java.util.concurrent.TimeUnit; + +public class CameraPreview extends TextureView { + + private static final String TAG = "CameraPreview"; + + private static final SparseIntArray ORIENTATIONS = new SparseIntArray();//从屏幕旋转转换为JPEG方向 + private static final int MAX_PREVIEW_WIDTH = 1920;//Camera2 API 保证的最大预览宽高 + private static final int MAX_PREVIEW_HEIGHT = 1080; + private static final int STATE_PREVIEW = 0;//显示相机预览 + private static final int STATE_WAITING_LOCK = 1;//焦点锁定中 + private static final int STATE_WAITING_PRE_CAPTURE = 2;//拍照中 + private static final int STATE_WAITING_NON_PRE_CAPTURE = 3;//其它状态 + private static final int STATE_PICTURE_TAKEN = 4;//拍照完毕 + + public static final int OPEN_TYPE_IMAGE = 1; + public static final int OPEN_TYPE_IMAGE_CUSTOM = 11; + public static final int OPEN_TYPE_OBJECT = 2; + + private int openType; + + private int mState = STATE_PREVIEW; + private int mRatioWidth = 0, mRatioHeight = 0; + private int mSensorOrientation; + private boolean mFlashSupported; + + private Semaphore mCameraOpenCloseLock = new Semaphore(1);//使用信号量 Semaphore 进行多线程任务调度 + private Activity activity; + private File mFile; + private HandlerThread mBackgroundThread; + private Handler mBackgroundHandler; + + private HandlerThread mImageBackgroundThread; + private Handler mImageBackgroundHandler; + private Size mPreviewSize; + private String mCameraId; + private CameraDevice mCameraDevice; + private CaptureRequest.Builder mPreviewRequestBuilder; + private CaptureRequest mPreviewRequest; + private CameraCaptureSession mCaptureSession; + private ImageReader mImageReader; + private ICameraDataCallBack iCameraDataCallBack; + + private ImageTrackingMobile imageTrackingMobile; + private GarbageTrackingMobile garbageTrackingMobile; + private ObjectTrackingMobile objectTrackingMobile; + + private boolean isPreBackgroundThreadPause; + private boolean isAlive; + + static { + ORIENTATIONS.append(Surface.ROTATION_0, 90); + ORIENTATIONS.append(Surface.ROTATION_90, 0); + ORIENTATIONS.append(Surface.ROTATION_180, 270); + ORIENTATIONS.append(Surface.ROTATION_270, 180); + } + + + public CameraPreview(@NonNull Context context) { + this(context, null); + } + + public CameraPreview(@NonNull Context context, @Nullable AttributeSet attrs) { + this(context, attrs, 0); + } + + public CameraPreview(@NonNull Context context, @Nullable AttributeSet attrs, int defStyleAttr) { + this(context, attrs, defStyleAttr, 0); + + } + + public CameraPreview(@NonNull Context context, @Nullable AttributeSet attrs, int defStyleAttr, int defStyleRes) { + super(context, attrs, defStyleAttr, defStyleRes); + } + + @Override + protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) { + super.onMeasure(widthMeasureSpec, heightMeasureSpec); + int width = MeasureSpec.getSize(widthMeasureSpec); + int height = MeasureSpec.getSize(heightMeasureSpec); + if (0 == mRatioWidth || 0 == mRatioHeight) { + setMeasuredDimension(width, height); + } else { + if (width < height * mRatioWidth / mRatioHeight) { + setMeasuredDimension(width, width * mRatioHeight / mRatioWidth); + } else { + setMeasuredDimension(height * mRatioWidth / mRatioHeight, height); + } + } + } + + public void setAspectRatio(int width, int height) { + if (width < 0 || height < 0) { + throw new IllegalArgumentException("Size can't be negative"); + } + mRatioWidth = width; + mRatioHeight = height; + requestLayout(); + } + + /** + * 处理生命周期内的回调事件 + */ + private final SurfaceTextureListener mSurfaceTextureListener = new SurfaceTextureListener() { + + @Override + public void onSurfaceTextureAvailable(SurfaceTexture texture, int width, int height) { + openCamera(width, height); + } + + @Override + public void onSurfaceTextureSizeChanged(SurfaceTexture texture, int width, int height) { + configureTransform(width, height); + } + + @Override + public boolean onSurfaceTextureDestroyed(SurfaceTexture texture) { + return true; + } + + @Override + public void onSurfaceTextureUpdated(SurfaceTexture texture) { + } + }; + + /** + * 相机状态改变回调 + */ + private final CameraDevice.StateCallback mStateCallback = new CameraDevice.StateCallback() { + + @Override + public void onOpened(@NonNull CameraDevice cameraDevice) { + mCameraOpenCloseLock.release(); + Log.d(TAG, "相机已打开"); + mCameraDevice = cameraDevice; + createCameraPreviewSession(); + } + + @Override + public void onDisconnected(@NonNull CameraDevice cameraDevice) { + mCameraOpenCloseLock.release(); + cameraDevice.close(); + mCameraDevice = null; + } + + @Override + public void onError(@NonNull CameraDevice cameraDevice, int error) { + mCameraOpenCloseLock.release(); + cameraDevice.close(); + mCameraDevice = null; + if (null != activity) { + activity.finish(); + } + } + }; + + /** + * 处理与照片捕获相关的事件 + */ + private CameraCaptureSession.CaptureCallback mCaptureCallback = new CameraCaptureSession.CaptureCallback() { + + private void process(CaptureResult result) { + switch (mState) { + case STATE_PREVIEW: { + break; + } + case STATE_WAITING_LOCK: { + Integer afState = result.get(CaptureResult.CONTROL_AF_STATE); + if (afState == null) { + captureStillPicture(); + } else if (CaptureResult.CONTROL_AF_STATE_FOCUSED_LOCKED == afState || + CaptureResult.CONTROL_AF_STATE_NOT_FOCUSED_LOCKED == afState) { + Integer aeState = result.get(CaptureResult.CONTROL_AE_STATE); + if (aeState == null || aeState == CaptureResult.CONTROL_AE_STATE_CONVERGED) { + mState = STATE_PICTURE_TAKEN; + captureStillPicture(); + } else { + runPreCaptureSequence(); + } + } + break; + } + case STATE_WAITING_PRE_CAPTURE: { + Integer aeState = result.get(CaptureResult.CONTROL_AE_STATE); + if (aeState == null || + aeState == CaptureResult.CONTROL_AE_STATE_PRECAPTURE || + aeState == CaptureRequest.CONTROL_AE_STATE_FLASH_REQUIRED) { + mState = STATE_WAITING_NON_PRE_CAPTURE; + } + break; + } + case STATE_WAITING_NON_PRE_CAPTURE: { + Integer aeState = result.get(CaptureResult.CONTROL_AE_STATE); + if (aeState == null || aeState != CaptureResult.CONTROL_AE_STATE_PRECAPTURE) { + mState = STATE_PICTURE_TAKEN; + captureStillPicture(); + } + break; + } + } + } + + @Override + public void onCaptureProgressed(@NonNull CameraCaptureSession session, + @NonNull CaptureRequest request, + @NonNull CaptureResult partialResult) { + process(partialResult); + } + + @Override + public void onCaptureCompleted(@NonNull CameraCaptureSession session, + @NonNull CaptureRequest request, + @NonNull TotalCaptureResult result) { + process(result); + } + + }; + + + public void onResume(Activity activity, int openType, TrackListener track) { + isAlive = true; + this.activity = activity; + this.openType = openType; + if (OPEN_TYPE_IMAGE == openType) { + if (null != track) { + imageTrackingMobile = (ImageTrackingMobile) track; + } + } else if (OPEN_TYPE_IMAGE_CUSTOM == openType) { + if (null != track) { + garbageTrackingMobile = (GarbageTrackingMobile) track; + } + }else if (OPEN_TYPE_OBJECT == openType) { + if (null != track) { + objectTrackingMobile = (ObjectTrackingMobile) track; + } + } + startBackgroundThread(); + //当Activity或Fragment OnResume()时,可以冲洗打开一个相机并开始预览,否则,这个Surface已经准备就绪 + if (this.isAvailable()) { + openCamera(this.getWidth(), this.getHeight()); + } else { + this.setSurfaceTextureListener(mSurfaceTextureListener); + } + } + + public void onPause() { + isAlive = false; + closeCamera(); + stopBackgroundThread(); + } + + private void startBackgroundThread() { + mBackgroundThread = new HandlerThread("CameraBackground"); + mBackgroundThread.start(); + mBackgroundHandler = new Handler(mBackgroundThread.getLooper()); + + if (OPEN_TYPE_IMAGE == openType || OPEN_TYPE_OBJECT == openType || OPEN_TYPE_IMAGE_CUSTOM == openType ) { + mImageBackgroundThread = new HandlerThread("MINDSPORE"); + mImageBackgroundThread.start(); + mImageBackgroundHandler = new Handler(mImageBackgroundThread.getLooper()); + mImageBackgroundHandler.post(classifyRunnable); + } + } + + public boolean isAlive() { + return isAlive; + } + + /** + * Detect time-consuming threads + */ + private Runnable classifyRunnable = new Runnable() { + public void run() { + synchronized (this) { + Bitmap bitmap = getBitmap(); + if (bitmap != null) { + long startTime = System.currentTimeMillis(); + // The current bitmap performs the sending request identification operation + String ret =""; + if (OPEN_TYPE_IMAGE == openType){ + ret = null == imageTrackingMobile ? "" : imageTrackingMobile.MindSpore_runnet(bitmap); + }else if(OPEN_TYPE_IMAGE_CUSTOM == openType){ + ret = null == garbageTrackingMobile ? "" : garbageTrackingMobile.MindSpore_runnet(bitmap); + }else if(OPEN_TYPE_OBJECT == openType){ + ret = null == objectTrackingMobile ? "" : objectTrackingMobile.MindSpore_runnet(bitmap); + } + long endTime = System.currentTimeMillis(); + if (mRecognitionDataCallBack != null) { + // Interface returns data。 + mRecognitionDataCallBack.onRecognitionDataCallBack(ret, (endTime - startTime) + "ms "); + } + if (!bitmap.isRecycled()) { + bitmap.recycle(); + } + } + if (mImageBackgroundHandler != null && !isPreBackgroundThreadPause) { + mImageBackgroundHandler.postDelayed(classifyRunnable,1000); + } + } + } + }; + + private void stopBackgroundThread() { + isPreBackgroundThreadPause = true; + mBackgroundThread.quitSafely(); + try { + mBackgroundThread.join(); + mBackgroundThread = null; + mBackgroundHandler = null; + + if (OPEN_TYPE_IMAGE == openType || OPEN_TYPE_IMAGE_CUSTOM == openType || OPEN_TYPE_OBJECT == openType) { + mImageBackgroundThread.quitSafely(); + mImageBackgroundThread.join(); + mImageBackgroundThread = null; + mImageBackgroundHandler = null; + } + + } catch (InterruptedException e) { + e.printStackTrace(); + } + } + + + /** + * Data interface returned after identification. + */ + public interface RecognitionDataCallBack { + /** + * Data interface returned after identification. + * + * @param result Recognition result + * @param time Response time + */ + void onRecognitionDataCallBack(String result, String time); + } + + + private RecognitionDataCallBack mRecognitionDataCallBack; + + public void addImageRecognitionDataCallBack(RecognitionDataCallBack recognitionDataCallBack) { + this.mRecognitionDataCallBack = recognitionDataCallBack; + } + + /** + * 根据mCameraId打开相机 + */ + private void openCamera(int width, int height) { + setUpCameraOutputs(width, height); + configureTransform(width, height); + CameraManager manager = (CameraManager) getContext().getSystemService(Context.CAMERA_SERVICE); + try { + if (!mCameraOpenCloseLock.tryAcquire(2500, TimeUnit.MILLISECONDS)) { + throw new RuntimeException("Time out waiting to lock camera opening."); + } + if (ActivityCompat.checkSelfPermission(activity, Manifest.permission.CAMERA) != PackageManager.PERMISSION_GRANTED) { + // TODO: Consider calling + // ActivityCompat#requestPermissions + return; + } + manager.openCamera(mCameraId, mStateCallback, mBackgroundHandler); + } catch (CameraAccessException e) { + e.printStackTrace(); + } catch (InterruptedException e) { + throw new RuntimeException("Interrupted while trying to lock camera opening.", e); + } + } + + /** + * 设置相机相关的属性或变量 + * + * @param width 相机预览的可用尺寸的宽度 + * @param height 相机预览的可用尺寸的高度 + */ + @SuppressWarnings("SuspiciousNameCombination") + private void setUpCameraOutputs(int width, int height) { + CameraManager manager = (CameraManager) getContext().getSystemService(Context.CAMERA_SERVICE); + try { + for (String cameraId : manager.getCameraIdList()) { + CameraCharacteristics characteristics = manager.getCameraCharacteristics(cameraId); + // 在这个例子中不使用前置摄像头 + Integer facing = characteristics.get(CameraCharacteristics.LENS_FACING); + if (facing != null && facing == CameraCharacteristics.LENS_FACING_FRONT) { + continue; + } + StreamConfigurationMap map = characteristics.get(CameraCharacteristics.SCALER_STREAM_CONFIGURATION_MAP); + if (map == null) { + continue; + } + + Size largest = Collections.max(Arrays.asList(map.getOutputSizes(ImageFormat.JPEG)), + new CompareSizesByArea()); + mImageReader = ImageReader.newInstance(largest.getWidth(), largest.getHeight(), + ImageFormat.JPEG, /*maxImages*/2); + mImageReader.setOnImageAvailableListener( + mOnImageAvailableListener, mBackgroundHandler); + + int displayRotation = activity.getWindowManager().getDefaultDisplay().getRotation(); + // noinspection ConstantConditions + mSensorOrientation = characteristics.get(CameraCharacteristics.SENSOR_ORIENTATION); + boolean swappedDimensions = false; + switch (displayRotation) { + case Surface.ROTATION_0: + case Surface.ROTATION_180: + if (mSensorOrientation == 90 || mSensorOrientation == 270) { + swappedDimensions = true; + } + break; + case Surface.ROTATION_90: + case Surface.ROTATION_270: + if (mSensorOrientation == 0 || mSensorOrientation == 180) { + swappedDimensions = true; + } + break; + default: + Log.e(TAG, "Display rotation is invalid: " + displayRotation); + } + + Point displaySize = new Point(); + activity.getWindowManager().getDefaultDisplay().getSize(displaySize); + int rotatedPreviewWidth = width; + int rotatedPreviewHeight = height; + int maxPreviewWidth = displaySize.x; + int maxPreviewHeight = displaySize.y; + + if (swappedDimensions) { + rotatedPreviewWidth = height; + rotatedPreviewHeight = width; + maxPreviewWidth = displaySize.y; + maxPreviewHeight = displaySize.x; + } + + if (maxPreviewWidth > MAX_PREVIEW_WIDTH) { + maxPreviewWidth = MAX_PREVIEW_WIDTH; + } + + if (maxPreviewHeight > MAX_PREVIEW_HEIGHT) { + maxPreviewHeight = MAX_PREVIEW_HEIGHT; + } + + mPreviewSize = chooseOptimalSize(map.getOutputSizes(SurfaceTexture.class), + rotatedPreviewWidth, rotatedPreviewHeight, maxPreviewWidth, + maxPreviewHeight, largest); + + int orientation = getResources().getConfiguration().orientation; + if (orientation == Configuration.ORIENTATION_LANDSCAPE) { + setAspectRatio(mPreviewSize.getWidth(), mPreviewSize.getHeight()); + } else { + setAspectRatio(mPreviewSize.getHeight(), mPreviewSize.getWidth()); + } + Boolean available = characteristics.get(CameraCharacteristics.FLASH_INFO_AVAILABLE); + mFlashSupported = available == null ? false : available; + + mCameraId = cameraId; + return; + } + } catch (CameraAccessException e) { + e.printStackTrace(); + } catch (NullPointerException e) { + Log.e(TAG, "设备不支持Camera2"); + } + } + + /** + * 在确定相机预览大小后应调用此方法 + * + * @param viewWidth 宽 + * @param viewHeight 高 + */ + private void configureTransform(int viewWidth, int viewHeight) { + if (null == mPreviewSize || null == activity) { + return; + } + int rotation = activity.getWindowManager().getDefaultDisplay().getRotation(); + Matrix matrix = new Matrix(); + RectF viewRect = new RectF(0, 0, viewWidth, viewHeight); + RectF bufferRect = new RectF(0, 0, mPreviewSize.getHeight(), mPreviewSize.getWidth()); + float centerX = viewRect.centerX(); + float centerY = viewRect.centerY(); + if (Surface.ROTATION_90 == rotation || Surface.ROTATION_270 == rotation) { + bufferRect.offset(centerX - bufferRect.centerX(), centerY - bufferRect.centerY()); + matrix.setRectToRect(viewRect, bufferRect, Matrix.ScaleToFit.FILL); + float scale = Math.max( + (float) viewHeight / mPreviewSize.getHeight(), + (float) viewWidth / mPreviewSize.getWidth()); + matrix.postScale(scale, scale, centerX, centerY); + matrix.postRotate(90 * (rotation - 2), centerX, centerY); + } else if (Surface.ROTATION_180 == rotation) { + matrix.postRotate(180, centerX, centerY); + } + this.setTransform(matrix); + } + + + /** + * 获取一个合适的相机预览尺寸 + * + * @param choices 支持的预览尺寸列表 + * @param textureViewWidth 相对宽度 + * @param textureViewHeight 相对高度 + * @param maxWidth 可以选择的最大宽度 + * @param maxHeight 可以选择的最大高度 + * @param aspectRatio 宽高比 + * @return 最佳预览尺寸 + */ + private static Size chooseOptimalSize(Size[] choices, int textureViewWidth, int textureViewHeight, + int maxWidth, int maxHeight, Size aspectRatio) { + List bigEnough = new ArrayList<>(); + List notBigEnough = new ArrayList<>(); + int w = aspectRatio.getWidth(); + int h = aspectRatio.getHeight(); + for (Size option : choices) { + if (option.getWidth() <= maxWidth && option.getHeight() <= maxHeight && + option.getHeight() == option.getWidth() * h / w) { + if (option.getWidth() >= textureViewWidth && + option.getHeight() >= textureViewHeight) { + bigEnough.add(option); + } else { + notBigEnough.add(option); + } + } + } + if (bigEnough.size() > 0) { + return Collections.min(bigEnough, new CompareSizesByArea()); + } else if (notBigEnough.size() > 0) { + return Collections.max(notBigEnough, new CompareSizesByArea()); + } else { + Log.e(TAG, "Couldn't find any suitable preview size"); + return choices[0]; + } + } + + /** + * 为相机预览创建新的CameraCaptureSession + */ + private void createCameraPreviewSession() { + try { + SurfaceTexture texture = this.getSurfaceTexture(); + assert texture != null; + // 将默认缓冲区的大小配置为想要的相机预览的大小 + texture.setDefaultBufferSize(mPreviewSize.getWidth(), mPreviewSize.getHeight()); + Surface surface = new Surface(texture); + mPreviewRequestBuilder = mCameraDevice.createCaptureRequest(CameraDevice.TEMPLATE_PREVIEW); + mPreviewRequestBuilder.addTarget(surface); + // 我们创建一个 CameraCaptureSession 来进行相机预览 + mCameraDevice.createCaptureSession(Arrays.asList(surface, mImageReader.getSurface()), + new CameraCaptureSession.StateCallback() { + + @Override + public void onConfigured(@NonNull CameraCaptureSession cameraCaptureSession) { + if (null == mCameraDevice) { + return; + } + // 会话准备好后,我们开始显示预览 + mCaptureSession = cameraCaptureSession; + try { + mPreviewRequestBuilder.set(CaptureRequest.CONTROL_AF_MODE, + CaptureRequest.CONTROL_AF_MODE_CONTINUOUS_PICTURE); + setAutoFlash(mPreviewRequestBuilder); + mPreviewRequest = mPreviewRequestBuilder.build(); + mCaptureSession.setRepeatingRequest(mPreviewRequest, mCaptureCallback, mBackgroundHandler); + } catch (CameraAccessException e) { + e.printStackTrace(); + } + } + + @Override + public void onConfigureFailed(@NonNull CameraCaptureSession cameraCaptureSession) { + } + }, null); + } catch (CameraAccessException e) { + e.printStackTrace(); + } + } + + + /** + * 从指定的屏幕旋转中检索照片方向 + * + * @param rotation 屏幕方向 + * @return 照片方向(0,90,270,360) + */ + private int getOrientation(int rotation) { + return (ORIENTATIONS.get(rotation) + mSensorOrientation + 270) % 360; + } + + /** + * 锁定焦点 + */ + private void lockFocus() { + try { + // 如何通知相机锁定焦点 + mPreviewRequestBuilder.set(CaptureRequest.CONTROL_AF_TRIGGER, CameraMetadata.CONTROL_AF_TRIGGER_START); + // 通知mCaptureCallback等待锁定 + mState = STATE_WAITING_LOCK; + mCaptureSession.capture(mPreviewRequestBuilder.build(), mCaptureCallback, mBackgroundHandler); + } catch (CameraAccessException e) { + e.printStackTrace(); + } + } + + /** + * 解锁焦点 + */ + private void unlockFocus() { + try { + mPreviewRequestBuilder.set(CaptureRequest.CONTROL_AF_TRIGGER, + CameraMetadata.CONTROL_AF_TRIGGER_CANCEL); + setAutoFlash(mPreviewRequestBuilder); + mCaptureSession.capture(mPreviewRequestBuilder.build(), mCaptureCallback, + mBackgroundHandler); + mState = STATE_PREVIEW; + mCaptureSession.setRepeatingRequest(mPreviewRequest, mCaptureCallback, + mBackgroundHandler); + } catch (CameraAccessException e) { + e.printStackTrace(); + } + } + + /** + * 拍摄静态图片 + */ + private void captureStillPicture() { + try { + if (null == activity || null == mCameraDevice) { + return; + } + final CaptureRequest.Builder captureBuilder = + mCameraDevice.createCaptureRequest(CameraDevice.TEMPLATE_STILL_CAPTURE); + captureBuilder.addTarget(mImageReader.getSurface()); + captureBuilder.set(CaptureRequest.CONTROL_AF_MODE, + CaptureRequest.CONTROL_AF_MODE_CONTINUOUS_PICTURE); + setAutoFlash(captureBuilder); + // 方向 + int rotation = activity.getWindowManager().getDefaultDisplay().getRotation(); + captureBuilder.set(CaptureRequest.JPEG_ORIENTATION, getOrientation(rotation)); + CameraCaptureSession.CaptureCallback captureCallback + = new CameraCaptureSession.CaptureCallback() { + + @Override + public void onCaptureCompleted(@NonNull CameraCaptureSession session, + @NonNull CaptureRequest request, + @NonNull TotalCaptureResult result) { + Toast.makeText(getContext(), "Saved: " + mFile, Toast.LENGTH_SHORT).show(); + Log.d(TAG, mFile.toString()); + unlockFocus(); + } + }; + mCaptureSession.stopRepeating(); + mCaptureSession.abortCaptures(); + mCaptureSession.capture(captureBuilder.build(), captureCallback, null); + } catch (CameraAccessException e) { + e.printStackTrace(); + } + } + + public void setAutoFlash(CaptureRequest.Builder requestBuilder) { + if (mFlashSupported) { + requestBuilder.set(CaptureRequest.CONTROL_AE_MODE, + CaptureRequest.CONTROL_AE_MODE_ON_AUTO_FLASH); + } + } + + public void takePicture() { +// SimpleDateFormat simpleDateFormat = new SimpleDateFormat("yyyyMMddHHmmss"); +// String date = simpleDateFormat.format(new Date()); + mFile = new File(getContext().getExternalFilesDir(null), "temp.jpg"); + if (mFile.length() > 0) { + mFile.delete(); + } + lockFocus(); + } + + /** + * 运行preCapture序列来捕获静止图像 + */ + private void runPreCaptureSequence() { + try { + // 设置拍照参数请求 + mPreviewRequestBuilder.set(CaptureRequest.CONTROL_AE_PRECAPTURE_TRIGGER, + CaptureRequest.CONTROL_AE_PRECAPTURE_TRIGGER_START); + mState = STATE_WAITING_PRE_CAPTURE; + mCaptureSession.capture(mPreviewRequestBuilder.build(), mCaptureCallback, mBackgroundHandler); + } catch (CameraAccessException e) { + e.printStackTrace(); + } + } + + /** + * 比较两者大小 + */ + private static class CompareSizesByArea implements Comparator { + + @Override + public int compare(Size lhs, Size rhs) { + return Long.signum((long) lhs.getWidth() * lhs.getHeight() - + (long) rhs.getWidth() * rhs.getHeight()); + } + } + + /** + * ImageReader的回调对象 + */ + private final ImageReader.OnImageAvailableListener mOnImageAvailableListener + = new ImageReader.OnImageAvailableListener() { + + @Override + public void onImageAvailable(ImageReader reader) { + mBackgroundHandler.post(new ImageSaver(reader.acquireNextImage(), mFile, iCameraDataCallBack)); + } + }; + + /** + * 将捕获到的图像保存到指定的文件中 + */ + private static class ImageSaver implements Runnable { + + private final Image mImage; + private final File mFile; + private final ICameraDataCallBack mICameraDataCallBack; + + ImageSaver(Image image, File file, ICameraDataCallBack iCameraDataCallBack) { + mImage = image; + mFile = file; + mICameraDataCallBack = iCameraDataCallBack; + } + + @Override + public void run() { + if (mFile.length() > 0) { + mFile.delete(); + } + ByteBuffer buffer = mImage.getPlanes()[0].getBuffer(); + byte[] bytes = new byte[buffer.remaining()]; + buffer.get(bytes); + FileOutputStream output = null; + try { + output = new FileOutputStream(mFile); + output.write(bytes); + } catch (IOException e) { + e.printStackTrace(); + } finally { + mImage.close(); + if (null != output) { + try { + output.close(); + } catch (IOException e) { + e.printStackTrace(); + } + } + if (mICameraDataCallBack != null) { + mICameraDataCallBack.takeCameraDataCallBack(mFile); + } + } + } + } + + /** + * 关闭相机 + */ + private void closeCamera() { + try { + mCameraOpenCloseLock.acquire(); + if (null != mCaptureSession) { + mCaptureSession.close(); + mCaptureSession = null; + } + if (null != mCameraDevice) { + mCameraDevice.close(); + mCameraDevice = null; + } + if (null != mImageReader) { + mImageReader.close(); + mImageReader = null; + } + } catch (InterruptedException e) { + throw new RuntimeException("Interrupted while trying to lock camera closing.", e); + } finally { + mCameraOpenCloseLock.release(); + } + } + + public interface ICameraDataCallBack { + void takeCameraDataCallBack(File file); + } + + public void addCameraDataCallBack(ICameraDataCallBack iCameraDataCallBack) { + this.iCameraDataCallBack = iCameraDataCallBack; + } +} diff --git a/model_zoo/official/lite/Himindspore/app/src/main/java/com/mindspore/himindspore/contract/ContractActivity.java b/model_zoo/official/lite/Himindspore/app/src/main/java/com/mindspore/himindspore/contract/ContractActivity.java new file mode 100644 index 0000000000..ca45154ed6 --- /dev/null +++ b/model_zoo/official/lite/Himindspore/app/src/main/java/com/mindspore/himindspore/contract/ContractActivity.java @@ -0,0 +1,55 @@ +package com.mindspore.himindspore.contract; + +import android.os.Bundle; +import android.text.TextUtils; +import android.view.View; +import android.widget.Button; +import android.widget.EditText; +import android.widget.Toast; + +import androidx.appcompat.app.AppCompatActivity; + +import com.mindspore.himindspore.R; + +import java.util.regex.Matcher; +import java.util.regex.Pattern; + +public class ContractActivity extends AppCompatActivity implements View.OnClickListener { + + private EditText emailEdit; + private Button submitBtn; + + @Override + protected void onCreate(Bundle savedInstanceState) { + super.onCreate(savedInstanceState); + setContentView(R.layout.activity_contract); + + emailEdit = findViewById(R.id.emailEditText); + submitBtn = findViewById(R.id.submitBtn); + submitBtn.setOnClickListener(this); + } + + @Override + public void onClick(View view) { + + if (R.id.submitBtn == view.getId()) { + String email = emailEdit.getText().toString(); + if (TextUtils.isEmpty(email)) { + Toast.makeText(ContractActivity.this,"Please input your email!",Toast.LENGTH_LONG).show(); + return; + } + if (isEmailFormat(email)){ + + }else{ + Toast.makeText(ContractActivity.this,"The email address you enterd is not in the correct format",Toast.LENGTH_LONG).show(); + return; + } + } + } + + private boolean isEmailFormat(String emailAdd) { + Pattern p = Pattern.compile("^([a-zA-Z0-9_-])+@([a-zA-Z0-9_-])+(\\.([a-zA-Z0-9_-])+)+$"); + Matcher m = p.matcher(emailAdd); + return m.matches(); + } +} \ No newline at end of file diff --git a/model_zoo/official/lite/Himindspore/app/src/main/java/com/mindspore/himindspore/contract/email/MailInfo.java b/model_zoo/official/lite/Himindspore/app/src/main/java/com/mindspore/himindspore/contract/email/MailInfo.java new file mode 100644 index 0000000000..08ea827270 --- /dev/null +++ b/model_zoo/official/lite/Himindspore/app/src/main/java/com/mindspore/himindspore/contract/email/MailInfo.java @@ -0,0 +1,109 @@ +package com.mindspore.himindspore.contract.email; + +import java.util.Properties; + +public class MailInfo { + + private String mailServerHost;// 发送邮件的服务器的IP + private String mailServerPort;// 发送邮件的服务器的端口 + private String fromAddress;// 邮件发送者的地址 + private String toAddress; // 邮件接收者的地址 + private String userName;// 登陆邮件发送服务器的用户名 + private String password;// 登陆邮件发送服务器的密码 + private boolean validate = true;// 是否需要身份验证 + private String subject;// 邮件主题 + private String content;// 邮件的文本内容 + private String[] attachFileNames;// 邮件附件的文件名 + + /** + * 获得邮件会话属性 + */ + public Properties getProperties() { + Properties p = new Properties(); + p.put("mail.smtp.host", this.mailServerHost); + p.put("mail.smtp.port", this.mailServerPort); + p.put("mail.smtp.auth", validate ? "true" : "false"); + return p; + } + + public String getMailServerHost() { + return mailServerHost; + } + + public void setMailServerHost(String mailServerHost) { + this.mailServerHost = mailServerHost; + } + + public String getMailServerPort() { + return mailServerPort; + } + + public void setMailServerPort(String mailServerPort) { + this.mailServerPort = mailServerPort; + } + + public boolean isValidate() { + return validate; + } + + public void setValidate(boolean validate) { + this.validate = validate; + } + + public String[] getAttachFileNames() { + return attachFileNames; + } + + public void setAttachFileNames(String[] fileNames) { + this.attachFileNames = fileNames; + } + + public String getFromAddress() { + return fromAddress; + } + + public void setFromAddress(String fromAddress) { + this.fromAddress = fromAddress; + } + + public String getPassword() { + return password; + } + + public void setPassword(String password) { + this.password = password; + } + + public String getToAddress() { + return toAddress; + } + + public void setToAddress(String toAddress) { + this.toAddress = toAddress; + } + + public String getUserName() { + return userName; + } + + public void setUserName(String userName) { + this.userName = userName; + } + + public String getSubject() { + return subject; + } + + public void setSubject(String subject) { + this.subject = subject; + } + + public String getContent() { + return content; + } + + public void setContent(String textContent) { + this.content = textContent; + } + +} \ No newline at end of file diff --git a/model_zoo/official/lite/Himindspore/app/src/main/java/com/mindspore/himindspore/contract/email/MailSender.java b/model_zoo/official/lite/Himindspore/app/src/main/java/com/mindspore/himindspore/contract/email/MailSender.java new file mode 100644 index 0000000000..bf171edfd0 --- /dev/null +++ b/model_zoo/official/lite/Himindspore/app/src/main/java/com/mindspore/himindspore/contract/email/MailSender.java @@ -0,0 +1,202 @@ +package com.mindspore.himindspore.contract.email; + +import android.util.Log; + +import java.io.File; +import java.util.Date; +import java.util.Properties; + +import javax.activation.DataHandler; +import javax.activation.FileDataSource; +import javax.mail.Address; +import javax.mail.Authenticator; +import javax.mail.BodyPart; +import javax.mail.Message; +import javax.mail.MessagingException; +import javax.mail.Multipart; +import javax.mail.PasswordAuthentication; +import javax.mail.Session; +import javax.mail.Transport; +import javax.mail.internet.InternetAddress; +import javax.mail.internet.MimeBodyPart; +import javax.mail.internet.MimeMessage; +import javax.mail.internet.MimeMultipart; +import javax.mail.internet.MimeUtility; + +public class MailSender { + /** + * 以文本格式发送邮件 + * + * @param mailInfo 待发送的邮件的信息 + */ + public boolean sendTextMail(final MailInfo mailInfo) { + + // 判断是否需要身份认证 + MyAuthenticator authenticator = null; + Properties pro = mailInfo.getProperties(); + if (mailInfo.isValidate()) { + // 如果需要身份认证,则创建一个密码验证器 + authenticator = new MyAuthenticator(mailInfo.getUserName(), mailInfo.getPassword()); + } + // 根据邮件会话属性和密码验证器构造一个发送邮件的session + Session sendMailSession = Session.getDefaultInstance(pro, authenticator); + +// Session sendMailSession = Session.getInstance(pro, new Authenticator() { +// @Override +// protected PasswordAuthentication getPasswordAuthentication() { +// return new PasswordAuthentication(mailInfo.getUserName(),mailInfo.getPassword()); +// } +// }); + + try { + // 根据session创建一个邮件消息 + Message mailMessage = new MimeMessage(sendMailSession); + // 创建邮件发送者地址 + Address from = new InternetAddress(mailInfo.getFromAddress()); + // 设置邮件消息的发送者 + mailMessage.setFrom(from); + // 创建邮件的接收者地址,并设置到邮件消息中 + Address to = new InternetAddress(mailInfo.getToAddress()); + mailMessage.setRecipient(Message.RecipientType.TO, to); + // 设置邮件消息的主题 + mailMessage.setSubject(mailInfo.getSubject()); + // 设置邮件消息发送的时间 + mailMessage.setSentDate(new Date()); + + // 设置邮件消息的主要内容 + String mailContent = mailInfo.getContent(); + mailMessage.setText(mailContent); + // 发送邮件 + Transport.send(mailMessage); + return true; + } catch (MessagingException ex) { + ex.printStackTrace(); + } + return false; + } + + /** + * 以HTML格式发送邮件 + * + * @param mailInfo 待发送的邮件信息 + */ + public static boolean sendHtmlMail(MailInfo mailInfo) { + // 判断是否需要身份认证 + MyAuthenticator authenticator = null; + Properties pro = mailInfo.getProperties(); + // 如果需要身份认证,则创建一个密码验证器 + if (mailInfo.isValidate()) { + authenticator = new MyAuthenticator(mailInfo.getUserName(), mailInfo.getPassword()); + } + // 根据邮件会话属性和密码验证器构造一个发送邮件的session + Session sendMailSession = Session.getDefaultInstance(pro, authenticator); + try { + // 根据session创建一个邮件消息 + Message mailMessage = new MimeMessage(sendMailSession); + // 创建邮件发送者地址 + Address from = new InternetAddress(mailInfo.getFromAddress()); + // 设置邮件消息的发送者 + mailMessage.setFrom(from); + // 创建邮件的接收者地址,并设置到邮件消息中 + Address to = new InternetAddress(mailInfo.getToAddress()); + // Message.RecipientType.TO属性表示接收者的类型为TO + mailMessage.setRecipient(Message.RecipientType.TO, to); + // 设置邮件消息的主题 + mailMessage.setSubject(mailInfo.getSubject()); + // 设置邮件消息发送的时间 + mailMessage.setSentDate(new Date()); + // MiniMultipart类是一个容器类,包含MimeBodyPart类型的对象 + Multipart mainPart = new MimeMultipart(); + // 创建一个包含HTML内容的MimeBodyPart + BodyPart html = new MimeBodyPart(); + // 设置HTML内容 + html.setContent(mailInfo.getContent(), "text/html; charset=utf-8"); + mainPart.addBodyPart(html); + // 将MiniMultipart对象设置为邮件内容 + mailMessage.setContent(mainPart); + // 发送邮件 + Transport.send(mailMessage); + return true; + } catch (MessagingException ex) { + ex.printStackTrace(); + } + return false; + } + + + /** + * 发送带附件的邮件 + * + * @param info + * @return + */ + public boolean sendFileMail(MailInfo info, File file) { + Message attachmentMail = createAttachmentMail(info, file); + try { + Transport.send(attachmentMail); + return true; + } catch (MessagingException e) { + e.printStackTrace(); + return false; + } + + } + + /** + * 创建带有附件的邮件 + * + * @return + */ + private Message createAttachmentMail(final MailInfo info, File file) { + //创建邮件 + MimeMessage message = null; + Properties pro = info.getProperties(); + try { + + Session sendMailSession = Session.getInstance(pro, new Authenticator() { + @Override + protected PasswordAuthentication getPasswordAuthentication() { + return new PasswordAuthentication(info.getUserName(), info.getPassword()); + } + }); + + message = new MimeMessage(sendMailSession); + // 设置邮件的基本信息 + //创建邮件发送者地址 + Address from = new InternetAddress(info.getFromAddress()); + //设置邮件消息的发送者 + message.setFrom(from); + //创建邮件的接受者地址,并设置到邮件消息中 + Address to = new InternetAddress(info.getToAddress()); + //设置邮件消息的接受者, Message.RecipientType.TO属性表示接收者的类型为TO + message.setRecipient(Message.RecipientType.TO, to); + //邮件标题 + message.setSubject(info.getSubject()); + + // 创建邮件正文,为了避免邮件正文中文乱码问题,需要使用CharSet=UTF-8指明字符编码 + MimeBodyPart text = new MimeBodyPart(); + text.setContent(info.getContent(), "text/html;charset=UTF-8"); + + // 创建容器描述数据关系 + MimeMultipart mp = new MimeMultipart(); + mp.addBodyPart(text); + // 创建邮件附件 + MimeBodyPart attach = new MimeBodyPart(); + + FileDataSource ds = new FileDataSource(file); + DataHandler dh = new DataHandler(ds); + attach.setDataHandler(dh); + attach.setFileName(MimeUtility.encodeText(dh.getName())); + mp.addBodyPart(attach); + mp.setSubType("mixed"); + message.setContent(mp); + message.saveChanges(); + + } catch (Exception e) { + Log.i("TAG","创建带附件的邮件失败"); + e.printStackTrace(); + } + // 返回生成的邮件 + return message; + } +} diff --git a/model_zoo/official/lite/Himindspore/app/src/main/java/com/mindspore/himindspore/contract/email/MyAuthenticator.java b/model_zoo/official/lite/Himindspore/app/src/main/java/com/mindspore/himindspore/contract/email/MyAuthenticator.java new file mode 100644 index 0000000000..05d14eeb28 --- /dev/null +++ b/model_zoo/official/lite/Himindspore/app/src/main/java/com/mindspore/himindspore/contract/email/MyAuthenticator.java @@ -0,0 +1,21 @@ +package com.mindspore.himindspore.contract.email; + +import javax.mail.Authenticator; +import javax.mail.PasswordAuthentication; + +public class MyAuthenticator extends Authenticator { + String userName = null; + String password = null; + + public MyAuthenticator() { + } + + public MyAuthenticator(String username, String password) { + this.userName = username; + this.password = password; + } + + protected PasswordAuthentication getPasswordAuthentication() { + return new PasswordAuthentication(userName, password); + } +} diff --git a/model_zoo/official/lite/Himindspore/app/src/main/java/com/mindspore/himindspore/contract/email/SendMailUtil.java b/model_zoo/official/lite/Himindspore/app/src/main/java/com/mindspore/himindspore/contract/email/SendMailUtil.java new file mode 100644 index 0000000000..78b03224d6 --- /dev/null +++ b/model_zoo/official/lite/Himindspore/app/src/main/java/com/mindspore/himindspore/contract/email/SendMailUtil.java @@ -0,0 +1,51 @@ +package com.mindspore.himindspore.contract.email; + +import androidx.annotation.NonNull; + +import java.io.File; + +public class SendMailUtil { + + public static void send(final File file, String toAdd) { + final MailInfo mailInfo = creatMail(toAdd); + final MailSender sms = new MailSender(); + new Thread(new Runnable() { + @Override + public void run() { + sms.sendFileMail(mailInfo, file); + } + }).start(); +} + + public static void send(String toAdd) { + final MailInfo mailInfo = creatMail(toAdd); + final MailSender sms = new MailSender(); + new Thread(new Runnable() { + @Override + public void run() { + sms.sendTextMail(mailInfo); + } + }).start(); + } + + @NonNull + private static MailInfo creatMail(String toAdd) { + +// String HOST = ShareUtils.getString(MyApplication.getInstance(), "HOST", ""); +// String PORT = ShareUtils.getString(MyApplication.getInstance(), "PORT", ""); +// String FROM_ADD = ShareUtils.getString(MyApplication.getInstance(), "FROM_ADD", ""); +// String FROM_PSW = ShareUtils.getString(MyApplication.getInstance(), "FROM_PSW", ""); + final MailInfo mailInfo = new MailInfo(); +// mailInfo.setMailServerHost(HOST);//发送方邮箱服务器 +// mailInfo.setMailServerPort(PORT);//发送方邮箱端口号 +// mailInfo.setValidate(true); +// mailInfo.setUserName(FROM_ADD); // 发送者邮箱地址 +// mailInfo.setPassword(FROM_PSW);// 发送者邮箱授权码 +// mailInfo.setFromAddress(FROM_ADD); // 发送者邮箱 +// mailInfo.setToAddress(toAdd); // 接收者邮箱 +// mailInfo.setSubject("Android应用测试"); // 邮件主题 +// mailInfo.setContent("哈哈"); // 邮件文本 + return mailInfo; + } + +} \ No newline at end of file diff --git a/model_zoo/official/lite/Himindspore/app/src/main/java/com/mindspore/himindspore/imageclassification/bean/RecognitionImageBean.java b/model_zoo/official/lite/Himindspore/app/src/main/java/com/mindspore/himindspore/imageclassification/bean/RecognitionImageBean.java new file mode 100644 index 0000000000..aac0c80cba --- /dev/null +++ b/model_zoo/official/lite/Himindspore/app/src/main/java/com/mindspore/himindspore/imageclassification/bean/RecognitionImageBean.java @@ -0,0 +1,46 @@ +/** + * Copyright 2020 Huawei Technologies Co., Ltd + * + * 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.mindspore.himindspore.imageclassification.bean; + +public class RecognitionImageBean { + + private String name; + private float score; + + public RecognitionImageBean(String name, float score) { + this.name = name; + this.score = score; + } + + public String getName() { + return name; + } + + public void setName(String name) { + this.name = name; + } + + public float getScore() { + return score; + } + + public void setScore(float score) { + this.score = score; + } + + +} diff --git a/model_zoo/official/lite/Himindspore/app/src/main/java/com/mindspore/himindspore/imageclassification/help/GarbageTrackingMobile.java b/model_zoo/official/lite/Himindspore/app/src/main/java/com/mindspore/himindspore/imageclassification/help/GarbageTrackingMobile.java new file mode 100644 index 0000000000..c6041ef24a --- /dev/null +++ b/model_zoo/official/lite/Himindspore/app/src/main/java/com/mindspore/himindspore/imageclassification/help/GarbageTrackingMobile.java @@ -0,0 +1,130 @@ +/** + * Copyright 2020 Huawei Technologies Co., Ltd + * + * 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.mindspore.himindspore.imageclassification.help; + +import android.content.Context; +import android.content.res.AssetManager; +import android.graphics.Bitmap; +import android.util.Log; + +import com.mindspore.himindspore.track.TrackListener; + +import java.io.InputStream; +import java.nio.ByteBuffer; + +/** + * Call the MindSpore interface API in the Java layer. + */ +public class GarbageTrackingMobile implements TrackListener { + private final static String TAG = "GarbageTrackingMobile"; + + static { + try { + System.loadLibrary("mlkit-label-MS"); + Log.i(TAG, "load libiMindSpore.so successfully."); + } catch (UnsatisfiedLinkError e) { + Log.e(TAG, "UnsatisfiedLinkError " + e.getMessage()); + } + } + + // The address of the running inference environment. + private long netEnv = 0; + + private final Context mActivity; + + public GarbageTrackingMobile(Context activity) { + this.mActivity = activity; + } + + /** + * JNI load model and also create model inference environment. + * + * @param modelBuffer Model buffer. + * @param numThread The num of thread. + * @return MindSpore Inference environment address. + */ + public native long loadModel(ByteBuffer modelBuffer, int numThread); + + /** + * Running model. + * + * @param netEnv Inference environment address. + * @param img A picture to be inferred. + * @return Inference result + */ + public native String runNet(long netEnv, Bitmap img); + + /** + * Unbind model data. + * + * @param netEnv Inference environment address. + * @return Unbound state. + */ + public native boolean unloadModel(long netEnv); + + /** + * The C++ side is encapsulated into a method of the MSNetWorks class + * + * @param modelPath Model file location + * @return Load model file status + */ + public boolean loadModelFromBuf(String modelPath) { + ByteBuffer buffer = loadModelFile(modelPath); + netEnv = loadModel(buffer, 2); //numThread's default setting is 2. + if (netEnv == 0){ // Loading model failed. + return false; + } + + return true; + } + + /** + * Run MindSpore inference. + */ + public String MindSpore_runnet(Bitmap img) { + String ret_str = runNet(netEnv, img); + return ret_str; + } + + /** + * Unload model. + * @return true + */ + public boolean unloadModel() { + unloadModel(netEnv); + return true; + } + + /** + * Load model file stream. + * @param modelPath Model file path. + * @return Model ByteBuffer. + */ + public ByteBuffer loadModelFile(String modelPath) { + InputStream is = null; + try { + is = mActivity.getAssets().open(modelPath); + byte[] bytes = new byte[is.available()]; + is.read(bytes); + return ByteBuffer.allocateDirect(bytes.length).put(bytes); + } catch (Exception e) { + Log.d("loadModelFile", " Exception occur. "); + Log.e(TAG, Log.getStackTraceString(e)); + } + return null; + } +} diff --git a/model_zoo/official/lite/Himindspore/app/src/main/java/com/mindspore/himindspore/imageclassification/help/ImageTrackingMobile.java b/model_zoo/official/lite/Himindspore/app/src/main/java/com/mindspore/himindspore/imageclassification/help/ImageTrackingMobile.java new file mode 100644 index 0000000000..505d3a0fd1 --- /dev/null +++ b/model_zoo/official/lite/Himindspore/app/src/main/java/com/mindspore/himindspore/imageclassification/help/ImageTrackingMobile.java @@ -0,0 +1,129 @@ +/** + * Copyright 2020 Huawei Technologies Co., Ltd + * + * 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.mindspore.himindspore.imageclassification.help; + +import android.content.Context; +import android.graphics.Bitmap; +import android.util.Log; + +import com.mindspore.himindspore.track.TrackListener; + +import java.io.InputStream; +import java.nio.ByteBuffer; + +/** + * Call the MindSpore interface API in the Java layer. + */ +public class ImageTrackingMobile implements TrackListener { + private final static String TAG = "ImageTrackingMobile"; + + static { + try { + System.loadLibrary("mlkit-label-MS"); + Log.i(TAG, "load libiMindSpore.so successfully."); + } catch (UnsatisfiedLinkError e) { + Log.e(TAG, "UnsatisfiedLinkError " + e.getMessage()); + } + } + + // The address of the running inference environment. + private long netEnv = 0; + + private final Context mActivity; + + public ImageTrackingMobile(Context activity) { + this.mActivity = activity; + } + + /** + * JNI load model and also create model inference environment. + * + * @param modelBuffer Model buffer. + * @param numThread The num of thread. + * @return MindSpore Inference environment address. + */ + public native long loadModel(ByteBuffer modelBuffer, int numThread); + + /** + * Running model. + * + * @param netEnv Inference environment address. + * @param img A picture to be inferred. + * @return Inference result + */ + public native String runNet(long netEnv, Bitmap img); + + /** + * Unbind model data. + * + * @param netEnv Inference environment address. + * @return Unbound state. + */ + public native boolean unloadModel(long netEnv); + + /** + * The C++ side is encapsulated into a method of the MSNetWorks class + * + * @param modelPath Model file location + * @return Load model file status + */ + public boolean loadModelFromBuf(String modelPath) { + ByteBuffer buffer = loadModelFile(modelPath); + netEnv = loadModel(buffer, 2); //numThread's default setting is 2. + if (netEnv == 0){ // Loading model failed. + return false; + } + + return true; + } + + /** + * Run MindSpore inference. + */ + public String MindSpore_runnet(Bitmap img) { + String ret_str = runNet(netEnv, img); + return ret_str; + } + + /** + * Unload model. + * @return true + */ + public boolean unloadModel() { + unloadModel(netEnv); + return true; + } + + /** + * Load model file stream. + * @param modelPath Model file path. + * @return Model ByteBuffer. + */ + public ByteBuffer loadModelFile(String modelPath) { + InputStream is = null; + try { + is = mActivity.getAssets().open(modelPath); + byte[] bytes = new byte[is.available()]; + is.read(bytes); + return ByteBuffer.allocateDirect(bytes.length).put(bytes); + } catch (Exception e) { + Log.d("loadModelFile", " Exception occur. "); + Log.e(TAG, Log.getStackTraceString(e)); + } + return null; + } +} diff --git a/model_zoo/official/lite/Himindspore/app/src/main/java/com/mindspore/himindspore/imageclassification/ui/HorTextView.java b/model_zoo/official/lite/Himindspore/app/src/main/java/com/mindspore/himindspore/imageclassification/ui/HorTextView.java new file mode 100644 index 0000000000..b115cb1943 --- /dev/null +++ b/model_zoo/official/lite/Himindspore/app/src/main/java/com/mindspore/himindspore/imageclassification/ui/HorTextView.java @@ -0,0 +1,73 @@ +/** + * Copyright 2020 Huawei Technologies Co., Ltd + * + * 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.mindspore.himindspore.imageclassification.ui; + +import android.content.Context; +import android.util.AttributeSet; +import android.view.LayoutInflater; +import android.view.View; +import android.widget.LinearLayout; +import android.widget.TextView; + +import androidx.annotation.Nullable; + +import com.mindspore.himindspore.R; + + +public class HorTextView extends LinearLayout { + private TextView tvLeftTitle; + + public TextView getTvRightContent() { + return tvRightContent; + } + + private TextView tvRightContent; + private View viewBottomLine; + + public HorTextView(Context context) { + this(context, null); + } + + public HorTextView(Context context, @Nullable AttributeSet attrs) { + this(context, attrs, 0); + } + + public HorTextView(Context context, @Nullable AttributeSet attrs, int defStyleAttr) { + super(context, attrs, defStyleAttr); + LayoutInflater.from(context).inflate(R.layout.layout_hor_text_view, this); + tvLeftTitle = findViewById(R.id.tv_left_title); + tvRightContent = findViewById(R.id.tv_right_content); + viewBottomLine = findViewById(R.id.view_bottom_line); + } + + + public void setLeftTitle(String title) { + tvLeftTitle.setText(title); + } + + public void setRightContent(String content) { + tvRightContent.setText(content); + } + + public void setBottomLineVisible(int isVisible) { + viewBottomLine.setVisibility(isVisible); + } + + public TextView getTvLeftTitle() { + return tvLeftTitle; + } +} diff --git a/model_zoo/official/lite/Himindspore/app/src/main/java/com/mindspore/himindspore/imageclassification/ui/ImageCameraActivity.java b/model_zoo/official/lite/Himindspore/app/src/main/java/com/mindspore/himindspore/imageclassification/ui/ImageCameraActivity.java new file mode 100644 index 0000000000..7b7175319a --- /dev/null +++ b/model_zoo/official/lite/Himindspore/app/src/main/java/com/mindspore/himindspore/imageclassification/ui/ImageCameraActivity.java @@ -0,0 +1,224 @@ +/** + * Copyright 2020 Huawei Technologies Co., Ltd + *

+ * 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.mindspore.himindspore.imageclassification.ui; + +import android.graphics.Color; +import android.os.Bundle; +import android.text.TextUtils; +import android.util.Log; +import android.view.Gravity; +import android.view.View; +import android.view.ViewGroup; +import android.widget.CompoundButton; +import android.widget.LinearLayout; +import android.widget.Switch; +import android.widget.TextView; + +import androidx.annotation.UiThread; +import androidx.appcompat.app.AppCompatActivity; + +import com.mindspore.himindspore.R; +import com.mindspore.himindspore.camera.CameraPreview; +import com.mindspore.himindspore.imageclassification.bean.RecognitionImageBean; +import com.mindspore.himindspore.imageclassification.help.GarbageTrackingMobile; +import com.mindspore.himindspore.imageclassification.help.ImageTrackingMobile; + +import java.util.ArrayList; +import java.util.Collections; +import java.util.Comparator; +import java.util.List; + +/** + * The main interface of camera preview. + * Using Camera 2 API. + */ +public class ImageCameraActivity extends AppCompatActivity implements CameraPreview.RecognitionDataCallBack { + private static final String TAG = "ImageCameraActivity"; + public static final String OPEN_TYPE = "OPEN_TYPE"; + public static final int TYPE_DEMO = 1; + public static final int TYPE_CUSTOM = 2; + + private int enterType; + + private LinearLayout bottomLayout; + + private List recognitionObjectBeanList; + + private CameraPreview cameraPreview; + + private ImageTrackingMobile mTrackingMobile; + + private GarbageTrackingMobile garbageTrackingMobile; + + @Override + protected void onCreate( Bundle savedInstanceState) { + super.onCreate(savedInstanceState); + setContentView(R.layout.activity_image_camera); + enterType = getIntent().getIntExtra(OPEN_TYPE,TYPE_DEMO); + + cameraPreview = findViewById(R.id.image_camera_preview); + bottomLayout = findViewById(R.id.layout_bottom_content); + cameraPreview.setVisibility(View.VISIBLE); + init(); + } + + private void init() { + if(enterType == TYPE_DEMO) { + mTrackingMobile = new ImageTrackingMobile(this); + String modelPath = "model/mobilenetv2.ms"; + boolean ret = mTrackingMobile.loadModelFromBuf(modelPath); + Log.d(TAG, "Loading model return value: " + ret); + }else { + garbageTrackingMobile = new GarbageTrackingMobile(this); + String garbageModelPath = "model/garbage_mobilenetv2.ms"; + boolean garbageRet = garbageTrackingMobile.loadModelFromBuf(garbageModelPath); + Log.d(TAG, "Garbage Loading model return value: " + garbageRet); + } + cameraPreview.addImageRecognitionDataCallBack(this); + } + + + @Override + protected void onResume() { + super.onResume(); + if(enterType == TYPE_DEMO) { + cameraPreview.onResume(this, CameraPreview.OPEN_TYPE_IMAGE, mTrackingMobile); + }else{ + cameraPreview.onResume(this, CameraPreview.OPEN_TYPE_IMAGE_CUSTOM, garbageTrackingMobile); + } + } + + @Override + protected void onPause() { + super.onPause(); + cameraPreview.onPause(); + } + + @Override + protected void onStop() { + super.onStop(); + if (mTrackingMobile != null) { + boolean ret = mTrackingMobile.unloadModel(); + Log.d(TAG, "Unload model return value: " + ret); + } + if (garbageTrackingMobile != null) { + boolean ret = garbageTrackingMobile.unloadModel(); + Log.d(TAG, "garbageTrackingMobile Unload model return value: " + ret); + } + } + + + @Override + public void onRecognitionDataCallBack(final String result, final String time) { + if(enterType == TYPE_DEMO) { + if (recognitionObjectBeanList != null) { + recognitionObjectBeanList.clear(); + } else { + recognitionObjectBeanList = new ArrayList<>(); + } + + if (!result.equals("")) { + String[] resultArray = result.split(";"); + for (String singleRecognitionResult : resultArray) { + String[] singleResult = singleRecognitionResult.split(":"); + float score = Float.parseFloat(singleResult[1]); + if (score > 0.5) { + recognitionObjectBeanList.add(new RecognitionImageBean(singleResult[0], score)); + } + } + Collections.sort(recognitionObjectBeanList, new Comparator() { + @Override + public int compare(RecognitionImageBean t1, RecognitionImageBean t2) { + return Float.compare(t2.getScore(), t1.getScore()); + } + }); + } + + runOnUiThread(new Runnable() { + @Override + public void run() { + showResultsInBottomSheet(recognitionObjectBeanList, time); + } + }); + } else { + runOnUiThread(new Runnable() { + @Override + public void run() { + showResultsInBottomSheetGarbage(result, time); + } + }); + } + } + + @UiThread + protected void showResultsInBottomSheet(List list, String time) { + bottomLayout.removeAllViews(); + if (list != null && list.size() > 0) { + int classNum = 0; + for (RecognitionImageBean bean : list) { + classNum++; + HorTextView horTextView = new HorTextView(this); + horTextView.setLeftTitle(bean.getName()); + horTextView.setRightContent(String.format("%.2f", (100 * bean.getScore())) + "%"); + horTextView.setBottomLineVisible(View.VISIBLE); + if (classNum == 1){ + horTextView.getTvLeftTitle().setTextColor(getResources().getColor(R.color.text_blue)); + horTextView.getTvRightContent().setTextColor(getResources().getColor(R.color.text_blue)); + }else{ + horTextView.getTvLeftTitle().setTextColor(getResources().getColor(R.color.white)); + horTextView.getTvRightContent().setTextColor(getResources().getColor(R.color.white)); + } + bottomLayout.addView(horTextView); + if (classNum > 4) { // set maximum display is 5. + break; + } + } + HorTextView horTextView = new HorTextView(this); + horTextView.setLeftTitle(getResources().getString(R.string.title_time)); + horTextView.setRightContent(time); + horTextView.setBottomLineVisible(View.INVISIBLE); + horTextView.getTvLeftTitle().setTextColor(getResources().getColor(R.color.text_blue)); + horTextView.getTvRightContent().setTextColor(getResources().getColor(R.color.text_blue)); + bottomLayout.addView(horTextView); + } else { + showLoadView(); + } + } + + @UiThread + protected void showResultsInBottomSheetGarbage(String result, String time) { + bottomLayout.removeAllViews(); + if (!TextUtils.isEmpty(result)) { + HorTextView horTextView = new HorTextView(this); + horTextView.setLeftTitle(result); + horTextView.setBottomLineVisible(View.VISIBLE); + bottomLayout.addView(horTextView); + } else { + showLoadView(); + } + } + + private void showLoadView(){ + TextView textView = new TextView(this); + textView.setLayoutParams(new LinearLayout.LayoutParams(ViewGroup.LayoutParams.MATCH_PARENT, ViewGroup.LayoutParams.WRAP_CONTENT)); + textView.setText("Keep moving."); + textView.setGravity(Gravity.CENTER); + textView.setTextColor(Color.WHITE); + textView.setTextSize(30); + bottomLayout.addView(textView); + } +} diff --git a/model_zoo/official/lite/Himindspore/app/src/main/java/com/mindspore/himindspore/imageclassification/ui/ImageMainActivity.java b/model_zoo/official/lite/Himindspore/app/src/main/java/com/mindspore/himindspore/imageclassification/ui/ImageMainActivity.java new file mode 100644 index 0000000000..802a826fdc --- /dev/null +++ b/model_zoo/official/lite/Himindspore/app/src/main/java/com/mindspore/himindspore/imageclassification/ui/ImageMainActivity.java @@ -0,0 +1,37 @@ +package com.mindspore.himindspore.imageclassification.ui; + +import androidx.appcompat.app.AppCompatActivity; + +import android.content.Intent; +import android.os.Bundle; +import android.view.View; + +import com.mindspore.himindspore.R; + +public class ImageMainActivity extends AppCompatActivity { + + @Override + protected void onCreate(Bundle savedInstanceState) { + super.onCreate(savedInstanceState); + setContentView(R.layout.activity_image_main); + + + findViewById(R.id.btn_demo).setOnClickListener(new View.OnClickListener() { + @Override + public void onClick(View view) { + Intent intent = new Intent(ImageMainActivity.this,ImageCameraActivity.class); + intent.putExtra(ImageCameraActivity.OPEN_TYPE,ImageCameraActivity.TYPE_DEMO); + startActivity(intent); + } + }); + + findViewById(R.id.btn_custom).setOnClickListener(new View.OnClickListener() { + @Override + public void onClick(View view) { + Intent intent = new Intent(ImageMainActivity.this,ImageCameraActivity.class); + intent.putExtra(ImageCameraActivity.OPEN_TYPE,ImageCameraActivity.TYPE_CUSTOM); + startActivity(intent); + } + }); + } +} \ No newline at end of file diff --git a/model_zoo/official/lite/Himindspore/app/src/main/java/com/mindspore/himindspore/objectdetection/bean/RecognitionObjectBean.java b/model_zoo/official/lite/Himindspore/app/src/main/java/com/mindspore/himindspore/objectdetection/bean/RecognitionObjectBean.java new file mode 100644 index 0000000000..77cb57b32c --- /dev/null +++ b/model_zoo/official/lite/Himindspore/app/src/main/java/com/mindspore/himindspore/objectdetection/bean/RecognitionObjectBean.java @@ -0,0 +1,159 @@ +package com.mindspore.himindspore.objectdetection.bean; + +import android.text.TextUtils; + +import java.util.ArrayList; +import java.util.List; + +public class RecognitionObjectBean { + private String rectID; + private String imgID; + private String objectName; + private float score; + private float left; + private float top; + private float right; + private float bottom; + + private RecognitionObjectBean(Builder builder){ + this.rectID = builder.rectID; + this.imgID = builder.imgID; + this.objectName = builder.objectName; + this.score = builder.score; + this.left = builder.left; + this.top = builder.top; + this.right = builder.right; + this.bottom = builder.bottom; + } + + + public static class Builder { + private String rectID; + private String imgID; + private String objectName; + private float score; + private float left; + private float top; + private float right; + private float bottom; + + public RecognitionObjectBean build(){ + return new RecognitionObjectBean(this); + } + + public Builder setRectID(String rectID) { + this.rectID = rectID; + return this; + } + + public Builder setImgID(String imgID) { + this.imgID = imgID; + return this; + } + + public Builder setObjectName(String objectName) { + this.objectName = objectName; + return this; + } + + public Builder setScore(float score) { + this.score = score; + return this; + } + + public Builder setLeft(float left) { + this.left = left; + return this; + } + + public Builder setTop(float top) { + this.top = top; + return this; + } + + public Builder setRight(float right) { + this.right = right; + return this; + } + + public Builder setBottom(float bottom) { + this.bottom = bottom; + return this; + } + } + + + public String getImgID() { + return imgID; + } + + public String getRectID() { + return rectID; + } + + public String getObjectName() { + return objectName; + } + + public float getScore() { + return score; + } + + public float getLeft() { + return left; + } + + public float getTop() { + return top; + } + + public float getRight() { + return right; + } + + public float getBottom() { + return bottom; + } + + + public static List getRecognitionList(String result) { + if (!TextUtils.isEmpty(result)) { + String[] resultArray = result.split(";"); + List list = new ArrayList<>(); + for (int i = 0; i < resultArray.length; i++) { + String singleRecognitionResult = resultArray[i]; + String[] singleResult = singleRecognitionResult.split("_"); + RecognitionObjectBean bean = new RecognitionObjectBean.Builder() + .setRectID(String.valueOf(i + 1)) + .setImgID(null != getData(0, singleResult) ? getData(0, singleResult) : "") + .setObjectName(null != getData(1, singleResult) ? getData(1, singleResult) : "") + .setScore(null != getData(2, singleResult) ? Float.parseFloat(getData(2, singleResult)) : 0) + .setLeft(null != getData(3, singleResult) ? Float.parseFloat(getData(3, singleResult)) : 0) + .setTop(null != getData(4, singleResult) ? Float.parseFloat(getData(4, singleResult)) : 0) + .setRight(null != getData(5, singleResult) ? Float.parseFloat(getData(5, singleResult)) : 0) + .setBottom(null != getData(6, singleResult) ? Float.parseFloat(getData(6, singleResult)) : 0) + .build(); + list.add(bean); + } + return list; + } else { + return null; + } + } + + /** + * @param index + * @param singleResult + * @return + */ + private static String getData(int index, String[] singleResult) { + if (index > singleResult.length) { + return null; + } else { + if (!TextUtils.isEmpty(singleResult[index])) { + return singleResult[index]; + } + } + return null; + } +} diff --git a/model_zoo/official/lite/Himindspore/app/src/main/java/com/mindspore/himindspore/objectdetection/help/ImageDegreeHelper.java b/model_zoo/official/lite/Himindspore/app/src/main/java/com/mindspore/himindspore/objectdetection/help/ImageDegreeHelper.java new file mode 100644 index 0000000000..67df17865e --- /dev/null +++ b/model_zoo/official/lite/Himindspore/app/src/main/java/com/mindspore/himindspore/objectdetection/help/ImageDegreeHelper.java @@ -0,0 +1,185 @@ +package com.mindspore.himindspore.objectdetection.help; + +import android.annotation.SuppressLint; +import android.content.ContentUris; +import android.content.Context; +import android.database.Cursor; +import android.graphics.Bitmap; +import android.graphics.Matrix; +import android.media.ExifInterface; +import android.net.Uri; +import android.os.Build; +import android.os.Environment; +import android.provider.DocumentsContract; +import android.provider.MediaStore; + +import java.io.IOException; + +public class ImageDegreeHelper { + /** + * 专为Android4.4及以上设计的从Uri获取文件绝对路径,以前的方法已不好使 + */ + @SuppressLint("NewApi") + public static String getPath(final Context context, final Uri uri) { + + final boolean isKitKat = Build.VERSION.SDK_INT >= Build.VERSION_CODES.KITKAT; + + // DocumentProvider + if (isKitKat && DocumentsContract.isDocumentUri(context, uri)) { + // ExternalStorageProvider + if (isExternalStorageDocument(uri)) { + final String docId = DocumentsContract.getDocumentId(uri); + final String[] split = docId.split(":"); + final String type = split[0]; + + if ("primary".equalsIgnoreCase(type)) { + return Environment.getExternalStorageDirectory() + "/" + split[1]; + } + + // TODO handle non-primary volumes + } + // DownloadsProvider + else if (isDownloadsDocument(uri)) { + + final String id = DocumentsContract.getDocumentId(uri); + final Uri contentUri = ContentUris.withAppendedId( + Uri.parse("content://downloads/public_downloads"), Long.valueOf(id)); + + return getDataColumn(context, contentUri, null, null); + } + // MediaProvider + else if (isMediaDocument(uri)) { + final String docId = DocumentsContract.getDocumentId(uri); + final String[] split = docId.split(":"); + final String type = split[0]; + + Uri contentUri = null; + if ("image".equals(type)) { + contentUri = MediaStore.Images.Media.EXTERNAL_CONTENT_URI; + } else if ("video".equals(type)) { + contentUri = MediaStore.Video.Media.EXTERNAL_CONTENT_URI; + } else if ("audio".equals(type)) { + contentUri = MediaStore.Audio.Media.EXTERNAL_CONTENT_URI; + } + + final String selection = "_id=?"; + final String[] selectionArgs = new String[]{split[1]}; + + return getDataColumn(context, contentUri, selection, selectionArgs); + } + } + // MediaStore (and general) + else if ("content".equalsIgnoreCase(uri.getScheme())) { + return getDataColumn(context, uri, null, null); + } + // File + else if ("file".equalsIgnoreCase(uri.getScheme())) { + return uri.getPath(); + } + + return null; + } + + /** + * Get the value of the data column for this Uri. This is useful for + * MediaStore Uris, and other file-based ContentProviders. + * + * @param context The context. + * @param uri The Uri to query. + * @param selection (Optional) Filter used in the query. + * @param selectionArgs (Optional) Selection arguments used in the query. + * @return The value of the _data column, which is typically a file path. + */ + public static String getDataColumn(Context context, Uri uri, String selection, + String[] selectionArgs) { + + Cursor cursor = null; + final String column = "_data"; + final String[] projection = {column}; + + try { + cursor = context.getContentResolver().query(uri, projection, selection, selectionArgs, + null); + if (cursor != null && cursor.moveToFirst()) { + final int column_index = cursor.getColumnIndexOrThrow(column); + return cursor.getString(column_index); + } + } finally { + if (cursor != null) + cursor.close(); + } + return null; + } + + /** + * @param uri The Uri to check. + * @return Whether the Uri authority is ExternalStorageProvider. + */ + public static boolean isExternalStorageDocument(Uri uri) { + return "com.android.externalstorage.documents".equals(uri.getAuthority()); + } + + /** + * @param uri The Uri to check. + * @return Whether the Uri authority is DownloadsProvider. + */ + public static boolean isDownloadsDocument(Uri uri) { + return "com.android.providers.downloads.documents".equals(uri.getAuthority()); + } + + /** + * @param uri The Uri to check. + * @return Whether the Uri authority is MediaProvider. + */ + public static boolean isMediaDocument(Uri uri) { + return "com.android.providers.media.documents".equals(uri.getAuthority()); + } + + /** + * 读取照片旋转角度 + * + * @param path 照片路径 + * @return 角度 + */ + public static int readPictureDegree(String path) { + int degree = 0; + try { + ExifInterface exifInterface = new ExifInterface(path); + int orientation = exifInterface.getAttributeInt(ExifInterface.TAG_ORIENTATION, ExifInterface.ORIENTATION_NORMAL); + switch (orientation) { + case ExifInterface.ORIENTATION_ROTATE_90: + degree = 90; + break; + case ExifInterface.ORIENTATION_ROTATE_180: + degree = 180; + break; + case ExifInterface.ORIENTATION_ROTATE_270: + degree = 270; + break; + } + } catch (IOException e) { + e.printStackTrace(); + } + return degree; + } + + + public static Bitmap rotaingImageView(int angle, Bitmap bitmap) { + Bitmap returnBm = null; + // 根据旋转角度,生成旋转矩阵 + Matrix matrix = new Matrix(); + matrix.postRotate(angle); + try { + // 将原始图片按照旋转矩阵进行旋转,并得到新的图片 + returnBm = Bitmap.createBitmap(bitmap, 0, 0, bitmap.getWidth(), bitmap.getHeight(), matrix, true); + } catch (OutOfMemoryError e) { + } + if (returnBm == null) { + returnBm = bitmap; + } + if (bitmap != returnBm) { + bitmap.recycle(); + } + return returnBm; + } +} diff --git a/model_zoo/official/lite/Himindspore/app/src/main/java/com/mindspore/himindspore/objectdetection/help/ObjectTrackingMobile.java b/model_zoo/official/lite/Himindspore/app/src/main/java/com/mindspore/himindspore/objectdetection/help/ObjectTrackingMobile.java new file mode 100644 index 0000000000..2fe485b746 --- /dev/null +++ b/model_zoo/official/lite/Himindspore/app/src/main/java/com/mindspore/himindspore/objectdetection/help/ObjectTrackingMobile.java @@ -0,0 +1,120 @@ +package com.mindspore.himindspore.objectdetection.help; + +import android.content.Context; +import android.content.res.AssetManager; +import android.graphics.Bitmap; +import android.util.Log; + +import com.mindspore.himindspore.track.TrackListener; + +import java.io.FileNotFoundException; +import java.io.InputStream; +import java.nio.ByteBuffer; +import java.util.HashMap; + +public class ObjectTrackingMobile implements TrackListener { + private final static String TAG = "ObjectTrackingMobile"; + + static { + try { + System.loadLibrary("mlkit-label-MS"); + Log.i(TAG, "load libiMindSpore.so successfully."); + } catch (UnsatisfiedLinkError e) { + Log.e(TAG, "UnsatisfiedLinkError " + e.getMessage()); + } + } + + public static HashMap synset_words_map = new HashMap<>(); + + public static float[] threshold = new float[494]; + + private long netEnv = 0; + + private final Context mActivity; + + public ObjectTrackingMobile(Context activity) throws FileNotFoundException { + this.mActivity = activity; + } + + /** + * jni加载模型 + * + * @param assetManager assetManager + * @param buffer buffer + * @param numThread numThread + * @return 加载模型数据 + */ + public native long loadModel(AssetManager assetManager, ByteBuffer buffer, int numThread); + + /** + * jni运行模型 + * + * @param netEnv 加载模型数据 + * @param img 当前图片 + * @return 运行模型数据 + */ + public native String runNet(long netEnv, Bitmap img); + + /** + * 解绑模型数据 + * + * @param netEnv 模型数据 + * @return 解绑状态 + */ + public native boolean unloadModel(long netEnv); + + /** + * C++侧封装成了MSNetWorks类的方法 + * + * @param assetManager 模型文件位置 + * @return 加载模型文件状态 + */ + public boolean loadModelFromBuf(AssetManager assetManager) { +// String ModelPath = "model/model_hebing_3branch.ms"; + String ModelPath = "model/ssd.ms"; + + ByteBuffer buffer = loadModelFile(ModelPath); + netEnv = loadModel(assetManager, buffer, 2); + return true; + } + + /** + * 运行Mindspore + * + * @param img 当前图片识别 + * @return 识别出来的文字信息 + */ + public String MindSpore_runnet(Bitmap img) { + String ret_str = runNet(netEnv, img); + return ret_str; + } + + /** + * 解绑模型 + * @return true + */ + public boolean unloadModel() { + unloadModel(netEnv); + return true; + } + + /** + * 加载模型文件流 + * @param modelPath 模型文件路径 + * @return 加载模型文件流 + */ + public ByteBuffer loadModelFile(String modelPath) { + InputStream is = null; + try { + is = mActivity.getAssets().open(modelPath); + byte[] bytes = new byte[is.available()]; + is.read(bytes); + return ByteBuffer.allocateDirect(bytes.length).put(bytes); + } catch (Exception e) { + Log.d("loadModelFile", " Exception occur "); + e.printStackTrace(); + } + return null; + } + +} diff --git a/model_zoo/official/lite/Himindspore/app/src/main/java/com/mindspore/himindspore/objectdetection/ui/ObjectCameraActivity.java b/model_zoo/official/lite/Himindspore/app/src/main/java/com/mindspore/himindspore/objectdetection/ui/ObjectCameraActivity.java new file mode 100644 index 0000000000..1fe93f1068 --- /dev/null +++ b/model_zoo/official/lite/Himindspore/app/src/main/java/com/mindspore/himindspore/objectdetection/ui/ObjectCameraActivity.java @@ -0,0 +1,87 @@ +package com.mindspore.himindspore.objectdetection.ui; + +import android.os.Bundle; +import android.text.TextUtils; +import android.util.Log; + +import androidx.appcompat.app.AppCompatActivity; + +import com.mindspore.himindspore.R; +import com.mindspore.himindspore.camera.CameraPreview; +import com.mindspore.himindspore.imageclassification.help.ImageTrackingMobile; +import com.mindspore.himindspore.objectdetection.bean.RecognitionObjectBean; +import com.mindspore.himindspore.objectdetection.help.ObjectTrackingMobile; + +import java.io.FileNotFoundException; +import java.util.List; + +import static com.mindspore.himindspore.objectdetection.bean.RecognitionObjectBean.getRecognitionList; + + +/** + * [入口主页面] + * + * 向JNI传入图片,测试MindSpore模型加载推理等. + */ + +public class ObjectCameraActivity extends AppCompatActivity implements CameraPreview.RecognitionDataCallBack { + + private final String TAG = "ObjectCameraActivity"; + + private CameraPreview cameraPreview; + + private ObjectTrackingMobile mTrackingMobile; + + private ObjectRectView mObjectRectView; + + private List recognitionObjectBeanList; + + + @Override + protected void onCreate(Bundle savedInstanceState) { + super.onCreate(savedInstanceState); + setContentView(R.layout.activity_object_camera); + + cameraPreview = findViewById(R.id.camera_preview); + mObjectRectView = findViewById(R.id.objRectView); + + init(); + } + + private void init() { + try { + mTrackingMobile = new ObjectTrackingMobile(this); + } catch (FileNotFoundException e) { + e.printStackTrace(); + } + boolean ret = mTrackingMobile.loadModelFromBuf(getAssets()); + Log.d(TAG, "TrackingMobile loadModelFromBuf: " + ret); + + cameraPreview.addImageRecognitionDataCallBack(this); + } + + + @Override + protected void onResume() { + super.onResume(); + cameraPreview.onResume(this,CameraPreview.OPEN_TYPE_OBJECT,mTrackingMobile); + + } + + @Override + protected void onPause() { + super.onPause(); + cameraPreview.onPause(); + } + + @Override + public void onRecognitionDataCallBack(String result, String time) { + if (TextUtils.isEmpty(result)) { + mObjectRectView.clearCanvas(); + return; + } + Log.d(TAG,result); + recognitionObjectBeanList = getRecognitionList(result); + mObjectRectView.setInfo(recognitionObjectBeanList); + } +} diff --git a/model_zoo/official/lite/Himindspore/app/src/main/java/com/mindspore/himindspore/objectdetection/ui/ObjectDetectionMainActivity.java b/model_zoo/official/lite/Himindspore/app/src/main/java/com/mindspore/himindspore/objectdetection/ui/ObjectDetectionMainActivity.java new file mode 100644 index 0000000000..d415d72488 --- /dev/null +++ b/model_zoo/official/lite/Himindspore/app/src/main/java/com/mindspore/himindspore/objectdetection/ui/ObjectDetectionMainActivity.java @@ -0,0 +1,87 @@ +package com.mindspore.himindspore.objectdetection.ui; + +import android.Manifest; +import android.content.Intent; +import android.os.Bundle; +import android.provider.MediaStore; +import android.view.View; +import android.widget.Button; + +import androidx.annotation.NonNull; +import androidx.annotation.Nullable; +import androidx.appcompat.app.AppCompatActivity; +import androidx.core.app.ActivityCompat; + +import com.mindspore.himindspore.R; + +public class ObjectDetectionMainActivity extends AppCompatActivity implements View.OnClickListener { + + private static final int RC_CHOOSE_PHOTO = 1; + + private static final int REQUEST_CAMERA_PERMISSION = 2; + private static final int REQUEST_PHOTO_PERMISSION = 3; + + private Button btnPhoto, btnCamera; + + @Override + protected void onCreate(Bundle savedInstanceState) { + super.onCreate(savedInstanceState); + setContentView(R.layout.activity_object_detection_main); + + btnPhoto = findViewById(R.id.btn_photo); + btnCamera = findViewById(R.id.btn_camera); + + btnPhoto.setOnClickListener(this); + btnCamera.setOnClickListener(this); + } + + + @Override + public void onClick(View view) { + if (R.id.btn_photo == view.getId()) { + ActivityCompat.requestPermissions(this, + new String[]{Manifest.permission.READ_EXTERNAL_STORAGE, Manifest.permission.WRITE_EXTERNAL_STORAGE, + Manifest.permission.READ_PHONE_STATE}, REQUEST_PHOTO_PERMISSION); + } else if (R.id.btn_camera == view.getId()) { + ActivityCompat.requestPermissions(this, + new String[]{Manifest.permission.READ_EXTERNAL_STORAGE, Manifest.permission.WRITE_EXTERNAL_STORAGE, + Manifest.permission.READ_PHONE_STATE, Manifest.permission.CAMERA}, REQUEST_CAMERA_PERMISSION); + } + } + + /** + * 权限申请结果回调 + */ + @Override + public void onRequestPermissionsResult(int requestCode, @NonNull String[] permissions, @NonNull int[] grantResults) { + if (REQUEST_PHOTO_PERMISSION == requestCode) { + choosePhoto(); + } else if (REQUEST_CAMERA_PERMISSION == requestCode) { + chooseCamera(); + } + } + + + private void choosePhoto() { + Intent intentToPickPic = new Intent(Intent.ACTION_PICK, null); + intentToPickPic.setDataAndType(MediaStore.Images.Media.EXTERNAL_CONTENT_URI, "image/*"); + startActivityForResult(intentToPickPic, RC_CHOOSE_PHOTO); + } + + private void chooseCamera() { + Intent intent = new Intent(ObjectDetectionMainActivity.this, ObjectCameraActivity.class); + startActivity(intent); + } + + @Override + protected void onActivityResult(int requestCode, int resultCode, @Nullable Intent data) { + super.onActivityResult(requestCode, resultCode, data); + + if (RC_CHOOSE_PHOTO == requestCode && null != data && null != data.getData()) { + Intent intent = new Intent(ObjectDetectionMainActivity.this, PhotoActivity.class); + intent.setData(data.getData()); + startActivity(intent); + } + } +} + diff --git a/model_zoo/official/lite/Himindspore/app/src/main/java/com/mindspore/himindspore/objectdetection/ui/ObjectRectView.java b/model_zoo/official/lite/Himindspore/app/src/main/java/com/mindspore/himindspore/objectdetection/ui/ObjectRectView.java new file mode 100644 index 0000000000..b0d3a3e99d --- /dev/null +++ b/model_zoo/official/lite/Himindspore/app/src/main/java/com/mindspore/himindspore/objectdetection/ui/ObjectRectView.java @@ -0,0 +1,115 @@ +package com.mindspore.himindspore.objectdetection.ui; + +import android.content.Context; +import android.graphics.Canvas; +import android.graphics.Color; +import android.graphics.Paint; +import android.graphics.Paint.Style; +import android.graphics.RectF; +import android.util.AttributeSet; +import android.util.Log; +import android.view.View; + + +import com.mindspore.himindspore.R; +import com.mindspore.himindspore.objectdetection.bean.RecognitionObjectBean; +import com.mindspore.himindspore.utils.DisplayUtil; + +import java.util.ArrayList; +import java.util.List; + +/** + * 针对物体检测的矩形框绘制类 + *

+ * 使用的API: + * 1. Canvas:代表“依附”于指定View的画布,用它的方法来绘制各种图形. + * 2. Paint:代表Canvas上的画笔,用于设置画笔颜色、画笔粗细、填充风格等. + */ + +public class ObjectRectView extends View { + + private final String TAG = "ObjectRectView"; + + private List mRecognitions = new ArrayList<>(); + private Paint mPaint = null; + + // 画框区域. + private RectF mObjRectF; + + private Context context; + + public ObjectRectView(Context context) { + this(context,null); + } + + public ObjectRectView(Context context, AttributeSet attrs) { + this(context, attrs,0); + } + + public ObjectRectView(Context context, AttributeSet attrs, int defStyleAttr) { + super(context, attrs, defStyleAttr); + this.context =context; + initialize(); + } + + + private static final int[] MyColor ={R.color.white,R.color.text_blue,R.color.text_yellow,R.color.text_orange,R.color.text_green}; + + + private void initialize() { + mObjRectF = new RectF(); + + mPaint = new Paint(Paint.ANTI_ALIAS_FLAG); + mPaint.setTextSize(DisplayUtil.sp2px(context,16)); + //只绘制图形轮廓(描边) + mPaint.setStyle(Style.STROKE); + mPaint.setStrokeWidth(DisplayUtil.dip2px(context,2)); + } + + /** + * 传入需绘制信息 + * + * @param recognitions + */ + public void setInfo(List recognitions) { + Log.i(TAG, "setInfo: "+recognitions.size()); + + mRecognitions.clear(); + mRecognitions.addAll(recognitions); + + //重新draw(). + invalidate(); + } + + public void clearCanvas(){ + mRecognitions.clear(); + //重新draw(). + invalidate(); + } + + @Override + protected void onDraw(Canvas canvas) { + super.onDraw(canvas); + + if (mRecognitions == null || mRecognitions.size() == 0) { + return; + } + for (int i = 0;i recognitionObjectBeanList; + + @Override + protected void onCreate(Bundle savedInstanceState) { + super.onCreate(savedInstanceState); + setContentView(R.layout.activity_object_photo); + + imgPhoto = findViewById(R.id.img_photo); + + Uri uri = getIntent().getData(); + String imgPath = ImageDegreeHelper.getPath(this,uri); + int degree = ImageDegreeHelper.readPictureDegree(imgPath); + Bitmap originBitmap = BitmapFactory.decodeFile(imgPath); + if (originBitmap != null) { + Bitmap bitmap = ImageDegreeHelper.rotaingImageView(degree, originBitmap.copy(Bitmap.Config.ARGB_8888, true)); + if (bitmap != null) { + imgPhoto.setImageBitmap(bitmap); + initMindspore(bitmap); + } + } + } + + private void initMindspore(Bitmap bitmap) { + try { + trackingMobile = new ObjectTrackingMobile(this); + } catch (FileNotFoundException e) { + Log.e(TAG, Log.getStackTraceString(e)); + e.printStackTrace(); + } + // 加载模型 + boolean ret = trackingMobile.loadModelFromBuf(getAssets()); + + if (!ret) { + Log.e(TAG, "Load model error."); + return; + } + // run net. + long startTime = System.currentTimeMillis(); + String result = trackingMobile.MindSpore_runnet(bitmap); + long endTime = System.currentTimeMillis(); + + Log.d(TAG, "RUNNET 耗时:"+(endTime-startTime)+"ms"); + Log.d(TAG, "result:"+ result); + + recognitionObjectBeanList = getRecognitionList(result); + + if (recognitionObjectBeanList != null && recognitionObjectBeanList.size() > 0) { + drawRect(bitmap); + } + } + + private void drawRect(Bitmap bitmap) { + Canvas canvas = new Canvas(bitmap); + Paint mPaint = new Paint(Paint.ANTI_ALIAS_FLAG); + mPaint.setTextSize(DisplayUtil.sp2px(this,30)); + //只绘制图形轮廓(描边) + mPaint.setStyle(Paint.Style.STROKE); + mPaint.setStrokeWidth(DisplayUtil.dip2px(this,2)); + + for (int i = 0; i < recognitionObjectBeanList.size(); i++) { + RecognitionObjectBean objectBean = recognitionObjectBeanList.get(i); + StringBuilder sb = new StringBuilder(); + sb.append(objectBean.getRectID()).append("_").append(objectBean.getObjectName()).append("_").append(String.format("%.2f", (100 * objectBean.getScore())) + "%"); + + int paintColor =getResources().getColor(COLORS[i % COLORS.length]); + mPaint.setColor(paintColor); + + RectF rectF = new RectF(objectBean.getLeft(), objectBean.getTop(), objectBean.getRight(), objectBean.getBottom()); + canvas.drawRect(rectF, mPaint); + canvas.drawText(sb.toString(),objectBean.getLeft(), objectBean.getTop()-10,mPaint); + } + } + + @Override + protected void onDestroy() { + super.onDestroy(); + trackingMobile.unloadModel(); + } + +} \ No newline at end of file diff --git a/model_zoo/official/lite/Himindspore/app/src/main/java/com/mindspore/himindspore/track/TrackListener.java b/model_zoo/official/lite/Himindspore/app/src/main/java/com/mindspore/himindspore/track/TrackListener.java new file mode 100644 index 0000000000..f9697c3e99 --- /dev/null +++ b/model_zoo/official/lite/Himindspore/app/src/main/java/com/mindspore/himindspore/track/TrackListener.java @@ -0,0 +1,4 @@ +package com.mindspore.himindspore.track; + +public interface TrackListener { +} diff --git a/model_zoo/official/lite/Himindspore/app/src/main/java/com/mindspore/himindspore/utils/DisplayUtil.java b/model_zoo/official/lite/Himindspore/app/src/main/java/com/mindspore/himindspore/utils/DisplayUtil.java new file mode 100644 index 0000000000..65366868c4 --- /dev/null +++ b/model_zoo/official/lite/Himindspore/app/src/main/java/com/mindspore/himindspore/utils/DisplayUtil.java @@ -0,0 +1,47 @@ +package com.mindspore.himindspore.utils; + +import android.content.Context; + +public class DisplayUtil { + + private DisplayUtil() { + /* cannot be instantiated */ + throw new UnsupportedOperationException("cannot be instantiated"); +} + + /** + * 将px值转换为dip或dp值,保证尺寸大小不变 + * DisplayMetrics类中属性density + */ + public static int px2dip(Context context, float pxValue) { + final float scale = context.getResources().getDisplayMetrics().density; + return (int) (pxValue / scale + 0.5f); + } + + /** + * 将dip或dp值转换为px值,保证尺寸大小不变 + * DisplayMetrics类中属性density + */ + public static int dip2px(Context context, float dipValue) { + final float scale = context.getResources().getDisplayMetrics().density; + return (int) (dipValue * scale + 0.5f); + } + + /** + * 将px值转换为sp值,保证文字大小不变 + * DisplayMetrics类中属性scaledDensity + */ + public static int px2sp(Context context, float pxValue) { + final float fontScale = context.getResources().getDisplayMetrics().scaledDensity; + return (int) (pxValue / fontScale + 0.5f); + } + + /** + * 将sp值转换为px值,保证文字大小不变 + * DisplayMetrics类中属性scaledDensity + */ + public static int sp2px(Context context, float spValue) { + final float fontScale = context.getResources().getDisplayMetrics().scaledDensity; + return (int) (spValue * fontScale + 0.5f); + } +} \ No newline at end of file diff --git a/model_zoo/official/lite/Himindspore/app/src/main/res/drawable-v24/ic_launcher_foreground.xml b/model_zoo/official/lite/Himindspore/app/src/main/res/drawable-v24/ic_launcher_foreground.xml new file mode 100644 index 0000000000..2b068d1146 --- /dev/null +++ b/model_zoo/official/lite/Himindspore/app/src/main/res/drawable-v24/ic_launcher_foreground.xml @@ -0,0 +1,30 @@ + + + + + + + + + + + \ No newline at end of file diff --git a/model_zoo/official/lite/Himindspore/app/src/main/res/drawable-xxhdpi/btn_code.png b/model_zoo/official/lite/Himindspore/app/src/main/res/drawable-xxhdpi/btn_code.png new file mode 100644 index 0000000000000000000000000000000000000000..40eda4c16dce769ac2b905f1cb6db0a565892ce1 GIT binary patch literal 3435 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 literal 0 HcmV?d00001 diff --git a/model_zoo/official/lite/Himindspore/app/src/main/res/drawable-xxhdpi/btn_image.png b/model_zoo/official/lite/Himindspore/app/src/main/res/drawable-xxhdpi/btn_image.png new file mode 100644 index 0000000000000000000000000000000000000000..1f41411f51ac823b930a62e49bb2255ddaea378d GIT binary patch literal 3287 zcmV;|3@G!7P)Px#L}ge>W=%~1DgXcg2mk?xX#fNO00031000^Q000001E2u_0{{R30RRC20H6W@ z1ONa40RR91Y@h=G1ONa40RR91YybcN0QuY69smFgK}keGRCodHoez*zRUOBF=e=E5 zP2{sB$EKagZ)>7sVP#?jp!Wik}a zau7qTWJ)DOOUZ~op}Dar?8@%G)AzURd3*1<_uY5j+jH*Q-FME+Z_hcu^Z)nRd(XM& zp7)-h61(|La)!)Mos@K1iMx`o`MpBW9jj}^{rNK5OcypQ&sX=n4YG<_Xb%(y#)hg42&bODrA~=Ce~O;LnQ@v&z0i8lBhnUUwu-Af3BzMW{Foy z*}0M~I(XnxN>STH7gHZ!C8h-JJ!?E|nsc6LFC{S7wzGhC zlSC&~WLqf1KqieDnP^vCwb*5P_{MOeIXy3A_qK(7OISV)oI14xnSg}CCbJ%wtlS7ZR(kWQ3oS2bxtd(93 z;7U^xQ+)+BYEP!-wbw?2yfbT!3dTW2?&o`a0mUVpZJ? zdv3sL@}X5V;>kDzj0D!yB-4NBq;I3*#dvD}P@D=~o+2$E==q8Ys$DrtY>lcRCot@R zjqJ!!BmM(EA60=%GiL!@7)Mas+^DmUp}=rnaF{qq&*Q54sJZUAn&Mg@m7;}pv&G7= z{DHY>nUpEfLX8eqn7NYC;Ux6kwN3I%VXXzOuv;7DbttelEVI&~&>`&3zl`_+rE`8v z3rI>yK@*UBJf@p~{Z%cz&6iHm#Ff>eRq5FGy%}D%Q6Ag8pK`u%L+yV>W4DZ)O@Ce{ z@Z!L^qAkFWM{R9%Goaz77Cf8^-N~6wT7U-b^fuxn%pXiZrpLS)R2Uf_7&(UKV1KMb z>73tt3oLAqXS7S&f|>$H)?xFVtFIP(oIUG&b66%rU&*HU1Fxmi^eqR=Zxj=;6*c1T zX+bxJ)rIHX({c1-iZ{&%Oi22KH&KPLYFVn^OI4cldv5_eG_S*)#aCIt;n=E=i4Pcv zo*XCvMa6NnDNOHl1}z}NZ44$Sd0X)TLqgy_%9;5Lytj}GTd;tjP#a^B=VPJ+v(OV) zWgH9OmWYcf#A!=^2Q0)!eOcvu4ztx!932>fx_JdU12PG6B7`3Z`3%Tr2qs;@zH@mj zU=#hlb)s+r?GvsTCtn(H?_zJ*0N7I#k)CRDtG^?%! zkS8=3aIZO;bATD0xe$lQKP_MP2WZ( z;{1~Hl>)5%l9pGmPfISpsA*u8nhE)kWFc>+@o_4j)9dpkmSe;;uzi{l+2Fo6rdqll zJxf#U)#7CzH4TjS2KCnj*Tu0i=09G_&(`u5ay!+E4$P=gU|2Zt?eB5TRQRyE{FtT- zi~bnO)3t=q^CeoIvWgl7)@?c7JM_NW;oIV)fX2HT0SA2w!VlUDwM1TkY}RyTA2q%; zg{y2U0PynMyK}+w*bC6}js~Z_3E^Adry&drALIzQ+fkPnd?14@1^PK0bzcd&3^ESF z|8zW`@;}h8(XZX`OX7?J>mCKaQu#RKY|p6D2-kLtM;#jc35@dl&=iYGfZ{t2{{t<7 z@arGl@f^DX`F;uiyXa$!p22q9#x0RBkb~&>6_CFbF@RMl^MfMF^{NOU3_}Gx|Nayb z220h?SKBR+KcdV8rA*4V0*pHkX#axu83=Fq^7B|e_a9{VI?C}&%tUX6-LRfU`}|K9 z*YP9Bb0~Xe+yW-@Sp&wMgW=zUT#q2H68Xy63Gq69Q07-I^C1(E_d#!B5@8Rl^K-O! z1o~Xx2Eea{HYN$pDllaLfY=V$kA6K4F?QM!&3CRp(TE$l?^bHusqQCrE8+l$^w`TSRv5# zO#&-(+JFaU6P5<;QpBx8D=9D-a|mQSWHiKhUr;A+H}L|a*K?MFlgr1P#Cu9e7??L!bDQ`=)K(`Ju4dGrp)RP!2)!8JVB?=5iyb*)rS1U^(ycg$n z>Ju(cBHz=HQQpKt!li(v`ez$jV!&WT-s-sz!eH{bFCfEsx?1~FF+MDuqez8kqKvLK`)dKCJRa`Nn~D!wQ?T7}ocrI-}B%uNmP;X#EM6XIwcS zuE&lDJm#okKHn%X6R=e8@<7{!d@R;?FjRj``50F&XtA0w2FxTZ)$<5wys>(&%Gd4h zf*3qgQhhH5 zTCad50gDHRc6cMV0Wz559@lSSsWB*MEv>X)>7x9ifh7zpkyj*9hO(K)(B6E9{t>5* zqJTMAaZGC`Pr<+(EIT0L)>;rS2P>{w?d6LN%)zn;p^%zm0duelF{$0Wv4Eiqz1ka$ zGX(roBz^`XPQUtEp18pDe#hR()eDBj9jq{`gzJg{=3s@}G7)W|z#ObZu;gx}5MT~g zZaXHH5*L_*l^BxfhGGH3URSI)#nDY2O?3;5I9AG?#|KR0_~teIz?Cmw?&_jb9IQzD zgxVNiB+s9>&N@94zrGH*!@ueJ0C_-Ph{C~&widqUfp9O~9#sO4SQ~|)+aZ!rSsjl{)k`4glN^one#})O>s0v-0JqrL< zE5LlNu1375x1z5SyV565q?=Ia89lR$Ctv|Vop|cKHVhWG9tv#Ed7^z}ilzfveF9x{ zO>|oT_HM2aG;K|_;P0~vb)mOJZ_as5au%Momt$hg`=L5-ITwy^0YJ!LN8`atNlRB; zE&d%Px#L}ge>W=%~1DgXcg2mk?xX#fNO00031000^Q000001E2u_0{{R30RRC20H6W@ z1ONa40RR91Y@h=G1ONa40RR91YybcN0QuY69smFmG)Y83RCodHU3-jG#TlQo%PuZu zE0n5HF(_@>s{Kdt6)H)KwrV2}!ETWvwD^imKw1?r!3xzxMcd+%XoO13miX8f6)-hz z6q}#~;)8!`E!1L(C_zhE1$pe_p8kGk=dO3>esk`e$G!I~-AN|r%r`UN_s#D+^Eh*6 z=6I?fx%toe!&HYlw_B+u&sR-QoqnXRR3pNT%2PveKg3h&u=3R*-%|&1e-LT*!0$kK zM=h?ZUcJ5i8m}F+e&YY0*r7T3{sO+WAMtsxZA;NlUJiQ z)t{&~KYWGvSyH~0$e%fbwYB-RTXO2VIi+R+fXTkE8p;iX=cyx*u^vxC>$n>Am*!>< zl{lL&GX|T#&ObHRspg{=&Bl{|O10nuAp21J*3{Ig<;$med#j~~O>WfeRAdPV;tGLB1%mY6e^Mf6Ad;t)>R8y~B`Smn!mvdfA!k@15N9Q`!w_(7`f&A6b zXQ(ASYYMTkD3Y=gK+c z=Bq;1+fht=~C zKEb9yK=%|{r(i^OZx%*mdgOXH9-cX9E=yswsbPqkvTU-)8I`iTQp&_u;EreglaF<% z7g6Bz!scJJoOTqm9N-zMDy1Hzv!EuYRU4<0%u-c^VK|*8J+f23;1y9uMmtI9EYOs$6Lp?jFI?ptx4cXNL>77axGSq|KCTFvGrXgqB3 zvFWMQFuoM~-Rjv8EoiK}L*n~o)ZC~1d!hJZoAO?*nws{@bz+0*5#e^U-?kZyRr-mv zm8)OVVYHPffY>}@5Yk?QkFIHs91}H(oXF+eV2H6cu+Qq(7Ac{;E}u9MAuc`ma9$uC zm&jmx)?J*)g@(I^)T&wiz0WEIGt0+vvb32FU6P~ z9arh0J8C@E*aS^;J3fI_^fQ0(o|X&VKZ{NSjQpWl_>T5!w|d<&_t=S9 zkgp}3+>TYl^csvJV_`nc=R|JW#)6@S!9NJJ_{oJ&aU5e2Z02MBo#aQpB&Ow9ZkC@o z2t4bEn?R35id@6=>ZbQly9S$*&?G$3GTpn;q$ddj)VavN6n=OTK9;;(qj3*v+X_p3 zEb_dk{oeqe8-&b9YwOkMB&)HCYl$GGki?~E7Grf`g%MvQQ*kg9S5Q3xKO=GI zOt;Y=puQIu-c!F@G2Q!dTty1=0YHMy51$tn()E28uB+kKz;6Y}Tuu<`z>iHojWQd4 z4!rcBz7F~n@T1`qySY#MzeU6NBa;yT--kZ_^tjzdq(1CbUtJ*?3rghpS>IGHPFNdj z8}drV;`G zdJE&1(^!K6u-FW;W{ouD@bmj*fIbbL1CQ8_`N;lkcBLAT-{Vej7MOfW!9|sKpO{;#p$1TVuD&ne&gWDztB-W^+@bO zaG9*}j4Z?1V8JdDX=4J`4RR@3WXVPbJk=Zx&M?RX^I{n5Zw5V;pyqoqn3+bKq4h~5 zC%^^xm`pGjX&{~^-6ia7FmCyg7^0niA?dn7fW!Bpr3QaCSCath#|AyspyodbOwHzT zsn#pDJmNxpBnCD-rL=`KXM;hoY%r|IEo2z;{x0M*-?L_xt7e%xYB@6vI`yGmi5yof zOAKsyN@)veP6i8hKH|GhVaEJ@U9H-bzmIdpQ+x*U%`@^o3lLN7vkd-4K`qZ5Jz}*~ zpkGmXR|1!pi)n#pfU6j3CpsC7+f;pd3G?oUn?1Arw@Z^_I5$|h&8Q3E#80*?sw9AUa5J4m zk3hTBNV^fu5+(ww=d}Edh6bjdMs#>E1Sb*KFm6r;LkHO?G2zZfi5`J=x{>yEBV4VZ zsfVd&B)x1n*>DYG?qo2uwW7f=<7>WbhRKrRPBVymjc~PsrXHszQN)Im4c9PPP6opw zEzw}Zy5k!R9eFJjJK0b!l}{T6&p1I-k5jb1ad=^^n~akU*DxVY1_OfziOD%CIFLk& za-nfLibgARNt@V0UP0Q70;4g4@R30_`!plEVnI`neOlkR z3W!~b4c9OsP6k6;D-JGdI_17COrQ~G_7x^jM)HaUO+Cy(NF+sgUWk;zhO3k|PjfOD zs;+1-9$J!T6!Xf(pM~y%!ax=8O+8FKBdKP?$%bnfb0>p=!5)c8aKelf;ZA$iNIDUt zF;|_8tmy=`{D}rPQ%@s0JQ#wLh-(-(S8(JhC>#eA#@r!py^)5ivADXanvU|#Rbi%{ z(e$(9B;y*w-N|5RYemMOCo&j1@}1D_9Yep_M!3>JQ~oX|%@SwvEAF)1Ifh_UKO@>9Xu-*fYY0mxgYk@9bdb`d>?j_z6~f;o z>QN*rTyZ-H;rm@OmLU!0Qhwf1FX|WDX?#YR3<5lXt4S{vb~0FSpfS$Km6-8VG>Ps? zLv!;!(zY1sr{Zbn!kCIVEpMtJutn=eFme|u#RFb&BFjq?o+whdq7Sfyz#-qZqo#;qm=HMQO zL-M@AB6>suz*&JTPa4_ILU>!bYMSP|4KgUF&^@X3DWpZd4{#9<&m@k-GcrSDa5k7; zEGmwe^x=?k+U_6B1D_)?&0SV@T(^*Rhus(!_m%y7fjf!&=_cj239DW?Ulm1Mn(?8Dqnh!30 z9OYf2<@bq*6CfVM#mr0mh@(k&4I_UvhwY%)8(h3s7ws~4V16~}rt7>m0GNw7pMal{ zrwLcCZrTV>u=m2#*!$r*Da{%8Q{lNAWg@&e-2_svdlGl4?Z6D(wC|~9*jBu-S8~Lg z!k0v+to&m_cSo?ant=8?ZV$ya(Yryov6q zXW}4im!rJHX|Hx zJOKZXm@Ju(`NxnS`NrfUW@EWoestlm>v` zhrbK{Z18G=UjWaS-Z$adhjG~f9j^?N&UmJ4Vm_VU9EBn}xeLw)wjAh;2Ygi;{k!xe zIB7m`)>L1ZGJn=oaL^V0MbcO8o)9|RdLC%Ju+2YFiZ2%pwWk|?Kd{v}=UTOX~2Cenxe7Nw6rg8M)Zq=H0e^w|X3->G^%gOSJ`T(B*CowWQ zF_FRWB%_zbS%Lf)iR>on1C8a_YdEGcNDPGey@QmCC zo4rp$(quA;6kDx}(iMtFv28;37}V84di$IqdntC;zhiDV;|vaJT+B*eL&ZY@%mMR;D_>ImWql?7ow7KsPb5jSt!R@dDXLc(Er8KDf!t?d zkCeWGw4*TAYJS#F<4bW`dRN_G`GV*_PRz%&)@<}ETn$wf2SE1W8{ZnPZc4gfrW^&x zjKT7SoWTkU!`&aj zY2G<3>haZ8g>k9ed+J5(B5C2qgt%+nQ_&*=lS~_o<(T^?f3Wwd`VckkOGdf&pk{DY z)JQa;jXY=z!}LakStE}id=TM-DBwYa_aOgRriJfy$p2sei|Vu$-}8?35;J}?DC7F6 znordv9GhSoi$9g?!jflhoN#hjV*VbC*2nQ7Ab$}5&?h3pfz&==maB}w_}=`DpK_cTRXJg%4c7IUnht#MqoBSH z&0%}4QU2pX-;|$+n4bAQr}W;{X*s=wEZ=0>VCcJS&EYdq2MSD&=I zYcRco@gfPJT2K96qL-qDljw=*SB{6WbKX3I}`lq<>1 z5-pPk19;+K1GQjl%eCG=lK{y48fIcLov_pWDs=Y9VE)?Tw_GMUMm`<{L8a_ws;R6|XH^a1UITeohJDt>scb?erh zxLdbw8{fNobH*!;&*x@$+e1s?-K~-V`t_R+@2w0JtyNWTaorr>yLCIn?$$qlFS*%h zZ?;>vh<@F^MRc>j{rB9jJOAg~J8{2={?GAkvJpW9mv_orF) zbwy@3(M^>u$to|oDL%Y=_oO1`>zm&(Wj{kJ@_#)4@!a{8i$XddT9jw#Iu6 z3S*6@lvbqv@0q`TWqtGEj$#+!yk7a_8lZcmBLmtP8`=>O>TYN%0N|u>;<#D=!t+M& zLUa)w^v^ru|9=W2gwhs3$fdWY=lb_Q5~Vl)apxOh{R5dr4um>V`jNEG^Z&@N!aEbM z5*T#%HZaS+E8L}_1YJ$F5iASXzZxN533=BvC-$4T*!^3I^I?|i##eOx1n3xV-6XKH91w!v zI-)%hPHAuv!JQ+IjSl8EW=`gvb|B{o5K3akRi%|z(?6XPJ4}qba=Mu+zCQS`@YoP< z;H|iP`Ol9`HdC{faMecJd58YcgPD?T1FEX>m066FAU4R^f=2iQeKdK}Qg&6_c&f`5 zD1Aq40Mu|cTiq}_U43rt(y*RVq?R6583$l?faxXbvyXofOFQ}UMOGvK&3~l%ZT=hB zr80xsl0qqA?1TSkP1t$=SeVh}7@PBww`I|=Z6A8uwCd9qTV*9s#F1KjPv^y}s0l|A zVE4j2YO>=tiOHPpAz)+;bG?L~6Wfo!jn?8`=Y%3!6}y#lM!1f(K+pF7JHW3Gcrx$k zo+oh0pq&9H*JwNQ%LIy&wJL0>mtQD*nAf7w)_7c=M$Y@ zj!jyt7UMJnz1KTIR8>YSE{-*HHXl1jTT?L=M_e;1%vnphhdMzPwcq>MO1_&@Y)0@4 z(;YN5dF+grdbxyb;&?X?HQEdI$wghsCAB|B%Dl1tWR$SB zA3ZEZA#2PgT=nl|L0OZm(eaZSv}V>q>!HL|Q5_h&x}t0|uSBK5gFwJ;3Hil%V8c(B zTA>^UQn#LVLb8o*GWYQxns@(wUvKi8hxC3q25POga)7T^djmYKg{CaBb{vs(3K)i{ zmK0tCYoYrodp06*W9)WRh2|EaW2rlVmeyY5sk0}&MNQN!sLsOK-V3WnV+GJHv%$?a zVzvM1!_Duz>-@5H(fVrf2w?WJ;bL2;)GpF9uys4LW2h*G9_5Jmj#XhcHBWXfu$-C+ z?`j}zM9*zr;!DuZgr@O}fo{o%e#NwM(M|Wv{yZHr=jV|4=iC2R!&QlUj#=dZOzABF zTS)7`8X>GN5HllM5h*EWF9a;?`#y9ksQ)s$l_0$HWc@VUO7g1XEYbR8=L&VwXh#@U zv^U!UgtHjtNAAT7Y9A_jJ^#Pd3qt(!m_<%Z{v$;E+^R|VU;}S^;yoSTm@uVbEgEDU z7C))NjQ-yQkz5IiIC9Ng@|^TxFb$v`+3}`>6r{A9`Vh>86%d}qy-&FiUKEFCDaiQ$ zj)A`F!36X1T{778#o0c3=f>|eU z)}!)r(;2KWItJvf_l^cyV7Zs&(in!Q+ANn2f1tkYI@YFS| zKvg*4-NuO{-*5dc|!c|KT6sBK<9JnyKN zAbDfWYU51CvLgJxNbzc5aTm zuN>fz3(iLMTTbH3`}Ak~zcdV5yXSgeA{+M`MNzCt?BvVYYKo7Zkc!b-Ir1)9Ola^~ zxXEs41pWVH=)tLo)Xy_d^QiT`DypkdtaXtGXTW`S?DeGTuWFFN7|!t_Cj^S1p`*CPlTsU-X-))fd^)HXRP*ga=gTKk zwP%xOI|BndO$DNX^XaI`NtiQ<_#EDAyvismb!ujC35dXtsUZcvKv$%kO?2X-Lgl>V zX`z?slltq@Ik(?t5);Ver(T7bBxAvKJ#n?hN;lK3Z7HndC8Z|- z7HZ-L8gd1(Ot{1Qw)*?$F8?$j>zBCUKIcs{eluvsgvFd_pjxfvMfQOym5eJ_J(KVk-=tY&h06-R@_S)vc8$qc=f9RT_{XaTxR`cR;Kz&zUmBn^gnL9<%X#~ zP6b2-3{QXb8U=kzkNwucyJ<-ly-diYvHtK7N|zhiOnNi1VxC-Oe?XoYZXpAG-V;qn*?4@Ikly26yh zq(J21?@;#I`gTVWB4Xd5?AcNO(sOGCeHxIBIG049H;D+2`TXv?qNLgUG^%Lx6lrWb zedwXxKkk2bfU2Q0KKT2>>KCHF5ua<9-g9JbLip>>%k`!d@uJD~EBh+Va+y7xp|F-x zA#1~Z{gDZ~pcsP)>6~mOXuzAnM&sfX!(i#qkw(_tH!jDC*IU&rX3t*g0F|o?imLoN z0N2EUb@+0si;29c?bYb@(xqQiGzyl52*2O`Z_MWrhY(4oi}4eaYJA>X5cINB?}Fp8 z9j2N2LQ%iorn5XU@VK{bX)_%cAn(RXfH;Rbql#kD_=d~Gk}GJ|HBaa#9xmkuCFU%5 z+w*L5y_3o22SA@*blK}({H`<&1g5u7zn}>7TFSgi;=dS#qUv6+lD!t8(rFoc#{WAr zcUsU?6WsjQ$dVx*tU1&Q#by9UX08awe?u8_6G{3IwT{`zIk1Ac_<|Fb6cn!)0uvu| zoG9zLy(C6%r%Xb{f~w1TA8BqrIN5g<;tYCcvwojGu1!4pTQUKD)vWAtc1S|obXvu9 zIWMdrwK@c6ph*=YcNw(k!yhC-rO}L6Q-OY+>pLKUH8nc5ya5%6o=sriZ8{0JUVSv5 zx`mdsBge2-asF=#lM%akv!xWgp`g$v;82pTM8n>MxnZY7qb_kMXsw(;oCD(b-{ako zOQf^B^*4>stGGju;NL)!vH#pYS@rV}+P2k>nk<{3Y@4L{Ksv~xyH!g;lY3w_ZO>Zw zj;2D4S$E4=hRom)Y)lz#(J15MzxdS=W>-`tu{Axoi4x5@Co4Utd=~L}2wG+vm0g8h zQg|(&i-ji7d@UY%`o~T?a#`W(!uPu2>W8-J)pDXSq&-smbm!CnfGLs1@Fm@NnxZQA~i*@BI{jhY>)0|{x6w~MHk^L-N@A74q`v7vvYF6Q7j@ z;Q5`7reonT!CG_@DebmYA5MnFuoaiqqMh@Gfr>oFr@6e+8IUToVKg` zF@DjcQr^>nw*g z{$+Ms0$pT9BV%rbd~f%rhG0jLk~_NBXcxU!?;^Mx!=d-?J87p6|GPDCN3#vL4L*O_d08L%ygTWao;v6)?FUzw&S z>9Gw|qs8~eCqHYC^nm3AZvXW&2B-&58{~^j&*cIZ&+3oWtE)i+i(xwDEGg9w&4+oF zqs?~Zj)JAURL0Z@cT^J34L*-4gUalR2I_|N*?@CrV>UCR4Oe)MCk@ zIBDgPRS4;d#){Fus4dGxn1&g>08|_DmI$0pf{NTJb57%O0!l6}8`_<1@E>sUul>z8 zxPd{LSb%V0uBDq_w1`kf-l%=}<#x=5~ zcfh<;(Uc4GXW(bb+otLwajnIV5*(Zu%8v>VbsZX1f!RM*l`;-pr8W4bkB1{2;>J|& zJk2V3Qipk&KKZ#HvvrB>TI&*zX);C#(Wu|OszGM31=0!^o)L>DMq;s4acffo9 znMjyBvsf7d$Dr7y^)G@`@TVeyr`qMv!j_zHNUt&nw$bual|NL)7I1`^@ky*P7 z&$NM{RBn?OL*JNnD@N|s04(T)QlFP=sTEq<-K=R;k#EXxR}b!wia_9rzSvr*CVk@z5hy(fZI{t?>C7Y{q)V6M`Z1O+6C74>A^R!>e+b@6$)9OI^dXz%Z#ga6}rl5JY%(;_|yrn zJKPZrfuNjXy~BbV!<)&1HJ%N)Bq;?pEVPjTVPkcDmmC5bk}qw59577OgA#+6`n;(# zm|f)BYLq*{ey(roBDP^ghZPa@m}|)4w%!Bz*!pT%%jxN`FG9 z*r#5qLU0Tx6I*;f$dRlpu=canG~wpe^=gg%&X!tF_~=#*@q4zp6y|hVD{q_pz|!np zesbe2MYgbj`NDI@4kO0B=l^?w@CWmtV%2S~2AT#~nC@5Z`+@}}$x!}G!;*5LT-wYq zd;hSlE*Hg)0b|%}Kp#7iRruS)$BvMJ1pbvrUB1xj?^O~4O8%fHKAU0Jgw-9G&w{$K zq(H?g%EPA;h|LWf&eD75lishHdf4_DopDBE6Ua1ezb{$|K)TJC_I%w3ebT#lQW zoNv=zuj4P#c!^h%Xet^{mIANW?0X*g`TU5%*4(d6j`GW2klDPB9EnwF$(CYv*kV<9 z`)ICODD{S(pjlM@=lV3i!S9j?x`uo>QI6OsJ@(bu3FDV^eV6?tI~S!69bU%nsNO${{==q&sg=C{_$a1seopPRt`wI5 zRIlD&8n%)7bU8L`<~}4>dVcC;cK%0Z?sQV7*6KPKIR$0I*_P@&>HLFG3m^UhC&Z`~ z@zC6*$F>yRQj?&AKQBX+P(!$qSw3jr_LZ}1C>|ha{dNIi%h{#=r+QAY6@qauN_yZ(GVRCiL z*-DE~B(>Vrf(u4=bK-Pp8sXE{L#ytb+8nyy*Vqfv!M(uEPAHoIYpyAqljHa6y_1H+ z?>EbXfZNuy;H&(J7N4+Vtu%BC!eSQC;{C@X8>*f4yN5RQE0KHwlXJ$6gdf@+DhyzY z;j`c5z|`tdq@&-Br32axw>44}Q6EV>p`VNPI?mSz7s(wC$IaH2CvX5^gv9v7J*%|& zhktXXQ_4T%h3f3NLmOpTS#xyw@hh-$+{}uln0pf;7Fe1|$vUiKd#2aa@?mF*~Bm-vqz*$rPJEKe32)sL<-HP$q)Vehz zJl>-kV4Y#lf|wn4cS9Kpoo-e>QuX+S^XRi|VGl!t&UU5c>G_6^E=5?%K7`>bQoAza%;^O==U$;OP}S(j`inN&c+ps`#&%7`hR^U;=!pYl0Jiw zf6iJ~$YP{TsN7c;-^zI-uN-HRSBxmP0<6%?&{@BkT;ui0v(oCw(s(r(TU48G0|V6- z<3Wqs1FB}HvFD8!gJyxleY4VYw%%D&2g{wd_TCz*`uz2z00{vQHo_y|g26`m8W!mi zu3`buB^5rX{^VvBfi-{yjj28Rp-FuBPa#V?kTSDITE*23E9;@*DbR78pt1i6vR;b=;35kzUAs zQLu=SYgDDksVG-6`W=v@=S=q%n?nc4v$3~xJQ^L0*7r~#TwmaTJGFQg7vJH=8FC0> z7z8iiNkuEWa=dorLbGH|F?n=^1#JGtT?|;vJgc<7q8IV8jnj)vBPOTSe#04}x%)@2 z@s5-mpn%$Qu7ygC^}PE_&Hk|1p&ySSx1g{GOUQN+0wMFVAPM_N_J4{kb@TLYgSP7- z%eVPJrES33Z#QAmO~y3+yjXqvy*>v8f5Zn$$wfn8urz@DslpEyt@h)iR#JEOsb3Ys z<3M0ytk{rtDa)ijtIO%>6u$O&L;s>@;2eLUgC2(4F}J>$bxq9}=;}IN8US|#)f<*^ zH6zr{lf@%>e$c8TP7W_yZtbAL*!+OCMj~7o|I?$=zDH!ErajMuPtBXgiXsLkzl6!t z0E0W3!Oi!K{$egI`ELdtVr%26SzayLQ|QwmIz0$E^yp_VWHp71EuP7>``n&HK8D&PE5ter&3k%H>m|zN_?Bo&}L7< zIV-=1TqqNQe2~BvM!oqbax>P@IIYwf5-q=}42zlhhvqM4v8MNH?MR6}M#B2-6nl9> z_NL;3Je*$Dp;P>B% zyjZEEg9;#szS}sNb7ho%kj;>l^r38yy$x0WIng6CR2swoezi+Mnl{RkbGPK4;lH4v z6g%Et`+UG?b)8(T`xnN)kH5>^JkG@?ZwZ=^BN1gkL`Q0joocCh@p4U8qtbb)SVr_# zkpK&vNQsrNDo?>pU|R#XlnqTO4~Q$yQu564Vt^zijZw*5e5V5(hESOt!BS=R8d1s8 za}M6;mB}`@D;T&)tG7i{ZUUf-6b4+AeZ!-NAn|NkHGA7$?c36mBZw;LRx{ims->(G z`V_fh^A|P3McOv${Auj)5^78ic^3cwQi%A1&y8fd{2L5ug|g%Sy0V}}WvI-ShK@dX zYA!XBeiSuJzWs9gHLLEEgo#XRtv_8yOZPuy=yab@ycol%r5zqw0A%ep4RpN;gAb47 zdPADLhp))5_q4CCuqRE2`i&=uYr^iu*6jHVW$&cy-cL4X&Jl)fXPNCxc%zomwzg(s z@R+N)tYj@E_3^hBDi%vmDQqAoyV2CvoxJ0DUYD^)!Zr+>V{lANFyYI}^Dlo*h=D8d zDec&JfTrL|{=iXIJUH0*Uw(&3ZztMU2l^~*HH$J`$p$VmDaz+xogo7?QKX8kJcC%; zq|<4eXQ<-}=lq-vBj9MXs?!EG%>2)w&d^T$T-}1p&SmGEufhd3Y^h#3RE}Le3YL2i;96}6guMNmO5gZM4u9x=wV8tZ%!jeL zg#b>4UJV->W$}Vcng^b({U*XAuUh-Jc@RykHD&NqHP&L@jf8@X%609~^pBN>Fym7M zzLh7$i?zw^(ro4u6X?1y2i}s#`1XpHUY|EjBM(Yar{#T`(Dvdud7vI~go~jSINp6Z zX?WoJTV?DS_UaY6$Y>}pdW*-OJcCH@GkcPj+sEV2bLIqQ9Thbf-X6svygPi+cp4@1AnI$EM-!_)4$7gr%4LvA+osfFA zE@ycE#xUC6vM(abtIqno+(KxJ&?y)0EAp>pgv1km4SMd` z%=>Zu=Hd!p6IC_m7Q;+UBG{SkDLmb1hqB4v57&%q&T{2Q@1Tz4{!)1HJm|pI)(QlE z-4~dHqfXbK{8`DU_l3C6`&BR|k9zFM$uROE`kmp~Z&ztwanmT-0p&CG>Q1{~FFYKo zkW4zgD=L6zpZzjO=#7JW{YH$my)(X%(!cJRpp|;U-xI%w!3Wx2aWnZ(@#1SiOM|$J z;fP98x1PtHCg8E@#+kvQ4-qL@B*OSZwshl;AOJ$oPnzY=4)OVC3j*js|wTGgEA+)szY8H7H$X=Ujb zYa#!@gQJ`F9Epb8Q6D2FU`A>ajQ>u)9%(vMz|TT# zipoEGm?U{7(*q@@{F+YcUYa81oYu~iGYbt)yEPS!tsK0@Aa83&=jc{CHx`YmK4%ww za0ensUcSieAm1RmlgitzC=}D3pZSW88>|}JhL)@N1~*X@{Ggw}GI43e_HGTKK{yn)m1|= zaGD`B&g9lXp^!J`=*HeO40qYzQRegF3~B`0N%rG4+0%X6J2o57zD+%Jw@@=uccA*p z%ScAo(?0H+^avc|5N`g*u87r{ExnC<|NYo)?+#k1*UY7sQ3_stof;{@tgk0({ ziY^4f(5Xtl^j$49L%(0{EZpJ|q)U6OS$S%_N%~~DyI{a-sna<%vZk@1Gpmb<%;!_C zOB0P5ANDj=;n6^_LiC&8-gAwqD(C<^n7+B8J9_ z3dv}$Fs7HgDT9Nvn`+ckBD2$L=`U$v0(Z%bNk4_9>nGSa#c-NPnI@X1P^sLRiLmi1 zK^}wl_^%bTpi7kLFeG&nqL??H z=1pRWnJmGod8hWgm)C!dn^_!j1&n_@fsxM8xVL+_1}=yNd{mgGCBHmt8P!JW90Ot9 ziDy+1GmqVHG^I_nk)+F zIEKOX>9B|Opa{U#qDz|XSH9WdO~^?5qkQuyc%^c10#&*PJuuzJrpoyD3yUTxPWq;p zKJwp9D_vZgO|bYj#BPDO&Jh(GPxMJc~pXWgnq!8 z+4cNeZ*Wihr$RXr^MwONm)WlmnT(>t8`>ko!>B2S=_ESgS6_(wqx(2t$%`cOx{hs@ zzy4e|^;+`fYNRmD6UMdXZg+cmOgMwuIM`sTz=xi}WE}9Ssd6xh8*BkZaVBeS>ja+q zo6Y%e3(H)gLPr$>*F!Pj1H&nG-`Prec72!i{yK~SpG7NXWWveJHDZYjxiZm$>opXlkcAOQ1y`g*_GoB9 z8@T))=XkQ_(&ATwEm0za{tx$|zF8n7?FV*32Y=FU5*n0PFie+`6 z=cs~oPM*?`m3f|Pq2>84bQS!^D{8%7$d}F;my^@=y=sg*A3Eb7VCvW#g($dj4w1M)>a?$>o>4>v0#-hc)h)nvW9$e`LAA}S?djyY-^aqcgB%vz0j%<|!tqh}Qv$)MZaqoL5{HXWFlC*J{w{6^0~&0Ig=Gzaw= z7{wwxIP$=kS5QhK25N89E}9dZfzi+^XC?U^p0lk5PR0e zQl1&IDE-L*qM22f$C9)H+|!lCTTmA_Ab|G$hOPOqSb<&{rS%sCM~(V7Yro|fEQW){ zSa+GbsR?%4<|Ti0wb(-f=3D6_(nZ=b1OPP~GJ=g9Uwnn?h0M-6sm}@1Ap@Si+6LO6-RndQy^?zKr?>m`<7L^1g!> zmE2;r?3mbYp|RxJOrarzZ#Z2)TJ3KcM9Ch8&Dh{SI?9{{F|}>aguNtg5zWm_jVF1- zUu_)%9frOVee{co!-8^3zK!UZ3PSY zFgA184xW{>cVmgmAM;EHfGs>mZe3PBlhdmFBBWwbS|CLj!8wUt-RJiORnBaADzHb0 z;SZ^ruJ#kJr??{_zvF@O9r_kh^D#S{VE`p}F_E5*)|2Vl9XhkKO>&gzDQ}`cSR!Ag zzwdLI%{*~P5+P%JVIs-M>u|ZS{ZaNez2}e$)IWtR@N@$Lk35jgEG`Q1{rvcT48qcb z=~#ZwIRE)ld0R}axgJtr17}QGDIwO@mG_Sto-G0_ zc$Jc2(v?iT99FUZXs+%*ZO=wU@%NO)L+=Re@cBb;1~9H(9H_Qb(YyON<@Xuo%Ls-> zvF~wbUESDvu8%6Nz9lwRZuHEq)->7trw(%$;J6v7>SPP|n4Ag( zJKFD4d;9O-btc0Pd509(yp&JiwJnv-Q03HW;oz9PyNI9W?#1} zdKx#ZUc#9ZADzG0l$=LVVF*7Tg;3*)o4OZ#Rf0HBWt;Q(Szk=?HmOqc*VOOXKiRto zY0w}{9Xgs}4ds&EGUwpvn0EN|WXuvP=%GB=vPrA)I0SINH~+`5wbSNsK>m2N@pzoW zIG6j6qK1D>##T=s2ivU?FngIHJXc*az>zg zyD|@+lHO6q_s!1Wr{vb>sF*0LjUDME6i&4U%#{IP@Svg=eI4W4P4w;aTkCgI1@Dzv z31o0BEM=*j+UWy*Qt-xUvxvZ&?OKQgPKD8NE=Y`t>=B}iB+WtxCUDqNGCaYA`gWYF zN|$T0uXu%l#{{kCYrT0YS;&QEt#X!GGatBAs27!)TdP4&i6UnAACb(YWv|{7GsC)` zm$C9Z+2w{eGN)a&YF0e+x+#nK0O0K@_zhDFPw z!c)qU`~tcM={soLNiCpx4UN9%Ip#Q320BzbIT3mx80V1#tMvfAh>Vfq9(4gt9KWe= zw@p0j3K}^AI<4e89}ImT`A+RbL#NN7orf@8yx|B4c{w{N#Y)Jb{>(g{;j+^ZD#6jy z8T=8B0gx#4W7}vR0+-I!>zAROE;D<}`r;$!cRj(?L%g*`$h7^5gMj_z?MVMweSGbn zEK|dtN1$hG2?;(a88$QO(05SKiSNllult;@X*ceq>5b3mB6j8U)3bUu6Q(-SA>MPb zyBbt;Ek@0qw2zB6%v-L?GK^i<9}tD5pElkCJYLJ{r?=O}z57RUD3*%bqxgqPiH5n` z5PN|-&Uh`D6gIS&L?OT@MW!Sg-$!D2@pJj1skw9o0KbosLw@2V(6{=3aJOUQ-!Q(B+KiK~Pq_-o%ql+2OPOf=re z7|P=?*!gxk*4ch0IxpP^U!*2|T*ci4nuCo)u7IN8-6HSl!v%jo5-$v9U~600PYBNf zIMwK=Kb&-~rAvPeoGI7y_AG=PSI!2sK*T1vJe2>3b#-f-`v{iXc7vGc=co;GOI$wr+F=;f3+1teHQso zH(>&0P~R5;rl5~t$fPbO$MENYhrYBaDpV(+0d_vgLD@s+XubolS{ z`0jtz`%MgBw5d~K+dpb)ThEx^ftJffUWpKNv`F$k*wibzb|WQx$JA%vv0lkSbIa|u zL*84=mB&|8z^7F98yG8=v)>m^JZ{H^2Uh`XY@XA$yZ8pTxB*T4wM=~5f);E&+4ZUi z{IB2_f7&*xZFHPo(u98q81V6QpDCYWJUO&L(w~f6+8FP1BX{bqfpb2)CD&)G!6!5B z*CppJv(+aV=xNg7X{Z(uFEi*6K?|St`JDm;w6 zD^S3mWxEep-Kf2#8e0A=1Zp6%pF}>yxGWeEFyGddp%Y=A_D}%+zIY#8)3HvSuPu_Y zJ>ZADZ99PVHmoACyx}A)g&FGxFqqsI%?y=E$vhd4+p3L8)QS0bh{b`PnQKW7mZuA{*Z9IcGXQrtg9wr-CI&X7Psr@@BC*hYt?x#zH z3@>dIELq7g3lm89Nj`MzeFbAb*r2XRn!0QECdYYTT!W4+RJBB7LrSOfm5(zGu?%SL zRlNGlUftzG_%+^R%qd_i4E6SqIPuey&PN&Z}lul|nISzK3kUH>DvG^QfOiYGeXRi{VEL%LX@P=U5ju@u8cMKBi$&EM(FbE6x&5t!;pQkar#Z9O6NeTZ;uqx2s89|2#07 zSuHrN*Bx1Y&Gw<3`g^J+=Oa$f+UB;9w>uul2Pxa+v#FiKg~Rp{Cn;IAK|(mWP*Vh% zN-BYpNuIyw_Oyt3+-{K?pj`0287vrLP~yU%_6p3|dCj}C<{1a$ANcj5Zo=3{P#LK; zAyC-w`^w`<{8*&OB_OoqJkozC(rMajXhjC+{P=aQaxDL^2eMKkKPCX?v6GI~3X&`q zMe-fFV+tW?l-$%MI~H;~@8Rq_(K6j7jV3zn>lU>0Ra~oqOl`rGgdtG-?C{eWiry#Y zTg?r5Ttc($1)@YmK40W4u2+U=s&!sjoL)A1d1Aq)($A2`f!e(w!8X>Irb<3x!_U*V4=%#O?uS%PCy^BRv%&X22tE>Z+@S z2mf>rz^UD^QIX5J2klab?bbM12CXA8e3C`V4E~TDmCEhyj-z=bj*R9KnXa*#rfC%@ z+F~_)Upd_tqMox2(q|Xy55gyeTHw*7wNz|4JGOUQ7eaPVET|*+8}C_|!khXO0OEQ> zJF)Xd00E^oP0R;<`-v-SOk}wShV7^*V#{6@6YyTCvw81@5@^D<2O8bN;vc%)mXalz zC$rLDN|#J6<0UTQog@QT2zD_%_zWxR{RmbrT-aEi?wf9y`N<2JYSa8d>3mojKlEKfcfU*G+kpY4n6DP)eIwDs>xOPgNRow#g`q{^{wtacr&vfxQ z_1-2?sJ!9~AyhV#Q%3&6;BlgW_)XM+is7M|m$XYs3xHQqFFp+}30XB!H0jhC<2SpA z>seWRbDzog84G?XA4B-$pv`x#p@IT=j@>qv=5K=x?Yjou4U}54R#@74suXX|H~fBR zUr5cuR;ELaV%@dxWahh;2g{g~pn`(@c(EyReG=NAdf*Wrd%Dqbc-W!`3y9YZSYhO< zi!cO5s0+Z=9%+%ci82MY6e%cRE%=rA8B-5h)tC(+uGCXWkDyk6v8criLo;)WT@SoR zEXg$g(%Pt^*L>std?WSX?7XAK9#RSSkfGna%EIL;+RZnTsm0be>XXes#sWnSQXqG5 z4E=H5(Eg~xjA*B$Nz%8+Y!&uG$@i;$?QnDZH5OG~FA>yz_wM;!=Tq>*C5}g!0xXKA zOgAspn@g!Dx}3+$aUW+q0U(XPZE2I z7RBa$eHpj**UmYI6<6C~Ocp<%-HxVbM{OX%)dfQ7B1?AK_5skn^<@IMxo6eUqU#%v zl$Y^Erhqr5dEJxcm!aqDLhjCWwvxEyC5~Sb%v_G%>NT%RxhCjUR8&j}N#l)f=+=k0 zv^HBv(syddQHY2I<9#jM#&FPji(mExvCI`gD0Sx8Zm`|L|Eh<}5~W+SO8ebm@%V5W z@#PR867x`>1KodkLmG)te1LtGk)_!&HeY^^=@l1zTM6nJ@l*@+gi>DxL}z1RTl8LP zN$2F#?i9*qi{k@}Fw0^4>uzorUlx|mTivp5Yt`7Uk;mH}O*`Z>>fCDZqaTo>sqLZF zW6Z4gfz9lhv;I@F?W*tjbWZLLqwbs^ZZOfOy2d^cnG|^ccN__!l0WA5`BG3kY^Zi} z0HNUghA%NH&ba?c2&t~mWDyeW$LJIAuR1IMJElO(isCXiSjG25Y8vkj*RO3gW&IRqyoy`miMFMtu%}G!!fsSJ zYmffJWJ%Y`z+@Y>$Tc6={?0JzU8rR*5edxgU_LGift0{(z&xgrtzqoI|$=vsz9EoG_wS83?M8|wW@=d54I zrrBl(AwSzWL@8~EV5fi9eb7^uahPD$-JBV#{WZ&CGuY(o8CY55G$r(B!sB@md9kR} z>+ijA^O0WgvOy^43vlvJ}>-a2aW>_x7KXZ@Ay>rT=1vL1-JWBkqoD_F#) zt6-qlD+*Hgx)ejA<=W_Anb#=(mF(S647&O_oQuC98Y#%q zr*5<{a>jU6f-(^mN0&_p3aJuVS&4GU2aBQmy1xm9j_MJA>+d)2Ko49SE$&SD6TAW;qNn>gz9;T>A9-gwt+fM2OM)xZCi^X_o}yAN4#7>7@3P^ zZ@|9GPQbBD9cP)Ip1D_>4*T7Le(Xk;X^5;dSL=5{-uAcpg=)_)q{IvyD9r3=N@FouO>Cj3RFOr3!!n(N5<#EgU~-51yTc4bZv8OPT4_)1_Q;PV*gOlkSy zG#81Ux=tD06g8Laz6NVrui`Hn>ulu-l-$FRERqhQ@{w*`^oQ}Iu)6pk#PanV`ZN)H zXSC+-dBV){>2E)*zwCDvx<8udcO*{h37F@Sm+q;hh7jfJZRPn$?pJG$|GNs~3JB}#gub*_Jl-UR8|C5SScPpJW zt#k|a4=IJM4<4D?+%k>$e6#GFY5zim;kPBfP%t^$0+=q_7oL(x*`C&%taVM)ffVQ= zhFRrfk~Rt!uP*Q1aR2+U0x9ibV#|yVDh8;#_Y^<=9FACyj%>)56sbOzdz>+UAtzS` zu(0V1U&y48yaCp=O*bj%ET-pr=eWA8LtPj~j7F2Ejd2fh#)1pL)DSr>`4p|s>$gL& zfSD4-s_t*3Gwh**7z6Hgn*oU@7Ljxl#JtGl!@mK+emC=YGv*-U{YP7X*%qgdl$zFTnN8CnzYBP@F=XfL+`6a6fY7dw6|1e2ptfy|Tzl{daSz zNI$yt2T+G;KkOpj=cYIGhC+WIENH9XXO3v<%M;go*QwP)bg79P;`<6n6}W+(n%O#P zneV4#LiVuojIL`|s+izN1sRN4vZJbsn&WSUjt8j>s_}>)@4g27pZ@{~{k~vNf5d|3*kwjaQm>4#I_v{-oRWR z+iefxDLl+nqA1wyjd>p$NLpJlI%u56b+;8fp6Qx~(2(e(dTB2Z*8Z85_R!hk*Fr8- zE>793dI^nvok4H4^Z{=1a+~4qzdU9#Zz?C#><|=>p0$F>FoJz{fer0;1c#)ShVtb)e9M!n!;Gq4-Fh}in>0pBX2zB zMS|BGPU=F-Ot}PPus6uO0(a3@en5oL{NF~(GS`RqhGUVRcl%;V_N>(7zAZEHbwIQx zhu}6LedX?&hWnQ(ewy&;!zkc zmu$t*W5KlW=$xV7mb>553Q6C(3_!$fJQoF~FsS)ER9ZSjiz$rMDe(dlKG}hv=~Gf7 zv_TBKZc3-WZY*ZBL&_$S-diqjrt4}tEv@$hB~UUq{Vy0!-NCnDKH7|Xd4gebrC1sf z`w|qcJs*;nY7v{YRErWfz3ZSNPWGn7MQ$j^q$NK{!eEJKU;^J zB^!@ZhiPF^tR~gu^#4QCSFp9!e?gW~pcD!%P^3tpSaF9C9E!VpaSiTJ0;Lp(;BLiT zgA^?q+}+*X9X9WOcRxV#+}xbsJ#*&F%;onzTyppECcK-o@ps0(dqId}?@ukBpKB|; zc9$2HH(Gm~B7Y2caNisn=8Gve-W2E@E&Bx>w{8hNUo4G0)TaKsZ2hsw3gJ>6+)vV0 zejn5wRIW0sgr#3Cj)s@6MpJ(7FNsCv3art)*aikh zmmd^T1V)_IP)t<-T?7Dv>4dq@eu!bQk(ixWCRO{W`lFUXynX^th`g$jV@?l~fA+{< ztLA+uhHpIwsM>mH+JbACw_rcDvGhHxO#&e>ZPpK%@ck+?JVYB}v^W|Do$hAwod`Vc z?Sf^qY%sbc@YhqH1>iXbf3{$MCAUc5`($Zhq-Z6TTho|+T|+)`i$o_j0d z7zE4xXxs`rX*@x;7dUqR_gM6Dv!9ObeUrIlVNd3!`G|ECkyj#IFp+sMbl7rpP5itS z`Lb58_~eVA814khKNLm?C;_?+bCASTi}=_3LtZDGQy_Hw)hV4@`g@o7Geu*6{uHb6#AO zVofT{lUau9O=pfacQ~cnQV=KJt9G-cLG3*VzGvatt+X%+^0K)u7mfIK_c4zTw+z;a zA>BCGyKetUtG;g|5O61Zm}JP9CbG31Sps4H*gHF; zu?kNAHp#OEe@kPdCu-8NdSD#f@gZfe@z?SL(S|A#%J41bH{qz!Mn>qSrMHNN5YT;J z)u2aRn%|tTc$%ikjPaR|nzSAMl6kJ@VV?Y3f7;u1=HlCu%(IA`S;>+v0hWc{UHDy{NFSx@uuq-3ZL}%c*WA+Fn!QsboMjYi)v^=pQ@QjH0(CfDh zeWCc+F6w)q-G0?3DapgEQ6M%S{<_95w9;v|+O6N$oAdH3nGMIe@uGD`wp~dH`BL$} z|J3a}5EJ8e@HCwzxE~)KBlb8z>U&JHk_l^d3p8#71p7J>_vTwij}`!1 zaCK8zv+qo5-ZE{qF!Je^>TeP;)uSFk>HAFhM83;0Cm96UVrczws1WjpU3&g)Ti#p4QH(PM0)V4CD6 zF8VBlQn$?9qI{-H(<2mac)^msWPQ27RL?#<1n{^$UU+_(ez_m;y*u}PKB;KHI?Y7k zB*k^9-Kz)kYW%8r?=x-bU>^7JMsst3J>nC0Ts}G4SdC&-b(WfdRljlKcn~cqHlyJv zMbShZN!Wu;7#nTgeMr&oVPrkB&Czm7eUHgDCNMwC>Z!Lz-}LRR2W-mD_H@ageIog2 zXT>uZk8JHEpM&EBeb9EE0k|=LWSUmR}2n z-t|lRZg9IdgAbH<3@(zwzCZkqSQrNRdOKb8Emc)T44H!`Av!Zl1LL-=K}QrT1q6;W z2HUA+%Qh7}Le7RW!w>F)4hNccP0uMKkSo=H5E#a)SFdvjNpZ>01qSBOxiP0tjouYg z7}@`fQ{uGxXS5xalnEnB>)k(*S;P^Vpd!%5&$G&;d3DfJ)Z|=d?HnKvgHBJOt;m|+ z!rM2_sHQT3&7k~BYFMK*55-!ZyV+oq=~(n|DIwU-O#7o-x` z3p_t8v24f6t2bOVM4>w#LQKOIO}wcpsuV`P%!MOt-KY?Cfun4=*h?A1t4f zxmTn@K8DV)9hl`oe+e3nP7zR_^WOry)I@uLraAa#Foe#DwnrH2@XOM@B^axuQv!wq zF)X!2PKSk@o!2+^NtA2~L$wLg-W{a?c8C&@( zKbz~t^eHcA*uzqHZ*tJ_+{GyAO5;u)_}_iR0m4T1VE6LH4yEftC25>DD&AQo1Dj=? z4eNQ4F58*zKl`5eCrxg&OwKrlg&*Uo4+f+eNgg7?Dnast`~8$F#x(unElBVoE@c$7Slx}kcdg| zJd%4FdQU=dMpCyuN1pHA=isW2xESOEI5qkZdPtxDZAQaIWGc9+2rgTW6CF>v-ORf` zv|Kdwc}mli5&%hAXm4osb1a3X-Z{MSo_a5USIeF$gt`H5&j+zdt=FNg_t%1q<1Czi zuKc?wd3pT9SK08mm?9sUy6D!CCuswE&L*8Wfbx0SIBnEj_f9stHSujh$296gJ~wtQ zFAhOg2cLF6M_wG?=QO=)!0tJGPRBl8fXoQ+oxDuDpFCXGt~?)?0h%7x9_+dsgQ^^k zoUJ((!qt0Z({cE@2hbQ-37*FuYuHp-*@0cvv!_>=FH%pV&AZf(-e)NQZlvHl-3PMTuf2MlHrS8Ix!|Kqs9e)U?>wIoM z9``{bBN*E+PXTQgo-NI+43}|yr!Pe>936{gX@H_G7Hf3Tmg8)+fs@TFtFZUA$fs zkVlW&Ql@e|0G4^&4Q~ehT5gVbD<4{)ez+}HJ!Cbt)`4$^7j8yIM%aa@Tz}&aRayI} z`)mppB3zW%)_2$|!?E?vRZ$wG9u(1?1o$@V^ktcKkzxR6ZCyu%sS+-eLHM89Dju>K zmKO7U)(j2*c{k}h%&ayC8h^dsyDXwrIat02tFaV)ogLh;a)$5QHG{U(ndscOG&V#W zDYP*T-`srRyk0FQ9J~1CDu%j$Y9+DWWf*JE)-{g(WXJ_=P& zYWr&I>&4xzE88ZiHQ6$){?hX66J-M=#&Y$bEFekM`7`OfFAeG^6r)W%3Vd!2FGf= znT@bOVj1`zaf+eYVi(Uz2Ghgh4lN{Ax^Gi+X^W#iD~(XU{^P#{|Aqqudu{sQY@s~- zjoKwsJ`IdM^Lcerecm-pFzn$l`zi{YF0(&)5-oqzvfaM0>{_m1J`kr>8al(v8GKQ< zSZ3KJ?lI=!?cM!HcVS!Q-xcY!+pH4Vbiv8$`_?&YPy7-~w%V{S9{3Efpe=BDHrpDC z9KhgonnodHUDSe7L7HToIryg?-X`SMO-v1b zJ{)v-Yw=p2;w(99*McC{ir5#9M|tlw+&d{PDYSL&tY@u@F~1w{H7>&W!8khF;SWPl zt3Q*goS;72Fj5kk_}>k=y1Gp7YiGWt?cAH;S1Qg16f#R899y|EG14~5CIzn*fZf~!TkwDWaKZ0Z@!;4o6OSI{H`f|Hy8q=59 zB%WP{szICT32#%A^ggXjk_cFpR0n{I_I3kQ3u)S|plWB5tC|X4%S#oRijzwnGZ2${ zzSgtgoXj!c^XUTAMn%@^jT#*{{WgrDP3C=B=jpW=%^(#0D+zh-I4X#zB}x1#}U=#!i2R zfh3FSrW#s56v!7HWZ9>y%9$`K5R>f3;hT`HTQ>nFp*(S8KhKmVOBHR$#7Rxr4Tti6 z?YKr+m~n;cU>~J(s7Nz=y4nsY<6{6C`g2-w#;*^As;Dj$O+g1zoMQtJ&QB8>9MdO;|(YuLrG}u; zO0p0k3OBb)ihO^)$%4ZA{t!`0?>;xm$(8$zLjm4fSV6Nol?vpl7Vl%<>`8=OXEC}S zPSUa2?Y8C&&hT#$3Ekx)GSp^wkL+|!5C(sV8FnQ&Ay5^lo;_;g5P)qbhlyA_hxptn z6p-$~=wiO}XY-C9myf5WK3Gd&p4|=eYYRkSp?fXmSUw>QyOxFL@Sm6D!qv9&GcWow zWbs5Tuok$m@%Ro+h^huHE-)=vE=gdCjg&BcFlDCk=zz@@12!0t(%L56uORH6j|1u^zx#^VRqtpkIpr?-Y@7i(XHxVNkikFOA0R1^$qT`j)1p z5X!pr%dUb&ezvn(_;QO5LB5%_c~%6{M}Mp|wyuyOz`YC?DOfqvjO6xRG` zp$%gAQz3$qeM6twPB%`u);I5fU9*Z~KyRQiQ2Q@9V2Wy`mu=-kr3f? z;Zp1yqs?)&RQU@eSQ^M@TL-6)b~A!9yDO65b6 zP34;ygS1Ny>?|I&eQ4&y__$=3jvv1q$9fzg+1E<;oD<8K|N3?^U#dS-z`x6_NV*U~ z$Wt(k_V8hWqGK_ln6GU6vXLp#uHm%W73*vY9yDjVN-{jeBRMQh?dj24!wlK7&saSc zxTE^v8ZquQ_aK6KFs80H=FrAz zuwh=o^f(@+bCu~X#UhT-ld|pdQ?6hj66;&!CiCH3NAQLQRfy;a291454asfzcC}%i zCV3b>chlP#3&-tTK%EmF?Tk$8=-0gh`H)!E+=CG0g5oo5!k0?*evJYc4eXYt#ep8dLxMnD~`xFvYch-{mO^25uZ1hlC(?xe7E=KE3)#n*})G8Mf~)d^^ji=(vxj zJAdZH-$fUqYqgUEi}ZSoWwCcpEpak{7ZvgjYX-)!tsCVk6;pL0lstdS(tiuDTHrfM z{;Z*1jERc-O!sl@NiC$Q;Bt00uBlez(p&0vrs+bKkDd5^5wvNX`#vE?pLlGNm&YbpDJ%8BKO{0#RH!Mmb&bzp zVbpd;_v(+;-`q`IoI2}pGnTcQY7ew@aNms8foyD7tER^K1d>#C0v$)UZ`F${8aCDe z`dn4!oz4gXuowLUSB54Qt0Lt=y_<+Dy%kg#t92^E-kn9<`Nh-qAKHrkmXjk#a*^zK zknv$cZSGMlN%a*{g|ovGMItv9Yi-fY(uZG)Yk-UVuq#xmV;;8h?cQNjEUnsP-v14LI~-UIm!f4r3`(_nTib7Rr=prgy{|S zr%?h_0&aeyqLRt?+3T`p;1_R^EbM&^V|x4?d4Q?<#uq7cK^FvNU8i9ylXM`MRF$s$-qA8YV6;x!4Uwkjk|;fPo3RXI?_AvCQdQ-ozaZ#t~z#? zBUM+(m`2e7<_y(NTQO1V;piO##cPzoT2Q~cdj(0)m}V!cYR zJzm>k`C*KC%t(EG&lq-0Oc_CXY- z=5^VvV4O$x96>;9Vc-}6Y;Dr9QW0@<7s!&xSPd9D`f7b^E+ch{TI~j=LnL4@P}vs_ z4XLF~@yY6s__r!R_4D+3AHxl@xV_B1m|5>SBt&Df4-ba|8m@9NnB59n&fk1FFn67; zt9nKVqwMYmjKIFdU;{yt|JK2jysdWNA6FmwzK>cr=|~TWM?6&hgmEar9Vn)!$3hj= ze1&j&bW!qB`??YbDA^c&eI32S*_KVDj+*?~&V&p>5kl1%0oglOwrrxvnxD zR$y*J^)jpi`W}@#UNo8QEy56sO%#^r_#+clRf0x?%E7uU>x}nXu~hQW^nqxw4oM!= z2wjvldbjyqIiwu5`z7bRDQ$s9uYsDOT5Aj+xtjsuy(n+OD2n z757<@8KPl)i0?ln0kByQr5jE{`A?IfG$$J69T=l17vD;NY``x|J(f8nF4v=?*&~fI zgHXv$E2jEZdize|Aq&+O)Y}=%*zhdKDfHz=bN*?uP9myZGxv^}9JjvNc)hduD!W86 z<(unXPYKQ9vR(z7AjoGPUGc{l5j1i7o6B!x$(&pTpG{65V+QJ@*;y7xnRR;JNEOe+ zhD818y}If6xAYxaU^*BW0 zQ#`xPR|Z+Ux743jFOnm}vEsd4tj@hc zV59SMSMr)^Oqh|hbr@7+y5Izh8S2#|m#n&tXo~z>TB+7@{Hr|39?A97neKh$vAlvh= zd%D4E^P4-Hfxwk zex`?l;2Da&_qvq(<>vAsxx885z}pZOL;z-t!GB{N(&TSwN$joFu#UXH?CVo+@Re>( z&H?xNv`Fd4?9$_bH~%%}(yrdipP6<0UEV4){QG(vZS_ebG?^+76qyaBWqKthSdPwQ zz!IIVs&6Jc)8WOrop-f7QxNUl0xGmRfmIlxjjmz_a{MNpM9ZM00%;$ATyPea_Km}2 zFoyPY6gFZ!u@($`Z#E=~qGN<|`+FOgv`%q`0Gl_Vfct_z(P#Th5YSczv$6a*mE0KN zs4IDkCa z@9Biv>^D6Hxc;RRKslKRB#FfW+!BU{@5=1ZgA|$D-ix(ME3AFqWFI;-n7?M~d@(~% zK{9Sy-l`1L8wC>&H9z%?HrY}=R*!kCT$b*ZqhP6Q9 z1MrmT|4PAccHbu$^j9ek8rilQ5=!b)L!#P(s^UvP3S_psmebHNuSmDO+%g=m?OmwC z>WuN@`y9l>Z-c>w0L1CHEAGwX^n93Ic~G+4e0<@UuJGe2kTO#cvfQo8ter1toCCNW z?F}irjTY=K{T^gulF+}ML>HY1>dAA<6*|ka2O8TLU#YSiz{fzv-z{>{h2G14QmjP8 z@_DgGLyiZ4$Nz|vzXc|oO>tFfTX8p~Pj~&b7qbFu#wGCIs~{A%FWkjV#SGBL6ZeEOriKP^fj@>G!l zn{E7@3P;7f*)#}K0QOIR)|v6zl+I>v_iUyKxDSvwqiC1V z$-5Gin)|lSBqixV?#B|JR*Qza@9(G@r*!Di1UavIdi(jU{~;hALTpl z%Z!e))ZdjNoVGvWR4yI7u6$;)J!nc>8ou3U;!|kIU0Ysvozn&w9F6imnIRi7 z-fhw=*kn~JFc0|2%Tb@H+40{sSA}rt{);@^o!q@sZW+c5T+>BZ<~4DVmuL7!lU)BT zmD#&Xl2A_*<*end9jAhiXX0-cP69tAml8v2C=R&YB&{}nmQ?i81U)y#fXS&h{@SVN zq`Ae1Z%-Wd7z`ruS{I47_2T&2aqO*mkxz&)O(aUN?RJvr$nMi;v0Sdwe2f7((@6lR zdCg*+FCBL;r19p0kWEz6XO$2=Er?!|JoVivdM_>gB7ROJ4$GVU0cu{XA#BW{AUd+> zK<`T58t=Mizp%^WuHQ`t&hD;W0o8vdip7a>Wpv?EW0$#@aek3H>O!k?^_)bhu_zm_ zSazI*C$dyac=WtC<2CJ^@uuIoluV&2V+ybBI2a=8ak2O^6Iym(0$nk4H`)55S5bcu7DMS0!8?`O*7xP6F!p%NfcqB z!f*=81gG{iix7@g91u2qOv8%jSL@K1+b)EiLT1Py-&s#QhU?FpibCzo+K)S(PIn!F z?3yvJFtw2c$H)y;?ov$NAf2hr^21h+uQl+eB5C#pQZ`IZV4ex9tubpQQVz8cPSYOU=jcC{t4JLm0$&yXJda=g$4mSD@Znm(v9o!Q z@eex(^9Rqt<(gWC%^5S^XG)}r&KpS~7GC;Pwe&IGZFqU+nfAB07Zhhr7qpLKZQD!T# z)oxR1XZ&SiotJyHBl0);7123GRa*Lfgt_zi=@o#?cfvMDbW5kITzhgF)puq8-R@-XC^-FehQHCs($1Pm#bv3-)*Ip2JTcdW+xl zOD}zMBwzz5o;+TndYzUMcu>XWmD@3VmRBt1SQ7seQk zGB*7|=ot{DW6hq4p33Uzb@5w5p^30uFMc4Oj^=svZf z;Q%c2+tG+RR*{1c|Bl68Z|C?q(ZDztbH!RzAO9^mk*faF)Luzsghp{~bl1O26Sod~ zNs}WWG$02UFxHy!vy`&#N-;I{K47Fq+X_T1yF^XCF&mfa440s_7dR{OeFmY2S;u|h zVu<|J5ZaP91QmNQ&m_fQl{z&lDQ(?;`giZ=RpKLO4-e_@sx{Wn>Au#>80(2on6c3! zGvGTCqPNth3190`7NgBsrkcixw1KEwn7FuX7eaKgUgtD+qhbd>z2gX+Et4UovFFA% zv1)|Bk_|(neJ|;q@~wGwYL{U_`HNXjG+B|UB%TQ^RK7PSrlA-%#BX4M@A3_svZ_}l z43#&@DscSCK>+!JO86KB0_@U@UHB{Jao1^|lnstR|C93df!o!P5h zY}b}luF+;*Ca%%K3J;r;;EQ<6#(#GI7P}lvx#D;L3q1To>@%75qcEIm4foQ zY>QFIh$YWQX*@^W1hIU~v7j?Lo*JXGyrii)G)7tDs-E$E8!&Ti6$OvYwx}fLUu~R? zp5}Or^t`|R^1OV@|DC`@7Xa0zP-vxAF_tbyjJbv5-zW+)r|Qil#&i>#VjC`%x5UUZROeV3Ujh33$oL64=;;s{?e7p8$+Jz!pdk_8Ns`fS z@9Pk@?NQ+y>v9EXP{uYIv|D5PH50}LO6K`&UE_Q+1&g;fH>=cenX?}NBs*9eJn3bn zTF%Yzu*~$xND!y{b-op=U2=rwb#s*PlzC=tyal9EJJ4fa7ED4sKC$lBABjtlxyo5@ z5lISk5N@Sfu?O|nn6p)t4FrM~>DW5wNi5l_*2azYF$i*mJe@IJJ{Z9UJCbM{1$U|! zj+Q;v)7@KC<;n?M)CT8iR883lfZmls@g$k~K~D4p-n-IsM;ehW*L9KHpQjXDZiHr_ zSATco> zHZ7IZ?L_W*67gJFRUc9PpWNOqk<`jn3Ve^~XnzKF7+_sQKau6K0XFslLS2ZC<`u=( z_p6!$ny^^3*o1N!44y1KNy5M+>qKIL39D81A0oaPB>-ZLY$-@6qI$7(sqI5y z>G>C52ASI!@vC9@UTtoo+4e&y0*d=-@%|04^41zSp^Fr8r_VIg2y6f)PZK)cTPplm zC3EM}UN&wgg&|}UKaG}3eKGq_J^f+DijB685jBHS&w@>>IOiiF>4kf|q9N(&~O3GB%mGPF`Pt1t~c!)eA zGa$lv!RjN`&1yfrCL4>BaPK*qWLxhf(r|>6fPU?Xa@ZF!&`t~5Z7Yc|w*Z|T*!q?s z`ndcEIk`$76htz9y=&dOdli`0QK27hvA5MeIAJQc9LJF5tHr}YteiH-((E_|}~^QnR_JQ!)} zoOuI~*4$QC#kgVR0r9x1yt}{9dL`V*!PnuBN*K@KW3&BXVZZmSnd7>474V zAjOKrCK=DmE00133nWC4hrq?~4X$OtyWpb+{?0JD&t249eHfnWKd<_|%*0!TLP}yk zy6&20VO4v3CAy3GSTt%W(Gzji`kjigV*#j4Mu|*FZ;ume{z;Jz4gl*PoA`URzHzj8 z0i3&k!hQ=W<};0sKQ{l}=muOyP3i@kpxfyfkR-y}AAfcZ$LhE{{8t+#RyJ!I%wI7Q ztpT`ul;Zo}nWV;WrIa(uRPkm6ytT+mW>UIJs&X35KWSE~&Awi!vHhzc47uk{y%d?X zLOoP69>vUu)Gkm~2srl8F4N$Z5$(Sh7?O}k7z_Xi&U(h&QKUoCWS`pppqbWtJzmxO z>e<1Gt}+Hq_p?I;5-l)=ciciHQq0-v#4T{6L8pj>!UmnPonBVj*VR5rUGPgv^f)0; z&Cf^5`byau{o+g?dk9ZCF`rdb(w)PM8jSf8Rh?j3zaoB@;WWbA>Qo4HPDOgw!CE2R zf8cq0+y&=tX6U31ZLsleRoxwvrzDe%xY0s7?1(E)gJ5L4azwvrpr%JJ{P~c}i_)7B zf4!+@njP%7Nw~x3FjeK&vYg@)B%`Yec22-+k&^mlLTW<{fdmiWq0{fk5dCw;(6k@6 z;$ArN@3Fsi*qR4l9evel8qGH&d zW0)W8#H{Av@R`GH-95R){4eI#?&pw*qC+va+jA;>qkjL4I&zrLtQ%6H596~|5Oz!z zqkff9T{CZxLmBPksCqV?o3>8FpJqn0B{AtE$&vXVmc=Of9kfLsmezsMB46@Bki$T` zmNCL9e^)zcrXXaknL{GCH`*kWp(ZfH7J4(<-nCHg>AuB_s-hF-a~v? zt?Lk>^twCmhXXx@J}JV$59oc2w`#_!B#rTnTK&u;@OH?7m=N#CEdH*#+GaF!eHY5YyZ!51B42A=+$bChgyUq`|@iC-s! z7(+}J?=LDp_|DvC-!G&a+GeD8chxcHTkLuog>nSB$`1ft60{17uMIN_;4p}#T9k`K!YqIytUsY0B6_tLl@tkFJddEMFeha;KSU0rqiv**Pd z>-o^{0(9ng zi)@rde}_Fm)Sem22m*^P~@ao!0ph>Q=qx~A?dw2$;@llGrf$pY^V4yMr5?y zE|pJ`C}n-W*XcaF7RzDi+=;atyVflN?A(sO3F1Qk>cG7QnW@(M@YauQV~XyS{teJ@ z=P7A38b+mvy3URUMK`a?=+=C?GNI@V6YF}dy0Aa!mqG36lhBj2KVKU8)TXVYZ)+QT zpes%MET_%Kp_L<; zL(>KOI$W^u1e|cQ8Qea>DjmpWE9A)EbVYAg&vzH7x%8B*yD_Y+neZ8%Zx{!iPYdyp zs}IAiQNyh@-u==`3e8)c0NWZc?(z_j8<(Klxa#PuLeOj&DX6P8?WdpWgW9 zZ?)= zR-QZDW-9jF0=@ru(wOfWhQ03$Db@m9X$xq<_wuaI>gG!(1;or*HYip ze+v3Lsp3!%!zb;tGbeNRUgd>JjScZeO!sD)@c0z6dw%=(<)@cUk@vOu*`Aejn=XJFcIe z+bj}q><{F_aB^kSE#m=E;s(Lj9m-6iQOR;693MJg|Cp}hs=sE%T~V;Pod^r3&ocRT z=#LeGLclVQH&9KaDp2!E&7gOrDjYVP#woxlac$&dr2@`Ls^grKL<1g1eS>~T*kd4w zOWvYu-;FgB`aZp!Y4$EXZPg!Bou;D^7ndX13~#h^&QSkHFH9`wr=oF-!^o?me}Clo zC({~Au{*|Vvu<1ZXS@8|4PHik54@xcuVfc$geI4r@EFEFR4nqRJ0g@#lFYYhPs55Y z*9$3S)9;VX)0Ff@1uyXt;3h7q&Ep+D8w!lIQK^9nn|&pCc;Jw!Fcs0}Vom+dBU8N8 z?esrqPBX+=>Zx9f1l2tB&#xW{Sy^Y?24%Hh`LF9K%&E+McwZ!~C8=^{gmL#XAeVW4 zwda?D?XLDXGv*XL-*J=)o40IbzJH_YHyqtSnT}5XJy&3A?I)NZ*x9@MJM&j(*&1y0 zGfDc}zIULTZxOd|IvdmV@XZ^hC2VRD5$79E^r?+rG{m{Fpxqweg#s4Y5(kx;P08-w zr~Yu1fcK=Bg?|$gOq?mbXtt(in)mci%G8=uoq-M0(^H8^IYGcvzy5|**;sB3&(o@J z3ba?|X+mY_ScCZZf)^z82e`7m!PSV35@e)J2@IQi32Gr*qiF0 zRf)_kYx(cloLMIqb)Al`d4~yNqkN_G6- zqmDvgb57jC`a&rU%S(HCp@pnPk3{uLN0haMX5ZZ#r1M>b3oiF^8 zlmJZteOv>F7*Ylc-g^Mm>q#;<45p4%4CyOd_s)*7?XfBtwwqM`-DGAR`X=f7xS&A7 z*{H$3b0h|;0Ti<=_9G#c-HE!F;;1CY-8cDy*RTw!S9UEE$xd8up?^xjoC~?J#ic$R z!(Dyt>%pp4tl)}^U*irDecG{J?|8e9)_L+??A#x>XO`|l>Vt8)>T$f6(FW1iv|B#} zE=H|Zz6osgYlm)L)ju6deomi{(|d(k-<&NKmjk3$5a%v}i~_@@rMDQr0_a4WX1J!>6jqE4riHJVLNso38JO>4-+1=-2{@D1Kzq5-s5BZW-gs{ zId!zY%Cpt#qAT}GF~wv{l+BOudn&fDechz<6D@NdL7o(_Z(`p8^P>=TjS+8^8FeVx zKd*=SnNVJ-JU6efx%vSCCnxW;Br``tv`mb>S!O#E(b*#=Us&nX#L_Mk3Q+AKB zm`nP*&j+XRD)dn|*h&>rx=X&_WAu<)3Tr^#+CJNb!x)mYhcK^R4TE_&uPvj=sCIEi zJ$kp}pOVS^s>RaxjEEoG29QAzZdv`cLcL=J13q278tT5f*B%3)vs|N7F{hBUlhY6$ zXfNg0o@`{`lsY!LjTb6XF4noO1@x#rV@$EHyqFLS(sE^w;zZjZ66K(Kh^8A3sY<>{ zyPdR8`w%nDVT;?#^o3Efs%HE8Y>`S!aG?G%N`^ zkQ8*`RZAcFAAU$55Q_Hk#wE&Nj|oGNulvFC>g(X7>otq^kQVOT&hnt8 zsW4abx>2m!EwsDKjk(}ZimpCIe&Yl2_?l`+w|+{b1%m+2sf?p%$9A{%cX08J3K#-v`g_b$Ov*$@G zu=UYj`0!Dj>6vo{rvu|bO8@+ND75^1UVP!TTBb4jG(KWh>{e{oc-c-H1D7{m`PcXO zOeR?!ZZvX8x*A5c&6%g0lnz&Y_6P1}9J&>NgA|(A>>7v)*uK6}39hNFUTl`w8S;o14bT&MOeDC9Ki-I~soBuc%Hqxm& ze$4E?o@5j#G|0@6MKO`DnhK#HC6U@RBlb!N$Fgwr)xe(HH}Tc6J0)+&m7TwTqgkL}Xb%b%7JjnL+y@|1~hg4D|V+eG>u??2WruR+8 zjNS$JPF5GZc4S@4RWUah+5j%n-Ov8n{53w3VxnCBX2N({;%C~Gb%dA6_GzsMNFi;Skcn%cZVrI0)GA%P++;+P+(Sm8&UnOlXn1YvGS!GyOHR31^Q?HWabv!$H`F(8Xu^_c{V z8||me{`a+V_+KdCk9eSd?@A?%CBHfx(Mdv@DDRbYva@0Wz-4NZgwb77h#InQ-1v)pbq; zHb%pRn$8@K`3f`k!)Ujh`;RL#;unmku`CXMgb)|qi!mR}SeI1|Zz_LRqR&L@_Y%^) z9x=L`n=5yT+fU(B8>n@&`z0s(Rkt^Ys*QZELhx1$Ouq(X`p0zD2ii6Or$ZSN4=UtNDa+OMEn%cIWj%}PE(TAKJ#uAl-%GN2%(0Sd!4 z*!D&-(3aw87A)HPUm2u`d~cL;_xuqxBVx0OBP!Bue6^jXRwJGI|FZxjO87i419?_H zuB7Go71qaxcas9!AG&xk=y%av>JS$;C06mn-op5NwkHP%WgpKWE3$)~sa}@+O$A^i z$~d%z%D!7W@dand0r#(?hsy|SS9bnpl%$mF!34*>pnVww&tx*BILIRb#&XofCQuof zHb7S)RW!;cJ5^<*M4`uaD*E?+9N$}uJbx`1CGw$f`~sf)mwR(p_bHPivm1eFO;~gh zXfCm=risDvk`4yo0-u!A>7(bo&sYUbpkYm3Cj9#AX=d*4qVh2zF2U4h#}zpFZIoiddtXx3P6rtkooq)F zu+heU*KY8@V{hly#A9Fj1w~pH1B@}%kcQI(8*1!8Oo`?sN9Y0^%$N8Nf;2E_a8$)x zRIJ<~F}GZ}+a+)56d&bA&=mvr{NHWJ$SSSLC2~v9=DWX-f^H7r%p0_Zk+VyY+Ifa1<#rvVjIn+YL+!3PvUyYV^&O3%mnHq^| zC9*uM)rO%LyK1FARNb@cg&y^#I)jeRaA#T$pzdMBVXr0uDwKc3RPYPTjiBFTf-71d zn zkn1o})!3$PBnq%mL-?RAsc~nmC`zwj5A1nkdkOSHizk2^5E&?EaHPt9)#1?+U(Ojis#f@)>^!nG)0o! zP8=Kc&Na|f=u*WRC08_Lo`wWzctBHtRNnC0*sD^2-gS`o|DozDyrO!ec7KS3q=Iw| z-65cKNC-%mbf?77ozh*>4N@XQcXyX`58d4{#69?Z_pWvSfm!R!dC$9_{lwmc!`{1! z6YtT|Gk%(T0m$gF!_0&fsXnp^;tcRMkgb;UxPwBLalg^zt1wR)Y|3UVV)u0sBONd9 zq6)_)_a=^_aL2OYOgIKRC0mni=35StB6!)4yGftCqU%`Jl~&3CqdymL4g@Xp!r2Dh1kGJ|8dez>R^3I_+PzAr zQ*YrM+YcJEt2wHKXKEjh%UkFXtT-2xgY~W&`4}XkK2o3& zx9@^R5_;8yZW4MKJHLi6EBXK>eBA!n&-i{koT$k5j`pXoi_mvBTC~-et{JMZ1G(WT z%gAfT{WodOC`JCQ5tDp8_)I8BLNWMxmyzm?jR zSH6q68^P8-)3iQE_1zX;734UFzppFeyk?^7`9q0Ufz82h?zZ*rkLo@)Wy}sCd~O@} zq-bAQN2|Z*K_;6X18Kck;&yDmCv#|NYD&GG?IO#6L-gNY`!{4^)k|}=)?v05xdbg^ zL{4f#2Vbm`EQRPpX}`@IhJ$s~^u5l5esgojxJ`Z@-ip^E5Y|x9}W8*M^xUmP>6>e7}4Pl|`3F zSl{%!Y1?UNXl<>ih`r2(>-nD-PXb*ad2codN5N+ zFy0;*phxW)gX^zn^1axxAZi+O`F1;cmT0>6G2^^oli;VU)s8)i%gQWe|2jatk;8d- zft^e$>geRNWSDrggE@!c^4_nBU3Q8|VSK}Fu2J>)GjU{nbrn8m)~`OOfKXc%Epq1! zEu`FJE7hnb!Y=EDw%&rFbr_1^QMy;%W***pF3#dzyny*e`Xc~$3FpKl>O+O2h zLec&-*D^bcjfc5}ynr(6Y=V_Qsj76ezR`ga|wY+MvJKMw_8+4(CUja-0RVIP%uq7m*Z${#bmxy~n z*u0pY6grzs%x|h>qu!O_iQH~RqR4G zVL-dHp)D6W8AnoNeDNQ}vSIJcP7cdRhQQ73&C@#6qKq_E;)Cq3ECXUww61__q}-KU ztH?-F-j7&)`}Lwm!I%@y3EOqeEoM7M{`{UU-^9$SrooIH%NBY~t^0s)gb|daLyGNX zQFk_eg`3vYjLXwiM@#&IK;ukpE7Q0gjekKQpRhj>JNtadx^kt0OX3m_=A(W~SEZGb zrPcggCIqu}vNp)+lsCGm1W%S5;ZrOZXkS}?)I`F_s=xD3D9Uy3L4i?Ged2gn3cs%w zzAkx^zkS>H(`fF*Fskk9F=IT%(i>_znqK(>!vzI1)G@pYz~b+x)o>uH?zEu0-mWp| z^H$u|E3Weki^Gr}oVZHR_lydiVlHuBj@jKs`}&;YeWMH>^TsVLK4$BiJFu)1 z;yP-B%gUy+XSZgeK^+OG+xn|Iz!MK;E{t;TNeb_i<=VN^BKf2HNipZYa;I32i!F4a z!IbY$XlsPU)zRb-@=ep*u^Pt_`ipmp zRKplYj{pRQlC=zH-M&R(k)UbfXt?b)yZjj5*ByHAu$+J@w60%x_Jb1Fj_Dn$wMZ*0 z>8X&brZGdSInD0S7&0QDo>8BtsXE6AcYi-N-P6;9)Oy|!JcG+xax*id`e&R^_|X=7 z+uvdfi;gNVZN1>L($;_9if1J>5p%5=9;aA4p=4cR}gMa;eR0^NzwG9460j1i|tSvwCfM3k`2$4WV`8?)ed>2k5f1XGH; z4ON-tx||AJ-#y>>&MfpwQJ1g&w=yt=L2N`v&5`j#pnvC>~64DT-_i41<2|_Me-rF6t znN82qX0wUgTY-|rL({2p-}b%tO&&x2om`Y!TAdPn@3!kxbE3*=9t& z7^0X1PBgL@Ym~1KRfa-!dQsw02Zz*`7%4uHkWnNYe)pW&76$fl4=)S3L%-6OU@2-_ zdfL-OD*SHdp~AdIo3zBGQ3*83P)*@m(Nhq-pB55oxLlIYGUwE!QEW_^{37)CCnO^i zN}gsv-}8a(`D<($jIPe=UvT6LPA2SHvi5xZl~?S(hQ zmvW?tT{22bKU{J*u~813S-zBijJl|k=)-gk-SBH{V^}&!uq8^7|F<8+ zM3*$$R-bL2h_}O)`Pd6A04bfB_0ULCe>dz^xQ9@m@*eZW;8kEP@Dw}NEY~>cUI^wT z)c=#);6;TI-ep>3mxJHuHqs<`)oNPo7OAB$=sFTJ?xM$j{0{qLTz}v6d8a@w=FHY& z0iK@c{T;|>Lq1FSY5)8I*Z}jBKfwM2HBkZoB0CRSrS(s-n4S`_;h*#0uiYZI0_u_A zNe{UW;lW4}M(WlkSfH*B0% z^)OywUb;ZBu&dRGuJT|R+rt%!%JEl6aG^dpVO({6pq5JZ-X>s z=G#uO5%?3>E~a6+gxQw_wMED)?U)Ff&#!}gwno_9jU8fs(^T}zG0*mo zvM~juc!2YOcZXk#$D=7wPF-D6f14cRLxLX{{Ul<<;yRrsBzhW3@WyDzFbRXrx>pDjbq&UuHggma3%qTVuuVj zTY`#WcYT;3>#2j1H*RqT=MiAkbQ7cxFCEY0Do_G%Fy~`Yw4%_6EQc5`@Wx8dGd)jj zyidlV4@*`h48>TsDdnP#Nedi#{s6@DYIAA&biZ$Oa{B@3{cCn)B|;w2+wQA7R_cD* z9U_5v5Gxl;NWDiE1vPEHNt`vA^o5DV4!;iUxTw(vWc*1kx@dle-9k+7#}3r7XZ7cb zRT0&lv5^BEXl$2T?mH~T+Nyv^*)>FS;X+xmv7C6uqATZMkZD=o-=_`zaE{tCN9L-R zYwFvZ!2#W%f7cWc)ZnKkA(JFQW894}2oDAP5=JrhnC2R^86VNSC0T+mMzkH8C%=E{ zyNeb6G8p>hYy3yn_VpKgfU9dBktf*P+}y;P^FEGFeN5msBRq-JYcY51pHx|F-@k5$(muJm51myh6XHlokDVxu3RrF!` zYYYVeezOA)byRt6(sx(U=U*-SLR`q z8&F6cEh9GetvfuB^kNu?U|^g+QrD?pJbufwuhl97zT73LX=g1o&}a1|H`nllX1A0% z{(I7nJ7X1NDQ=AyiQa8O& z0`vqEa?5ULtWeo~C$f+e{;`oO*|)=yRh6EUJH#$^l`>aZ$f2vxdxPf80Hh9hHtmcs z9u86W2K`s|Y;I4>R-jg#dVxYN9-`N1Z{967r2i^Qu77FB)oYbbp_4T=9Q?KaK^vP_2IARLr)1+yLl`G9+-7Ap(OG-k=QZA} zvq=Cmyx*c?H9~l?tT7!k{!GPqn}W3F^qcQiYZ;2%uQqliRfp;UrMXP-OzFO z5@_htCOVab;TggBxpPpD+cUz4|61li@j|pC3-yr93 z*B$v?oaB)nA{oVKO)i!kuZfmAB9`-c7GRom*w>*S0i)CXLBJE01K~cW?YyVYA|7Bv~6HeV4-7GGbxL2_WWzA)4Rh= zftJYk0F1bQQ^xHl;9Fk8Tifj0R1dGF!}qO3bqn2PieL1>I8F*K4~0CT>%W8dnSW69 zkXrbC24GPnx5btHp8Lf<4tm9EdT};YUyi3(@byUs)KQKV`9(u!w&&EAw&j0&a)63=G7a z6Vo=^bJ{UMyD`1yw>Rsdf#HR??gwc^yeZ6|_m?8Mi z42d@2&1E8cr0WhSofuDe#-Eq7TqLFY#%=e%cSXjFYi{9RQU5Sk=yDSQl=4m4SmqCY zkInYjuRL4RfH+TbVWq4VeM}JaUA9XW#>6z1kyV#tC^3%t)FAuk+_KvHvS7vPa`5qkaO9unQem^)qTx7hPM%liGEzozhfG8hPV{ZHlJNbb1u|8 z%@PjRBUZg-M!g7uW9bVUEkF-kj(cZ4Se203yu<2K-N@ba4%YJb?t(U{ShHEIA;ttY zabU~1xBfbf-Lk>=_hKrqQmeNek<*gYcLHTqvXWO@e(WXeO(XIW7D`A}$h=0}sLE2L zg1aVd7LD1THv$no8H#h2Ih3zxnkKHq21@(^{>j8jqSLTo=(S8#&xM(ra8Zb;*;uBw z*HflXeVvXCn7eS`D8((0Kb>!<+qXW`6SI8$dz74>Qy0n623LSJ(CN*Y&FQ%bUZQx5 zKpz~kGWX?39wZ$(q9TTfQq3-vd6JaZS^8;k9?hhQ-Y&X3*&h!}n~~lRo(P&_(NP*Q z@B7*7=#~wP{rYHZR&cp-%GA?q9mx2M^7F>6u8E`Xw3hgAOo6jkU4FxGCFpcQO%;%F zq<|09M9RRqg>NG}!}-hLh=w6YCkYtD;6|HU{Zq{De6D#fHGM93Vd0h8Jm6n_RqDM0 z;NT|Tg16HGK5G#ybl=b`+%cbg+F%=l`OZu<{8g3G|JI+u z_-~q!*D2%8Sf-n^#;}6AjgyJBmrg6U)~*+HNrR5awo?*;H$4JdOyp@?p`@P!yD?5( z!GdL(>;Ixek86|X#6u{f22ZZxOJVKVcQmQmm6YAr9~L%4)3Q?c_&a1hbt~4{m?8a+ z31Ag7V$b$=X*AQ8=bZ8I=kzd)W2-)VP5m)29w+3&}H) zD#RIw-~n2i8K)Uf3jZhzl{CuSN41{$S3iMf8CK}|l^VZGa+Dp17I1oQyS8+Bc-7QZ z*sh_Kn$lwvDSKc(9_<{LF_*EpN57RC81jX9IF9Kjjyt&C^OOx4ZElV5xDb1vENi+P zVoJ&)FH1R^J{DbjJk3A5{t{rnE|qN{=mU6K z_YKJHsc)q*rc*^zo<;teGcgHovz3iaSyM_#nA46j;gGED2R6NXrT-RR{FS@edo?#95?aU*RUY}Km?j}7xwT|_d>p0wI_C> z+4L*!eh0Upw=ZI3z$@4i{vK-Ain^Yb7PpK0zy=zciNrhk=gFi|CV`9YjS^G7WV5$F zf2DXUt83ZYl-j6>i__@&JaoCVeZkVt3e4NayhDYRdS7IKO6x2pQ!tkY(ik@&2fHuU z-oO@I|JWGHhlTU=i7vgzbU_AW*&G0K>`yF-M7D_%7?KakNiAXmfrzY%k_{`D-cv!F zFOEKegby*pzL-DRb|WeU9nb~g;);xLbujYsIv&1g%vNb!)*b`~=a0|=d(3ZY zIsX(3v{duR#6cR2gav6TG5E=@)@GG1Lj5JI9~5AzmX1Be@G^@Z;c5QE@GSQ~cRG2V z9;hgy1j#=bk$;feu@7@zt3&Ij?Hok@n`So(P%O&wPm_8XSdAusj=;r9qXUcfo68ZY zeBG)-sXc3IU-65EC1-hlI4bE_wczMn^?57*BNU5O22tX1=5${k@p|9Ivwma(^fFJ(d+L(K{-)HcC<{*6mwNk;tkT^{pUkzpMWChPj<(b0L&CkeA`B3BtAEzoR=3Y=1P-+1|+@LJQg} ztY4<5!;$c7p|n%--AvH{SxIiY-}~nFN$iif*ZD~}8{mQ5Ocrnv1RrMGF1}x_2Ljo#=(i&4-esZ>#Ow-s4)edKiQ+iamf7xWUF5&Y zH_$s?oYXuD1!eacr6|ChH{euQNztD!~ChbZ#4qKF4wm1UoS|+;kjK ze-$#K8z77wbG=sLk=kx;Kw%u_^~)b$Gj}Gkrt2(dYb}T9ZjT~g`mwXk zC}`u-_Y(Ke`-?UD6klzto9%P^%)WwQiq!<0C)ykt*e5JizJmREImKm1)()#ZUq7W5 zbcD%yg&TaCJi*BnIQxC%xgk-S8lS|naNtk&`*jmI@02(cjKBn*w2LR9duE4%ra z1O$xWb-m1W$C>)X$CFvsqpjeZYTPYu&ZJv>3GC5pLDIV)h&Dw~n_>r=McSloJV(<~SB(1tRJW0cG#n$wT1tF>{IYP2_knuBg%Ag-PVRe($A=`VuDhCxX3&iSr3jy{E(I;kWTW z8<=(7?Y^G$pO-46Qn}Ie#tu|b)f#2!bZFbd>9mggwU#3#jkH^qFpmW2K+m3v!2d;g z*}z+4SBTd+8039rW{~sdLfC6=PNB7BZ-|d=4?-!Yrn?Cq2(8o%u4xDYd{a!B-J9*{ zpC+~h69>=pCmqjPYEgE!Np?h1D3B{O|AP@}?%S7jY~%xHtadWyQfI>Lhm5l`^GDsZ z+4^=!9~Houc!{**ATq@Qg6`Dod;Xqz4%S^ite8QOMM}M=NwZl!{wtZP93PEqqMvur zxxv@|@8Hd1)z^TzA~Ux+mO>w$7n@ES1zl}+fNBfxeCol3KF zm4Qgx{?bSXeqScsWVCnxeqm$f9#bfWYh@R-fy2=FO;v4Y#-n&B{OYd|a=pWGjH6P@ z(LrLjGo)$=(^UF-Q>WOa&y=R;zmJ;I6@_AJq=c z;zlpnIM>cJ_#EB>iECWv9pdF#dtKmfDp0vK{%KNM%H|oE0~(8-Lq}r}>difq7b(-R z#TwcYUYOPAr$iZMrFc8hxzsD)_L$YWQuJ}A1u0|aYRG6Fk&8`lu!(;4yUTtek@vNl z9_#U6Ken-*)7h4tIFMEKe&5mvw=Wx#_T%{5&kT`{*)*pCM(y8#VOD}J*MV7r=T|E` z5(wrhYhbXs0Tf1Rwe&@9mW@$jj>@VkGfsT%yWWsrB^!G_sucJEHh&W@Gz0(w*ZG&b zRIgU4&6C6=LnT+yn55o_Dd8tdv*(3JpobI_?uhH*SLRBhl5K8Mf_OTE&T%1FT5~1= z^4Y0K5Y^fFZ}Qw_Z9P4`!!~C{fiIj#qi4s@iz^;h8%qsIgqO(V2+BnMkU~VOcXR4x z{RxvPDF=EkW+v+jqLsU?<3?nhHKIftHO0D*qjEQ^pFlX)ZS!p5fDQ{cFBuNlKZ5e- zMw!E!H{3UhKo859iQA(CczDeP)gp`gojrHeLT(ZSI>C0m^+e8nrJKnlF{Q6B0(M0p zv-{JgX0J~!R1=Te6#PLyc}|7d5i{6ke3L36?X6`mJv1uF7eZOy^OBq}2s2=_B)Vt$ zcp-=8<_e9^y*=)Zb#Ix^v`2kx=4Bskyf!_Gcvt__zNS9o8$M;GU=1l{V%6nsVk)Q% zf{wPFcCYUtwZd{$TdGpr-3tZlAgMg~9LcJrEP4c4-1e}FCZDI|w~o(qveBWZ6&QL~ zW`Y+&D@(_YHsdF$G@4K{WZIf6nl(vdd2a0xx=#(f4Yp?w^GIzE^AZ8S|0dQ>2hKOR z*Stf>6+K33&B>R>lF;->4p4+V#5`))3@+N=!_WvE_3VbKQndD8VC?@xJxX28U&Nqr zi|Z96Z^Ctou%Qih^Klqva$T#zXuT06JWGE*X~ns7+5Upekk>ldDNU0c$0rMO7I7}k z1Mz`NkIaK&MrqWYU$ZQKT;@*J%wR!5er&EW&H;L0BxR9(814U9Cl=q z-$6=rrmhr)pIuMpvjn*hXK_BJEdsRW0FD%Reg7Gx5eg!F$qJ%Gzx)1yjPG?$>(ot+ zOCbX`_I0c_FM<{Fo_w(loDs+s00s)G`lvX6{Y~btr4cISFznEkqAsAZx;)2CMjmy} zU76o+U-wi@7ZsP9&mKfpbiblfc=4_%eSc||==s{$UlJ)v!coWvj6*}xRXdkQ3{s4h z)VLY)D=^HR?abRVe{cg2ZB3a zlaH0;;>*c^j>K|okyiBcl9?`T4zCHc+%@2`TnqYqaAH|aqz2CBU8>s)CWrdSgmb?M z#!G2C2`u$F7QC30AoaSIc;2f{PK~YW_FGvUK4Kop$UFgCiI?dO6Qypxse`mL4VzYJ zTcWmU*hIS&lyw*(044RYburwZ1mCo`Q~Owju+)LLRITDk3kHa>R2-eiZypfK$Y1NfZoOx>4f}_ zl%;g5T7qt@+z#!i>AF6>;aP)uS#bG!PlL7IY0?CDsGsa*wWKY>OW3#%!5B6+>MQeM zQqjS4Z87TVGp5$f`2yZRv~1``Cik&%tKvNQ)z?b>za$bk1S7o*_k%_&w{a)n>u1Mo zCQ2MV{V|D>wtE}>pN|v7Iv2?z1c#=#(4xITQ)3*T>Lwl?Ou+Rho+Mnj~XWp z1=Q-mf)sX5jC4Yc;^{xw*nLos!xoSL6BQGoQ_NrTX*}@BQ@79a!sxF0vnlZ+8(>vJ zhLi|aGs*Da;w?53+B==E7nx;xZ>ITNIuhr+x5{}$MEl8;cCnMG_1oII)C9Umf}dJ{ zFI)AiqEyh*AMa-t^81E_HszhIq!F|v|v&keIiTNhIJxfN#*^y%2CEaJ?R=|s}2D=?55flP&AYlOGK z^?I{9ltR?XB$#uX`V{Hu_$EtT$mQ7W4#Rv&INd})-y&zNfiUQo)sGr_QzM;~bPn&0 z@kOC~PP+p^X(loOpC%o?`=Fags}@BP*#}ZvA&9b%+(;+FkRP@_ki(TO6>7LSInmIl z|E=e#V)K3SF}|jZxVGNb+r+^M{56vh3Uc1gzK=h8SkiUZP`I{O9-W%57cuvFcKsfa zzqiU5h2E-^2w;mCMPGa26vcD_xPkEnK7Eg%;TXH{qtI8)zv4pVG&4d6o;;eJ=-fWl z-oAy=+T<81dnAMk<4EI;t;s_z8Uw}o-7Pt2eRX&r4=46K+8!TOb4%2p9w?P$!u#Gg zVO8Kd^M7OS5X>3>?u0?&bA^;jYdz6}jEkwRUI}SETW+)0e`t^8%LT#|4e%^S-9!?8 zM#R0BhOmL3EINf_dfYbv2)=cR?rkwAvy2xU+9swCE5KjCtUiTsF;N+6z^Tts&d}NT_@vkJ4Ynru0`w7ThP~m zci?`Oio5QPMiRjffa2$GEaS9u5+R3%8=pvBBg1UQ@RpW5eM&IdHaSYPK0@8*I00eM zuRNgx6=T{!G`M7G>ZqgbZW(F6DtxEdGePMUR%BndyC42<4Gf_e5yYk9h=}3lqx)#j z?P)(FxFQ&O+PzBI@_&~&Pd2+x zXl8E%HIbWpY+H~iiEaALiuM7RNzZlMJNPFa)!;U|dg`IT&8Z8Ew);a4Y~kvGXnDkT zinw*$v(nGw-0Z~JFZ5GLc+F)hlvd2N)!?l%`! zlVzT>RZxw$@K}PD&xAmW3h8L@y?iteCS&`x27i{aim?_x)CwkNdOQ zSg!y>+)dKLOdkxB*Hx~Rd2DEwRO$NLQXB0@_7X6T^iZGoKna&8kfoX2tZfVau^Rl( z_7;)g{_^Mu3?I7R<$kXHc|NE@=GcyLH@vGv|Js;ERi;(PbUT9^APV=y&t3vDM~0Su zc=(FhQ^|wG!wF%9uYS_}0RAdTXGXOnRo2#1w5QDyD=TwUoLlmQG57b+-F46Vfm?W) zZq?%`{JYu&Kn^E~nt5fxt)fyY-b@MVI06&8Q_V3XFM`Q&RcK9P+cX_?)tG{{+e&v* zFE<+P!$iG${(%?}ohlkH#(RSK`@w|?%9RIy4@2BpV%|LQJyY<3%kBNcq18U?f4yXH zaudz`B_@Kn>2Pc#e6j32DzhPKgd=BP;avM6mNJA&tcpNG~_kq*SZjSdpQl-;1|7kbl)>Bxa{O(YUDewBSVX-PiARXpZ#RGvEz-| zFj95-mttsB%SJLaLKT@lyiCCnb%M|QQwD)p30_^bwHDUs>dATpyzM2wQ_^}T_hIgJ z=ivZ@qV*_2is0G(&8KM6BT2|Hp8(6gYRi+EAU6-E!$Z{fpmn;b814SNYf)^BI5t#T z2zhRk?RgPx=EHWD5XdE$&Ss95JoFJsW`p0$WSFVmv;pUFzZ%jAM+`A;!C{MLXv zsfww6^GB{`0slr7SLtSIqVF^5aZsw%AqRgl`b+{ttssLv*v;I)+#pJ?DI^dvPux%$ z^Z;SR5UOtpZaoc_w@&AIw8|C6irV{AoLbZc|xUmdjGhF*{;Un=qfpjD!RYZ0)GVDF4LcOhNZk^*Y_Mo0Sa`O5GHJ zF4Rwa6Z(cUBJ&;Y>{D*ZGkOXvqh-va2Ncv5{UJ>N(8ipdY6g#H0@UVy@Je&xH?Y-Q z=Nb3XYJ^gNk;r`kdNq)yFPtV6u)myHT=`}$uGa9YC$Cw$5Evw-b@H>1Z6>Y<<9d?^ z%%vOkdN+`_YdoF8lj@E_zDYMLiqHR8j<<#1YM z{O_Idojb#V$4I8_oGOV{Y>GsMa&TGZZ%zwYHF+(G(f3BN$Y=dYgE^jJH)>g=WN;NF zKIgi6y2*uY~p+@Q$t=%@#Q58p7&TyWr-Jo8o1Y5#I=7_ zlsmg3c-OmFK=rLU+w(%_dCQ414$1nWwUkUdsf&JK6cBM{kpZU|>iyl;pS5jNOrOWh zd5$rr9+}A6v}KpDMFc7s{-SoeK$E(LUJ|RtY1t1U44+qs(rh(dCEa$sH%(%r?j=0q z>HIU)Nsl^Pq2+SqnHD@V*Zuj5FLIezfU(5iKj^pdOTm z7{V@mSgt2+*6rG1kHqn_cM~)=I+igm0Db)9B(CQBM64!y%fi^$fm!0V1H2WgqFjV6 z*^~QxzWt+nmFKIub=goSQG;|R>hhNe6x%8=A;?&ty>e^*?&8MlY$b>kCr_s7>`5U4 zd}*7fXkxPeNQI%G?3x+6=dQHLP(_$Z5CzEKQKGg&`N%CN9*(X7VXS_D&^Kl!U5M_h5ysxtPM7DD=&Cja`8A5m6&CO#T zUo-zu?l|}PcuPnUsJWg$6%@og1zNbdb=RNAevzok=E*0Nb5~zkr`w1Gp zw~}ez?MIk=m};tiT^KWE!&2H`bHn6RGeSenTmIyYB#hB}33&sqV#9#LZbO6)^Ph%D z@W^pjL=NG2@_O%8a?1@mIyiA@+tt|-Nh)Ibulfb>%s?Ki5+j-%{4q>rrgI|Uot1_N z2Lph@wf;9P%5xQ)*1C0rYs)l!RIx65IBfc?+m&Sm3j*E|cXf2KH#lCR)Vs^@*dWCj zS#$+`HY+-_@Ni4q9)nz}swU-1{|d7;N^{!&9DT-5xSPWMDfGW@4Prd@3Luhz-tZoN zU}KPkHvP->MW+B@YK?5krm&yxei#(QcmyrIt$i5EL*ok&tYXtSja~$*UNdJ;T2e(uHK^Vdc6lb z@i>kx%Zjr#S&Y0vI2cqWdWac)7^z8UxPsn?E$y}V7a||EeCN&G7`=Si(Rt zF&OH7LO|Vd=6+s#B)11cjHp6Sy&xYOBk;(yVrLD~A@;N=(d}?mBOYz|Q)EqkenU5` zIU(@n&oko3a?_4N-6J@=Z@;rBVTt~gP8=0xpk{Cwt~WzakqT zJ3U0!@=v|XM-h5D#h-c+`DiFZLvNBblCcP)op$V{gmS$xZyt&YL0*TMLOv`}DnZsc z_kW_fw)5{|#|9#&dm5(HOr(%fWUC~B1X=(6fY{81z1Y&zqWyk`95k!h>GiEZ;HF{+ z(t!Fuk~v2Cr4dxl)`36pohA-3=<8d$EJE%Ga^K`%qC7#NJPC*m-jQVwJCT!_+N>Oi zZ(=tIALcyBIsE4w6J6VPATC5pZ!B2U*3;5;-OFR4JEHhPc_1FcQug4Ci#`Xx?aK~LY4WWSAe z70)kumDLrU8K+IRTKjXt9en}T((WOY!@Cz`A{B=sVwacoZLhsv>lUk3+$nGL2kw#R`{9pB8R$0XQd+X$J3yEXGL{Z8ONa2=| z(1nIs)Hulqy~x{^XR-^tEU#P8(`smI-w~3@eh{Ikd=-#@fDhH~AJ@ug`pD?w?qL$s z`l}$XSv3HAOP}JvjRe1WepgO~rtw<3;T{$;A|^)j;l_iABBAxAq@2HqBNvv{d<18{ zJXjh?drUHBnZYNc`^J&(bJ(a^G2FH8yWJy3)9$(0OyqnTm=R1D`>Gu}n~k<%Z{jI| z6-oQ+>vu_sM%^qQ^+hdcmZy3VL9m=)e*XdwK_)&kOk31gfGul9^p)o@L@I90Jx!?~3xe-v$^z6ghH)Q8*Ouhosa!S^=Ok^j zW_q78wt4uhV53WGG~1QrXe}KRovy027P9JjJ{jVmO2LiCC(nl2p4%I**TUcZH@oFg zc0{S>v|eAmGWJoZai}YJ%U5r(@|8#HKfU$D&|}pk&8ft{1`Iy89a<5n%@iv)nDR7) z>lHi|VPB5~@pI~y%`Su@P)p3E0=|V%ulV-ZwEvWR-IeSfQBUsIJE;|?H_^kT?W-fX_C)r~Fo`Ho`B zNxo?xL*}0hm+wf%wLO)VYqX&jN!B7sa;ty3#~bL%(8r(;j@BjwECU|@ECaCD-4g4P z#-s_#QA59jySD@U8?y2RIsjQY13nnwN)z0FN0DbS8yi;qB*T|bRqBp4>lsV0-+LX5`gDi7F*^Lo|laN}55~*iIasbUn z?Z-Di#BateDjL5l%d<}|4}EqUZM#YS`|9& z@&aTW?e4KWYG>zk~owBu1|z!X#41HP%Qwz>XnYWDd&bKBj9P4kPvsfJQ+_`f&j z&LOAvG?`|nMCav<+mC-R75xpkVBmuUFCIHl<@q0gSk!%J zFO3?W47 zWRP~iyJhBSu5YIZ@FFZt>zTtO#yU%!^w_j~UQ3zAlNTKSKH)#=bhr?&Q;sg|MDR%* zil9SKG%&(#2$Xm`mBPc_Cp!(JFZa_3Prdi|gBvT)#U~Kvu@cl@x#P?=K>=ria3FSO z%m+DLURbBSje`*zFqwmD%!-hj8P|*Q$PAGlj+&VM>y|;;v}Vcj;ppnxwiFuks}gyz zl_cr<*$Z+Y_A=jCjO4%LQF)9kI`z8A*N6k$o-q+c z#}6YFXYiv60hZysfArF7V9*ZvUX8knfLPvsqnhMudg;VKR5MrMBFXPvodI~vgGPLj zU3cxFo(?{$X1=d?mk_aJ@(Gqu-579^D|jlFEA^W2e`cfXp8rOnhlEG@(D@4q3%NZ_5Ym(P@|l|bqUOAk*J_ z?7C@BTo{t(sFZt^roB3!%m;hbDq3Va6NG@mJ)_#D^SeDr3=$Xp@VG;|r&E9F=~YNd zo|l69vE>b|9Y<6oHJ`(?q>tFq&1jT55R?4-c}uyvVSdpS$W?gWOzfH6He>@FWQ|IZ zcVdLF1k;6**L};#NJ2i(i6!zNRBk9l{_~-}K#5O-*gQc>=c4_s3mfyV%l}o*Ur12( zVKv@wj>987r!@M`A*0)j0$Gf|pSulXdrjE_f5mCY>aq+Y$d)j}LzHP1us`srOMFhG z^0qN@FuIh1eAH$$aGHm z<)nI8xFFJWAvw(?Sb&K}xepTSnhu+>Ka*y1Ez)@#ZcDa6zefy(i~r(nI_IHIr-7^T zzp{1Rboy9$+4m!+evBDVBT?cIRtt@M{OcQ-XYVfb6rBY#02^#s zeS6+6CUv?-ptm)a#Eb6;&Av4y_U_3>9~evsG$-u^oG7)$bYj`>%vJHf z|MA?1vSy`t>{ocCMYfOdIIk{NJH2(%kaXTMC7#aRe$S1_4!<9~{SL9{p76p*2ZU2y zS#7)4QTF_V8ITA4MKnvmOJ)Cigx5+^Uyg-Ur(QjQC~<21IY(-Vf)Vv3kvQ}im zN~>lSWtm=jIp)Xd6=|!`V`PW6HgcssMnG93YkKkLGzm5#M&i0%f0p4adgQ6_cN$Li z%8NtU*8SO)sbKrlPmKAG(WF`((TJ&I;KcMWJzFH;FA<}lW$x!ZU?GEuscaXsNccux zCEQ5^F37NaQmAW1Y$0QmtiumX5Y^7jWn@ha-~_!kyQx;?+P*qAM7Yr0o-RpWt!=Jr z`evSY(*>`ub5S|MdvB}<0a7&&{P(}JFUAC}+pBK8pL*~WO}~pWYcR-zhmeq5LyU|l zJit#&R!;CS#+>?Ed+$8t!sj;rk+W1;JtIqrzPD}*k)bPD9X zJc>NlExPiCO7^x!URq2{DJJocGF(g|u6C~?>~52R$Fu(d??VNG0X%D+{r+ZdqUuQtu=xw7B8*_JgLkK{@i6#mx+cHx`D|=cQ5ZGHD^`Q+ zYp^>zrXWer-^KOAyN3Py#!X4@P31xpILpaqY^RY`C3U?T?S?9uR#SFGqO@)#TAp2R zzz(Rhxz=^w{=#es=m6bQgPIE~iPhJmu|yKU%1$RildONDDlzlsHNG*Fm=by$t0p@n z`|1q!4?MZH+6??uH-@3$0ih*0E&5D-7OsqmbyRh;6v{2CIDm>5X+Y%OKX#XbcZbKC zq2qPjr1xNLS=E{fL(YNRX+&5Z{hqf};dSLG;0-eGmS+l6IzDW@d;ykrH!w+wjghr2pz+9Fz4k@g$ z0A%yDEIX<5IjZxFX9|mjvApenY>rPeT|!)z9EpX6-2fkRj&)?Ql=%@-5?3V%Fmn&P z<6i-xL3Zr_Q1#t^N$+pi=X*M39!pXyOA$|*S-G-OoOnvh+?J(=14p@V;})@UFHFgi zi`2}_ofG#aH8uA_+~NdrfC%b?=XpIpJpaMxe!cH;UDrK6gZ1_XLz9sS0^u?_7ik(F z)cT%^PIZ<@_RmWy{C)erYdO~iIK2KK>Oh6424fN8!2KD4Rhd-t?rbiFt7_TH{tgIr z;#5(;$vL_2BL;`#jkolN<0=PmNDn!%+!(fz!gc(ufAM1VJ+GZL-lm7r)@^3mDe3;tQ15{dmlG>3F_ z8&b&6mG(*7Ly zPHcZeo*h~%{q*w^JJVjY`caI%!pOr%#|6?e@9WQi`E$Bw4{}$#+s<&W^;9i2_kHBz zB8@FNDq52k0 zu=Zww2d8*7Q2N96leyQVJ+)y}KO0rpnGNK;zr5V9vRgT$9q{797)PyEokL0UOTfgI zou-g`NQor&)wG#uyb?JWYrC|U_JgaczSiF%ta}Qh>IY7Zjn`~=)OE^8NbZYwTkRx! zXN)}}BzR)r)GgvA{%#J~sMqJws$jeHU3RpDZMvuF%NMR!jD*GQoh|Uv0H_0Gjp5>w z1zVWaY3^mH)%CSV^s$a4)`I&U>CV7bY(Jk2mz) ztqcEJ-MQ%4UHGdIczwRBDS$$0q-2uF^u{aqY_?AAE6pg7lKKed&{B=zd(?U7 zUwt>T&pj%GPiFfd#q`+>O3GW3O;mN)s#K-*l7?1Q&yAHv}+&%@S)Bb&-H)To3DD#hUD#D{YB79 zcjjPu>W=ruMp_fP&|qGSjZ|h2j?fqz-cg#dp}W3Zq=a2@S!X#4E(5jm`hb2mb)sV<)=%Q=M@w6vMNQ1!NLO24KPZsN)wJ_Q3m6-OY|S@5A5e6 zAaNS?lB&z#Ij^tmb5Rorn&TO6VX_(%^rP*E|^THK4@;fVv>M0o!S z7M86a!Qh2~jf|xrMrfLblD_f5a|_*%FEJO#W^z$)=!v>XO9C5yg6I#Kkv+1t8W*+` z@?o!0$3>LD^{Hc85VPgOqwnkE!cL^K3(03R^t$wJXIw}gm}`pzp-RPXQ0*!CO$o+! zy&BF$sOI7Quwc{aQ@KGVKzOlz@^T5)vFv%nva3+dppZS_qw(BkCEuuJQ~8S9lzTBi z2DE9`w?rLrIh%V0RYCFIJ!~yGo-Eo8uV-i)2K=}(YbLBhHYvw#jvL>b?KXi+Mo;W!1;s1xh~T_JB0VT8zoy zVXH2b1InTI`N*!W9u{L7D}J`RA>NJe(NMy)K5O%MmP{tL zh_3>CVpK$HvTVvRDz#7U_TH(Hlxex1Um_be#Oda{ztI*>59-63Uwk;1o3s0Gu&Qx* zpZa~eV@bV?UbJffH=q{wBFU(>c5z9~?H66r~ zas7*5e(1j$pG}3he=RWp5$b)mR`u88{{Ce?Bm;8pjo&FExqS2b>}9#}Z@_1usXOkS zUPBiybxQAgOgfKFWKO_eYRZ$fkIhl}(-PL_1kDMD_@Gr|*o5kM(~ydRKmk09K0s~m zsTsZW+$|=~QmFVkNlCBH?`S{+kO@IGD}BFpZtHoGKzvVI4BUM-y9*J}UJ^vBZmeR? z&Y9M6EU7|XOb0@c`mlQ0@y>=;v-JP07oM>)^?FSlQlh|I#2P zd3p@>30&*hki>d{jdtw`W~Zm-#7(xn_19gUeat_ySvoGmZA$*ZhAYv6QaAS zZ*T+5dl>`ZM&ILL3UagGb(5&G6#VVIc7>r%2jvUwQUw-t2Yg`I-8<~Qc1DjS^X4zX zLUcJ+k0=IEMUqO6*aCD=q)%<_5jk_&IjWOeza47b8`Jfv8F3I_5|#YyVClJb z$$KgI!ShYVM`%Sw^ZCfW-GWQrbUJ0)cawh-3nSPYAu71jT{$LCEiq{Hj+b#9aNFhh zd+%>Mc*a>qa44k=T~D5gs_wOihYyGa>^2;9_1C91mef*USsVV#v555bS21D%p0Sos z9|X`koe5G(MF$faar;6a3)Ll3W^*Tnt(ylSFM4dE9fQ^ZL8N{^$%EZwygNbL;Oue!ajT5(n(iN0n93-;^xj=OIGV0aDWNR3$Ov@@4HM z@C-dq_-JEbjWK}fP+cv$n$-P>%nLm}`qGqCULHhG)b|YP52(KxzVs6f3H4~amTQP` zC&>{M-JF$vgKm|ynqu{))_V1*lMs~^dCdJ!Vth^WQcY!N?<1$`d?0((kTQ2GKbR2a z6Y|8Ve>>wBM=N&tybT@vR|09zX#G;&b6FLGPrYJclfw4PEVqHh9@?Cj(ahm|)BY+( z7)61D_oDl>vt$0jJFKp1xDNsr2|)1NNA?@uUNc#=4|sPC^8Cr&55GqDh7P}f7So5Y zMoCAj$wOn|m(1_2MAs0({k`r45Pt8O~Br{7XB=wp{p8JoYO?81yLh?1g# zP78nL`p{howeEC7)~zO3`Ri;Iqt?D&b4q)fi%<@VcnaKNTM#!Nm zI-5TA>YNOfA>x8o1Rq3@XC-_=!|h_PLJc;=KV( z#?UrScq(vlerHl}pRuE%F>?q$#wT-g8w-0i#hb^R&W4F1>?n!5N#pm`etAt%4T9!V zuSCvPEtJYVIw?b$)&=dPgogSDz4)U(s`r*Z-Zj#zTPkJ~QDZADo5qkm zXsT2FO^%q@QGZ~M%b?tNTFS1r#SNO&#K7sqW-~KZEF(%&ZSPuU<5ENIF)eOSO@Y@>g)czR z+}|x8o8`Z?^>{Pg{Rl9_94N15A8e8)LWO{6%D;DvFjIBab>Ui8ayU>6s3 z^!0e+ac*>91T7s~0}nczAS}3P<|acsXgbhAhZ0RYNdzZW&m2knj08)Eqf_0#UNgD&QfA$n~!~%v({?<}G-YqXVoIBo5&W|GC zo~^fy2kW>k;nb z=jT)>>52y~O*Cqy<{08_hDR#aH_RY^^A__jb z2Jh^)anOAX-<)3l(w%>1EKzCl5m}La{n52?8+w3IptfbLe=NIM!c#`|w+I(-WIt`n zIvXLRum64%$(&o4uSUfwT?S9$SOugh+L?JUsDJt}6n3~bwK|qW$k@!GPq-ri))F*G zXRy$(SUP=$u@gr=-sqh)zV#|zE5maUWZE8p(i-#?i&G^>FGsLA<{~s{Ix^no*{gkFmgdZh}mz*iMxtBOr&x1>z?=Q z(gzlqza1d+J*nLvSfG>hu)Aa>Nx=LSEIdxhyYIPK54L>}_IR?1K@AR|@BX_wX6N!1 zSL2c7=24ke;x!BP`mADLF;E?#4AZ#lHmiIT^;%ZKLU_W5y$B8g5(%imFgF@IHu4!H~hfU3xY*}%rkwR4veVgc#pDn@O} zw^}}x&*8|RR5UtMxQWULI$9F!j|0Uvt(ZyNusZzI5xm>fw<+VEtxNn|Fn2LMI&SHV zHND_HinZCqA{!r3s>3EOS^egCfby&+y#t`lSl#YAv5O#58^71$Px4|{IE4rk66#QG zeCj)V#}~Tl*v{S(&`t1e3UDKlH}zS($3Lq)r~g05cLH(i9bMsyzW8#Vze08U+6(3! zOYhEfAjVv-wEspFP<%@c=Sv!s8b|!$PW@;9UX>wNEtvVo)Mb>W;XDrw`gX+P7&P&V zxr`60N!z~XnZdf8r0otb6uSI?Mncg!^n(sCu6ZR-9Af!aQCGwQ>y#S(?o?cBQDE4C zuE`~@XI!sb-zg`te`z;J36weHIu+#0pDR}n;K6{W#L|M7${$p)hJ)`@Reb5a9IK|p zRJdej1Fbk7FH~|B{(v8LAN=el-I6|=I2E*hpXGsATFIFe4(BBnV(Gr9%z^r%Q)7ky zMQQzt4a6c2fPpRjz*- zTC_Mva$eR9EQ&2vIwuT4eV%z3Y4X=tjadgWV%D288RWaA&D>HXbHq{c!i_sDv15vG z;Og?g5km6G;Aum|AYfyIKoA5ySOQub&h|;&J5F#szjV&&t5^EFZ`e0}`+adl-@{`@ z-i)B_i!X9=G)H8IwM!qrf`U?_09+wV9Q%52pS^6N6EsYkIKU7&ST8kmbYcdSxN0j> z1f5HGZPecF7Tlr`{r8f0|5dO^Q=44$uY4YFm&M)JA<{I#w!QfO+P zL-7$l4zy+LkuLh{^Pk>&B>AHc<$oC(n~@g!*P0G;f>^x^bYj5$!{rwDv_rrGVfEv7iy5*trU@nb8Oj<)fLFheIaWDP31PLEb zQ#@bIZN}tIpY^PvsnoH~rzqgWz#}zNAC)km`^C}I)304-4j09ugACdQNhU&3-W#bM zV4`qzK02-JTnicl(#RwP!J3BCFFM@KAJ70wg3=XwPkm4YzKeWzO4Zw_iP`1~@h9y` z2d#FJd&ywgJpf;TQuTe+n6|JdMNv~v=i_dRj7!X3V?$ha(K_rJJKhR9^0&UT7_8FS z5RKW1UzfqRC)-P!DM!LRm_IKB?9Q3$#{mGsWUAQb2=3HBGZbY0bnheF1bdWW*pf%J z3!%=Z|7(xRUr(@3XYv)e%f6bjDfo#S52m{3TPdn5=#N>>p*!1Ow7De$!3QgjTJ=iIq+FcE8VV=_sJGxw6P&}Lz#vhUcj z7TWM**9X;bs^kEU1G|E0XwPFjA+8^9W078@xdk6ut@L@iYD~_I>T1LDR)XLx>%5Ec zzmNaE*S?@E&bHSTN$qD-@3GyU1-z`7griG4$PyEc#%*P*PEP&-!EfVq?19NPdTjY^*?^X(M4jddK z6Lrkc8aYrplp`c{6G6w$7?v(7A-z!9dt)-%Z5Z9D#nS5Dbe=(({I?t&0;KI?s_ z0Vqrcs`?+>0eJg|LZ*$bI$0}B+2nQL!bOlP?r7n_ogIn_azQYNeRX0Yu&!<^*}WqE z+4%pstA+UV{n>=@Y^}2iEFX0=*G6wT!p$6Xe<-$hDHU^=-!IKoAs;EEKgTa=6kDH_ zadKb`1|gN@6;VeUSttkJmx|tr?s6?SKcm$Eq7Q^dwYt~zWGRX^QJDYIL8xX~C`Cc2 z*w06*iza5b-frUhdDddame*_hl0dHi^VS@N_g;tM5ptl*&T%qd4YpVNrHHFdj~rgh zvKPfVYEH`$g|Q-5IW!(0dA(=OmR_F7{n;Ynm3O?Hg_>PvI*>5OxA0UBQ4pGN1|#gu zDvsL|4>>x}52E)3=*NMGdQE@s_8k@NUiw~v-)*}a(@_G#xRFBimeCuowt|bkfir~&V{#QE+jwM z!etzN9~@!^%<*&h@h*1wKPA?7nmvkIH{Idzv0%2puUvWl;d4MiP%lH7=*E8Aw_cxF zL*Je_Zq56A_U-g{;AW_*6Rb1hc-=m>C34r0KL zm*7n_AJqLr!=_#H{-dru@e~UqCEATIjyWkXh|%57fX_2~n8t%o-6nt3r?H+snqQsU zo}kX{G##~4kP3&}zP;bBS-4#A(c9%wBdz@udq^MMl^vtpwsH2$7}>!KquhE>xA4K{ z-C;K7_NUGhoS(}%UKGlAJb28-1P{V`aPdU+k8whRyUMaW-nnt~o%r-S z+P{8@M53=EGfT+EYG$Mal}q@vYp~h+X^J547+o^T7@s-XOIro_&FBUhyq2sap4Z(R z)(I}4#Mu@s;Z1(*HJ2+QkM^lksbXDmQU zJz7LFZV72XvooWj&ytWh|L&^@fU5sKG2`R+Jjc!L+@Ce62O9ya$w8ovnOdKP**=Q{ z8w2*D5y7TR!y;#H^xfz?9kxpI=IiaT)+LT|8)wL*altjzkV$R%V+FSKKV)TZv|f- z_=&kCLxXV2<*<3;X9C+dUXgp3(C6@0dA8_INBPs>c|o-H^!LHoH>+PeSyRG6o1JU> zD_wI#89sZ7($b~dP0(uS3}X-gr7NrW$0Q($Hh4GB9NAZfyD#r?orAq>tE?;MAov}u zwCD?~qBOv!Nc&3{Y=0(M=H!%!oL6=mW?yE4AMc0wy?xCMy6sU044)kK;;vjsU3PMe z5Gz#JN_+r(;P+!;OQ4-VO7XXkn_b$7f%E768yP)*AE_l50F1geyV|^6m}pp^aXdYfLfH_efTkN6YyUg` zYP0C{9wnT1wCuoU9OK#SqURB9Yi{DG{3$$G!3W|Ku=UpFG@DUCi0GLOjXAb}RxONN zq94x%`P3^CRUwK5SZ+MZ`Za9ETNjWTfX8$8Sco#W5RRQs&v!#!TYPz$w|)>FI?^qLN+{Wa-v~rdWETWL=$f07d@r`qoHJb+ITO*{V6tIK@anodaw?}4MXhz^@scxP7)sW*US7qc8F({};QTeIr@7#V*`YS!` zziq3*HHunTcwo|=0$H_bRruPs{otP>VsoVXM1q-a$H(Z)`q|K}T=R>VyIA;$`;?4Kg>|d@2gB_E z5Fu61P$wj?i_@slX5v|j+7WjXbEt$ZaU37?fiW47scE}KZ-vBqL~#LWGI8s4{}K~8~PRY%;gw11SMXt%E=^|%YCq{~oNR&KA;h>r`E86U+O z+ZD#mEq$%}(rvN!q?-ORd6MJ?wVvd#XUZY<_~apTebX*Wd63T*b--nGRrz5x?Of_Z zy|c1ag$f>Buf7Kod8K;i)}w%q3S!}jd1+1R0ldr0W|OCYRIO9o zilYhGa7{KCe+iCaC75VetvR$`IZC(a-aGg;w`VOh!OnZmjh!m3aeLn`Es*0B6a*Vf z)Zw3{j?APWCGP;63`EMloMQE;^R{5M*X*R{39W9ahKg-=x(m@17;CSSEkj=GoAKE+ zR=up1y6YVgsL`|`-ySNq2`=UJMiu%%)ArgGMOER~$dhBzs*lhWpoH0XZ?LbwHE!Qn zA+Es=t?q&O*SCRItieKswf#~tLGE$q{cXlAMuuT){&02Gw7>g5zSng9@*4KW~jP>`_>hoj%bVySXbkx((&FTE74^UQ@s9zFnw!2h}0q zd<%U7ohUJPs}{!3$Gz)XSk_0gly9_9fu*YTvpo(Y9TXGDjqt8(%W{oo5Qsz3Gk{N~ zxNty{uqoup?eLn4?HjEMEffVyY9v>=@L1uiol?EIasdl$>MymzFO|0Z)OplvjI>nE z7m_?QXY30%YC9L6t<1?-S&5eO+s_pxW6MRV#eZ9zG~lXEv=>4#EIC*Op{&=+mor0b zmz9yi)B4>L5z__)1s*;Pc)sP>ey(}DJ2ic$IW@X=R$dh~8s=@mZD^bt4!~E2U+lQD zXbqRLVjx4DwY-3c}QvHmA3B7>Ok64-KDqY$*jJ}6uKJ9>PY0@ z$_pJP!OX>E13sWE^;-wVAg@}^nWgPnSa~DQ9m-Ms=EVIj`A|2V*1xQY}-0rvY4 z9ZG3gnc6SHy?(=&;}E6CE8#PjfEMY&!ouFeIK}v(jT2B2dZN5?33@r$+xe+yz9~u` z`q#z8P9>oD{`O;4cF%PN`Ko$*!NNlp_rILEpSC~w7q+yZ>jpZ1;$P{w?f_QGuNfT|yX6JL1IJ`s$V}905DUBqO0rOx$5>i+AJJt8+!L zy@BtyflYVP%`!^QR4*v5Xzpq#8K1&d>`V*G)UUXDpRk?(GOiG*Mou#reNOp*alWK)Gz?SYfPOj*-ws%hIsK~JkMlQQn_!_46IOzh% zJ4#h4!lP=3aj{I5a!S7QHPb-FsT6Ld&c2WGX7O_ePhTWOb_~Wqj#G5f_cPQo()D+E z?8x>DWBx)tNk91?GlSPnlv!c}P{F{zw6%RxzEHmFV+ZDCq-!c*zM@4)%@{iIDDjH2 z^)~pVK+9X5JUyqN&$Xd>_mwIeNywzT8psFeH#TNuynJT*HnoOvhYYq{;#uevc5R$* zukEHjG-q7*=`#xCRK_a^yN|W<(tevB7C1irG9m6&Hqbp5A*XUJ^VULJRB8V;ps?d> zlH)D@NLgfTD|9HKMeQcRteDwc92#gJJCFnzAYM$CM=Q7yux+@bp#W;6cHNf2su>JG zkk4DIk)8xar=$*o5KT7jX4#u%VJAHL-iaf8`fB#rc+XR(MH6XVSSM4fQHnNy)Gp;| z(-S#=ZhrAIRa>K&8R6cRc>ak7HyBniFSVr&^}0I$HrIx`Yz04B-^yEIPrEAeRdLAsWT7R55q+y@s#czPim-=k3A?SW-Hz5mx%w zv?*guG#zQXQ^jqDIZ0XKTTkrt6@3&(QEWWqpkaM?(vbG1x}wgvZ6p&p#@3JLH3=M} zx*yW=k>eoaV)gp{KVCpF+oFWm`LUY<8$br3-`^3s(@}$ru$CI1JZ0iu`aliNQ}RXS ziT)QMv2wn(I!Ua*co7gJ!S`$Q16FYmvXTb3iXBvY-BnV%_}-jZFPg5Uqh5b&5^-A? zB?+>DB8rdeg;QZF-kCd>d?Minnf(fOO4T8s`bn*0|Bb-h{*TrV)N^6F2fE(N-{AO&{Wz(}9N#5_BT#B!QyD zx0m{hF#b`@SR;9GJ6@mcP`Qkj-O|*c2Hw)=-Kl&+{UW;fG}%o9Xz?vN?qIDzT@H}Y zT2?GCHJpQn7d?{;j7{Zu>KUVv1}zS30Gd&vXMCNe+E-rvp*Ly-D7o(R(-*sj!3#d*SYNxO^Ja6A7^^atut- zvwiP73O?ON59rZA5?^}J2RSuI`=n!Cob7I zOsV=&wu1JhQJW91yVoQ!(s+9D|8xrw08bEYv2rMf$k9a;WaIlMgv$A|l>^^JBr*g+ zJHVQKGq{wK6=xNk?sFr6r{so(h;@-TO~cDqI7h1Ynrh{UsnycK(`6Im@lZ2HGc>If zeh@(m*SssBS9}KHdk$Oj5VG+8fDWe^P-FEUo{(pF$?G z@KoW!wkETgB%p)dAUq}a#a$ND>dUL_iMwe>ryL*l3jq%gW&-zSmy_%azq{>k43Ou_ z@pa+-s%r<{A9v^GFw+Zdd372N%0VS(=bBa2?6jy2rwbx%@QLApz2gz0A+?bP9>8e(d?Gwti;J1_WwxZ zFC1Nbk$%M?i#=FF*jCc}2c$AR!C0DM0giW$*Me9L$M$xvK6XBdN7Z$8lM~bH70OCo zL|yMZdYEsjrB5O5d{yuDqr*Zghj-+x(-YZB3Ee1V``DM zagB2w-Zkdv$_0S(?6x1r^fN3SYnLBBha9Z=YpS?^%isIEPHGPVQIAUA@pE=$0NV%h zb*j&>Ys;ptvcq3#tkhA~@E;ncUiophQVt_|lqjYF0nwhgII1pf7A+=tzO!$n1+P3f z6){>Mz^8D~+e8Bm`|0 zDZ&&(!c$lUQfTRksNAx;>hhq{pmjd{nh|F7-F-3}w<#}#H4b~Ah7~M++#)IUErRk) z=XwL^sJ&4f~EBDZlYS zUMKwitY`aaX>*u2fCkCv%RG+hmHK&K%W3LWAhk5;LS>}?x8pmiM3pN_4G-YT(LY)<%TxLA78&Oh5b}eg=`w_ELC`z*YMvqRv2&1m5mm+f;hNj4??s6 z_uQ1n))VjUWM_oyL$gz&X>6ft?59=HrAU>mGwx#+(YV|N1HD3V;mZ7A{q+`pW<+e} zdehkRUi*1?W~fSLK|UiuN0R@6eV7zO&lLzd*MvwSY(HB4M$)oQcpFoi4NzWF|xs|E? z?^)8SZYqnuoJ9-rGQk>SXPuH31*b)>%-h=0ZKz)HAJA{Te5(KMbGe!?bBho5v}#S8-}4v2WY1_GH&|XKeq~DvrYE)U zWf1;8$8`Hrn9W|UwNKWSnXk#2>+q)e}z_g`3Zz+@N`@7 z(seG0)Q493ZSmm4=XpIk9(%wC>$j6#YGhUYaJBAw6QY?cHb_z9j@FjMZ#y(h{ayL{ z*E%#M>wvvUD#W>$EwyJ>KTg4w82LM|)%MH46TEpe5~eWL+lFie@6fAzPC~M4WK(3w zc%&<^xTAaI0&IT$4>)=9LY#bx*;rYllX|^#$dMCfsOJB5_M;jqCrXyf@SbLRdcPw7 zciU~0thfz51u_@8qSfx3>!Fk3^3M&I56t~pnxF}1`1_1}&;3?hmf%$|e@9ua`M2OW zD8ZAVg`S^p)d5F$;#2`k|Lf)g^SsgQ_>|hu>37CMt394%prRD3`THGe z*bVRhnE6t*0w|-h;fYHJ`4PxR`Ik)X6N41x&R+883BgOxozBHs*1lcQd&V8K`{Y%@ zEHE)aGDEO7{@Of_Jr{;JY!M?OJKmrwkh~)wYA%naKSKofI@GkkPDZwSDCTEe9pqDsr)+^cS-<|OOkZimsw zI#q5LksG3R;!lL;p@Q?&mGFgB=+UPneXn_gOyo0@aJW^ka3y^}Mj82o6gGMd&Bn(= z#{Rr}A4dvD-@zwO#Pv|(ewVS>LRrejO1E>vg1U7DB65GZMy=j3Udir0{YBSPIvhVrSc!OppEI+z zi1XdeeU9AwQ9bs>{aypVPB#-)g0FrRwT2$FtC0lNX^CPFVwSPBMGPD98HxGUf zJ?|}9KTPvqG*DW5;XNomaW>!?`o`ooPmUDCsh5&15cRkT@iTEb(7*!NZKc~PHT(e} zp{x2uq%)jWukcH`I5ej;h&7%Y*bHaa?f)yPysJJ?7fVIIYL{tzPyW{DqrCXq4C9D! zk1q%MB)Q2b1f1F#RiN!T-t0&jaJCYz&64Ets9DXH1*mzM@IfI<*DcpHnZ1{%Tq`Wc zGF8K6!NAdXNwD|iqN7V#T0)y!q(k6ZbVYmF#yDSxXm=jAb>51L^u)twUY4p^eseXV z62juK(Uxu*{xpYYZSH&rP@9XBvby-ZUjvhEgzmCx@_6c`>(!|I(xr@doDex|8RWY`aC<0c)RMtGVaF~&d6?RDtj(E2q}M7mylA4gvzc;LV@@n zfj1Bpq44>D=$c_AdN8TNk#q(wax=~A0s>3Rm!DKU3^e15dTd;=Mg}v7?lG@84NfwRqJkvAeX6i-u>U!S7`qVk5F5 zb-w;k+&5?cfxxxZLf>rOp3&YFyGUjYDo}Bkl6xb-FBnwD`OE3ie%xMgDEvc&Azw!& ztfD4UO$QY@+Ns8A0#(-3Gfaa-V+3(XhDWPFu1JzDAhIr_O#xQSy=Mjw`~lCdp~axh z`Z?+@j|!}2-Oav2(d6#YXbKe_pY2oF51ms;k_NA2tn;E7TiLLk>rvOr!rK=qHx4s4 zcot5=P0{0J{I(gF2K)2@q~+O=2FoS6v<4&sb}Z6`w97y?jkB)=t^hyr z#~Erv;aM|U2%SJi3nUpS(4BR&aZvyff_Q}XbogE1TshX$t3tAALeyVoZ!LJAU=QO} zNzxUm&8`J>M_kp&%1PSax>xKKdJ(uFViNvv;)9Y9CZsCmVfG+O3T}I; z@!?%t7oXn$$mN|uz*OEz;$xen4Aue@5}IDb$?_yi#>k@~o0`;bbGR^)%5zuq6&&LF z<(a_o;b(<6H^lOml;X1Xe9hUJ1-^Px3Md+oiS~5iGQ_a)F;K|}boc^o{p;O~EOM+> z!RmZ*D56cZI!YrgeTDAVaXXS`kRUb69-hos`F}67q6AU6Htkl}&_7Xm)K2)hSF=E< zzmLQ0MwaOTr$wHY$@WcOx!B9NtwoTPabSI)YjdsNUoD7^`Qwi}o0{qmU(9(dbVy6K zoTki5DL}k?b=omWPTQrVh<3aVX_26>!24 zLS-AGYCFTeApWF%vxYp@9kQl>A#v?~)bI8EPeuf0Hk6+e$KqQiE}uF z3F8+}fuwio7ls9b_Q{CCgR|pa?Fs?; z`a9~y%y~uYU-WFn9|L`%c(W+nmgVitGk}x<_Kc>0rGw^hLp7^A)l5Soz{Ni7XA22B z9=f-TZ2!{I2R{L(|E1g9Y89b0MFrB0o-W0r1U!c_dzENQ#Udg$(FS==2ktexS_3@F zy|dqOs$5`#Am1D>B2|`xCp?P|2W&un^-RLoBi=|0rIvK=$+N%fN6t4?F?O2=lQg?t zB+6L zeuR^4pu$I$te@=uuwAPQI`YrUE1xI$h^gPzn%Wa!;Is2+OdSTQ^OvLi@>ew-Ikox- zs;2f&O3|wLdOZJzqV>c%k5Yu9$7}8O$IbQ(p|&0e-^m;|O_B#SUQBSq3m7e8`W{`w zWn^Jeerq8{tcB*dew9r6Q^jAw1j5w0|8M(#sH$`Cadhm=7!7RYgh&GopHKr%?Uy2M~PbqqOHax(6C zr=EnW{~@d@b>c98DA>o@u>r|lcu;ol&yuN#dHTn%H1Q<8h z<3L3orEgXO3qT2N`&9#OFZqF$c#j=?4Mo4Qw52$|S{;+;FCc1KIeS*yM6KQYZvD4n zuBvrjqf9&jZQ`wIQbIYUJW%kGsq!)Rrrhz~+DVjNr+~%mt1}{b~)bKPn_@A3zvpI2L!m8bPTj0FMSNR&9%TKT?|SHL1v_?zJP@3;SR1*kH(NuJD6 z2RTnW_^wv8k{m0~frZaEx-wScYAh>Aq*4+l_$w%tf$%PtgY;wio!y4}qRSFC6=Vxd z^j;VTXb{ejD)x!@<+qi*1yy%?9hR=UmF2$d?{F@KXP!qTvNEM5KwiraaP!XM zjhVpC5X^YH)WyyU8XG3KFsQzFa7)tRm()$nsdMh+b8=*w;5 zR|cuvvPt$_9C3=1Y)KvRQ>d#bPKQ;_6U!ny7#?1ZkdmB97Ysgqe99gM<6cmc&OZ+> z7N7hs3UjCk?-HH1K&&t)hkhsi$L^4>OP!~2Y3%<-*-Hb%t=~;cOO)O5-=Fkqv~f4g zplbWe+zj~OQhTLZ)^jIi6TYJL!UOj>a!|L+wcTd>^9Vq5EcxXaC+PaChr`)Exu^er z1T_DAY5kV|PIjWcBx@U2s)yVvGiP_&;Ehln#Nzy~O&R2~Xg>Nqe3r{b>=fEYr5dqg z2&vFhSXPAN=%|#%VogTd_Wx<^+W(nu|9_>DJ57}4&Tw~-%3--{ zGQ?d(T5;v|oY&*$rTbw)r6OYGST1zJLzxiWL@h3K+SiBowWfuZ_aJIdZOOlYdB0L&=>cbF7Y*x@Y~P8a^s!6gs-~- zC_!s5msp|G)oJF&6-pi1(W_Ivvrto<4x`U`Bx4S`Rp^s>26i(&Rg?U@6p?8*#Q(yQD%gE%(X$_wrF=hEq~L5eD>fZ3bvF*xuKYaue6|0>>fuB) zRHrGiB4Sl)#ZqZtYuJVsIb!$uPOvcDEZ3}mR)6HNdw9EMc?JPa3~dY41duU8lR#OU z3(#K&^J1WAuZ}I^m_x@&acg0D0u9LfqQ3$aGO%}U{8+Ymc1{wo5UFC`GT=xm|b(4 znfkQ8&_C~$*DmJllsUWp!Ai$N7ay|xq9ho9B&{G&fv@LEcOa>D%3(+SG#2Jm1`Hm8 z0*8Htqmr+J4hop}_Wjg9;^bLX-IH=8&VaOKgWJ+3)oxyMnj0LfY)zogiw`L^WgCcN zW`933*RqyEw^xuexB+&vN%nro``E5?@JO`Yfq{yK$q)qlR@i=F!Y)MU%VRksnN zY24T4gUL7&3G@m!bF1wbrUN{)(DuH4F_Dp%E-OiG10TN2F!)ef|NSw2;KcYzv&Ngv z{6aK;cxsJgMw$5o(ZA%WisN?;&T~}EfthuVZ}6L2i`>Yr);yP-b*kTbTZopiw{M!< zsGJgkb-5vR@epgWzr6KcfvaXSn4@ghoM&~@A@q3hVU`f1Kh^2rS=34!JiV2@k)3fT z0c*xyA;i*H6f}Dp8RvTy^h;T%cvaLraLT*eq&a+6tv)CTU$Qt*>g%!f#(ay^Hwlcg z8(x~+xmv1=X|L}8w=AIjou1x45Uecf(p09e5AP!1BG;_*@GO&8nP4vN$YZBobg;>( z92x){<0qEvUwKsA$Y7KIxn$G}?|E>^$j=F_Q}C?af^6pMQf;nOzB~rZ{6F}T@lt!h zGtL~joHoOibI6XN+NU3K_qJ_%ys%5>Un#r90{#y1Cug5dQWTCj-k@t^U%V!Z*MGXG zcvclP_5PhXfLHKqx@ym;#Xq5W+N{?Ky5V>Pal+qPuFtn-E(x8pU!$8%sGf@1knyPj zL}#!b{)ym^E{-i;)CtuR4ZtGgYbF)e9Hx=@1KjWPqs&YvQrLA$BqTW;do=C zbIj|kW=*h@0wQ1zLC_f#FQLildwq|g6K)C!0Bo0cSaqDkF0K3$)(ki;^}7qxPJ_g8 zw{jNE429C1bS=GUIsecDyr~b9WiUW&0MnnfGbsc+wAoj+MhW!tU9lZ6PtDLHp`Icp zBsK`+S;~+q#gW+zZ`X5Do3APNev;KUFc#HU}+EABKBh3!@is}Cj zFxQ#Qtj>W)A>+^yo;HoT9{Bq9=-<#f44`gSr}r&Xoc`R**X(-RPNfDyBYtn(3D}0j zD(65P?Abt-ep(9R`$Dx>AyCD%&@-`ajuioMBq} zWb|3{Raxk*oUUN|Lg31t{8kz~oy9-%5mh5V+K!zM?FiI zY)Ms|JSA~E*@~Gbrw25*)1THSxUNJ@qL-Fp*X=Ksv22Hq6pmVamXh-PQCR8%NuJ+4 z&K$1~e9Y$h+JZb1tvNDT0u0^|#b45yS+!bX)gCrLG&GwBcR}%<9doF}L%#SEWUM|~ z3O831cCT$z=7fhlsZC{8Neh;rp}fG0_;vk!jad5p`+1X`5~C0RGzT<|P2*Za`$cuP z-j*@{%4wT?{W5Gwt9B%h{v9lChd0TtX+PSqxZKDqTChQDVr}K^I&}WJpMF6q9v^@` z49@O7I_AjpN%QSTNs_r)rg4rYK*RMV0dU)w4s0MIw2^#>hlb)RA2)kD-Xyy&>VyDO zvUQc?&i1+$k^}8PRat}{Pr%J85K%t@c+cKl%oSVzuy~en;ZDKf>O738n0$QRhY{0i zFD30~7tzVM1)fpG?Mo8DaiSLGvF<%NU2g|T+lD-xb_e?Bu5A#u{Gy8gPT?M&RaSj! zJF|0S=aAH^=-QY62DxrtCsCc^KPe|H4A>SlUW@vqcvN}pI!D|;Vo65nob2i`+rsgt z7;MHR-kN8+pN8h@)$9QC;|O$pX&+Z@96DaRY+t`YOEuE>ZNUiVm{(Xi|1?0HEDRU_ z&MewehUC@$#K1o9QJF%SjlG_x^`?rmd5Pv)y+Y+_;mdS6LkgrWs*5J8BuPnMfq0tM z3kOb_(BbbrM#D0AdvVyW$^cEJ%+Rj+Ha3nv==yV)XdE+jHs$^P%V6Kvuap$QFD_V7 zqGO<)$o;7LFM=y-#eTf;bMWj=cTWu{FFa4|o>ZK(FeWVB@)Ay(=O#o~g$%ctxcT5s zZV-ISq#&4ivN8E1SBuu?S=_@y4Im9*EYvS8SGfA>KiSXVuE9^!H#@P@L&CL|j144+ zrqW8$SZsnqmQA^Tm@I#SlKbFsvnL5rKWM0~ut=!u*;L zE)kW0<8*kb{dI7e-3=5Dsji*L8`Tu2FPR!Gqthy4sudO33(>WD3*(tuD3=Y4b~1;i zX!qO^ns$@)ioDj;rgEQc>dg!c1wT08RFS#(i5J}FRB@6Sew$N$nYR#Av9EapS>Wj| zz<7=SoLdgEIft5u)ayCG1TbP0nIIzEU|ZM8-ZyHa%j!ap5CR;7h4Yn{O+pJtV4JVD z>#|#%_QFy*?cF8kTIgq2CDuJt^XreVcqpmX-P?#r-sdXI#z0t|7V+O#OFJS>e!zqa zwUrxd|0E)^$4+SIV}`R_cu!j!#duv7SwTsfU7o!Jr@OpDzOyis{})b)r=RkYoD!UC ztij91(dqpdD457bn47q*Aihloqn=^r06PY?&<+0>J!kK^lh-Zo z82*_V)HP|>qx9w>yX$_e1?MDTQ=?%Zvh0H`rw~*g2SO#W<^yaNsi{yB=8QS!>f^%N z)}Z4CNPP)Ev(>oCEbKn3-l!Sbuj!HDL-3mGy}ZLF0Cv-uH`j3Hy!F^K(Q%>YTP7Tu zyz*TEblg+Zi!xS-`BwTCZk3ks*5|O|sSN40FX@EryG0QJfmxx-kxAeYy~I4j%xhB|Ng9PVu}U`}^A*_s)u{~{)kLG{f+5TY zxZF!)~D+udcwKkwltzQ_I|#zWD08FF5$``I-Zv|eq|2(uGCFQU(~ zhQczOBC*8uLD>ywht?!J{g|nP_3S;rK`|)wH4`=K)mUOAIhGTCyAAo(K}OsDf|uLm zDe(62?|AvKQf+OkLY;x;Ao8oT#jKG}l?3!? z%E#1Fllp+YAduo>DXlwbrLT++&a^fokVD<|vn(h&f9?jiuX5+{nUT7Y#IS#*vCexeH~4ccODbX zy=rJ{A4r^Ek#@I4wNuYUTP1IepR_5|!8r_Vp)*p$dEBJAzoX7L>(tO-(h*h^wi`f_ zvp*v>S}lJB0|r-!s1+pVPrGcT&F(gf@A1DL6@v;vC;@1qH^GTG`?dhcHaGOpqz4-X zC)oN@Ry1O*bZj+J)^H4C9fI}L10}5-%Ow}vPinu8k|MU-8aOmZ9jrDkv(r{l4#oH3 zoVF=k>=@~Hq zX)Rh&Y4+^XoPX7Yj0Tv--nF^aIDMu5Z9#13)RWvG62@6zR&qgarliK7!PEfsm!|m{ z)X4sWU;}MH!ET@eXW)JyKt--2+S(YK{mGFFq?7^<HWhu@S03##Zw~z4pQ6ijcGWkLEbeR}EX>N5~z8@Y5#_C;b%qi8o_Gc#6D_T_vc*rd45ilqPte_Mq z9U#1;coK=6ker)Zx1PhrbNPU!h`h*i1WN<%qMvfIDx~V*+9b!HBs-Jf7Ff@kJi=Op zt0DY3B5kIEe{a;){C5|q3{dZ`oIkAA{3JZyk8w_`&WR1r#@&*JzMr&b+4A(x&Sa=3VyO4cfXhuYF5e-0vI<$4SNhXB5YHH`+rkRZ; zifSz6Lf-EOsG^Pc8Z;3Y@06Su&%X~T$$jQ($djZgr)tY6{_*U7qL2GGUpj7I$TSfT z$vL#MELUKtDG+alTerP5aYx&)2hP1mXl~rJdm_q!;KfrDX)o5Us@oOThBZBVUEq7{ z`fDjEJMii9=q#~jDtb6Qwe1<87b?p;Aqmay43^dvpGL~*Drzs3najyf6Of3)E(s~M z_=Aan8JK0G%P&z#jcwg&!yty`Qgs-=FjhRXo7bgYHMXKME|-6l&5A*r%oqGNty63w z)b>zVe`j4~D}`S#q!XJng@ zh0J%Sb?gT6a1y>7$8N#KR(G-&=S-o}-lQxVDnH{y9hkbJOa_RjdTIWTp3g=q%xd=1 z9ufP7qj*GXJjaGLsZ`EtfnSbRm1;{X?oB6`Y>Zy}ln##oo>c)TPJd?QtktsF@znaC zcfA5-I@;vej z9Z9M(z+Fd`&kK&QzohFiZF=l^aI@F($OV#dcLMs!e^xR^O3kLa<~8rFPQ#_H*HT`P z;F=aq{J_%xPV0e_*w4O=yg$r1_bu1=SQqF|uNae@fH{fvYmd6>pz8C~l~jRUPr{#- zZLk}tUa1qFW+rw&mI-)j?x`FxK;~C0Ryv5N`dtNo&MACt%#@FNIdP-;!PMV|dig#s zA6l@?=OqIv(XxR6?R*= zKGHZmx(^3W|B8 zXBdrCJ$mhMqR~K5pQ~sb=<3XqTx%w+Ux$_}E5`pZJROhJ!&QCB(PLV>;M$G?*`akt z41uV*4B=R(^+=&adpGw0^ELonyVNVHc0P26=MYbU^S67EiZi9BN5h7}&mA7~pN{RQ zf|4V2a%<-ee^9Wr%j$V2$R~OH6DpzqC^_HV1-wfr8l<|>t$e`zC`i8x5Q9c#re6az!pWW?Q_`QvS)nh0sH+E z%iq8|qV^OySZtiTeM2kC0#{PQLUrff6_cMXjg-}mU(?6Pk%8e6uFuRc@CoLZR%8M1 zhgz3yKeYgze_btkib&NVt`4_9ek)9gb!++)jJ%%jiqwo*U|tyw0oXOo1jn*mQV#Vk z+w+I*IFQD&Ds1115>N7$<;3Xlli@2dekkRrcUXw?Xo(%A?I7C`AYXxD6mN6Y={Gu$ z!-3^kb%eGeK2p^yuW74?f(pp z7bkdg{-y)}L$70%OjoD1O3-rfm#2Nh1j`?sZk5jIszqLxET_YH+jDIkC`i3$vY!LA zvjiO2C1v+WO)>@^3PF-0$TBIPKByyf?I-sQ6aC1ZkDc$rw`7!bpCZ&Z;3;pt3FRNQ z*P&yrO4Ko{ZB)Hr2~aw;5NEh0$PdH;^d)XFQ$$PebpWBs8~{_MwF|o@FuHaBAZh@} zC99%Hl-TM(6FS`KT_!ofUKO$XlZKw@F*{!2^*W!VRR1^z8nvQ3SNB=gR{&vPVT3gz z7oOOTqN54cmz>eKRaSt2(jDfR)8)J&r zBc!q2nXR-5+f{jv3?U z4g;YCr3-{7z@beO2Yf_@1wW4&=Hd zxW`fz$FYj~E)taJGW($IyzU9)b!*+MXtt00?XeiKNBVBHW7SG9FBggV=(6FtQOGK^ z7HMtPZx9uOAj@_U$}O2mc@2A}zquH^274mF4yKg6)~@3N$<=Fs&;kEBWl_4xW%a6> z^_Av9oxXdZgLzYQNZq}5TK6q-*RJBo`vsU2N@m-K0{T%b#1{U@Z*}|}oxrS3`dJ7n zk%m%g;fQy-jOcXO#cryjHX?WqIF=GHz&6E~?8v)-!z;{5Ey2w&>WKQm$Bxwl#vaw< z2abuBh168(f6JzHB7I08`bH{n#pv^(xZtW?B7!aHq_=<&vFzbsISTe}{m zvE0w~3fGD{X+R44)M=ff^$WswM0n}#(1X@pFIulu=_D9%OB0qWD{ULSLbf9`uMa@? z-Ys&^&AU_mj$-=3Ud$w^_eWMteP`6tE#rVK51jN2r0Wyw>TH;|cM(QjKNCdk6kj9) z+P)@kU|9{12+0LpUON9e*9+K0JJPiE*qZf@m~N?9nTy_NEzLso({$UTnAH3TB%n0c zSl;&L%Ms%Zg<*}arpT6r%1eTC0*5~VCT!150aZ;io3{3-t|L*mnD{cb%mZ}DBuh(s z3Kd8F<_pp+1W<>yo8;T#X6x_uGXJr8_m#qEeU#ohyP}1a&*l1sxTvqS(jX3fQ`mzw z8>s%^zG3$gWYi)?%hje)opqGQ zJP)K5f`H;q;Vu%1HptuY2(xxxcNlp2@}+up)LI)&k%x0TS%+RRbjxXaYsF>jYcF4 z{kLJ9S2#@yLN*jpAONXb`ZDYC^zn%{S|)Ye!AX!VPAf$?coOvMLDOVMfn{KCSZ|ou zE2}e3)(p%0W!Eyxu<)+_pV#LL77F<8{Fnb3Y;6+!HHqr@f~FwLU*B6`>-?=_jfO+d zY84mV+Q{CRF1TYpkBr;+KUfUX$OaLRaeu;?rj8x-?|}^uEI7mq#^z=z<|JT_1}Sc9 zD{dBKoO*$gw7O7A!Et+qyu0XffRqdE#Z6rZ#w(OeD=lH%nrUxR`9c)&S@S%1wBzMq zBtHOXaY3(3@bDQFF%VNu)Al8Z_DG4G!dqQFcXZl8!b8GjIceeT`ahEyyaAa(8T<;o zJ|&isZEMpM0BsT%eyM;Xe$pd2!99_T18iQ&HEc@wL;2j5k_biw=H<`ME>UHXKlAO? zc~6O`=Y5`U3e?E~8qWt;Hu@qN5oHk=i$GuZF0USgj^xdFWbal+Im*B+F(u3YWpF0^ zVG>tk+-6LlN)`3UOA1zj`WoZ4^wbN`E}GhkDXTss01=R!VRsKrVhgH6UJjMJ55>9* z`=&SbT%k7g0ZNd*j-kjhpU`-=2>tTX$rrpURuAnXrTTBumWiwHirqKeg_w2MdCbJO zre7_o)k(QV4efMjW;5frn|gwsKD`@RE!9O)U19v*u=dpVz~85X4Ftk~-i@X~27>mB z|LZ;DwFeUE6EtMD#iWYymoB-<0xk!R;=RHkt=T}`aMs6!oa?|z|Ns2`hM4}gC8cEb WNo)=lFunu$F}-SLlzYYg>Hh#Hoo9If literal 0 HcmV?d00001 diff --git a/model_zoo/official/lite/Himindspore/app/src/main/res/drawable-xxhdpi/logo2.png b/model_zoo/official/lite/Himindspore/app/src/main/res/drawable-xxhdpi/logo2.png new file mode 100644 index 0000000000000000000000000000000000000000..c90f1dda4399a42e4c0c504f144508afb99ec326 GIT binary patch literal 9405 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 bch3}6_!Z^E0PU9}M+mZ#N)i>HjQsu&-oj&1 literal 0 HcmV?d00001 diff --git a/model_zoo/official/lite/Himindspore/app/src/main/res/drawable/btn_code.png b/model_zoo/official/lite/Himindspore/app/src/main/res/drawable/btn_code.png new file mode 100644 index 0000000000000000000000000000000000000000..9ec5a41cf5b4c00ce9106eac47595d23dba0850e GIT binary patch literal 1833 zcmV+^2iEwBP)Px#L}ge>W=%~1DgXcg2mk?xX#fNO00031000^Q000001E2u_0{{R30RRC20H6W@ z1ONa40RR91HlPCl1ONa40RR91HUIzs0P{mGqyPX0lSxEDRA>e5S$%9&MG&98qlFeU z(8{L)5|w|fk|-KuFnTD45MoF~uh)nsD&b4$hX@K0Qh|su3WXYoK}Dd__97_$)0n8V ziiw8AC?X0P`9oq5g(fPcAKV*%b95iuyKDR2y*)k*o4lKO^JZq}w>vv;-yYG`c69`< zjzBI)K;_c8_^#N?Be{*8<^h`~ede@a%WUKi-Yz+trT zwQ1^%Yh!`20xTZq@RyQyOfQSviefb}^`pcpDcTyR6%JE1)?HY~kJ*&x(M>UR zNXvrKz`!bZ^XNnu?LwdtGZ+NBxo9LUsh*~`9e+|$t(KeErxGFZ5s;ye)hx!6m z(a6&fz)oDlbtiCeoWnF5@1zzj%Z66Pxd0Wl2EON-_5io5C`9x8rlu!g$<#~G8dmfI z(#Ev;Xvjc%YTd)?lo?DAesP>1Q4Vbdz@e@mMMv{cHP^?~cdo1-#tbr}Dn|19M1mTy zmhRCC7{w0oOHrO{wag}6vYW?uc2Yfj-fj48M^UQ8Uh_>u)+^g5u*wFWjU})m^a)xGo9E2}h5j1`70}#QXR3et@|O{qIBh*iYXNGRnR;-# zEq@p-w!KT}E5P!4egMyR0^De=hoFBBg(+GSRo`k^Doq3SY(00?uKJSy&W-4*PY990NAS9nA)OcChY+XpBXc-p0-1wSS!K7fp8whHq27L zYT2cr%&aOJsS16sLt)9+F#te(P$A7pO-&bAau?xH6Gr9y9!?Ph)axZT(u#R^s4nfu z3q=rvp%=q-NGbKT7aOlS<#C?G4&Hf8HyJjc6_286*uI_9vLvwOd${;aC;eiS*po=T zC-8FoSSxy^05uG{pWwP3mkh`_fI9)&XM>$&$+gfu)~6yKVv}%9dmC*=D$drr0J3UR znOdXolOKU?oiw8ds6v`(0hILN;OciL0#H(Q58^rs-VGM_8JnaXKs=7}FI7{w8JZEA z1P0_|wGipox^brZL*IL)DKjNz0%`$f;7<=e%Ahj_7v3Z!RnSvItUtMmif(gxFdEqT zb!9&B!S@1c4nS@5=q6(59WeMlS-g%gC+8a&Y)&q;B(Mv&{<)x_U@z<3-B5#X_8&#M zMIc~7L3M{y0;+d5Co#)b4tTN#W(kyFi^xxRYtc+lniVjMpd|i?0ixO91F^n=*#re& zvcB#!5ZIkDluhZtYioj{ynLleN%vFzl19dlxxVDJU}b%Ye_u0(5tcyi5<|40pu|T+ ze!5!=3Wr<1wa_!|`2p+?nCDKsE1GH8?A@$?OYuhNpr~j&G8k>AC;I<&`bV)bWKgEq48T^hb zCr5f#W=u!i3%InebLiE;AM?jZ*s<9wFx-Pl2kl>{Fd>A3VO$^cAVnOIxz8% zpwEE3ozmAuy|tQ(7d7&JI{V)OOQs^;2gi&TLP5p*tiQSX8JA1^IDrBrUxm^X^;ok> zV4*zP&s}s5yAHV@?{v{uN#!f(2a~hU3^$pI@=ppty<$GN!uV6FkXD#6yKAhrOs&Lp zF2v!-aZ}-P=5bcjftZ`}^J!A*v3$luoagrNhy>G+%NbNSK|J9GI_~!`uFDydr`Y}j XUSdAde`*m-00000NkvXXu0mjfJ{&~! literal 0 HcmV?d00001 diff --git a/model_zoo/official/lite/Himindspore/app/src/main/res/drawable/btn_help.png b/model_zoo/official/lite/Himindspore/app/src/main/res/drawable/btn_help.png new file mode 100644 index 0000000000000000000000000000000000000000..67c28d7cb5dd1e9ad673a6c0a397e3b398cdd1cb GIT binary patch literal 1100 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 il}PVI_yNGE&(VKjD@HN)2Bvxd00005 literal 0 HcmV?d00001 diff --git a/model_zoo/official/lite/Himindspore/app/src/main/res/drawable/btn_object.png b/model_zoo/official/lite/Himindspore/app/src/main/res/drawable/btn_object.png new file mode 100644 index 0000000000000000000000000000000000000000..94b8a7323bda2d19f44e57eac03d6e086ed5a73c GIT binary patch literal 2795 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 literal 0 HcmV?d00001 diff --git a/model_zoo/official/lite/Himindspore/app/src/main/res/drawable/ic_launcher_background.xml b/model_zoo/official/lite/Himindspore/app/src/main/res/drawable/ic_launcher_background.xml new file mode 100644 index 0000000000..07d5da9cbf --- /dev/null +++ b/model_zoo/official/lite/Himindspore/app/src/main/res/drawable/ic_launcher_background.xml @@ -0,0 +1,170 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/model_zoo/official/lite/Himindspore/app/src/main/res/layout/activity_contract.xml b/model_zoo/official/lite/Himindspore/app/src/main/res/layout/activity_contract.xml new file mode 100644 index 0000000000..f34a8c35db --- /dev/null +++ b/model_zoo/official/lite/Himindspore/app/src/main/res/layout/activity_contract.xml @@ -0,0 +1,57 @@ + + + + + + + + + +