├── .gitignore ├── LICENSE ├── README.md ├── app ├── .gitignore ├── CMakeLists.txt ├── build.gradle ├── proguard-rules.pro └── src │ ├── androidTest │ └── java │ │ └── com │ │ └── facesdk │ │ └── ExampleInstrumentedTest.java │ ├── main │ ├── AndroidManifest.xml │ ├── assets │ │ ├── RFB-320-quant-ADMM-32.mnn │ │ ├── RFB-320-quant-KL-5792.mnn │ │ ├── RFB-320.mnn │ │ ├── slim-320-quant-ADMM-50.mnn │ │ └── slim-320.mnn │ ├── cpp │ │ ├── UltraFace.cpp │ │ ├── UltraFace.hpp │ │ ├── Ultra_jni.cpp │ │ ├── include │ │ │ ├── AutoTime.hpp │ │ │ ├── Backend.hpp │ │ │ ├── ErrorCode.hpp │ │ │ ├── HalideRuntime.h │ │ │ ├── ImageProcess.hpp │ │ │ ├── Interpreter.hpp │ │ │ ├── MNNDefine.h │ │ │ ├── MNNForwardType.h │ │ │ ├── MNNSharedContext.h │ │ │ ├── Matrix.h │ │ │ ├── NonCopyable.hpp │ │ │ ├── Rect.h │ │ │ ├── Tensor.hpp │ │ │ └── revertMNNModel.hpp │ │ ├── net.cpp │ │ └── net.h │ ├── java │ │ └── com │ │ │ └── facesdk │ │ │ ├── FaceSDKNative.java │ │ │ └── MainActivity.java │ ├── jniLibs │ │ ├── arm64-v8a │ │ │ ├── libMNN.so │ │ │ ├── libMNN_CL.so │ │ │ └── libMNN_Express.so │ │ └── armeabi-v7a │ │ │ ├── libMNN.so │ │ │ ├── libMNN_CL.so │ │ │ └── libMNN_Express.so │ └── res │ │ ├── layout │ │ ├── activity_main.xml │ │ └── main.xml │ │ ├── mipmap-hdpi │ │ └── ic_launcher.png │ │ ├── mipmap-mdpi │ │ └── ic_launcher.png │ │ ├── mipmap-xhdpi │ │ └── ic_launcher.png │ │ ├── mipmap-xxhdpi │ │ └── ic_launcher.png │ │ ├── mipmap-xxxhdpi │ │ └── ic_launcher.png │ │ ├── values-w820dp │ │ └── dimens.xml │ │ └── values │ │ ├── colors.xml │ │ ├── dimens.xml │ │ ├── strings.xml │ │ └── styles.xml │ └── test │ └── java │ └── com │ └── facesdk │ └── ExampleUnitTest.java ├── build.gradle ├── gradle.properties ├── gradle └── wrapper │ ├── gradle-wrapper.jar │ └── gradle-wrapper.properties ├── gradlew ├── gradlew.bat ├── res_img └── result.jpg └── settings.gradle /.gitignore: -------------------------------------------------------------------------------- 1 | *.iml 2 | .gradle 3 | /local.properties 4 | /.idea 5 | .DS_Store 6 | /build 7 | /captures 8 | .externalNativeBuild 9 | -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | MIT License 2 | 3 | Copyright (c) 2020 jackweiwang 4 | 5 | Permission is hereby granted, free of charge, to any person obtaining a copy 6 | of this software and associated documentation files (the "Software"), to deal 7 | in the Software without restriction, including without limitation the rights 8 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 9 | copies of the Software, and to permit persons to whom the Software is 10 | furnished to do so, subject to the following conditions: 11 | 12 | The above copyright notice and this permission notice shall be included in all 13 | copies or substantial portions of the Software. 14 | 15 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 16 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 17 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 18 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 19 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 20 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 21 | SOFTWARE. 22 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | ## Face detection android 2 | v1.0 3 | v1.1 remove opencv 4 | v1.2 inference reduction about 20ms 5 | ## Platform 6 | android 7 | ## Master 8 | [Ultra-Light-Fast-Generic-Face-Detector-1MB](https://github.com/Linzaer/Ultra-Light-Fast-Generic-Face-Detector-1MB) with [MNN](https://github.com/alibaba/MNN) 9 | 10 | ## Build 11 | Android Studio Build. 12 | 13 | ## Show Result 14 | Device | Input resolution 15 | -------| --------- 16 | Lenovo: PB2-690N | 320x240 17 | 18 | Model | Inference(ms) 19 | ---- | ---- 20 | RFB-320-quant-ADMM-32.mnn | 35 21 | 22 | ## MNN Lib Version 23 | 0.2.2.1 24 | -------------------------------------------------------------------------------- /app/.gitignore: -------------------------------------------------------------------------------- 1 | /build 2 | -------------------------------------------------------------------------------- /app/CMakeLists.txt: -------------------------------------------------------------------------------- 1 | cmake_minimum_required(VERSION 3.4.1) 2 | #set (CMAKE_C_FLAGS "${CMAKE_C_FLAGS} -fopenmp") 3 | #set (CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -fopenmp") 4 | 5 | #set (CMAKE_C_FLAGS "${CMAKE_C_FLAGS} -std=gnu99 -fvisibility=hidden -fomit-frame-pointer -fstrict-aliasing -ffunction-sections -fdata-sections -ffast-math -flax-vector-conversions") 6 | set (CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -std=c++11") 7 | #set (CMAKE_LINKER_FLAGS "${CMAKE_LINKER_FLAGS} -Wl,--gc-sections") 8 | 9 | include_directories(src/main/cpp/include 10 | src/main/cpp/) 11 | 12 | #add_library(lib_opencv STATIC IMPORTED ) #引入libopencv_java3.so文件 13 | #set_target_properties(lib_opencv 14 | # PROPERTIES 15 | # IMPORTED_LOCATION ${CMAKE_SOURCE_DIR}/src/main/jniLibs/${ANDROID_ABI}/libopencv_java3.so 16 | # ) 17 | 18 | add_library(MNN SHARED IMPORTED) 19 | add_library(MNN_CL SHARED IMPORTED) 20 | #add_library(MNN_GL SHARED IMPORTED) 21 | 22 | set_target_properties(MNN 23 | PROPERTIES IMPORTED_LOCATION 24 | ${CMAKE_SOURCE_DIR}/src/main/jniLibs/${ANDROID_ABI}/libMNN.so) 25 | set_target_properties(MNN_CL 26 | PROPERTIES IMPORTED_LOCATION 27 | ${CMAKE_SOURCE_DIR}/src/main/jniLibs/${ANDROID_ABI}/libMNN_CL.so) 28 | #set_target_properties(MNN_GL 29 | # PROPERTIES IMPORTED_LOCATION 30 | # ${CMAKE_SOURCE_DIR}/src/main/jniLibs/${ANDROID_ABI}/libMNN_GL.so) 31 | 32 | add_library(facedetect SHARED src/main/cpp/Ultra_jni.cpp 33 | src/main/cpp/UltraFace.cpp 34 | src/main/cpp/net.cpp) 35 | 36 | find_library(log-lib log) 37 | find_library( jnigraphics-lib jnigraphics ) 38 | add_definitions(-DMNN_USE_LOGCAT) 39 | target_link_libraries(facedetect 40 | MNN 41 | MNN_CL 42 | #MNN_GL 43 | #lib_opencv 44 | jnigraphics 45 | z 46 | ${log-lib}) 47 | 48 | 49 | -------------------------------------------------------------------------------- /app/build.gradle: -------------------------------------------------------------------------------- 1 | apply plugin: 'com.android.application' 2 | 3 | android { 4 | compileSdkVersion 28 5 | defaultConfig { 6 | applicationId "com.facesdk" 7 | minSdkVersion 14 8 | targetSdkVersion 28 9 | versionCode 1 10 | versionName "1.0" 11 | testInstrumentationRunner "android.support.test.runner.AndroidJUnitRunner" 12 | 13 | externalNativeBuild { 14 | cmake { 15 | arguments "-DANDROID_STL=c++_shared", "-DANDROID_ARM_NEON=TRUE", "-DANDROID_PLATFORM=android-21", 16 | "-DMNN_OPENCL=true", "-DMNN_OPENGL=true" 17 | abiFilters 'arm64-v8a', 'armeabi-v7a' 18 | } 19 | } 20 | } 21 | buildTypes { 22 | debug { 23 | ndk { 24 | abiFilters 'arm64-v8a', 'armeabi-v7a' 25 | } 26 | } 27 | release { 28 | minifyEnabled false 29 | proguardFiles getDefaultProguardFile('proguard-android.txt'), 'proguard-rules.pro' 30 | } 31 | } 32 | 33 | externalNativeBuild { 34 | cmake { 35 | path "CMakeLists.txt" 36 | } 37 | } 38 | 39 | 40 | } 41 | 42 | dependencies { 43 | // implementation fileTree(dir: 'libs', include: ['*.jar']) 44 | // implementation 'com.android.support:appcompat-v7:28.0.0' 45 | // implementation 'com.android.support.constraint:constraint-layout:1.1.3' 46 | // testImplementation 'junit:junit:4.12' 47 | // androidTestImplementation 'com.android.support.test:runner:1.0.2' 48 | // androidTestImplementation 'com.android.support.test.espresso:espresso-core:3.0.2' 49 | // implementation 'com.android.support:design:28.0.0' 50 | // implementation 'com.android.support:cardview-v7:28.0.0' 51 | 52 | implementation fileTree(include: ['*.jar'], dir: 'libs') 53 | androidTestImplementation('com.android.support.test.espresso:espresso-core:3.0.2', { 54 | exclude group: 'com.android.support', module: 'support-annotations' 55 | }) 56 | implementation 'com.android.support:appcompat-v7:28.0.0' 57 | implementation 'com.android.support:support-v4:28.0.0' 58 | testImplementation 'junit:junit:4.12' 59 | implementation 'com.google.android.gms:play-services-appindexing:8.4.0' 60 | } 61 | -------------------------------------------------------------------------------- /app/proguard-rules.pro: -------------------------------------------------------------------------------- 1 | # Add project specific ProGuard rules here. 2 | # By default, the flags in this file are appended to flags specified 3 | # in D:\AndroidSDK/tools/proguard/proguard-android.txt 4 | # You can edit the include path and order by changing the proguardFiles 5 | # directive in build.gradle. 6 | # 7 | # For more details, see 8 | # http://developer.android.com/guide/developing/tools/proguard.html 9 | 10 | # Add any project specific keep options here: 11 | 12 | # If your project uses WebView with JS, uncomment the following 13 | # and specify the fully qualified class name to the JavaScript interface 14 | # class: 15 | #-keepclassmembers class fqcn.of.javascript.interface.for.webview { 16 | # public *; 17 | #} 18 | -------------------------------------------------------------------------------- /app/src/androidTest/java/com/facesdk/ExampleInstrumentedTest.java: -------------------------------------------------------------------------------- 1 | package com.facesdk; 2 | 3 | import android.content.Context; 4 | import android.support.test.InstrumentationRegistry; 5 | import android.support.test.runner.AndroidJUnit4; 6 | 7 | import org.junit.Test; 8 | import org.junit.runner.RunWith; 9 | 10 | import static org.junit.Assert.*; 11 | 12 | /** 13 | * Instrumentation test, which will execute on an Android device. 14 | * 15 | * @see Testing documentation 16 | */ 17 | @RunWith(AndroidJUnit4.class) 18 | public class ExampleInstrumentedTest { 19 | @Test 20 | public void useAppContext() throws Exception { 21 | // Context of the app under test. 22 | Context appContext = InstrumentationRegistry.getTargetContext(); 23 | 24 | assertEquals("com.facesdk", appContext.getPackageName()); 25 | } 26 | } 27 | -------------------------------------------------------------------------------- /app/src/main/AndroidManifest.xml: -------------------------------------------------------------------------------- 1 | 2 | 4 | 5 | 6 | 7 | 8 | 9 | 15 | 16 | 17 | 18 | 19 | 20 | 21 | 23 | 26 | 27 | 28 | -------------------------------------------------------------------------------- /app/src/main/assets/RFB-320-quant-ADMM-32.mnn: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/jackweiwang/Android-FaceDetection-UltraNet-MNN/6f4e5e0184bbdfe6ed6bcf9b993d393bc03f18a2/app/src/main/assets/RFB-320-quant-ADMM-32.mnn -------------------------------------------------------------------------------- /app/src/main/assets/RFB-320-quant-KL-5792.mnn: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/jackweiwang/Android-FaceDetection-UltraNet-MNN/6f4e5e0184bbdfe6ed6bcf9b993d393bc03f18a2/app/src/main/assets/RFB-320-quant-KL-5792.mnn -------------------------------------------------------------------------------- /app/src/main/assets/RFB-320.mnn: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/jackweiwang/Android-FaceDetection-UltraNet-MNN/6f4e5e0184bbdfe6ed6bcf9b993d393bc03f18a2/app/src/main/assets/RFB-320.mnn -------------------------------------------------------------------------------- /app/src/main/assets/slim-320-quant-ADMM-50.mnn: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/jackweiwang/Android-FaceDetection-UltraNet-MNN/6f4e5e0184bbdfe6ed6bcf9b993d393bc03f18a2/app/src/main/assets/slim-320-quant-ADMM-50.mnn -------------------------------------------------------------------------------- /app/src/main/assets/slim-320.mnn: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/jackweiwang/Android-FaceDetection-UltraNet-MNN/6f4e5e0184bbdfe6ed6bcf9b993d393bc03f18a2/app/src/main/assets/slim-320.mnn -------------------------------------------------------------------------------- /app/src/main/cpp/UltraFace.cpp: -------------------------------------------------------------------------------- 1 | // Created by Linzaer on 2019/11/15. 2 | // Copyright © 2019 Linzaer. All rights reserved. 3 | 4 | #define clip(x, y) (x < 0 ? 0 : (x > y ? y : x)) 5 | 6 | #include "UltraFace.hpp" 7 | #define TAG "cpp" 8 | #define LOGD(...) __android_log_print(ANDROID_LOG_DEBUG, TAG, __VA_ARGS__) 9 | using namespace std; 10 | 11 | UltraFace::UltraFace(std::string &mnn_path, 12 | int input_width, int input_length, int num_thread_, 13 | float score_threshold_, float iou_threshold_) { 14 | num_thread = num_thread_; 15 | score_threshold = score_threshold_; 16 | iou_threshold = iou_threshold_; 17 | in_w = input_width; 18 | in_h = input_length; 19 | w_h_list = {in_w, in_h}; 20 | 21 | for (auto size : w_h_list) { 22 | std::vector fm_item; 23 | for (float stride : strides) { 24 | fm_item.push_back(ceil(size / stride)); 25 | } 26 | featuremap_size.push_back(fm_item); 27 | } 28 | 29 | for (auto size : w_h_list) { 30 | shrinkage_size.push_back(strides); 31 | } 32 | /* generate prior anchors */ 33 | for (int index = 0; index < num_featuremap; index++) { 34 | float scale_w = in_w / shrinkage_size[0][index]; 35 | float scale_h = in_h / shrinkage_size[1][index]; 36 | for (int j = 0; j < featuremap_size[1][index]; j++) { 37 | for (int i = 0; i < featuremap_size[0][index]; i++) { 38 | float x_center = (i + 0.5) / scale_w; 39 | float y_center = (j + 0.5) / scale_h; 40 | 41 | for (float k : min_boxes[index]) { 42 | float w = k / in_w; 43 | float h = k / in_h; 44 | priors.push_back({clip(x_center, 1), clip(y_center, 1), clip(w, 1), clip(h, 1)}); 45 | } 46 | } 47 | } 48 | } 49 | /* generate prior anchors finished */ 50 | 51 | num_anchors = priors.size(); 52 | 53 | ultra_net.load_param(mnn_path, num_thread); 54 | ultra_net.set_params(0, 1, mean_vals, norm_vals); 55 | 56 | } 57 | 58 | int UltraFace::detect(unsigned char *data, int width, int height, int channel, std::vector &face_list ) { 59 | 60 | 61 | image_h = height; 62 | image_w = width; 63 | 64 | Inference_engine_tensor out; 65 | 66 | string scores = "scores"; 67 | out.add_name(scores); 68 | 69 | string boxes = "boxes"; 70 | out.add_name(boxes); 71 | 72 | ultra_net.infer_img(data, width, height, channel, in_w, in_h, out); 73 | 74 | std::vector bbox_collection; 75 | generateBBox(bbox_collection, out.score(0).get() , out.score(1).get()); 76 | //LOGD("bbox_collection == %d", bbox_collection.size()); 77 | nms(bbox_collection, face_list); 78 | return 0; 79 | } 80 | 81 | void UltraFace::generateBBox(std::vector &bbox_collection, float* scores, float* boxes) { 82 | for (int i = 0; i < num_anchors; i++) { 83 | if (scores[i * 2 + 1 ] > score_threshold) { 84 | 85 | FaceInfo rects; 86 | float x_center = boxes[i * 4] * center_variance * priors[i][2] + priors[i][0]; 87 | float y_center = boxes[i * 4 + 1] * center_variance * priors[i][3] + priors[i][1]; 88 | float w = exp(boxes[i * 4 + 2] * size_variance) * priors[i][2]; 89 | float h = exp(boxes[i * 4 + 3] * size_variance) * priors[i][3]; 90 | 91 | rects.x1 = clip(x_center - w / 2.0, 1) * image_w; 92 | rects.y1 = clip(y_center - h / 2.0, 1) * image_h; 93 | rects.x2 = clip(x_center + w / 2.0, 1) * image_w; 94 | rects.y2 = clip(y_center + h / 2.0, 1) * image_h; 95 | rects.score = clip(scores[i * 2 + 1 ], 1); 96 | 97 | bbox_collection.push_back(rects); 98 | } 99 | } 100 | } 101 | 102 | void UltraFace::nms(std::vector &input, std::vector &output, int type) { 103 | std::sort(input.begin(), input.end(), [](const FaceInfo &a, const FaceInfo &b) { return a.score > b.score; }); 104 | 105 | int box_num = input.size(); 106 | 107 | std::vector merged(box_num, 0); 108 | 109 | for (int i = 0; i < box_num; i++) { 110 | if (merged[i]) 111 | continue; 112 | std::vector buf; 113 | 114 | buf.push_back(input[i]); 115 | merged[i] = 1; 116 | 117 | float h0 = input[i].y2 - input[i].y1 + 1; 118 | float w0 = input[i].x2 - input[i].x1 + 1; 119 | 120 | float area0 = h0 * w0; 121 | 122 | for (int j = i + 1; j < box_num; j++) { 123 | if (merged[j]) 124 | continue; 125 | 126 | float inner_x0 = input[i].x1 > input[j].x1 ? input[i].x1 : input[j].x1; 127 | float inner_y0 = input[i].y1 > input[j].y1 ? input[i].y1 : input[j].y1; 128 | 129 | float inner_x1 = input[i].x2 < input[j].x2 ? input[i].x2 : input[j].x2; 130 | float inner_y1 = input[i].y2 < input[j].y2 ? input[i].y2 : input[j].y2; 131 | 132 | float inner_h = inner_y1 - inner_y0 + 1; 133 | float inner_w = inner_x1 - inner_x0 + 1; 134 | 135 | if (inner_h <= 0 || inner_w <= 0) 136 | continue; 137 | 138 | float inner_area = inner_h * inner_w; 139 | 140 | float h1 = input[j].y2 - input[j].y1 + 1; 141 | float w1 = input[j].x2 - input[j].x1 + 1; 142 | 143 | float area1 = h1 * w1; 144 | 145 | float score; 146 | 147 | score = inner_area / (area0 + area1 - inner_area); 148 | 149 | if (score > iou_threshold) { 150 | merged[j] = 1; 151 | buf.push_back(input[j]); 152 | } 153 | } 154 | switch (type) { 155 | case hard_nms: { 156 | output.push_back(buf[0]); 157 | break; 158 | } 159 | case blending_nms: { 160 | float total = 0; 161 | for (int i = 0; i < buf.size(); i++) { 162 | total += exp(buf[i].score); 163 | } 164 | FaceInfo rects; 165 | memset(&rects, 0, sizeof(rects)); 166 | for (int i = 0; i < buf.size(); i++) { 167 | float rate = exp(buf[i].score) / total; 168 | rects.x1 += buf[i].x1 * rate; 169 | rects.y1 += buf[i].y1 * rate; 170 | rects.x2 += buf[i].x2 * rate; 171 | rects.y2 += buf[i].y2 * rate; 172 | rects.score += buf[i].score * rate; 173 | } 174 | output.push_back(rects); 175 | break; 176 | } 177 | default: { 178 | printf("wrong type of nms."); 179 | exit(-1); 180 | } 181 | } 182 | } 183 | } 184 | -------------------------------------------------------------------------------- /app/src/main/cpp/UltraFace.hpp: -------------------------------------------------------------------------------- 1 | // Created by Linzaer on 2019/11/15. 2 | // Copyright © 2019 Linzaer. All rights reserved. 3 | 4 | #ifndef UltraFace_hpp 5 | #define UltraFace_hpp 6 | 7 | #pragma once 8 | 9 | #include 10 | #include 11 | #include 12 | #include 13 | #include 14 | #include "net.h" 15 | 16 | #define num_featuremap 4 17 | #define hard_nms 1 18 | #define blending_nms 2 /* mix nms was been proposaled in paper blaze face, aims to minimize the temporal jitter*/ 19 | typedef struct FaceInfo { 20 | float x1; 21 | float y1; 22 | float x2; 23 | float y2; 24 | float score; 25 | 26 | } FaceInfo; 27 | 28 | class UltraFace { 29 | public: 30 | UltraFace(std::string &mnn_path, int input_width, int input_length, int num_thread_ = 4, float score_threshold_ = 0.7, float iou_threshold_ = 0.35); 31 | 32 | //~UltraFace(); 33 | 34 | int detect(unsigned char *raw_image, int width, int height, int channel, std::vector &face_list); 35 | 36 | void generateBBox(std::vector &bbox_collection, float* scores, float* boxes); 37 | 38 | void nms(std::vector &input, std::vector &output, int type = blending_nms); 39 | 40 | private: 41 | Inference_engine ultra_net; 42 | 43 | int num_thread; 44 | int image_w; 45 | int image_h; 46 | 47 | int in_w; 48 | int in_h; 49 | int num_anchors; 50 | 51 | float score_threshold; 52 | float iou_threshold; 53 | 54 | float mean_vals[3] = {127, 127, 127}; 55 | float norm_vals[3] = {1.0 / 128, 1.0 / 128, 1.0 / 128}; 56 | 57 | const float center_variance = 0.1; 58 | const float size_variance = 0.2; 59 | const std::vector> min_boxes = { 60 | {10.0f, 16.0f, 24.0f}, 61 | {32.0f, 48.0f}, 62 | {64.0f, 96.0f}, 63 | {128.0f, 192.0f, 256.0f}}; 64 | const std::vector strides = {8.0, 16.0, 32.0, 64.0}; 65 | std::vector> featuremap_size; 66 | std::vector> shrinkage_size; 67 | std::vector w_h_list; 68 | 69 | std::vector> priors = {}; 70 | }; 71 | 72 | #endif /* UltraFace_hpp */ 73 | -------------------------------------------------------------------------------- /app/src/main/cpp/Ultra_jni.cpp: -------------------------------------------------------------------------------- 1 | #include 2 | #include 3 | #include 4 | #include 5 | #include 6 | #include "UltraFace.hpp" 7 | 8 | #define TAG "FaceSDKNative" 9 | #define LOGD(...) __android_log_print(ANDROID_LOG_DEBUG, TAG, __VA_ARGS__) 10 | 11 | using namespace std; 12 | 13 | static UltraFace *ultra; 14 | bool detection_sdk_init_ok = false; 15 | 16 | extern "C" { 17 | 18 | JNIEXPORT jboolean JNICALL 19 | Java_com_facesdk_FaceSDKNative_FaceDetectionModelInit(JNIEnv *env, jobject instance, 20 | jstring faceDetectionModelPath_) { 21 | LOGD("JNI init native sdk"); 22 | if (detection_sdk_init_ok) { 23 | LOGD("sdk already init"); 24 | return true; 25 | } 26 | jboolean tRet = false; 27 | if (NULL == faceDetectionModelPath_) { 28 | LOGD("model dir is empty"); 29 | return tRet; 30 | } 31 | 32 | //获取模型的绝对路径的目录(不是/aaa/bbb.bin这样的路径,是/aaa/) 33 | const char *faceDetectionModelPath = env->GetStringUTFChars(faceDetectionModelPath_, 0); 34 | if (NULL == faceDetectionModelPath) { 35 | LOGD("model dir is empty"); 36 | return tRet; 37 | } 38 | 39 | string tFaceModelDir = faceDetectionModelPath; 40 | string tLastChar = tFaceModelDir.substr(tFaceModelDir.length()-1, 1); 41 | //RFB-320 42 | //RFB-320-quant-ADMM-32 43 | //RFB-320-quant-KL-5792 44 | //slim-320 45 | //slim-320-quant-ADMM-50 46 | //量化模型需要使用CPU方式 net.cpp中修改 sch_config.type = (MNNForwardType)MNN_FORWARD_CPU 47 | // change names 48 | string str = tFaceModelDir + "RFB-320-quant-ADMM-32.mnn"; 49 | 50 | ultra = new UltraFace(str, 320, 240, 4, 0.65 ); // config model input 51 | 52 | env->ReleaseStringUTFChars(faceDetectionModelPath_, faceDetectionModelPath); 53 | detection_sdk_init_ok = true; 54 | tRet = true; 55 | 56 | return tRet; 57 | } 58 | 59 | JNIEXPORT jintArray JNICALL 60 | Java_com_facesdk_FaceSDKNative_FaceDetect(JNIEnv *env, jobject instance, jbyteArray imageDate_, 61 | jint imageWidth, jint imageHeight, jint imageChannel) { 62 | if(!detection_sdk_init_ok){ 63 | LOGD("sdk not init"); 64 | return NULL; 65 | } 66 | 67 | int tImageDateLen = env->GetArrayLength(imageDate_); 68 | if(imageChannel == tImageDateLen / imageWidth / imageHeight){ 69 | LOGD("imgW=%d, imgH=%d,imgC=%d",imageWidth,imageHeight,imageChannel); 70 | } 71 | else{ 72 | LOGD("img data format error"); 73 | return NULL; 74 | } 75 | 76 | jbyte *imageDate = env->GetByteArrayElements(imageDate_, NULL); 77 | if (NULL == imageDate){ 78 | LOGD("img data is null"); 79 | return NULL; 80 | } 81 | 82 | if(imageWidth<200||imageHeight<200){ 83 | LOGD("img is too small"); 84 | return NULL; 85 | } 86 | 87 | 88 | std::vector face_info; 89 | //detect face 90 | ultra ->detect((unsigned char*)imageDate, imageWidth, imageHeight, imageChannel, face_info ); 91 | 92 | int32_t num_face = static_cast(face_info.size()); 93 | 94 | int out_size = 1+num_face*4; 95 | int *allfaceInfo = new int[out_size]; 96 | allfaceInfo[0] = num_face; 97 | for (int i=0; iNewIntArray(out_size); 107 | env->SetIntArrayRegion(tFaceInfo, 0, out_size, allfaceInfo); 108 | env->ReleaseByteArrayElements(imageDate_, imageDate, 0); 109 | 110 | 111 | delete [] allfaceInfo; 112 | 113 | return tFaceInfo; 114 | } 115 | 116 | JNIEXPORT jboolean JNICALL 117 | Java_com_facesdk_FaceSDKNative_FaceDetectionModelUnInit(JNIEnv *env, jobject instance) { 118 | 119 | jboolean tDetectionUnInit = false; 120 | 121 | if (!detection_sdk_init_ok) { 122 | LOGD("sdk not inited, do nothing"); 123 | return true; 124 | } 125 | 126 | delete ultra; 127 | 128 | detection_sdk_init_ok = false; 129 | 130 | tDetectionUnInit = true; 131 | 132 | LOGD("sdk release ok"); 133 | 134 | return tDetectionUnInit; 135 | } 136 | 137 | } 138 | -------------------------------------------------------------------------------- /app/src/main/cpp/include/AutoTime.hpp: -------------------------------------------------------------------------------- 1 | // 2 | // AutoTime.hpp 3 | // MNN 4 | // 5 | // Created by MNN on 2018/07/27. 6 | // Copyright © 2018, Alibaba Group Holding Limited 7 | // 8 | 9 | #ifndef AutoTime_hpp 10 | #define AutoTime_hpp 11 | 12 | #include 13 | #include 14 | #include "MNNDefine.h" 15 | 16 | namespace MNN { 17 | 18 | /** time tracing util. prints duration between init and deinit. */ 19 | class MNN_PUBLIC AutoTime { 20 | public: 21 | AutoTime(int line, const char* func); 22 | ~AutoTime(); 23 | AutoTime(const AutoTime&) = delete; 24 | AutoTime(const AutoTime&&) = delete; 25 | AutoTime& operator=(const AutoTime&) = delete; 26 | AutoTime& operator=(const AutoTime&&) = delete; 27 | 28 | private: 29 | int mLine; 30 | char* mName; 31 | uint64_t mCurrentTime; 32 | }; 33 | } // namespace MNN 34 | 35 | #ifdef MNN_OPEN_TIME_TRACE 36 | #define AUTOTIME MNN::AutoTime ___t(__LINE__, __func__) 37 | #else 38 | #define AUTOTIME 39 | #endif 40 | 41 | #endif /* AutoTime_hpp */ 42 | -------------------------------------------------------------------------------- /app/src/main/cpp/include/Backend.hpp: -------------------------------------------------------------------------------- 1 | // 2 | // Backend.hpp 3 | // MNN 4 | // 5 | // Created by MNN on 2018/07/06. 6 | // Copyright © 2018, Alibaba Group Holding Limited 7 | // 8 | 9 | #ifndef Backend_hpp 10 | #define Backend_hpp 11 | 12 | #include 13 | #include 14 | #include 15 | #include 16 | #include "ErrorCode.hpp" 17 | #include "MNNForwardType.h" 18 | #include "NonCopyable.hpp" 19 | #include "Tensor.hpp" 20 | 21 | namespace MNN { 22 | 23 | struct Op; 24 | struct GpuLibrary; 25 | class Execution; 26 | 27 | /** abstract backend */ 28 | class Backend : public NonCopyable { 29 | public: 30 | /** info used to create backend */ 31 | struct Info { 32 | /** forward type. */ 33 | MNNForwardType type = MNN_FORWARD_CPU; 34 | /** for CPU only. number of threads. */ 35 | int numThread = 4; 36 | /** user data. */ 37 | BackendConfig* user = NULL; 38 | enum Mode { 39 | // The Op will be run in execution->onExecute 40 | DIRECT = 0, 41 | 42 | // The Op will be recorded. Run in onExecuteBegin and Wait in onExecuteEnd 43 | INDIRECT = 1 44 | }; 45 | Mode mode = DIRECT; 46 | }; 47 | 48 | /** backend buffer storage type */ 49 | enum StorageType { 50 | /** 51 | use NOT reusable memory. 52 | - allocates memory when `onAcquireBuffer` is called. 53 | - releases memory when `onReleaseBuffer` is called or when the backend is deleted. 54 | - do NOTHING when `onClearBuffer` is called. 55 | */ 56 | STATIC, 57 | /** 58 | use reusable memory. 59 | - allocates or reuses memory when `onAcquireBuffer` is called. prefers reusing. 60 | - collects memory for reuse when `onReleaseBuffer` is called. 61 | - releases memory when `onClearBuffer` is called or when the backend is deleted. 62 | */ 63 | DYNAMIC, 64 | /** 65 | use NOT reusable memory. 66 | - allocates memory when `onAcquireBuffer` is called. 67 | - do NOTHING when `onReleaseBuffer` is called. 68 | - releases memory when `onClearBuffer` is called or when the backend is deleted. 69 | */ 70 | DYNAMIC_SEPERATE 71 | }; 72 | 73 | public: 74 | /** 75 | * @brief initializer. 76 | * @param type forward type. 77 | */ 78 | Backend(MNNForwardType type) : mType(type) { 79 | // nothing to do 80 | } 81 | 82 | /** 83 | * @brief deinitializer. 84 | */ 85 | virtual ~Backend() = default; 86 | 87 | public: 88 | /** 89 | * @brief measure the cost for op with input and output tensors. 90 | * @param inputs input tensors. 91 | * @param outputs output tensors. 92 | * @param op given op. 93 | * @return std::make_pair(timeDelayInMs, support); 94 | */ 95 | virtual std::pair onMeasure(const std::vector& inputs, const std::vector& outputs, 96 | const MNN::Op* op) { 97 | return std::make_pair(0.0f, false); 98 | } 99 | 100 | /** 101 | * @brief create execution for op with input and output tensors. 102 | * @param inputs input tensors. 103 | * @param outputs output tensors. 104 | * @param op given op. 105 | * @return created execution if op is supported, nullptr otherwise. 106 | */ 107 | virtual Execution* onCreate(const std::vector& inputs, const std::vector& outputs, 108 | const MNN::Op* op) = 0; 109 | 110 | /** 111 | * @brief callback before resize ops. 112 | */ 113 | virtual void onResizeBegin() { 114 | // nothing to do 115 | } 116 | /** 117 | * @brief callback after resize ops. 118 | */ 119 | virtual void onResizeEnd() { 120 | // nothing to do 121 | } 122 | 123 | /** 124 | * @brief callback before executing ops. 125 | */ 126 | virtual void onExecuteBegin() const = 0; 127 | /** 128 | * @brief callback after executing ops. 129 | */ 130 | virtual void onExecuteEnd() const = 0; 131 | /** 132 | * @brief wait for all async execution to be finished. 133 | * @return success or not. 134 | */ 135 | virtual bool onWaitFinish() { 136 | return true; 137 | } 138 | /** 139 | * @brief load GPU library resource. 140 | * @param library loading load GPU library. 141 | * @return success or not. 142 | */ 143 | virtual bool onLoadLibrary(const GpuLibrary* library) { 144 | return false; 145 | } 146 | 147 | public: 148 | /** 149 | * @brief allocate buffer of tensor for given storage type. 150 | * @param tensor buffer provider. 151 | * @param storageType buffer storage type. 152 | * @return success or not. 153 | */ 154 | virtual bool onAcquireBuffer(const Tensor* tensor, StorageType storageType) = 0; 155 | 156 | /** 157 | * @brief release buffer of tensor for given storage type. 158 | * @param tensor buffer provider. 159 | * @param storageType buffer storage type. 160 | * @return success or not. 161 | */ 162 | virtual bool onReleaseBuffer(const Tensor* tensor, StorageType storageType) = 0; 163 | 164 | /** 165 | * @brief callback after all buffers needed by backend ops were allocated. 166 | * @return success or not. (result not used currently) 167 | */ 168 | virtual bool onAllocateBuffer() { 169 | return true; 170 | } 171 | 172 | /** 173 | * @brief clear all dynamic buffers. 174 | * @return success or not. 175 | */ 176 | virtual bool onClearBuffer() = 0; 177 | 178 | /** 179 | * @brief copy buffer from tensor to tensor. 180 | * @param srcTensor source buffer provider. 181 | * @param dstTensor dest buffer provider. 182 | */ 183 | virtual void onCopyBuffer(const Tensor* srcTensor, const Tensor* dstTensor) const = 0; 184 | 185 | public: 186 | /** 187 | * @brief get forward type. 188 | * @return forward type. 189 | */ 190 | inline MNNForwardType type() const { 191 | return mType; 192 | } 193 | 194 | private: 195 | const MNNForwardType mType; 196 | }; 197 | 198 | /** abstract backend register */ 199 | class BackendCreator { 200 | public: 201 | /** 202 | @brief initializer. 203 | */ 204 | virtual ~BackendCreator() = default; 205 | 206 | /** 207 | @brief create backend with given info. 208 | @param info info to create backend. 209 | @return created backend 210 | */ 211 | virtual Backend* onCreate(const Backend::Info& info) const = 0; 212 | 213 | 214 | /** 215 | @brief Turn info to supported. 216 | @param info info to valid. 217 | @return success or not 218 | */ 219 | virtual bool onValid(Backend::Info& info) const { 220 | info.mode = Backend::Info::DIRECT; 221 | return true; 222 | } 223 | protected: 224 | /** 225 | @brief deinitializer. 226 | */ 227 | BackendCreator() = default; 228 | }; 229 | 230 | /** 231 | * @brief get registered backend creator for given forward type. 232 | * @param type given forward type. 233 | * @return backend creator pointer if registered, nullptr otherwise. 234 | */ 235 | MNN_PUBLIC const BackendCreator* MNNGetExtraBackendCreator(MNNForwardType type); 236 | 237 | /** 238 | * @brief register backend creator for given forward type. 239 | * @param type given forward type. 240 | * @param creator registering backend creator. 241 | * @return true if backend creator for given forward type was not registered before, false otherwise. 242 | */ 243 | MNN_PUBLIC bool MNNInsertExtraBackendCreator(MNNForwardType type, const BackendCreator* creator, 244 | bool needCheck = false); 245 | 246 | } // namespace MNN 247 | 248 | #endif /* Backend_hpp */ 249 | -------------------------------------------------------------------------------- /app/src/main/cpp/include/ErrorCode.hpp: -------------------------------------------------------------------------------- 1 | // 2 | // ErrorCode.hpp 3 | // MNN 4 | // 5 | // Created by MNN on 2018/09/18. 6 | // Copyright © 2018, Alibaba Group Holding Limited 7 | // 8 | 9 | #ifndef ErrorCode_h 10 | #define ErrorCode_h 11 | 12 | namespace MNN { 13 | enum ErrorCode { 14 | #ifdef NO_ERROR 15 | #undef NO_ERROR 16 | #endif // NO_ERROR 17 | NO_ERROR = 0, 18 | OUT_OF_MEMORY = 1, 19 | NOT_SUPPORT = 2, 20 | COMPUTE_SIZE_ERROR = 3, 21 | NO_EXECUTION = 4, 22 | INVALID_VALUE = 5, 23 | 24 | // User error 25 | INPUT_DATA_ERROR = 10, 26 | CALL_BACK_STOP = 11, 27 | 28 | // Op Resize Error 29 | TENSOR_NOT_SUPPORT = 20, 30 | TENSOR_NEED_DIVIDE = 21, 31 | }; 32 | } // namespace MNN 33 | 34 | #endif /* ErrorCode_h */ 35 | -------------------------------------------------------------------------------- /app/src/main/cpp/include/HalideRuntime.h: -------------------------------------------------------------------------------- 1 | #ifndef HALIDE_HALIDERUNTIME_H 2 | #define HALIDE_HALIDERUNTIME_H 3 | 4 | #include 5 | #include 6 | #include 7 | 8 | #ifdef __cplusplus 9 | extern "C" { 10 | #endif 11 | 12 | // Note that you should not use "inline" along with HALIDE_ALWAYS_INLINE; 13 | // it is not necessary, and may produce warnings for some build configurations. 14 | #ifdef _MSC_VER 15 | #define HALIDE_ALWAYS_INLINE __forceinline 16 | #define HALIDE_NEVER_INLINE __declspec(noinline) 17 | #else 18 | #define HALIDE_ALWAYS_INLINE __attribute__((always_inline)) inline 19 | #define HALIDE_NEVER_INLINE __attribute__((noinline)) 20 | #endif 21 | 22 | /** \file 23 | * 24 | * This file declares the routines used by Halide internally in its 25 | * runtime. On platforms that support weak linking, these can be 26 | * replaced with user-defined versions by defining an extern "C" 27 | * function with the same name and signature. 28 | * 29 | * When doing Just In Time (JIT) compilation methods on the Func being 30 | * compiled must be called instead. The corresponding methods are 31 | * documented below. 32 | * 33 | * All of these functions take a "void *user_context" parameter as their 34 | * first argument; if the Halide kernel that calls back to any of these 35 | * functions has been compiled with the UserContext feature set on its Target, 36 | * then the value of that pointer passed from the code that calls the 37 | * Halide kernel is piped through to the function. 38 | * 39 | * Some of these are also useful to call when using the default 40 | * implementation. E.g. halide_shutdown_thread_pool. 41 | * 42 | * Note that even on platforms with weak linking, some linker setups 43 | * may not respect the override you provide. E.g. if the override is 44 | * in a shared library and the halide object files are linked directly 45 | * into the output, the builtin versions of the runtime functions will 46 | * be called. See your linker documentation for more details. On 47 | * Linux, LD_DYNAMIC_WEAK=1 may help. 48 | * 49 | */ 50 | 51 | // Forward-declare to suppress warnings if compiling as C. 52 | struct halide_buffer_t; 53 | 54 | /** Types in the halide type system. They can be ints, unsigned ints, 55 | * or floats (of various bit-widths), or a handle (which is always 64-bits). 56 | * Note that the int/uint/float values do not imply a specific bit width 57 | * (the bit width is expected to be encoded in a separate value). 58 | */ 59 | typedef enum halide_type_code_t 60 | { 61 | halide_type_int = 0, //!< signed integers 62 | halide_type_uint = 1, //!< unsigned integers 63 | halide_type_float = 2, //!< floating point numbers 64 | halide_type_handle = 3 //!< opaque pointer type (void *) 65 | } halide_type_code_t; 66 | 67 | // Note that while __attribute__ can go before or after the declaration, 68 | // __declspec apparently is only allowed before. 69 | #ifndef HALIDE_ATTRIBUTE_ALIGN 70 | #ifdef _MSC_VER 71 | #define HALIDE_ATTRIBUTE_ALIGN(x) __declspec(align(x)) 72 | #else 73 | #define HALIDE_ATTRIBUTE_ALIGN(x) __attribute__((aligned(x))) 74 | #endif 75 | #endif 76 | 77 | /** A runtime tag for a type in the halide type system. Can be ints, 78 | * unsigned ints, or floats of various bit-widths (the 'bits' 79 | * field). Can also be vectors of the same (by setting the 'lanes' 80 | * field to something larger than one). This struct should be 81 | * exactly 32-bits in size. */ 82 | struct halide_type_t { 83 | /** The basic type code: signed integer, unsigned integer, or floating point. */ 84 | #if __cplusplus >= 201103L 85 | HALIDE_ATTRIBUTE_ALIGN(1) halide_type_code_t code; // halide_type_code_t 86 | #else 87 | HALIDE_ATTRIBUTE_ALIGN(1) uint8_t code; // halide_type_code_t 88 | #endif 89 | 90 | /** The number of bits of precision of a single scalar value of this type. */ 91 | HALIDE_ATTRIBUTE_ALIGN(1) uint8_t bits; 92 | 93 | /** How many elements in a vector. This is 1 for scalar types. */ 94 | HALIDE_ATTRIBUTE_ALIGN(2) uint16_t lanes; 95 | 96 | #ifdef __cplusplus 97 | /** Construct a runtime representation of a Halide type from: 98 | * code: The fundamental type from an enum. 99 | * bits: The bit size of one element. 100 | * lanes: The number of vector elements in the type. */ 101 | HALIDE_ALWAYS_INLINE halide_type_t(halide_type_code_t code, uint8_t bits, uint16_t lanes = 1) 102 | : code(code), bits(bits), lanes(lanes) { 103 | } 104 | 105 | /** Default constructor is required e.g. to declare halide_trace_event 106 | * instances. */ 107 | HALIDE_ALWAYS_INLINE halide_type_t() : code((halide_type_code_t)0), bits(0), lanes(0) {} 108 | 109 | /** Compare two types for equality. */ 110 | HALIDE_ALWAYS_INLINE bool operator==(const halide_type_t &other) const { 111 | return (code == other.code && 112 | bits == other.bits && 113 | lanes == other.lanes); 114 | } 115 | 116 | HALIDE_ALWAYS_INLINE bool operator!=(const halide_type_t &other) const { 117 | return !(*this == other); 118 | } 119 | 120 | /** Size in bytes for a single element, even if width is not 1, of this type. */ 121 | HALIDE_ALWAYS_INLINE int bytes() const { return (bits + 7) / 8; } 122 | #endif 123 | }; 124 | 125 | /** An opaque struct containing per-GPU API implementations of the 126 | * device functions. */ 127 | struct halide_device_interface_impl_t; 128 | 129 | /** Each GPU API provides a halide_device_interface_t struct pointing 130 | * to the code that manages device allocations. You can access these 131 | * functions directly from the struct member function pointers, or by 132 | * calling the functions declared below. Note that the global 133 | * functions are not available when using Halide as a JIT compiler. 134 | * If you are using raw halide_buffer_t in that context you must use 135 | * the function pointers in the device_interface struct. 136 | * 137 | * The function pointers below are currently the same for every GPU 138 | * API; only the impl field varies. These top-level functions do the 139 | * bookkeeping that is common across all GPU APIs, and then dispatch 140 | * to more API-specific functions via another set of function pointers 141 | * hidden inside the impl field. 142 | */ 143 | struct halide_device_interface_t { 144 | int (*device_malloc)(void *user_context, struct halide_buffer_t *buf, 145 | const struct halide_device_interface_t *device_interface); 146 | int (*device_free)(void *user_context, struct halide_buffer_t *buf); 147 | int (*device_sync)(void *user_context, struct halide_buffer_t *buf); 148 | void (*device_release)(void *user_context, 149 | const struct halide_device_interface_t *device_interface); 150 | int (*copy_to_host)(void *user_context, struct halide_buffer_t *buf); 151 | int (*copy_to_device)(void *user_context, struct halide_buffer_t *buf, 152 | const struct halide_device_interface_t *device_interface); 153 | int (*device_and_host_malloc)(void *user_context, struct halide_buffer_t *buf, 154 | const struct halide_device_interface_t *device_interface); 155 | int (*device_and_host_free)(void *user_context, struct halide_buffer_t *buf); 156 | int (*buffer_copy)(void *user_context, struct halide_buffer_t *src, 157 | const struct halide_device_interface_t *dst_device_interface, struct halide_buffer_t *dst); 158 | int (*device_crop)(void *user_context, const struct halide_buffer_t *src, 159 | struct halide_buffer_t *dst); 160 | int (*device_release_crop)(void *user_context, struct halide_buffer_t *buf); 161 | int (*wrap_native)(void *user_context, struct halide_buffer_t *buf, uint64_t handle, 162 | const struct halide_device_interface_t *device_interface); 163 | int (*detach_native)(void *user_context, struct halide_buffer_t *buf); 164 | const struct halide_device_interface_impl_t *impl; 165 | }; 166 | 167 | typedef struct halide_dimension_t { 168 | int32_t min, extent, stride; 169 | 170 | // Per-dimension flags. None are defined yet (This is reserved for future use). 171 | uint32_t flags; 172 | 173 | #ifdef __cplusplus 174 | HALIDE_ALWAYS_INLINE halide_dimension_t() : min(0), extent(0), stride(0), flags(0) {} 175 | HALIDE_ALWAYS_INLINE halide_dimension_t(int32_t m, int32_t e, int32_t s, uint32_t f = 0) : 176 | min(m), extent(e), stride(s), flags(f) {} 177 | 178 | HALIDE_ALWAYS_INLINE bool operator==(const halide_dimension_t &other) const { 179 | return (min == other.min) && 180 | (extent == other.extent) && 181 | (stride == other.stride) && 182 | (flags == other.flags); 183 | } 184 | 185 | HALIDE_ALWAYS_INLINE bool operator!=(const halide_dimension_t &other) const { 186 | return !(*this == other); 187 | } 188 | #endif 189 | } halide_dimension_t; 190 | 191 | #ifdef __cplusplus 192 | } // extern "C" 193 | #endif 194 | 195 | typedef enum {halide_buffer_flag_host_dirty = 1, 196 | halide_buffer_flag_device_dirty = 2} halide_buffer_flags; 197 | 198 | /** 199 | * The raw representation of an image passed around by generated 200 | * Halide code. It includes some stuff to track whether the image is 201 | * not actually in main memory, but instead on a device (like a 202 | * GPU). For a more convenient C++ wrapper, use Halide::Buffer. */ 203 | typedef struct halide_buffer_t { 204 | /** A device-handle for e.g. GPU memory used to back this buffer. */ 205 | uint64_t device; 206 | 207 | /** The interface used to interpret the above handle. */ 208 | const struct halide_device_interface_t *device_interface; 209 | 210 | /** A pointer to the start of the data in main memory. In terms of 211 | * the Halide coordinate system, this is the address of the min 212 | * coordinates (defined below). */ 213 | uint8_t* host; 214 | 215 | /** flags with various meanings. */ 216 | uint64_t flags; 217 | 218 | /** The type of each buffer element. */ 219 | struct halide_type_t type; 220 | 221 | /** The dimensionality of the buffer. */ 222 | int32_t dimensions; 223 | 224 | /** The shape of the buffer. Halide does not own this array - you 225 | * must manage the memory for it yourself. */ 226 | halide_dimension_t *dim; 227 | 228 | /** Pads the buffer up to a multiple of 8 bytes */ 229 | void *padding; 230 | } halide_buffer_t; 231 | 232 | 233 | #ifdef __cplusplus 234 | 235 | namespace { 236 | template struct check_is_pointer; 237 | template struct check_is_pointer {}; 238 | } 239 | 240 | /** Construct the halide equivalent of a C type */ 241 | template 242 | HALIDE_ALWAYS_INLINE halide_type_t halide_type_of() { 243 | // Create a compile-time error if T is not a pointer (without 244 | // using any includes - this code goes into the runtime). 245 | check_is_pointer check; 246 | (void)check; 247 | return halide_type_t(halide_type_handle, 64); 248 | } 249 | 250 | template<> 251 | HALIDE_ALWAYS_INLINE halide_type_t halide_type_of() { 252 | return halide_type_t(halide_type_float, 32); 253 | } 254 | 255 | template<> 256 | HALIDE_ALWAYS_INLINE halide_type_t halide_type_of() { 257 | return halide_type_t(halide_type_float, 64); 258 | } 259 | 260 | template<> 261 | HALIDE_ALWAYS_INLINE halide_type_t halide_type_of() { 262 | return halide_type_t(halide_type_uint, 1); 263 | } 264 | 265 | template<> 266 | HALIDE_ALWAYS_INLINE halide_type_t halide_type_of() { 267 | return halide_type_t(halide_type_uint, 8); 268 | } 269 | 270 | template<> 271 | HALIDE_ALWAYS_INLINE halide_type_t halide_type_of() { 272 | return halide_type_t(halide_type_uint, 16); 273 | } 274 | 275 | template<> 276 | HALIDE_ALWAYS_INLINE halide_type_t halide_type_of() { 277 | return halide_type_t(halide_type_uint, 32); 278 | } 279 | 280 | template<> 281 | HALIDE_ALWAYS_INLINE halide_type_t halide_type_of() { 282 | return halide_type_t(halide_type_uint, 64); 283 | } 284 | 285 | template<> 286 | HALIDE_ALWAYS_INLINE halide_type_t halide_type_of() { 287 | return halide_type_t(halide_type_int, 8); 288 | } 289 | 290 | template<> 291 | HALIDE_ALWAYS_INLINE halide_type_t halide_type_of() { 292 | return halide_type_t(halide_type_int, 16); 293 | } 294 | 295 | template<> 296 | HALIDE_ALWAYS_INLINE halide_type_t halide_type_of() { 297 | return halide_type_t(halide_type_int, 32); 298 | } 299 | 300 | template<> 301 | HALIDE_ALWAYS_INLINE halide_type_t halide_type_of() { 302 | return halide_type_t(halide_type_int, 64); 303 | } 304 | 305 | #endif 306 | 307 | #endif // HALIDE_HALIDERUNTIME_H 308 | -------------------------------------------------------------------------------- /app/src/main/cpp/include/ImageProcess.hpp: -------------------------------------------------------------------------------- 1 | // 2 | // ImageProcess.hpp 3 | // MNN 4 | // 5 | // Created by MNN on 2018/09/19. 6 | // Copyright © 2018, Alibaba Group Holding Limited 7 | // 8 | 9 | #ifndef ImageProcess_hpp 10 | #define ImageProcess_hpp 11 | 12 | #include "ErrorCode.hpp" 13 | #include "Matrix.h" 14 | #include "Tensor.hpp" 15 | 16 | namespace MNN { 17 | namespace CV { 18 | enum ImageFormat { 19 | RGBA = 0, 20 | RGB, 21 | BGR, 22 | GRAY, 23 | BGRA, 24 | YUV_NV21 = 11, 25 | }; 26 | 27 | enum Filter { NEAREST = 0, BILINEAR = 1, BICUBIC = 2 }; 28 | 29 | enum Wrap { CLAMP_TO_EDGE = 0, ZERO = 1, REPEAT = 2 }; 30 | 31 | /** 32 | * handle image process for tensor. 33 | * step: 34 | * 1: Do transform compute and get points 35 | * 2: Sample line and do format convert 36 | * 3: Turn RGBA to float tensor, and do sub and normalize 37 | */ 38 | class MNN_PUBLIC ImageProcess { 39 | public: 40 | struct Inside; 41 | struct Config { 42 | /** data filter */ 43 | Filter filterType = NEAREST; 44 | /** format of source data */ 45 | ImageFormat sourceFormat = RGBA; 46 | /** format of destination data */ 47 | ImageFormat destFormat = RGBA; 48 | 49 | // Only valid if the dest type is float 50 | float mean[4] = {0.0f, 0.0f, 0.0f, 0.0f}; 51 | float normal[4] = {1.0f, 1.0f, 1.0f, 1.0f}; 52 | 53 | /** edge wrapper */ 54 | Wrap wrap = CLAMP_TO_EDGE; 55 | }; 56 | 57 | public: 58 | /** 59 | * @brief create image process with given config for given tensor. 60 | * @param config given config. 61 | * @param dstTensor given tensor. 62 | * @return image processor. 63 | */ 64 | static ImageProcess* create(const Config& config, const Tensor* dstTensor = nullptr); 65 | 66 | /** 67 | * @brief create image process with given config for given tensor. 68 | * @param means given means 69 | * @param meanCount given means count 70 | * @param normals given normals 71 | * @param normalCount given normal count 72 | * @param sourceFormat format of source data 73 | * @param destFormat format of destination data 74 | * @param dstTensor given tensor. 75 | * @return image processor. 76 | */ 77 | static ImageProcess* create(const ImageFormat sourceFormat = RGBA, const ImageFormat destFormat = RGBA, 78 | const float* means = nullptr, const int meanCount = 0, const float* normals = nullptr, 79 | const int normalCount = 0, const Tensor* dstTensor = nullptr); 80 | 81 | ~ImageProcess(); 82 | 83 | /** 84 | * @brief get affine transform matrix. 85 | * @return affine transform matrix. 86 | */ 87 | inline const Matrix& matrix() const { 88 | return mTransform; 89 | } 90 | void setMatrix(const Matrix& matrix); 91 | 92 | /** 93 | * @brief convert source data to given tensor. 94 | * @param source source data. 95 | * @param iw source width. 96 | * @param ih source height. 97 | * @param stride number of elements per row. eg: 100 width RGB contains at least 300 elements. 98 | * @param dest given tensor. 99 | * @return result code. 100 | */ 101 | ErrorCode convert(const uint8_t* source, int iw, int ih, int stride, Tensor* dest); 102 | 103 | /** 104 | * @brief convert source data to given tensor. 105 | * @param source source data. 106 | * @param iw source width. 107 | * @param ih source height. 108 | * @param stride number of elements per row. eg: 100 width RGB contains at least 300 elements. 109 | * @param dest dest data. 110 | * @param ow output width. 111 | * @param oh output height. 112 | * @param outputBpp output bpp, if 0, set as the save and config.destFormat. 113 | * @param outputStride output stride, if 0, set as ow * outputBpp. 114 | * @param type Only support halide_type_of and halide_type_of. 115 | * @return result code. 116 | */ 117 | ErrorCode convert(const uint8_t* source, int iw, int ih, int stride, void* dest, int ow, int oh, int outputBpp = 0, 118 | int outputStride = 0, halide_type_t type = halide_type_of()); 119 | 120 | /** 121 | * @brief create tensor with given data. 122 | * @param w image width. 123 | * @param h image height. 124 | * @param bpp bytes per pixel. 125 | * @param p pixel data pointer. 126 | * @return created tensor. 127 | */ 128 | template 129 | static Tensor* createImageTensor(int w, int h, int bpp, void* p = nullptr) { 130 | return createImageTensor(halide_type_of(), w, h, bpp, p); 131 | } 132 | static Tensor* createImageTensor(halide_type_t type, int w, int h, int bpp, void* p = nullptr); 133 | 134 | private: 135 | ImageProcess(const Config& config); 136 | Matrix mTransform; 137 | Matrix mTransformInvert; 138 | Inside* mInside; 139 | }; 140 | } // namespace CV 141 | } // namespace MNN 142 | 143 | #endif /* ImageProcess_hpp */ 144 | -------------------------------------------------------------------------------- /app/src/main/cpp/include/Interpreter.hpp: -------------------------------------------------------------------------------- 1 | // 2 | // Interpreter.hpp 3 | // MNN 4 | // 5 | // Created by MNN on 2018/07/23. 6 | // Copyright © 2018, Alibaba Group Holding Limited 7 | // 8 | 9 | #ifndef Interpreter_hpp 10 | #define Interpreter_hpp 11 | 12 | #include 13 | #include 14 | #include 15 | #include "ErrorCode.hpp" 16 | #include "MNNForwardType.h" 17 | #include "Tensor.hpp" 18 | 19 | namespace MNN { 20 | 21 | /** session schedule config */ 22 | struct ScheduleConfig { 23 | /** which tensor should be kept */ 24 | std::vector saveTensors; 25 | /** forward type */ 26 | MNNForwardType type = MNN_FORWARD_CPU; 27 | /** number of threads in parallel */ 28 | int numThread = 4; 29 | 30 | /** subpath to run */ 31 | struct Path { 32 | std::vector inputs; 33 | std::vector outputs; 34 | 35 | enum Mode { 36 | /** 37 | * Op Mode 38 | * - inputs means the source op, can NOT be empty. 39 | * - outputs means the sink op, can be empty. 40 | * The path will start from source op, then flow when encounter the sink op. 41 | * The sink op will not be compute in this path. 42 | */ 43 | Op = 0, 44 | 45 | /** 46 | * Tensor Mode (NOT supported yet) 47 | * - inputs means the inputs tensors, can NOT be empty. 48 | * - outputs means the outputs tensors, can NOT be empty. 49 | * It will find the pipeline that compute outputs from inputs. 50 | */ 51 | Tensor = 1 52 | }; 53 | 54 | /** running mode */ 55 | Mode mode = Op; 56 | }; 57 | Path path; 58 | 59 | /** backup backend used to create execution when desinated backend do NOT support any op */ 60 | MNNForwardType backupType = MNN_FORWARD_CPU; 61 | 62 | /** extra backend config */ 63 | BackendConfig* backendConfig = nullptr; 64 | }; 65 | 66 | class Session; 67 | struct Content; 68 | class Tensor; 69 | class Backend; 70 | 71 | class MNN_PUBLIC OperatorInfo { 72 | struct Info; 73 | 74 | public: 75 | /** Operator's name*/ 76 | const std::string& name() const; 77 | 78 | /** Operator's type*/ 79 | const std::string& type() const; 80 | 81 | /** Operator's flops, in M*/ 82 | float flops() const; 83 | 84 | protected: 85 | OperatorInfo(); 86 | ~OperatorInfo(); 87 | Info* mContent; 88 | }; 89 | 90 | typedef std::function&, const std::string& /*opName*/)> TensorCallBack; 91 | typedef std::function&, const OperatorInfo*)> TensorCallBackWithInfo; 92 | 93 | /** net data holder. multiple sessions could share same net. */ 94 | class MNN_PUBLIC Interpreter { 95 | public: 96 | /** 97 | * @brief create net from file. 98 | * @param file given file. 99 | * @return created net if success, NULL otherwise. 100 | */ 101 | static Interpreter* createFromFile(const char* file); 102 | /** 103 | * @brief create net from buffer. 104 | * @param buffer given data buffer. 105 | * @param size size of data buffer. 106 | * @return created net if success, NULL otherwise. 107 | */ 108 | static Interpreter* createFromBuffer(const void* buffer, size_t size); 109 | ~Interpreter(); 110 | 111 | public: 112 | /** 113 | * @brief create session with schedule config. created session will be managed in net. 114 | * @param config session schedule config. 115 | * @return created session if success, NULL otherwise. 116 | */ 117 | Session* createSession(const ScheduleConfig& config); 118 | 119 | /** 120 | * @brief create multi-path session with schedule configs. created session will be managed in net. 121 | * @param configs session schedule configs. 122 | * @return created session if success, NULL otherwise. 123 | */ 124 | Session* createMultiPathSession(const std::vector& configs); 125 | 126 | /** 127 | * @brief release session. 128 | * @param session given session. 129 | * @return true if given session is held by net and is freed. 130 | */ 131 | bool releaseSession(Session* session); 132 | 133 | /** 134 | * @brief call this function to get tensors ready. output tensor buffer (host or deviceId) should be retrieved 135 | * after resize of any input tensor. 136 | * @param session given session. 137 | */ 138 | void resizeSession(Session* session); 139 | 140 | /** 141 | * @brief call this function if don't need resize or create session any more, it will save a few memory that equal 142 | * to the size of model buffer 143 | */ 144 | void releaseModel(); 145 | 146 | /** 147 | * @brief Get the model buffer for user to save 148 | * @return std::make_pair(modleBuffer, modelSize). 149 | * @example: 150 | * std::ofstream output("trainResult.alinn") 151 | * auto buffer = net->getModelBuffer(); 152 | * output.write((const char*)buffer.first, buffer.second); 153 | */ 154 | std::pair getModelBuffer() const; 155 | 156 | /** 157 | * @brief update Session's Tensor to model's Const Op 158 | * @param session given session. 159 | * @return result of running. 160 | */ 161 | ErrorCode updateSessionToModel(Session* session); 162 | 163 | /** 164 | * @brief run session. 165 | * @param session given session. 166 | * @return result of running. 167 | */ 168 | ErrorCode runSession(Session* session) const; 169 | 170 | /* 171 | * @brief run session. 172 | * @param session given session. 173 | * @param before callback before each op. return true to run the op; return false to skip the op. 174 | * @param after callback after each op. return true to continue running; return false to interrupt the session. 175 | * @param sync synchronously wait for finish of execution or not. 176 | * @return result of running. 177 | */ 178 | ErrorCode runSessionWithCallBack(const Session* session, const TensorCallBack& before, const TensorCallBack& end, 179 | bool sync = false) const; 180 | 181 | /* 182 | * @brief run session. 183 | * @param session given session. 184 | * @param before callback before each op. return true to run the op; return false to skip the op. 185 | * @param after callback after each op. return true to continue running; return false to interrupt the session. 186 | * @param sync synchronously wait for finish of execution or not. 187 | * @return result of running. 188 | */ 189 | ErrorCode runSessionWithCallBackInfo(const Session* session, const TensorCallBackWithInfo& before, 190 | const TensorCallBackWithInfo& end, bool sync = false) const; 191 | 192 | /** 193 | * @brief get input tensor for given name. 194 | * @param session given session. 195 | * @param name given name. if NULL, return first input. 196 | * @return tensor if found, NULL otherwise. 197 | */ 198 | Tensor* getSessionInput(const Session* session, const char* name); 199 | /** 200 | * @brief get output tensor for given name. 201 | * @param session given session. 202 | * @param name given name. if NULL, return first output. 203 | * @return tensor if found, NULL otherwise. 204 | */ 205 | Tensor* getSessionOutput(const Session* session, const char* name); 206 | 207 | /** 208 | * @brief get all input tensors. 209 | * @param session given session. 210 | * @return all input tensors mapped with name. 211 | */ 212 | const std::map& getSessionOutputAll(const Session* session) const; 213 | /** 214 | * @brief get all output tensors. 215 | * @param session given session. 216 | * @return all output tensors mapped with name. 217 | */ 218 | const std::map& getSessionInputAll(const Session* session) const; 219 | 220 | public: 221 | /** 222 | * @brief resize given tensor. 223 | * @param tensor given tensor. 224 | * @param dims new dims. at most 6 dims. 225 | */ 226 | void resizeTensor(Tensor* tensor, const std::vector& dims); 227 | 228 | /** 229 | * @brief resize given tensor by nchw. 230 | * @param batch / N. 231 | * @param channel / C. 232 | * @param height / H. 233 | * @param width / W 234 | */ 235 | void resizeTensor(Tensor* tensor, int batch, int channel, int height, int width); 236 | 237 | /** 238 | * @brief get backend used to create given tensor. 239 | * @param session given session. 240 | * @param tensor given tensor. 241 | * @return backend used to create given tensor, may be NULL. 242 | */ 243 | const Backend* getBackend(const Session* session, const Tensor* tensor) const; 244 | 245 | /** 246 | * @brief get business code (model identifier). 247 | * @return business code. 248 | */ 249 | const char* bizCode() const; 250 | 251 | private: 252 | static Interpreter* createFromBufferInternal(Content* net); 253 | 254 | Content* mNet = nullptr; 255 | Interpreter(Content* net); 256 | 257 | Interpreter(const Interpreter&) = delete; 258 | Interpreter(const Interpreter&&) = delete; 259 | Interpreter& operator=(const Interpreter&) = delete; 260 | Interpreter& operator=(const Interpreter&&) = delete; 261 | }; 262 | } // namespace MNN 263 | 264 | #endif /* Interpreter_hpp */ 265 | -------------------------------------------------------------------------------- /app/src/main/cpp/include/MNNDefine.h: -------------------------------------------------------------------------------- 1 | // 2 | // MNNDefine.h 3 | // MNN 4 | // 5 | // Created by MNN on 2018/08/09. 6 | // Copyright © 2018, Alibaba Group Holding Limited 7 | // 8 | 9 | #ifndef MNNDefine_h 10 | #define MNNDefine_h 11 | 12 | #include 13 | #include 14 | 15 | #if defined(__APPLE__) 16 | #include "TargetConditionals.h" 17 | #if TARGET_OS_IPHONE 18 | #define MNN_BUILD_FOR_IOS 19 | #endif 20 | #endif 21 | 22 | #ifdef MNN_USE_LOGCAT 23 | #include 24 | #define MNN_ERROR(format, ...) __android_log_print(ANDROID_LOG_ERROR, "MNNJNI", format, ##__VA_ARGS__) 25 | #define MNN_PRINT(format, ...) __android_log_print(ANDROID_LOG_INFO, "MNNJNI", format, ##__VA_ARGS__) 26 | #else 27 | #define MNN_PRINT(format, ...) printf(format, ##__VA_ARGS__) 28 | #define MNN_ERROR(format, ...) printf(format, ##__VA_ARGS__) 29 | #endif 30 | 31 | #ifdef DEBUG 32 | #define MNN_ASSERT(x) \ 33 | { \ 34 | int res = (x); \ 35 | if (!res) { \ 36 | MNN_ERROR("Error for %s, %d\n", __FILE__, __LINE__); \ 37 | assert(res); \ 38 | } \ 39 | } 40 | #else 41 | #define MNN_ASSERT(x) \ 42 | { \ 43 | int res = (x); \ 44 | if (!res) { \ 45 | MNN_ERROR("Error for %s, %d\n", __FILE__, __LINE__); \ 46 | } \ 47 | } 48 | #endif 49 | 50 | #define FUNC_PRINT(x) MNN_PRINT(#x "=%d in %s, %d \n", x, __func__, __LINE__); 51 | #define FUNC_PRINT_ALL(x, type) MNN_PRINT(#x "=" #type " %" #type " in %s, %d \n", x, __func__, __LINE__); 52 | 53 | #define MNN_CHECK(success, log) \ 54 | if(!(success)){ \ 55 | MNN_ERROR("Check failed: %s ==> %s\n", #success, #log); \ 56 | } 57 | 58 | #if defined(_MSC_VER) 59 | #if defined(BUILDING_MNN_DLL) 60 | #define MNN_PUBLIC __declspec(dllexport) 61 | #elif defined(USING_MNN_DLL) 62 | #define MNN_PUBLIC __declspec(dllimport) 63 | #else 64 | #define MNN_PUBLIC 65 | #endif 66 | #else 67 | #define MNN_PUBLIC __attribute__((visibility("default"))) 68 | #endif 69 | 70 | #endif /* MNNDefine_h */ 71 | -------------------------------------------------------------------------------- /app/src/main/cpp/include/MNNForwardType.h: -------------------------------------------------------------------------------- 1 | // 2 | // MNNForwardType.h 3 | // MNN 4 | // 5 | // Created by MNN on 2019/01/19. 6 | // Copyright © 2018, Alibaba Group Holding Limited 7 | // 8 | 9 | #ifndef MNNForwardType_h 10 | #define MNNForwardType_h 11 | #include 12 | #include 13 | 14 | typedef enum { 15 | MNN_FORWARD_CPU = 0, 16 | 17 | /* 18 | Firtly find the first available backends not equal to CPU 19 | If no other backends, use cpu 20 | */ 21 | MNN_FORWARD_AUTO = 4, 22 | 23 | /*Hand write metal*/ 24 | MNN_FORWARD_METAL = 1, 25 | 26 | /*Use IOS's MPS instead of hand-write metal, Not Support yet*/ 27 | MNN_FORWARD_MPS = 2, 28 | 29 | /*Android / Common Device GPU API*/ 30 | MNN_FORWARD_OPENCL = 3, 31 | MNN_FORWARD_OPENGL = 6, 32 | MNN_FORWARD_VULKAN = 7, 33 | 34 | /*Android 8.1's NNAPI, Not Support yet*/ 35 | MNN_FORWARD_NN = 5, 36 | 37 | /*User can use API from Backend.hpp to add or search Backend*/ 38 | MNN_FORWARD_USER_0 = 8, 39 | MNN_FORWARD_USER_1 = 9, 40 | MNN_FORWARD_USER_2 = 10, 41 | MNN_FORWARD_USER_3 = 11, 42 | 43 | MNN_FORWARD_ALL 44 | } MNNForwardType; 45 | #ifdef __cplusplus 46 | namespace MNN { 47 | struct BackendConfig { 48 | enum MemoryMode { Memory_Normal = 0, Memory_High, Memory_Low }; 49 | 50 | MemoryMode memory = Memory_Normal; 51 | 52 | enum PowerMode { Power_Normal = 0, Power_High, Power_Low }; 53 | 54 | PowerMode power = Power_Normal; 55 | 56 | enum PrecisionMode { Precision_Normal = 0, Precision_High, Precision_Low }; 57 | 58 | PrecisionMode precision = Precision_Normal; 59 | 60 | /** user defined context */ 61 | union { 62 | void* sharedContext = nullptr; 63 | size_t flags; // Valid for CPU Backend 64 | }; 65 | }; 66 | }; // namespace MNN 67 | #endif 68 | #endif /* MNNForwardType_h */ 69 | -------------------------------------------------------------------------------- /app/src/main/cpp/include/MNNSharedContext.h: -------------------------------------------------------------------------------- 1 | // 2 | // MNNSharedContext.h 3 | // MNN 4 | // 5 | // Created by MNN on 2018/10/11. 6 | // Copyright © 2018, Alibaba Group Holding Limited 7 | // 8 | 9 | #ifndef MNNSharedContext_h 10 | #define MNNSharedContext_h 11 | #ifdef __cplusplus 12 | extern "C" { 13 | #endif 14 | 15 | #include /*uint32_t*/ 16 | 17 | #ifndef VK_DEFINE_HANDLE 18 | #define VK_DEFINE_HANDLE(object) typedef struct object##_T* object; 19 | VK_DEFINE_HANDLE(VkInstance) 20 | VK_DEFINE_HANDLE(VkPhysicalDevice) 21 | VK_DEFINE_HANDLE(VkDevice) 22 | VK_DEFINE_HANDLE(VkQueue) 23 | #endif 24 | struct MNNVulkanContext { 25 | VkInstance pInstance; 26 | VkPhysicalDevice pPhysicalDevice; 27 | VkDevice pDevice; 28 | VkQueue pQueue; 29 | uint32_t iQueueFamilyIndex; 30 | }; 31 | #ifdef __cplusplus 32 | } 33 | #endif 34 | 35 | #endif /* MNNSharedContext_h */ 36 | -------------------------------------------------------------------------------- /app/src/main/cpp/include/Matrix.h: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright 2006 The Android Open Source Project 3 | * 4 | * Use of this source code is governed by a BSD-style license that can be 5 | * found in the LICENSE file. 6 | */ 7 | 8 | /* Generated by tools/bookmaker from include/core/Matrix.h and docs/SkMatrix_Reference.bmh 9 | on 2018-07-13 08:15:11. Additional documentation and examples can be found at: 10 | https://skia.org/user/api/SkMatrix_Reference 11 | 12 | You may edit either file directly. Structural changes to public interfaces require 13 | editing both files. After editing docs/SkMatrix_Reference.bmh, run: 14 | bookmaker -b docs -i include/core/Matrix.h -p 15 | to create an updated version of this file. 16 | */ 17 | 18 | 19 | // 20 | // Modified by jiangxiaotang on 2018/09/19. 21 | // Copyright © 2018, Alibaba Group Holding Limited 22 | // 23 | 24 | #ifndef SkMatrix_DEFINED 25 | #define SkMatrix_DEFINED 26 | 27 | #include 28 | #include 29 | #include "Rect.h" 30 | 31 | namespace MNN { 32 | namespace CV { 33 | 34 | /** \class Matrix 35 | Matrix holds a 3x3 matrix for transforming coordinates. This allows mapping 36 | Point and vectors with translation, scaling, skewing, rotation, and 37 | perspective. 38 | 39 | Matrix elements are in row major order. Matrix does not have a constructor, 40 | so it must be explicitly initialized. setIdentity() initializes Matrix 41 | so it has no effect. setTranslate(), setScale(), setSkew(), setRotate(), set9 and setAll() 42 | initializes all Matrix elements with the corresponding mapping. 43 | 44 | Matrix includes a hidden variable that classifies the type of matrix to 45 | improve performance. Matrix is not thread safe unless getType() is called first. 46 | */ 47 | 48 | class MNN_PUBLIC Matrix { 49 | public: 50 | Matrix() { 51 | setIdentity(); 52 | } 53 | 54 | /** Sets Matrix to scale by (sx, sy). Returned matrix is: 55 | 56 | | sx 0 0 | 57 | | 0 sy 0 | 58 | | 0 0 1 | 59 | 60 | @param sx horizontal scale factor 61 | @param sy vertical scale factor 62 | @return Matrix with scale 63 | */ 64 | static Matrix MakeScale(float sx, float sy) { 65 | Matrix m; 66 | m.setScale(sx, sy); 67 | return m; 68 | } 69 | 70 | /** Sets Matrix to scale by (scale, scale). Returned matrix is: 71 | 72 | | scale 0 0 | 73 | | 0 scale 0 | 74 | | 0 0 1 | 75 | 76 | @param scale horizontal and vertical scale factor 77 | @return Matrix with scale 78 | */ 79 | static Matrix MakeScale(float scale) { 80 | Matrix m; 81 | m.setScale(scale, scale); 82 | return m; 83 | } 84 | 85 | /** Sets Matrix to translate by (dx, dy). Returned matrix is: 86 | 87 | | 1 0 dx | 88 | | 0 1 dy | 89 | | 0 0 1 | 90 | 91 | @param dx horizontal translation 92 | @param dy vertical translation 93 | @return Matrix with translation 94 | */ 95 | static Matrix MakeTrans(float dx, float dy) { 96 | Matrix m; 97 | m.setTranslate(dx, dy); 98 | return m; 99 | } 100 | 101 | /** Sets Matrix to: 102 | 103 | | scaleX skewX transX | 104 | | skewY scaleY transY | 105 | | pers0 pers1 pers2 | 106 | 107 | @param scaleX horizontal scale factor 108 | @param skewX horizontal skew factor 109 | @param transX horizontal translation 110 | @param skewY vertical skew factor 111 | @param scaleY vertical scale factor 112 | @param transY vertical translation 113 | @param pers0 input x-axis perspective factor 114 | @param pers1 input y-axis perspective factor 115 | @param pers2 perspective scale factor 116 | @return Matrix constructed from parameters 117 | */ 118 | static Matrix MakeAll(float scaleX, float skewX, float transX, float skewY, float scaleY, float transY, float pers0, 119 | float pers1, float pers2) { 120 | Matrix m; 121 | m.setAll(scaleX, skewX, transX, skewY, scaleY, transY, pers0, pers1, pers2); 122 | return m; 123 | } 124 | 125 | /** \enum Matrix::TypeMask 126 | Enum of bit fields for mask returned by getType(). 127 | Used to identify the complexity of Matrix, to optimize performance. 128 | */ 129 | enum TypeMask { 130 | kIdentity_Mask = 0, //!< identity Matrix; all bits clear 131 | kTranslate_Mask = 0x01, //!< translation Matrix 132 | kScale_Mask = 0x02, //!< scale Matrix 133 | kAffine_Mask = 0x04, //!< skew or rotate Matrix 134 | kPerspective_Mask = 0x08, //!< perspective Matrix 135 | }; 136 | 137 | /** Returns a bit field describing the transformations the matrix may 138 | perform. The bit field is computed conservatively, so it may include 139 | false positives. For example, when kPerspective_Mask is set, all 140 | other bits are set. 141 | 142 | @return kIdentity_Mask, or combinations of: kTranslate_Mask, kScale_Mask, 143 | kAffine_Mask, kPerspective_Mask 144 | */ 145 | TypeMask getType() const { 146 | if (fTypeMask & kUnknown_Mask) { 147 | fTypeMask = this->computeTypeMask(); 148 | } 149 | // only return the public masks 150 | return (TypeMask)(fTypeMask & 0xF); 151 | } 152 | 153 | /** Returns true if Matrix is identity. Identity matrix is: 154 | 155 | | 1 0 0 | 156 | | 0 1 0 | 157 | | 0 0 1 | 158 | 159 | @return true if Matrix has no effect 160 | */ 161 | bool isIdentity() const { 162 | return this->getType() == 0; 163 | } 164 | 165 | /** Returns true if Matrix at most scales and translates. Matrix may be identity, 166 | contain only scale elements, only translate elements, or both. Matrix form is: 167 | 168 | | scale-x 0 translate-x | 169 | | 0 scale-y translate-y | 170 | | 0 0 1 | 171 | 172 | @return true if Matrix is identity; or scales, translates, or both 173 | */ 174 | bool isScaleTranslate() const { 175 | return !(this->getType() & ~(kScale_Mask | kTranslate_Mask)); 176 | } 177 | 178 | /** Returns true if Matrix is identity, or translates. Matrix form is: 179 | 180 | | 1 0 translate-x | 181 | | 0 1 translate-y | 182 | | 0 0 1 | 183 | 184 | @return true if Matrix is identity, or translates 185 | */ 186 | bool isTranslate() const { 187 | return !(this->getType() & ~(kTranslate_Mask)); 188 | } 189 | 190 | /** Returns true Matrix maps Rect to another Rect. If true, Matrix is identity, 191 | or scales, or rotates a multiple of 90 degrees, or mirrors on axes. In all 192 | cases, Matrix may also have translation. Matrix form is either: 193 | 194 | | scale-x 0 translate-x | 195 | | 0 scale-y translate-y | 196 | | 0 0 1 | 197 | 198 | or 199 | 200 | | 0 rotate-x translate-x | 201 | | rotate-y 0 translate-y | 202 | | 0 0 1 | 203 | 204 | for non-zero values of scale-x, scale-y, rotate-x, and rotate-y. 205 | 206 | Also called preservesAxisAlignment(); use the one that provides better inline 207 | documentation. 208 | 209 | @return true if Matrix maps one Rect into another 210 | */ 211 | bool rectStaysRect() const { 212 | if (fTypeMask & kUnknown_Mask) { 213 | fTypeMask = this->computeTypeMask(); 214 | } 215 | return (fTypeMask & kRectStaysRect_Mask) != 0; 216 | } 217 | 218 | /** Returns true Matrix maps Rect to another Rect. If true, Matrix is identity, 219 | or scales, or rotates a multiple of 90 degrees, or mirrors on axes. In all 220 | cases, Matrix may also have translation. Matrix form is either: 221 | 222 | | scale-x 0 translate-x | 223 | | 0 scale-y translate-y | 224 | | 0 0 1 | 225 | 226 | or 227 | 228 | | 0 rotate-x translate-x | 229 | | rotate-y 0 translate-y | 230 | | 0 0 1 | 231 | 232 | for non-zero values of scale-x, scale-y, rotate-x, and rotate-y. 233 | 234 | Also called rectStaysRect(); use the one that provides better inline 235 | documentation. 236 | 237 | @return true if Matrix maps one Rect into another 238 | */ 239 | bool preservesAxisAlignment() const { 240 | return this->rectStaysRect(); 241 | } 242 | 243 | /** Matrix organizes its values in row order. These members correspond to 244 | each value in Matrix. 245 | */ 246 | static constexpr int kMScaleX = 0; //!< horizontal scale factor 247 | static constexpr int kMSkewX = 1; //!< horizontal skew factor 248 | static constexpr int kMTransX = 2; //!< horizontal translation 249 | static constexpr int kMSkewY = 3; //!< vertical skew factor 250 | static constexpr int kMScaleY = 4; //!< vertical scale factor 251 | static constexpr int kMTransY = 5; //!< vertical translation 252 | static constexpr int kMPersp0 = 6; //!< input x perspective factor 253 | static constexpr int kMPersp1 = 7; //!< input y perspective factor 254 | static constexpr int kMPersp2 = 8; //!< perspective bias 255 | 256 | /** Affine arrays are in column major order to match the matrix used by 257 | PDF and XPS. 258 | */ 259 | static constexpr int kAScaleX = 0; //!< horizontal scale factor 260 | static constexpr int kASkewY = 1; //!< vertical skew factor 261 | static constexpr int kASkewX = 2; //!< horizontal skew factor 262 | static constexpr int kAScaleY = 3; //!< vertical scale factor 263 | static constexpr int kATransX = 4; //!< horizontal translation 264 | static constexpr int kATransY = 5; //!< vertical translation 265 | 266 | /** Returns one matrix value. Asserts if index is out of range and SK_DEBUG is 267 | defined. 268 | 269 | @param index one of: kMScaleX, kMSkewX, kMTransX, kMSkewY, kMScaleY, kMTransY, 270 | kMPersp0, kMPersp1, kMPersp2 271 | @return value corresponding to index 272 | */ 273 | float operator[](int index) const { 274 | MNN_ASSERT((unsigned)index < 9); 275 | return fMat[index]; 276 | } 277 | 278 | /** Returns one matrix value. Asserts if index is out of range and SK_DEBUG is 279 | defined. 280 | 281 | @param index one of: kMScaleX, kMSkewX, kMTransX, kMSkewY, kMScaleY, kMTransY, 282 | kMPersp0, kMPersp1, kMPersp2 283 | @return value corresponding to index 284 | */ 285 | float get(int index) const { 286 | MNN_ASSERT((unsigned)index < 9); 287 | return fMat[index]; 288 | } 289 | 290 | /** Returns scale factor multiplied by x-axis input, contributing to x-axis output. 291 | With mapPoints(), scales Point along the x-axis. 292 | 293 | @return horizontal scale factor 294 | */ 295 | float getScaleX() const { 296 | return fMat[kMScaleX]; 297 | } 298 | 299 | /** Returns scale factor multiplied by y-axis input, contributing to y-axis output. 300 | With mapPoints(), scales Point along the y-axis. 301 | 302 | @return vertical scale factor 303 | */ 304 | float getScaleY() const { 305 | return fMat[kMScaleY]; 306 | } 307 | 308 | /** Returns scale factor multiplied by x-axis input, contributing to y-axis output. 309 | With mapPoints(), skews Point along the y-axis. 310 | Skewing both axes can rotate Point. 311 | 312 | @return vertical skew factor 313 | */ 314 | float getSkewY() const { 315 | return fMat[kMSkewY]; 316 | } 317 | 318 | /** Returns scale factor multiplied by y-axis input, contributing to x-axis output. 319 | With mapPoints(), skews Point along the x-axis. 320 | Skewing both axes can rotate Point. 321 | 322 | @return horizontal scale factor 323 | */ 324 | float getSkewX() const { 325 | return fMat[kMSkewX]; 326 | } 327 | 328 | /** Returns translation contributing to x-axis output. 329 | With mapPoints(), moves Point along the x-axis. 330 | 331 | @return horizontal translation factor 332 | */ 333 | float getTranslateX() const { 334 | return fMat[kMTransX]; 335 | } 336 | 337 | /** Returns translation contributing to y-axis output. 338 | With mapPoints(), moves Point along the y-axis. 339 | 340 | @return vertical translation factor 341 | */ 342 | float getTranslateY() const { 343 | return fMat[kMTransY]; 344 | } 345 | 346 | /** Returns factor scaling input x-axis relative to input y-axis. 347 | 348 | @return input x-axis perspective factor 349 | */ 350 | float getPerspX() const { 351 | return fMat[kMPersp0]; 352 | } 353 | 354 | /** Returns factor scaling input y-axis relative to input x-axis. 355 | 356 | @return input y-axis perspective factor 357 | */ 358 | float getPerspY() const { 359 | return fMat[kMPersp1]; 360 | } 361 | 362 | /** Returns writable Matrix value. Asserts if index is out of range and SK_DEBUG is 363 | defined. Clears internal cache anticipating that caller will change Matrix value. 364 | 365 | Next call to read Matrix state may recompute cache; subsequent writes to Matrix 366 | value must be followed by dirtyMatrixTypeCache(). 367 | 368 | @param index one of: kMScaleX, kMSkewX, kMTransX, kMSkewY, kMScaleY, kMTransY, 369 | kMPersp0, kMPersp1, kMPersp2 370 | @return writable value corresponding to index 371 | */ 372 | float& operator[](int index) { 373 | MNN_ASSERT((unsigned)index < 9); 374 | this->setTypeMask(kUnknown_Mask); 375 | return fMat[index]; 376 | } 377 | 378 | /** Sets Matrix value. Asserts if index is out of range and SK_DEBUG is 379 | defined. Safer than operator[]; internal cache is always maintained. 380 | 381 | @param index one of: kMScaleX, kMSkewX, kMTransX, kMSkewY, kMScaleY, kMTransY, 382 | kMPersp0, kMPersp1, kMPersp2 383 | @param value scalar to store in Matrix 384 | */ 385 | void set(int index, float value) { 386 | MNN_ASSERT((unsigned)index < 9); 387 | fMat[index] = value; 388 | this->setTypeMask(kUnknown_Mask); 389 | } 390 | 391 | /** Sets horizontal scale factor. 392 | 393 | @param v horizontal scale factor to store 394 | */ 395 | void setScaleX(float v) { 396 | this->set(kMScaleX, v); 397 | } 398 | 399 | /** Sets vertical scale factor. 400 | 401 | @param v vertical scale factor to store 402 | */ 403 | void setScaleY(float v) { 404 | this->set(kMScaleY, v); 405 | } 406 | 407 | /** Sets vertical skew factor. 408 | 409 | @param v vertical skew factor to store 410 | */ 411 | void setSkewY(float v) { 412 | this->set(kMSkewY, v); 413 | } 414 | 415 | /** Sets horizontal skew factor. 416 | 417 | @param v horizontal skew factor to store 418 | */ 419 | void setSkewX(float v) { 420 | this->set(kMSkewX, v); 421 | } 422 | 423 | /** Sets horizontal translation. 424 | 425 | @param v horizontal translation to store 426 | */ 427 | void setTranslateX(float v) { 428 | this->set(kMTransX, v); 429 | } 430 | 431 | /** Sets vertical translation. 432 | 433 | @param v vertical translation to store 434 | */ 435 | void setTranslateY(float v) { 436 | this->set(kMTransY, v); 437 | } 438 | 439 | /** Sets input x-axis perspective factor, which causes mapXY() to vary input x-axis values 440 | inversely proportional to input y-axis values. 441 | 442 | @param v perspective factor 443 | */ 444 | void setPerspX(float v) { 445 | this->set(kMPersp0, v); 446 | } 447 | 448 | /** Sets input y-axis perspective factor, which causes mapXY() to vary input y-axis values 449 | inversely proportional to input x-axis values. 450 | 451 | @param v perspective factor 452 | */ 453 | void setPerspY(float v) { 454 | this->set(kMPersp1, v); 455 | } 456 | 457 | /** Sets all values from parameters. Sets matrix to: 458 | 459 | | scaleX skewX transX | 460 | | skewY scaleY transY | 461 | | persp0 persp1 persp2 | 462 | 463 | @param scaleX horizontal scale factor to store 464 | @param skewX horizontal skew factor to store 465 | @param transX horizontal translation to store 466 | @param skewY vertical skew factor to store 467 | @param scaleY vertical scale factor to store 468 | @param transY vertical translation to store 469 | @param persp0 input x-axis values perspective factor to store 470 | @param persp1 input y-axis values perspective factor to store 471 | @param persp2 perspective scale factor to store 472 | */ 473 | void setAll(float scaleX, float skewX, float transX, float skewY, float scaleY, float transY, float persp0, 474 | float persp1, float persp2) { 475 | fMat[kMScaleX] = scaleX; 476 | fMat[kMSkewX] = skewX; 477 | fMat[kMTransX] = transX; 478 | fMat[kMSkewY] = skewY; 479 | fMat[kMScaleY] = scaleY; 480 | fMat[kMTransY] = transY; 481 | fMat[kMPersp0] = persp0; 482 | fMat[kMPersp1] = persp1; 483 | fMat[kMPersp2] = persp2; 484 | this->setTypeMask(kUnknown_Mask); 485 | } 486 | 487 | /** Copies nine scalar values contained by Matrix into buffer, in member value 488 | ascending order: kMScaleX, kMSkewX, kMTransX, kMSkewY, kMScaleY, kMTransY, 489 | kMPersp0, kMPersp1, kMPersp2. 490 | 491 | @param buffer storage for nine scalar values 492 | */ 493 | void get9(float buffer[9]) const { 494 | memcpy(buffer, fMat, 9 * sizeof(float)); 495 | } 496 | 497 | /** Sets Matrix to nine scalar values in buffer, in member value ascending order: 498 | kMScaleX, kMSkewX, kMTransX, kMSkewY, kMScaleY, kMTransY, kMPersp0, kMPersp1, 499 | kMPersp2. 500 | 501 | Sets matrix to: 502 | 503 | | buffer[0] buffer[1] buffer[2] | 504 | | buffer[3] buffer[4] buffer[5] | 505 | | buffer[6] buffer[7] buffer[8] | 506 | 507 | In the future, set9 followed by get9 may not return the same values. Since Matrix 508 | maps non-homogeneous coordinates, scaling all nine values produces an equivalent 509 | transformation, possibly improving precision. 510 | 511 | @param buffer nine scalar values 512 | */ 513 | void set9(const float buffer[9]); 514 | 515 | /** Sets Matrix to identity; which has no effect on mapped Point. Sets Matrix to: 516 | 517 | | 1 0 0 | 518 | | 0 1 0 | 519 | | 0 0 1 | 520 | 521 | Also called setIdentity(); use the one that provides better inline 522 | documentation. 523 | */ 524 | void reset(); 525 | 526 | /** Sets Matrix to identity; which has no effect on mapped Point. Sets Matrix to: 527 | 528 | | 1 0 0 | 529 | | 0 1 0 | 530 | | 0 0 1 | 531 | 532 | Also called reset(); use the one that provides better inline 533 | documentation. 534 | */ 535 | void setIdentity() { 536 | this->reset(); 537 | } 538 | 539 | /** Sets Matrix to translate by (dx, dy). 540 | 541 | @param dx horizontal translation 542 | @param dy vertical translation 543 | */ 544 | void setTranslate(float dx, float dy); 545 | 546 | /** Sets Matrix to scale by sx and sy, about a pivot point at (px, py). 547 | The pivot point is unchanged when mapped with Matrix. 548 | 549 | @param sx horizontal scale factor 550 | @param sy vertical scale factor 551 | @param px pivot x 552 | @param py pivot y 553 | */ 554 | void setScale(float sx, float sy, float px, float py); 555 | 556 | /** Sets Matrix to scale by sx and sy about at pivot point at (0, 0). 557 | 558 | @param sx horizontal scale factor 559 | @param sy vertical scale factor 560 | */ 561 | void setScale(float sx, float sy); 562 | 563 | /** Sets Matrix to rotate by degrees about a pivot point at (px, py). 564 | The pivot point is unchanged when mapped with Matrix. 565 | 566 | Positive degrees rotates clockwise. 567 | 568 | @param degrees angle of axes relative to upright axes 569 | @param px pivot x 570 | @param py pivot y 571 | */ 572 | void setRotate(float degrees, float px, float py); 573 | 574 | /** Sets Matrix to rotate by degrees about a pivot point at (0, 0). 575 | Positive degrees rotates clockwise. 576 | 577 | @param degrees angle of axes relative to upright axes 578 | */ 579 | void setRotate(float degrees); 580 | 581 | /** Sets Matrix to rotate by sinValue and cosValue, about a pivot point at (px, py). 582 | The pivot point is unchanged when mapped with Matrix. 583 | 584 | Vector (sinValue, cosValue) describes the angle of rotation relative to (0, 1). 585 | Vector length specifies scale. 586 | 587 | @param sinValue rotation vector x-axis component 588 | @param cosValue rotation vector y-axis component 589 | @param px pivot x-axis 590 | @param py pivot y-axis 591 | */ 592 | void setSinCos(float sinValue, float cosValue, float px, float py); 593 | 594 | /** Sets Matrix to rotate by sinValue and cosValue, about a pivot point at (0, 0). 595 | 596 | Vector (sinValue, cosValue) describes the angle of rotation relative to (0, 1). 597 | Vector length specifies scale. 598 | 599 | @param sinValue rotation vector x-axis component 600 | @param cosValue rotation vector y-axis component 601 | */ 602 | void setSinCos(float sinValue, float cosValue); 603 | 604 | /** Sets Matrix to skew by kx and ky, about a pivot point at (px, py). 605 | The pivot point is unchanged when mapped with Matrix. 606 | 607 | @param kx horizontal skew factor 608 | @param ky vertical skew factor 609 | @param px pivot x 610 | @param py pivot y 611 | */ 612 | void setSkew(float kx, float ky, float px, float py); 613 | 614 | /** Sets Matrix to skew by kx and ky, about a pivot point at (0, 0). 615 | 616 | @param kx horizontal skew factor 617 | @param ky vertical skew factor 618 | */ 619 | void setSkew(float kx, float ky); 620 | 621 | /** Sets Matrix to Matrix a multiplied by Matrix b. Either a or b may be this. 622 | 623 | Given: 624 | 625 | | A B C | | J K L | 626 | a = | D E F |, b = | M N O | 627 | | G H I | | P Q R | 628 | 629 | sets Matrix to: 630 | 631 | | A B C | | J K L | | AJ+BM+CP AK+BN+CQ AL+BO+CR | 632 | a * b = | D E F | * | M N O | = | DJ+EM+FP DK+EN+FQ DL+EO+FR | 633 | | G H I | | P Q R | | GJ+HM+IP GK+HN+IQ GL+HO+IR | 634 | 635 | @param a Matrix on left side of multiply expression 636 | @param b Matrix on right side of multiply expression 637 | */ 638 | void setConcat(const Matrix& a, const Matrix& b); 639 | 640 | /** Sets Matrix to Matrix multiplied by Matrix constructed from translation (dx, dy). 641 | This can be thought of as moving the point to be mapped before applying Matrix. 642 | 643 | Given: 644 | 645 | | A B C | | 1 0 dx | 646 | Matrix = | D E F |, T(dx, dy) = | 0 1 dy | 647 | | G H I | | 0 0 1 | 648 | 649 | sets Matrix to: 650 | 651 | | A B C | | 1 0 dx | | A B A*dx+B*dy+C | 652 | Matrix * T(dx, dy) = | D E F | | 0 1 dy | = | D E D*dx+E*dy+F | 653 | | G H I | | 0 0 1 | | G H G*dx+H*dy+I | 654 | 655 | @param dx x-axis translation before applying Matrix 656 | @param dy y-axis translation before applying Matrix 657 | */ 658 | void preTranslate(float dx, float dy); 659 | 660 | /** Sets Matrix to Matrix multiplied by Matrix constructed from scaling by (sx, sy) 661 | about pivot point (px, py). 662 | This can be thought of as scaling about a pivot point before applying Matrix. 663 | 664 | Given: 665 | 666 | | A B C | | sx 0 dx | 667 | Matrix = | D E F |, S(sx, sy, px, py) = | 0 sy dy | 668 | | G H I | | 0 0 1 | 669 | 670 | where 671 | 672 | dx = px - sx * px 673 | dy = py - sy * py 674 | 675 | sets Matrix to: 676 | 677 | | A B C | | sx 0 dx | | A*sx B*sy A*dx+B*dy+C | 678 | Matrix * S(sx, sy, px, py) = | D E F | | 0 sy dy | = | D*sx E*sy D*dx+E*dy+F | 679 | | G H I | | 0 0 1 | | G*sx H*sy G*dx+H*dy+I | 680 | 681 | @param sx horizontal scale factor 682 | @param sy vertical scale factor 683 | @param px pivot x 684 | @param py pivot y 685 | */ 686 | void preScale(float sx, float sy, float px, float py); 687 | 688 | /** Sets Matrix to Matrix multiplied by Matrix constructed from scaling by (sx, sy) 689 | about pivot point (0, 0). 690 | This can be thought of as scaling about the origin before applying Matrix. 691 | 692 | Given: 693 | 694 | | A B C | | sx 0 0 | 695 | Matrix = | D E F |, S(sx, sy) = | 0 sy 0 | 696 | | G H I | | 0 0 1 | 697 | 698 | sets Matrix to: 699 | 700 | | A B C | | sx 0 0 | | A*sx B*sy C | 701 | Matrix * S(sx, sy) = | D E F | | 0 sy 0 | = | D*sx E*sy F | 702 | | G H I | | 0 0 1 | | G*sx H*sy I | 703 | 704 | @param sx horizontal scale factor 705 | @param sy vertical scale factor 706 | */ 707 | void preScale(float sx, float sy); 708 | 709 | /** Sets Matrix to Matrix multiplied by Matrix constructed from rotating by degrees 710 | about pivot point (px, py). 711 | This can be thought of as rotating about a pivot point before applying Matrix. 712 | 713 | Positive degrees rotates clockwise. 714 | 715 | Given: 716 | 717 | | A B C | | c -s dx | 718 | Matrix = | D E F |, R(degrees, px, py) = | s c dy | 719 | | G H I | | 0 0 1 | 720 | 721 | where 722 | 723 | c = cos(degrees) 724 | s = sin(degrees) 725 | dx = s * py + (1 - c) * px 726 | dy = -s * px + (1 - c) * py 727 | 728 | sets Matrix to: 729 | 730 | | A B C | | c -s dx | | Ac+Bs -As+Bc A*dx+B*dy+C | 731 | Matrix * R(degrees, px, py) = | D E F | | s c dy | = | Dc+Es -Ds+Ec D*dx+E*dy+F | 732 | | G H I | | 0 0 1 | | Gc+Hs -Gs+Hc G*dx+H*dy+I | 733 | 734 | @param degrees angle of axes relative to upright axes 735 | @param px pivot x 736 | @param py pivot y 737 | */ 738 | void preRotate(float degrees, float px, float py); 739 | 740 | /** Sets Matrix to Matrix multiplied by Matrix constructed from rotating by degrees 741 | about pivot point (0, 0). 742 | This can be thought of as rotating about the origin before applying Matrix. 743 | 744 | Positive degrees rotates clockwise. 745 | 746 | Given: 747 | 748 | | A B C | | c -s 0 | 749 | Matrix = | D E F |, R(degrees, px, py) = | s c 0 | 750 | | G H I | | 0 0 1 | 751 | 752 | where 753 | 754 | c = cos(degrees) 755 | s = sin(degrees) 756 | 757 | sets Matrix to: 758 | 759 | | A B C | | c -s 0 | | Ac+Bs -As+Bc C | 760 | Matrix * R(degrees, px, py) = | D E F | | s c 0 | = | Dc+Es -Ds+Ec F | 761 | | G H I | | 0 0 1 | | Gc+Hs -Gs+Hc I | 762 | 763 | @param degrees angle of axes relative to upright axes 764 | */ 765 | void preRotate(float degrees); 766 | 767 | /** Sets Matrix to Matrix multiplied by Matrix constructed from skewing by (kx, ky) 768 | about pivot point (px, py). 769 | This can be thought of as skewing about a pivot point before applying Matrix. 770 | 771 | Given: 772 | 773 | | A B C | | 1 kx dx | 774 | Matrix = | D E F |, K(kx, ky, px, py) = | ky 1 dy | 775 | | G H I | | 0 0 1 | 776 | 777 | where 778 | 779 | dx = -kx * py 780 | dy = -ky * px 781 | 782 | sets Matrix to: 783 | 784 | | A B C | | 1 kx dx | | A+B*ky A*kx+B A*dx+B*dy+C | 785 | Matrix * K(kx, ky, px, py) = | D E F | | ky 1 dy | = | D+E*ky D*kx+E D*dx+E*dy+F | 786 | | G H I | | 0 0 1 | | G+H*ky G*kx+H G*dx+H*dy+I | 787 | 788 | @param kx horizontal skew factor 789 | @param ky vertical skew factor 790 | @param px pivot x 791 | @param py pivot y 792 | */ 793 | void preSkew(float kx, float ky, float px, float py); 794 | 795 | /** Sets Matrix to Matrix multiplied by Matrix constructed from skewing by (kx, ky) 796 | about pivot point (0, 0). 797 | This can be thought of as skewing about the origin before applying Matrix. 798 | 799 | Given: 800 | 801 | | A B C | | 1 kx 0 | 802 | Matrix = | D E F |, K(kx, ky) = | ky 1 0 | 803 | | G H I | | 0 0 1 | 804 | 805 | sets Matrix to: 806 | 807 | | A B C | | 1 kx 0 | | A+B*ky A*kx+B C | 808 | Matrix * K(kx, ky) = | D E F | | ky 1 0 | = | D+E*ky D*kx+E F | 809 | | G H I | | 0 0 1 | | G+H*ky G*kx+H I | 810 | 811 | @param kx horizontal skew factor 812 | @param ky vertical skew factor 813 | */ 814 | void preSkew(float kx, float ky); 815 | 816 | /** Sets Matrix to Matrix multiplied by Matrix other. 817 | This can be thought of mapping by other before applying Matrix. 818 | 819 | Given: 820 | 821 | | A B C | | J K L | 822 | Matrix = | D E F |, other = | M N O | 823 | | G H I | | P Q R | 824 | 825 | sets Matrix to: 826 | 827 | | A B C | | J K L | | AJ+BM+CP AK+BN+CQ AL+BO+CR | 828 | Matrix * other = | D E F | * | M N O | = | DJ+EM+FP DK+EN+FQ DL+EO+FR | 829 | | G H I | | P Q R | | GJ+HM+IP GK+HN+IQ GL+HO+IR | 830 | 831 | @param other Matrix on right side of multiply expression 832 | */ 833 | void preConcat(const Matrix& other); 834 | 835 | /** Sets Matrix to Matrix constructed from translation (dx, dy) multiplied by Matrix. 836 | This can be thought of as moving the point to be mapped after applying Matrix. 837 | 838 | Given: 839 | 840 | | J K L | | 1 0 dx | 841 | Matrix = | M N O |, T(dx, dy) = | 0 1 dy | 842 | | P Q R | | 0 0 1 | 843 | 844 | sets Matrix to: 845 | 846 | | 1 0 dx | | J K L | | J+dx*P K+dx*Q L+dx*R | 847 | T(dx, dy) * Matrix = | 0 1 dy | | M N O | = | M+dy*P N+dy*Q O+dy*R | 848 | | 0 0 1 | | P Q R | | P Q R | 849 | 850 | @param dx x-axis translation after applying Matrix 851 | @param dy y-axis translation after applying Matrix 852 | */ 853 | void postTranslate(float dx, float dy); 854 | 855 | /** Sets Matrix to Matrix constructed from scaling by (sx, sy) about pivot point 856 | (px, py), multiplied by Matrix. 857 | This can be thought of as scaling about a pivot point after applying Matrix. 858 | 859 | Given: 860 | 861 | | J K L | | sx 0 dx | 862 | Matrix = | M N O |, S(sx, sy, px, py) = | 0 sy dy | 863 | | P Q R | | 0 0 1 | 864 | 865 | where 866 | 867 | dx = px - sx * px 868 | dy = py - sy * py 869 | 870 | sets Matrix to: 871 | 872 | | sx 0 dx | | J K L | | sx*J+dx*P sx*K+dx*Q sx*L+dx+R | 873 | S(sx, sy, px, py) * Matrix = | 0 sy dy | | M N O | = | sy*M+dy*P sy*N+dy*Q sy*O+dy*R | 874 | | 0 0 1 | | P Q R | | P Q R | 875 | 876 | @param sx horizontal scale factor 877 | @param sy vertical scale factor 878 | @param px pivot x 879 | @param py pivot y 880 | */ 881 | void postScale(float sx, float sy, float px, float py); 882 | 883 | /** Sets Matrix to Matrix constructed from scaling by (sx, sy) about pivot point 884 | (0, 0), multiplied by Matrix. 885 | This can be thought of as scaling about the origin after applying Matrix. 886 | 887 | Given: 888 | 889 | | J K L | | sx 0 0 | 890 | Matrix = | M N O |, S(sx, sy) = | 0 sy 0 | 891 | | P Q R | | 0 0 1 | 892 | 893 | sets Matrix to: 894 | 895 | | sx 0 0 | | J K L | | sx*J sx*K sx*L | 896 | S(sx, sy) * Matrix = | 0 sy 0 | | M N O | = | sy*M sy*N sy*O | 897 | | 0 0 1 | | P Q R | | P Q R | 898 | 899 | @param sx horizontal scale factor 900 | @param sy vertical scale factor 901 | */ 902 | void postScale(float sx, float sy); 903 | 904 | /** Sets Matrix to Matrix constructed from scaling by (1/divx, 1/divy) about pivot point (px, py), multiplied by 905 | Matrix. 906 | 907 | Returns false if either divx or divy is zero. 908 | 909 | Given: 910 | 911 | | J K L | | sx 0 0 | 912 | Matrix = | M N O |, I(divx, divy) = | 0 sy 0 | 913 | | P Q R | | 0 0 1 | 914 | 915 | where 916 | 917 | sx = 1 / divx 918 | sy = 1 / divy 919 | 920 | sets Matrix to: 921 | 922 | | sx 0 0 | | J K L | | sx*J sx*K sx*L | 923 | I(divx, divy) * Matrix = | 0 sy 0 | | M N O | = | sy*M sy*N sy*O | 924 | | 0 0 1 | | P Q R | | P Q R | 925 | 926 | @param divx integer divisor for inverse scale in x 927 | @param divy integer divisor for inverse scale in y 928 | @return true on successful scale 929 | */ 930 | bool postIDiv(int divx, int divy); 931 | 932 | /** Sets Matrix to Matrix constructed from rotating by degrees about pivot point 933 | (px, py), multiplied by Matrix. 934 | This can be thought of as rotating about a pivot point after applying Matrix. 935 | 936 | Positive degrees rotates clockwise. 937 | 938 | Given: 939 | 940 | | J K L | | c -s dx | 941 | Matrix = | M N O |, R(degrees, px, py) = | s c dy | 942 | | P Q R | | 0 0 1 | 943 | 944 | where 945 | 946 | c = cos(degrees) 947 | s = sin(degrees) 948 | dx = s * py + (1 - c) * px 949 | dy = -s * px + (1 - c) * py 950 | 951 | sets Matrix to: 952 | 953 | |c -s dx| |J K L| |cJ-sM+dx*P cK-sN+dx*Q cL-sO+dx+R| 954 | R(degrees, px, py) * Matrix = |s c dy| |M N O| = |sJ+cM+dy*P sK+cN+dy*Q sL+cO+dy*R| 955 | |0 0 1| |P Q R| | P Q R| 956 | 957 | @param degrees angle of axes relative to upright axes 958 | @param px pivot x 959 | @param py pivot y 960 | */ 961 | void postRotate(float degrees, float px, float py); 962 | 963 | /** Sets Matrix to Matrix constructed from rotating by degrees about pivot point 964 | (0, 0), multiplied by Matrix. 965 | This can be thought of as rotating about the origin after applying Matrix. 966 | 967 | Positive degrees rotates clockwise. 968 | 969 | Given: 970 | 971 | | J K L | | c -s 0 | 972 | Matrix = | M N O |, R(degrees, px, py) = | s c 0 | 973 | | P Q R | | 0 0 1 | 974 | 975 | where 976 | 977 | c = cos(degrees) 978 | s = sin(degrees) 979 | 980 | sets Matrix to: 981 | 982 | | c -s dx | | J K L | | cJ-sM cK-sN cL-sO | 983 | R(degrees, px, py) * Matrix = | s c dy | | M N O | = | sJ+cM sK+cN sL+cO | 984 | | 0 0 1 | | P Q R | | P Q R | 985 | 986 | @param degrees angle of axes relative to upright axes 987 | */ 988 | void postRotate(float degrees); 989 | 990 | /** Sets Matrix to Matrix constructed from skewing by (kx, ky) about pivot point 991 | (px, py), multiplied by Matrix. 992 | This can be thought of as skewing about a pivot point after applying Matrix. 993 | 994 | Given: 995 | 996 | | J K L | | 1 kx dx | 997 | Matrix = | M N O |, K(kx, ky, px, py) = | ky 1 dy | 998 | | P Q R | | 0 0 1 | 999 | 1000 | where 1001 | 1002 | dx = -kx * py 1003 | dy = -ky * px 1004 | 1005 | sets Matrix to: 1006 | 1007 | | 1 kx dx| |J K L| |J+kx*M+dx*P K+kx*N+dx*Q L+kx*O+dx+R| 1008 | K(kx, ky, px, py) * Matrix = |ky 1 dy| |M N O| = |ky*J+M+dy*P ky*K+N+dy*Q ky*L+O+dy*R| 1009 | | 0 0 1| |P Q R| | P Q R| 1010 | 1011 | @param kx horizontal skew factor 1012 | @param ky vertical skew factor 1013 | @param px pivot x 1014 | @param py pivot y 1015 | */ 1016 | void postSkew(float kx, float ky, float px, float py); 1017 | 1018 | /** Sets Matrix to Matrix constructed from skewing by (kx, ky) about pivot point 1019 | (0, 0), multiplied by Matrix. 1020 | This can be thought of as skewing about the origin after applying Matrix. 1021 | 1022 | Given: 1023 | 1024 | | J K L | | 1 kx 0 | 1025 | Matrix = | M N O |, K(kx, ky) = | ky 1 0 | 1026 | | P Q R | | 0 0 1 | 1027 | 1028 | sets Matrix to: 1029 | 1030 | | 1 kx 0 | | J K L | | J+kx*M K+kx*N L+kx*O | 1031 | K(kx, ky) * Matrix = | ky 1 0 | | M N O | = | ky*J+M ky*K+N ky*L+O | 1032 | | 0 0 1 | | P Q R | | P Q R | 1033 | 1034 | @param kx horizontal skew factor 1035 | @param ky vertical skew factor 1036 | */ 1037 | void postSkew(float kx, float ky); 1038 | 1039 | /** Sets Matrix to Matrix other multiplied by Matrix. 1040 | This can be thought of mapping by other after applying Matrix. 1041 | 1042 | Given: 1043 | 1044 | | J K L | | A B C | 1045 | Matrix = | M N O |, other = | D E F | 1046 | | P Q R | | G H I | 1047 | 1048 | sets Matrix to: 1049 | 1050 | | A B C | | J K L | | AJ+BM+CP AK+BN+CQ AL+BO+CR | 1051 | other * Matrix = | D E F | * | M N O | = | DJ+EM+FP DK+EN+FQ DL+EO+FR | 1052 | | G H I | | P Q R | | GJ+HM+IP GK+HN+IQ GL+HO+IR | 1053 | 1054 | @param other Matrix on left side of multiply expression 1055 | */ 1056 | void postConcat(const Matrix& other); 1057 | 1058 | /** \enum Matrix::ScaleToFit 1059 | ScaleToFit describes how Matrix is constructed to map one Rect to another. 1060 | ScaleToFit may allow Matrix to have unequal horizontal and vertical scaling, 1061 | or may restrict Matrix to square scaling. If restricted, ScaleToFit specifies 1062 | how Matrix maps to the side or center of the destination Rect. 1063 | */ 1064 | enum ScaleToFit { 1065 | kFill_ScaleToFit, //!< scales in x and y to fill destination Rect 1066 | kStart_ScaleToFit, //!< scales and aligns to left and top 1067 | kCenter_ScaleToFit, //!< scales and aligns to center 1068 | kEnd_ScaleToFit, //!< scales and aligns to right and bottom 1069 | }; 1070 | 1071 | /** Sets Matrix to scale and translate src Rect to dst Rect. stf selects whether 1072 | mapping completely fills dst or preserves the aspect ratio, and how to align 1073 | src within dst. Returns false if src is empty, and sets Matrix to identity. 1074 | Returns true if dst is empty, and sets Matrix to: 1075 | 1076 | | 0 0 0 | 1077 | | 0 0 0 | 1078 | | 0 0 1 | 1079 | 1080 | @param src Rect to map from 1081 | @param dst Rect to map to 1082 | @param stf one of: kFill_ScaleToFit, kStart_ScaleToFit, 1083 | kCenter_ScaleToFit, kEnd_ScaleToFit 1084 | @return true if Matrix can represent Rect mapping 1085 | */ 1086 | bool setRectToRect(const Rect& src, const Rect& dst, ScaleToFit stf); 1087 | 1088 | /** Returns Matrix set to scale and translate src Rect to dst Rect. stf selects 1089 | whether mapping completely fills dst or preserves the aspect ratio, and how to 1090 | align src within dst. Returns the identity Matrix if src is empty. If dst is 1091 | empty, returns Matrix set to: 1092 | 1093 | | 0 0 0 | 1094 | | 0 0 0 | 1095 | | 0 0 1 | 1096 | 1097 | @param src Rect to map from 1098 | @param dst Rect to map to 1099 | @param stf one of: kFill_ScaleToFit, kStart_ScaleToFit, 1100 | kCenter_ScaleToFit, kEnd_ScaleToFit 1101 | @return Matrix mapping src to dst 1102 | */ 1103 | static Matrix MakeRectToRect(const Rect& src, const Rect& dst, ScaleToFit stf) { 1104 | Matrix m; 1105 | m.setRectToRect(src, dst, stf); 1106 | return m; 1107 | } 1108 | 1109 | /** Sets Matrix to map src to dst. count must be zero or greater, and four or less. 1110 | 1111 | If count is zero, sets Matrix to identity and returns true. 1112 | If count is one, sets Matrix to translate and returns true. 1113 | If count is two or more, sets Matrix to map Point if possible; returns false 1114 | if Matrix cannot be constructed. If count is four, Matrix may include 1115 | perspective. 1116 | 1117 | @param src Point to map from 1118 | @param dst Point to map to 1119 | @param count number of Point in src and dst 1120 | @return true if Matrix was constructed successfully 1121 | */ 1122 | bool setPolyToPoly(const Point src[], const Point dst[], int count); 1123 | 1124 | /** Sets inverse to reciprocal matrix, returning true if Matrix can be inverted. 1125 | Geometrically, if Matrix maps from source to destination, inverse Matrix 1126 | maps from destination to source. If Matrix can not be inverted, inverse is 1127 | unchanged. 1128 | 1129 | @param inverse storage for inverted Matrix; may be nullptr 1130 | @return true if Matrix can be inverted 1131 | */ 1132 | bool invert(Matrix* inverse) const { 1133 | // Allow the trivial case to be inlined. 1134 | if (this->isIdentity()) { 1135 | if (inverse) { 1136 | inverse->reset(); 1137 | } 1138 | return true; 1139 | } 1140 | return this->invertNonIdentity(inverse); 1141 | } 1142 | 1143 | /** Fills affine with identity values in column major order. 1144 | Sets affine to: 1145 | 1146 | | 1 0 0 | 1147 | | 0 1 0 | 1148 | 1149 | Affine 3x2 matrices in column major order are used by OpenGL and XPS. 1150 | 1151 | @param affine storage for 3x2 affine matrix 1152 | */ 1153 | static void SetAffineIdentity(float affine[6]); 1154 | 1155 | /** Fills affine in column major order. Sets affine to: 1156 | 1157 | | scale-x skew-x translate-x | 1158 | | skew-y scale-y translate-y | 1159 | 1160 | If Matrix contains perspective, returns false and leaves affine unchanged. 1161 | 1162 | @param affine storage for 3x2 affine matrix; may be nullptr 1163 | @return true if Matrix does not contain perspective 1164 | */ 1165 | bool asAffine(float affine[6]) const; 1166 | 1167 | /** Sets Matrix to affine values, passed in column major order. Given affine, 1168 | column, then row, as: 1169 | 1170 | | scale-x skew-x translate-x | 1171 | | skew-y scale-y translate-y | 1172 | 1173 | Matrix is set, row, then column, to: 1174 | 1175 | | scale-x skew-x translate-x | 1176 | | skew-y scale-y translate-y | 1177 | | 0 0 1 | 1178 | 1179 | @param affine 3x2 affine matrix 1180 | */ 1181 | void setAffine(const float affine[6]); 1182 | 1183 | /** Maps src Point array of length count to dst Point array of equal or greater 1184 | length. Point are mapped by multiplying each Point by Matrix. Given: 1185 | 1186 | | A B C | | x | 1187 | Matrix = | D E F |, pt = | y | 1188 | | G H I | | 1 | 1189 | 1190 | where 1191 | 1192 | for (i = 0; i < count; ++i) { 1193 | x = src[i].fX 1194 | y = src[i].fY 1195 | } 1196 | 1197 | each dst Point is computed as: 1198 | 1199 | |A B C| |x| Ax+By+C Dx+Ey+F 1200 | Matrix * pt = |D E F| |y| = |Ax+By+C Dx+Ey+F Gx+Hy+I| = ------- , ------- 1201 | |G H I| |1| Gx+Hy+I Gx+Hy+I 1202 | 1203 | src and dst may point to the same storage. 1204 | 1205 | @param dst storage for mapped Point 1206 | @param src Point to transform 1207 | @param count number of Point to transform 1208 | */ 1209 | void mapPoints(Point dst[], const Point src[], int count) const { 1210 | MNN_ASSERT((dst && src && count > 0) || 0 == count); 1211 | // no partial overlap 1212 | MNN_ASSERT(src == dst || &dst[count] <= &src[0] || &src[count] <= &dst[0]); 1213 | this->getMapPtsProc()(*this, dst, src, count); 1214 | } 1215 | 1216 | /** Maps pts Point array of length count in place. Point are mapped by multiplying 1217 | each Point by Matrix. Given: 1218 | 1219 | | A B C | | x | 1220 | Matrix = | D E F |, pt = | y | 1221 | | G H I | | 1 | 1222 | 1223 | where 1224 | 1225 | for (i = 0; i < count; ++i) { 1226 | x = pts[i].fX 1227 | y = pts[i].fY 1228 | } 1229 | 1230 | each resulting pts Point is computed as: 1231 | 1232 | |A B C| |x| Ax+By+C Dx+Ey+F 1233 | Matrix * pt = |D E F| |y| = |Ax+By+C Dx+Ey+F Gx+Hy+I| = ------- , ------- 1234 | |G H I| |1| Gx+Hy+I Gx+Hy+I 1235 | 1236 | @param pts storage for mapped Point 1237 | @param count number of Point to transform 1238 | */ 1239 | void mapPoints(Point pts[], int count) const { 1240 | this->mapPoints(pts, pts, count); 1241 | } 1242 | 1243 | /** Maps Point (x, y) to result. Point is mapped by multiplying by Matrix. Given: 1244 | 1245 | | A B C | | x | 1246 | Matrix = | D E F |, pt = | y | 1247 | | G H I | | 1 | 1248 | 1249 | result is computed as: 1250 | 1251 | |A B C| |x| Ax+By+C Dx+Ey+F 1252 | Matrix * pt = |D E F| |y| = |Ax+By+C Dx+Ey+F Gx+Hy+I| = ------- , ------- 1253 | |G H I| |1| Gx+Hy+I Gx+Hy+I 1254 | 1255 | @param x x-axis value of Point to map 1256 | @param y y-axis value of Point to map 1257 | @param result storage for mapped Point 1258 | */ 1259 | void mapXY(float x, float y, Point* result) const { 1260 | this->getMapXYProc()(*this, x, y, result); 1261 | } 1262 | 1263 | /** Returns Point (x, y) multiplied by Matrix. Given: 1264 | 1265 | | A B C | | x | 1266 | Matrix = | D E F |, pt = | y | 1267 | | G H I | | 1 | 1268 | 1269 | result is computed as: 1270 | 1271 | |A B C| |x| Ax+By+C Dx+Ey+F 1272 | Matrix * pt = |D E F| |y| = |Ax+By+C Dx+Ey+F Gx+Hy+I| = ------- , ------- 1273 | |G H I| |1| Gx+Hy+I Gx+Hy+I 1274 | 1275 | @param x x-axis value of Point to map 1276 | @param y y-axis value of Point to map 1277 | @return mapped Point 1278 | */ 1279 | Point mapXY(float x, float y) const { 1280 | Point result; 1281 | this->getMapXYProc()(*this, x, y, &result); 1282 | return result; 1283 | } 1284 | 1285 | /** Sets dst to bounds of src corners mapped by Matrix. 1286 | Returns true if mapped corners are dst corners. 1287 | 1288 | Returned value is the same as calling rectStaysRect(). 1289 | 1290 | @param dst storage for bounds of mapped Point 1291 | @param src Rect to map 1292 | @return true if dst is equivalent to mapped src 1293 | */ 1294 | bool mapRect(Rect* dst, const Rect& src) const; 1295 | 1296 | /** Sets rect to bounds of rect corners mapped by Matrix. 1297 | Returns true if mapped corners are computed rect corners. 1298 | 1299 | Returned value is the same as calling rectStaysRect(). 1300 | 1301 | @param rect rectangle to map, and storage for bounds of mapped corners 1302 | @return true if result is equivalent to mapped src 1303 | */ 1304 | bool mapRect(Rect* rect) const { 1305 | return this->mapRect(rect, *rect); 1306 | } 1307 | 1308 | /** Returns bounds of src corners mapped by Matrix. 1309 | 1310 | @param src rectangle to map 1311 | @return mapped bounds 1312 | */ 1313 | Rect mapRect(const Rect& src) const { 1314 | Rect dst; 1315 | (void)this->mapRect(&dst, src); 1316 | return dst; 1317 | } 1318 | 1319 | /** Sets dst to bounds of src corners mapped by Matrix. If matrix contains 1320 | elements other than scale or translate: asserts if SK_DEBUG is defined; 1321 | otherwise, results are undefined. 1322 | 1323 | @param dst storage for bounds of mapped Point 1324 | @param src Rect to map 1325 | */ 1326 | void mapRectScaleTranslate(Rect* dst, const Rect& src) const; 1327 | 1328 | /** Returns true if Matrix equals m, using an efficient comparison. 1329 | 1330 | Returns false when the sign of zero values is the different; when one 1331 | matrix has positive zero value and the other has negative zero value. 1332 | 1333 | Returns true even when both Matrix contain NaN. 1334 | 1335 | NaN never equals any value, including itself. To improve performance, NaN values 1336 | are treated as bit patterns that are equal if their bit patterns are equal. 1337 | 1338 | @param m Matrix to compare 1339 | @return true if m and Matrix are represented by identical bit patterns 1340 | */ 1341 | bool cheapEqualTo(const Matrix& m) const { 1342 | return 0 == memcmp(fMat, m.fMat, sizeof(fMat)); 1343 | } 1344 | 1345 | /** Compares a and b; returns true if a and b are numerically equal. Returns true 1346 | even if sign of zero values are different. Returns false if either Matrix 1347 | contains NaN, even if the other Matrix also contains NaN. 1348 | 1349 | @param a Matrix to compare 1350 | @param b Matrix to compare 1351 | @return true if Matrix a and Matrix b are numerically equal 1352 | */ 1353 | friend MNN_PUBLIC bool operator==(const Matrix& a, const Matrix& b); 1354 | 1355 | /** Compares a and b; returns true if a and b are not numerically equal. Returns false 1356 | even if sign of zero values are different. Returns true if either Matrix 1357 | contains NaN, even if the other Matrix also contains NaN. 1358 | 1359 | @param a Matrix to compare 1360 | @param b Matrix to compare 1361 | @return true if Matrix a and Matrix b are numerically not equal 1362 | */ 1363 | friend MNN_PUBLIC bool operator!=(const Matrix& a, const Matrix& b) { 1364 | return !(a == b); 1365 | } 1366 | 1367 | /** Writes text representation of Matrix to standard output. Floating point values 1368 | are written with limited precision; it may not be possible to reconstruct 1369 | original Matrix from output. 1370 | */ 1371 | void dump() const; 1372 | 1373 | /** Returns the minimum scaling factor of Matrix by decomposing the scaling and 1374 | skewing elements. 1375 | Returns -1 if scale factor overflows or Matrix contains perspective. 1376 | 1377 | @return minimum scale factor 1378 | */ 1379 | float getMinScale() const; 1380 | 1381 | /** Returns the maximum scaling factor of Matrix by decomposing the scaling and 1382 | skewing elements. 1383 | Returns -1 if scale factor overflows or Matrix contains perspective. 1384 | 1385 | @return maximum scale factor 1386 | */ 1387 | float getMaxScale() const; 1388 | 1389 | /** Sets scaleFactors[0] to the minimum scaling factor, and scaleFactors[1] to the 1390 | maximum scaling factor. Scaling factors are computed by decomposing 1391 | the Matrix scaling and skewing elements. 1392 | 1393 | Returns true if scaleFactors are found; otherwise, returns false and sets 1394 | scaleFactors to undefined values. 1395 | 1396 | @param scaleFactors storage for minimum and maximum scale factors 1397 | @return true if scale factors were computed correctly 1398 | */ 1399 | bool getMinMaxScales(float scaleFactors[2]) const; 1400 | 1401 | /** Returns reference to const identity Matrix. Returned Matrix is set to: 1402 | 1403 | | 1 0 0 | 1404 | | 0 1 0 | 1405 | | 0 0 1 | 1406 | 1407 | @return const identity Matrix 1408 | */ 1409 | static const Matrix& I(); 1410 | 1411 | /** Returns reference to a const Matrix with invalid values. Returned Matrix is set 1412 | to: 1413 | 1414 | | SK_ScalarMax SK_ScalarMax SK_ScalarMax | 1415 | | SK_ScalarMax SK_ScalarMax SK_ScalarMax | 1416 | | SK_ScalarMax SK_ScalarMax SK_ScalarMax | 1417 | 1418 | @return const invalid Matrix 1419 | */ 1420 | static const Matrix& InvalidMatrix(); 1421 | 1422 | /** Returns Matrix a multiplied by Matrix b. 1423 | 1424 | Given: 1425 | 1426 | | A B C | | J K L | 1427 | a = | D E F |, b = | M N O | 1428 | | G H I | | P Q R | 1429 | 1430 | sets Matrix to: 1431 | 1432 | | A B C | | J K L | | AJ+BM+CP AK+BN+CQ AL+BO+CR | 1433 | a * b = | D E F | * | M N O | = | DJ+EM+FP DK+EN+FQ DL+EO+FR | 1434 | | G H I | | P Q R | | GJ+HM+IP GK+HN+IQ GL+HO+IR | 1435 | 1436 | @param a Matrix on left side of multiply expression 1437 | @param b Matrix on right side of multiply expression 1438 | @return Matrix computed from a times b 1439 | */ 1440 | static Matrix Concat(const Matrix& a, const Matrix& b) { 1441 | Matrix result; 1442 | result.setConcat(a, b); 1443 | return result; 1444 | } 1445 | 1446 | /** Sets internal cache to unknown state. Use to force update after repeated 1447 | modifications to Matrix element reference returned by operator[](int index). 1448 | */ 1449 | void dirtyMatrixTypeCache() { 1450 | this->setTypeMask(kUnknown_Mask); 1451 | } 1452 | 1453 | /** Initializes Matrix with scale and translate elements. 1454 | 1455 | | sx 0 tx | 1456 | | 0 sy ty | 1457 | | 0 0 1 | 1458 | 1459 | @param sx horizontal scale factor to store 1460 | @param sy vertical scale factor to store 1461 | @param tx horizontal translation to store 1462 | @param ty vertical translation to store 1463 | */ 1464 | void setScaleTranslate(float sx, float sy, float tx, float ty) { 1465 | fMat[kMScaleX] = sx; 1466 | fMat[kMSkewX] = 0; 1467 | fMat[kMTransX] = tx; 1468 | 1469 | fMat[kMSkewY] = 0; 1470 | fMat[kMScaleY] = sy; 1471 | fMat[kMTransY] = ty; 1472 | 1473 | fMat[kMPersp0] = 0; 1474 | fMat[kMPersp1] = 0; 1475 | fMat[kMPersp2] = 1; 1476 | 1477 | unsigned mask = 0; 1478 | if (sx != 1 || sy != 1) { 1479 | mask |= kScale_Mask; 1480 | } 1481 | if (tx || ty) { 1482 | mask |= kTranslate_Mask; 1483 | } 1484 | this->setTypeMask(mask | kRectStaysRect_Mask); 1485 | } 1486 | 1487 | /** Returns true if all elements of the matrix are finite. Returns false if any 1488 | element is infinity, or NaN. 1489 | 1490 | @return true if matrix has only finite elements 1491 | */ 1492 | 1493 | private: 1494 | /** Set if the matrix will map a rectangle to another rectangle. This 1495 | can be true if the matrix is scale-only, or rotates a multiple of 1496 | 90 degrees. 1497 | 1498 | This bit will be set on identity matrices 1499 | */ 1500 | static constexpr int kRectStaysRect_Mask = 0x10; 1501 | 1502 | /** Set if the perspective bit is valid even though the rest of 1503 | the matrix is Unknown. 1504 | */ 1505 | static constexpr int kOnlyPerspectiveValid_Mask = 0x40; 1506 | 1507 | static constexpr int kUnknown_Mask = 0x80; 1508 | 1509 | static constexpr int kORableMasks = kTranslate_Mask | kScale_Mask | kAffine_Mask | kPerspective_Mask; 1510 | 1511 | static constexpr int kAllMasks = 1512 | kTranslate_Mask | kScale_Mask | kAffine_Mask | kPerspective_Mask | kRectStaysRect_Mask; 1513 | 1514 | float fMat[9]; 1515 | mutable uint32_t fTypeMask; 1516 | 1517 | static void ComputeInv(float dst[9], const float src[9], double invDet, bool isPersp); 1518 | 1519 | uint8_t computeTypeMask() const; 1520 | uint8_t computePerspectiveTypeMask() const; 1521 | 1522 | void setTypeMask(int mask) { 1523 | // allow kUnknown or a valid mask 1524 | MNN_ASSERT(kUnknown_Mask == mask || (mask & kAllMasks) == mask || 1525 | ((kUnknown_Mask | kOnlyPerspectiveValid_Mask) & mask) == 1526 | (kUnknown_Mask | kOnlyPerspectiveValid_Mask)); 1527 | fTypeMask = (uint8_t)(mask); 1528 | } 1529 | 1530 | void orTypeMask(int mask) { 1531 | MNN_ASSERT((mask & kORableMasks) == mask); 1532 | fTypeMask = (uint8_t)(fTypeMask | mask); 1533 | } 1534 | 1535 | void clearTypeMask(int mask) { 1536 | // only allow a valid mask 1537 | MNN_ASSERT((mask & kAllMasks) == mask); 1538 | fTypeMask = fTypeMask & ~mask; 1539 | } 1540 | 1541 | TypeMask getPerspectiveTypeMaskOnly() const { 1542 | if ((fTypeMask & kUnknown_Mask) && !(fTypeMask & kOnlyPerspectiveValid_Mask)) { 1543 | fTypeMask = this->computePerspectiveTypeMask(); 1544 | } 1545 | return (TypeMask)(fTypeMask & 0xF); 1546 | } 1547 | 1548 | /** Returns true if we already know that the matrix is identity; 1549 | false otherwise. 1550 | */ 1551 | bool isTriviallyIdentity() const { 1552 | if (fTypeMask & kUnknown_Mask) { 1553 | return false; 1554 | } 1555 | return ((fTypeMask & 0xF) == 0); 1556 | } 1557 | 1558 | inline void updateTranslateMask() { 1559 | if ((fMat[kMTransX] != 0) | (fMat[kMTransY] != 0)) { 1560 | fTypeMask |= kTranslate_Mask; 1561 | } else { 1562 | fTypeMask &= ~kTranslate_Mask; 1563 | } 1564 | } 1565 | 1566 | typedef void (*MapXYProc)(const Matrix& mat, float x, float y, Point* result); 1567 | 1568 | static MapXYProc GetMapXYProc(TypeMask mask) { 1569 | MNN_ASSERT((mask & ~kAllMasks) == 0); 1570 | return gMapXYProcs[mask & kAllMasks]; 1571 | } 1572 | 1573 | MapXYProc getMapXYProc() const { 1574 | return GetMapXYProc(this->getType()); 1575 | } 1576 | 1577 | typedef void (*MapPtsProc)(const Matrix& mat, Point dst[], const Point src[], int count); 1578 | 1579 | static MapPtsProc GetMapPtsProc(TypeMask mask) { 1580 | MNN_ASSERT((mask & ~kAllMasks) == 0); 1581 | return gMapPtsProcs[mask & kAllMasks]; 1582 | } 1583 | 1584 | MapPtsProc getMapPtsProc() const { 1585 | return GetMapPtsProc(this->getType()); 1586 | } 1587 | 1588 | bool invertNonIdentity(Matrix* inverse) const; 1589 | 1590 | static void Identity_xy(const Matrix&, float, float, Point*); 1591 | static void Trans_xy(const Matrix&, float, float, Point*); 1592 | static void Scale_xy(const Matrix&, float, float, Point*); 1593 | static void ScaleTrans_xy(const Matrix&, float, float, Point*); 1594 | static void Rot_xy(const Matrix&, float, float, Point*); 1595 | static void RotTrans_xy(const Matrix&, float, float, Point*); 1596 | static void Persp_xy(const Matrix&, float, float, Point*); 1597 | 1598 | static const MapXYProc gMapXYProcs[]; 1599 | 1600 | static void Identity_pts(const Matrix&, Point[], const Point[], int); 1601 | static void Trans_pts(const Matrix&, Point dst[], const Point[], int); 1602 | static void Scale_pts(const Matrix&, Point dst[], const Point[], int); 1603 | static void ScaleTrans_pts(const Matrix&, Point dst[], const Point[], int count); 1604 | static void Persp_pts(const Matrix&, Point dst[], const Point[], int); 1605 | 1606 | static void Affine_vpts(const Matrix&, Point dst[], const Point[], int); 1607 | 1608 | static const MapPtsProc gMapPtsProcs[]; 1609 | static bool Poly2Proc(const Point srcPt[], Matrix* dst); 1610 | static bool Poly3Proc(const Point srcPt[], Matrix* dst); 1611 | static bool Poly4Proc(const Point srcPt[], Matrix* dst); 1612 | }; 1613 | } // namespace CV 1614 | } // namespace MNN 1615 | #endif 1616 | -------------------------------------------------------------------------------- /app/src/main/cpp/include/NonCopyable.hpp: -------------------------------------------------------------------------------- 1 | // 2 | // NonCopyable.hpp 3 | // MNN 4 | // 5 | // Created by MNN on 2018/09/19. 6 | // Copyright © 2018, Alibaba Group Holding Limited 7 | // 8 | 9 | #ifndef NonCopyable_hpp 10 | #define NonCopyable_hpp 11 | 12 | namespace MNN { 13 | /** protocol class. used to delete assignment operator. */ 14 | class NonCopyable { 15 | public: 16 | NonCopyable() = default; 17 | NonCopyable(const NonCopyable&) = delete; 18 | NonCopyable(const NonCopyable&&) = delete; 19 | NonCopyable& operator=(const NonCopyable&) = delete; 20 | NonCopyable& operator=(const NonCopyable&&) = delete; 21 | }; 22 | } // namespace MNN 23 | 24 | #endif /* NonCopyable_hpp */ 25 | -------------------------------------------------------------------------------- /app/src/main/cpp/include/Rect.h: -------------------------------------------------------------------------------- 1 | // 2 | // Rect.h 3 | // MNN 4 | // 5 | // Modified by jiangxiaotang on 2018/09/19. 6 | // Copyright © 2018, Alibaba Group Holding Limited 7 | // 8 | 9 | /* 10 | * Copyright 2006 The Android Open Source Project 11 | * 12 | * Use of this source code is governed by a BSD-style license that can be 13 | * found in the LICENSE file. 14 | */ 15 | 16 | /* Generated by tools/bookmaker from include/core/Rect.h and docs/SkRect_Reference.bmh 17 | on 2018-07-13 08:15:11. Additional documentation and examples can be found at: 18 | https://skia.org/user/api/SkRect_Reference 19 | 20 | You may edit either file directly. Structural changes to public interfaces require 21 | editing both files. After editing docs/SkRect_Reference.bmh, run: 22 | bookmaker -b docs -i include/core/Rect.h -p 23 | to create an updated version of this file. 24 | */ 25 | 26 | #ifndef SkRect_DEFINED 27 | #define SkRect_DEFINED 28 | 29 | #include 30 | #include 31 | #include 32 | #include "MNNDefine.h" 33 | 34 | namespace MNN { 35 | namespace CV { 36 | 37 | struct Point { 38 | float fX; 39 | float fY; 40 | 41 | void set(float x, float y) { 42 | fX = x; 43 | fY = y; 44 | } 45 | }; 46 | 47 | /** \struct Rect 48 | Rect holds four float coordinates describing the upper and 49 | lower bounds of a rectangle. Rect may be created from outer bounds or 50 | from position, width, and height. Rect describes an area; if its right 51 | is less than or equal to its left, or if its bottom is less than or equal to 52 | its top, it is considered empty. 53 | */ 54 | struct MNN_PUBLIC Rect { 55 | float fLeft; //!< smaller x-axis bounds 56 | float fTop; //!< smaller y-axis bounds 57 | float fRight; //!< larger x-axis bounds 58 | float fBottom; //!< larger y-axis bounds 59 | 60 | /** Returns constructed Rect set to (0, 0, 0, 0). 61 | Many other rectangles are empty; if left is equal to or greater than right, 62 | or if top is equal to or greater than bottom. Setting all members to zero 63 | is a convenience, but does not designate a special empty rectangle. 64 | 65 | @return bounds (0, 0, 0, 0) 66 | */ 67 | static constexpr Rect MakeEmpty() { 68 | return Rect{0, 0, 0, 0}; 69 | } 70 | 71 | #ifdef SK_SUPPORT_LEGACY_RECTMAKELARGEST 72 | /** Deprecated. 73 | */ 74 | static Rect MakeLargest() { 75 | return {SK_ScalarMin, SK_ScalarMin, SK_ScalarMax, SK_ScalarMax}; 76 | } 77 | #endif 78 | 79 | /** Returns constructed Rect set to float values (0, 0, w, h). Does not 80 | validate input; w or h may be negative. 81 | 82 | Passing integer values may generate a compiler warning since Rect cannot 83 | represent 32-bit integers exactly. Use SkIRect for an exact integer rectangle. 84 | 85 | @param w float width of constructed Rect 86 | @param h float height of constructed Rect 87 | @return bounds (0, 0, w, h) 88 | */ 89 | static constexpr Rect MakeWH(float w, float h) { 90 | return Rect{0, 0, w, h}; 91 | } 92 | 93 | /** Returns constructed Rect set to integer values (0, 0, w, h). Does not validate 94 | input; w or h may be negative. 95 | 96 | Use to avoid a compiler warning that input may lose precision when stored. 97 | Use SkIRect for an exact integer rectangle. 98 | 99 | @param w integer width of constructed Rect 100 | @param h integer height of constructed Rect 101 | @return bounds (0, 0, w, h) 102 | */ 103 | static Rect MakeIWH(int w, int h) { 104 | Rect r; 105 | r.set(0, 0, (float)(w), (float)(h)); 106 | return r; 107 | } 108 | 109 | /** Returns constructed Rect set to (l, t, r, b). Does not sort input; Rect may 110 | result in fLeft greater than fRight, or fTop greater than fBottom. 111 | 112 | @param l float stored in fLeft 113 | @param t float stored in fTop 114 | @param r float stored in fRight 115 | @param b float stored in fBottom 116 | @return bounds (l, t, r, b) 117 | */ 118 | static constexpr Rect MakeLTRB(float l, float t, float r, float b) { 119 | return Rect{l, t, r, b}; 120 | } 121 | 122 | /** Returns constructed Rect set to (x, y, x + w, y + h). Does not validate input; 123 | w or h may be negative. 124 | 125 | @param x stored in fLeft 126 | @param y stored in fTop 127 | @param w added to x and stored in fRight 128 | @param h added to y and stored in fBottom 129 | @return bounds at (x, y) with width w and height h 130 | */ 131 | static constexpr Rect MakeXYWH(float x, float y, float w, float h) { 132 | return Rect{x, y, x + w, y + h}; 133 | } 134 | 135 | /** Returns true if fLeft is equal to or greater than fRight, or if fTop is equal 136 | to or greater than fBottom. Call sort() to reverse rectangles with negative 137 | width() or height(). 138 | 139 | @return true if width() or height() are zero or negative 140 | */ 141 | bool isEmpty() const { 142 | // We write it as the NOT of a non-empty rect, so we will return true if any values 143 | // are NaN. 144 | return !(fLeft < fRight && fTop < fBottom); 145 | } 146 | 147 | /** Returns true if fLeft is equal to or less than fRight, or if fTop is equal 148 | to or less than fBottom. Call sort() to reverse rectangles with negative 149 | width() or height(). 150 | 151 | @return true if width() or height() are zero or positive 152 | */ 153 | bool isSorted() const { 154 | return fLeft <= fRight && fTop <= fBottom; 155 | } 156 | 157 | /** Returns left edge of Rect, if sorted. Call isSorted() to see if Rect is valid. 158 | Call sort() to reverse fLeft and fRight if needed. 159 | 160 | @return fLeft 161 | */ 162 | float x() const { 163 | return fLeft; 164 | } 165 | 166 | /** Returns top edge of Rect, if sorted. Call isEmpty() to see if Rect may be invalid, 167 | and sort() to reverse fTop and fBottom if needed. 168 | 169 | @return fTop 170 | */ 171 | float y() const { 172 | return fTop; 173 | } 174 | 175 | /** Returns left edge of Rect, if sorted. Call isSorted() to see if Rect is valid. 176 | Call sort() to reverse fLeft and fRight if needed. 177 | 178 | @return fLeft 179 | */ 180 | float left() const { 181 | return fLeft; 182 | } 183 | 184 | /** Returns top edge of Rect, if sorted. Call isEmpty() to see if Rect may be invalid, 185 | and sort() to reverse fTop and fBottom if needed. 186 | 187 | @return fTop 188 | */ 189 | float top() const { 190 | return fTop; 191 | } 192 | 193 | /** Returns right edge of Rect, if sorted. Call isSorted() to see if Rect is valid. 194 | Call sort() to reverse fLeft and fRight if needed. 195 | 196 | @return fRight 197 | */ 198 | float right() const { 199 | return fRight; 200 | } 201 | 202 | /** Returns bottom edge of Rect, if sorted. Call isEmpty() to see if Rect may be invalid, 203 | and sort() to reverse fTop and fBottom if needed. 204 | 205 | @return fBottom 206 | */ 207 | float bottom() const { 208 | return fBottom; 209 | } 210 | 211 | /** Returns span on the x-axis. This does not check if Rect is sorted, or if 212 | result fits in 32-bit float; result may be negative or infinity. 213 | 214 | @return fRight minus fLeft 215 | */ 216 | float width() const { 217 | return fRight - fLeft; 218 | } 219 | 220 | /** Returns span on the y-axis. This does not check if Rect is sorted, or if 221 | result fits in 32-bit float; result may be negative or infinity. 222 | 223 | @return fBottom minus fTop 224 | */ 225 | float height() const { 226 | return fBottom - fTop; 227 | } 228 | 229 | /** Returns average of left edge and right edge. Result does not change if Rect 230 | is sorted. Result may overflow to infinity if Rect is far from the origin. 231 | 232 | @return midpoint in x 233 | */ 234 | float centerX() const { 235 | // don't use floatHalf(fLeft + fBottom) as that might overflow before the 0.5 236 | return 0.5f * (fLeft) + 0.5f * (fRight); 237 | } 238 | 239 | /** Returns average of top edge and bottom edge. Result does not change if Rect 240 | is sorted. 241 | 242 | @return midpoint in y 243 | */ 244 | float centerY() const { 245 | // don't use floatHalf(fTop + fBottom) as that might overflow before the 0.5 246 | return 0.5f * (fTop) + 0.5f * (fBottom); 247 | } 248 | 249 | /** Sets Rect to (0, 0, 0, 0). 250 | 251 | Many other rectangles are empty; if left is equal to or greater than right, 252 | or if top is equal to or greater than bottom. Setting all members to zero 253 | is a convenience, but does not designate a special empty rectangle. 254 | */ 255 | void setEmpty() { 256 | *this = MakeEmpty(); 257 | } 258 | 259 | /** Sets Rect to (left, top, right, bottom). 260 | left and right are not sorted; left is not necessarily less than right. 261 | top and bottom are not sorted; top is not necessarily less than bottom. 262 | 263 | @param left stored in fLeft 264 | @param top stored in fTop 265 | @param right stored in fRight 266 | @param bottom stored in fBottom 267 | */ 268 | void set(float left, float top, float right, float bottom) { 269 | fLeft = left; 270 | fTop = top; 271 | fRight = right; 272 | fBottom = bottom; 273 | } 274 | 275 | /** Sets Rect to (left, top, right, bottom). 276 | left and right are not sorted; left is not necessarily less than right. 277 | top and bottom are not sorted; top is not necessarily less than bottom. 278 | 279 | @param left stored in fLeft 280 | @param top stored in fTop 281 | @param right stored in fRight 282 | @param bottom stored in fBottom 283 | */ 284 | void setLTRB(float left, float top, float right, float bottom) { 285 | this->set(left, top, right, bottom); 286 | } 287 | 288 | /** Sets Rect to (left, top, right, bottom). 289 | All parameters are promoted from integer to scalar. 290 | left and right are not sorted; left is not necessarily less than right. 291 | top and bottom are not sorted; top is not necessarily less than bottom. 292 | 293 | @param left promoted to float and stored in fLeft 294 | @param top promoted to float and stored in fTop 295 | @param right promoted to float and stored in fRight 296 | @param bottom promoted to float and stored in fBottom 297 | */ 298 | void iset(int left, int top, int right, int bottom) { 299 | fLeft = (float)(left); 300 | fTop = (float)(top); 301 | fRight = (float)(right); 302 | fBottom = (float)(bottom); 303 | } 304 | 305 | /** Sets Rect to (0, 0, width, height). 306 | width and height may be zero or negative. width and height are promoted from 307 | integer to float, large values may lose precision. 308 | 309 | @param width promoted to float and stored in fRight 310 | @param height promoted to float and stored in fBottom 311 | */ 312 | void isetWH(int width, int height) { 313 | fLeft = fTop = 0; 314 | fRight = (float)(width); 315 | fBottom = (float)(height); 316 | } 317 | 318 | /** Sets Rect to (x, y, x + width, y + height). Does not validate input; 319 | width or height may be negative. 320 | 321 | @param x stored in fLeft 322 | @param y stored in fTop 323 | @param width added to x and stored in fRight 324 | @param height added to y and stored in fBottom 325 | */ 326 | void setXYWH(float x, float y, float width, float height) { 327 | fLeft = x; 328 | fTop = y; 329 | fRight = x + width; 330 | fBottom = y + height; 331 | } 332 | 333 | /** Sets Rect to (0, 0, width, height). Does not validate input; 334 | width or height may be negative. 335 | 336 | @param width stored in fRight 337 | @param height stored in fBottom 338 | */ 339 | void setWH(float width, float height) { 340 | fLeft = 0; 341 | fTop = 0; 342 | fRight = width; 343 | fBottom = height; 344 | } 345 | 346 | /** Returns Rect offset by (dx, dy). 347 | 348 | If dx is negative, Rect returned is moved to the left. 349 | If dx is positive, Rect returned is moved to the right. 350 | If dy is negative, Rect returned is moved upward. 351 | If dy is positive, Rect returned is moved downward. 352 | 353 | @param dx added to fLeft and fRight 354 | @param dy added to fTop and fBottom 355 | @return Rect offset on axes, with original width and height 356 | */ 357 | Rect makeOffset(float dx, float dy) const { 358 | return MakeLTRB(fLeft + dx, fTop + dy, fRight + dx, fBottom + dy); 359 | } 360 | 361 | /** Returns Rect, inset by (dx, dy). 362 | 363 | If dx is negative, Rect returned is wider. 364 | If dx is positive, Rect returned is narrower. 365 | If dy is negative, Rect returned is taller. 366 | If dy is positive, Rect returned is shorter. 367 | 368 | @param dx added to fLeft and subtracted from fRight 369 | @param dy added to fTop and subtracted from fBottom 370 | @return Rect inset symmetrically left and right, top and bottom 371 | */ 372 | Rect makeInset(float dx, float dy) const { 373 | return MakeLTRB(fLeft + dx, fTop + dy, fRight - dx, fBottom - dy); 374 | } 375 | 376 | /** Returns Rect, outset by (dx, dy). 377 | 378 | If dx is negative, Rect returned is narrower. 379 | If dx is positive, Rect returned is wider. 380 | If dy is negative, Rect returned is shorter. 381 | If dy is positive, Rect returned is taller. 382 | 383 | @param dx subtracted to fLeft and added from fRight 384 | @param dy subtracted to fTop and added from fBottom 385 | @return Rect outset symmetrically left and right, top and bottom 386 | */ 387 | Rect makeOutset(float dx, float dy) const { 388 | return MakeLTRB(fLeft - dx, fTop - dy, fRight + dx, fBottom + dy); 389 | } 390 | 391 | /** Offsets Rect by adding dx to fLeft, fRight; and by adding dy to fTop, fBottom. 392 | 393 | If dx is negative, moves Rect to the left. 394 | If dx is positive, moves Rect to the right. 395 | If dy is negative, moves Rect upward. 396 | If dy is positive, moves Rect downward. 397 | 398 | @param dx offset added to fLeft and fRight 399 | @param dy offset added to fTop and fBottom 400 | */ 401 | void offset(float dx, float dy) { 402 | fLeft += dx; 403 | fTop += dy; 404 | fRight += dx; 405 | fBottom += dy; 406 | } 407 | 408 | /** Offsets Rect so that fLeft equals newX, and fTop equals newY. width and height 409 | are unchanged. 410 | 411 | @param newX stored in fLeft, preserving width() 412 | @param newY stored in fTop, preserving height() 413 | */ 414 | void offsetTo(float newX, float newY) { 415 | fRight += newX - fLeft; 416 | fBottom += newY - fTop; 417 | fLeft = newX; 418 | fTop = newY; 419 | } 420 | 421 | /** Insets Rect by (dx, dy). 422 | 423 | If dx is positive, makes Rect narrower. 424 | If dx is negative, makes Rect wider. 425 | If dy is positive, makes Rect shorter. 426 | If dy is negative, makes Rect taller. 427 | 428 | @param dx added to fLeft and subtracted from fRight 429 | @param dy added to fTop and subtracted from fBottom 430 | */ 431 | void inset(float dx, float dy) { 432 | fLeft += dx; 433 | fTop += dy; 434 | fRight -= dx; 435 | fBottom -= dy; 436 | } 437 | 438 | /** Outsets Rect by (dx, dy). 439 | 440 | If dx is positive, makes Rect wider. 441 | If dx is negative, makes Rect narrower. 442 | If dy is positive, makes Rect taller. 443 | If dy is negative, makes Rect shorter. 444 | 445 | @param dx subtracted to fLeft and added from fRight 446 | @param dy subtracted to fTop and added from fBottom 447 | */ 448 | void outset(float dx, float dy) { 449 | this->inset(-dx, -dy); 450 | } 451 | 452 | private: 453 | static bool Intersects(float al, float at, float ar, float ab, float bl, float bt, float br, float bb) { 454 | float L = std::max(al, bl); 455 | float R = std::min(ar, br); 456 | float T = std::max(at, bt); 457 | float B = std::min(ab, bb); 458 | return L < R && T < B; 459 | } 460 | 461 | public: 462 | /** Constructs Rect to intersect from (left, top, right, bottom). Does not sort 463 | construction. 464 | 465 | Returns true if Rect intersects construction. 466 | Returns false if either construction or Rect is empty, or do not intersect. 467 | 468 | @param left x-axis minimum of constructed Rect 469 | @param top y-axis minimum of constructed Rect 470 | @param right x-axis maximum of constructed Rect 471 | @param bottom y-axis maximum of constructed Rect 472 | @return true if construction and Rect have area in common 473 | */ 474 | bool intersects(float left, float top, float right, float bottom) const { 475 | return Intersects(fLeft, fTop, fRight, fBottom, left, top, right, bottom); 476 | } 477 | 478 | /** Returns true if Rect intersects r. 479 | Returns false if either r or Rect is empty, or do not intersect. 480 | 481 | @param r Rect to intersect 482 | @return true if r and Rect have area in common 483 | */ 484 | bool intersects(const Rect& r) const { 485 | return Intersects(fLeft, fTop, fRight, fBottom, r.fLeft, r.fTop, r.fRight, r.fBottom); 486 | } 487 | 488 | /** Returns true if a intersects b. 489 | Returns false if either a or b is empty, or do not intersect. 490 | 491 | @param a Rect to intersect 492 | @param b Rect to intersect 493 | @return true if a and b have area in common 494 | */ 495 | static bool Intersects(const Rect& a, const Rect& b) { 496 | return Intersects(a.fLeft, a.fTop, a.fRight, a.fBottom, b.fLeft, b.fTop, b.fRight, b.fBottom); 497 | } 498 | 499 | /** Sets Rect to the union of itself and r. 500 | 501 | Asserts if r is empty and SK_DEBUG is defined. 502 | If Rect is empty, sets Rect to r. 503 | 504 | May produce incorrect results if r is empty. 505 | 506 | @param r expansion Rect 507 | */ 508 | void joinNonEmptyArg(const Rect& r) { 509 | MNN_ASSERT(!r.isEmpty()); 510 | // if we are empty, just assign 511 | if (fLeft >= fRight || fTop >= fBottom) { 512 | *this = r; 513 | } else { 514 | this->joinPossiblyEmptyRect(r); 515 | } 516 | } 517 | 518 | /** Sets Rect to the union of itself and the construction. 519 | 520 | May produce incorrect results if Rect or r is empty. 521 | 522 | @param r expansion Rect 523 | */ 524 | void joinPossiblyEmptyRect(const Rect& r) { 525 | fLeft = std::min(fLeft, r.left()); 526 | fTop = std::min(fTop, r.top()); 527 | fRight = std::max(fRight, r.right()); 528 | fBottom = std::max(fBottom, r.bottom()); 529 | } 530 | 531 | /** Returns true if: fLeft <= x < fRight && fTop <= y < fBottom. 532 | Returns false if Rect is empty. 533 | 534 | @param x test Point x-coordinate 535 | @param y test Point y-coordinate 536 | @return true if (x, y) is inside Rect 537 | */ 538 | bool contains(float x, float y) const { 539 | return x >= fLeft && x < fRight && y >= fTop && y < fBottom; 540 | } 541 | 542 | /** Swaps fLeft and fRight if fLeft is greater than fRight; and swaps 543 | fTop and fBottom if fTop is greater than fBottom. Result may be empty; 544 | and width() and height() will be zero or positive. 545 | */ 546 | void sort() { 547 | using std::swap; 548 | if (fLeft > fRight) { 549 | swap(fLeft, fRight); 550 | } 551 | 552 | if (fTop > fBottom) { 553 | swap(fTop, fBottom); 554 | } 555 | } 556 | 557 | /** Returns Rect with fLeft and fRight swapped if fLeft is greater than fRight; and 558 | with fTop and fBottom swapped if fTop is greater than fBottom. Result may be empty; 559 | and width() and height() will be zero or positive. 560 | 561 | @return sorted Rect 562 | */ 563 | Rect makeSorted() const { 564 | return MakeLTRB(std::min(fLeft, fRight), std::min(fTop, fBottom), std::max(fLeft, fRight), 565 | std::max(fTop, fBottom)); 566 | } 567 | 568 | /** Returns pointer to first scalar in Rect, to treat it as an array with four 569 | entries. 570 | 571 | @return pointer to fLeft 572 | */ 573 | const float* asScalars() const { 574 | return &fLeft; 575 | } 576 | }; 577 | 578 | } // namespace CV 579 | } // namespace MNN 580 | #endif 581 | -------------------------------------------------------------------------------- /app/src/main/cpp/include/Tensor.hpp: -------------------------------------------------------------------------------- 1 | // 2 | // Tensor.hpp 3 | // MNN 4 | // 5 | // Created by MNN on 2018/08/14. 6 | // Copyright © 2018, Alibaba Group Holding Limited 7 | // 8 | 9 | #ifndef Tensor_hpp 10 | #define Tensor_hpp 11 | 12 | #include 13 | #include "HalideRuntime.h" 14 | #include "MNNDefine.h" 15 | 16 | namespace MNN { 17 | 18 | /** 19 | * data container. 20 | * data for host tensor is saved in `host` field. its memory is allocated malloc directly. 21 | * data for device tensor is saved in `deviceId` field. its memory is allocated by session's backend. 22 | * usually, device tensors are created by engine (like net, session). 23 | * meanwhile, host tensors could be created by engine or user. 24 | */ 25 | class MNN_PUBLIC Tensor { 26 | public: 27 | struct InsideDescribe; 28 | 29 | /** dimension type used to create tensor */ 30 | enum DimensionType { 31 | /** for tensorflow net type. uses NHWC as data format. */ 32 | TENSORFLOW, 33 | /** for caffe net type. uses NCHW as data format. */ 34 | CAFFE, 35 | /** for caffe net type. uses NC4HW4 as data format. */ 36 | CAFFE_C4 37 | }; 38 | 39 | /** handle type */ 40 | enum HandleDataType { 41 | /** default handle type */ 42 | HANDLE_NONE = 0, 43 | /** string handle type */ 44 | HANDLE_STRING = 1 45 | }; 46 | 47 | public: 48 | /** 49 | * @brief create a tensor with dimension size and type without acquire memory for data. 50 | * @param dimSize dimension size. 51 | * @param type dimension type. 52 | */ 53 | Tensor(int dimSize = 4, DimensionType type = CAFFE); 54 | 55 | /** 56 | * @brief create a tensor with same shape as given tensor. 57 | * @param tensor shape provider. 58 | * @param type dimension type. 59 | * @param allocMemory acquire memory for data or not. 60 | * @warning tensor data won't be copied. 61 | */ 62 | Tensor(const Tensor* tensor, DimensionType type = CAFFE, bool allocMemory = true); 63 | 64 | /** deinitializer */ 65 | ~Tensor(); 66 | 67 | private: 68 | // remove all assignment operator 69 | Tensor(const Tensor& tensor) = delete; 70 | Tensor(const Tensor&& tensor) = delete; 71 | Tensor& operator=(const Tensor&) = delete; 72 | Tensor& operator=(const Tensor&&) = delete; 73 | 74 | public: 75 | /** 76 | * @brief create tensor with shape, data type and dimension type. 77 | * @param shape tensor shape. 78 | * @param type data type. 79 | * @param dimType dimension type. 80 | * @return created tensor. 81 | * @warning memory for data won't be acquired. call backend's onAcquireBuffer to get memory ready. 82 | */ 83 | static Tensor* createDevice(const std::vector& shape, halide_type_t type, DimensionType dimType = TENSORFLOW); 84 | 85 | /** 86 | * @brief create tensor with shape and dimension type. data type is represented by `T`. 87 | * @param shape tensor shape. 88 | * @param dimType dimension type. 89 | * @return created tensor. 90 | * @warning memory for data won't be acquired. call backend's onAcquireBuffer to get memory ready. 91 | */ 92 | template 93 | static Tensor* createDevice(const std::vector& shape, DimensionType dimType = TENSORFLOW) { 94 | return createDevice(shape, halide_type_of(), dimType); 95 | } 96 | 97 | /** 98 | * @brief create tensor with shape, data type, data and dimension type. 99 | * @param shape tensor shape. 100 | * @param type data type. 101 | * @param data data to save. 102 | * @param dimType dimension type. 103 | * @return created tensor. 104 | */ 105 | static Tensor* create(const std::vector& shape, halide_type_t type, void* data = NULL, 106 | DimensionType dimType = TENSORFLOW); 107 | 108 | /** 109 | * @brief create tensor with shape, data and dimension type. data type is represented by `T`. 110 | * @param shape tensor shape. 111 | * @param data data to save. 112 | * @param dimType dimension type. 113 | * @return created tensor. 114 | */ 115 | template 116 | static Tensor* create(const std::vector& shape, void* data = NULL, DimensionType dimType = TENSORFLOW) { 117 | return create(shape, halide_type_of(), data, dimType); 118 | } 119 | 120 | public: 121 | /** 122 | * @brief for DEVICE tensor, copy data from given host tensor. 123 | * @param hostTensor host tensor, the data provider. 124 | * @return true for DEVICE tensor, and false for HOST tensor. 125 | */ 126 | bool copyFromHostTensor(const Tensor* hostTensor); 127 | 128 | /** 129 | * @brief for DEVICE tensor, copy data to given host tensor. 130 | * @param hostTensor host tensor, the data consumer. 131 | * @return true for DEVICE tensor, and false for HOST tensor. 132 | */ 133 | bool copyToHostTensor(Tensor* hostTensor) const; 134 | 135 | /** 136 | * @brief create HOST tensor from DEVICE tensor, with or without data copying. 137 | * @param deviceTensor given device tensor. 138 | * @param copyData copy data or not. 139 | * @return created host tensor. 140 | */ 141 | static Tensor* createHostTensorFromDevice(const Tensor* deviceTensor, bool copyData = true); 142 | 143 | public: 144 | const halide_buffer_t& buffer() const { 145 | return mBuffer; 146 | } 147 | halide_buffer_t& buffer() { 148 | return mBuffer; 149 | } 150 | 151 | /** 152 | * @brief get dimension type. 153 | * @return dimension type. 154 | */ 155 | DimensionType getDimensionType() const; 156 | 157 | /** 158 | * @brief handle data type. used when data type code is halide_type_handle. 159 | * @return handle data type. 160 | */ 161 | HandleDataType getHandleDataType() const; 162 | 163 | /** 164 | * @brief set data type. 165 | * @param type data type defined in 'Type_generated.h'. 166 | */ 167 | void setType(int type); 168 | 169 | /** 170 | * @brief get data type. 171 | * @return data type. 172 | */ 173 | inline halide_type_t getType() const { 174 | return mBuffer.type; 175 | } 176 | 177 | /** 178 | * @brief visit host memory, data type is represented by `T`. 179 | * @return data point in `T` type. 180 | */ 181 | template 182 | T* host() const { 183 | return (T*)mBuffer.host; 184 | } 185 | 186 | /** 187 | * @brief visit device memory. 188 | * @return device data ID. what the ID means varies between backends. 189 | */ 190 | uint64_t deviceId() const { 191 | return mBuffer.device; 192 | } 193 | 194 | public: 195 | int dimensions() const { 196 | return mBuffer.dimensions; 197 | } 198 | 199 | /** 200 | * @brief get all dimensions' extent. 201 | * @return dimensions' extent. 202 | */ 203 | std::vector shape() const; 204 | 205 | /** 206 | * @brief calculate number of bytes needed to store data taking reordering flag into account. 207 | * @return bytes needed to store data 208 | */ 209 | int size() const; 210 | 211 | /** 212 | * @brief calculate number of elements needed to store data taking reordering flag into account. 213 | * @return elements needed to store data 214 | */ 215 | inline int elementSize() const { 216 | return size() / mBuffer.type.bytes(); 217 | } 218 | 219 | public: 220 | inline int width() const { 221 | if (getDimensionType() == TENSORFLOW) { 222 | return mBuffer.dim[2].extent; 223 | } 224 | 225 | return mBuffer.dim[3].extent; 226 | } 227 | inline int height() const { 228 | if (getDimensionType() == TENSORFLOW) { 229 | return mBuffer.dim[1].extent; 230 | } 231 | return mBuffer.dim[2].extent; 232 | } 233 | inline int channel() const { 234 | if (getDimensionType() == TENSORFLOW) { 235 | return mBuffer.dim[3].extent; 236 | } 237 | return mBuffer.dim[1].extent; 238 | } 239 | inline int batch() const { 240 | return mBuffer.dim[0].extent; 241 | } 242 | 243 | // visit dimension's extent & stride 244 | inline int stride(int index) const { 245 | return mBuffer.dim[index].stride; 246 | } 247 | inline int length(int index) const { 248 | return mBuffer.dim[index].extent; 249 | } 250 | inline void setStride(int index, int stride) { 251 | mBuffer.dim[index].stride = stride; 252 | } 253 | inline void setLength(int index, int length) { 254 | mBuffer.dim[index].extent = length; 255 | } 256 | 257 | public: 258 | /** 259 | * @brief print tensor data. for DEBUG use only. 260 | */ 261 | void print() const; 262 | 263 | private: 264 | halide_buffer_t mBuffer; 265 | struct InsideDescribe* mDescribe; 266 | 267 | private: 268 | friend class TensorUtils; 269 | }; 270 | } // namespace MNN 271 | 272 | #endif /* Tensor_hpp */ 273 | -------------------------------------------------------------------------------- /app/src/main/cpp/include/revertMNNModel.hpp: -------------------------------------------------------------------------------- 1 | // 2 | // revertMNNModel.hpp 3 | // MNN 4 | // 5 | // Created by MNN on 2019/01/31. 6 | // Copyright © 2018, Alibaba Group Holding Limited 7 | // 8 | 9 | #ifndef REVERTMNNMODEL_HPP 10 | #define REVERTMNNMODEL_HPP 11 | 12 | #include "mnn/MNN_generated.h" 13 | 14 | class Revert { 15 | public: 16 | Revert(const char* originalModelFileName); 17 | ~Revert(); 18 | void* getBuffer() const; 19 | const size_t getBufferSize() const; 20 | void initialize(); 21 | static float getRandValue(); 22 | 23 | private: 24 | Revert(); 25 | std::unique_ptr mMNNNet; 26 | size_t mBufferSize; 27 | std::shared_ptr mBuffer; 28 | void randStart(); 29 | void packMNNNet(); 30 | }; 31 | 32 | #endif // REVERTMNNMODEL_HPP 33 | -------------------------------------------------------------------------------- /app/src/main/cpp/net.cpp: -------------------------------------------------------------------------------- 1 | #include "net.h" 2 | #define TAG "net" 3 | #define LOGD(...) __android_log_print(ANDROID_LOG_DEBUG, TAG, __VA_ARGS__) 4 | Inference_engine::Inference_engine() 5 | { } 6 | 7 | Inference_engine::~Inference_engine() 8 | { 9 | if ( netPtr != NULL ) 10 | { 11 | if ( sessionPtr != NULL) 12 | { 13 | netPtr->releaseSession(sessionPtr); 14 | sessionPtr = NULL; 15 | } 16 | 17 | delete netPtr; 18 | netPtr = NULL; 19 | } 20 | } 21 | 22 | int Inference_engine::load_param(std::string & file, int num_thread) 23 | { 24 | 25 | if (!file.empty()) 26 | { 27 | 28 | if (file.find(".mnn") != std::string::npos) 29 | { 30 | 31 | netPtr = MNN::Interpreter::createFromFile(file.c_str()); 32 | if (nullptr == netPtr) return -1; 33 | 34 | MNN::ScheduleConfig sch_config; 35 | //sch_config.type = (MNNForwardType)MNN_FORWARD_OPENCL; 36 | sch_config.type = (MNNForwardType)MNN_FORWARD_CPU; 37 | if ( num_thread > 0 )sch_config.numThread = num_thread; 38 | 39 | MNN::BackendConfig backendConfig; 40 | 41 | backendConfig.precision = (MNN::BackendConfig::PrecisionMode)2; 42 | sch_config.backendConfig = &backendConfig; 43 | 44 | sessionPtr = netPtr->createSession(sch_config); 45 | if (nullptr == sessionPtr) return -1; 46 | } 47 | else 48 | { 49 | return -1; 50 | } 51 | } 52 | 53 | return 0; 54 | } 55 | 56 | int Inference_engine::set_params(int srcType, int dstType, 57 | float *mean, float *scale) 58 | { 59 | config.destFormat = (MNN::CV::ImageFormat)dstType; 60 | config.sourceFormat = (MNN::CV::ImageFormat)srcType; 61 | 62 | ::memcpy(config.mean, mean, 3 * sizeof(float)); 63 | ::memcpy(config.normal, scale, 3 * sizeof(float)); 64 | 65 | config.filterType = (MNN::CV::Filter)(1); 66 | config.wrap = (MNN::CV::Wrap)(2); 67 | 68 | return 0; 69 | } 70 | 71 | // infer 72 | int Inference_engine::infer_img(unsigned char *data, int width, int height, int channel, int dstw, int dsth, Inference_engine_tensor& out) 73 | { 74 | MNN::Tensor* tensorPtr = netPtr->getSessionInput(sessionPtr, nullptr); 75 | MNN::CV::Matrix transform; 76 | 77 | int h = height; 78 | int w = width; 79 | int c = channel; 80 | // auto resize for full conv network. 81 | bool auto_resize = false; 82 | if ( !auto_resize ) 83 | { 84 | std::vectordims = { 1, c, dsth, dstw }; 85 | netPtr->resizeTensor(tensorPtr, dims); 86 | netPtr->resizeSession(sessionPtr); 87 | } 88 | 89 | transform.postScale(1.0f/dstw, 1.0f/dsth); 90 | transform.postScale(w, h); 91 | 92 | std::unique_ptr process(MNN::CV::ImageProcess::create(config.sourceFormat, config.destFormat, config.mean, 3, config.normal, 3)); 93 | 94 | process->setMatrix(transform); 95 | 96 | process->convert(data, w, h, w*c, tensorPtr); 97 | netPtr->runSession(sessionPtr); 98 | 99 | for (int i = 0; i < out.layer_name.size(); i++) 100 | { 101 | const char* layer_name = NULL; 102 | if( strcmp(out.layer_name[i].c_str(), "") != 0) 103 | { 104 | layer_name = out.layer_name[i].c_str(); 105 | } 106 | MNN::Tensor* tensorOutPtr = netPtr->getSessionOutput(sessionPtr, layer_name); 107 | 108 | std::vector shape = tensorOutPtr->shape(); 109 | 110 | auto tensor = reinterpret_cast(tensorOutPtr); 111 | 112 | std::unique_ptr hostTensor(new MNN::Tensor(tensor, tensor->getDimensionType(), true)); 113 | tensor->copyToHostTensor(hostTensor.get()); 114 | tensor = hostTensor.get(); 115 | 116 | auto size = tensorOutPtr->elementSize(); 117 | std::shared_ptr destPtr(new float[size * sizeof(float)]); 118 | 119 | ::memcpy(destPtr.get(), tensorOutPtr->host(), size * sizeof(float)); 120 | 121 | out.out_feat.push_back(destPtr); 122 | } 123 | 124 | return 0; 125 | } 126 | -------------------------------------------------------------------------------- /app/src/main/cpp/net.h: -------------------------------------------------------------------------------- 1 | #ifndef _NET_H_ 2 | #define _NET_H_ 3 | 4 | #include 5 | #include 6 | #include 7 | #include 8 | #include 9 | #include 10 | 11 | class Inference_engine_tensor 12 | { 13 | public: 14 | Inference_engine_tensor() 15 | { } 16 | 17 | ~Inference_engine_tensor() 18 | { } 19 | 20 | void add_name(std::string &layer) 21 | { 22 | layer_name.push_back(layer); 23 | } 24 | std::shared_ptr score(int idx) 25 | { 26 | return out_feat[idx]; 27 | } 28 | 29 | public: 30 | std::vector layer_name; 31 | std::vector> out_feat; 32 | }; 33 | 34 | class Inference_engine 35 | { 36 | public: 37 | Inference_engine(); 38 | ~Inference_engine(); 39 | 40 | int load_param(std::string &file, int num_thread = 1); 41 | int set_params(int inType, int outType, float *mean, float *scale); 42 | int infer_img(unsigned char* data, int w, int h, int channel, int dstw, int dsth, Inference_engine_tensor& out); 43 | private: 44 | MNN::Interpreter* netPtr; 45 | MNN::Session* sessionPtr; 46 | MNN::CV::ImageProcess::Config config; 47 | 48 | }; 49 | #endif -------------------------------------------------------------------------------- /app/src/main/java/com/facesdk/FaceSDKNative.java: -------------------------------------------------------------------------------- 1 | package com.facesdk; 2 | 3 | public class FaceSDKNative { 4 | //SDK初始化 5 | public native boolean FaceDetectionModelInit(String faceDetectionModelPath); 6 | 7 | //SDK人脸检测接口 8 | public native int[] FaceDetect(byte[] imageDate, int imageWidth , int imageHeight, int imageChannel); 9 | 10 | //SDK销毁 11 | public native boolean FaceDetectionModelUnInit(); 12 | 13 | static { 14 | System.loadLibrary("facedetect"); 15 | } 16 | 17 | } 18 | -------------------------------------------------------------------------------- /app/src/main/java/com/facesdk/MainActivity.java: -------------------------------------------------------------------------------- 1 | package com.facesdk; 2 | 3 | import android.Manifest; 4 | import android.app.Activity; 5 | import android.content.Intent; 6 | import android.content.pm.PackageManager; 7 | import android.graphics.Bitmap; 8 | import android.graphics.BitmapFactory; 9 | import android.graphics.Canvas; 10 | import android.graphics.Color; 11 | import android.graphics.Paint; 12 | import android.net.Uri; 13 | import android.os.Bundle; 14 | import android.os.Environment; 15 | import android.support.v4.app.ActivityCompat; 16 | import android.util.Log; 17 | import android.view.View; 18 | import android.widget.Button; 19 | import android.widget.ImageView; 20 | import android.widget.TextView; 21 | 22 | import com.google.android.gms.appindexing.AppIndex; 23 | import com.google.android.gms.common.api.GoogleApiClient; 24 | 25 | import java.io.File; 26 | import java.io.FileNotFoundException; 27 | import java.io.FileOutputStream; 28 | import java.io.IOException; 29 | import java.io.InputStream; 30 | import java.nio.ByteBuffer; 31 | 32 | import static android.content.ContentValues.TAG; 33 | 34 | public class MainActivity extends Activity { 35 | private static final int SELECT_IMAGE = 1; 36 | 37 | private TextView infoResult; 38 | private ImageView imageView; 39 | private Bitmap yourSelectedImage = null; 40 | 41 | private FaceSDKNative faceSDKNative = new FaceSDKNative(); 42 | /** 43 | * ATTENTION: This was auto-generated to implement the App Indexing API. 44 | * See https://g.co/AppIndexing/AndroidStudio for more information. 45 | */ 46 | private GoogleApiClient client; 47 | 48 | //Check Permissions 49 | private static final int REQUEST_CODE_PERMISSION = 2; 50 | private static String[] PERMISSIONS_REQ = { 51 | Manifest.permission.READ_EXTERNAL_STORAGE, 52 | Manifest.permission.WRITE_EXTERNAL_STORAGE, 53 | Manifest.permission.CAMERA 54 | }; 55 | 56 | @Override 57 | protected void onDestroy() { 58 | super.onDestroy(); 59 | faceSDKNative.FaceDetectionModelUnInit(); 60 | } 61 | 62 | /** 63 | * Called when the activity is first created. 64 | */ 65 | @Override 66 | public void onCreate(Bundle savedInstanceState) { 67 | super.onCreate(savedInstanceState); 68 | setContentView(R.layout.main); 69 | 70 | // For API 23+ you need to request the read/write permissions even if they are already in your manifest. 71 | int currentapiVersion = android.os.Build.VERSION.SDK_INT; 72 | 73 | //if (currentapiVersion >= Build.VERSION_CODES.M) { 74 | verifyPermissions(this); 75 | //} 76 | 77 | //copy model 78 | try { 79 | //RFB-320-quant-ADMM-32 80 | copyBigDataToSD("RFB-320.mnn"); 81 | copyBigDataToSD("RFB-320-quant-ADMM-32.mnn"); 82 | copyBigDataToSD("RFB-320-quant-KL-5792.mnn"); 83 | copyBigDataToSD("slim-320.mnn"); 84 | copyBigDataToSD("slim-320-quant-ADMM-50.mnn"); 85 | 86 | } catch (IOException e) { 87 | e.printStackTrace(); 88 | } 89 | 90 | File sdDir = Environment.getExternalStorageDirectory();//get model store dir 91 | String sdPath = sdDir.toString() + "/facesdk/"; 92 | faceSDKNative.FaceDetectionModelInit(sdPath); 93 | 94 | 95 | infoResult = (TextView) findViewById(R.id.infoResult); 96 | imageView = (ImageView) findViewById(R.id.imageView); 97 | 98 | Button buttonImage = (Button) findViewById(R.id.buttonImage); 99 | buttonImage.setOnClickListener(new View.OnClickListener() { 100 | @Override 101 | public void onClick(View arg0) { 102 | Intent i = new Intent(Intent.ACTION_PICK); 103 | i.setType("image/*"); 104 | startActivityForResult(i, SELECT_IMAGE); 105 | } 106 | }); 107 | 108 | Button buttonDetect = (Button) findViewById(R.id.buttonDetect); 109 | buttonDetect.setOnClickListener(new View.OnClickListener() { 110 | @Override 111 | public void onClick(View arg0) { 112 | if (yourSelectedImage == null) 113 | return; 114 | int width = yourSelectedImage.getWidth(); 115 | int height = yourSelectedImage.getHeight(); 116 | byte[] imageDate = getPixelsRGBA(yourSelectedImage); 117 | 118 | long timeDetectFace = System.currentTimeMillis(); 119 | //do FaceDetect 120 | int faceInfo[] = faceSDKNative.FaceDetect(imageDate, width, height,4); 121 | timeDetectFace = System.currentTimeMillis() - timeDetectFace; 122 | 123 | //Get Results 124 | if (faceInfo.length>1) { 125 | int faceNum = faceInfo[0]; 126 | infoResult.setText("detect time:"+timeDetectFace+"ms, face number:" + faceNum); 127 | Log.i(TAG, "detect time:"+timeDetectFace); 128 | Log.i(TAG, "face num:" + faceNum ); 129 | 130 | Bitmap drawBitmap = yourSelectedImage.copy(Bitmap.Config.ARGB_8888, true); 131 | for (int i=0; i 0) { 241 | myOutput.write(buffer, 0, length); 242 | length = myInput.read(buffer); 243 | } 244 | myOutput.flush(); 245 | myInput.close(); 246 | myOutput.close(); 247 | Log.i(TAG, "end copy file " + strOutFileName); 248 | 249 | } 250 | 251 | /** 252 | * Checks if the app has permission to write to device storage or open camera 253 | * If the app does not has permission then the user will be prompted to grant permissions 254 | * 255 | * @param activity 256 | */ 257 | private static boolean verifyPermissions(Activity activity) { 258 | // Check if we have write permission 259 | int write_permission = ActivityCompat.checkSelfPermission(activity, Manifest.permission.WRITE_EXTERNAL_STORAGE); 260 | int read_permission = ActivityCompat.checkSelfPermission(activity, Manifest.permission.READ_EXTERNAL_STORAGE); 261 | int camera_permission = ActivityCompat.checkSelfPermission(activity, Manifest.permission.CAMERA); 262 | 263 | if (write_permission != PackageManager.PERMISSION_GRANTED || 264 | read_permission != PackageManager.PERMISSION_GRANTED || 265 | camera_permission != PackageManager.PERMISSION_GRANTED) { 266 | // We don't have permission so prompt the user 267 | ActivityCompat.requestPermissions( 268 | activity, 269 | PERMISSIONS_REQ, 270 | REQUEST_CODE_PERMISSION 271 | ); 272 | return false; 273 | } else { 274 | return true; 275 | } 276 | } 277 | 278 | } 279 | -------------------------------------------------------------------------------- /app/src/main/jniLibs/arm64-v8a/libMNN.so: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/jackweiwang/Android-FaceDetection-UltraNet-MNN/6f4e5e0184bbdfe6ed6bcf9b993d393bc03f18a2/app/src/main/jniLibs/arm64-v8a/libMNN.so -------------------------------------------------------------------------------- /app/src/main/jniLibs/arm64-v8a/libMNN_CL.so: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/jackweiwang/Android-FaceDetection-UltraNet-MNN/6f4e5e0184bbdfe6ed6bcf9b993d393bc03f18a2/app/src/main/jniLibs/arm64-v8a/libMNN_CL.so -------------------------------------------------------------------------------- /app/src/main/jniLibs/arm64-v8a/libMNN_Express.so: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/jackweiwang/Android-FaceDetection-UltraNet-MNN/6f4e5e0184bbdfe6ed6bcf9b993d393bc03f18a2/app/src/main/jniLibs/arm64-v8a/libMNN_Express.so -------------------------------------------------------------------------------- /app/src/main/jniLibs/armeabi-v7a/libMNN.so: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/jackweiwang/Android-FaceDetection-UltraNet-MNN/6f4e5e0184bbdfe6ed6bcf9b993d393bc03f18a2/app/src/main/jniLibs/armeabi-v7a/libMNN.so -------------------------------------------------------------------------------- /app/src/main/jniLibs/armeabi-v7a/libMNN_CL.so: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/jackweiwang/Android-FaceDetection-UltraNet-MNN/6f4e5e0184bbdfe6ed6bcf9b993d393bc03f18a2/app/src/main/jniLibs/armeabi-v7a/libMNN_CL.so -------------------------------------------------------------------------------- /app/src/main/jniLibs/armeabi-v7a/libMNN_Express.so: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/jackweiwang/Android-FaceDetection-UltraNet-MNN/6f4e5e0184bbdfe6ed6bcf9b993d393bc03f18a2/app/src/main/jniLibs/armeabi-v7a/libMNN_Express.so -------------------------------------------------------------------------------- /app/src/main/res/layout/activity_main.xml: -------------------------------------------------------------------------------- 1 | 2 | 12 | 13 | 18 | 19 | -------------------------------------------------------------------------------- /app/src/main/res/layout/main.xml: -------------------------------------------------------------------------------- 1 | 2 | 6 | 7 | 11 | 12 |