├── 109128646.png ├── CMakeLists.txt ├── README.md ├── cn-tw.md ├── cn-zh.md ├── contact.jpg ├── en.md ├── images ├── 000001.jpg ├── 1.jpg ├── 20201005155933906.jpg ├── 20201005155936164.jpg ├── 20201005161220950.jpg ├── 20201005162000315.jpg ├── 20201005164100541.jpg ├── 20201006162604178.jpg ├── 3.jpg ├── 5080e34da5dd1c2f.jpg ├── 666.jpg ├── 6661.jpg ├── mmexport1566183579726.jpg ├── plate.jpg └── plate.png ├── include ├── AutoTime.hpp ├── ErrorCode.hpp ├── HalideRuntime.h ├── ImageProcess.hpp ├── Interpreter.hpp ├── MNNDefine.h ├── MNNForwardType.h ├── MNNSharedContext.h ├── Matrix.h ├── Rect.h ├── Tensor.hpp ├── mlpdr │ ├── MLPDR.h │ └── label.hpp ├── stb_image.h ├── stb_image_resize.h └── stb_image_write.h ├── install.md ├── install_en.md ├── kr.md ├── lib-arm └── libMNN.so ├── lib-win32 ├── MNN.dll ├── MNN.lib └── MNN_Express.dll ├── lib └── libMNN.so ├── light-LPR.png ├── models ├── det1.caffemodel ├── det1.mnn ├── det1.prototxt ├── det2.caffemodel ├── det2.mnn ├── det2.prototxt ├── det3.caffemodel ├── det3.mnn ├── det3.prototxt ├── lpc.mnn └── lpr.mnn └── src ├── examples ├── CMakeLists.txt └── demo.cpp └── mlpdr ├── CMakeLists.txt ├── MLPDR.cpp └── java_api.cpp /109128646.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/lqian/light-LPR/6449d571739d164f3fd84d8862883f1f6dadb8ae/109128646.png -------------------------------------------------------------------------------- /CMakeLists.txt: -------------------------------------------------------------------------------- 1 | cmake_minimum_required(VERSION 3.0.0) 2 | 3 | project(Light-LPR C CXX) 4 | 5 | set(_MAIJOR_VERION 1) 6 | set(_MINOR_VERION 1) 7 | set(_PATCH_VERION 0) 8 | 9 | ## redefine executable and library output path 10 | set(EXECUTABLE_OUTPUT_PATH ${PROJECT_BINARY_DIR}/bin) 11 | set(LIBRARY_OUTPUT_PATH ${PROJECT_BINARY_DIR}/lib) 12 | 13 | option(BUILD_EXAMPLES "build examples" ON) 14 | option(BUILD_SHARED_LIBS "build shared labraries, else build archive" ON) 15 | option(BUILD_JAVA_API "build API for java language" ON) 16 | 17 | 18 | # need the real opencv install path if build on windows 19 | set(OpenCV_DIR "C:\\Users\\xinhuan\\Desktop\\workspace\\opencv\\build-win32\\install") 20 | 21 | ## required package 22 | find_package(OpenMP REQUIRED) 23 | find_package(OpenCV 3.0.0 REQUIRED COMPONENTS core imgcodecs imgproc highgui) 24 | 25 | if (OPENMP_FOUND) 26 | add_definitions("-D_OPENMP") 27 | endif() 28 | 29 | include_directories( 30 | include 31 | ) 32 | 33 | if (WIN32 OR MSVC OR MSYS OR MINGW) 34 | set(CMAKE_CXX_STANDARD 11) 35 | set(CMAKE_WINDOWS_EXPORT_ALL_SYMBOLS TRUE) 36 | link_directories( 37 | lib-win32 38 | ) 39 | else() 40 | # c++11 required 41 | set(CMAKE_CXX_STANDARD 11) 42 | set(CMAKE_CXX_STANDARD_REQUIRED ON) 43 | set(CMAKE_CXX_FLAGS "-std=c++11 ${CMAKE_CXX_FLAGS}") 44 | if(DEFINED LIGHT_LPR_ARCH) 45 | if (${LIGHT_LPR_ARCH} STREQUAL "arm") 46 | link_directories( 47 | lib-arm 48 | ) 49 | else() 50 | link_directories( 51 | lib 52 | ) 53 | endif() 54 | else() 55 | link_directories( 56 | lib 57 | ) 58 | endif() 59 | endif() 60 | 61 | 62 | add_subdirectory(src/mlpdr) 63 | 64 | if(BUILD_EXAMPLES) 65 | add_subdirectory(src/examples) 66 | endif() 67 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 |
 2 |  __       __    _______  __    __  .___________.        __      .______   .______      
 3 | |  |     |  |  /  _____||  |  |  | |           |       |  |     |   _  \  |   _  \     
 4 | |  |     |  | |  |  __  |  |__|  | `---|  |----`______ |  |     |  |_)  | |  |_)  |    
 5 | |  |     |  | |  | |_ | |   __   |     |  |    |______||  |     |   ___/  |      /     
 6 | |  `----.|  | |  |__| | |  |  |  |     |  |            |  `----.|  |      |  |\  \----.
 7 | |_______||__|  \______| |__|  |__|     |__|            |_______|| _|      | _| `._____|
 8 | 
9 | 10 | Light-LPR or named MLPR is an open source project aimed at license plate recognition that can run on embedded devices, mobile phones, and x86 platforms. It aims to support license plate recognition in various scenarios. The accuracy rate of license plate character recognition exceeds 99.95%, and the comprehensive recognition accuracy rate exceeds 99%. The project supports multi-lingual and multi-country license plates. For all test video please visit [Light-LPR official video resources](https://space.bilibili.com/1190293246). 11 | 12 | [ English ](en.md) | [ 中文 ](cn-zh.md) | [ 中文繁体 ](cn-tw.md)| [ 한국어 ](kr.md) 13 | 14 | Light-LPR(MLPR)是一个瞄准可以在嵌入式设备、手机端和x86平台上运行的车牌识别开源项目,旨在支持各种场景的车牌识别,车牌字符识别准确率超99.95%,综合识别准确率超过99%,支持多国家多语言的车牌识别。 15 | 16 | Light-LPR(MLPR)は、埋め込みデバイス、携帯電話、x86プラットフォームで実行できるナンバープレート認識を目的としたオープンソースプロジェクトです。さまざまなシナリオでナンバープレート認識をサポートすることを目的としています。ナンバープレート文字認識の精度は99.95%を超え、包括的な認識精度は99%を超えます。 多国間および多言語のナンバープレート認識をサポートします。 17 | 18 | Light-LPR(MLPR)은 임베디드 장치, 휴대폰 및 x86 플랫폼에서 실행할 수있는 번호판 인식을 목표로하는 오픈 소스 프로젝트로, 다양한 시나리오에서 번호판 인식을 지원하는 것을 목표로하며, 번호판 문자 인식의 정확도는 99.95 %를 초과하고 포괄적 인 인식 정확도는 99를 초과합니다. %, 다중 국가, 다중 언어 및 다중 유형 번호판 인식을 지원합니다. 19 | 20 | - We look forward to cooperating to develop license plate recognition. 21 | - ナンバープレート認識の開発にご協力いただければ幸いです。 22 | - 우리는 번호판 인식을 개발하기 위해 협력을 기대합니다 23 | - Nous sommes impatients de travailler avec vous pour développer la reconnaissance des plaques d'immatriculation 24 | - Мы с нетерпением ждем совместной работы с вами по разработке системы распознавания автомобильных номеров 25 | - Expectamus ad vos cum operantes develop per licentiam laminam recognition 26 | - نتطلع إلى العمل معك لتطوير التعرف على لوحة الترخيص 27 | - เราหวังว่าจะทำงานร่วมกับคุณเพื่อพัฒนาการจดจำป้ายทะเบียน 28 | - हम लाइसेंस प्लेट मान्यता विकसित करने के लिए आपके साथ काम करने के लिए तत्पर हैं 29 | 30 | 31 | ## Change Log 32 | - 2023-05 LLPR-G1 Alpha高位相机发布,最大可同时跟踪6车位;支持云台、变焦;横栏/竖栏识别;支持远程运维。 33 | - 2023-01 LLPR-Z1E低功耗模组,支持免布线路内停车场景。 34 | - 2022-12 LLPR-P4L/P4S发布,涵盖充电桩车位管理吊装场景。 35 | - 2022-08 发布[LLPR云](cloud.lightlpr.com),LLPR全线产品可以接入[LLPR云](cloud.lightlpr.com),支持远程运维,远程批量更新。 36 | - 2022-06 LLPR全线产品支持4G远程播放1080P视频,支持WEB管理工具。 37 | - 2022-05 LLPR-P4充电桩专用模块发布,最大支持管理2个充电位,对接2个地锁。 38 | - 2022-03 LLPR-Z1路内低位停车桩模块发布,最大可支持识别2个路边停车位。 39 | - 2022-01 LLPR-E5系列发布,效能翻倍,独有LBOX识别,化繁为简,50毫秒开闸,领先一步。 40 | - 2021-11 LLPR-400-CN中国大陆系列模组[LLPR淘宝小铺](https://lightlpr.taobao.com/)上线 41 | - 2021-09 LLPR-400E系列发布,支持路侧视频桩车牌识别场景 42 | - 2021-06 LLPR-313E发布,支持充电桩、地下车库场景识别,最多可同时识别3个车牌。支持中国、台湾和南韩车牌识别 43 | - 2021-04 LLPR-CN-330E发布支持车载巡检车牌识别; light-lpr-api 1.4.5发布,开放设备管理接口 44 | - 2021-02 第四代识别引擎light-lpr-pro 4.0发布,全力支持中国大陆地区双行车牌的识别,改善对小尺寸车牌识别的效果 45 | - 2021-01 硬件识别引擎支持FTP上传,HTTP上传,[light-lpr-httpdemo-0.0.1](https://github.com/lqian/light-lpr-httpdemo)发布 46 | - 2020-11 light-lpr-api 1.2.0发布,支持c/c++, c#, Java, android. 47 | - 2020-10 识别模组管理工具Light LPR Tool发布,[演示视频链接](https://pan.baidu.com/s/16D2S6StjKsv879nMFSZAmQ) 提取码: ewqb , [演示视频2](https://pan.baidu.com/s/1wV_agW71bthTpzhxKLf6cA) 提取码: cun3 48 | - 2020-08 第三代硬件识别引擎 49 | - 2020-06 第三代识别引擎 50 | - 2020-04 2019 스타일 번호판, 1973 스타일 번호판, 1996 스타일 번호판에 대한 완벽한 지원 51 | - 2020-04 LightLPR apk支持 android 5,6,7,8,9,10 52 | - 2020-01 한국 번호판 인식 지원 53 | - 2019-12 支持台湾车牌识别 54 | - 2019-11 开源Windows x86编译方法,提供Opencv-3.4.2 Windows预编译包 55 | - 2019-08 开源支持黄牌识别的模型和车牌颜色识别模型 56 | - 2019-07 开源支持中华人民共和国境内蓝色车牌识别模型 57 | 58 | 59 | ## License plate recognization benchmark for 1080P image 60 | 61 | | | CPU | Memory | average cost of community version (ms) | average cost of Commercial version(ms) | 62 | | :-------- | :----- | :----: | ----: | ----: | 63 | | X86 | i5-8265 | 8G | 451 | < 50 | 64 | | ARM | A53 | 1G | 1532| < 160 | 65 | | Huwei P20 pro| ... | 4G | - | < 100 | 66 | | LLPR-420E | 900MHz | 256M | - | < 45 (NPU support) | 67 | | LLPR-410E | 700MHz | 256M | - | < 85 (NPU support) | 68 | 69 | ## API supported programming Language 70 | - C/C++ 71 | - C# 72 | - JAVA 73 | - Android 74 | 75 | ## License 76 | LGPL 77 | 78 | ## Please refer to the project file for the installation method [install_en.md](install_en.md) 79 | ## Others 80 | - official site: www.lightlpr.com 81 | - Email: link.com@yeah.net, Phone: +86 18010870244, Skype: +86 18010870244 82 | - For technical communication, data exchanges and donations, please contact the author or join QQ group, light-LPR group number: 813505078, Image Processing Analysis Machine Vision gropu number, 109128646. 83 | - ![](light-LPR.png) ![](109128646.png) 84 | -------------------------------------------------------------------------------- /cn-tw.md: -------------------------------------------------------------------------------- 1 | 2 | Light-LPR是一個瞄準可以在嵌入式設備、手機端和x86平台上運行的車牌識別開源項目,旨在支持各種場景的車牌識別,車牌字符識別準確率超99.95%,綜合識別準確率超過99%,支持多國家多語言的車牌識別。 3 | 4 | [ About ](README.md) | [ English ](en.md) | [ 中文 ](cn-zh.md) | [ 中文繁体 ](cn-tw.md)| [ 한국어 ](kr.md) 5 | 6 | ## 商业版支持的中华人民共和国台湾车牌 7 | | 车牌种类 | 7代(1992式) | 8代1版(2012式) | 8代2版(2014式) | 8 | | --------: | :-----: | :----: | :----: | 9 | | 自用小客車/自用小貨車 | Y | Y | Y | 10 | | 自用大客車 | Y | Y | Y | 11 | | 自用大貨車 | Y | Y | Y | 12 | | 身心障礙者專用車 | Y | Y | Y | 13 | | 營業小客車 | Y | Y | Y | 14 | | 營業小貨車 | - | E | Y | 15 | | 營業大客車 | Y | E | Y | 16 | | 營業大貨車 | Y | E | Y | 17 | | 營業大貨車 | Y | E | Y | 18 | | 營業大貨車 | Y | E | Y | 19 | | 營業貨櫃曳引車 | Y | E | Y | 20 | | 租賃小客車/租賃小貨車 | Y | E | Y | 21 | | 交通車 | Y | E | Y | 22 | | 自用拖車 | - | Y | Y | 23 | | 營業拖車 | - | Y | Y | 24 | | 電動自用小客車/電動自用小貨車 | Y | Y | Y | 25 | | 電動營業小客車 | Y | Y | Y | 26 | | 電動身心障礙者專用車 | - | Y | Y | 27 | | 電動營業大客車 | Y | Y | Y | 28 | | 大型重型機車 I | - | Y | Y | 29 | | 大型重型機車 II | - | Y | Y | 30 | | 普通重型機車 | E | Y | Y | 31 | | 普通輕型機車 | E | Y | Y | 32 | | 小型輕型機車 | E | Y | Y | 33 | | 電動大型重型機車 I | - | - | Y | 34 | | 電動大型重型機車 II | - | - | Y | 35 | | 電動普通重型機車 | - | - | Y | 36 | | 電動普通重型機車 | - | - | Y | 37 | | 電動小型輕型機車 | - | - | Y | 38 | 39 | 备注: Y 支持,- 不支持或者无此车牌种类,E 处于评估阶段 40 | 41 | ## 1080P图片识别基准性能 42 | | 平台 | CPU型号 | 内存 | 开源版平均识别时间(ms) | 专业和完全版平均识别时间(ms) | 43 | | :-------- | :----- | :----: | ----: | ----: | 44 | | X86 | i5-8265 | 8G | 451 | <50 | 45 | | ARM | A53 | 1G | 1532| <160 | 46 | | Huwei P20 pro| ... | 4G | - | <100 | 47 | | 3516DV300 | A79 | 256M | - | <45 (AI算力支持) | 48 | | 3516CV500 | A79 | 256M | - | <85 (AI算力支持) | 49 | 50 | 51 | ## API支持的編程語言 52 | - C/C++ 53 | - C# 54 | - JAVA 55 | 56 | ## 安裝方法請參考項目文件 [install.md](install.md) 57 | ## 其他 58 | - 商務合作聯繫: link.com@yeah.net, 手機: +86 18010870244 59 | - 技術交流、數據交流和捐贈請聯繫作者或加QQ群,light-LPR群號:813505078, 圖像處理分析機器視覺 109128646[已滿], 60 | - ![](light-LPR.png) ![](109128646.png) -------------------------------------------------------------------------------- /cn-zh.md: -------------------------------------------------------------------------------- 1 | Light-LPR是一个瞄准可以在嵌入式设备、手机端和x86平台上运行的车牌识别开源项目,旨在支持各种场景的车牌识别,车牌字符识别准确率超99.95%,综合识别准确率超过99%,支持多国家多语言的车牌识别。 2 | 3 | [ About ](README.md) | [ English ](en.md) | [ 中文 ](cn-zh.md) | [ 中文繁体 ](cn-tw.md)| [ 한국어 ](kr.md) 4 | 5 | ## 支持的中华人民共和国大陆车牌 6 | 7 | | 车牌种类 | 开源版 | 专业版 | 完全版 | 8 | | --------: | :-----: | :----: | :----: | 9 | | 蓝 | Y | Y | Y | 10 | | 黄 | Y | Y | Y | 11 | | 新能源 | - | Y | Y | 12 | | 大型新能源 | - | Y | Y | 13 | | 教练车牌 | - | Y | Y | 14 | | 双层黄牌| - | Y | Y | 15 | | 摩托车牌 | - | Y | Y | 16 | | 警牌 | - | - | Y| 17 | | 军牌 | - | - | Y| 18 | | 双层军牌 | - | - |Y | 19 | | 武警车牌 | - | - | Y| 20 | | 双层武警牌照 | - | - | Y| 21 | | 港澳通行牌 | - | - | Y | 22 | | 普通黑牌 | - | - | Y | 23 | | 应急车牌 | - | - | E | 24 | | 民航车牌 | - | - | E | 25 | | 使、领馆车牌 | - | - | E | 26 | | 临牌 | - | - | E | 27 | | 低速农用车牌 | - | - | - | 28 | 29 | 备注: Y 支持,- 不支持,E 处于评估阶段 30 | 31 | ## 1080P图片识别基准性能 32 | | 平台 | CPU型号 | 内存 | 开源版平均识别时间(ms) | 专业和完全版平均识别时间(ms) | 33 | | :-------- | :----- | :----: | ----: | ----: | 34 | | X86 | i5-8265 | 8G | 451 | <50 | 35 | | ARM | A53 | 1G | 1532| <160 | 36 | | Huwei P20 pro| ... | 4G | - | <100 | 37 | | 3516DV300 | A79 | 256M | - | <45 (AI算力支持) | 38 | | 3516CV500 | A79 | 256M | - | <85 (AI算力支持) | 39 | 40 | ## API支持的编程语言 41 | - C/C++ 42 | - C# 43 | - JAVA 44 | 45 | 46 | ## 安装方法请参考项目文件 [install.md](install.md) 47 | ## 其他 48 | - 商务合作联系: link.com@yeah.net, 微信: +86 18010870244 49 | - 技术交流、数据交流和捐赠请联系作者或加QQ群,light-LPR群号:813505078, 图像处理分析机器视觉 109128646[已满], 50 | - ![](light-LPR.png) ![](109128646.png) -------------------------------------------------------------------------------- /contact.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/lqian/light-LPR/6449d571739d164f3fd84d8862883f1f6dadb8ae/contact.jpg -------------------------------------------------------------------------------- /en.md: -------------------------------------------------------------------------------- 1 | Light-LPR is an open source project aimed at license plate recognition that can run on embedded devices, mobile phones, and x86 platforms. It aims to support license plate recognition in various scenarios. The accuracy rate of license plate character recognition exceeds 99.95%, and the comprehensive recognition accuracy rate exceeds 99%. The project supports multi-lingual and multi-country license plates. 2 | 3 | [ About ](README.md) | [ English ](en.md) | [ 中文 ](cn-zh.md) | [ 中文繁体 ](cn-tw.md)| [ 한국어 ](kr.md) 4 | 5 | ## Change Log 6 | - 2022-08 release [LLPR cloud](cloud.lightlpr.com),support remote maintenance and update batch。 7 | - 2022-06 LLPR product supports to play 1080P video via 4G. support WEB management tool。 8 | - 2022-05 LLPR-P4 parking spot management module, support 2 spot and 2 locks。 9 | - 2022-03 LLPR-Z1 is released for roadside parking management,support 2-max roadside parking spot. 10 | - 2022-01 LLPR-E5 series released, LBOX-tech recognize license plate number within 50 ms 11 | - 2021-11 LLPR-400-CN Chinese mainlaind serial of hardware modules [Light-LPR taobao shop](https://lightlpr.taobao.com/) is online. 12 | - 2021-09 LLPR-400E is released, supports road-side video pile license plate recognition scenes. 13 | - 2021-06 LLPR-313E is released, supporting scene recognition of charging piles and underground garages, and can recognize up to 3 license plates at the same time. Support license plate recognition in China, Taiwan and South Korea 14 | - 2021-04 LLPR-CN-330E is released to support vehicle-mounted inspection license plate recognition; light-lpr-api 1.4.5 is released, and the device management interface is opened 15 | - 2021-02 The fourth-generation recognition engine light-lpr-pro 4.0 is released, which fully supports the recognition of dual-line license plates in mainland China and improves the recognition of small-size license plates 16 | - 2021-01 hardware recognition engine supports FTP upload and HTTP upload, [light-lpr-httpdemo-0.0.1](https://github.com/lqian/light-lpr-httpdemo) is released. 17 | - 2020-11 replease light-lpr-api 1.2.0 for development with LightLPR device, support c/c++, c#, Java, Android programming. 18 | - 2020-10 release light-lpr-tool for LightLPR device management, and test on road. [test video1](https://pan.baidu.com/s/16D2S6StjKsv879nMFSZAmQ) code: ewqb , [test video1](https://pan.baidu.com/s/1wV_agW71bthTpzhxKLf6cA) code: cun3 19 | - 2020-08 release LightLPR engine 3.0 at Hisilcon 3516CV500 and 3516DV300 20 | - 2020-06 release LightLPR engine 3.0 21 | - 2020-04 full Support for 2019 new license plates of the Republic of South Korea 22 | - 2020-04 LightLPR apk support device which run android 5, android 6, android 7, android 8, android 9, android 10 23 | - 2020-01 support South Korean License Plate Recognization 24 | - 2019-12 Support Taiwan license plate recognition 25 | - 2019-11 Open source Windows x86 compilation method, providing Opencv-3.4.2 Windows pre-compiled package 26 | - 2019-08 Open source model supporting yellow card recognition and license plate color recognition model 27 | - 2019-07 Open source supports the blue license plate recognition model in the People's Republic of China 28 | 29 | ## License plate recognization benchmark for 1080P image 30 | 31 | | | CPU | Memory | average cost of community version (ms) | average cost of Commercial version(ms) | 32 | | :-------- | :----- | :----: | ----: | ----: | 33 | | X86 | i5-8265 | 8G | 451 | < 50 | 34 | | ARM | A53 | 1G | 1532| < 160 | 35 | | Huwei P20 pro| ... | 4G | - | < 100 | 36 | | LLPR-320E | ... | | - | < 45 (NPU support) | 37 | | LLPR-310E | ... | | - | < 85 (NPU support) | 38 | 39 | ## License 40 | LGPL 41 | 42 | ## [install](install_en.md) 43 | 44 | ## Others 45 | - official site: www.lightlpr.com 46 | - Contact: link.com@yeah.net, WeChat: +86 18010870244, Skype: +86 18010870244 -------------------------------------------------------------------------------- /images/000001.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/lqian/light-LPR/6449d571739d164f3fd84d8862883f1f6dadb8ae/images/000001.jpg -------------------------------------------------------------------------------- /images/1.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/lqian/light-LPR/6449d571739d164f3fd84d8862883f1f6dadb8ae/images/1.jpg -------------------------------------------------------------------------------- /images/20201005155933906.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/lqian/light-LPR/6449d571739d164f3fd84d8862883f1f6dadb8ae/images/20201005155933906.jpg -------------------------------------------------------------------------------- /images/20201005155936164.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/lqian/light-LPR/6449d571739d164f3fd84d8862883f1f6dadb8ae/images/20201005155936164.jpg -------------------------------------------------------------------------------- /images/20201005161220950.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/lqian/light-LPR/6449d571739d164f3fd84d8862883f1f6dadb8ae/images/20201005161220950.jpg -------------------------------------------------------------------------------- /images/20201005162000315.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/lqian/light-LPR/6449d571739d164f3fd84d8862883f1f6dadb8ae/images/20201005162000315.jpg -------------------------------------------------------------------------------- /images/20201005164100541.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/lqian/light-LPR/6449d571739d164f3fd84d8862883f1f6dadb8ae/images/20201005164100541.jpg -------------------------------------------------------------------------------- /images/20201006162604178.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/lqian/light-LPR/6449d571739d164f3fd84d8862883f1f6dadb8ae/images/20201006162604178.jpg -------------------------------------------------------------------------------- /images/3.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/lqian/light-LPR/6449d571739d164f3fd84d8862883f1f6dadb8ae/images/3.jpg -------------------------------------------------------------------------------- /images/5080e34da5dd1c2f.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/lqian/light-LPR/6449d571739d164f3fd84d8862883f1f6dadb8ae/images/5080e34da5dd1c2f.jpg -------------------------------------------------------------------------------- /images/666.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/lqian/light-LPR/6449d571739d164f3fd84d8862883f1f6dadb8ae/images/666.jpg -------------------------------------------------------------------------------- /images/6661.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/lqian/light-LPR/6449d571739d164f3fd84d8862883f1f6dadb8ae/images/6661.jpg -------------------------------------------------------------------------------- /images/mmexport1566183579726.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/lqian/light-LPR/6449d571739d164f3fd84d8862883f1f6dadb8ae/images/mmexport1566183579726.jpg -------------------------------------------------------------------------------- /images/plate.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/lqian/light-LPR/6449d571739d164f3fd84d8862883f1f6dadb8ae/images/plate.jpg -------------------------------------------------------------------------------- /images/plate.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/lqian/light-LPR/6449d571739d164f3fd84d8862883f1f6dadb8ae/images/plate.png -------------------------------------------------------------------------------- /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 | -------------------------------------------------------------------------------- /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 | -------------------------------------------------------------------------------- /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 | -------------------------------------------------------------------------------- /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 | -------------------------------------------------------------------------------- /include/Interpreter.hpp: -------------------------------------------------------------------------------- 1 | // 2 | // Interpreter.hpp 3 | // MNN 4 | // 5 | // Created by MNN on 2018/07/23. 6 | // Copyright © 2018, Alibaba Group Holding Limited 7 | // 8 | 9 | #ifndef Interpreter_hpp 10 | #define Interpreter_hpp 11 | 12 | #include 13 | #include 14 | #include 15 | #include "ErrorCode.hpp" 16 | #include "MNNForwardType.h" 17 | #include "Tensor.hpp" 18 | 19 | namespace MNN { 20 | 21 | /** session schedule config */ 22 | struct ScheduleConfig { 23 | /** which tensor should be kept */ 24 | std::vector saveTensors; 25 | /** forward type */ 26 | MNNForwardType type = MNN_FORWARD_CPU; 27 | /** number of threads in parallel */ 28 | int numThread = 4; 29 | 30 | /** subpath to run */ 31 | struct Path { 32 | std::vector inputs; 33 | std::vector outputs; 34 | 35 | enum Mode { 36 | /** 37 | * Op Mode 38 | * - inputs means the source op, can NOT be empty. 39 | * - outputs means the sink op, can be empty. 40 | * The path will start from source op, then flow when encounter the sink op. 41 | * The sink op will not be compute in this path. 42 | */ 43 | Op = 0, 44 | 45 | /** 46 | * Tensor Mode (NOT supported yet) 47 | * - inputs means the inputs tensors, can NOT be empty. 48 | * - outputs means the outputs tensors, can NOT be empty. 49 | * It will find the pipeline that compute outputs from inputs. 50 | */ 51 | Tensor = 1 52 | }; 53 | 54 | /** running mode */ 55 | Mode mode = Op; 56 | }; 57 | Path path; 58 | 59 | /** backup backend used to create execution when desinated backend do NOT support any op */ 60 | MNNForwardType backupType = MNN_FORWARD_CPU; 61 | 62 | /** extra backend config */ 63 | BackendConfig* backendConfig = nullptr; 64 | }; 65 | 66 | class Session; 67 | struct Content; 68 | class Tensor; 69 | class Backend; 70 | 71 | class MNN_PUBLIC OperatorInfo { 72 | struct Info; 73 | 74 | public: 75 | /** Operator's name*/ 76 | const std::string& name() const; 77 | 78 | /** Operator's type*/ 79 | const std::string& type() const; 80 | 81 | /** Operator's flops, in M*/ 82 | float flops() const; 83 | 84 | protected: 85 | OperatorInfo(); 86 | ~OperatorInfo(); 87 | Info* mContent; 88 | }; 89 | 90 | typedef std::function&, const std::string& /*opName*/)> TensorCallBack; 91 | typedef std::function&, const OperatorInfo*)> TensorCallBackWithInfo; 92 | 93 | /** net data holder. multiple sessions could share same net. */ 94 | class MNN_PUBLIC Interpreter { 95 | public: 96 | /** 97 | * @brief create net from file. 98 | * @param file given file. 99 | * @return created net if success, NULL otherwise. 100 | */ 101 | static Interpreter* createFromFile(const char* file); 102 | /** 103 | * @brief create net from buffer. 104 | * @param buffer given data buffer. 105 | * @param size size of data buffer. 106 | * @return created net if success, NULL otherwise. 107 | */ 108 | static Interpreter* createFromBuffer(const void* buffer, size_t size); 109 | ~Interpreter(); 110 | 111 | public: 112 | /** 113 | * @brief create session with schedule config. created session will be managed in net. 114 | * @param config session schedule config. 115 | * @return created session if success, NULL otherwise. 116 | */ 117 | Session* createSession(const ScheduleConfig& config); 118 | 119 | /** 120 | * @brief create multi-path session with schedule configs. created session will be managed in net. 121 | * @param configs session schedule configs. 122 | * @return created session if success, NULL otherwise. 123 | */ 124 | Session* createMultiPathSession(const std::vector& configs); 125 | 126 | /** 127 | * @brief release session. 128 | * @param session given session. 129 | * @return true if given session is held by net and is freed. 130 | */ 131 | bool releaseSession(Session* session); 132 | 133 | /** 134 | * @brief call this function to get tensors ready. output tensor buffer (host or deviceId) should be retrieved 135 | * after resize of any input tensor. 136 | * @param session given session. 137 | */ 138 | void resizeSession(Session* session); 139 | 140 | /** 141 | * @brief call this function if don't need resize or create session any more, it will save a few memory that equal 142 | * to the size of model buffer 143 | */ 144 | void releaseModel(); 145 | 146 | /** 147 | * @brief Get the model buffer for user to save 148 | * @return std::make_pair(modleBuffer, modelSize). 149 | * @example: 150 | * std::ofstream output("trainResult.alinn") 151 | * auto buffer = net->getModelBuffer(); 152 | * output.write((const char*)buffer.first, buffer.second); 153 | */ 154 | std::pair getModelBuffer() const; 155 | 156 | /** 157 | * @brief update Session's Tensor to model's Const Op 158 | * @param session given session. 159 | * @return result of running. 160 | */ 161 | ErrorCode updateSessionToModel(Session* session); 162 | 163 | /** 164 | * @brief run session. 165 | * @param session given session. 166 | * @return result of running. 167 | */ 168 | ErrorCode runSession(Session* session) const; 169 | 170 | /* 171 | * @brief run session. 172 | * @param session given session. 173 | * @param before callback before each op. return true to run the op; return false to skip the op. 174 | * @param after callback after each op. return true to continue running; return false to interrupt the session. 175 | * @param sync synchronously wait for finish of execution or not. 176 | * @return result of running. 177 | */ 178 | ErrorCode runSessionWithCallBack(const Session* session, const TensorCallBack& before, const TensorCallBack& end, 179 | bool sync = false) const; 180 | 181 | /* 182 | * @brief run session. 183 | * @param session given session. 184 | * @param before callback before each op. return true to run the op; return false to skip the op. 185 | * @param after callback after each op. return true to continue running; return false to interrupt the session. 186 | * @param sync synchronously wait for finish of execution or not. 187 | * @return result of running. 188 | */ 189 | ErrorCode runSessionWithCallBackInfo(const Session* session, const TensorCallBackWithInfo& before, 190 | const TensorCallBackWithInfo& end, bool sync = false) const; 191 | 192 | /** 193 | * @brief get input tensor for given name. 194 | * @param session given session. 195 | * @param name given name. if NULL, return first input. 196 | * @return tensor if found, NULL otherwise. 197 | */ 198 | Tensor* getSessionInput(const Session* session, const char* name); 199 | /** 200 | * @brief get output tensor for given name. 201 | * @param session given session. 202 | * @param name given name. if NULL, return first output. 203 | * @return tensor if found, NULL otherwise. 204 | */ 205 | Tensor* getSessionOutput(const Session* session, const char* name); 206 | 207 | /** 208 | * @brief get all input tensors. 209 | * @param session given session. 210 | * @return all input tensors mapped with name. 211 | */ 212 | const std::map& getSessionOutputAll(const Session* session) const; 213 | /** 214 | * @brief get all output tensors. 215 | * @param session given session. 216 | * @return all output tensors mapped with name. 217 | */ 218 | const std::map& getSessionInputAll(const Session* session) const; 219 | 220 | public: 221 | /** 222 | * @brief resize given tensor. 223 | * @param tensor given tensor. 224 | * @param dims new dims. at most 6 dims. 225 | */ 226 | void resizeTensor(Tensor* tensor, const std::vector& dims); 227 | 228 | /** 229 | * @brief resize given tensor by nchw. 230 | * @param batch / N. 231 | * @param channel / C. 232 | * @param height / H. 233 | * @param width / W 234 | */ 235 | void resizeTensor(Tensor* tensor, int batch, int channel, int height, int width); 236 | 237 | /** 238 | * @brief get backend used to create given tensor. 239 | * @param session given session. 240 | * @param tensor given tensor. 241 | * @return backend used to create given tensor, may be NULL. 242 | */ 243 | const Backend* getBackend(const Session* session, const Tensor* tensor) const; 244 | 245 | /** 246 | * @brief get business code (model identifier). 247 | * @return business code. 248 | */ 249 | const char* bizCode() const; 250 | 251 | private: 252 | static Interpreter* createFromBufferInternal(Content* net); 253 | 254 | Content* mNet = nullptr; 255 | Interpreter(Content* net); 256 | 257 | Interpreter(const Interpreter&) = delete; 258 | Interpreter(const Interpreter&&) = delete; 259 | Interpreter& operator=(const Interpreter&) = delete; 260 | Interpreter& operator=(const Interpreter&&) = delete; 261 | }; 262 | } // namespace MNN 263 | 264 | #endif /* Interpreter_hpp */ 265 | -------------------------------------------------------------------------------- /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 | -------------------------------------------------------------------------------- /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 | -------------------------------------------------------------------------------- /include/MNNSharedContext.h: -------------------------------------------------------------------------------- 1 | // 2 | // MNNSharedContext.h 3 | // MNN 4 | // 5 | // Created by MNN on 2018/10/11. 6 | // Copyright © 2018, Alibaba Group Holding Limited 7 | // 8 | 9 | #ifndef MNNSharedContext_h 10 | #define MNNSharedContext_h 11 | #ifdef __cplusplus 12 | extern "C" { 13 | #endif 14 | 15 | #include /*uint32_t*/ 16 | 17 | #ifndef VK_DEFINE_HANDLE 18 | #define VK_DEFINE_HANDLE(object) typedef struct object##_T* object; 19 | VK_DEFINE_HANDLE(VkInstance) 20 | VK_DEFINE_HANDLE(VkPhysicalDevice) 21 | VK_DEFINE_HANDLE(VkDevice) 22 | VK_DEFINE_HANDLE(VkQueue) 23 | #endif 24 | struct MNNVulkanContext { 25 | VkInstance pInstance; 26 | VkPhysicalDevice pPhysicalDevice; 27 | VkDevice pDevice; 28 | VkQueue pQueue; 29 | uint32_t iQueueFamilyIndex; 30 | }; 31 | #ifdef __cplusplus 32 | } 33 | #endif 34 | 35 | #endif /* MNNSharedContext_h */ 36 | -------------------------------------------------------------------------------- /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 | -------------------------------------------------------------------------------- /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 | -------------------------------------------------------------------------------- /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 | -------------------------------------------------------------------------------- /include/mlpdr/MLPDR.h: -------------------------------------------------------------------------------- 1 | /* 2 | * LPRDetector.h 3 | * 4 | * Created on: Jun 25, 2019 5 | * Author: lqian 6 | */ 7 | 8 | #ifndef INCLUDE_MLPDR_MLPDR_H_ 9 | #define INCLUDE_MLPDR_MLPDR_H_ 10 | 11 | #include 12 | #include 13 | #include 14 | #ifdef _OPENMP 15 | #include 16 | #endif 17 | 18 | using std::string; 19 | using std::vector; 20 | 21 | namespace mlpdr { 22 | 23 | typedef struct FaceBox { 24 | float xmin; 25 | float ymin; 26 | float xmax; 27 | float ymax; 28 | float score; 29 | } FaceBox; 30 | typedef struct PlateInfo { 31 | float bbox_reg[4]; 32 | float landmark_reg[8]; 33 | float landmark[8]; 34 | FaceBox bbox; 35 | string plateNo; 36 | string plateColor; 37 | } FaceInfo; 38 | 39 | class MLPDR { 40 | public: 41 | MLPDR(const string& proto_model_dir, float threhold_p = 0.7f, 42 | float threhold_r = 0.8f, float threhold_o = 0.8f, float factor = 43 | 0.709f); 44 | std::vector recognize(const cv::Mat & img); 45 | std::vector Detect(const cv::Mat& img, const int min_face = 64, 46 | const int stage = 3); 47 | vector detect(unsigned char * inputImage, int height, int width, const int min_face=64, const int stagee=3); 48 | std::vector Detect_MaxFace(const cv::Mat& img, 49 | const int min_face = 64, const int stage = 3); 50 | 51 | virtual ~MLPDR(); 52 | private: 53 | int threads_num = 2; 54 | 55 | vector plateColorDict; 56 | void recognize_plate_infos(const cv::Mat & img, vector & plateInfos); 57 | }; 58 | 59 | } /* namespace mlpdr */ 60 | 61 | #endif /* INCLUDE_MLPDR_MLPDR_H_ */ 62 | -------------------------------------------------------------------------------- /include/mlpdr/label.hpp: -------------------------------------------------------------------------------- 1 | /* 2 | * label.hpp 3 | * 4 | * Created on: Jun 24, 2019 5 | * Author: lqian 6 | */ 7 | 8 | #ifndef SRC_LABEL_HPP_ 9 | #define SRC_LABEL_HPP_ 10 | 11 | #include 12 | #include 13 | 14 | std::vector label = { 15 | " ", 16 | "#", 17 | "京", 18 | "津", 19 | "冀", 20 | "晋", 21 | "蒙", 22 | "辽", 23 | "吉", 24 | "黑", 25 | "沪", 26 | "苏", 27 | "浙", 28 | "皖", 29 | "闽", 30 | "赣", 31 | "鲁", 32 | "豫", 33 | "鄂", 34 | "湘", 35 | "粤", 36 | "桂", 37 | "琼", 38 | "渝", 39 | "川", 40 | "贵", 41 | "云", 42 | "藏", 43 | "陕", 44 | "甘", 45 | "青", 46 | "宁", 47 | "新", 48 | "使", 49 | "0", 50 | "1", 51 | "2", 52 | "3", 53 | "4", 54 | "5", 55 | "6", 56 | "7", 57 | "8", 58 | "9", 59 | "A", 60 | "B", 61 | "C", 62 | "D", 63 | "E", 64 | "F", 65 | "G", 66 | "H", 67 | "J", 68 | "K", 69 | "L", 70 | "M", 71 | "N", 72 | "O", 73 | "P", 74 | "Q", 75 | "R", 76 | "S", 77 | "T", 78 | "U", 79 | "V", 80 | "W", 81 | "X", 82 | "Y", 83 | "Z", 84 | "警", 85 | "学", 86 | "挂", 87 | "港", 88 | "澳", 89 | "领", 90 | "民", 91 | "航", 92 | "应", 93 | "急" 94 | }; 95 | 96 | 97 | #endif /* SRC_LABEL_HPP_ */ 98 | -------------------------------------------------------------------------------- /install.md: -------------------------------------------------------------------------------- 1 | ## 安装依赖 2 | cmake >= 3.10.0 3 | opencv >= 3.0.0 4 | openmp 5 | 6 | ## x86平台Linux安装指令 7 |
 8 | git clone https://github.com/lqian/light-LPR
 9 | cd light-LPR && mkdir build && cd build
10 | cmake ../
11 | make
12 | 
13 | 14 | ## ARM平台Linux安装指令 15 |
16 | git clone https://github.com/lqian/light-LPR
17 | cd light-LPR && mkdir build && cd build
18 | cmake ../ -DLIGHT_LPR_ARCH=arm
19 | make
20 | 
21 | 22 | ## Windows平台上安装指令 23 | - 下载cmake 3.10以上版本并安装 24 | - 首先下载Visual Studio 2017或者 Native Builder Tool for Visual Studio 2017,安装c++编译工具 25 | - 如果编译64位系统,下载64位[opencv-3.4.2-install-win64.zip](https://pan.baidu.com/s/1CtabojjfEK-bK_XwfG9HTA), 32位系统则下载[opencv-3.4.2-install-win32.zip](https://pan.baidu.com/s/1E7zhRsrrpc9JEhB_6gpehg),解压到任意目录 26 | - 克隆[MNN](https://github.com/alibaba/MNN)的源码 27 | - 下载[flatc_windows_exe.zip](https://github.com/google/flatbuffers/releases/download/v1.11.0/flatc_windows_exe.zip),把flatc.exe可执行文件复制到{MNN}/3rd_party/flatbuffers/tmp目录下 28 | - 以管理员权限打开powershell.exe,然后执行set-executionpolicy -executionpolicy unrestricted,提示选Y 29 | - 注释掉MNN的源码目录中的CMakelist.txt中的`COMMAND powershell ${CMAKE_CURRENT_SOURCE_DIR}/schema/generate.ps1 -lazy`这行,大约在461行 30 |
31 | > cd MNN
32 | > schema\enerate.ps1
33 | > mkdir build 
34 | > cd build
35 | 按win键,根据需要,搜索x86 native tools command prompt for VS 2017 或者x64 native tools command prompt for VS 2017
36 | > cmake -G "NMake Makefiles" -DCMAKE_BUILD_TYPE=Release ../
37 | > nmake 
38 | 把编译成功的MNN.dll、MNN.lib文件复制到light-LPR项目的lib目录下
39 | > cd light-LPR && mkdir build && cd build
40 | > set OpenCV_DIR=/path/to/opencv-install/directory
41 | > cmake -G "NMake Makefiles" ..
42 | > nmake
43 | 
44 | 45 | ## 运行测试 46 | `./examples/demo ../models/ [/path/to/a/image]` 47 | 本项目在Fedora 29,CentOS 7.6, Windows 10 64位家庭版,Ubuntu 18.04 mate for ARM平台测试通过 48 | 49 | 50 | ## 参考和引用 51 | - [Alibaba MNN](https://github.com/alibaba/MNN) 52 | - [License-Plate-Detect-Recognition-via-Deep-Neural-Networks-accuracy-up-to-99.9](https://github.com/zhubenfu/License-Plate-Detect-Recognition-via-Deep-Neural-Networks-accuracy-up-to-99.9) 53 | - [Caffe_OCR](https://github.com/senlinuc/caffe_ocr) 54 | - [MNN MTCNN CPU OPENCL](https://github.com/liushuan/MNN-MTCNN-CPU-OPENCL) -------------------------------------------------------------------------------- /install_en.md: -------------------------------------------------------------------------------- 1 | ## requirements 2 | cmake >= 3.10.0 3 | opencv >= 3.0.0 4 | openmp 5 | 6 | ## installation commands for x86 Linux 7 |
 8 | git clone https://github.com/lqian/light-LPR
 9 | cd light-LPR && mkdir build && cd build
10 | cmake ../
11 | make
12 | 
13 | 14 | ## installation commands for ARM Linux 15 |
16 | git clone https://github.com/lqian/light-LPR
17 | cd light-LPR && mkdir build && cd build
18 | cmake ../ -DLIGHT_LPR_ARCH=arm
19 | make
20 | 
21 | 22 | ## installation commands for Windows 23 | - Download and install cmake 3.10 and above 24 | - Download Visual Studio 2017 or Native Builder Tool for Visual Studio 2017, install c ++ compilation tool 25 | - if you compile for x64, download [opencv-3.4.2-install-win64.zip](https://pan.baidu.com/s/1CtabojjfEK-bK_XwfG9HTA), x86 archictecture [opencv-3.4.2-install-win32.zip](https://pan.baidu.com/s/1E7zhRsrrpc9JEhB_6gpehg),and unzip the file. 26 | - clone source code: [MNN](https://github.com/alibaba/MNN) 27 | - download [flatc_windows_exe.zip](https://github.com/google/flatbuffers/releases/download/v1.11.0/flatc_windows_exe.zip),and put the file to {MNN}/3rd_party/flatbuffers/tmp directory 28 | - run powershell.exe as administrator,and then run the command: set-executionpolicy -executionpolicy unrestricted,select Y from prompt. 29 | - comment the line `COMMAND powershell ${CMAKE_CURRENT_SOURCE_DIR}/schema/generate.ps1 -lazy` in CMakeLists.txt of MNN project,the line number is about 461. 30 |
31 | > cd MNN
32 | > schema\enerate.ps1
33 | > mkdir build 
34 | > cd build
35 | run x86 native tools command prompt for VS 2017 or x64 native tools command prompt for VS 2017
36 | > cmake -G "NMake Makefiles" -DCMAKE_BUILD_TYPE=Release ../
37 | > nmake 
38 | copy the MNN.dll、MNN.lib into light-LPR/lib directory
39 | > cd light-LPR && mkdir build && cd build
40 | > set OpenCV_DIR=/path/to/opencv-install/directory
41 | > cmake -G "NMake Makefiles" ..
42 | > nmake
43 | 
44 | 45 | ## test 46 | `./examples/demo ../models/ [/path/to/a/image]` 47 | This project passed the test on Fedora 29, CentOS 7.6, Windows 10 64-bit Home Edition, Ubuntu 18.04 mate for ARM platform 48 | 49 | 50 | ## reference 51 | - [Alibaba MNN](https://github.com/alibaba/MNN) 52 | - [License-Plate-Detect-Recognition-via-Deep-Neural-Networks-accuracy-up-to-99.9](https://github.com/zhubenfu/License-Plate-Detect-Recognition-via-Deep-Neural-Networks-accuracy-up-to-99.9) 53 | - [Caffe_OCR](https://github.com/senlinuc/caffe_ocr) 54 | - [MNN MTCNN CPU OPENCL](https://github.com/liushuan/MNN-MTCNN-CPU-OPENCL) -------------------------------------------------------------------------------- /kr.md: -------------------------------------------------------------------------------- 1 | Light-LPR은 임베디드 장치, 휴대폰 및 x86 플랫폼에서 실행할 수있는 번호판 인식을 목표로하는 오픈 소스 프로젝트로, 다양한 시나리오에서 번호판 인식을 지원하는 것을 목표로하며, 번호판 문자 인식의 정확도는 99.95 %를 초과하고 포괄적 인 인식 정확도는 99를 초과합니다. %, 다중 국가, 다중 언어 및 다중 유형 번호판 인식을 지원합니다. [testing video resources](https://www.bilibili.com/video/BV12L4y1Y7K1?spm_id_from=333.999.0.0) 2 | 3 | 4 | [ About ](README.md) | [ English ](en.md) | [ 中文 ](cn-zh.md) | [ 中文繁体 ](cn-tw.md)| [ 한국어 ](kr.md) 5 | 6 | ## 비즈니스 에디션 지원 한국 번호판 인식 7 | | 번호판 유형 | 1973 년 이전 | 1996년 이전 | 2004년 이전 | 2006공식 | 2013공식 | 2019공식 | 8 | | --------: | :-----: | :----: | :----: | :----: |:----: |:----: | 9 | | 전용차 |Y | Y | Y | Y | Y | Y | 10 | | 상용차 | - | Y | Y | Y | Y | | 11 | | 특수차 | E | Y| Y| -| | | 12 | | 렌트카 | | Y |Y| Y| Y | | 13 | | 군용 | -|E|E|E| | | 14 | | 엔지니어링 차량 | | E| E | | | | 15 | | 외교 | | | E |E | E | | 16 | 17 | 비고: Y 지원,- 지원되지 않거나 사용할 수 없음,E 평가 단계에서 18 | 19 | ## 1080P 이미지 인식 벤치 마크 성능 20 | | | CPU | Memory | average cost of community version (ms) | average cost of Commercial version(ms) | 21 | | :-------- | :----- | :----: | ----: | ----: | 22 | | X86 | i5-8265 | 8G | 451 | < 50 | 23 | | ARM | A53 | 1G | 1532| < 160 | 24 | | Huwei P20 pro| ... | 4G | - | < 100 | 25 | | 3519A100 | ... | | - | < 16 (NPU support) | 26 | | 3516CV500 | ... | | - | < 45 (NPU support) | 27 | 28 | ## 지원되는 프로그래밍 언어 29 | - C/C++ 30 | - C# 31 | - JAVA 32 | 33 | ## 설치 방법은 프로젝트 파일을 참조하십시오 [install_en.md](install_en.md) 34 | 35 | ## 기타 36 | 사업 제휴 연락처 : link.com@yeah.net, 휴대폰 : +86 18010870244, Skype: +86 18010870244 -------------------------------------------------------------------------------- /lib-arm/libMNN.so: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/lqian/light-LPR/6449d571739d164f3fd84d8862883f1f6dadb8ae/lib-arm/libMNN.so -------------------------------------------------------------------------------- /lib-win32/MNN.dll: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/lqian/light-LPR/6449d571739d164f3fd84d8862883f1f6dadb8ae/lib-win32/MNN.dll -------------------------------------------------------------------------------- /lib-win32/MNN.lib: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/lqian/light-LPR/6449d571739d164f3fd84d8862883f1f6dadb8ae/lib-win32/MNN.lib -------------------------------------------------------------------------------- /lib-win32/MNN_Express.dll: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/lqian/light-LPR/6449d571739d164f3fd84d8862883f1f6dadb8ae/lib-win32/MNN_Express.dll -------------------------------------------------------------------------------- /lib/libMNN.so: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/lqian/light-LPR/6449d571739d164f3fd84d8862883f1f6dadb8ae/lib/libMNN.so -------------------------------------------------------------------------------- /light-LPR.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/lqian/light-LPR/6449d571739d164f3fd84d8862883f1f6dadb8ae/light-LPR.png -------------------------------------------------------------------------------- /models/det1.caffemodel: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/lqian/light-LPR/6449d571739d164f3fd84d8862883f1f6dadb8ae/models/det1.caffemodel -------------------------------------------------------------------------------- /models/det1.mnn: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/lqian/light-LPR/6449d571739d164f3fd84d8862883f1f6dadb8ae/models/det1.mnn -------------------------------------------------------------------------------- /models/det1.prototxt: -------------------------------------------------------------------------------- 1 | name: "PNet" 2 | input: "data" 3 | input_dim: 1 4 | input_dim: 3 5 | input_dim: 12 6 | input_dim: 30 7 | 8 | layer { 9 | name: "conv1" 10 | type: "Convolution" 11 | bottom: "data" 12 | top: "conv1" 13 | param { 14 | lr_mult: 1 15 | decay_mult: 1 16 | } 17 | param { 18 | lr_mult: 2 19 | decay_mult: 0 20 | } 21 | convolution_param { 22 | num_output: 10 23 | kernel_size: 3 24 | stride: 1 25 | weight_filler { 26 | type: "xavier" 27 | } 28 | bias_filler { 29 | type: "constant" 30 | value: 0 31 | } 32 | } 33 | } 34 | layer { 35 | name: "PReLU1" 36 | type: "PReLU" 37 | bottom: "conv1" 38 | top: "conv1" 39 | } 40 | layer { 41 | name: "pool1" 42 | type: "Pooling" 43 | bottom: "conv1" 44 | top: "pool1" 45 | pooling_param { 46 | pool: MAX 47 | kernel_size: 2 48 | stride: 2 49 | } 50 | } 51 | 52 | layer { 53 | name: "conv2" 54 | type: "Convolution" 55 | bottom: "pool1" 56 | top: "conv2" 57 | param { 58 | lr_mult: 1 59 | decay_mult: 1 60 | } 61 | param { 62 | lr_mult: 2 63 | decay_mult: 0 64 | } 65 | convolution_param { 66 | num_output: 16 67 | kernel_size: 3 68 | stride: 1 69 | weight_filler { 70 | type: "xavier" 71 | } 72 | bias_filler { 73 | type: "constant" 74 | value: 0 75 | } 76 | } 77 | } 78 | layer { 79 | name: "PReLU2" 80 | type: "PReLU" 81 | bottom: "conv2" 82 | top: "conv2" 83 | } 84 | 85 | layer { 86 | name: "conv3" 87 | type: "Convolution" 88 | bottom: "conv2" 89 | top: "conv3" 90 | param { 91 | lr_mult: 1 92 | decay_mult: 1 93 | } 94 | param { 95 | lr_mult: 2 96 | decay_mult: 0 97 | } 98 | convolution_param { 99 | num_output: 32 100 | kernel_size: 3 101 | stride: 1 102 | weight_filler { 103 | type: "xavier" 104 | } 105 | bias_filler { 106 | type: "constant" 107 | value: 0 108 | } 109 | } 110 | } 111 | layer { 112 | name: "PReLU3" 113 | type: "PReLU" 114 | bottom: "conv3" 115 | top: "conv3" 116 | } 117 | 118 | 119 | 120 | layer { 121 | name: "conv4j-1" 122 | type: "Convolution" 123 | bottom: "conv3" 124 | top: "conv4-1" 125 | param { 126 | lr_mult: 1 127 | decay_mult: 1 128 | } 129 | param { 130 | lr_mult: 2 131 | decay_mult: 0 132 | } 133 | convolution_param { 134 | num_output: 2 135 | kernel_h:1 136 | kernel_w:10 137 | stride:1 138 | 139 | weight_filler { 140 | type: "xavier" 141 | } 142 | bias_filler { 143 | type: "constant" 144 | value: 0 145 | } 146 | } 147 | } 148 | 149 | 150 | 151 | 152 | layer { 153 | name: "conv4j-2" 154 | type: "Convolution" 155 | bottom: "conv3" 156 | top: "conv4-2" 157 | param { 158 | lr_mult: 1 159 | decay_mult: 1 160 | } 161 | param { 162 | lr_mult: 2 163 | decay_mult: 0 164 | } 165 | convolution_param { 166 | num_output: 4 167 | kernel_h:1 168 | kernel_w:10 169 | stride: 1 170 | 171 | weight_filler { 172 | type: "xavier" 173 | } 174 | bias_filler { 175 | type: "constant" 176 | value: 0 177 | } 178 | } 179 | } 180 | layer { 181 | name: "prob1" 182 | type: "Softmax" 183 | bottom: "conv4-1" 184 | top: "prob1" 185 | } -------------------------------------------------------------------------------- /models/det2.caffemodel: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/lqian/light-LPR/6449d571739d164f3fd84d8862883f1f6dadb8ae/models/det2.caffemodel -------------------------------------------------------------------------------- /models/det2.mnn: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/lqian/light-LPR/6449d571739d164f3fd84d8862883f1f6dadb8ae/models/det2.mnn -------------------------------------------------------------------------------- /models/det2.prototxt: -------------------------------------------------------------------------------- 1 | name: "RNet" 2 | input: "data" 3 | input_dim: 1 4 | input_dim: 3 5 | input_dim: 24 6 | input_dim: 60 7 | ################################################ 8 | layer { 9 | name: "conv1" 10 | type: "Convolution" 11 | bottom: "data" 12 | top: "conv1" 13 | param { 14 | lr_mult: 1 15 | decay_mult: 1 16 | } 17 | param { 18 | lr_mult: 2 19 | decay_mult: 1 20 | } 21 | convolution_param { 22 | num_output: 28 23 | kernel_size: 3 24 | stride: 1 25 | weight_filler { 26 | type: "xavier" 27 | } 28 | bias_filler { 29 | type: "constant" 30 | value: 0 31 | } 32 | } 33 | } 34 | layer { 35 | name: "prelu1" 36 | type: "PReLU" 37 | bottom: "conv1" 38 | top: "conv1" 39 | } 40 | layer { 41 | name: "pool1" 42 | type: "Pooling" 43 | bottom: "conv1" 44 | top: "pool1" 45 | pooling_param { 46 | pool: MAX 47 | kernel_size: 3 48 | stride: 2 49 | } 50 | } 51 | 52 | layer { 53 | name: "conv2" 54 | type: "Convolution" 55 | bottom: "pool1" 56 | top: "conv2" 57 | param { 58 | lr_mult: 1 59 | decay_mult: 1 60 | } 61 | param { 62 | lr_mult: 2 63 | decay_mult: 1 64 | } 65 | convolution_param { 66 | num_output: 48 67 | kernel_size: 3 68 | stride: 1 69 | weight_filler { 70 | type: "xavier" 71 | } 72 | bias_filler { 73 | type: "constant" 74 | value: 0 75 | } 76 | } 77 | } 78 | layer { 79 | name: "prelu2" 80 | type: "PReLU" 81 | bottom: "conv2" 82 | top: "conv2" 83 | } 84 | layer { 85 | name: "pool2" 86 | type: "Pooling" 87 | bottom: "conv2" 88 | top: "pool2" 89 | pooling_param { 90 | pool: MAX 91 | kernel_size: 3 92 | stride: 2 93 | } 94 | } 95 | #################################### 96 | 97 | ################################## 98 | layer { 99 | name: "conv3" 100 | type: "Convolution" 101 | bottom: "pool2" 102 | top: "conv3" 103 | param { 104 | lr_mult: 1 105 | decay_mult: 1 106 | } 107 | param { 108 | lr_mult: 2 109 | decay_mult: 1 110 | } 111 | convolution_param { 112 | num_output: 64 113 | kernel_size: 2 114 | stride: 1 115 | weight_filler { 116 | type: "xavier" 117 | } 118 | bias_filler { 119 | type: "constant" 120 | value: 0 121 | } 122 | } 123 | } 124 | layer { 125 | name: "prelu3" 126 | type: "PReLU" 127 | bottom: "conv3" 128 | top: "conv3" 129 | } 130 | ############################### 131 | 132 | ############################### 133 | 134 | layer { 135 | name: "conv4i" 136 | type: "InnerProduct" 137 | bottom: "conv3" 138 | top: "conv4" 139 | param { 140 | lr_mult: 1 141 | decay_mult: 1 142 | } 143 | param { 144 | lr_mult: 2 145 | decay_mult: 1 146 | } 147 | inner_product_param { 148 | num_output: 128 149 | weight_filler { 150 | type: "xavier" 151 | } 152 | bias_filler { 153 | type: "constant" 154 | value: 0 155 | } 156 | } 157 | } 158 | layer { 159 | name: "prelu4" 160 | type: "PReLU" 161 | bottom: "conv4" 162 | top: "conv4" 163 | } 164 | 165 | layer { 166 | name: "conv5i-1" 167 | type: "InnerProduct" 168 | bottom: "conv4" 169 | top: "conv5-1" 170 | param { 171 | lr_mult: 1 172 | decay_mult: 1 173 | } 174 | param { 175 | lr_mult: 2 176 | decay_mult: 1 177 | } 178 | inner_product_param { 179 | num_output: 2 180 | #kernel_size: 1 181 | #stride: 1 182 | weight_filler { 183 | type: "xavier" 184 | } 185 | bias_filler { 186 | type: "constant" 187 | value: 0 188 | } 189 | } 190 | } 191 | 192 | 193 | 194 | 195 | 196 | layer { 197 | name: "conv5i-2" 198 | type: "InnerProduct" 199 | bottom: "conv4" 200 | top: "conv5-2" 201 | param { 202 | lr_mult: 1 203 | decay_mult: 1 204 | } 205 | param { 206 | lr_mult: 2 207 | decay_mult: 1 208 | } 209 | inner_product_param { 210 | num_output: 4 211 | #kernel_size: 1 212 | #stride: 1 213 | weight_filler { 214 | type: "xavier" 215 | } 216 | bias_filler { 217 | type: "constant" 218 | value: 0 219 | } 220 | } 221 | } 222 | 223 | layer { 224 | name: "prob1" 225 | type: "Softmax" 226 | bottom: "conv5-1" 227 | top: "prob1" 228 | } -------------------------------------------------------------------------------- /models/det3.caffemodel: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/lqian/light-LPR/6449d571739d164f3fd84d8862883f1f6dadb8ae/models/det3.caffemodel -------------------------------------------------------------------------------- /models/det3.mnn: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/lqian/light-LPR/6449d571739d164f3fd84d8862883f1f6dadb8ae/models/det3.mnn -------------------------------------------------------------------------------- /models/det3.prototxt: -------------------------------------------------------------------------------- 1 | name: "ONet" 2 | input: "data" 3 | input_dim: 1 4 | input_dim: 3 5 | input_dim: 48 6 | input_dim: 120 7 | 8 | ################################## 9 | layer { 10 | name: "conv1" 11 | type: "Convolution" 12 | bottom: "data" 13 | top: "conv1" 14 | param { 15 | lr_mult: 1 16 | decay_mult: 1 17 | } 18 | param { 19 | lr_mult: 2 20 | decay_mult: 1 21 | } 22 | convolution_param { 23 | num_output: 32 24 | kernel_size: 3 25 | stride: 1 26 | weight_filler { 27 | type: "xavier" 28 | } 29 | bias_filler { 30 | type: "constant" 31 | value: 0 32 | } 33 | } 34 | } 35 | layer { 36 | name: "prelu1" 37 | type: "PReLU" 38 | bottom: "conv1" 39 | top: "conv1" 40 | } 41 | layer { 42 | name: "pool1" 43 | type: "Pooling" 44 | bottom: "conv1" 45 | top: "pool1" 46 | pooling_param { 47 | pool: MAX 48 | kernel_size: 3 49 | stride: 2 50 | } 51 | } 52 | layer { 53 | name: "conv2" 54 | type: "Convolution" 55 | bottom: "pool1" 56 | top: "conv2" 57 | param { 58 | lr_mult: 1 59 | decay_mult: 1 60 | } 61 | param { 62 | lr_mult: 2 63 | decay_mult: 1 64 | } 65 | convolution_param { 66 | num_output: 64 67 | kernel_size: 3 68 | stride: 1 69 | weight_filler { 70 | type: "xavier" 71 | } 72 | bias_filler { 73 | type: "constant" 74 | value: 0 75 | } 76 | } 77 | } 78 | 79 | layer { 80 | name: "prelu2" 81 | type: "PReLU" 82 | bottom: "conv2" 83 | top: "conv2" 84 | } 85 | layer { 86 | name: "pool2" 87 | type: "Pooling" 88 | bottom: "conv2" 89 | top: "pool2" 90 | pooling_param { 91 | pool: MAX 92 | kernel_size: 3 93 | stride: 2 94 | } 95 | } 96 | 97 | layer { 98 | name: "conv3" 99 | type: "Convolution" 100 | bottom: "pool2" 101 | top: "conv3" 102 | param { 103 | lr_mult: 1 104 | decay_mult: 1 105 | } 106 | param { 107 | lr_mult: 2 108 | decay_mult: 1 109 | } 110 | convolution_param { 111 | num_output: 64 112 | kernel_size: 3 113 | weight_filler { 114 | type: "xavier" 115 | } 116 | bias_filler { 117 | type: "constant" 118 | value: 0 119 | } 120 | } 121 | } 122 | layer { 123 | name: "prelu3" 124 | type: "PReLU" 125 | bottom: "conv3" 126 | top: "conv3" 127 | } 128 | layer { 129 | name: "pool3" 130 | type: "Pooling" 131 | bottom: "conv3" 132 | top: "pool3" 133 | pooling_param { 134 | pool: MAX 135 | kernel_size: 2 136 | stride: 2 137 | } 138 | } 139 | layer { 140 | name: "conv4" 141 | type: "Convolution" 142 | bottom: "pool3" 143 | top: "conv4" 144 | param { 145 | lr_mult: 1 146 | decay_mult: 1 147 | } 148 | param { 149 | lr_mult: 2 150 | decay_mult: 1 151 | } 152 | convolution_param { 153 | num_output: 128 154 | kernel_size: 2 155 | weight_filler { 156 | type: "xavier" 157 | } 158 | bias_filler { 159 | type: "constant" 160 | value: 0 161 | } 162 | } 163 | } 164 | layer { 165 | name: "prelu4" 166 | type: "PReLU" 167 | bottom: "conv4" 168 | top: "conv4" 169 | } 170 | 171 | 172 | layer { 173 | name: "conv5i" 174 | type: "InnerProduct" 175 | bottom: "conv4" 176 | top: "conv5" 177 | param { 178 | lr_mult: 1 179 | decay_mult: 1 180 | } 181 | param { 182 | lr_mult: 2 183 | decay_mult: 1 184 | } 185 | inner_product_param { 186 | #kernel_size: 3 187 | num_output: 256 188 | weight_filler { 189 | type: "xavier" 190 | } 191 | bias_filler { 192 | type: "constant" 193 | value: 0 194 | } 195 | } 196 | } 197 | 198 | layer { 199 | name: "drop5i" 200 | type: "Dropout" 201 | bottom: "conv5" 202 | top: "conv5" 203 | dropout_param { 204 | dropout_ratio: 0.25 205 | } 206 | } 207 | layer { 208 | name: "prelu5" 209 | type: "PReLU" 210 | bottom: "conv5" 211 | top: "conv5" 212 | } 213 | 214 | 215 | layer { 216 | name: "conv6i-1" 217 | type: "InnerProduct" 218 | bottom: "conv5" 219 | top: "conv6-1" 220 | param { 221 | lr_mult: 1 222 | decay_mult: 1 223 | } 224 | param { 225 | lr_mult: 2 226 | decay_mult: 1 227 | } 228 | inner_product_param { 229 | #kernel_size: 1 230 | num_output: 2 231 | weight_filler { 232 | type: "xavier" 233 | } 234 | bias_filler { 235 | type: "constant" 236 | value: 0 237 | } 238 | } 239 | } 240 | 241 | 242 | layer { 243 | name: "conv6i-2" 244 | type: "InnerProduct" 245 | bottom: "conv5" 246 | top: "conv6-2" 247 | param { 248 | lr_mult: 1 249 | decay_mult: 1 250 | } 251 | param { 252 | lr_mult: 2 253 | decay_mult: 1 254 | } 255 | inner_product_param { 256 | #kernel_size: 1 257 | num_output: 4 258 | weight_filler { 259 | type: "xavier" 260 | } 261 | bias_filler { 262 | type: "constant" 263 | value: 0 264 | } 265 | } 266 | } 267 | 268 | layer { 269 | name: "conv6-3" 270 | type: "InnerProduct" 271 | bottom: "conv5" 272 | top: "conv6-3" 273 | param { 274 | lr_mult: 1 275 | decay_mult: 1 276 | } 277 | param { 278 | lr_mult: 2 279 | decay_mult: 1 280 | } 281 | inner_product_param { 282 | #kernel_size: 1 283 | num_output: 8 284 | weight_filler { 285 | type: "xavier" 286 | } 287 | bias_filler { 288 | type: "constant" 289 | value: 0 290 | } 291 | } 292 | } 293 | 294 | 295 | layer { 296 | name: "prob1" 297 | type: "Softmax" 298 | bottom: "conv6-1" 299 | top: "prob1" 300 | } -------------------------------------------------------------------------------- /models/lpc.mnn: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/lqian/light-LPR/6449d571739d164f3fd84d8862883f1f6dadb8ae/models/lpc.mnn -------------------------------------------------------------------------------- /models/lpr.mnn: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/lqian/light-LPR/6449d571739d164f3fd84d8862883f1f6dadb8ae/models/lpr.mnn -------------------------------------------------------------------------------- /src/examples/CMakeLists.txt: -------------------------------------------------------------------------------- 1 | find_package(OpenCV 3.4.0 REQUIRED COMPONENTS core imgcodecs imgproc highgui) 2 | 3 | file(GLOB_RECURSE test_srcs *.cpp) 4 | 5 | foreach(source_file ${test_srcs}) 6 | # get file name 7 | get_filename_component(name ${source_file} NAME_WE) 8 | # get folder name 9 | get_filename_component(path ${source_file} PATH) 10 | get_filename_component(folder ${path} NAME_WE) 11 | 12 | add_executable(${name} ${source_file}) 13 | target_link_libraries(${name} mlpdr ${OpenCV_LIBRARIES} ${OpenMP_CXX}) 14 | 15 | 16 | # set back RUNTIME_OUTPUT_DIRECTORY 17 | set_target_properties(${name} PROPERTIES 18 | RUNTIME_OUTPUT_DIRECTORY "${PROJECT_BINARY_DIR}/${folder}") 19 | 20 | endforeach() 21 | -------------------------------------------------------------------------------- /src/examples/demo.cpp: -------------------------------------------------------------------------------- 1 | /* 2 | * mtcnn_test.cpp 3 | * 4 | * Created on: Jun 25, 2019 5 | * Author: lqian 6 | */ 7 | 8 | #include 9 | #include 10 | #include 11 | 12 | #include 13 | #include "mlpdr/MLPDR.h" 14 | 15 | using namespace std; 16 | using namespace mlpdr; 17 | using namespace cv; 18 | int main(int argc, char ** argv) { 19 | MLPDR detector(argv[1], 0.9f, 0.7f, 0.7f); 20 | Mat img = imread(argv[2]); 21 | assert(!img.empty()); 22 | TickMeter tm; 23 | tm.start(); 24 | // std::vector plateInfos = detector.Detect(img, 40, 3); // 608.23 ms 25 | vector plateInfos = detector.recognize(img); 26 | tm.stop(); 27 | printf("detect cost: %f (ms)\n", tm.getTimeMilli()); 28 | 29 | for (auto pi: plateInfos) { 30 | cout << "plateNo: " << pi.plateNo << endl; 31 | } 32 | 33 | } 34 | -------------------------------------------------------------------------------- /src/mlpdr/CMakeLists.txt: -------------------------------------------------------------------------------- 1 | file(GLOB_RECURSE mlpdr_srcs *.cpp) 2 | file(GLOB mlpdr_api_srcs *_api.cpp) 3 | 4 | if(NOT BUILD_JAVA_API) 5 | list(REMOVE_ITEM mlpdr_srcs ${mlpdr_api_srcs} ) 6 | endif() 7 | 8 | add_library(mlpdr ${mlpdr_srcs}) 9 | target_link_libraries(mlpdr MNN ${OpenMP_CXX} ${OpenCV_LIBRARIES}) -------------------------------------------------------------------------------- /src/mlpdr/MLPDR.cpp: -------------------------------------------------------------------------------- 1 | /* 2 | * LPRDetector.cpp 3 | * 4 | * Created on: Jun 25, 2019 5 | * Author: lqian 6 | */ 7 | 8 | #include "mlpdr/MLPDR.h" 9 | #include "mlpdr/label.hpp" 10 | 11 | #include 12 | #include 13 | #include 14 | #include 15 | 16 | namespace mlpdr { 17 | 18 | using namespace std; 19 | using namespace MNN; 20 | using namespace MNN::CV; 21 | using namespace cv; 22 | 23 | std::shared_ptr LPRNet = NULL; 24 | Session * ocr_session = nullptr; 25 | Tensor * ocr_input = nullptr; 26 | Tensor * ocr_output = nullptr; 27 | 28 | std::shared_ptr LPCNet_ = NULL; 29 | Session * lpc_session = nullptr; 30 | Tensor * lpc_input = nullptr; 31 | Tensor * lpc_output = nullptr; 32 | 33 | 34 | std::shared_ptr PNet_ = NULL; 35 | std::shared_ptr RNet_ = NULL; 36 | std::shared_ptr ONet_ = NULL; 37 | 38 | 39 | MNN::Session * sess_p = NULL; 40 | MNN::Session * sess_r = NULL; 41 | MNN::Session * sess_o = NULL; 42 | 43 | MNN::Tensor * p_input = nullptr; 44 | MNN::Tensor * p_out_pro = nullptr; 45 | MNN::Tensor * p_out_reg = nullptr; 46 | 47 | MNN::Tensor * r_input = nullptr; 48 | MNN::Tensor * r_out_pro = nullptr; 49 | MNN::Tensor * r_out_reg = nullptr; 50 | 51 | MNN::Tensor * o_input = nullptr; 52 | MNN::Tensor * o_out_pro = nullptr; 53 | MNN::Tensor * o_out_reg = nullptr; 54 | MNN::Tensor * o_out_lank = nullptr; 55 | 56 | std::shared_ptr pretreat_data; 57 | std::shared_ptr lpr_pretreat_data, lpc_pretreat_data; 58 | 59 | std::vector candidate_boxes_; 60 | std::vector total_boxes_; 61 | 62 | static float threhold_p = 0.7f; 63 | static float threhold_r = 0.8f; 64 | static float threhold_o = 0.8f; 65 | static float iou_threhold = 0.7f; 66 | static float factor = 0.709f; 67 | //static int min_face = 48; 68 | 69 | //pnet config 70 | static const float pnet_stride = 2; 71 | static const float pnet_cell_size_height = 12; 72 | static const float pnet_cell_size_width = 30; 73 | static const int pnet_max_detect_num = 5000; 74 | //mean & std 75 | static const float mean_val = 127.5f; 76 | static const float std_val = 0.0078125f; 77 | 78 | static bool CompareBBox(const PlateInfo & a, const PlateInfo & b) { 79 | return a.bbox.score > b.bbox.score; 80 | } 81 | 82 | static float IoU(float xmin, float ymin, float xmax, float ymax, float xmin_, 83 | float ymin_, float xmax_, float ymax_, bool is_iom) { 84 | float iw = std::min(xmax, xmax_) - std::max(xmin, xmin_) + 1; 85 | float ih = std::min(ymax, ymax_) - std::max(ymin, ymin_) + 1; 86 | if (iw <= 0 || ih <= 0) 87 | return 0; 88 | float s = iw * ih; 89 | if (is_iom) { 90 | float ov = s / std::min((xmax - xmin + 1) * (ymax - ymin + 1), (xmax_ - xmin_ + 1) * (ymax_ - ymin_ + 1)); 91 | return ov; 92 | } else { 93 | float ov = s / ((xmax - xmin + 1) * (ymax - ymin + 1) + (xmax_ - xmin_ + 1) * (ymax_ - ymin_ + 1) - s); 94 | return ov; 95 | } 96 | } 97 | 98 | static std::vector NMS(std::vector& bboxes, float thresh, 99 | char methodType) { 100 | std::vector bboxes_nms; 101 | if (bboxes.size() == 0) { 102 | return bboxes_nms; 103 | } 104 | std::sort(bboxes.begin(), bboxes.end(), CompareBBox); 105 | 106 | int32_t select_idx = 0; 107 | int32_t num_bbox = static_cast(bboxes.size()); 108 | std::vector mask_merged(num_bbox, 0); 109 | bool all_merged = false; 110 | 111 | while (!all_merged) { 112 | while (select_idx < num_bbox && mask_merged[select_idx] == 1) 113 | select_idx++; 114 | if (select_idx == num_bbox) { 115 | all_merged = true; 116 | continue; 117 | } 118 | bboxes_nms.push_back(bboxes[select_idx]); 119 | mask_merged[select_idx] = 1; 120 | 121 | FaceBox select_bbox = bboxes[select_idx].bbox; 122 | float area1 = static_cast((select_bbox.xmax - select_bbox.xmin 123 | + 1) * (select_bbox.ymax - select_bbox.ymin + 1)); 124 | float x1 = static_cast(select_bbox.xmin); 125 | float y1 = static_cast(select_bbox.ymin); 126 | float x2 = static_cast(select_bbox.xmax); 127 | float y2 = static_cast(select_bbox.ymax); 128 | 129 | select_idx++; 130 | #ifdef _OPENMP 131 | #pragma omp parallel for num_threads(threads_num) 132 | #endif 133 | for (int32_t i = select_idx; i < num_bbox; i++) { 134 | if (mask_merged[i] == 1) 135 | continue; 136 | 137 | FaceBox & bbox_i = bboxes[i].bbox; 138 | float x = std::max(x1, static_cast(bbox_i.xmin)); 139 | float y = std::max(y1, static_cast(bbox_i.ymin)); 140 | float w = std::min(x2, static_cast(bbox_i.xmax)) - x + 1; 141 | float h = std::min(y2, static_cast(bbox_i.ymax)) - y + 1; 142 | if (w <= 0 || h <= 0) 143 | continue; 144 | 145 | float area2 = static_cast((bbox_i.xmax - bbox_i.xmin + 1) 146 | * (bbox_i.ymax - bbox_i.ymin + 1)); 147 | float area_intersect = w * h; 148 | 149 | switch (methodType) { 150 | case 'u': 151 | if (static_cast(area_intersect) 152 | / (area1 + area2 - area_intersect) > thresh) 153 | mask_merged[i] = 1; 154 | break; 155 | case 'm': 156 | if (static_cast(area_intersect) / std::min(area1, area2) 157 | > thresh) 158 | mask_merged[i] = 1; 159 | break; 160 | default: 161 | break; 162 | } 163 | } 164 | } 165 | return bboxes_nms; 166 | } 167 | static void BBoxRegression(vector& bboxes) { 168 | #ifdef _OPENMP 169 | #pragma omp parallel for num_threads(threads_num) 170 | #endif 171 | for (int i = 0; i < bboxes.size(); ++i) { 172 | FaceBox &bbox = bboxes[i].bbox; 173 | float *bbox_reg = bboxes[i].bbox_reg; 174 | float w = bbox.xmax - bbox.xmin + 1; 175 | float h = bbox.ymax - bbox.ymin + 1; 176 | bbox.xmin += bbox_reg[0] * w; 177 | bbox.ymin += bbox_reg[1] * h; 178 | bbox.xmax += bbox_reg[2] * w; 179 | bbox.ymax += bbox_reg[3] * h; 180 | 181 | // bbox.xmax += bbox_reg[2] * w; 182 | // bbox.ymax += bbox_reg[3] * h; 183 | } 184 | } 185 | static void BBoxPad(vector& bboxes, int width, int height) { 186 | #ifdef _OPENMP 187 | #pragma omp parallel for num_threads(threads_num) 188 | #endif 189 | for (int i = 0; i < bboxes.size(); ++i) { 190 | FaceBox &bbox = bboxes[i].bbox; 191 | bbox.xmin = round(std::max(bbox.xmin, 1.0f)); 192 | bbox.ymin = round(std::max(bbox.ymin, 1.0f)); 193 | bbox.xmax = round(std::min(bbox.xmax, width - 1.f)); 194 | bbox.ymax = round(std::min(bbox.ymax, height - 1.f)); 195 | } 196 | } 197 | static void BBoxPadSquare(vector& bboxes, int width, int height) { 198 | #ifdef _OPENMP 199 | #pragma omp parallel for num_threads(threads_num) 200 | #endif 201 | for (int i = 0; i < bboxes.size(); ++i) { 202 | FaceBox &bbox = bboxes[i].bbox; 203 | float w = bbox.xmax - bbox.xmin + 1; 204 | float h = bbox.ymax - bbox.ymin + 1; 205 | float side = h > w ? h : w; 206 | float side_w = h>w ? h:w; 207 | float side_h = 12 / 30.0 * side_w; // 24,60, 48,120 208 | 209 | bbox.xmin = round(std::max(bbox.xmin + (w - side_w) * 0.5f, 0.f)); 210 | bbox.ymin = round(std::max(bbox.ymin + (h - side_h) * 0.5f, 0.f)); 211 | bbox.xmax = round(std::min(bbox.xmin + side_w - 1, width - 1.f)); 212 | bbox.ymax = round(std::min(bbox.ymin + side_h - 1, height - 1.f)); 213 | } 214 | } 215 | static void GenerateBBox(float * prob1_confidence, float *reg_box, 216 | int feature_map_w_, int feature_map_h_, float scale, float thresh) { 217 | candidate_boxes_.clear(); 218 | int spatical_size = feature_map_w_ * feature_map_h_; 219 | for (int h=0; h thresh) { 222 | PlateInfo faceInfo; 223 | FaceBox & bbox = faceInfo.bbox; 224 | bbox.score = *prob1_confidence; 225 | bbox.xmin = w * pnet_stride * 2 / scale; 226 | bbox.ymin = h * pnet_stride / scale; 227 | bbox.xmax = ( w * pnet_stride + pnet_cell_size_width + 1 - 1.f) / scale; 228 | bbox.ymax = ( h * pnet_stride + pnet_cell_size_height + 1 - 1.f) / scale; 229 | 230 | // NxCxHxW format 231 | int index = h * feature_map_w_ + w; 232 | faceInfo.bbox_reg[0] = reg_box[index]; 233 | faceInfo.bbox_reg[1] = reg_box[index + spatical_size]; 234 | faceInfo.bbox_reg[2] = reg_box[index + spatical_size * 2]; 235 | faceInfo.bbox_reg[3] = reg_box[index + spatical_size * 3]; 236 | candidate_boxes_.push_back(faceInfo); 237 | } 238 | } 239 | } 240 | 241 | // float v_scale = 1 / scale; 242 | // for (int i = 0; i < spatical_size; ++i) { 243 | // int stride = i << 2; 244 | // if (confidence_data[stride + 1] >= thresh) { 245 | // int y = i / feature_map_w_; 246 | // int x = i - feature_map_w_ * y; 247 | // FaceInfo faceInfo; 248 | // FaceBox &faceBox = faceInfo.bbox; 249 | // 250 | // faceBox.xmin = (float) (x * pnet_stride) * v_scale; 251 | // faceBox.ymin = (float) (y * pnet_stride) * v_scale; 252 | // faceBox.xmax = (float) (x * pnet_stride + pnet_cell_size_width - 1.f) * v_scale; 253 | // faceBox.ymax = (float) (y * pnet_stride + pnet_cell_size_height - 1.f) * v_scale; 254 | // 255 | // faceInfo.bbox_reg[0] = reg_box[stride]; 256 | // faceInfo.bbox_reg[1] = reg_box[stride + 1]; 257 | // faceInfo.bbox_reg[2] = reg_box[stride + 2]; 258 | // faceInfo.bbox_reg[3] = reg_box[stride + 3]; 259 | // 260 | // faceBox.score = confidence_data[stride]; 261 | // candidate_boxes_.push_back(faceInfo); 262 | // } 263 | // } 264 | } 265 | 266 | MLPDR::MLPDR(const string& proto_model_dir, 267 | float threhold_p_, float threhold_r_, float threhold_o_ , float factor_) { 268 | threhold_p = threhold_p_; 269 | threhold_r = threhold_r_; 270 | threhold_o = threhold_o_; 271 | factor = factor_; 272 | // threads_num = 2; 273 | PNet_ = std::shared_ptr(MNN::Interpreter::createFromFile((proto_model_dir + "/det1.mnn").c_str())); 274 | RNet_ = std::shared_ptr(MNN::Interpreter::createFromFile((proto_model_dir + "/det2.mnn").c_str())); 275 | ONet_ = std::shared_ptr(MNN::Interpreter::createFromFile((proto_model_dir + "/det3.mnn").c_str())); 276 | 277 | MNN::ScheduleConfig config; 278 | config.type = MNN_FORWARD_CPU; 279 | config.numThread = 4; // 1 faster 280 | 281 | // we dont use backend config for x86 testing 282 | // BackendConfig backendConfig; 283 | // backendConfig.precision = BackendConfig::Precision_Low; 284 | // backendConfig.power = BackendConfig::Power_High; 285 | // config.backendConfig = &backendConfig; 286 | 287 | sess_p = PNet_->createSession(config); 288 | sess_r = RNet_->createSession(config); 289 | sess_o = ONet_->createSession(config); 290 | 291 | p_input = PNet_->getSessionInput(sess_p, NULL); 292 | p_out_pro = PNet_->getSessionOutput(sess_p, "prob1"); 293 | p_out_reg = PNet_->getSessionOutput(sess_p, "conv4-2"); 294 | 295 | r_input = RNet_->getSessionInput(sess_r, NULL); 296 | r_out_pro = RNet_->getSessionOutput(sess_r, "prob1"); 297 | r_out_reg = RNet_->getSessionOutput(sess_r, "conv5-2"); 298 | 299 | // auto outputs = ONet_->getSessionOutputAll(sess_o); 300 | // for (auto output: outputs) { 301 | // printf("ONet output name: %s \n", output.first.c_str()); 302 | // // ,output.second->shape()[1], 303 | // // output.second->shape()[2], 304 | // // output.second->shape()[3] 305 | //// ); 306 | // } 307 | 308 | o_input = ONet_->getSessionInput(sess_o, NULL); 309 | o_out_pro = ONet_->getSessionOutput(sess_o, "prob1"); 310 | o_out_reg = ONet_->getSessionOutput(sess_o, "conv6-2"); 311 | o_out_lank = ONet_->getSessionOutput(sess_o, "conv6-3"); 312 | 313 | 314 | 315 | LPRNet = shared_ptr( Interpreter::createFromFile((proto_model_dir + "/lpr.mnn").c_str())); 316 | ocr_session = LPRNet->createSession(config); 317 | ocr_input = LPRNet->getSessionInput(ocr_session, NULL); 318 | ocr_output = LPRNet->getSessionOutput(ocr_session, NULL); 319 | LPRNet->resizeTensor(ocr_input, ocr_input->shape()); 320 | LPRNet->resizeSession(ocr_session); 321 | 322 | Matrix lpr_trans; 323 | lpr_trans.setScale(1.0f, 1.0f); 324 | ImageProcess::Config lpr_config; 325 | lpr_config.filterType = NEAREST; 326 | const float mean_vals[3] = { 116.407, 133.722, 124.187 }; 327 | const float norm_vals[3] = { 1.0f, 1.0f, 1.0f }; 328 | ::memcpy(lpr_config.mean, mean_vals, sizeof(mean_vals)); 329 | ::memcpy(lpr_config.normal, norm_vals, sizeof(norm_vals)); 330 | lpr_config.sourceFormat = RGBA; 331 | lpr_config.destFormat = BGR; 332 | 333 | lpr_pretreat_data = std::shared_ptr(ImageProcess::create(lpr_config)); 334 | lpr_pretreat_data->setMatrix(lpr_trans); 335 | 336 | LPCNet_ = shared_ptr( Interpreter::createFromFile((proto_model_dir + "/lpc.mnn").c_str())); 337 | lpc_session = LPCNet_->createSession(config); 338 | lpc_input = LPCNet_->getSessionInput(lpc_session, NULL); 339 | lpc_output = LPCNet_->getSessionOutput(lpc_session, NULL); 340 | 341 | Matrix lpc_trans; 342 | lpc_trans.setScale(1.0f, 1.0f); 343 | ImageProcess::Config lpc_config; 344 | lpc_config.filterType = BICUBIC; 345 | const float lpc_mean_vals[3] = { 89.9372, 81.1989, 73.6352 }; 346 | // norm_vals[3] = { 1.0f, 1.0f, 1.0f }; 347 | ::memcpy(lpc_config.mean, mean_vals, sizeof(lpc_mean_vals)); 348 | ::memcpy(lpc_config.normal, norm_vals, sizeof(norm_vals)); 349 | lpc_config.sourceFormat = RGBA; 350 | lpc_config.destFormat = BGR; 351 | lpc_pretreat_data = std::shared_ptr(ImageProcess::create(lpc_config)); 352 | lpc_pretreat_data->setMatrix(lpc_trans); 353 | 354 | plateColorDict.push_back("白"); 355 | plateColorDict.push_back("黄"); 356 | plateColorDict.push_back("蓝"); 357 | plateColorDict.push_back("黑"); 358 | plateColorDict.push_back("绿"); 359 | 360 | printf("initialized!\n"); 361 | } 362 | 363 | MLPDR::~MLPDR() { 364 | PNet_->releaseModel(); 365 | RNet_->releaseModel(); 366 | ONet_->releaseModel(); 367 | LPRNet->releaseModel(); 368 | } 369 | 370 | uint8_t* get_img(cv::Mat img) { 371 | uchar * colorData = new uchar[img.total() * 4]; 372 | cv::Mat MatTemp(img.size(), CV_8UC4, colorData); 373 | cv::cvtColor(img, MatTemp, CV_BGR2RGBA, 4); 374 | return (uint8_t *) MatTemp.data; 375 | } 376 | 377 | 378 | void ctc_decode(Tensor * output, vector & codes) { 379 | Tensor outputHost(output, output->getDimensionType()); 380 | output->copyToHostTensor(&outputHost); 381 | auto values = outputHost.host(); 382 | int prev_class_idx = -1; 383 | for (int t=0; tbatch(); t++) { 384 | int max_class_idx = 0; 385 | float max_prob = *values; 386 | values++; 387 | for (int c=1; c < output->height(); c++, values++) { 388 | if (*values > max_prob) { 389 | max_prob = *values; 390 | max_class_idx = c; 391 | } 392 | } 393 | 394 | if (max_class_idx !=0 && max_class_idx != prev_class_idx) { 395 | codes.push_back(max_class_idx); 396 | } 397 | prev_class_idx = max_class_idx; 398 | } 399 | } 400 | 401 | std::string decode_plateNo(const vector & codes) { 402 | string plateNo = ""; 403 | for( auto it=codes.begin(); it != codes.end(); ++it) { 404 | plateNo += label[*it]; 405 | } 406 | return plateNo; 407 | } 408 | 409 | void MLPDR::recognize_plate_infos(const cv::Mat & img, vector & plateInfos) { 410 | for (auto & faceInfo: plateInfos) { 411 | vector codes = {}; 412 | cv::Point2f srcPoints[4]; 413 | cv::Point2f dstPoints[4]; 414 | 415 | int x0 = 0; int y0 = 0; 416 | int x1 = 128; int y1 = 0; 417 | int x2 = 128; int y2 = 32; 418 | int x3 = 0; int y3 = 32; 419 | dstPoints[0] = cv::Point2f(x0, y0); 420 | dstPoints[1] = cv::Point2f(x1, y1); 421 | dstPoints[2] = cv::Point2f(x2, y2); 422 | dstPoints[3] = cv::Point2f(x3, y3); 423 | 424 | for (int i=0; i<4; i++) { 425 | int x = i*2; 426 | int y = x + 1; 427 | srcPoints[i] = cv::Point2f(faceInfo.landmark[x], faceInfo.landmark[y]); 428 | } 429 | 430 | cv::Mat plate = cv::Mat::zeros(32, 128, img.type()); 431 | cv::Mat warp_mat = cv::getAffineTransform(srcPoints, dstPoints); 432 | cv::warpAffine(img, plate, warp_mat, plate.size(), cv::INTER_LINEAR); 433 | 434 | uint8_t *pImg = get_img(plate); 435 | lpr_pretreat_data->convert(pImg, plate.cols, plate.rows, 0, ocr_input); 436 | LPRNet->runSession(ocr_session); 437 | ctc_decode(ocr_output, codes); 438 | faceInfo.plateNo = decode_plateNo(codes); 439 | delete pImg; 440 | 441 | // predict plate color 442 | cv::resize(plate, plate, Size(110, 22)); 443 | pImg = get_img(plate); 444 | lpc_pretreat_data->convert(pImg, plate.cols, plate.rows, 0, lpc_input); 445 | LPCNet_->runSession(lpc_session); 446 | Tensor lpc_output_host(lpc_output, lpc_output->getDimensionType()); 447 | lpc_output->copyToHostTensor(&lpc_output_host); 448 | auto * probs = lpc_output_host.host(); 449 | float max = probs[0]; 450 | int clsId = 0; 451 | for (int i=1; i<5; i++) { 452 | if (probs[i] > max) { 453 | max = probs[i]; 454 | clsId = i; 455 | } 456 | } 457 | faceInfo.plateColor = plateColorDict[clsId]; 458 | delete pImg; 459 | } 460 | } 461 | 462 | void fillInput(const std::vector & dim, const cv::Mat & sample, Tensor* input) { 463 | int hs = dim[2]; 464 | int ws = dim[3]; 465 | auto inputHost = std::shared_ptr(MNN::Tensor::create({1, hs, ws, 3})); 466 | cv::Mat resized; 467 | if (sample.cols != ws || sample.rows != hs) { 468 | // printf("resize to %d x %d \n", hs, ws); 469 | cv::resize(sample, resized, cv::Size(ws, hs), 0, 0, cv::INTER_NEAREST); 470 | } 471 | else { 472 | resized = sample; 473 | } 474 | 475 | int index=0; 476 | for (int h=0; h < hs; h++) { 477 | for (int w=0; w < ws; w++) { 478 | cv::Vec3f pixel = resized.at(h, w); 479 | for (int c = 0 ; c < 3; c++, index++) { 480 | inputHost->host()[index] = pixel.val[c]; 481 | } 482 | } 483 | } 484 | input->copyFromHostTensor(inputHost.get()); 485 | } 486 | 487 | 488 | static vector ProposalNet(unsigned char * inputImage, int height, int width, int minSize, 489 | float threshold, float factor) { 490 | 491 | float scale = 12.0f / minSize; 492 | float minWH = std::min(height, width) * scale; 493 | std::vector scales; 494 | while (minWH >= minSize) { 495 | scales.push_back(scale); 496 | minWH *= factor; 497 | scale *= factor; 498 | } 499 | total_boxes_.clear(); 500 | 501 | for (int i = 0; i < scales.size(); i++) { 502 | int ws = (int) std::ceil(width * scales[i]); 503 | int hs = (int) std::ceil(height * scales[i]); 504 | std::vector inputDims = { 1, 3, hs, ws }; 505 | PNet_->resizeTensor(p_input, inputDims); 506 | 507 | PNet_->resizeSession(sess_p); 508 | 509 | MNN::CV::Matrix trans; 510 | trans.postScale(1.0f / ws, 1.0f / hs); 511 | trans.postScale(width, height); 512 | pretreat_data->setMatrix(trans); 513 | pretreat_data->convert(inputImage, width, height, 0, p_input); 514 | 515 | PNet_->runSession(sess_p); 516 | 517 | //onCopy to NCHW format 518 | Tensor prob_host(p_out_pro, p_out_pro->getDimensionType()); 519 | Tensor reg_host(p_out_reg, p_out_reg->getDimensionType()); 520 | p_out_pro->copyToHostTensor(&prob_host); 521 | p_out_reg->copyToHostTensor(®_host); 522 | auto * prob1_confidence = prob_host.host() + prob_host.stride(1); 523 | auto * reg = reg_host.host(); 524 | 525 | int feature_w = p_out_pro->width(); 526 | int feature_h = p_out_pro->height(); 527 | 528 | GenerateBBox(prob1_confidence, reg, feature_w, feature_h, scales[i], threshold); 529 | std::vector bboxes_nms = NMS(candidate_boxes_, 0.5f, 'u'); 530 | if (bboxes_nms.size() > 0) { 531 | total_boxes_.insert(total_boxes_.end(), bboxes_nms.begin(), 532 | bboxes_nms.end()); 533 | } 534 | } 535 | 536 | int num_box = (int) total_boxes_.size(); 537 | vector res_boxes; 538 | if (num_box != 0) { 539 | res_boxes = NMS(total_boxes_, 0.7f, 'u'); 540 | BBoxRegression(res_boxes); 541 | BBoxPadSquare(res_boxes, width, height); 542 | } 543 | return res_boxes; 544 | } 545 | 546 | /** 547 | * @sample is a normalized Mat 548 | */ 549 | static vector ProposalNet(const cv::Mat& sample, int minSize, 550 | float threshold, float factor) { 551 | int width = sample.cols; 552 | int height = sample.rows; 553 | float scale = factor; 554 | float minWH = std::min(height, width) * factor; 555 | std::vector scales; 556 | while (minWH >= minSize) { 557 | scales.push_back(scale); 558 | minWH *= factor; 559 | scale *= factor; 560 | } 561 | total_boxes_.clear(); 562 | 563 | for (int i = 0; i < scales.size(); i++) { 564 | int ws = (int) std::ceil(width * scales[i]); 565 | int hs = (int) std::ceil(height * scales[i]); 566 | std::vector inputDims = { 1, 3, hs, ws }; 567 | PNet_->resizeTensor(p_input, inputDims); 568 | PNet_->resizeSession(sess_p); 569 | fillInput(inputDims, sample, p_input); 570 | PNet_->runSession(sess_p); 571 | // MNN_PRINT("input tensor shape: %d %d %d %d\n", p_input->batch(), p_input->channel(), p_input->height(), p_input->width()); 572 | 573 | Tensor prob_host(p_out_pro, p_out_pro->getDimensionType()); 574 | Tensor reg_host(p_out_reg, p_out_reg->getDimensionType()); 575 | p_out_pro->copyToHostTensor(&prob_host); 576 | p_out_reg->copyToHostTensor(®_host); 577 | auto * prob1_confidence = prob_host.host() + prob_host.stride(1); 578 | auto * reg = reg_host.host(); 579 | int feature_w = p_out_pro->width(); 580 | int feature_h = p_out_pro->height(); 581 | // MNN_PRINT("output tensor shape: %d %d %d %d \n", p_out_reg->batch(), p_out_reg->channel(), p_out_reg->height(), p_out_reg->width()); 582 | GenerateBBox(prob1_confidence, reg, feature_w, feature_h, scales[i], threshold); 583 | std::vector bboxes_nms = NMS(candidate_boxes_, 0.5f, 'u'); 584 | if (bboxes_nms.size() > 0) { 585 | total_boxes_.insert(total_boxes_.end(), bboxes_nms.begin(), 586 | bboxes_nms.end()); 587 | } 588 | } 589 | int num_box = (int) total_boxes_.size(); 590 | vector res_boxes; 591 | if (num_box != 0) { 592 | res_boxes = NMS(total_boxes_, 0.7f, 'u'); 593 | BBoxRegression(res_boxes); 594 | BBoxPadSquare(res_boxes, width, height); 595 | } 596 | return res_boxes; 597 | } 598 | 599 | static std::vector NextStage(const cv::Mat& sample, 600 | vector &pre_stage_res, int input_w, int input_h, 601 | int stage_num, const float threshold) { 602 | vector res; 603 | int batch_size = pre_stage_res.size(); 604 | std::vector inputDims = {1, 3, input_h, input_w }; 605 | switch (stage_num) { 606 | case 2: { 607 | for (int n = 0; n < batch_size; ++n) { 608 | FaceBox & box = pre_stage_res[n].bbox; 609 | cv::Rect rect(cv::Point((int) box.xmin, (int) box.ymin), 610 | cv::Point((int) box.xmax, (int) box.ymax)); 611 | // cout << "regression bbox: " << rect << endl; 612 | cv::Mat roi(sample, rect); 613 | imshow("rnet sample", roi); 614 | waitKey(0); 615 | fillInput(inputDims, roi, r_input); 616 | RNet_->runSession(sess_r); 617 | 618 | Tensor r_out_pro_host(r_out_pro, r_out_pro->getDimensionType()); 619 | Tensor r_out_reg_host(r_out_reg, r_out_reg->getDimensionType()); 620 | r_out_pro->copyToHostTensor(&r_out_pro_host); 621 | r_out_reg->copyToHostTensor(&r_out_reg_host); 622 | MNN_PRINT("rnet run session and on copy to host\n"); 623 | auto confidence = r_out_pro_host.host() + r_out_pro_host.stride(1); 624 | auto reg_box = r_out_reg_host.host(); 625 | 626 | float conf = confidence[0]; 627 | if (conf >= threshold) { 628 | PlateInfo info; 629 | info.bbox.score = conf; 630 | info.bbox.xmin = pre_stage_res[n].bbox.xmin; 631 | info.bbox.ymin = pre_stage_res[n].bbox.ymin; 632 | info.bbox.xmax = pre_stage_res[n].bbox.xmax; 633 | info.bbox.ymax = pre_stage_res[n].bbox.ymax; 634 | for (int i = 0; i < 4; ++i) { 635 | info.bbox_reg[i] = reg_box[i]; 636 | } 637 | res.push_back(info); 638 | } 639 | } 640 | break; 641 | } 642 | case 3: { 643 | #ifdef _OPENMP 644 | #pragma omp parallel for num_threads(threads_num) 645 | #endif 646 | for (int n = 0; n < batch_size; ++n) { 647 | FaceBox &box = pre_stage_res[n].bbox; 648 | cv::Rect rect(cv::Point((int) box.xmin, (int) box.ymin), 649 | cv::Point((int) box.xmax, (int) box.ymax)); 650 | cv::Mat roi(sample, rect); 651 | fillInput(inputDims, roi, o_input); 652 | ONet_->runSession(sess_o); 653 | 654 | Tensor o_out_pro_host(o_out_pro, o_out_pro->getDimensionType()); 655 | Tensor o_out_reg_host(o_out_reg, o_out_reg->getDimensionType()); 656 | Tensor o_out_lank_host(o_out_lank, o_out_lank->getDimensionType()); 657 | o_out_pro->copyToHostTensor(&o_out_pro_host); 658 | o_out_reg->copyToHostTensor(&o_out_reg_host); 659 | o_out_lank->copyToHostTensor(&o_out_lank_host); 660 | 661 | auto confidence = o_out_pro_host.host() + o_out_pro_host.stride(1); 662 | auto reg_box = o_out_reg_host.host(); 663 | auto reg_landmark = o_out_lank_host.host(); 664 | 665 | float conf = confidence[0]; 666 | if (*confidence >= threshold) { 667 | PlateInfo info; 668 | info.bbox.score = conf; 669 | info.bbox.xmin = pre_stage_res[n].bbox.xmin; 670 | info.bbox.ymin = pre_stage_res[n].bbox.ymin; 671 | info.bbox.xmax = pre_stage_res[n].bbox.xmax; 672 | info.bbox.ymax = pre_stage_res[n].bbox.ymax; 673 | for (int i = 0; i < 4; ++i) { 674 | info.bbox_reg[i] = reg_box[i]; 675 | } 676 | float w = info.bbox.xmax - info.bbox.xmin + 1.f; 677 | float h = info.bbox.ymax - info.bbox.ymin + 1.f; 678 | // x x x x y y y y to x y x y x y x y 679 | for (int i = 0; i < 4; ++i) { 680 | info.landmark[2 * i] = reg_landmark[2 * i] * w + info.bbox.xmin; 681 | info.landmark[2 * i + 1] = reg_landmark[2 * i + 1] * h + info.bbox.ymin; 682 | } 683 | res.push_back(info); 684 | } 685 | } 686 | break; 687 | } 688 | default: 689 | return res; 690 | break; 691 | } 692 | return res; 693 | } 694 | 695 | vector MLPDR::detect(unsigned char * inputImage, int height, int width, const int min_face, const int stage) { 696 | vector pnet_res; 697 | vector rnet_res; 698 | vector onet_res; 699 | 700 | if (stage >= 1) { 701 | pnet_res = ProposalNet(inputImage, height, width, min_face, threhold_p, factor); 702 | } 703 | 704 | if (stage == 1) { 705 | return pnet_res; 706 | } else if (stage == 2) { 707 | return rnet_res; 708 | } else if (stage == 3) { 709 | return onet_res; 710 | } else { 711 | return onet_res; 712 | } 713 | } 714 | 715 | std::vector MLPDR::recognize(const cv::Mat & img) { 716 | vector faceInfos = Detect(img, 40, 3); 717 | recognize_plate_infos(img, faceInfos); 718 | return faceInfos; 719 | } 720 | vector MLPDR::Detect(const cv::Mat & image, int min_face, 721 | int stage) { 722 | vector pnet_res; 723 | vector rnet_res; 724 | vector onet_res; 725 | 726 | cv::Mat sample = image.clone(); 727 | sample.convertTo(sample, CV_32FC3, 0.0078125, -127.5*0.0078125); 728 | 729 | if (stage >= 1) { 730 | pnet_res = ProposalNet(sample, min_face, threhold_p, factor); 731 | MNN_PRINT("done ProposalNet with %d proposal \n", pnet_res.size()); 732 | } 733 | 734 | if (stage >= 2 && pnet_res.size() > 0) { 735 | if (pnet_max_detect_num < (int) pnet_res.size()) { 736 | pnet_res.resize(pnet_max_detect_num); 737 | } 738 | rnet_res = NextStage(sample, pnet_res, 60, 24, 2, threhold_r); 739 | rnet_res = NMS(rnet_res, iou_threhold, 'u'); 740 | BBoxRegression(rnet_res); 741 | BBoxPadSquare(rnet_res, image.cols, image.rows); 742 | } 743 | if (stage >= 3 && rnet_res.size() > 0) { 744 | onet_res = NextStage(sample, rnet_res, 120, 48, 3, threhold_o); 745 | BBoxRegression(onet_res); 746 | onet_res = NMS(onet_res, iou_threhold, 'm'); 747 | BBoxPad(onet_res, image.cols, image.rows); 748 | } 749 | if (stage == 1) { 750 | return pnet_res; 751 | } else if (stage == 2) { 752 | return rnet_res; 753 | } else if (stage == 3) { 754 | return onet_res; 755 | } else { 756 | return onet_res; 757 | } 758 | } 759 | 760 | static void extractMaxFace(vector& boundingBox_) { 761 | if (boundingBox_.empty()) { 762 | return; 763 | } 764 | sort(boundingBox_.begin(), boundingBox_.end(), CompareBBox); 765 | for (std::vector::iterator itx = boundingBox_.begin() + 1; 766 | itx != boundingBox_.end();) { 767 | itx = boundingBox_.erase(itx); 768 | } 769 | } 770 | 771 | std::vector MLPDR::Detect_MaxFace(const cv::Mat& img, 772 | const int min_face, const int stage) { 773 | vector pnet_res; 774 | vector rnet_res; 775 | vector onet_res; 776 | 777 | //total_boxes_.clear(); 778 | //candidate_boxes_.clear(); 779 | 780 | int width = img.cols; 781 | int height = img.rows; 782 | float scale = 12.0f / min_face; 783 | float minWH = std::min(height, width) * scale; 784 | std::vector scales; 785 | while (minWH >= 24) { 786 | scales.push_back(scale); 787 | minWH *= factor; 788 | scale *= factor; 789 | } 790 | 791 | std::reverse(scales.begin(), scales.end()); 792 | 793 | uint8_t *pImg = get_img(img); 794 | for (int i = 0; i < scales.size(); i++) { 795 | int ws = (int) std::ceil(width * scales[i]); 796 | int hs = (int) std::ceil(height * scales[i]); 797 | std::vector inputDims = { 1, 3, hs, ws }; 798 | PNet_->resizeTensor(p_input, inputDims); 799 | PNet_->resizeSession(sess_p); 800 | 801 | MNN::CV::Matrix trans; 802 | trans.postScale(1.0f / ws, 1.0f / hs); 803 | trans.postScale(width, height); 804 | pretreat_data->setMatrix(trans); 805 | pretreat_data->convert(pImg, width, height, 0, p_input); 806 | 807 | PNet_->runSession(sess_p); 808 | float * confidence = p_out_pro->host(); 809 | float * reg = p_out_reg->host(); 810 | 811 | int feature_w = p_out_pro->width(); 812 | int feature_h = p_out_pro->height(); 813 | 814 | GenerateBBox(confidence, reg, feature_w, feature_h, scales[i], 815 | threhold_p); 816 | std::vector bboxes_nms = NMS(candidate_boxes_, 0.5f, 'u'); 817 | 818 | //nmsTwoBoxs(bboxes_nms, pnet_res, 0.5); 819 | if (bboxes_nms.size() > 0) { 820 | pnet_res.insert(pnet_res.end(), bboxes_nms.begin(), 821 | bboxes_nms.end()); 822 | } else { 823 | continue; 824 | } 825 | BBoxRegression(pnet_res); 826 | BBoxPadSquare(pnet_res, width, height); 827 | 828 | bboxes_nms.clear(); 829 | bboxes_nms = NextStage(img, pnet_res, 60, 24, 2, threhold_r); 830 | bboxes_nms = NMS(bboxes_nms, iou_threhold, 'u'); 831 | //nmsTwoBoxs(bboxes_nms, rnet_res, 0.5) 832 | if (bboxes_nms.size() > 0) { 833 | rnet_res.insert(rnet_res.end(), bboxes_nms.begin(), 834 | bboxes_nms.end()); 835 | } else { 836 | pnet_res.clear(); 837 | continue; 838 | } 839 | BBoxRegression(rnet_res); 840 | BBoxPadSquare(rnet_res, img.cols, img.rows); 841 | 842 | onet_res = NextStage(img, rnet_res, 120, 48, 3, threhold_r); 843 | 844 | BBoxRegression(onet_res); 845 | onet_res = NMS(onet_res, iou_threhold, 'm'); 846 | BBoxPad(onet_res, img.cols, img.rows); 847 | 848 | if (onet_res.size() < 1) { 849 | pnet_res.clear(); 850 | rnet_res.clear(); 851 | continue; 852 | } else { 853 | extractMaxFace(onet_res); 854 | delete pImg; 855 | return onet_res; 856 | } 857 | } 858 | delete pImg; 859 | return std::vector { }; 860 | } 861 | 862 | } /* namespace mlpdr */ 863 | -------------------------------------------------------------------------------- /src/mlpdr/java_api.cpp: -------------------------------------------------------------------------------- 1 | /* 2 | * api.cpp 3 | * 4 | * Created on: Jul 14, 2019 5 | * Author: link 6 | */ 7 | 8 | 9 | #include 10 | #include 11 | #include 12 | #include 13 | #include 14 | #include 15 | 16 | #include 17 | 18 | using namespace std; 19 | using namespace mlpdr; 20 | using namespace cv; 21 | 22 | mlpdr::MLPDR * ptr_mlpdr; 23 | 24 | #if (defined _WIN32 || defined WINCE || defined __CYGWIN__) && defined CVAPI_EXPORTS 25 | # define API_EXPORTS __declspec(dllexport) 26 | #elif defined __GNUC__ && __GNUC__ >= 4 27 | # define API_EXPORTS __attribute__ ((visibility ("default"))) 28 | #else 29 | # define API_EXPORTS 30 | #endif 31 | 32 | #ifdef __cplusplus 33 | extern "C" { 34 | #endif 35 | 36 | 37 | API_EXPORTS void coreInitContext() { 38 | ptr_mlpdr = new MLPDR("/home/lqian/cpp-workspace/light-LPDR/models/"); 39 | } 40 | 41 | API_EXPORTS void cleanContext() { 42 | delete ptr_mlpdr; 43 | } 44 | 45 | /** 46 | * @res resource URL represent a image to be recognized 47 | * @contentType char buffer, full filename, network resource etc 48 | * @output a char point that point output content buffer 49 | */ 50 | API_EXPORTS int recogSingleJson(char * res, int contentType, char ** output) { 51 | Mat img = imread(res); 52 | if (img.empty()) { 53 | return -1; 54 | } 55 | 56 | vector plateInfos = ptr_mlpdr->recognize(img); 57 | if (plateInfos.size() == 0) return 0; 58 | string plateNo = plateInfos[0].plateNo; //only return 0 for highest confidence plate 59 | int len = plateNo.length(); 60 | *output = (char *) calloc(len, sizeof(char)); 61 | memcpy(*output, plateNo.c_str(), len); 62 | return len; 63 | } 64 | 65 | #ifdef __cplusplus 66 | } 67 | #endif 68 | 69 | --------------------------------------------------------------------------------