!9626 [MS][LITE]add scene detection android demo
From: @sishuikang Reviewed-by: @zhanghaibo5,@zhang_xue_tong Signed-off-by: @zhanghaibo5,@zhang_xue_tongpull/9626/MERGE
commit
36b4d93ad7
@ -0,0 +1,83 @@
|
||||
# MindSpore
|
||||
build/
|
||||
mindspore/lib
|
||||
app/src/main/assets/model/
|
||||
app/src/main/cpp/mindspore-lite-*
|
||||
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
|
@ -0,0 +1 @@
|
||||
/build
|
@ -0,0 +1,82 @@
|
||||
# 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)
|
||||
|
||||
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)
|
||||
# --------------- 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
|
||||
|
||||
# --- other dependencies.---
|
||||
-ljnigraphics
|
||||
android
|
||||
|
||||
# Links the target library to the log library
|
||||
${log-lib}
|
||||
)
|
@ -0,0 +1,75 @@
|
||||
apply plugin: 'com.android.application'
|
||||
|
||||
android {
|
||||
compileSdkVersion 30
|
||||
buildToolsVersion "30.0.1"
|
||||
|
||||
defaultConfig {
|
||||
applicationId "com.mindspore.scene"
|
||||
minSdkVersion 21
|
||||
targetSdkVersion 30
|
||||
versionCode 1
|
||||
versionName "1.0.0"
|
||||
|
||||
testInstrumentationRunner "androidx.test.runner.AndroidJUnitRunner"
|
||||
externalNativeBuild {
|
||||
cmake {
|
||||
arguments "-DANDROID_STL=c++_shared"
|
||||
cppFlags ""
|
||||
}
|
||||
}
|
||||
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.1.0'
|
||||
implementation 'androidx.constraintlayout:constraintlayout:1.1.3'
|
||||
|
||||
testImplementation 'junit:junit:4.12'
|
||||
androidTestImplementation 'androidx.test.ext:junit:1.1.1'
|
||||
androidTestImplementation 'androidx.test.espresso:espresso-core:3.2.0'
|
||||
|
||||
implementation 'com.google.android.material:material:1.0.0'
|
||||
androidTestImplementation 'com.android.support.test:rules:1.0.2'
|
||||
androidTestImplementation 'com.google.truth:truth:1.0.1'
|
||||
}
|
@ -0,0 +1,82 @@
|
||||
/**
|
||||
* 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.1/lite/android_aarch64/${mindsporeLite_Version}.tar.gz"
|
||||
|
||||
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 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/mobilenetv2.ms").exists()) {
|
||||
downloadModelFile.enabled = false
|
||||
}
|
||||
|
||||
preBuild.dependsOn cleanCmakeCache
|
||||
preBuild.dependsOn downloadModelFile
|
||||
preBuild.dependsOn downloadMindSporeLibrary
|
||||
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)
|
||||
}
|
||||
}
|
@ -0,0 +1,21 @@
|
||||
# Add project specific ProGuard rules here.
|
||||
# You can control the set of applied configuration files using the
|
||||
# proguardFiles setting in build.gradle.
|
||||
#
|
||||
# For more details, see
|
||||
# http://developer.android.com/guide/developing/tools/proguard.html
|
||||
|
||||
# If your project uses WebView with JS, uncomment the following
|
||||
# and specify the fully qualified class name to the JavaScript interface
|
||||
# class:
|
||||
#-keepclassmembers class fqcn.of.javascript.interface.for.webview {
|
||||
# public *;
|
||||
#}
|
||||
|
||||
# Uncomment this to preserve the line number information for
|
||||
# debugging stack traces.
|
||||
#-keepattributes SourceFile,LineNumberTable
|
||||
|
||||
# If you keep the line number information, uncomment this to
|
||||
# hide the original source file name.
|
||||
#-renamesourcefileattribute SourceFile
|
@ -0,0 +1,32 @@
|
||||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<manifest xmlns:android="http://schemas.android.com/apk/res/android"
|
||||
package="com.mindspore.scene"
|
||||
android:versionCode="1"
|
||||
android:versionName="1.0">
|
||||
|
||||
<uses-permission android:name="android.permission.CAMERA" />
|
||||
<uses-permission android:name="android.permission.WRITE_INTERNAL_STORAGE" />
|
||||
<uses-permission android:name="android.permission.READ_INTERNAL_STORAGE" />
|
||||
<uses-permission android:name="android.permission.READ_EXTERNAL_STORAGE" />
|
||||
<uses-permission android:name="android.permission.WRITE_EXTERNAL_STORAGE" />
|
||||
<uses-permission android:name="android.permission.MOUNT_UNMOUNT_FILESYSTEM" />
|
||||
<uses-permission android:name="android.permission.READ_PHONE_STATE" />
|
||||
|
||||
<application
|
||||
android:allowBackup="true"
|
||||
android:icon="@mipmap/ic_launcher"
|
||||
android:label="@string/app_name"
|
||||
android:roundIcon="@mipmap/ic_launcher_round"
|
||||
android:supportsRtl="true"
|
||||
android:theme="@style/AppTheme">
|
||||
<activity
|
||||
android:name="com.mindspore.scene.widget.CameraActivity"
|
||||
android:screenOrientation="portrait">
|
||||
<intent-filter>
|
||||
<action android:name="android.intent.action.MAIN" />
|
||||
<category android:name="android.intent.category.LAUNCHER" />
|
||||
</intent-filter>
|
||||
</activity>
|
||||
</application>
|
||||
|
||||
</manifest>
|
@ -0,0 +1,63 @@
|
||||
/**
|
||||
* 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 <android/log.h>
|
||||
#include <iostream>
|
||||
#include <string>
|
||||
#include "include/errorcode.h"
|
||||
|
||||
#define MS_PRINT(format, ...) __android_log_print(ANDROID_LOG_INFO, "MSJNI", format, ##__VA_ARGS__)
|
||||
|
||||
MSNetWork::MSNetWork(void) : session_(nullptr), model_(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.
|
||||
model_ = mindspore::lite::Model::Import(modelBuffer, bufferLen);
|
||||
if (model_ == nullptr) {
|
||||
ReleaseNets();
|
||||
MS_PRINT("Import model failed.");
|
||||
return;
|
||||
}
|
||||
|
||||
int ret = session_->CompileGraph(model_);
|
||||
if (ret != mindspore::lite::RET_OK) {
|
||||
ReleaseNets();
|
||||
MS_PRINT("CompileGraph failed.");
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
void MSNetWork::ReleaseNets(void) {
|
||||
if (model_ != nullptr) {
|
||||
model_->Free();
|
||||
delete model_;
|
||||
model_ = nullptr;
|
||||
}
|
||||
if (session_ != nullptr) {
|
||||
delete session_;
|
||||
session_ = nullptr;
|
||||
}
|
||||
}
|
@ -0,0 +1,58 @@
|
||||
/**
|
||||
* 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 <context.h>
|
||||
#include <lite_session.h>
|
||||
#include <model.h>
|
||||
#include <errorcode.h>
|
||||
#include <cstdio>
|
||||
#include <algorithm>
|
||||
#include <fstream>
|
||||
#include <functional>
|
||||
#include <sstream>
|
||||
#include <vector>
|
||||
#include <map>
|
||||
#include <string>
|
||||
#include <memory>
|
||||
#include <utility>
|
||||
|
||||
struct ImgDims {
|
||||
int channel = 0;
|
||||
int width = 0;
|
||||
int height = 0;
|
||||
};
|
||||
|
||||
class MSNetWork {
|
||||
public:
|
||||
MSNetWork();
|
||||
|
||||
~MSNetWork();
|
||||
|
||||
void CreateSessionMS(char *modelBuffer, size_t bufferLen, mindspore::lite::Context *ctx);
|
||||
|
||||
void ReleaseNets(void);
|
||||
|
||||
mindspore::session::LiteSession *session() const { return session_; }
|
||||
|
||||
private:
|
||||
mindspore::session::LiteSession *session_;
|
||||
mindspore::lite::Model *model_;
|
||||
};
|
||||
|
||||
#endif
|
File diff suppressed because it is too large
Load Diff
@ -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 MINDSPORE_JNI_HMS_DEBUG_MINDSPORENETNATIVE_H
|
||||
#define MINDSPORE_JNI_HMS_DEBUG_MINDSPORENETNATIVE_H
|
||||
|
||||
|
||||
#endif // MINDSPORE_JNI_HMS_DEBUG_MINDSPORENETNATIVE_H
|
@ -0,0 +1,34 @@
|
||||
/**
|
||||
* Copyright 2020 Huawei Technologies Co., Ltd
|
||||
* <p>
|
||||
* 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
|
||||
* <p>
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
* <p>
|
||||
* 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.scene.gallery.classify;
|
||||
|
||||
import android.util.Size;
|
||||
|
||||
import java.util.Comparator;
|
||||
|
||||
/**
|
||||
* Data comparator.
|
||||
*/
|
||||
|
||||
public class CompareSizesByArea implements Comparator<Size> {
|
||||
|
||||
@Override
|
||||
public int compare(Size lhs, Size rhs) {
|
||||
// We cast here to ensure the multiplications won't overflow
|
||||
return Long.signum((long) lhs.getWidth() * lhs.getHeight() - (long) rhs.getWidth() * rhs.getHeight());
|
||||
}
|
||||
|
||||
}
|
@ -0,0 +1,45 @@
|
||||
/**
|
||||
* Copyright 2020 Huawei Technologies Co., Ltd
|
||||
* <p>
|
||||
* 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
|
||||
* <p>
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
* <p>
|
||||
* 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.scene.gallery.classify;
|
||||
|
||||
public class RecognitionObjectBean {
|
||||
|
||||
private String name;
|
||||
private float score;
|
||||
|
||||
public RecognitionObjectBean(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;
|
||||
}
|
||||
|
||||
|
||||
}
|
@ -0,0 +1,126 @@
|
||||
/**
|
||||
* Copyright 2020 Huawei Technologies Co., Ltd
|
||||
* <p>
|
||||
* 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
|
||||
* <p>
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
* <p>
|
||||
* 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.scene.gallery.classify;
|
||||
|
||||
import android.content.Context;
|
||||
import android.graphics.Bitmap;
|
||||
import android.util.Log;
|
||||
|
||||
import java.io.InputStream;
|
||||
import java.nio.ByteBuffer;
|
||||
|
||||
/**
|
||||
* Call the MindSpore interface API in the Java layer.
|
||||
*/
|
||||
public class TrackingMobile {
|
||||
private final static String TAG = "TrackingMobile";
|
||||
|
||||
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 TrackingMobile(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;
|
||||
}
|
||||
}
|
@ -0,0 +1,73 @@
|
||||
/**
|
||||
* Copyright 2020 Huawei Technologies Co., Ltd
|
||||
* <p>
|
||||
* 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
|
||||
* <p>
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
* <p>
|
||||
* 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.scene.widget;
|
||||
|
||||
import android.content.Context;
|
||||
import android.util.AttributeSet;
|
||||
import android.view.TextureView;
|
||||
|
||||
public class AutoFitTextureView extends TextureView {
|
||||
|
||||
private int mRatioWidth = 0;
|
||||
private int mRatioHeight = 0;
|
||||
|
||||
public AutoFitTextureView(Context context) {
|
||||
this(context, null);
|
||||
}
|
||||
|
||||
public AutoFitTextureView(Context context, AttributeSet attrs) {
|
||||
this(context, attrs, 0);
|
||||
}
|
||||
|
||||
public AutoFitTextureView(Context context, AttributeSet attrs, int defStyle) {
|
||||
super(context, attrs, defStyle);
|
||||
}
|
||||
|
||||
/**
|
||||
* Sets the aspect ratio for this view. The size of the view will be measured based on the ratio
|
||||
* calculated from the parameters. Note that the actual sizes of parameters don't matter, that
|
||||
* is, calling setAspectRatio(2, 3) and setAspectRatio(4, 6) make the same result.
|
||||
*
|
||||
* @param width Relative horizontal size
|
||||
* @param height Relative vertical size
|
||||
*/
|
||||
public void setAspectRatio(int width, int height) {
|
||||
if (width < 0 || height < 0) {
|
||||
throw new IllegalArgumentException("Size cannot be negative.");
|
||||
}
|
||||
mRatioWidth = width;
|
||||
mRatioHeight = height;
|
||||
requestLayout();
|
||||
}
|
||||
|
||||
@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);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
}
|
File diff suppressed because it is too large
Load Diff
@ -0,0 +1,62 @@
|
||||
/**
|
||||
* Copyright 2020 Huawei Technologies Co., Ltd
|
||||
* <p>
|
||||
* 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
|
||||
* <p>
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
* <p>
|
||||
* 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.scene.widget;
|
||||
|
||||
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.scene.R;
|
||||
|
||||
public class HorTextView extends LinearLayout {
|
||||
private TextView tvLeftTitle, 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);
|
||||
}
|
||||
|
||||
}
|
@ -0,0 +1,30 @@
|
||||
<vector xmlns:android="http://schemas.android.com/apk/res/android"
|
||||
xmlns:aapt="http://schemas.android.com/aapt"
|
||||
android:width="108dp"
|
||||
android:height="108dp"
|
||||
android:viewportWidth="108"
|
||||
android:viewportHeight="108">
|
||||
<path android:pathData="M31,63.928c0,0 6.4,-11 12.1,-13.1c7.2,-2.6 26,-1.4 26,-1.4l38.1,38.1L107,108.928l-32,-1L31,63.928z">
|
||||
<aapt:attr name="android:fillColor">
|
||||
<gradient
|
||||
android:endX="85.84757"
|
||||
android:endY="92.4963"
|
||||
android:startX="42.9492"
|
||||
android:startY="49.59793"
|
||||
android:type="linear">
|
||||
<item
|
||||
android:color="#44000000"
|
||||
android:offset="0.0" />
|
||||
<item
|
||||
android:color="#00000000"
|
||||
android:offset="1.0" />
|
||||
</gradient>
|
||||
</aapt:attr>
|
||||
</path>
|
||||
<path
|
||||
android:fillColor="#FFFFFF"
|
||||
android:fillType="nonZero"
|
||||
android:pathData="M65.3,45.828l3.8,-6.6c0.2,-0.4 0.1,-0.9 -0.3,-1.1c-0.4,-0.2 -0.9,-0.1 -1.1,0.3l-3.9,6.7c-6.3,-2.8 -13.4,-2.8 -19.7,0l-3.9,-6.7c-0.2,-0.4 -0.7,-0.5 -1.1,-0.3C38.8,38.328 38.7,38.828 38.9,39.228l3.8,6.6C36.2,49.428 31.7,56.028 31,63.928h46C76.3,56.028 71.8,49.428 65.3,45.828zM43.4,57.328c-0.8,0 -1.5,-0.5 -1.8,-1.2c-0.3,-0.7 -0.1,-1.5 0.4,-2.1c0.5,-0.5 1.4,-0.7 2.1,-0.4c0.7,0.3 1.2,1 1.2,1.8C45.3,56.528 44.5,57.328 43.4,57.328L43.4,57.328zM64.6,57.328c-0.8,0 -1.5,-0.5 -1.8,-1.2s-0.1,-1.5 0.4,-2.1c0.5,-0.5 1.4,-0.7 2.1,-0.4c0.7,0.3 1.2,1 1.2,1.8C66.5,56.528 65.6,57.328 64.6,57.328L64.6,57.328z"
|
||||
android:strokeWidth="1"
|
||||
android:strokeColor="#00000000" />
|
||||
</vector>
|
@ -0,0 +1,170 @@
|
||||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<vector xmlns:android="http://schemas.android.com/apk/res/android"
|
||||
android:width="108dp"
|
||||
android:height="108dp"
|
||||
android:viewportHeight="108"
|
||||
android:viewportWidth="108">
|
||||
<path
|
||||
android:fillColor="#26A69A"
|
||||
android:pathData="M0,0h108v108h-108z" />
|
||||
<path
|
||||
android:fillColor="#00000000"
|
||||
android:pathData="M9,0L9,108"
|
||||
android:strokeColor="#33FFFFFF"
|
||||
android:strokeWidth="0.8" />
|
||||
<path
|
||||
android:fillColor="#00000000"
|
||||
android:pathData="M19,0L19,108"
|
||||
android:strokeColor="#33FFFFFF"
|
||||
android:strokeWidth="0.8" />
|
||||
<path
|
||||
android:fillColor="#00000000"
|
||||
android:pathData="M29,0L29,108"
|
||||
android:strokeColor="#33FFFFFF"
|
||||
android:strokeWidth="0.8" />
|
||||
<path
|
||||
android:fillColor="#00000000"
|
||||
android:pathData="M39,0L39,108"
|
||||
android:strokeColor="#33FFFFFF"
|
||||
android:strokeWidth="0.8" />
|
||||
<path
|
||||
android:fillColor="#00000000"
|
||||
android:pathData="M49,0L49,108"
|
||||
android:strokeColor="#33FFFFFF"
|
||||
android:strokeWidth="0.8" />
|
||||
<path
|
||||
android:fillColor="#00000000"
|
||||
android:pathData="M59,0L59,108"
|
||||
android:strokeColor="#33FFFFFF"
|
||||
android:strokeWidth="0.8" />
|
||||
<path
|
||||
android:fillColor="#00000000"
|
||||
android:pathData="M69,0L69,108"
|
||||
android:strokeColor="#33FFFFFF"
|
||||
android:strokeWidth="0.8" />
|
||||
<path
|
||||
android:fillColor="#00000000"
|
||||
android:pathData="M79,0L79,108"
|
||||
android:strokeColor="#33FFFFFF"
|
||||
android:strokeWidth="0.8" />
|
||||
<path
|
||||
android:fillColor="#00000000"
|
||||
android:pathData="M89,0L89,108"
|
||||
android:strokeColor="#33FFFFFF"
|
||||
android:strokeWidth="0.8" />
|
||||
<path
|
||||
android:fillColor="#00000000"
|
||||
android:pathData="M99,0L99,108"
|
||||
android:strokeColor="#33FFFFFF"
|
||||
android:strokeWidth="0.8" />
|
||||
<path
|
||||
android:fillColor="#00000000"
|
||||
android:pathData="M0,9L108,9"
|
||||
android:strokeColor="#33FFFFFF"
|
||||
android:strokeWidth="0.8" />
|
||||
<path
|
||||
android:fillColor="#00000000"
|
||||
android:pathData="M0,19L108,19"
|
||||
android:strokeColor="#33FFFFFF"
|
||||
android:strokeWidth="0.8" />
|
||||
<path
|
||||
android:fillColor="#00000000"
|
||||
android:pathData="M0,29L108,29"
|
||||
android:strokeColor="#33FFFFFF"
|
||||
android:strokeWidth="0.8" />
|
||||
<path
|
||||
android:fillColor="#00000000"
|
||||
android:pathData="M0,39L108,39"
|
||||
android:strokeColor="#33FFFFFF"
|
||||
android:strokeWidth="0.8" />
|
||||
<path
|
||||
android:fillColor="#00000000"
|
||||
android:pathData="M0,49L108,49"
|
||||
android:strokeColor="#33FFFFFF"
|
||||
android:strokeWidth="0.8" />
|
||||
<path
|
||||
android:fillColor="#00000000"
|
||||
android:pathData="M0,59L108,59"
|
||||
android:strokeColor="#33FFFFFF"
|
||||
android:strokeWidth="0.8" />
|
||||
<path
|
||||
android:fillColor="#00000000"
|
||||
android:pathData="M0,69L108,69"
|
||||
android:strokeColor="#33FFFFFF"
|
||||
android:strokeWidth="0.8" />
|
||||
<path
|
||||
android:fillColor="#00000000"
|
||||
android:pathData="M0,79L108,79"
|
||||
android:strokeColor="#33FFFFFF"
|
||||
android:strokeWidth="0.8" />
|
||||
<path
|
||||
android:fillColor="#00000000"
|
||||
android:pathData="M0,89L108,89"
|
||||
android:strokeColor="#33FFFFFF"
|
||||
android:strokeWidth="0.8" />
|
||||
<path
|
||||
android:fillColor="#00000000"
|
||||
android:pathData="M0,99L108,99"
|
||||
android:strokeColor="#33FFFFFF"
|
||||
android:strokeWidth="0.8" />
|
||||
<path
|
||||
android:fillColor="#00000000"
|
||||
android:pathData="M19,29L89,29"
|
||||
android:strokeColor="#33FFFFFF"
|
||||
android:strokeWidth="0.8" />
|
||||
<path
|
||||
android:fillColor="#00000000"
|
||||
android:pathData="M19,39L89,39"
|
||||
android:strokeColor="#33FFFFFF"
|
||||
android:strokeWidth="0.8" />
|
||||
<path
|
||||
android:fillColor="#00000000"
|
||||
android:pathData="M19,49L89,49"
|
||||
android:strokeColor="#33FFFFFF"
|
||||
android:strokeWidth="0.8" />
|
||||
<path
|
||||
android:fillColor="#00000000"
|
||||
android:pathData="M19,59L89,59"
|
||||
android:strokeColor="#33FFFFFF"
|
||||
android:strokeWidth="0.8" />
|
||||
<path
|
||||
android:fillColor="#00000000"
|
||||
android:pathData="M19,69L89,69"
|
||||
android:strokeColor="#33FFFFFF"
|
||||
android:strokeWidth="0.8" />
|
||||
<path
|
||||
android:fillColor="#00000000"
|
||||
android:pathData="M19,79L89,79"
|
||||
android:strokeColor="#33FFFFFF"
|
||||
android:strokeWidth="0.8" />
|
||||
<path
|
||||
android:fillColor="#00000000"
|
||||
android:pathData="M29,19L29,89"
|
||||
android:strokeColor="#33FFFFFF"
|
||||
android:strokeWidth="0.8" />
|
||||
<path
|
||||
android:fillColor="#00000000"
|
||||
android:pathData="M39,19L39,89"
|
||||
android:strokeColor="#33FFFFFF"
|
||||
android:strokeWidth="0.8" />
|
||||
<path
|
||||
android:fillColor="#00000000"
|
||||
android:pathData="M49,19L49,89"
|
||||
android:strokeColor="#33FFFFFF"
|
||||
android:strokeWidth="0.8" />
|
||||
<path
|
||||
android:fillColor="#00000000"
|
||||
android:pathData="M59,19L59,89"
|
||||
android:strokeColor="#33FFFFFF"
|
||||
android:strokeWidth="0.8" />
|
||||
<path
|
||||
android:fillColor="#00000000"
|
||||
android:pathData="M69,19L69,89"
|
||||
android:strokeColor="#33FFFFFF"
|
||||
android:strokeWidth="0.8" />
|
||||
<path
|
||||
android:fillColor="#00000000"
|
||||
android:pathData="M79,19L79,89"
|
||||
android:strokeColor="#33FFFFFF"
|
||||
android:strokeWidth="0.8" />
|
||||
</vector>
|
@ -0,0 +1,13 @@
|
||||
<?xml version="1.0" encoding="UTF-8"?>
|
||||
<shape xmlns:android="http://schemas.android.com/apk/res/android"
|
||||
android:id="@+id/listview_background_shape">
|
||||
<stroke
|
||||
android:width="1dp"
|
||||
android:color="@android:color/darker_gray" />
|
||||
<padding
|
||||
android:bottom="2dp"
|
||||
android:left="2dp"
|
||||
android:right="2dp"
|
||||
android:top="2dp" />
|
||||
<solid android:color="#ffffffff" />
|
||||
</shape>
|
@ -0,0 +1,8 @@
|
||||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<shape xmlns:android="http://schemas.android.com/apk/res/android">
|
||||
<size
|
||||
android:width="40dp"
|
||||
android:height="40dp" />
|
||||
<corners android:radius="20dp" />
|
||||
<solid android:color="#82eae5e5" />
|
||||
</shape>
|
@ -0,0 +1,8 @@
|
||||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<shape xmlns:android="http://schemas.android.com/apk/res/android">
|
||||
<size
|
||||
android:width="100dp"
|
||||
android:height="40dp" />
|
||||
<corners android:radius="20dp" />
|
||||
<solid android:color="#784b4b4b" />
|
||||
</shape>
|
@ -0,0 +1,43 @@
|
||||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<RelativeLayout xmlns:android="http://schemas.android.com/apk/res/android"
|
||||
xmlns:tools="http://schemas.android.com/tools"
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="match_parent"
|
||||
android:background="#00000000">
|
||||
|
||||
<FrameLayout
|
||||
android:id="@+id/container"
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="match_parent"
|
||||
android:background="@android:color/black"
|
||||
tools:context="com.mindspore.scene.widget.CameraActivity" />
|
||||
|
||||
<LinearLayout
|
||||
android:id="@+id/layout_bottom_content"
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="100dp"
|
||||
android:layout_alignParentBottom="true"
|
||||
android:background="#ffffff"
|
||||
android:gravity="center"
|
||||
android:orientation="vertical" />
|
||||
|
||||
<androidx.appcompat.widget.Toolbar
|
||||
android:id="@+id/toolbar"
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="?attr/actionBarSize"
|
||||
android:layout_alignParentTop="true"
|
||||
android:background="@color/mindspore_semi_transparent">
|
||||
|
||||
<TextView
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="wrap_content"
|
||||
android:drawableStart="@mipmap/logo"
|
||||
android:drawablePadding="5dp"
|
||||
android:gravity="center_vertical"
|
||||
android:text="MS Scene Detection"
|
||||
android:textColor="#ffffff"
|
||||
android:textSize="25sp" />
|
||||
</androidx.appcompat.widget.Toolbar>
|
||||
|
||||
|
||||
</RelativeLayout>
|
@ -0,0 +1,12 @@
|
||||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<androidx.constraintlayout.widget.ConstraintLayout xmlns:android="http://schemas.android.com/apk/res/android"
|
||||
xmlns:tools="http://schemas.android.com/tools"
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="match_parent"
|
||||
tools:context=".widget.TestActivity">
|
||||
|
||||
<ImageView
|
||||
android:layout_width="wrap_content"
|
||||
android:layout_height="wrap_content"
|
||||
android:id="@+id/img" />
|
||||
</androidx.constraintlayout.widget.ConstraintLayout>
|
Some files were not shown because too many files have changed in this diff Show More
Loading…
Reference in new issue