├── CMakeLists.txt ├── LICENSE ├── MNN └── include │ ├── AutoTime.hpp │ ├── ErrorCode.hpp │ ├── HalideRuntime.h │ ├── ImageProcess.hpp │ ├── Interpreter.hpp │ ├── MNNDefine.h │ ├── MNNForwardType.h │ ├── MNNSharedContext.h │ ├── Matrix.h │ ├── Rect.h │ └── Tensor.hpp ├── README.md ├── data ├── demo.jpg ├── demo_res.jpg ├── selfie.jpg ├── selfie_res.jpg ├── test_5.jpg └── test_5_res.jpg ├── models ├── symbol_10_320_20L_5scales_v2_deploy.mnn └── symbol_10_560_25L_8scales_v1_deploy.mnn └── src ├── MNN_LFFD.cpp ├── MNN_LFFD.h └── test.cpp /CMakeLists.txt: -------------------------------------------------------------------------------- 1 | 2 | #1.cmake verson 3 | cmake_minimum_required(VERSION 2.8) 4 | 5 | #2.project name 6 | project(test C CXX) 7 | 8 | #opencv 9 | find_package(OpenCV REQUIRED) 10 | 11 | #3.set environment variable 12 | set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -std=c++11") 13 | 14 | #4.mnn include 15 | include_directories(${CMAKE_CURRENT_LIST_DIR}/MNN/include) 16 | 17 | # mnn lib 18 | add_library( MNN SHARED IMPORTED ) 19 | set_target_properties( 20 | MNN 21 | PROPERTIES IMPORTED_LOCATION 22 | ${CMAKE_CURRENT_LIST_DIR}/MNN/lib/libMNN.so 23 | ) 24 | 25 | #6.source directory 26 | file(GLOB TEST_SRC ${CMAKE_CURRENT_LIST_DIR}/src/*.h 27 | ${CMAKE_CURRENT_LIST_DIR}/src/*.cpp) 28 | set(TEST_COMPILE_CODE ${TEST_SRC}) 29 | 30 | 31 | add_executable(test ${TEST_COMPILE_CODE}) 32 | 33 | target_link_libraries(test 34 | MNN 35 | ${OpenCV_LIBS} 36 | ) -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | MIT License 2 | 3 | Copyright (c) 2020 SyGoing 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 | -------------------------------------------------------------------------------- /MNN/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 | -------------------------------------------------------------------------------- /MNN/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 | -------------------------------------------------------------------------------- /MNN/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 | -------------------------------------------------------------------------------- /MNN/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 | -------------------------------------------------------------------------------- /MNN/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 | -------------------------------------------------------------------------------- /MNN/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 | #if defined(_MSC_VER) 54 | #ifdef BUILDING_DLL 55 | #define MNN_PUBLIC __declspec(dllexport) 56 | #else 57 | #define MNN_PUBLIC __declspec(dllimport) 58 | #endif 59 | #else 60 | #define MNN_PUBLIC __attribute__((visibility("default"))) 61 | #endif 62 | 63 | #endif /* MNNDefine_h */ 64 | -------------------------------------------------------------------------------- /MNN/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 | 13 | typedef enum { 14 | MNN_FORWARD_CPU = 0, 15 | 16 | /* 17 | Firtly find the first available backends not equal to CPU 18 | If no other backends, use cpu 19 | */ 20 | MNN_FORWARD_AUTO = 4, 21 | 22 | /*Hand write metal*/ 23 | MNN_FORWARD_METAL = 1, 24 | 25 | /*Use IOS's MPS instead of hand-write metal, Not Support yet*/ 26 | MNN_FORWARD_MPS = 2, 27 | 28 | /*Android / Common Device GPU API*/ 29 | MNN_FORWARD_OPENCL = 3, 30 | MNN_FORWARD_OPENGL = 6, 31 | MNN_FORWARD_VULKAN = 7, 32 | 33 | /*Android 8.1's NNAPI, Not Support yet*/ 34 | MNN_FORWARD_NN = 5, 35 | 36 | /*User can use API from Backend.hpp to add or search Backend*/ 37 | MNN_FORWARD_USER_0 = 8, 38 | MNN_FORWARD_USER_1 = 9, 39 | MNN_FORWARD_USER_2 = 10, 40 | MNN_FORWARD_USER_3 = 11, 41 | 42 | MNN_FORWARD_ALL 43 | } MNNForwardType; 44 | #ifdef __cplusplus 45 | namespace MNN { 46 | struct BackendConfig { 47 | enum MemoryMode { Memory_Normal = 0, Memory_High, Memory_Low }; 48 | 49 | MemoryMode memory = Memory_Normal; 50 | 51 | enum PowerMode { Power_Normal = 0, Power_High, Power_Low }; 52 | 53 | PowerMode power = Power_Normal; 54 | 55 | enum PrecisionMode { Precision_Normal = 0, Precision_High, Precision_Low }; 56 | 57 | PrecisionMode precision = Precision_Normal; 58 | 59 | /** user defined context */ 60 | union { 61 | void* sharedContext = nullptr; 62 | size_t flags; // Valid for CPU Backend 63 | }; 64 | }; 65 | }; // namespace MNN 66 | #endif 67 | #endif /* MNNForwardType_h */ 68 | -------------------------------------------------------------------------------- /MNN/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 | -------------------------------------------------------------------------------- /MNN/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 | -------------------------------------------------------------------------------- /MNN/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 | -------------------------------------------------------------------------------- /MNN/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 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | 2 | ## the C++ implemententation of LFFD with MNN 3 | I have implemented the LFFD referring to the official python implementation 4 | 5 | paper:[LFFD: A Light and Fast Face Detector for Edge Devices](https://arxiv.org/abs/1904.10633) 6 | 7 | official github: [LFFD](https://github.com/YonghaoHe/A-Light-and-Fast-Face-Detector-for-Edge-Devices) 8 | 9 | My ncnn implementation is [here](https://github.com/SyGoing/LFFD-with-ncnn) 10 | 11 | My OpenVINO [implementation](https://github.com/SyGoing/LFFD-OpenVINO) 12 | ## some tips 13 | * You can set the input tensor shape smaller ,since you need to reduce the memory and accelerate the inference. 14 | * You can set the scale_num=8 to use another larger model. 15 | * I just test it on vs2019 PC and the result is correct compared to original implementation,you can use the code to another device such as android、RK3399、and so on. 16 | 17 | ## how to convert the original model to mnn 18 | The original mxnet model has merged the preporcess(means and norms) and the detection output tensor has been sliced with the mxnet slice op in the symbol ,which caused convert failure. 19 | so,you need to remove these ops ,in that way you can convert the model to onnx successfully.I will show you how to do that step by step, so when you train the model by yourself, 20 | you can convert to your own model to onnx , and do more things. 21 | 22 | * First ,follow the author's original github to build the devolopment environment. 23 | 24 | * Modify symbol_10_320_20L_5scales_v2.py (your_path/A-Light-and-Fast-Face-Detector-for-Edge-Devices\face_detection\symbol_farm) 25 | 26 | in function loss_branch,Note out(注释掉) the line 57(predict_score = mxnet.symbol.slice_axis(predict_score, axis=1, begin=0, end=1) 27 | 28 | in function get_net_symbol, Note out(注释掉)the line 99(data = (data - 127.5) / 127.5,preprocess). 29 | 30 | * Next,in this path , by doing "python symbol_10_320_20L_5scales_v2.py ",generate the symbol.json. symbol_10_560_25L_8scales_v1.py do the same thing . 31 | 32 | * To generate onnx model, cd your_path\A-Light-and-Fast-Face-Detector-for-Edge-Devices\face_detection\deploy_tensorrt 33 | python to_onnx.py 34 | by doing this, you can find the generated onnx model in your_path\A-Light-and-Fast-Face-Detector-for-Edge-Devices\face_detection\deploy_tensorrt\onnx_files 35 | 36 | * In the last, you can use the MNN's MNNConvert to convert the model. have fun! 37 | ## test 38 | * Follow the MNN official doc to compile the MNN lib 39 | * Put the lib in MNN folder (include and lib) 40 | * compile and test 41 | 42 | cd LFFD-MNN 43 | 44 | mkdir build 45 | 46 | cd build 47 | 48 | cmake .. 49 | 50 | make 51 | 52 | ./test ../models ../data/demo.jpg 53 | 54 | ## result show 55 | 56 | ![demo_res.jpg](https://raw.githubusercontent.com/SyGoing/LFFD-MNN/master/data/demo_res.jpg) 57 | 58 | ![selfie_res](https://raw.githubusercontent.com/SyGoing/LFFD-MNN/master/data/selfie_res.jpg) 59 | 60 | ![test_5_res](https://raw.githubusercontent.com/SyGoing/LFFD-MNN/master/data/test_5_res.jpg) 61 | 62 | ## TODO(you can refer this implementation to do more) 63 | - [x] MNN finished 64 | - [x] NCNN finished 65 | - [x] openvino demo: mxnet model-->onnx-->openvino 66 | - [x] TensorRT demo: mxnet model --> onnx-->trt engine(finished and coming soon) 67 | 68 | 69 | 70 | -------------------------------------------------------------------------------- /data/demo.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/SyGoing/LFFD-MNN/6ffa3cf86921e7b7cf7dfa5135466cabe0c5e17f/data/demo.jpg -------------------------------------------------------------------------------- /data/demo_res.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/SyGoing/LFFD-MNN/6ffa3cf86921e7b7cf7dfa5135466cabe0c5e17f/data/demo_res.jpg -------------------------------------------------------------------------------- /data/selfie.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/SyGoing/LFFD-MNN/6ffa3cf86921e7b7cf7dfa5135466cabe0c5e17f/data/selfie.jpg -------------------------------------------------------------------------------- /data/selfie_res.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/SyGoing/LFFD-MNN/6ffa3cf86921e7b7cf7dfa5135466cabe0c5e17f/data/selfie_res.jpg -------------------------------------------------------------------------------- /data/test_5.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/SyGoing/LFFD-MNN/6ffa3cf86921e7b7cf7dfa5135466cabe0c5e17f/data/test_5.jpg -------------------------------------------------------------------------------- /data/test_5_res.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/SyGoing/LFFD-MNN/6ffa3cf86921e7b7cf7dfa5135466cabe0c5e17f/data/test_5_res.jpg -------------------------------------------------------------------------------- /models/symbol_10_320_20L_5scales_v2_deploy.mnn: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/SyGoing/LFFD-MNN/6ffa3cf86921e7b7cf7dfa5135466cabe0c5e17f/models/symbol_10_320_20L_5scales_v2_deploy.mnn -------------------------------------------------------------------------------- /models/symbol_10_560_25L_8scales_v1_deploy.mnn: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/SyGoing/LFFD-MNN/6ffa3cf86921e7b7cf7dfa5135466cabe0c5e17f/models/symbol_10_560_25L_8scales_v1_deploy.mnn -------------------------------------------------------------------------------- /src/MNN_LFFD.cpp: -------------------------------------------------------------------------------- 1 | 2 | #include "MNN_LFFD.h" 3 | 4 | const float mean_vals[3] = { 127.5f, 127.5f, 127.5f }; 5 | const float norm_vals[3] = { 0.0078431373, 0.0078431373, 0.0078431373 }; 6 | 7 | LFFD::LFFD(const std::string& model_path, int scale_num, int num_thread_) 8 | { 9 | num_output_scales = scale_num; 10 | num_thread = num_thread_; 11 | outputTensors.resize(scale_num*2); 12 | if (num_output_scales == 5) { 13 | mnn_model_file = model_path+ "/symbol_10_320_20L_5scales_v2_deploy.mnn"; 14 | receptive_field_list = { 20, 40, 80, 160, 320 }; 15 | receptive_field_stride = { 4, 8, 16, 32, 64 }; 16 | bbox_small_list = { 10, 20, 40, 80, 160 }; 17 | bbox_large_list = { 20, 40, 80, 160, 320 }; 18 | receptive_field_center_start = { 3, 7, 15, 31, 63 }; 19 | 20 | for (int i = 0; i < receptive_field_list.size(); i++) { 21 | constant.push_back(receptive_field_list[i] / 2); 22 | } 23 | 24 | output_blob_names = { "softmax0","conv8_3_bbox", 25 | "softmax1","conv11_3_bbox", 26 | "softmax2","conv14_3_bbox", 27 | "softmax3","conv17_3_bbox", 28 | "softmax4","conv20_3_bbox" }; 29 | } 30 | else if (num_output_scales == 8) { 31 | mnn_model_file = model_path + "/symbol_10_560_25L_8scales_v1_deploy.mnn"; 32 | receptive_field_list = { 15, 20, 40, 70, 110, 250, 400, 560 }; 33 | receptive_field_stride = { 4, 4, 8, 8, 16, 32, 32, 32 }; 34 | bbox_small_list = { 10, 15, 20, 40, 70, 110, 250, 400 }; 35 | bbox_large_list = { 15, 20, 40, 70, 110, 250, 400, 560 }; 36 | receptive_field_center_start = { 3, 3, 7, 7, 15, 31, 31, 31 }; 37 | 38 | for (int i = 0; i < receptive_field_list.size(); i++) { 39 | constant.push_back(receptive_field_list[i] / 2); 40 | } 41 | 42 | output_blob_names = { "softmax0","conv8_3_bbox", 43 | "softmax1","conv10_3_bbox", 44 | "softmax2","conv13_3_bbox", 45 | "softmax3","conv15_3_bbox", 46 | "softmax4","conv18_3_bbox", 47 | "softmax5","conv21_3_bbox", 48 | "softmax6","conv23_3_bbox", 49 | "softmax7","conv25_3_bbox" }; 50 | } 51 | 52 | lffd = std::shared_ptr(MNN::Interpreter::createFromFile(mnn_model_file.c_str())); 53 | MNN::ScheduleConfig config; 54 | config.type = MNN_FORWARD_CPU; 55 | config.numThread = num_thread; 56 | 57 | MNN::BackendConfig backendConfig; 58 | backendConfig.precision = MNN::BackendConfig::Precision_High; 59 | backendConfig.power = MNN::BackendConfig::Power_High; 60 | config.backendConfig = &backendConfig; 61 | 62 | sess_lffd = lffd->createSession(config); 63 | input_tensor = lffd->getSessionInput(sess_lffd, NULL); 64 | for (int i = 0; i < output_blob_names.size(); i++) { 65 | outputTensors[i] = lffd->getSessionOutput(sess_lffd, output_blob_names[i].c_str()); 66 | } 67 | 68 | ::memcpy(img_config.mean, mean_vals, sizeof(mean_vals)); 69 | ::memcpy(img_config.normal, norm_vals, sizeof(norm_vals)); 70 | 71 | 72 | img_config.sourceFormat = (MNN::CV::ImageFormat)2; 73 | img_config.destFormat = (MNN::CV::ImageFormat)2; 74 | 75 | img_config.filterType = (MNN::CV::Filter)(2); 76 | img_config.wrap = (MNN::CV::Wrap)(2); 77 | 78 | } 79 | 80 | LFFD::~LFFD() 81 | { 82 | lffd->releaseModel(); 83 | lffd->releaseSession(sess_lffd); 84 | } 85 | 86 | int LFFD::detect(cv::Mat& img, std::vector& face_list, int resize_h, int resize_w, 87 | float score_threshold, float nms_threshold, int top_k, std::vector skip_scale_branch_list) 88 | { 89 | 90 | if (img.empty()) { 91 | std::cout << "image is empty ,please check!" << std::endl; 92 | return -1; 93 | } 94 | 95 | image_h = img.rows; 96 | image_w = img.cols; 97 | 98 | cv::Mat in; 99 | cv::resize(img,in,cv::Size(resize_w,resize_h)); 100 | float ratio_w=(float)image_w/ resize_w; 101 | float ratio_h=(float)image_h/ resize_h; 102 | 103 | //resize session and input tensor 104 | std::vector inputDims = { 1, 3, resize_h, resize_w }; 105 | std::vector shape = input_tensor->shape(); 106 | shape[0] = 1; 107 | shape[2] = resize_h; 108 | shape[3] = resize_w; 109 | lffd->resizeTensor(input_tensor, shape); 110 | lffd->resizeSession(sess_lffd); 111 | 112 | //prepare data 113 | std::shared_ptr pretreat(MNN::CV::ImageProcess::create(img_config)); 114 | pretreat->convert(in.data, resize_w, resize_h, in.step[0], input_tensor); 115 | 116 | 117 | //forward 118 | lffd->runSession(sess_lffd); 119 | 120 | std::vector bbox_collection; 121 | for (int i = 0; i copyToHostTensor(tensor_score); 125 | 126 | MNN::Tensor* tensor_location = new MNN::Tensor(outputTensors[2*i+1], MNN::Tensor::CAFFE); 127 | outputTensors[2 * i + 1]->copyToHostTensor(tensor_location); 128 | 129 | std::vector score; 130 | std::vector location; 131 | std::vector shape_score= tensor_score->shape(); 132 | std::vector shape_loc=tensor_location->shape(); 133 | for (int j = 0; j < 100; j++) { 134 | score.push_back(tensor_score->host()[j]); 135 | } 136 | 137 | for (int j = 0; j < 100; j++) { 138 | location.push_back(tensor_location->host()[j]); 139 | //std::cout << location[j] << std::endl; 140 | } 141 | 142 | 143 | generateBBox(bbox_collection, tensor_score, tensor_location, score_threshold, 144 | tensor_score->width(), tensor_score->height(), img.cols, img.rows, i); 145 | 146 | delete tensor_score; 147 | delete tensor_location; 148 | } 149 | std::vector valid_input; 150 | get_topk_bbox(bbox_collection, valid_input, top_k); 151 | nms(valid_input, face_list, nms_threshold); 152 | 153 | for(int i=0;i h ? w : h; 165 | cenx=face_list[i].x1+w/2; 166 | ceny=face_list[i].y1+h/2; 167 | face_list[i].x1=cenx-maxSize/2>0? cenx - maxSize / 2:0; 168 | face_list[i].y1=ceny-maxSize/2>0? ceny - maxSize / 2:0; 169 | face_list[i].x2=cenx+maxSize/2>image_w? image_w-1: cenx + maxSize / 2; 170 | face_list[i].y2=ceny+maxSize/2> image_h? image_h-1: ceny + maxSize / 2; 171 | 172 | } 173 | return 0; 174 | } 175 | 176 | void LFFD::generateBBox(std::vector& bbox_collection, MNN::Tensor* score_map, MNN::Tensor* box_map, float score_threshold, int fea_w, int fea_h, int cols, int rows, int scale_id) 177 | { 178 | float* RF_center_Xs = new float[fea_w]; 179 | float* RF_center_Xs_mat = new float[fea_w * fea_h]; 180 | float* RF_center_Ys = new float[fea_h]; 181 | float* RF_center_Ys_mat = new float[fea_h * fea_w]; 182 | 183 | for (int x = 0; x < fea_w; x++) { 184 | RF_center_Xs[x] = receptive_field_center_start[scale_id] + receptive_field_stride[scale_id] * x; 185 | } 186 | for (int x = 0; x < fea_h; x++) { 187 | for (int y = 0; y < fea_w; y++) { 188 | RF_center_Xs_mat[x * fea_w + y] = RF_center_Xs[y]; 189 | } 190 | } 191 | 192 | for (int x = 0; x < fea_h; x++) { 193 | RF_center_Ys[x] = receptive_field_center_start[scale_id] + receptive_field_stride[scale_id] * x; 194 | for (int y = 0; y < fea_w; y++) { 195 | RF_center_Ys_mat[x * fea_w + y] = RF_center_Ys[x]; 196 | } 197 | } 198 | 199 | float* x_lt_mat = new float[fea_h * fea_w]; 200 | float* y_lt_mat = new float[fea_h * fea_w]; 201 | float* x_rb_mat = new float[fea_h * fea_w]; 202 | float* y_rb_mat = new float[fea_h * fea_w]; 203 | 204 | 205 | 206 | //x-left-top 207 | float mid_value = 0; 208 | 209 | float* box_map_ptr = box_map->host(); 210 | int fea_spacial_size = fea_h * fea_w; 211 | for (int j = 0; j < fea_spacial_size; j++) { 212 | mid_value = RF_center_Xs_mat[j] - box_map_ptr[0*fea_spacial_size+j] * constant[scale_id]; 213 | x_lt_mat[j] = mid_value < 0 ? 0 : mid_value; 214 | } 215 | //y-left-top 216 | for (int j = 0; j < fea_spacial_size; j++) { 217 | mid_value = RF_center_Ys_mat[j] - box_map_ptr[1 * fea_spacial_size + j] * constant[scale_id]; 218 | y_lt_mat[j] = mid_value < 0 ? 0 : mid_value; 219 | } 220 | //x-right-bottom 221 | for (int j = 0; j < fea_spacial_size; j++) { 222 | mid_value = RF_center_Xs_mat[j] - box_map_ptr[2 * fea_spacial_size + j] * constant[scale_id]; 223 | x_rb_mat[j] = mid_value > cols - 1 ? cols - 1 : mid_value; 224 | } 225 | //y-right-bottom 226 | for (int j = 0; j < fea_spacial_size; j++) { 227 | mid_value = RF_center_Ys_mat[j] - box_map_ptr[3 * fea_spacial_size + j] * constant[scale_id]; 228 | y_rb_mat[j] = mid_value > rows - 1 ? rows - 1 : mid_value; 229 | } 230 | 231 | float* score_map_ptr = score_map->host(); 232 | 233 | 234 | for (int k = 0; k < fea_spacial_size; k++) { 235 | if (score_map_ptr[k] > score_threshold) { 236 | FaceInfo faceinfo; 237 | faceinfo.x1 = x_lt_mat[k]; 238 | faceinfo.y1 = y_lt_mat[k]; 239 | faceinfo.x2 = x_rb_mat[k]; 240 | faceinfo.y2 = y_rb_mat[k]; 241 | faceinfo.score = score_map_ptr[k]; 242 | faceinfo.area = (faceinfo.x2 - faceinfo.x1) * (faceinfo.y2 - faceinfo.y1); 243 | bbox_collection.push_back(faceinfo); 244 | } 245 | } 246 | 247 | delete[] RF_center_Xs; RF_center_Xs = NULL; 248 | delete[] RF_center_Ys; RF_center_Ys = NULL; 249 | delete[] RF_center_Xs_mat; RF_center_Xs_mat = NULL; 250 | delete[] RF_center_Ys_mat; RF_center_Ys_mat = NULL; 251 | delete[] x_lt_mat; x_lt_mat = NULL; 252 | delete[] y_lt_mat; y_lt_mat = NULL; 253 | delete[] x_rb_mat; x_rb_mat = NULL; 254 | delete[] y_rb_mat; y_rb_mat = NULL; 255 | } 256 | 257 | void LFFD::get_topk_bbox(std::vector& input, std::vector& output, int top_k) 258 | { 259 | std::sort(input.begin(), input.end(), 260 | [](const FaceInfo& a, const FaceInfo& b) 261 | { 262 | return a.score > b.score; 263 | }); 264 | 265 | if (input.size() > top_k) { 266 | for (int k = 0; k < top_k; k++) { 267 | output.push_back(input[k]); 268 | } 269 | } 270 | else { 271 | output = input; 272 | } 273 | } 274 | 275 | void LFFD::nms(std::vector& input, std::vector& output, float threshold, int type) 276 | { 277 | if (input.empty()) { 278 | return; 279 | } 280 | std::sort(input.begin(), input.end(), 281 | [](const FaceInfo& a, const FaceInfo& b) 282 | { 283 | return a.score > b.score; 284 | }); 285 | 286 | int box_num = input.size(); 287 | 288 | std::vector merged(box_num, 0); 289 | 290 | for (int i = 0; i < box_num; i++) 291 | { 292 | if (merged[i]) 293 | continue; 294 | 295 | output.push_back(input[i]); 296 | 297 | float h0 = input[i].y2 - input[i].y1 + 1; 298 | float w0 = input[i].x2 - input[i].x1 + 1; 299 | 300 | float area0 = h0 * w0; 301 | 302 | 303 | for (int j = i + 1; j < box_num; j++) 304 | { 305 | if (merged[j]) 306 | continue; 307 | 308 | float inner_x0 = input[i].x1 > input[j].x1 ? input[i].x1 : input[j].x1;//std::max(input[i].x1, input[j].x1); 309 | float inner_y0 = input[i].y1 > input[j].y1 ? input[i].y1 : input[j].y1; 310 | 311 | float inner_x1 = input[i].x2 < input[j].x2 ? input[i].x2 : input[j].x2; //bug fixed ,sorry 312 | float inner_y1 = input[i].y2 < input[j].y2 ? input[i].y2 : input[j].y2; 313 | 314 | float inner_h = inner_y1 - inner_y0 + 1; 315 | float inner_w = inner_x1 - inner_x0 + 1; 316 | 317 | 318 | if (inner_h <= 0 || inner_w <= 0) 319 | continue; 320 | 321 | float inner_area = inner_h * inner_w; 322 | 323 | float h1 = input[j].y2 - input[j].y1 + 1; 324 | float w1 = input[j].x2 - input[j].x1 + 1; 325 | 326 | float area1 = h1 * w1; 327 | 328 | float score= inner_area/area1; 329 | 330 | if (score > threshold) 331 | merged[j] = 1; 332 | } 333 | 334 | } 335 | } 336 | -------------------------------------------------------------------------------- /src/MNN_LFFD.h: -------------------------------------------------------------------------------- 1 | #pragma once 2 | 3 | #include 4 | #include 5 | #include 6 | #include 7 | #include 8 | 9 | #include 10 | #include 11 | #include 12 | #include 13 | #include 14 | 15 | 16 | #define NMS_UNION 1 17 | #define NMS_MIN 2 18 | 19 | typedef struct FaceInfo { 20 | float x1; 21 | float y1; 22 | float x2; 23 | float y2; 24 | float score; 25 | float area; 26 | 27 | float landmarks[10]; 28 | }; 29 | 30 | class LFFD { 31 | public: 32 | LFFD(const std::string& model_path, int scale_num=5, int num_thread_=1 ); 33 | ~LFFD(); 34 | 35 | int detect(cv::Mat& img, std::vector& face_lis,int resize_h=480,int resize_w=640, 36 | float score_threshold = 0.6, float nms_threshold = 0.4, int top_k = 10000, 37 | std::vector skip_scale_branch_list = {}); 38 | 39 | private: 40 | void generateBBox(std::vector& collection, MNN::Tensor* score_map, MNN::Tensor* box_map, float score_threshold, 41 | int fea_w, int fea_h, int cols, int rows, int scale_id); 42 | void get_topk_bbox(std::vector& input, std::vector& output, int topk); 43 | void nms(std::vector& input, std::vector& output, 44 | float threshold, int type = NMS_MIN); 45 | private: 46 | std::shared_ptr lffd; 47 | MNN::Session* sess_lffd = nullptr; 48 | 49 | MNN::Tensor* input_tensor = nullptr; 50 | std::vector< MNN::Tensor*> outputTensors; 51 | MNN::CV::ImageProcess::Config img_config; 52 | 53 | 54 | int num_thread; 55 | int num_output_scales; 56 | int image_w; 57 | int image_h; 58 | 59 | std::string mnn_model_file; 60 | 61 | std::vector receptive_field_list; 62 | std::vector receptive_field_stride; 63 | std::vector bbox_small_list; 64 | std::vector bbox_large_list; 65 | std::vector receptive_field_center_start; 66 | std::vector constant; 67 | 68 | std::vector output_blob_names; 69 | 70 | }; 71 | -------------------------------------------------------------------------------- /src/test.cpp: -------------------------------------------------------------------------------- 1 | #include 2 | #include "MNN_LFFD.h" 3 | #include 4 | #include 5 | #include 6 | #include 7 | using namespace cv; 8 | 9 | #define IMAGE_TEST 10 | //#define VIDEO 11 | 12 | int main(int argc, char** argv) { 13 | 14 | if (argc !=3) 15 | { 16 | std::cout << " name.exe mode_path image_file/video_file" << std::endl; 17 | return -1; 18 | } 19 | 20 | //eg : std::string model_path="your_path/models" 21 | 22 | std::string model_path = argv[1]; 23 | std::string image_or_video_file = argv[2]; 24 | 25 | 26 | LFFD* face_detector = new LFFD(model_path,5,2); 27 | 28 | #ifdef IMAGE_TEST 29 | cv::Mat image = cv::imread(image_or_video_file); 30 | std::vector finalBox; 31 | 32 | std::chrono::time_point t1 = std::chrono::system_clock::now(); 33 | face_detector->detect(image, finalBox,image.rows,image.cols); 34 | std::chrono::time_point t2 = std::chrono::system_clock::now(); 35 | std::cout << "mtcnn time:" << (float)std::chrono::duration_cast(t2 - t1).count() / 1000 << "ms" << std::endl; 36 | 37 | for (int i = 0; i < finalBox.size(); i++) { 38 | FaceInfo facebox = finalBox[i]; 39 | cv::Rect box=cv::Rect(facebox.x1,facebox.y1,facebox.x2-facebox.x1,facebox.y2-facebox.y1); 40 | cv::rectangle(image, box, cv::Scalar(255, 0, 21), 2); 41 | } 42 | std::cout << finalBox.size() << std::endl; 43 | cv::imwrite("res.jpg", image); 44 | cv::namedWindow("MNN", CV_WINDOW_NORMAL); 45 | cv::imshow("MNN", image); 46 | cv::waitKey(); 47 | #endif // IMAGE_TEST 48 | 49 | #ifdef VIDEO 50 | cv::VideoCapture cap(image_or_video_file); 51 | cv::Mat image; 52 | 53 | int MAXNUM = 100; 54 | float AVE_TIME = 0; 55 | float MAX_TIME = -1; 56 | float MIN_TIME = 99999999999999; 57 | int count = 0; 58 | while (count<10000) { 59 | cap >> image; 60 | std::vector finalBox; 61 | std::chrono::time_point t1 = std::chrono::system_clock::now(); 62 | face_detector->detect(image, finalBox); 63 | std::chrono::time_point t2 = std::chrono::system_clock::now(); 64 | float dur = (float)std::chrono::duration_cast(t2 - t1).count() / 1000; 65 | std::cout << "lffd time:" << dur << "ms" << std::endl; 66 | 67 | AVE_TIME += dur; 68 | 69 | if (MAX_TIME < dur) { 70 | MAX_TIME = dur; 71 | } 72 | if (MIN_TIME > dur) { 73 | MIN_TIME = dur; 74 | } 75 | 76 | 77 | for (int i = 0; i < finalBox.size(); i++) { 78 | FaceInfo facebox = finalBox[i]; 79 | cv::Rect box = cv::Rect(facebox.x1, facebox.y1, facebox.x2 - facebox.x1, facebox.y2 - facebox.y1); 80 | cv::rectangle(image, box, cv::Scalar(255, 0, 21), 2); 81 | } 82 | 83 | 84 | cv::namedWindow("MNN", CV_WINDOW_NORMAL); 85 | cv::imshow("MNN", image); 86 | cv::waitKey(1); 87 | count++; 88 | } 89 | 90 | std::cout << "IMAGE SHAPE: " << "640x480" << std::endl; 91 | std::cout << "MAX LOOP TIMES: " << MAXNUM << std::endl; 92 | std::cout << "AVE TIME: " << AVE_TIME / count << " ms" << std::endl; 93 | std::cout << "MAX TIME: " << MAX_TIME << " ms" << std::endl; 94 | std::cout << "MIN TIME: " << MIN_TIME << " ms" << std::endl; 95 | 96 | #endif 97 | return 0; 98 | } 99 | 100 | --------------------------------------------------------------------------------