├── models ├── det1.mnn ├── det2.mnn └── det3-half.mnn ├── library ├── libMNN.so ├── libMNN_CL.so └── include │ ├── ErrorCode.hpp │ ├── MNNSharedContext.h │ ├── AutoTime.hpp │ ├── MNNForwardType.h │ ├── MNNDefine.h │ ├── ImageProcess.hpp │ ├── Interpreter.hpp │ ├── Tensor.hpp │ ├── HalideRuntime.h │ ├── Rect.h │ └── Matrix.h ├── faster_models ├── det1.mnn ├── det2.mnn └── det3.mnn ├── README.md ├── main.cpp ├── Makefile ├── face_detect.h └── face_detect.cpp /models/det1.mnn: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/liushuan/MNN-MTCNN-CPU-OPENCL/HEAD/models/det1.mnn -------------------------------------------------------------------------------- /models/det2.mnn: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/liushuan/MNN-MTCNN-CPU-OPENCL/HEAD/models/det2.mnn -------------------------------------------------------------------------------- /library/libMNN.so: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/liushuan/MNN-MTCNN-CPU-OPENCL/HEAD/library/libMNN.so -------------------------------------------------------------------------------- /library/libMNN_CL.so: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/liushuan/MNN-MTCNN-CPU-OPENCL/HEAD/library/libMNN_CL.so -------------------------------------------------------------------------------- /models/det3-half.mnn: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/liushuan/MNN-MTCNN-CPU-OPENCL/HEAD/models/det3-half.mnn -------------------------------------------------------------------------------- /faster_models/det1.mnn: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/liushuan/MNN-MTCNN-CPU-OPENCL/HEAD/faster_models/det1.mnn -------------------------------------------------------------------------------- /faster_models/det2.mnn: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/liushuan/MNN-MTCNN-CPU-OPENCL/HEAD/faster_models/det2.mnn -------------------------------------------------------------------------------- /faster_models/det3.mnn: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/liushuan/MNN-MTCNN-CPU-OPENCL/HEAD/faster_models/det3.mnn -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # MNN-MTCNN-CPU-OPENCL 2 | MNN MTCNN C++ 3 | 4 | 5 | 实现了 MTCNN 在 MNN 平台的移植, 目前 人脸检测 在 Arm芯片RK3399 上面 检测速度1080P@60size 可以达到 50FPS. 6 | 请使用 faster_models下的模型测试,如果需要更好的人脸检测效果,可以使用 models下面的模型。 7 | 8 | 9 | 10 | Email:547691062@qq.com 11 | 12 | 13 | 14 | -------------------------------------------------------------------------------- /library/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 | 23 | // User error 24 | INPUT_DATA_ERROR = 10, 25 | CALL_BACK_STOP = 11, 26 | 27 | // Op Resize Error 28 | TENSOR_NOT_SUPPORT = 20, 29 | TENSOR_NEED_DIVIDE = 21, 30 | }; 31 | } 32 | 33 | #endif /* ErrorCode_h */ 34 | -------------------------------------------------------------------------------- /main.cpp: -------------------------------------------------------------------------------- 1 | #include 2 | #include 3 | #include 4 | #include "face_detect.h" 5 | using namespace cv; 6 | 7 | void face_detect1(){ 8 | 9 | cv::Mat img1 = cv::imread("./img/001.jpg"); 10 | 11 | //检测 人脸的对象。 12 | std::string model_path = "./models/"; 13 | TIEVD::FaceDetect face_detect(model_path, 0.7f, 0.8f, 0.9f); 14 | 15 | std::vector face_info1 = face_detect.Detect_MaxFace(img1, 32, 3); 16 | std::vector face_info2 = face_detect.Detect(img1, 32, 3); 17 | 18 | std::cout<<"face_info1:"< /*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 | -------------------------------------------------------------------------------- /Makefile: -------------------------------------------------------------------------------- 1 | CXX = g++ 2 | OBJS = $(SRCS:.cpp = .o) 3 | SRCS = $(wildcard *.cpp) 4 | 5 | 6 | MNN = /root/work_space/software/MNN 7 | OPENCV = /root/work_space/software/opencv3.3.0 8 | INCPATH = -I${MNN}/include \ 9 | -I${OPENCV}/include 10 | 11 | LIBS = -lopencv_core -lopencv_highgui -lopencv_imgproc -lopencv_imgcodecs -lopencv_video -lopencv_videoio -lopencv_videostab -lMNN -lMNN_CL -linterfaceAFR -ldl 12 | LIBPATH = -L${OPENCV}/lib -L${MNN}/build -L/root/work_space/source_code/mnn/interface_AFR 13 | 14 | 15 | CXXFLAGS = -std=c++11 16 | OUTPUT = AFR_test #输出程序名称 17 | 18 | #%:%.cpp 19 | # $(CXX) $(INCPATH)$(LIBPATH) $^ ${NCNN}/build/install/lib/libncnn.a $(LIBS) -o $@ 20 | 21 | all:$(OUTPUT) 22 | $(OUTPUT) : $(OBJS) 23 | $(CXX) $^ -o $@ $(INCPATH) $(CXXFLAGS) $(LIBPATH) $(LIBS) -Wl,-R${MNN}/build -Wl,-R/root/work_space/source_code/mnn/interface_AFR 24 | .PHONY:clean 25 | clean: 26 | rm -rf *.out *.o $(OUTPUT) #清除中间文件及生成文件 27 | 28 | -------------------------------------------------------------------------------- /library/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 | -------------------------------------------------------------------------------- /face_detect.h: -------------------------------------------------------------------------------- 1 | #ifndef _FACE_DETECT_H_ 2 | #define _FACE_DETECT_H_ 3 | 4 | #include 5 | #include 6 | #include 7 | #ifdef _OPENMP 8 | #include 9 | #endif 10 | 11 | using std::string; 12 | using std::vector; 13 | 14 | namespace TIEVD{ 15 | 16 | 17 | typedef struct FaceBox { 18 | float xmin; 19 | float ymin; 20 | float xmax; 21 | float ymax; 22 | float score; 23 | } FaceBox; 24 | typedef struct FaceInfo { 25 | float bbox_reg[4]; 26 | float landmark_reg[10]; 27 | float landmark[10]; 28 | FaceBox bbox; 29 | } FaceInfo; 30 | 31 | class FaceDetect { 32 | public: 33 | FaceDetect(const string& proto_model_dir, float threhold_p=0.7f, float threhold_r=0.8f, float threhold_o = 0.8f, float factor = 0.709f); 34 | std::vector Detect(const cv::Mat& img, const int min_face = 64 , const int stage = 3); 35 | std::vector Detect_MaxFace(const cv::Mat& img, const int min_face= 64, const int stage = 3); 36 | ~FaceDetect(); 37 | private: 38 | int threads_num = 2; 39 | 40 | }; 41 | 42 | } 43 | 44 | 45 | 46 | 47 | 48 | 49 | 50 | 51 | 52 | #endif // _FaceDetect_H_ 53 | 54 | -------------------------------------------------------------------------------- /library/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 | 12 | typedef enum { 13 | MNN_FORWARD_CPU = 0, 14 | 15 | /* 16 | Firtly find the first available backends not equal to CPU 17 | If no other backends, use cpu 18 | */ 19 | MNN_FORWARD_AUTO = 4, 20 | 21 | /*Hand write metal*/ 22 | MNN_FORWARD_METAL = 1, 23 | 24 | /*Use IOS's MPS instead of hand-write metal, Not Support yet*/ 25 | MNN_FORWARD_MPS = 2, 26 | 27 | /*Android / Common Device GPU API*/ 28 | MNN_FORWARD_OPENCL = 3, 29 | MNN_FORWARD_OPENGL = 6, 30 | MNN_FORWARD_VULKAN = 7, 31 | 32 | /*Android 8.1's NNAPI, Not Support yet*/ 33 | MNN_FORWARD_NN = 5, 34 | 35 | /*User can use API from Backend.hpp to add or search Backend*/ 36 | MNN_FORWARD_USER_0 = 8, 37 | MNN_FORWARD_USER_1 = 9, 38 | MNN_FORWARD_USER_2 = 10, 39 | MNN_FORWARD_USER_3 = 11, 40 | 41 | MNN_FORWARD_ALL 42 | } MNNForwardType; 43 | #ifdef __cplusplus 44 | namespace MNN { 45 | struct BackendConfig { 46 | enum MemoryMode { 47 | Memory_Normal = 0, 48 | Memory_High, 49 | Memory_Low 50 | }; 51 | 52 | MemoryMode memory = Memory_Normal; 53 | 54 | enum PowerMode { 55 | Power_Normal = 0, 56 | Power_High, 57 | Power_Low 58 | }; 59 | 60 | PowerMode power = Power_Normal; 61 | 62 | enum PrecisionMode { 63 | Precision_Normal = 0, 64 | Precision_High, 65 | Precision_Low 66 | }; 67 | 68 | PrecisionMode precision = Precision_Normal; 69 | 70 | /** user defined context */ 71 | void* sharedContext = nullptr; 72 | }; 73 | }; 74 | #endif 75 | #endif /* MNNForwardType_h */ 76 | -------------------------------------------------------------------------------- /library/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 | -------------------------------------------------------------------------------- /library/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 create tensor with given data. 105 | * @param w image width. 106 | * @param h image height. 107 | * @param bpp bytes per pixel. 108 | * @param p pixel data pointer. 109 | * @return created tensor. 110 | */ 111 | template 112 | static Tensor* createImageTensor(int w, int h, int bpp, void* p = nullptr) { 113 | return createImageTensor(halide_type_of(), w, h, bpp, p); 114 | } 115 | static Tensor* createImageTensor(halide_type_t type, int w, int h, int bpp, void* p = nullptr); 116 | 117 | private: 118 | ImageProcess(const Config& config); 119 | Matrix mTransform; 120 | Matrix mTransformInvert; 121 | Inside* mInside; 122 | }; 123 | } // namespace CV 124 | } // namespace MNN 125 | 126 | #endif /* ImageProcess_hpp */ 127 | -------------------------------------------------------------------------------- /library/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 run session. 148 | * @param session given session. 149 | * @return result of running. 150 | */ 151 | ErrorCode runSession(Session* session) const; 152 | 153 | /* 154 | * @brief run session. 155 | * @param session given session. 156 | * @param before callback before each op. return true to run the op; return false to skip the op. 157 | * @param after callback after each op. return true to continue running; return false to interrupt the session. 158 | * @param sync synchronously wait for finish of execution or not. 159 | * @return result of running. 160 | */ 161 | ErrorCode runSessionWithCallBack(const Session* session, const TensorCallBack& before, const TensorCallBack& end, 162 | bool sync = false) const; 163 | 164 | /* 165 | * @brief run session. 166 | * @param session given session. 167 | * @param before callback before each op. return true to run the op; return false to skip the op. 168 | * @param after callback after each op. return true to continue running; return false to interrupt the session. 169 | * @param sync synchronously wait for finish of execution or not. 170 | * @return result of running. 171 | */ 172 | ErrorCode runSessionWithCallBackInfo(const Session* session, const TensorCallBackWithInfo& before, 173 | const TensorCallBackWithInfo& end, bool sync = false) const; 174 | 175 | /** 176 | * @brief get input tensor for given name. 177 | * @param session given session. 178 | * @param name given name. if NULL, return first input. 179 | * @return tensor if found, NULL otherwise. 180 | */ 181 | Tensor* getSessionInput(const Session* session, const char* name); 182 | /** 183 | * @brief get output tensor for given name. 184 | * @param session given session. 185 | * @param name given name. if NULL, return first output. 186 | * @return tensor if found, NULL otherwise. 187 | */ 188 | Tensor* getSessionOutput(const Session* session, const char* name); 189 | 190 | /** 191 | * @brief get all input tensors. 192 | * @param session given session. 193 | * @return all input tensors mapped with name. 194 | */ 195 | const std::map& getSessionOutputAll(const Session* session) const; 196 | /** 197 | * @brief get all output tensors. 198 | * @param session given session. 199 | * @return all output tensors mapped with name. 200 | */ 201 | const std::map& getSessionInputAll(const Session* session) const; 202 | 203 | public: 204 | /** 205 | * @brief resize given tensor. 206 | * @param tensor given tensor. 207 | * @param dims new dims. at most 6 dims. 208 | */ 209 | void resizeTensor(Tensor* tensor, const std::vector& dims); 210 | 211 | /** 212 | * @brief resize given tensor by nchw. 213 | * @param batch / N. 214 | * @param channel / C. 215 | * @param height / H. 216 | * @param width / W 217 | */ 218 | void resizeTensor(Tensor* tensor, int batch, int channel, int height, int width); 219 | 220 | /** 221 | * @brief get backend used to create given tensor. 222 | * @param session given session. 223 | * @param tensor given tensor. 224 | * @return backend used to create given tensor, may be NULL. 225 | */ 226 | const Backend* getBackend(const Session* session, const Tensor* tensor) const; 227 | 228 | /** 229 | * @brief get business code (model identifier). 230 | * @return business code. 231 | */ 232 | const char* bizCode() const; 233 | 234 | private: 235 | static Interpreter* createFromBufferInternal(Content* net); 236 | 237 | Content* mNet = nullptr; 238 | Interpreter(Content* net); 239 | 240 | Interpreter(const Interpreter&) = delete; 241 | Interpreter(const Interpreter&&) = delete; 242 | Interpreter& operator=(const Interpreter&) = delete; 243 | Interpreter& operator=(const Interpreter&&) = delete; 244 | }; 245 | } // namespace MNN 246 | 247 | #endif /* Interpreter_hpp */ 248 | -------------------------------------------------------------------------------- /library/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 | /** dimension reorder flag */ 48 | enum DataReorderType { 49 | /** default reorder type, do not reorder */ 50 | NO_REORDER = 0, 51 | /** reorder dimension 4 by 4. usually used with NC4HW4 or NHWC4 while data type is float. */ 52 | REORDER_4 = 1, 53 | /** reorder dimension 8 by 8. usually used with NC4HW4 or NHWC4 while data type is uint8 or int8. */ 54 | REORDER_8 55 | }; 56 | 57 | public: 58 | /** 59 | * @brief create a tensor with dimension size and type without acquire memory for data. 60 | * @param dimSize dimension size. 61 | * @param type dimension type. 62 | */ 63 | Tensor(int dimSize = 4, DimensionType type = CAFFE); 64 | 65 | /** 66 | * @brief create a tensor with same shape as given tensor. 67 | * @param tensor shape provider. 68 | * @param type dimension type. 69 | * @param allocMemory acquire memory for data or not. 70 | * @warning tensor data won't be copied. 71 | */ 72 | Tensor(const Tensor* tensor, DimensionType type = CAFFE, bool allocMemory = true); 73 | 74 | /** deinitializer */ 75 | ~Tensor(); 76 | 77 | private: 78 | // remove all assignment operator 79 | Tensor(const Tensor& tensor) = delete; 80 | Tensor(const Tensor&& tensor) = delete; 81 | Tensor& operator=(const Tensor&) = delete; 82 | Tensor& operator=(const Tensor&&) = delete; 83 | 84 | public: 85 | /** 86 | * @brief create tensor with shape, data type and dimension type. 87 | * @param shape tensor shape. 88 | * @param type data type. 89 | * @param dimType dimension type. 90 | * @return created tensor. 91 | * @warning memory for data won't be acquired. call backend's onAcquireBuffer to get memory ready. 92 | */ 93 | static Tensor* createDevice(const std::vector& shape, halide_type_t type, DimensionType dimType = TENSORFLOW); 94 | 95 | /** 96 | * @brief create tensor with shape and dimension type. data type is represented by `T`. 97 | * @param shape tensor shape. 98 | * @param dimType dimension type. 99 | * @return created tensor. 100 | * @warning memory for data won't be acquired. call backend's onAcquireBuffer to get memory ready. 101 | */ 102 | template 103 | static Tensor* createDevice(const std::vector& shape, DimensionType dimType = TENSORFLOW) { 104 | return createDevice(shape, halide_type_of(), dimType); 105 | } 106 | 107 | /** 108 | * @brief create tensor with shape, data type, data and dimension type. 109 | * @param shape tensor shape. 110 | * @param type data type. 111 | * @param data data to save. 112 | * @param dimType dimension type. 113 | * @return created tensor. 114 | */ 115 | static Tensor* create(const std::vector& shape, halide_type_t type, void* data = NULL, 116 | DimensionType dimType = TENSORFLOW); 117 | 118 | /** 119 | * @brief create tensor with shape, data and dimension type. data type is represented by `T`. 120 | * @param shape tensor shape. 121 | * @param data data to save. 122 | * @param dimType dimension type. 123 | * @return created tensor. 124 | */ 125 | template 126 | static Tensor* create(const std::vector& shape, void* data = NULL, DimensionType dimType = TENSORFLOW) { 127 | return create(shape, halide_type_of(), data, dimType); 128 | } 129 | 130 | public: 131 | /** 132 | * @brief for DEVICE tensor, copy data from given host tensor. 133 | * @param hostTensor host tensor, the data provider. 134 | * @return true for DEVICE tensor, and false for HOST tensor. 135 | */ 136 | bool copyFromHostTensor(const Tensor* hostTensor); 137 | 138 | /** 139 | * @brief for DEVICE tensor, copy data to given host tensor. 140 | * @param hostTensor host tensor, the data consumer. 141 | * @return true for DEVICE tensor, and false for HOST tensor. 142 | */ 143 | bool copyToHostTensor(Tensor* hostTensor) const; 144 | 145 | /** 146 | * @brief create HOST tensor from DEVICE tensor, with or without data copying. 147 | * @param deviceTensor given device tensor. 148 | * @param copyData copy data or not. 149 | * @return created host tensor. 150 | */ 151 | static Tensor* createHostTensorFromDevice(const Tensor* deviceTensor, bool copyData = true); 152 | 153 | public: 154 | const halide_buffer_t& buffer() const { 155 | return mBuffer; 156 | } 157 | halide_buffer_t& buffer() { 158 | return mBuffer; 159 | } 160 | 161 | /** 162 | * @brief get dimension type. 163 | * @return dimension type. 164 | */ 165 | DimensionType getDimensionType() const; 166 | 167 | /** 168 | * @brief handle data type. used when data type code is halide_type_handle. 169 | * @return handle data type. 170 | */ 171 | HandleDataType getHandleDataType() const; 172 | 173 | /** 174 | * @brief set data type. 175 | * @param type data type defined in 'Type_generated.h'. 176 | */ 177 | void setType(int type); 178 | 179 | /** 180 | * @brief get data type. 181 | * @return data type. 182 | */ 183 | inline halide_type_t getType() const { 184 | return mBuffer.type; 185 | } 186 | 187 | /** 188 | * @brief visit host memory, data type is represented by `T`. 189 | * @return data point in `T` type. 190 | */ 191 | template 192 | T* host() const { 193 | return (T*)mBuffer.host; 194 | } 195 | 196 | /** 197 | * @brief visit device memory. 198 | * @return device data ID. what the ID means varies between backends. 199 | */ 200 | uint64_t deviceId() const { 201 | return mBuffer.device; 202 | } 203 | 204 | public: 205 | int dimensions() const { 206 | return mBuffer.dimensions; 207 | } 208 | 209 | /** 210 | * @brief get all dimensions' extent. 211 | * @return dimensions' extent. 212 | */ 213 | std::vector shape() const; 214 | 215 | /** 216 | * @brief calculate number of bytes needed to store data taking reordering flag into account. 217 | * @return bytes needed to store data 218 | */ 219 | int size() const; 220 | 221 | /** 222 | * @brief calculate number of elements needed to store data taking reordering flag into account. 223 | * @return elements needed to store data 224 | */ 225 | inline int elementSize() const { 226 | return size() / mBuffer.type.bytes(); 227 | } 228 | 229 | public: 230 | inline int width() const { 231 | if (getDimensionType() == TENSORFLOW) { 232 | return mBuffer.dim[2].extent; 233 | } 234 | 235 | return mBuffer.dim[3].extent; 236 | } 237 | inline int height() const { 238 | if (getDimensionType() == TENSORFLOW) { 239 | return mBuffer.dim[1].extent; 240 | } 241 | return mBuffer.dim[2].extent; 242 | } 243 | inline int channel() const { 244 | if (getDimensionType() == TENSORFLOW) { 245 | return mBuffer.dim[3].extent; 246 | } 247 | return mBuffer.dim[1].extent; 248 | } 249 | inline int batch() const { 250 | return mBuffer.dim[0].extent; 251 | } 252 | 253 | // visit dimension's extent & stride 254 | inline int stride(int index) const { 255 | return mBuffer.dim[index].stride; 256 | } 257 | inline int length(int index) const { 258 | return mBuffer.dim[index].extent; 259 | } 260 | inline void setStride(int index, int stride) { 261 | mBuffer.dim[index].stride = stride; 262 | } 263 | inline void setLength(int index, int length) { 264 | mBuffer.dim[index].extent = length; 265 | } 266 | 267 | public: 268 | /** 269 | * @brief print tensor data. for DEBUG use only. 270 | */ 271 | void print() const; 272 | 273 | private: 274 | halide_buffer_t mBuffer; 275 | struct InsideDescribe* mDescribe; 276 | 277 | private: 278 | friend class TensorUtils; 279 | }; 280 | } // namespace MNN 281 | 282 | #endif /* Tensor_hpp */ 283 | -------------------------------------------------------------------------------- /library/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 | -------------------------------------------------------------------------------- /face_detect.cpp: -------------------------------------------------------------------------------- 1 | 2 | #include "face_detect.h" 3 | 4 | #include 5 | #include 6 | #include 7 | #include 8 | 9 | namespace TIEVD{ 10 | 11 | using namespace MNN; 12 | using namespace MNN::CV; 13 | 14 | std::shared_ptr PNet_ = NULL; 15 | std::shared_ptr RNet_ = NULL; 16 | std::shared_ptr ONet_ = NULL; 17 | 18 | MNN::Session * sess_p = NULL; 19 | MNN::Session * sess_r = NULL; 20 | MNN::Session * sess_o = NULL; 21 | 22 | MNN::Tensor * p_input = nullptr; 23 | MNN::Tensor * p_out_pro = nullptr; 24 | MNN::Tensor * p_out_reg = nullptr; 25 | 26 | MNN::Tensor * r_input = nullptr; 27 | MNN::Tensor * r_out_pro = nullptr; 28 | MNN::Tensor * r_out_reg = nullptr; 29 | 30 | MNN::Tensor * o_input = nullptr; 31 | MNN::Tensor * o_out_pro = nullptr; 32 | MNN::Tensor * o_out_reg = nullptr; 33 | MNN::Tensor * o_out_lank = nullptr; 34 | 35 | std::shared_ptr pretreat_data; 36 | 37 | std::vector candidate_boxes_; 38 | std::vector total_boxes_; 39 | 40 | static float threhold_p = 0.8f; 41 | static float threhold_r = 0.8f; 42 | static float threhold_o = 0.9f; 43 | static float iou_threhold = 0.7f; 44 | static float factor = 0.709f; 45 | //static int min_face = 48; 46 | 47 | //pnet config 48 | static const float pnet_stride = 2; 49 | static const float pnet_cell_size = 12; 50 | static const int pnet_max_detect_num = 5000; 51 | //mean & std 52 | static const float mean_val = 127.5f; 53 | static const float std_val = 0.0078125f; 54 | 55 | 56 | static bool CompareBBox(const FaceInfo & a, const FaceInfo & b) { 57 | return a.bbox.score > b.bbox.score; 58 | } 59 | 60 | 61 | static float IoU(float xmin, float ymin, float xmax, float ymax, 62 | float xmin_, float ymin_, float xmax_, float ymax_, bool is_iom) { 63 | float iw = std::min(xmax, xmax_) - std::max(xmin, xmin_) + 1; 64 | float ih = std::min(ymax, ymax_) - std::max(ymin, ymin_) + 1; 65 | if (iw <= 0 || ih <= 0) 66 | return 0; 67 | float s = iw*ih; 68 | if (is_iom) { 69 | float ov = s / std::min((xmax - xmin + 1)*(ymax - ymin + 1), (xmax_ - xmin_ + 1)*(ymax_ - ymin_ + 1)); 70 | return ov; 71 | } 72 | else { 73 | float ov = s / ((xmax - xmin + 1)*(ymax - ymin + 1) + (xmax_ - xmin_ + 1)*(ymax_ - ymin_ + 1) - s); 74 | return ov; 75 | } 76 | } 77 | 78 | static std::vector NMS(std::vector& bboxes, 79 | float thresh, char methodType) { 80 | std::vector bboxes_nms; 81 | if (bboxes.size() == 0) { 82 | return bboxes_nms; 83 | } 84 | std::sort(bboxes.begin(), bboxes.end(), CompareBBox); 85 | 86 | int32_t select_idx = 0; 87 | int32_t num_bbox = static_cast(bboxes.size()); 88 | std::vector mask_merged(num_bbox, 0); 89 | bool all_merged = false; 90 | 91 | while (!all_merged) { 92 | while (select_idx < num_bbox && mask_merged[select_idx] == 1) 93 | select_idx++; 94 | if (select_idx == num_bbox) { 95 | all_merged = true; 96 | continue; 97 | } 98 | bboxes_nms.push_back(bboxes[select_idx]); 99 | mask_merged[select_idx] = 1; 100 | 101 | FaceBox select_bbox = bboxes[select_idx].bbox; 102 | float area1 = static_cast((select_bbox.xmax - select_bbox.xmin + 1) * (select_bbox.ymax - select_bbox.ymin + 1)); 103 | float x1 = static_cast(select_bbox.xmin); 104 | float y1 = static_cast(select_bbox.ymin); 105 | float x2 = static_cast(select_bbox.xmax); 106 | float y2 = static_cast(select_bbox.ymax); 107 | 108 | select_idx++; 109 | #ifdef _OPENMP 110 | #pragma omp parallel for num_threads(threads_num) 111 | #endif 112 | for (int32_t i = select_idx; i < num_bbox; i++) { 113 | if (mask_merged[i] == 1) 114 | continue; 115 | 116 | FaceBox & bbox_i = bboxes[i].bbox; 117 | float x = std::max(x1, static_cast(bbox_i.xmin)); 118 | float y = std::max(y1, static_cast(bbox_i.ymin)); 119 | float w = std::min(x2, static_cast(bbox_i.xmax)) - x + 1; 120 | float h = std::min(y2, static_cast(bbox_i.ymax)) - y + 1; 121 | if (w <= 0 || h <= 0) 122 | continue; 123 | 124 | float area2 = static_cast((bbox_i.xmax - bbox_i.xmin + 1) * (bbox_i.ymax - bbox_i.ymin + 1)); 125 | float area_intersect = w * h; 126 | 127 | switch (methodType) { 128 | case 'u': 129 | if (static_cast(area_intersect) / (area1 + area2 - area_intersect) > thresh) 130 | mask_merged[i] = 1; 131 | break; 132 | case 'm': 133 | if (static_cast(area_intersect) / std::min(area1, area2) > thresh) 134 | mask_merged[i] = 1; 135 | break; 136 | default: 137 | break; 138 | } 139 | } 140 | } 141 | return bboxes_nms; 142 | } 143 | static void BBoxRegression(vector& bboxes) { 144 | #ifdef _OPENMP 145 | #pragma omp parallel for num_threads(threads_num) 146 | #endif 147 | for (int i = 0; i < bboxes.size(); ++i) { 148 | FaceBox &bbox = bboxes[i].bbox; 149 | float *bbox_reg = bboxes[i].bbox_reg; 150 | float w = bbox.xmax - bbox.xmin + 1; 151 | float h = bbox.ymax - bbox.ymin + 1; 152 | bbox.xmin += bbox_reg[0] * w; 153 | bbox.ymin += bbox_reg[1] * h; 154 | bbox.xmax += bbox_reg[2] * w; 155 | bbox.ymax += bbox_reg[3] * h; 156 | } 157 | } 158 | static void BBoxPad(vector& bboxes, int width, int height) { 159 | #ifdef _OPENMP 160 | #pragma omp parallel for num_threads(threads_num) 161 | #endif 162 | for (int i = 0; i < bboxes.size(); ++i) { 163 | FaceBox &bbox = bboxes[i].bbox; 164 | bbox.xmin = round(std::max(bbox.xmin, 0.f)); 165 | bbox.ymin = round(std::max(bbox.ymin, 0.f)); 166 | bbox.xmax = round(std::min(bbox.xmax, width - 1.f)); 167 | bbox.ymax = round(std::min(bbox.ymax, height - 1.f)); 168 | } 169 | } 170 | static void BBoxPadSquare(vector& bboxes, int width, int height) { 171 | #ifdef _OPENMP 172 | #pragma omp parallel for num_threads(threads_num) 173 | #endif 174 | for (int i = 0; i < bboxes.size(); ++i) { 175 | FaceBox &bbox = bboxes[i].bbox; 176 | float w = bbox.xmax - bbox.xmin + 1; 177 | float h = bbox.ymax - bbox.ymin + 1; 178 | float side = h>w ? h : w; 179 | bbox.xmin = round(std::max(bbox.xmin + (w - side)*0.5f, 0.f)); 180 | bbox.ymin = round(std::max(bbox.ymin + (h - side)*0.5f, 0.f)); 181 | bbox.xmax = round(std::min(bbox.xmin + side - 1, width - 1.f)); 182 | bbox.ymax = round(std::min(bbox.ymin + side - 1, height - 1.f)); 183 | } 184 | } 185 | static void GenerateBBox(float * confidence_data, float *reg_box, int feature_map_w_, int feature_map_h_, float scale, float thresh) { 186 | 187 | int spatical_size = feature_map_w_*feature_map_h_; 188 | 189 | candidate_boxes_.clear(); 190 | float v_scale = 1.0/scale; 191 | for (int i = 0; i= thresh) { 194 | int y = i / feature_map_w_; 195 | int x = i - feature_map_w_ * y; 196 | FaceInfo faceInfo; 197 | FaceBox &faceBox = faceInfo.bbox; 198 | 199 | faceBox.xmin = (float)(x * pnet_stride) * v_scale; 200 | faceBox.ymin = (float)(y * pnet_stride) * v_scale; 201 | faceBox.xmax = (float)(x * pnet_stride + pnet_cell_size - 1.f) * v_scale; 202 | faceBox.ymax = (float)(y * pnet_stride + pnet_cell_size - 1.f) * v_scale; 203 | 204 | faceInfo.bbox_reg[0] = reg_box[stride]; 205 | faceInfo.bbox_reg[1] = reg_box[stride + 1]; 206 | faceInfo.bbox_reg[2] = reg_box[stride + 2]; 207 | faceInfo.bbox_reg[3] = reg_box[stride + 3]; 208 | 209 | faceBox.score = confidence_data[stride + 1]; 210 | candidate_boxes_.push_back(faceInfo); 211 | } 212 | } 213 | } 214 | 215 | FaceDetect::FaceDetect(const string& proto_model_dir, float threhold_p_, float threhold_r_, float threhold_o_, float factor_){ 216 | threhold_p = threhold_p_; 217 | threhold_r = threhold_r_; 218 | threhold_o = threhold_o_; 219 | factor = factor_; 220 | threads_num = 2; 221 | PNet_ = std::shared_ptr(MNN::Interpreter::createFromFile((proto_model_dir + "det1.mnn").c_str())); 222 | 223 | RNet_ = std::shared_ptr(MNN::Interpreter::createFromFile((proto_model_dir + "det2.mnn").c_str())); 224 | 225 | ONet_ = std::shared_ptr(MNN::Interpreter::createFromFile((proto_model_dir + "det3-half.mnn").c_str())); 226 | 227 | 228 | MNN::ScheduleConfig config; 229 | config.type = (MNNForwardType)0; 230 | config.numThread = 1; // 1 faster 231 | 232 | BackendConfig backendConfig; 233 | backendConfig.precision = BackendConfig::Precision_Low; 234 | backendConfig.power = BackendConfig::Power_High; 235 | config.backendConfig = &backendConfig; 236 | 237 | sess_p = PNet_->createSession(config); 238 | sess_r = RNet_->createSession(config); 239 | sess_o = ONet_->createSession(config); 240 | 241 | 242 | p_input = PNet_->getSessionInput(sess_p, NULL); 243 | p_out_pro = PNet_->getSessionOutput(sess_p, "prob1"); 244 | p_out_reg = PNet_->getSessionOutput(sess_p, "conv4-2"); 245 | 246 | r_input = RNet_->getSessionInput(sess_r, NULL); 247 | r_out_pro = RNet_->getSessionOutput(sess_r, "prob1"); 248 | r_out_reg = RNet_->getSessionOutput(sess_r, "conv5-2"); 249 | 250 | o_input = ONet_->getSessionInput(sess_o, NULL); 251 | o_out_pro = ONet_->getSessionOutput(sess_o, "prob1"); 252 | o_out_reg = ONet_->getSessionOutput(sess_o, "conv6-2"); 253 | o_out_lank = ONet_->getSessionOutput(sess_o, "conv6-3"); 254 | 255 | ImageProcess::Config config_data; 256 | config_data.filterType = BILINEAR; 257 | const float mean_vals[3] = {mean_val, mean_val, mean_val}; 258 | const float norm_vals[3] = {std_val, std_val, std_val}; 259 | ::memcpy(config_data.mean, mean_vals, sizeof(mean_vals)); 260 | ::memcpy(config_data.normal, norm_vals, sizeof(norm_vals)); 261 | config_data.sourceFormat = RGBA; 262 | config_data.destFormat = BGR; 263 | 264 | pretreat_data = std::shared_ptr(ImageProcess::create(config_data)); 265 | 266 | 267 | } 268 | 269 | FaceDetect::~FaceDetect() { 270 | PNet_->releaseModel(); 271 | RNet_->releaseModel(); 272 | ONet_->releaseModel(); 273 | candidate_boxes_.clear(); 274 | total_boxes_.clear(); 275 | } 276 | 277 | uint8_t* get_img(cv::Mat img){ 278 | uchar * colorData = new uchar[img.total() * 4]; 279 | cv::Mat MatTemp(img.size(), CV_8UC4, colorData); 280 | cv::cvtColor(img, MatTemp, CV_BGR2RGBA, 4); 281 | return (uint8_t *)MatTemp.data; 282 | } 283 | 284 | static vector ProposalNet(const cv::Mat& img, int minSize, float threshold, float factor) { 285 | int width = img.cols; 286 | int height = img.rows; 287 | float scale = 12.0f / minSize; 288 | float minWH = std::min(height, width) *scale; 289 | std::vector scales; 290 | while (minWH >= 12) { 291 | scales.push_back(scale); 292 | minWH *= factor; 293 | scale *= factor; 294 | } 295 | total_boxes_.clear(); 296 | 297 | uint8_t *pImg = get_img(img); 298 | for (int i = 0; i < scales.size(); i++) { 299 | int ws = (int)std::ceil(width*scales[i]); 300 | int hs = (int)std::ceil(height*scales[i]); 301 | std::vector inputDims = {1, 3, hs, ws}; 302 | PNet_->resizeTensor(p_input, inputDims); 303 | PNet_->resizeSession(sess_p); 304 | 305 | MNN::CV::Matrix trans; 306 | trans.postScale(1.0f/ws, 1.0f/hs); 307 | trans.postScale(width, height); 308 | pretreat_data->setMatrix(trans); 309 | pretreat_data->convert(pImg, width, height, 0, p_input); 310 | 311 | PNet_->runSession(sess_p); 312 | float * confidence = p_out_pro->host(); 313 | float * reg = p_out_reg->host(); 314 | 315 | int feature_w = p_out_pro->width(); 316 | int feature_h = p_out_pro->height(); 317 | 318 | GenerateBBox(confidence, reg, feature_w, feature_h, scales[i], threshold); 319 | std::vector bboxes_nms = NMS(candidate_boxes_, 0.5f, 'u'); 320 | if (bboxes_nms.size() > 0) { 321 | total_boxes_.insert(total_boxes_.end(), bboxes_nms.begin(), bboxes_nms.end()); 322 | } 323 | } 324 | 325 | int num_box = (int)total_boxes_.size(); 326 | vector res_boxes; 327 | if (num_box != 0) { 328 | res_boxes = NMS(total_boxes_, 0.5f, 'u'); 329 | BBoxRegression(res_boxes); 330 | BBoxPadSquare(res_boxes, width, height); 331 | } 332 | delete pImg; 333 | return res_boxes; 334 | } 335 | 336 | static std::vector NextStage(const cv::Mat& image, vector &pre_stage_res, int input_w, int input_h, int stage_num, const float threshold) { 337 | vector res; 338 | int batch_size = pre_stage_res.size(); 339 | 340 | switch (stage_num) { 341 | case 2: { 342 | 343 | for (int n = 0; n < batch_size; ++n) 344 | { 345 | FaceBox &box = pre_stage_res[n].bbox; 346 | cv::Mat roi = image(cv::Rect(cv::Point((int)box.xmin, (int)box.ymin), cv::Point((int)box.xmax, (int)box.ymax))).clone(); 347 | 348 | //cv::imshow("face", roi); 349 | //cv::waitKey(0); 350 | 351 | MNN::CV::Matrix trans; 352 | trans.postScale(1.0/input_w, 1.0/input_h); 353 | trans.postScale(roi.cols, roi.rows); 354 | pretreat_data->setMatrix(trans); 355 | 356 | uint8_t *pImg = get_img(roi); 357 | pretreat_data->convert(pImg, roi.cols, roi.rows, 0, r_input); 358 | delete pImg; 359 | RNet_->runSession(sess_r); 360 | 361 | float * confidence = r_out_pro->host(); 362 | float * reg_box = r_out_reg->host(); 363 | 364 | float conf = confidence[1]; 365 | if (conf >= threshold) { 366 | FaceInfo info; 367 | info.bbox.score = conf; 368 | info.bbox.xmin = pre_stage_res[n].bbox.xmin; 369 | info.bbox.ymin = pre_stage_res[n].bbox.ymin; 370 | info.bbox.xmax = pre_stage_res[n].bbox.xmax; 371 | info.bbox.ymax = pre_stage_res[n].bbox.ymax; 372 | for (int i = 0; i < 4; ++i) { 373 | info.bbox_reg[i] = reg_box[i]; 374 | } 375 | res.push_back(info); 376 | 377 | } 378 | } 379 | break; 380 | } 381 | case 3:{ 382 | //#ifdef _OPENMP 383 | //#pragma omp parallel for num_threads(threads_num) 384 | //#endif 385 | for (int n = 0; n < batch_size; ++n) 386 | { 387 | FaceBox &box = pre_stage_res[n].bbox; 388 | cv::Mat roi = image(cv::Rect(cv::Point((int)box.xmin, (int)box.ymin), cv::Point((int)box.xmax, (int)box.ymax))).clone(); 389 | 390 | //cv::imshow("face", roi); 391 | //cv::waitKey(0); 392 | 393 | MNN::CV::Matrix trans; 394 | trans.postScale(1.0f/input_w, 1.0f/input_h); 395 | trans.postScale(roi.cols, roi.rows); 396 | pretreat_data->setMatrix(trans); 397 | uint8_t *pImg = get_img(roi); 398 | pretreat_data->convert(pImg, roi.cols, roi.rows, 0, o_input); 399 | delete pImg; 400 | ONet_->runSession(sess_o); 401 | float * confidence = o_out_pro->host(); 402 | float * reg_box = o_out_reg->host(); 403 | float * reg_landmark = o_out_lank->host(); 404 | 405 | float conf = confidence[1]; 406 | //std::cout<<"stage three:"<= threshold) { 408 | FaceInfo info; 409 | info.bbox.score = conf; 410 | info.bbox.xmin = pre_stage_res[n].bbox.xmin; 411 | info.bbox.ymin = pre_stage_res[n].bbox.ymin; 412 | info.bbox.xmax = pre_stage_res[n].bbox.xmax; 413 | info.bbox.ymax = pre_stage_res[n].bbox.ymax; 414 | for (int i = 0; i < 4; ++i) { 415 | info.bbox_reg[i] = reg_box[i]; 416 | } 417 | float w = info.bbox.xmax - info.bbox.xmin + 1.f; 418 | float h = info.bbox.ymax - info.bbox.ymin + 1.f; 419 | for (int i = 0; i < 5; ++i) { 420 | info.landmark[2 * i] = reg_landmark[2 * i] * w + info.bbox.xmin; 421 | info.landmark[2 * i + 1] = reg_landmark[2 * i + 1] * h + info.bbox.ymin; 422 | } 423 | res.push_back(info); 424 | } 425 | } 426 | break; 427 | } 428 | default: 429 | return res; 430 | break; 431 | } 432 | return res; 433 | } 434 | 435 | vector FaceDetect::Detect(const cv::Mat& image, const int min_face, const int stage) { 436 | 437 | vector pnet_res; 438 | vector rnet_res; 439 | vector onet_res; 440 | 441 | if (stage >= 1) { 442 | pnet_res = ProposalNet(image, min_face, threhold_p, factor); 443 | } 444 | //std::cout<<"p size is:"<= 2 && pnet_res.size()>0) { 446 | if (pnet_max_detect_num < (int)pnet_res.size()) { 447 | pnet_res.resize(pnet_max_detect_num); 448 | } 449 | rnet_res = NextStage(image, pnet_res, 24, 24, 2, threhold_r); 450 | rnet_res = NMS(rnet_res, iou_threhold, 'u'); 451 | BBoxRegression(rnet_res); 452 | BBoxPadSquare(rnet_res, image.cols, image.rows); 453 | } 454 | //std::cout<<"r size is:"<= 3 && rnet_res.size()>0) { 456 | onet_res = NextStage(image, rnet_res, 48, 48, 3, threhold_o); 457 | BBoxRegression(onet_res); 458 | onet_res = NMS(onet_res, iou_threhold, 'm'); 459 | BBoxPad(onet_res, image.cols, image.rows); 460 | } 461 | if (stage == 1) { 462 | return pnet_res; 463 | } 464 | else if (stage == 2) { 465 | return rnet_res; 466 | } 467 | else if (stage == 3) { 468 | return onet_res; 469 | } 470 | else { 471 | return onet_res; 472 | } 473 | } 474 | 475 | 476 | static std::vector extractMaxFace(std::vector boundingBox_) 477 | { 478 | if (boundingBox_.empty()) { 479 | return std::vector{}; 480 | } 481 | /* 482 | sort(boundingBox_.begin(), boundingBox_.end(), CompareBBox); 483 | for (std::vector::iterator itx = boundingBox_.begin() + 1; itx != boundingBox_.end();) { 484 | itx = boundingBox_.erase(itx); 485 | } 486 | */ 487 | float max_area = 0; 488 | int index = 0; 489 | for (int i = 0; i < boundingBox_.size(); ++i){ 490 | FaceBox select_bbox = boundingBox_[i].bbox; 491 | float area1 = static_cast((select_bbox.xmax - select_bbox.xmin + 1) * (select_bbox.ymax - select_bbox.ymin + 1)); 492 | if (area1 > max_area){ 493 | max_area = area1; 494 | index = i; 495 | } 496 | } 497 | return std::vector{boundingBox_[index]}; 498 | } 499 | 500 | std::vector FaceDetect::Detect_MaxFace(const cv::Mat& img, const int min_face, const int stage){ 501 | vector pnet_res; 502 | vector rnet_res; 503 | vector onet_res; 504 | 505 | //total_boxes_.clear(); 506 | //candidate_boxes_.clear(); 507 | 508 | int width = img.cols; 509 | int height = img.rows; 510 | float scale = 12.0f / min_face; 511 | float minWH = std::min(height, width) *scale; 512 | std::vector scales; 513 | while (minWH >= 12) { 514 | scales.push_back(scale); 515 | minWH *= factor; 516 | scale *= factor; 517 | } 518 | 519 | //sort(scales.begin(), scales.end()); 520 | std::reverse(scales.begin(), scales.end()); 521 | 522 | uint8_t *pImg = get_img(img); 523 | for (int i = 0; i < scales.size(); i++) { 524 | int ws = (int)std::ceil(width*scales[i]); 525 | int hs = (int)std::ceil(height*scales[i]); 526 | std::vector inputDims = {1, 3, hs, ws}; 527 | PNet_->resizeTensor(p_input, inputDims); 528 | PNet_->resizeSession(sess_p); 529 | 530 | MNN::CV::Matrix trans; 531 | trans.postScale(1.0f/ws, 1.0f/hs); 532 | trans.postScale(width, height); 533 | pretreat_data->setMatrix(trans); 534 | pretreat_data->convert(pImg, width, height, 0, p_input); 535 | 536 | PNet_->runSession(sess_p); 537 | float * confidence = p_out_pro->host(); 538 | float * reg = p_out_reg->host(); 539 | 540 | int feature_w = p_out_pro->width(); 541 | int feature_h = p_out_pro->height(); 542 | 543 | GenerateBBox(confidence, reg, feature_w, feature_h, scales[i], threhold_p); 544 | std::vector bboxes_nms = NMS(candidate_boxes_, 0.5f, 'u'); 545 | 546 | //nmsTwoBoxs(bboxes_nms, pnet_res, 0.5); 547 | if (bboxes_nms.size() > 0) { 548 | pnet_res.insert(pnet_res.end(), bboxes_nms.begin(), bboxes_nms.end()); 549 | }else{ 550 | continue; 551 | } 552 | BBoxRegression(pnet_res); 553 | BBoxPadSquare(pnet_res, width, height); 554 | 555 | bboxes_nms.clear(); 556 | bboxes_nms = NextStage(img, pnet_res, 24, 24, 2, threhold_r); 557 | bboxes_nms = NMS(bboxes_nms, iou_threhold, 'u'); 558 | //nmsTwoBoxs(bboxes_nms, rnet_res, 0.5) 559 | if (bboxes_nms.size() > 0) { 560 | rnet_res.insert(rnet_res.end(), bboxes_nms.begin(), bboxes_nms.end()); 561 | }else{ 562 | pnet_res.clear(); 563 | continue; 564 | } 565 | BBoxRegression(rnet_res); 566 | BBoxPadSquare(rnet_res, img.cols, img.rows); 567 | 568 | 569 | onet_res = NextStage(img, rnet_res, 48, 48, 3, threhold_r); 570 | 571 | BBoxRegression(onet_res); 572 | onet_res = NMS(onet_res, iou_threhold, 'm'); 573 | BBoxPad(onet_res, img.cols, img.rows); 574 | 575 | if(onet_res.size() < 1){ 576 | pnet_res.clear(); 577 | rnet_res.clear(); 578 | continue; 579 | }else{ 580 | onet_res = extractMaxFace(onet_res); 581 | delete pImg; 582 | return onet_res; 583 | } 584 | } 585 | delete pImg; 586 | return std::vector{}; 587 | } 588 | 589 | } 590 | -------------------------------------------------------------------------------- /library/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 | -------------------------------------------------------------------------------- /library/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 | }; 1610 | } // namespace CV 1611 | } // namespace MNN 1612 | #endif 1613 | --------------------------------------------------------------------------------