├── .gitignore ├── CMakeLists.txt ├── LICENSE ├── README.md ├── images └── test.jpg ├── models └── README.md └── src ├── CMakeLists.txt ├── common └── common.h ├── det ├── CMakeLists.txt ├── clipper │ ├── clipper.cpp │ └── clipper.hpp ├── detector.cpp ├── detector.h ├── lanms.hpp └── test.cpp ├── idcard ├── CMakeLists.txt ├── idcard.cpp ├── idcard.h ├── idcard_c_api.h └── test.cpp └── rec ├── CMakeLists.txt ├── decode.cpp ├── decode.h ├── recognizer.cpp ├── recognizer.h └── test.cpp /.gitignore: -------------------------------------------------------------------------------- 1 | .DS_Store 2 | build 3 | .vscode 4 | third_party 5 | models/*.onnx 6 | 3rdparty/onnxruntime/* 7 | 8 | # Prerequisites 9 | *.d 10 | 11 | # Compiled Object files 12 | *.slo 13 | *.lo 14 | *.o 15 | *.obj 16 | 17 | # Precompiled Headers 18 | *.gch 19 | *.pch 20 | 21 | # Compiled Dynamic libraries 22 | *.so 23 | *.dylib 24 | *.dll 25 | 26 | # Fortran module files 27 | *.mod 28 | *.smod 29 | 30 | # Compiled Static libraries 31 | *.lai 32 | *.la 33 | *.a 34 | *.lib 35 | 36 | # Executables 37 | *.exe 38 | *.out 39 | *.app 40 | -------------------------------------------------------------------------------- /CMakeLists.txt: -------------------------------------------------------------------------------- 1 | cmake_minimum_required(VERSION 3.5) 2 | project(TuYuIDCard) 3 | 4 | set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -std=c++11") 5 | 6 | include_directories(${PROJECT_SOURCE_DIR}/third_party) 7 | 8 | if(MSVC) 9 | set(OpenCV_DIR ${PROJECT_SOURCE_DIR}/third_party/win64/opencv/x64/vc15/lib) 10 | find_package(OpenCV REQUIRED) 11 | include_directories(${OpenCV_INCLUDE_DIR}) 12 | include_directories(${PROJECT_SOURCE_DIR}/third_party/win64/onnxruntime-win-x64-1.8.0/include) 13 | link_directories(${PROJECT_SOURCE_DIR}/third_party/win64/onnxruntime-win-x64-1.8.0/lib) 14 | else(MSVC) 15 | find_package(OpenCV REQUIRED) 16 | include_directories(${OpenCV_INCLUDE_DIR}) 17 | include_directories(${PROJECT_SOURCE_DIR}/third_party/onnxruntime/include) 18 | link_directories(${PROJECT_SOURCE_DIR}/third_party/onnxruntime/lib) 19 | endif() 20 | 21 | add_subdirectory(src) -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | Apache License 2 | Version 2.0, January 2004 3 | http://www.apache.org/licenses/ 4 | 5 | TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION 6 | 7 | 1. Definitions. 8 | 9 | "License" shall mean the terms and conditions for use, reproduction, 10 | and distribution as defined by Sections 1 through 9 of this document. 11 | 12 | "Licensor" shall mean the copyright owner or entity authorized by 13 | the copyright owner that is granting the License. 14 | 15 | "Legal Entity" shall mean the union of the acting entity and all 16 | other entities that control, are controlled by, or are under common 17 | control with that entity. For the purposes of this definition, 18 | "control" means (i) the power, direct or indirect, to cause the 19 | direction or management of such entity, whether by contract or 20 | otherwise, or (ii) ownership of fifty percent (50%) or more of the 21 | outstanding shares, or (iii) beneficial ownership of such entity. 22 | 23 | "You" (or "Your") shall mean an individual or Legal Entity 24 | exercising permissions granted by this License. 25 | 26 | "Source" form shall mean the preferred form for making modifications, 27 | including but not limited to software source code, documentation 28 | source, and configuration files. 29 | 30 | "Object" form shall mean any form resulting from mechanical 31 | transformation or translation of a Source form, including but 32 | not limited to compiled object code, generated documentation, 33 | and conversions to other media types. 34 | 35 | "Work" shall mean the work of authorship, whether in Source or 36 | Object form, made available under the License, as indicated by a 37 | copyright notice that is included in or attached to the work 38 | (an example is provided in the Appendix below). 39 | 40 | "Derivative Works" shall mean any work, whether in Source or Object 41 | form, that is based on (or derived from) the Work and for which the 42 | editorial revisions, annotations, elaborations, or other modifications 43 | represent, as a whole, an original work of authorship. For the purposes 44 | of this License, Derivative Works shall not include works that remain 45 | separable from, or merely link (or bind by name) to the interfaces of, 46 | the Work and Derivative Works thereof. 47 | 48 | "Contribution" shall mean any work of authorship, including 49 | the original version of the Work and any modifications or additions 50 | to that Work or Derivative Works thereof, that is intentionally 51 | submitted to Licensor for inclusion in the Work by the copyright owner 52 | or by an individual or Legal Entity authorized to submit on behalf of 53 | the copyright owner. For the purposes of this definition, "submitted" 54 | means any form of electronic, verbal, or written communication sent 55 | to the Licensor or its representatives, including but not limited to 56 | communication on electronic mailing lists, source code control systems, 57 | and issue tracking systems that are managed by, or on behalf of, the 58 | Licensor for the purpose of discussing and improving the Work, but 59 | excluding communication that is conspicuously marked or otherwise 60 | designated in writing by the copyright owner as "Not a Contribution." 61 | 62 | "Contributor" shall mean Licensor and any individual or Legal Entity 63 | on behalf of whom a Contribution has been received by Licensor and 64 | subsequently incorporated within the Work. 65 | 66 | 2. Grant of Copyright License. Subject to the terms and conditions of 67 | this License, each Contributor hereby grants to You a perpetual, 68 | worldwide, non-exclusive, no-charge, royalty-free, irrevocable 69 | copyright license to reproduce, prepare Derivative Works of, 70 | publicly display, publicly perform, sublicense, and distribute the 71 | Work and such Derivative Works in Source or Object form. 72 | 73 | 3. Grant of Patent License. Subject to the terms and conditions of 74 | this License, each Contributor hereby grants to You a perpetual, 75 | worldwide, non-exclusive, no-charge, royalty-free, irrevocable 76 | (except as stated in this section) patent license to make, have made, 77 | use, offer to sell, sell, import, and otherwise transfer the Work, 78 | where such license applies only to those patent claims licensable 79 | by such Contributor that are necessarily infringed by their 80 | Contribution(s) alone or by combination of their Contribution(s) 81 | with the Work to which such Contribution(s) was submitted. If You 82 | institute patent litigation against any entity (including a 83 | cross-claim or counterclaim in a lawsuit) alleging that the Work 84 | or a Contribution incorporated within the Work constitutes direct 85 | or contributory patent infringement, then any patent licenses 86 | granted to You under this License for that Work shall terminate 87 | as of the date such litigation is filed. 88 | 89 | 4. Redistribution. You may reproduce and distribute copies of the 90 | Work or Derivative Works thereof in any medium, with or without 91 | modifications, and in Source or Object form, provided that You 92 | meet the following conditions: 93 | 94 | (a) You must give any other recipients of the Work or 95 | Derivative Works a copy of this License; and 96 | 97 | (b) You must cause any modified files to carry prominent notices 98 | stating that You changed the files; and 99 | 100 | (c) You must retain, in the Source form of any Derivative Works 101 | that You distribute, all copyright, patent, trademark, and 102 | attribution notices from the Source form of the Work, 103 | excluding those notices that do not pertain to any part of 104 | the Derivative Works; and 105 | 106 | (d) If the Work includes a "NOTICE" text file as part of its 107 | distribution, then any Derivative Works that You distribute must 108 | include a readable copy of the attribution notices contained 109 | within such NOTICE file, excluding those notices that do not 110 | pertain to any part of the Derivative Works, in at least one 111 | of the following places: within a NOTICE text file distributed 112 | as part of the Derivative Works; within the Source form or 113 | documentation, if provided along with the Derivative Works; or, 114 | within a display generated by the Derivative Works, if and 115 | wherever such third-party notices normally appear. The contents 116 | of the NOTICE file are for informational purposes only and 117 | do not modify the License. You may add Your own attribution 118 | notices within Derivative Works that You distribute, alongside 119 | or as an addendum to the NOTICE text from the Work, provided 120 | that such additional attribution notices cannot be construed 121 | as modifying the License. 122 | 123 | You may add Your own copyright statement to Your modifications and 124 | may provide additional or different license terms and conditions 125 | for use, reproduction, or distribution of Your modifications, or 126 | for any such Derivative Works as a whole, provided Your use, 127 | reproduction, and distribution of the Work otherwise complies with 128 | the conditions stated in this License. 129 | 130 | 5. Submission of Contributions. Unless You explicitly state otherwise, 131 | any Contribution intentionally submitted for inclusion in the Work 132 | by You to the Licensor shall be under the terms and conditions of 133 | this License, without any additional terms or conditions. 134 | Notwithstanding the above, nothing herein shall supersede or modify 135 | the terms of any separate license agreement you may have executed 136 | with Licensor regarding such Contributions. 137 | 138 | 6. Trademarks. This License does not grant permission to use the trade 139 | names, trademarks, service marks, or product names of the Licensor, 140 | except as required for reasonable and customary use in describing the 141 | origin of the Work and reproducing the content of the NOTICE file. 142 | 143 | 7. Disclaimer of Warranty. Unless required by applicable law or 144 | agreed to in writing, Licensor provides the Work (and each 145 | Contributor provides its Contributions) on an "AS IS" BASIS, 146 | WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or 147 | implied, including, without limitation, any warranties or conditions 148 | of TITLE, NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A 149 | PARTICULAR PURPOSE. You are solely responsible for determining the 150 | appropriateness of using or redistributing the Work and assume any 151 | risks associated with Your exercise of permissions under this License. 152 | 153 | 8. Limitation of Liability. In no event and under no legal theory, 154 | whether in tort (including negligence), contract, or otherwise, 155 | unless required by applicable law (such as deliberate and grossly 156 | negligent acts) or agreed to in writing, shall any Contributor be 157 | liable to You for damages, including any direct, indirect, special, 158 | incidental, or consequential damages of any character arising as a 159 | result of this License or out of the use or inability to use the 160 | Work (including but not limited to damages for loss of goodwill, 161 | work stoppage, computer failure or malfunction, or any and all 162 | other commercial damages or losses), even if such Contributor 163 | has been advised of the possibility of such damages. 164 | 165 | 9. Accepting Warranty or Additional Liability. While redistributing 166 | the Work or Derivative Works thereof, You may choose to offer, 167 | and charge a fee for, acceptance of support, warranty, indemnity, 168 | or other liability obligations and/or rights consistent with this 169 | License. However, in accepting such obligations, You may act only 170 | on Your own behalf and on Your sole responsibility, not on behalf 171 | of any other Contributor, and only if You agree to indemnify, 172 | defend, and hold each Contributor harmless for any liability 173 | incurred by, or claims asserted against, such Contributor by reason 174 | of your accepting any such warranty or additional liability. 175 | 176 | END OF TERMS AND CONDITIONS 177 | 178 | APPENDIX: How to apply the Apache License to your work. 179 | 180 | To apply the Apache License to your work, attach the following 181 | boilerplate notice, with the fields enclosed by brackets "[]" 182 | replaced with your own identifying information. (Don't include 183 | the brackets!) The text should be enclosed in the appropriate 184 | comment syntax for the file format. We also recommend that a 185 | file or class name and description of purpose be included on the 186 | same "printed page" as the copyright notice for easier 187 | identification within third-party archives. 188 | 189 | Copyright [2020] [TuYuAI authors] 190 | 191 | Licensed under the Apache License, Version 2.0 (the "License"); 192 | you may not use this file except in compliance with the License. 193 | You may obtain a copy of the License at 194 | 195 | http://www.apache.org/licenses/LICENSE-2.0 196 | 197 | Unless required by applicable law or agreed to in writing, software 198 | distributed under the License is distributed on an "AS IS" BASIS, 199 | WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 200 | See the License for the specific language governing permissions and 201 | limitations under the License. 202 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # TuYuIDCard 2 | TuYuIDCard高精度跨平台身份证识别,全C++实现, 支持Windows,Linux, MacOS。 3 | 4 | 在线身份证OCR demo [**TuYuIDCard**](http://49.235.99.66/) 5 | 6 | ## 下载代码和依赖 7 | 本项目依赖opencv-4.5.2, onnxruntime-1.8.0, windows下要求VisualStudio2019,linux 下GCC>6.0, cmake > 3.5。 8 | 9 | 下载代码 10 | 11 | `git clone https://github.com/tuyuai/TuYuIDCard.git` 12 | 13 | 下载模型和依赖 14 | 15 | 在百度网盘下载 链接:https://pan.baidu.com/s/1V-xcXYJptHy_ud51P_aCGw 提取码:ygho , 并放入TuYuIDCard目录下。 16 | 17 | 18 | 19 | ## Build 20 | 21 | ```shell 22 | cd TuYuIDCard && mkdir build && cd build 23 | cmake .. 24 | make -j4 25 | ``` 26 | 27 | ## Test 28 | 29 | 在build目录下执行 30 | 31 | ```shell 32 | ./src/idcard_ocr_test ../models/det.onnx ../models/rec.onnx ../images/test.jpg 33 | ``` 34 | 35 | ## License 36 | 37 | This project is licensed under the [Apache-2.0 License](LICENSE). -------------------------------------------------------------------------------- /images/test.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/tuyuai/TuYuIDCard/e004645ed4b4c6b0eb578a7af8bce95614a4b8ec/images/test.jpg -------------------------------------------------------------------------------- /models/README.md: -------------------------------------------------------------------------------- 1 | 将det.onnx和rec.onnx放在当前目录下。 -------------------------------------------------------------------------------- /src/CMakeLists.txt: -------------------------------------------------------------------------------- 1 | include_directories(${PROJECT_SOURCE_DIR}/src) 2 | 3 | add_library(idcard_det SHARED det/detector.cpp det/clipper/clipper.cpp) 4 | target_link_libraries(idcard_det ${OpenCV_LIBS} onnxruntime) 5 | 6 | add_executable(idcard_det_test det/test.cpp) 7 | target_link_libraries(idcard_det_test idcard_det ${OpenCV_LIBS} onnxruntime) 8 | 9 | add_library(idcard_rec SHARED rec/recognizer.cpp rec/decode.cpp) 10 | target_link_libraries(idcard_rec ${OpenCV_LIBS} onnxruntime) 11 | 12 | add_executable(idcard_rec_test rec/test.cpp) 13 | target_link_libraries(idcard_rec_test idcard_rec ${OpenCV_LIBS} onnxruntime) 14 | 15 | add_library(idcard_ocr SHARED idcard/idcard.cpp) 16 | target_link_libraries(idcard_ocr ${OpenCV_LIBS} onnxruntime idcard_det idcard_rec) 17 | 18 | add_executable(idcard_ocr_test idcard/test.cpp) 19 | target_link_libraries(idcard_ocr_test idcard_ocr ${OpenCV_LIBS} onnxruntime) -------------------------------------------------------------------------------- /src/common/common.h: -------------------------------------------------------------------------------- 1 | #ifndef TUYUIDCARD_EXPORT_H_ 2 | #define TUYUIDCARD_EXPORT_H_ 3 | 4 | #define TUYUIDCARD_API_EXPORT 5 | //----------------------------- 6 | #if defined(WIN32) || defined(_WIN32) || defined(__WIN32__) || defined(__NT__) 7 | 8 | #ifdef TUYUIDCARD_API_EXPORT 9 | #define TUYUIDCARD_API __declspec(dllexport) 10 | #else 11 | #define TUYUIDCARD_API __declspec(dllimport) 12 | #endif 13 | 14 | #else 15 | // other os 16 | #define TAISHANOCR_API 17 | #endif 18 | 19 | 20 | 21 | #ifdef _WIN32 22 | #define TUYUIDCARD_HIDDEN 23 | #if defined(TUYUIDCARD_BUILD_SHARED_LIBS) 24 | #define TUYUIDCARD_EXPORT __declspec(dllexport) 25 | #define TUYUIDCARD_IMPORT __declspec(dllimport) 26 | #else 27 | #define TUYUIDCARD_EXPORT 28 | #define TUYUIDCARD_IMPORT 29 | #endif 30 | #else // _WIN32 31 | #if defined(__GNUC__) 32 | #define TUYUIDCARD_EXPORT __attribute__((__visibility__("default"))) 33 | #define TUYUIDCARD_HIDDEN __attribute__((__visibility__("hidden"))) 34 | #else // defined(__GNUC__) 35 | #define TUYUIDCARD_EXPORT 36 | #define TUYUIDCARD_HIDDEN 37 | #endif // defined(__GNUC__) 38 | #define TUYUIDCARD_IMPORT TUYUIDCARD_EXPORT 39 | #endif // _WIN32 40 | 41 | #ifdef NO_EXPORT 42 | #undef TUYUIDCARD_EXPORT 43 | #define TUYUIDCARD_EXPORT 44 | #endif 45 | 46 | 47 | #endif // TUYUIDCARD_EXPORT_H_ -------------------------------------------------------------------------------- /src/det/CMakeLists.txt: -------------------------------------------------------------------------------- 1 | include_directories(${CMAKE_CURRENT_DIR}) 2 | -------------------------------------------------------------------------------- /src/det/clipper/clipper.hpp: -------------------------------------------------------------------------------- 1 | /******************************************************************************* 2 | * * 3 | * Author : Angus Johnson * 4 | * Version : 6.4.0 * 5 | * Date : 2 July 2015 * 6 | * Website : http://www.angusj.com * 7 | * Copyright : Angus Johnson 2010-2015 * 8 | * * 9 | * License: * 10 | * Use, modification & distribution is subject to Boost Software License Ver 1. * 11 | * http://www.boost.org/LICENSE_1_0.txt * 12 | * * 13 | * Attributions: * 14 | * The code in this library is an extension of Bala Vatti's clipping algorithm: * 15 | * "A generic solution to polygon clipping" * 16 | * Communications of the ACM, Vol 35, Issue 7 (July 1992) pp 56-63. * 17 | * http://portal.acm.org/citation.cfm?id=129906 * 18 | * * 19 | * Computer graphics and geometric modeling: implementation and algorithms * 20 | * By Max K. Agoston * 21 | * Springer; 1 edition (January 4, 2005) * 22 | * http://books.google.com/books?q=vatti+clipping+agoston * 23 | * * 24 | * See also: * 25 | * "Polygon Offsetting by Computing Winding Numbers" * 26 | * Paper no. DETC2005-85513 pp. 565-575 * 27 | * ASME 2005 International Design Engineering Technical Conferences * 28 | * and Computers and Information in Engineering Conference (IDETC/CIE2005) * 29 | * September 24-28, 2005 , Long Beach, California, USA * 30 | * http://www.me.berkeley.edu/~mcmains/pubs/DAC05OffsetPolygon.pdf * 31 | * * 32 | *******************************************************************************/ 33 | 34 | #ifndef clipper_hpp 35 | #define clipper_hpp 36 | 37 | #define CLIPPER_VERSION "6.2.6" 38 | 39 | //use_int32: When enabled 32bit ints are used instead of 64bit ints. This 40 | //improve performance but coordinate values are limited to the range +/- 46340 41 | //#define use_int32 42 | 43 | //use_xyz: adds a Z member to IntPoint. Adds a minor cost to perfomance. 44 | //#define use_xyz 45 | 46 | //use_lines: Enables line clipping. Adds a very minor cost to performance. 47 | #define use_lines 48 | 49 | //use_deprecated: Enables temporary support for the obsolete functions 50 | //#define use_deprecated 51 | 52 | #include 53 | #include 54 | #include 55 | #include 56 | #include 57 | #include 58 | #include 59 | #include 60 | #include 61 | 62 | namespace ClipperLib { 63 | 64 | enum ClipType { ctIntersection, ctUnion, ctDifference, ctXor }; 65 | enum PolyType { ptSubject, ptClip }; 66 | //By far the most widely used winding rules for polygon filling are 67 | //EvenOdd & NonZero (GDI, GDI+, XLib, OpenGL, Cairo, AGG, Quartz, SVG, Gr32) 68 | //Others rules include Positive, Negative and ABS_GTR_EQ_TWO (only in OpenGL) 69 | //see http://glprogramming.com/red/chapter11.html 70 | enum PolyFillType { pftEvenOdd, pftNonZero, pftPositive, pftNegative }; 71 | 72 | #ifdef use_int32 73 | typedef int cInt; 74 | static cInt const loRange = 0x7FFF; 75 | static cInt const hiRange = 0x7FFF; 76 | #else 77 | typedef signed long long cInt; 78 | static cInt const loRange = 0x3FFFFFFF; 79 | static cInt const hiRange = 0x3FFFFFFFFFFFFFFFLL; 80 | typedef signed long long long64; //used by Int128 class 81 | typedef unsigned long long ulong64; 82 | 83 | #endif 84 | 85 | struct IntPoint { 86 | cInt X; 87 | cInt Y; 88 | #ifdef use_xyz 89 | cInt Z; 90 | IntPoint(cInt x = 0, cInt y = 0, cInt z = 0): X(x), Y(y), Z(z) {}; 91 | #else 92 | IntPoint(cInt x = 0, cInt y = 0): X(x), Y(y) {}; 93 | #endif 94 | 95 | friend inline bool operator== (const IntPoint& a, const IntPoint& b) 96 | { 97 | return a.X == b.X && a.Y == b.Y; 98 | } 99 | friend inline bool operator!= (const IntPoint& a, const IntPoint& b) 100 | { 101 | return a.X != b.X || a.Y != b.Y; 102 | } 103 | }; 104 | //------------------------------------------------------------------------------ 105 | 106 | struct DoublePoint 107 | { 108 | double X; 109 | double Y; 110 | DoublePoint(double x = 0, double y = 0) : X(x), Y(y) {} 111 | DoublePoint(IntPoint ip) : X((double)ip.X), Y((double)ip.Y) {} 112 | }; 113 | 114 | typedef std::vector< IntPoint > Path; 115 | typedef std::vector< Path > Paths; 116 | 117 | inline Path& operator <<(Path& poly, const IntPoint& p) {poly.push_back(p); return poly;} 118 | inline Paths& operator <<(Paths& polys, const Path& p) {polys.push_back(p); return polys;} 119 | 120 | std::ostream& operator <<(std::ostream &s, const IntPoint &p); 121 | std::ostream& operator <<(std::ostream &s, const Path &p); 122 | std::ostream& operator <<(std::ostream &s, const Paths &p); 123 | 124 | 125 | //------------------------------------------------------------------------------ 126 | 127 | #ifdef use_xyz 128 | typedef void (*ZFillCallback)(IntPoint& e1bot, IntPoint& e1top, IntPoint& e2bot, IntPoint& e2top, IntPoint& pt); 129 | #endif 130 | 131 | enum InitOptions {ioReverseSolution = 1, ioStrictlySimple = 2, ioPreserveCollinear = 4}; 132 | enum JoinType {jtSquare, jtRound, jtMiter}; 133 | enum EndType {etClosedPolygon, etClosedLine, etOpenButt, etOpenSquare, etOpenRound}; 134 | 135 | class PolyNode; 136 | typedef std::vector< PolyNode* > PolyNodes; 137 | 138 | class PolyNode 139 | { 140 | public: 141 | PolyNode(); 142 | virtual ~PolyNode(){}; 143 | Path Contour; 144 | PolyNodes Childs; 145 | PolyNode* Parent; 146 | PolyNode* GetNext() const; 147 | bool IsHole() const; 148 | bool IsOpen() const; 149 | int ChildCount() const; 150 | private: 151 | unsigned Index; //node index in Parent.Childs 152 | bool m_IsOpen; 153 | JoinType m_jointype; 154 | EndType m_endtype; 155 | PolyNode* GetNextSiblingUp() const; 156 | void AddChild(PolyNode& child); 157 | friend class Clipper; //to access Index 158 | friend class ClipperOffset; 159 | }; 160 | 161 | class PolyTree: public PolyNode 162 | { 163 | public: 164 | ~PolyTree(){Clear();}; 165 | PolyNode* GetFirst() const; 166 | void Clear(); 167 | int Total() const; 168 | private: 169 | PolyNodes AllNodes; 170 | friend class Clipper; //to access AllNodes 171 | }; 172 | 173 | bool Orientation(const Path &poly); 174 | double Area(const Path &poly); 175 | int PointInPolygon(const IntPoint &pt, const Path &path); 176 | 177 | void SimplifyPolygon(const Path &in_poly, Paths &out_polys, PolyFillType fillType = pftEvenOdd); 178 | void SimplifyPolygons(const Paths &in_polys, Paths &out_polys, PolyFillType fillType = pftEvenOdd); 179 | void SimplifyPolygons(Paths &polys, PolyFillType fillType = pftEvenOdd); 180 | 181 | void CleanPolygon(const Path& in_poly, Path& out_poly, double distance = 1.415); 182 | void CleanPolygon(Path& poly, double distance = 1.415); 183 | void CleanPolygons(const Paths& in_polys, Paths& out_polys, double distance = 1.415); 184 | void CleanPolygons(Paths& polys, double distance = 1.415); 185 | 186 | void MinkowskiSum(const Path& pattern, const Path& path, Paths& solution, bool pathIsClosed); 187 | void MinkowskiSum(const Path& pattern, const Paths& paths, Paths& solution, bool pathIsClosed); 188 | void MinkowskiDiff(const Path& poly1, const Path& poly2, Paths& solution); 189 | 190 | void PolyTreeToPaths(const PolyTree& polytree, Paths& paths); 191 | void ClosedPathsFromPolyTree(const PolyTree& polytree, Paths& paths); 192 | void OpenPathsFromPolyTree(PolyTree& polytree, Paths& paths); 193 | 194 | void ReversePath(Path& p); 195 | void ReversePaths(Paths& p); 196 | 197 | struct IntRect { cInt left; cInt top; cInt right; cInt bottom; }; 198 | 199 | //enums that are used internally ... 200 | enum EdgeSide { esLeft = 1, esRight = 2}; 201 | 202 | //forward declarations (for stuff used internally) ... 203 | struct TEdge; 204 | struct IntersectNode; 205 | struct LocalMinimum; 206 | struct OutPt; 207 | struct OutRec; 208 | struct Join; 209 | 210 | typedef std::vector < OutRec* > PolyOutList; 211 | typedef std::vector < TEdge* > EdgeList; 212 | typedef std::vector < Join* > JoinList; 213 | typedef std::vector < IntersectNode* > IntersectList; 214 | 215 | //------------------------------------------------------------------------------ 216 | 217 | //ClipperBase is the ancestor to the Clipper class. It should not be 218 | //instantiated directly. This class simply abstracts the conversion of sets of 219 | //polygon coordinates into edge objects that are stored in a LocalMinima list. 220 | class ClipperBase 221 | { 222 | public: 223 | ClipperBase(); 224 | virtual ~ClipperBase(); 225 | virtual bool AddPath(const Path &pg, PolyType PolyTyp, bool Closed); 226 | bool AddPaths(const Paths &ppg, PolyType PolyTyp, bool Closed); 227 | virtual void Clear(); 228 | IntRect GetBounds(); 229 | bool PreserveCollinear() {return m_PreserveCollinear;}; 230 | void PreserveCollinear(bool value) {m_PreserveCollinear = value;}; 231 | protected: 232 | void DisposeLocalMinimaList(); 233 | TEdge* AddBoundsToLML(TEdge *e, bool IsClosed); 234 | virtual void Reset(); 235 | TEdge* ProcessBound(TEdge* E, bool IsClockwise); 236 | void InsertScanbeam(const cInt Y); 237 | bool PopScanbeam(cInt &Y); 238 | bool LocalMinimaPending(); 239 | bool PopLocalMinima(cInt Y, const LocalMinimum *&locMin); 240 | OutRec* CreateOutRec(); 241 | void DisposeAllOutRecs(); 242 | void DisposeOutRec(PolyOutList::size_type index); 243 | void SwapPositionsInAEL(TEdge *edge1, TEdge *edge2); 244 | void DeleteFromAEL(TEdge *e); 245 | void UpdateEdgeIntoAEL(TEdge *&e); 246 | 247 | typedef std::vector MinimaList; 248 | MinimaList::iterator m_CurrentLM; 249 | MinimaList m_MinimaList; 250 | 251 | bool m_UseFullRange; 252 | EdgeList m_edges; 253 | bool m_PreserveCollinear; 254 | bool m_HasOpenPaths; 255 | PolyOutList m_PolyOuts; 256 | TEdge *m_ActiveEdges; 257 | 258 | typedef std::priority_queue ScanbeamList; 259 | ScanbeamList m_Scanbeam; 260 | }; 261 | //------------------------------------------------------------------------------ 262 | 263 | class Clipper : public virtual ClipperBase 264 | { 265 | public: 266 | Clipper(int initOptions = 0); 267 | bool Execute(ClipType clipType, 268 | Paths &solution, 269 | PolyFillType fillType = pftEvenOdd); 270 | bool Execute(ClipType clipType, 271 | Paths &solution, 272 | PolyFillType subjFillType, 273 | PolyFillType clipFillType); 274 | bool Execute(ClipType clipType, 275 | PolyTree &polytree, 276 | PolyFillType fillType = pftEvenOdd); 277 | bool Execute(ClipType clipType, 278 | PolyTree &polytree, 279 | PolyFillType subjFillType, 280 | PolyFillType clipFillType); 281 | bool ReverseSolution() { return m_ReverseOutput; }; 282 | void ReverseSolution(bool value) {m_ReverseOutput = value;}; 283 | bool StrictlySimple() {return m_StrictSimple;}; 284 | void StrictlySimple(bool value) {m_StrictSimple = value;}; 285 | //set the callback function for z value filling on intersections (otherwise Z is 0) 286 | #ifdef use_xyz 287 | void ZFillFunction(ZFillCallback zFillFunc); 288 | #endif 289 | protected: 290 | virtual bool ExecuteInternal(); 291 | private: 292 | JoinList m_Joins; 293 | JoinList m_GhostJoins; 294 | IntersectList m_IntersectList; 295 | ClipType m_ClipType; 296 | typedef std::list MaximaList; 297 | MaximaList m_Maxima; 298 | TEdge *m_SortedEdges; 299 | bool m_ExecuteLocked; 300 | PolyFillType m_ClipFillType; 301 | PolyFillType m_SubjFillType; 302 | bool m_ReverseOutput; 303 | bool m_UsingPolyTree; 304 | bool m_StrictSimple; 305 | #ifdef use_xyz 306 | ZFillCallback m_ZFill; //custom callback 307 | #endif 308 | void SetWindingCount(TEdge& edge); 309 | bool IsEvenOddFillType(const TEdge& edge) const; 310 | bool IsEvenOddAltFillType(const TEdge& edge) const; 311 | void InsertLocalMinimaIntoAEL(const cInt botY); 312 | void InsertEdgeIntoAEL(TEdge *edge, TEdge* startEdge); 313 | void AddEdgeToSEL(TEdge *edge); 314 | bool PopEdgeFromSEL(TEdge *&edge); 315 | void CopyAELToSEL(); 316 | void DeleteFromSEL(TEdge *e); 317 | void SwapPositionsInSEL(TEdge *edge1, TEdge *edge2); 318 | bool IsContributing(const TEdge& edge) const; 319 | bool IsTopHorz(const cInt XPos); 320 | void DoMaxima(TEdge *e); 321 | void ProcessHorizontals(); 322 | void ProcessHorizontal(TEdge *horzEdge); 323 | void AddLocalMaxPoly(TEdge *e1, TEdge *e2, const IntPoint &pt); 324 | OutPt* AddLocalMinPoly(TEdge *e1, TEdge *e2, const IntPoint &pt); 325 | OutRec* GetOutRec(int idx); 326 | void AppendPolygon(TEdge *e1, TEdge *e2); 327 | void IntersectEdges(TEdge *e1, TEdge *e2, IntPoint &pt); 328 | OutPt* AddOutPt(TEdge *e, const IntPoint &pt); 329 | OutPt* GetLastOutPt(TEdge *e); 330 | bool ProcessIntersections(const cInt topY); 331 | void BuildIntersectList(const cInt topY); 332 | void ProcessIntersectList(); 333 | void ProcessEdgesAtTopOfScanbeam(const cInt topY); 334 | void BuildResult(Paths& polys); 335 | void BuildResult2(PolyTree& polytree); 336 | void SetHoleState(TEdge *e, OutRec *outrec); 337 | void DisposeIntersectNodes(); 338 | bool FixupIntersectionOrder(); 339 | void FixupOutPolygon(OutRec &outrec); 340 | void FixupOutPolyline(OutRec &outrec); 341 | bool IsHole(TEdge *e); 342 | bool FindOwnerFromSplitRecs(OutRec &outRec, OutRec *&currOrfl); 343 | void FixHoleLinkage(OutRec &outrec); 344 | void AddJoin(OutPt *op1, OutPt *op2, const IntPoint offPt); 345 | void ClearJoins(); 346 | void ClearGhostJoins(); 347 | void AddGhostJoin(OutPt *op, const IntPoint offPt); 348 | bool JoinPoints(Join *j, OutRec* outRec1, OutRec* outRec2); 349 | void JoinCommonEdges(); 350 | void DoSimplePolygons(); 351 | void FixupFirstLefts1(OutRec* OldOutRec, OutRec* NewOutRec); 352 | void FixupFirstLefts2(OutRec* InnerOutRec, OutRec* OuterOutRec); 353 | void FixupFirstLefts3(OutRec* OldOutRec, OutRec* NewOutRec); 354 | #ifdef use_xyz 355 | void SetZ(IntPoint& pt, TEdge& e1, TEdge& e2); 356 | #endif 357 | }; 358 | //------------------------------------------------------------------------------ 359 | 360 | class ClipperOffset 361 | { 362 | public: 363 | ClipperOffset(double miterLimit = 2.0, double roundPrecision = 0.25); 364 | ~ClipperOffset(); 365 | void AddPath(const Path& path, JoinType joinType, EndType endType); 366 | void AddPaths(const Paths& paths, JoinType joinType, EndType endType); 367 | void Execute(Paths& solution, double delta); 368 | void Execute(PolyTree& solution, double delta); 369 | void Clear(); 370 | double MiterLimit; 371 | double ArcTolerance; 372 | private: 373 | Paths m_destPolys; 374 | Path m_srcPoly; 375 | Path m_destPoly; 376 | std::vector m_normals; 377 | double m_delta, m_sinA, m_sin, m_cos; 378 | double m_miterLim, m_StepsPerRad; 379 | IntPoint m_lowest; 380 | PolyNode m_polyNodes; 381 | 382 | void FixOrientations(); 383 | void DoOffset(double delta); 384 | void OffsetPoint(int j, int& k, JoinType jointype); 385 | void DoSquare(int j, int k); 386 | void DoMiter(int j, int k, double r); 387 | void DoRound(int j, int k); 388 | }; 389 | //------------------------------------------------------------------------------ 390 | 391 | class clipperException : public std::exception 392 | { 393 | public: 394 | clipperException(const char* description): m_descr(description) {} 395 | virtual ~clipperException() throw() {} 396 | virtual const char* what() const throw() {return m_descr.c_str();} 397 | private: 398 | std::string m_descr; 399 | }; 400 | //------------------------------------------------------------------------------ 401 | 402 | } //ClipperLib namespace 403 | 404 | #endif //clipper_hpp 405 | 406 | 407 | -------------------------------------------------------------------------------- /src/det/detector.cpp: -------------------------------------------------------------------------------- 1 | // Copyright(c) TuYuAI authors.All rights reserved. 2 | // Licensed under the Apache-2.0 License. 3 | // 4 | 5 | #include "detector.h" 6 | #include 7 | #include 8 | #include 9 | #include "det/lanms.hpp" 10 | #include "spdlog/spdlog.h" 11 | using namespace lanms; 12 | 13 | #define ORT_ABORT_ON_ERROR(expr) \ 14 | do { \ 15 | OrtStatus* onnx_status = (expr); \ 16 | if (onnx_status != NULL) { \ 17 | const char* msg = ort_api_->GetErrorMessage(onnx_status); \ 18 | fprintf(stderr, "%s\n", msg); \ 19 | ort_api_->ReleaseStatus(onnx_status); \ 20 | abort(); \ 21 | } \ 22 | } while (0); 23 | 24 | Detector::~Detector() { 25 | if (session_ != nullptr) { 26 | ort_api_->ReleaseSession(session_); 27 | } 28 | if (session_options_ != nullptr) { 29 | ort_api_->ReleaseSessionOptions(session_options_); 30 | } 31 | } 32 | 33 | void Detector::InitModel(const std::string& model_path) { 34 | ORT_ABORT_ON_ERROR(ort_api_->CreateSessionOptions(&session_options_)); 35 | ort_api_->SetIntraOpNumThreads(session_options_, 1); 36 | ort_api_->SetSessionGraphOptimizationLevel(session_options_, ORT_ENABLE_ALL); 37 | #if defined(WIN32) || defined(_WIN32) || defined(__WIN32__) || defined(__NT__) 38 | std::wstring w_model_path; 39 | ORT_ABORT_ON_ERROR(ort_api_->CreateSession(env_, w_model_path.c_str(), 40 | session_options_, &session_)); 41 | #else 42 | ORT_ABORT_ON_ERROR(ort_api_->CreateSession(env_, model_path.c_str(), 43 | session_options_, &session_)); 44 | #endif 45 | } 46 | 47 | void Detector::GetInputs() { 48 | size_t num_input_nodes; 49 | OrtStatus* status; 50 | OrtAllocator* allocator; 51 | ORT_ABORT_ON_ERROR(ort_api_->GetAllocatorWithDefaultOptions(&allocator)); 52 | ort_api_->SessionGetInputCount(session_, &num_input_nodes); 53 | input_node_names_.resize(num_input_nodes); 54 | printf("Number of inputs = %zu\n", num_input_nodes); 55 | 56 | // iterate over all input nodes 57 | for (size_t i = 0; i < num_input_nodes; i++) { 58 | // print input node names 59 | char* input_name; 60 | status = ort_api_->SessionGetInputName(session_, i, allocator, &input_name); 61 | printf("Input %zu : name=%s\n", i, input_name); 62 | input_node_names_[i] = input_name; 63 | 64 | // print input node types 65 | OrtTypeInfo* typeinfo; 66 | status = ort_api_->SessionGetInputTypeInfo(session_, i, &typeinfo); 67 | const OrtTensorTypeAndShapeInfo* tensor_info; 68 | ORT_ABORT_ON_ERROR( 69 | ort_api_->CastTypeInfoToTensorInfo(typeinfo, &tensor_info)); 70 | ONNXTensorElementDataType type; 71 | ORT_ABORT_ON_ERROR(ort_api_->GetTensorElementType(tensor_info, &type)); 72 | printf("Input %zu : type=%d\n", i, type); 73 | 74 | // print input shapes/dims 75 | size_t num_dims; 76 | ORT_ABORT_ON_ERROR(ort_api_->GetDimensionsCount(tensor_info, &num_dims)); 77 | printf("Input %zu : num_dims=%zu\n", i, num_dims); 78 | input_node_dims_.resize(num_dims); 79 | ort_api_->GetDimensions(tensor_info, (int64_t*)input_node_dims_.data(), 80 | num_dims); 81 | for (size_t j = 0; j < num_dims; j++) 82 | printf("Input %zu : dim %zu=%jd\n", i, j, input_node_dims_[j]); 83 | 84 | ort_api_->ReleaseTypeInfo(typeinfo); 85 | } 86 | } 87 | 88 | void Detector::GetTensorDataAndShape(OrtValue* input_map, float** array, 89 | std::vector& node_dims) { 90 | ORT_ABORT_ON_ERROR(ort_api_->GetTensorMutableData(input_map, (void**)array)); 91 | 92 | OrtTensorTypeAndShapeInfo* tensor_info; 93 | ORT_ABORT_ON_ERROR(ort_api_->GetTensorTypeAndShape(input_map, &tensor_info)); 94 | 95 | size_t num_dims; 96 | ORT_ABORT_ON_ERROR(ort_api_->GetDimensionsCount(tensor_info, &num_dims)); 97 | node_dims.resize(num_dims); 98 | ORT_ABORT_ON_ERROR(ort_api_->GetDimensions( 99 | tensor_info, (int64_t*)node_dims.data(), num_dims)); 100 | ort_api_->ReleaseTensorTypeAndShapeInfo(tensor_info); 101 | } 102 | 103 | void Detector::Preprocess(const cv::Mat& input_image, cv::Mat& out_image, 104 | float& ratio_w, float& ratio_h) { 105 | cv::Mat image; 106 | cv::cvtColor(input_image, image, cv::COLOR_BGR2RGB); 107 | int input_width = image.cols; 108 | int input_height = image.rows; 109 | int param_w = 602; 110 | int param_h = 378; 111 | 112 | int param_max_side_length = 1200; 113 | float ratio = 1.0; 114 | if (std::max(input_height, input_width) > param_max_side_length) { 115 | ratio = float(param_max_side_length) / input_height; 116 | if (input_height < input_width) { 117 | ratio = float(param_max_side_length) / input_width; 118 | } 119 | } 120 | 121 | int new_h = int(input_height * ratio); 122 | int new_w = int(input_width * ratio); 123 | 124 | float param_h_ratio = (float)param_h / float(new_h); 125 | float param_w_ratio = (float)param_w / float(new_w); 126 | 127 | ratio = std::min(param_w_ratio, param_h_ratio); 128 | new_h = int(new_h * ratio); 129 | new_w = int(new_w * ratio); 130 | 131 | if (new_h % 32 != 0) { 132 | new_h = int(new_h / 32) * 32; 133 | } 134 | if (new_w % 32 != 0) { 135 | new_w = int(new_w / 32) * 32; 136 | } 137 | 138 | cv::Mat resize_image; 139 | cv::resize(image, resize_image, cv::Size(new_w, new_h)); 140 | resize_image.convertTo(out_image, CV_32FC3); 141 | SPDLOG_INFO("image resize from [{} {}] -> [{} {}]", input_width, input_height, 142 | new_w, new_h); 143 | 144 | ratio_w = new_w / float(input_width); 145 | ratio_h = new_h / float(input_height); 146 | } 147 | 148 | std::vector RestoreRBox(float* geo_array, float* score_array, int height, 149 | int width) { 150 | std::vector quad_data; 151 | for (int i = 0; i < height; i++) { 152 | for (int j = 0; j < width; j++) { 153 | int idx = i * width + j; 154 | if (score_array[idx] > 0.8) { 155 | int x = j * 4; 156 | int y = i * 4; 157 | float d0 = geo_array[i * width * 5 + j * 5 + 0]; 158 | float d1 = geo_array[i * width * 5 + j * 5 + 1]; 159 | float d2 = geo_array[i * width * 5 + j * 5 + 2]; 160 | float d3 = geo_array[i * width * 5 + j * 5 + 3]; 161 | float angle = geo_array[i * width * 5 + j * 5 + 4]; 162 | if (angle >= 0) { 163 | float a0 = 0; 164 | float a1 = -d0 - d2; 165 | float a2 = d1 + d3; 166 | float a3 = -d0 - d2; 167 | float a4 = d1 + d3; 168 | float a5 = 0; 169 | float a6 = 0; 170 | float a7 = 0; 171 | float a8 = d3; 172 | float a9 = -d2; 173 | 174 | float rotate_x0 = std::cos(angle); 175 | float rotate_x1 = std::sin(angle); 176 | 177 | float rotate_y0 = -std::sin(angle); 178 | float rotate_y1 = std::cos(angle); 179 | 180 | float bx0 = rotate_x0 * a0 + rotate_x1 * a1; 181 | float bx1 = rotate_x0 * a2 + rotate_x1 * a3; 182 | float bx2 = rotate_x0 * a4 + rotate_x1 * a5; 183 | float bx3 = rotate_x0 * a6 + rotate_x1 * a7; 184 | float bx4 = rotate_x0 * a8 + rotate_x1 * a9; 185 | 186 | float by0 = rotate_y0 * a0 + rotate_y1 * a1; 187 | float by1 = rotate_y0 * a2 + rotate_y1 * a3; 188 | float by2 = rotate_y0 * a4 + rotate_y1 * a5; 189 | float by3 = rotate_y0 * a6 + rotate_y1 * a7; 190 | float by4 = rotate_y0 * a8 + rotate_y1 * a9; 191 | 192 | float org_x = x - bx4; 193 | float org_y = y - by4; 194 | float new_px0 = bx0 + org_x; 195 | float new_py0 = by0 + org_y; 196 | float new_px1 = bx1 + org_x; 197 | float new_py1 = by1 + org_y; 198 | float new_px2 = bx2 + org_x; 199 | float new_py2 = by2 + org_y; 200 | float new_px3 = bx3 + org_x; 201 | float new_py3 = by3 + org_y; 202 | 203 | quad_data.push_back(new_px0); 204 | quad_data.push_back(new_py0); 205 | quad_data.push_back(new_px1); 206 | quad_data.push_back(new_py1); 207 | quad_data.push_back(new_px2); 208 | quad_data.push_back(new_py2); 209 | quad_data.push_back(new_px3); 210 | quad_data.push_back(new_py3); 211 | quad_data.push_back(score_array[idx]); 212 | } else { 213 | float a0 = -d1 - d3; 214 | float a1 = -d0 - d2; 215 | float a2 = 0; 216 | float a3 = -d0 - d2; 217 | float a4 = 0; 218 | float a5 = 0; 219 | float a6 = -d1 - d3; 220 | float a7 = 0; 221 | float a8 = -d1; 222 | float a9 = -d2; 223 | 224 | float rotate_x0 = std::cos(-angle); 225 | float rotate_x1 = -std::sin(-angle); 226 | 227 | float rotate_y0 = std::sin(-angle); 228 | float rotate_y1 = std::cos(-angle); 229 | 230 | float bx0 = rotate_x0 * a0 + rotate_x1 * a1; 231 | float bx1 = rotate_x0 * a2 + rotate_x1 * a3; 232 | float bx2 = rotate_x0 * a4 + rotate_x1 * a5; 233 | float bx3 = rotate_x0 * a6 + rotate_x1 * a7; 234 | float bx4 = rotate_x0 * a8 + rotate_x1 * a9; 235 | 236 | float by0 = rotate_y0 * a0 + rotate_y1 * a1; 237 | float by1 = rotate_y0 * a2 + rotate_y1 * a3; 238 | float by2 = rotate_y0 * a4 + rotate_y1 * a5; 239 | float by3 = rotate_y0 * a6 + rotate_y1 * a7; 240 | float by4 = rotate_y0 * a8 + rotate_y1 * a9; 241 | 242 | float org_x = x - bx4; 243 | float org_y = y - by4; 244 | float new_px0 = bx0 + org_x; 245 | float new_py0 = by0 + org_y; 246 | float new_px1 = bx1 + org_x; 247 | float new_py1 = by1 + org_y; 248 | float new_px2 = bx2 + org_x; 249 | float new_py2 = by2 + org_y; 250 | float new_px3 = bx3 + org_x; 251 | float new_py3 = by3 + org_y; 252 | 253 | quad_data.push_back(new_px0); 254 | quad_data.push_back(new_py0); 255 | quad_data.push_back(new_px1); 256 | quad_data.push_back(new_py1); 257 | quad_data.push_back(new_px2); 258 | quad_data.push_back(new_py2); 259 | quad_data.push_back(new_px3); 260 | quad_data.push_back(new_py3); 261 | quad_data.push_back(score_array[idx]); 262 | } 263 | } 264 | } 265 | } 266 | 267 | return quad_data; 268 | } 269 | 270 | void Detector::Predict(const cv::Mat& image, 271 | std::vector>& textlines) { 272 | cv::Mat out_image; 273 | float ratio_h; 274 | float ratio_w; 275 | Preprocess(image, out_image, ratio_w, ratio_h); 276 | SPDLOG_DEBUG("image h={} w={} resize h={} w={} ratio h={} ratio w = {}", 277 | image.rows, image.cols, out_image.rows, out_image.cols, ratio_h, 278 | ratio_w); 279 | int image_width = out_image.cols; 280 | int image_height = out_image.rows; 281 | int image_channels = out_image.channels(); 282 | 283 | std::vector input_node_dims = {1, image_height, image_width, 284 | image_channels}; 285 | size_t input_tensor_size = image_width * image_height * image_channels; 286 | 287 | // create input tensor object from data values 288 | OrtMemoryInfo* allocator_info; 289 | ORT_ABORT_ON_ERROR(ort_api_->CreateCpuMemoryInfo( 290 | OrtArenaAllocator, OrtMemTypeDefault, &allocator_info)); 291 | OrtValue* input_tensor = NULL; 292 | ORT_ABORT_ON_ERROR(ort_api_->CreateTensorWithDataAsOrtValue( 293 | allocator_info, out_image.data, input_tensor_size * sizeof(float), 294 | input_node_dims.data(), 4, ONNX_TENSOR_ELEMENT_DATA_TYPE_FLOAT, 295 | &input_tensor)); 296 | int is_tensor; 297 | ORT_ABORT_ON_ERROR(ort_api_->IsTensor(input_tensor, &is_tensor)); 298 | assert(is_tensor); 299 | 300 | OrtStatus* status; 301 | // const char* input_names[] = {"input_images:0"}; 302 | // const char* output_names[] = {"feature_fusion/concat_3:0", 303 | // "feature_fusion/Conv_7/Sigmoid:0"}; 304 | const char* input_names[] = {"input"}; 305 | const char* output_names[] = {"geo_map", "score_map"}; 306 | 307 | OrtValue* output_tensor[2]; 308 | output_tensor[0] = NULL; 309 | output_tensor[1] = NULL; 310 | SPDLOG_INFO("East run begin"); 311 | ORT_ABORT_ON_ERROR(ort_api_->Run(this->session_, NULL, input_names, 312 | (const OrtValue* const*)&input_tensor, 1, 313 | output_names, 2, output_tensor)); 314 | SPDLOG_INFO("East run end"); 315 | OrtValue* geo_map = output_tensor[0]; 316 | OrtValue* score_map = output_tensor[1]; 317 | 318 | std::vector geo_shape; 319 | float* geo_array; 320 | GetTensorDataAndShape(geo_map, &geo_array, geo_shape); 321 | std::vector score_shape; 322 | float* score_array; 323 | GetTensorDataAndShape(score_map, &score_array, score_shape); 324 | 325 | int batch = score_shape[0]; 326 | int height = score_shape[1]; 327 | int width = score_shape[2]; 328 | int geo_count = geo_shape[3]; 329 | int score_count = score_shape[3]; 330 | 331 | std::vector quad_data = 332 | RestoreRBox(geo_array, score_array, height, width); 333 | 334 | for (int i = 0; i < quad_data.size() / 9; i++) { 335 | for (int j = 0; j < 8; j++) { 336 | quad_data[i * 9 + j] *= 10000.0f; 337 | } 338 | } 339 | std::vector polys = 340 | merge_quadrangle_n9(quad_data.data(), quad_data.size() / 9, 0.2f); 341 | std::vector> boxes = polys2floats_new(polys); 342 | 343 | for (int i = 0; i < boxes.size(); i++) { 344 | auto box = boxes[i]; 345 | std::vector line_item; 346 | for (int i = 0; i < 4; i++) { 347 | line_item.push_back( 348 | cv::Point2f(box[2 * i + 0] / ratio_w, box[2 * i + 1] / ratio_h)); 349 | } 350 | textlines.push_back(line_item); 351 | } 352 | 353 | ort_api_->ReleaseValue(geo_map); 354 | ort_api_->ReleaseValue(score_map); 355 | ort_api_->ReleaseValue(input_tensor); 356 | ort_api_->ReleaseMemoryInfo(allocator_info); 357 | return; 358 | } 359 | 360 | cv::Mat Detector::ShowTextLines( 361 | const cv::Mat& input, std::vector>& textlines) { 362 | cv::Mat image = input.clone(); 363 | for (int i = 0; i < textlines.size(); i++) { 364 | std::vector& pts = textlines[i]; 365 | cv::line(image, pts[0], pts[1], cv::Scalar(0, 255, 0), 2); 366 | cv::line(image, pts[1], pts[2], cv::Scalar(0, 255, 0), 2); 367 | cv::line(image, pts[2], pts[3], cv::Scalar(0, 255, 0), 2); 368 | cv::line(image, pts[3], pts[0], cv::Scalar(0, 255, 0), 2); 369 | } 370 | 371 | return image; 372 | } -------------------------------------------------------------------------------- /src/det/detector.h: -------------------------------------------------------------------------------- 1 | // Copyright(c) TuYuAI authors.All rights reserved. 2 | // Licensed under the Apache-2.0 License. 3 | // 4 | 5 | #pragma once 6 | #include "common/common.h" 7 | #include 8 | #include 9 | #include "onnxruntime_c_api.h" 10 | 11 | class TUYUIDCARD_API Detector { 12 | public: 13 | Detector(const OrtApi* ort_api, OrtEnv* env) 14 | : ort_api_(ort_api), 15 | env_(env), 16 | session_options_(nullptr), 17 | session_(nullptr) {} 18 | ~Detector(); 19 | 20 | void GetInputs(); 21 | void GetOutputs(); 22 | void Preprocess(const cv::Mat& image, cv::Mat& out, float& ratio_w, 23 | float& ratio_h); 24 | void InitModel(const std::string& onnx_model_name); 25 | void Predict(const cv::Mat& image, 26 | std::vector>& bboxes); 27 | void GetTensorDataAndShape(OrtValue* input_map, float** array, 28 | std::vector& node_dims); 29 | 30 | cv::Mat ShowTextLines(const cv::Mat& input, 31 | std::vector>& bboxes); 32 | 33 | private: 34 | const OrtApi* ort_api_; 35 | OrtEnv* env_; 36 | OrtSessionOptions* session_options_; 37 | OrtSession* session_; 38 | 39 | std::vector input_node_names_; 40 | std::vector input_node_dims_; 41 | }; 42 | -------------------------------------------------------------------------------- /src/det/lanms.hpp: -------------------------------------------------------------------------------- 1 | #pragma once 2 | 3 | #include 4 | #include "det/clipper/clipper.hpp" 5 | 6 | // locality-aware NMS 7 | namespace lanms { 8 | 9 | namespace cl = ClipperLib; 10 | 11 | struct Polygon { 12 | cl::Path poly; 13 | float score; 14 | }; 15 | 16 | float paths_area(const ClipperLib::Paths &ps) { 17 | float area = 0; 18 | for (auto &&p : ps) area += cl::Area(p); 19 | return area; 20 | } 21 | 22 | float poly_iou(const Polygon &a, const Polygon &b) { 23 | cl::Clipper clpr; 24 | clpr.AddPath(a.poly, cl::ptSubject, true); 25 | clpr.AddPath(b.poly, cl::ptClip, true); 26 | 27 | cl::Paths inter, uni; 28 | clpr.Execute(cl::ctIntersection, inter, cl::pftEvenOdd); 29 | clpr.Execute(cl::ctUnion, uni, cl::pftEvenOdd); 30 | 31 | auto inter_area = paths_area(inter), uni_area = paths_area(uni); 32 | return std::abs(inter_area) / std::max(std::abs(uni_area), 1.0f); 33 | } 34 | 35 | bool should_merge(const Polygon &a, const Polygon &b, float iou_threshold) { 36 | return poly_iou(a, b) > iou_threshold; 37 | } 38 | 39 | /** 40 | * Incrementally merge polygons 41 | */ 42 | class PolyMerger { 43 | public: 44 | PolyMerger() : score(0), nr_polys(0) { memset(data, 0, sizeof(data)); } 45 | 46 | /** 47 | * Add a new polygon to be merged. 48 | */ 49 | void add(const Polygon &p_given) { 50 | Polygon p; 51 | if (nr_polys > 0) { 52 | // vertices of two polygons to merge may not in the same order; 53 | // we match their vertices by choosing the ordering that 54 | // minimizes the total squared distance. 55 | // see function normalize_poly for details. 56 | p = normalize_poly(get(), p_given); 57 | } else { 58 | p = p_given; 59 | } 60 | assert(p.poly.size() == 4); 61 | auto &poly = p.poly; 62 | auto s = p.score; 63 | data[0] += poly[0].X * s; 64 | data[1] += poly[0].Y * s; 65 | 66 | data[2] += poly[1].X * s; 67 | data[3] += poly[1].Y * s; 68 | 69 | data[4] += poly[2].X * s; 70 | data[5] += poly[2].Y * s; 71 | 72 | data[6] += poly[3].X * s; 73 | data[7] += poly[3].Y * s; 74 | 75 | score += p.score; 76 | 77 | nr_polys += 1; 78 | } 79 | 80 | inline std::int64_t sqr(std::int64_t x) { return x * x; } 81 | 82 | Polygon normalize_poly(const Polygon &ref, const Polygon &p) { 83 | std::int64_t min_d = std::numeric_limits::max(); 84 | size_t best_start = 0, best_order = 0; 85 | 86 | for (size_t start = 0; start < 4; start++) { 87 | size_t j = start; 88 | std::int64_t d = (sqr(ref.poly[(j + 0) % 4].X - p.poly[(j + 0) % 4].X) + 89 | sqr(ref.poly[(j + 0) % 4].Y - p.poly[(j + 0) % 4].Y) + 90 | sqr(ref.poly[(j + 1) % 4].X - p.poly[(j + 1) % 4].X) + 91 | sqr(ref.poly[(j + 1) % 4].Y - p.poly[(j + 1) % 4].Y) + 92 | sqr(ref.poly[(j + 2) % 4].X - p.poly[(j + 2) % 4].X) + 93 | sqr(ref.poly[(j + 2) % 4].Y - p.poly[(j + 2) % 4].Y) + 94 | sqr(ref.poly[(j + 3) % 4].X - p.poly[(j + 3) % 4].X) + 95 | sqr(ref.poly[(j + 3) % 4].Y - p.poly[(j + 3) % 4].Y)); 96 | if (d < min_d) { 97 | min_d = d; 98 | best_start = start; 99 | best_order = 0; 100 | } 101 | 102 | d = (sqr(ref.poly[(j + 0) % 4].X - p.poly[(j + 3) % 4].X) + 103 | sqr(ref.poly[(j + 0) % 4].Y - p.poly[(j + 3) % 4].Y) + 104 | sqr(ref.poly[(j + 1) % 4].X - p.poly[(j + 2) % 4].X) + 105 | sqr(ref.poly[(j + 1) % 4].Y - p.poly[(j + 2) % 4].Y) + 106 | sqr(ref.poly[(j + 2) % 4].X - p.poly[(j + 1) % 4].X) + 107 | sqr(ref.poly[(j + 2) % 4].Y - p.poly[(j + 1) % 4].Y) + 108 | sqr(ref.poly[(j + 3) % 4].X - p.poly[(j + 0) % 4].X) + 109 | sqr(ref.poly[(j + 3) % 4].Y - p.poly[(j + 0) % 4].Y)); 110 | if (d < min_d) { 111 | min_d = d; 112 | best_start = start; 113 | best_order = 1; 114 | } 115 | } 116 | 117 | Polygon r; 118 | r.poly.resize(4); 119 | auto j = best_start; 120 | if (best_order == 0) { 121 | for (size_t i = 0; i < 4; i++) r.poly[i] = p.poly[(j + i) % 4]; 122 | } else { 123 | for (size_t i = 0; i < 4; i++) r.poly[i] = p.poly[(j + 4 - i - 1) % 4]; 124 | } 125 | r.score = p.score; 126 | return r; 127 | } 128 | 129 | Polygon get() const { 130 | Polygon p; 131 | 132 | auto &poly = p.poly; 133 | poly.resize(4); 134 | auto score_inv = 1.0f / std::max(1e-8f, score); 135 | poly[0].X = data[0] * score_inv; 136 | poly[0].Y = data[1] * score_inv; 137 | poly[1].X = data[2] * score_inv; 138 | poly[1].Y = data[3] * score_inv; 139 | poly[2].X = data[4] * score_inv; 140 | poly[2].Y = data[5] * score_inv; 141 | poly[3].X = data[6] * score_inv; 142 | poly[3].Y = data[7] * score_inv; 143 | 144 | assert(score > 0); 145 | p.score = score; 146 | 147 | return p; 148 | } 149 | 150 | private: 151 | std::int64_t data[8]; 152 | float score; 153 | std::int32_t nr_polys; 154 | }; 155 | 156 | /** 157 | * The standard NMS algorithm. 158 | */ 159 | std::vector standard_nms(std::vector &polys, 160 | float iou_threshold) { 161 | size_t n = polys.size(); 162 | if (n == 0) return {}; 163 | std::vector indices(n); 164 | std::iota(std::begin(indices), std::end(indices), 0); 165 | std::sort(std::begin(indices), std::end(indices), [&](size_t i, size_t j) { 166 | return polys[i].score > polys[j].score; 167 | }); 168 | 169 | std::vector keep; 170 | while (indices.size()) { 171 | size_t p = 0, cur = indices[0]; 172 | keep.emplace_back(cur); 173 | for (size_t i = 1; i < indices.size(); i++) { 174 | if (!should_merge(polys[cur], polys[indices[i]], iou_threshold)) { 175 | indices[p++] = indices[i]; 176 | } 177 | } 178 | indices.resize(p); 179 | } 180 | 181 | std::vector ret; 182 | for (auto &&i : keep) { 183 | ret.emplace_back(polys[i]); 184 | } 185 | return ret; 186 | } 187 | 188 | std::vector merge_quadrangle_n9(const float *data, size_t n, 189 | float iou_threshold) { 190 | using cInt = cl::cInt; 191 | 192 | // first pass 193 | std::vector polys; 194 | for (size_t i = 0; i < n; i++) { 195 | auto p = data + i * 9; 196 | Polygon poly{ 197 | { 198 | {cInt(p[0]), cInt(p[1])}, 199 | {cInt(p[2]), cInt(p[3])}, 200 | {cInt(p[4]), cInt(p[5])}, 201 | {cInt(p[6]), cInt(p[7])}, 202 | }, 203 | p[8], 204 | }; 205 | 206 | if (polys.size()) { 207 | // merge with the last one 208 | auto &bpoly = polys.back(); 209 | if (should_merge(poly, bpoly, iou_threshold)) { 210 | PolyMerger merger; 211 | merger.add(bpoly); 212 | merger.add(poly); 213 | bpoly = merger.get(); 214 | } else { 215 | polys.emplace_back(poly); 216 | } 217 | } else { 218 | polys.emplace_back(poly); 219 | } 220 | } 221 | return standard_nms(polys, iou_threshold); 222 | } 223 | 224 | std::vector> polys2floats( 225 | const std::vector &polys) { 226 | std::vector> ret; 227 | for (size_t i = 0; i < polys.size(); i++) { 228 | auto &p = polys[i]; 229 | auto &poly = p.poly; 230 | ret.emplace_back(std::vector{ 231 | float(poly[0].X), 232 | float(poly[0].Y), 233 | float(poly[1].X), 234 | float(poly[1].Y), 235 | float(poly[2].X), 236 | float(poly[2].Y), 237 | float(poly[3].X), 238 | float(poly[3].Y), 239 | float(p.score), 240 | }); 241 | } 242 | 243 | return ret; 244 | } 245 | 246 | std::vector> polys2floats_new( 247 | std::vector &polys) { 248 | std::vector> ret; 249 | for (size_t i = 0; i < polys.size(); i++) { 250 | auto &p = polys[i]; 251 | auto &poly = p.poly; 252 | ret.emplace_back(std::vector{ 253 | float(static_cast(poly[0].X) / 10000.0), 254 | float(static_cast(poly[0].Y) / 10000.0), 255 | float(static_cast(poly[1].X) / 10000.0), 256 | float(static_cast(poly[1].Y) / 10000.0), 257 | float(static_cast(poly[2].X) / 10000.0), 258 | float(static_cast(poly[2].Y) / 10000.0), 259 | float(static_cast(poly[3].X) / 10000.0), 260 | float(static_cast(poly[3].Y) / 10000.0), 261 | float(p.score), 262 | }); 263 | } 264 | return ret; 265 | } 266 | 267 | } // namespace lanms 268 | -------------------------------------------------------------------------------- /src/det/test.cpp: -------------------------------------------------------------------------------- 1 | // Copyright(c) TuYuAI authors.All rights reserved. 2 | // Licensed under the Apache-2.0 License. 3 | // 4 | 5 | #include 6 | #include "det/detector.h" 7 | #include "onnxruntime_c_api.h" 8 | 9 | int main(int argc, char* argv[]) { 10 | if (argc < 3) { 11 | std::cout << "idcard_rec_test model_path image_path" << std::endl; 12 | return 0; 13 | } 14 | std::string model_path = argv[1]; 15 | std::string image_path = argv[2]; 16 | const OrtApi* g_ort = OrtGetApiBase()->GetApi(ORT_API_VERSION); 17 | OrtEnv* env; 18 | g_ort->CreateEnv(ORT_LOGGING_LEVEL_WARNING, "idcard", &env); 19 | Detector detector(g_ort, env); 20 | detector.InitModel(model_path); 21 | std::vector > textlines; 22 | cv::Mat image = cv::imread(image_path); 23 | detector.Predict(image, textlines); 24 | cv::Mat show_image = detector.ShowTextLines(image, textlines); 25 | cv::imwrite("show.png", show_image); 26 | return 0; 27 | } -------------------------------------------------------------------------------- /src/idcard/CMakeLists.txt: -------------------------------------------------------------------------------- 1 | include_directories(${CMAKE_CURRENT_DIR}) 2 | include_directories(${CMAKE_CURRENT_DIR}/../) 3 | add_library(idcard_ocr SHARED idcard.cpp) 4 | target_link_libraries(idcard_ocr ${OpenCV_LIBS} onnxruntime) 5 | 6 | add_executable(idcard_ocr_test test.cpp) 7 | target_link_libraries(idcard_ocr_test idcard_ocr ${OpenCV_LIBS} onnxruntime) 8 | -------------------------------------------------------------------------------- /src/idcard/idcard.cpp: -------------------------------------------------------------------------------- 1 | #include "idcard.h" 2 | #include "det/detector.h" 3 | #include "rec/recognizer.h" 4 | 5 | IDCardOCR::~IDCardOCR() { 6 | if (detector_ != nullptr) { 7 | delete detector_; 8 | } 9 | if (recognizer_ != nullptr) { 10 | delete recognizer_; 11 | } 12 | } 13 | 14 | void IDCardOCR::InitModel(const std::string& det_model, 15 | const std::string& rec_model) { 16 | detector_ = new Detector(ort_api_, env_); 17 | recognizer_ = new Recognizer(ort_api_, env_); 18 | detector_->InitModel(det_model); 19 | recognizer_->InitModel(rec_model); 20 | } 21 | 22 | void IDCardOCR::ParseHead( 23 | const cv::Mat& image, 24 | std::vector>& infos) { 25 | std::vector> textlines; 26 | detector_->Predict(image, textlines); 27 | 28 | for (auto textline : textlines) { 29 | cv::Rect line_rect = cv::boundingRect(textline); 30 | cv::Mat text_image = image(line_rect); 31 | std::string res = recognizer_->Predict(text_image); 32 | std::cout << res << std::endl; 33 | } 34 | } 35 | 36 | void IDCardOCR::ParseEmblem( 37 | const cv::Mat& image, 38 | std::vector>& infos) { 39 | std::vector> textlines; 40 | detector_->Predict(image, textlines); 41 | for (auto textline : textlines) { 42 | cv::Rect line_rect = cv::boundingRect(textline); 43 | cv::Mat text_image = image(line_rect); 44 | std::string res = recognizer_->Predict(text_image); 45 | } 46 | } 47 | -------------------------------------------------------------------------------- /src/idcard/idcard.h: -------------------------------------------------------------------------------- 1 | #pragma once 2 | #include "common/common.h" 3 | #include "det/detector.h" 4 | #include "rec/recognizer.h" 5 | 6 | class TUYUIDCARD_API IDCardOCR { 7 | public: 8 | IDCardOCR(const OrtApi* ort_api, OrtEnv* env) 9 | : ort_api_(ort_api), env_(env) {} 10 | virtual ~IDCardOCR(); 11 | 12 | void InitModel(const std::string& det_model, const std::string& rec_model); 13 | void ParseHead(const cv::Mat& image, 14 | std::vector>& infos); 15 | void ParseEmblem(const cv::Mat& image, 16 | std::vector>& infos); 17 | 18 | private: 19 | const OrtApi* ort_api_; 20 | OrtEnv* env_; 21 | Detector* detector_; 22 | Recognizer* recognizer_; 23 | }; -------------------------------------------------------------------------------- /src/idcard/idcard_c_api.h: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/tuyuai/TuYuIDCard/e004645ed4b4c6b0eb578a7af8bce95614a4b8ec/src/idcard/idcard_c_api.h -------------------------------------------------------------------------------- /src/idcard/test.cpp: -------------------------------------------------------------------------------- 1 | #include 2 | #include "idcard.h" 3 | #include "onnxruntime_c_api.h" 4 | 5 | int main(int argc, char* argv[]) { 6 | if (argc < 4) { 7 | std::cout << "idcard_rec_test det_model_path rec_model_path image_path" 8 | << std::endl; 9 | return 0; 10 | } 11 | std::string det_model_path = argv[1]; 12 | std::string rec_model_path = argv[2]; 13 | std::string image_path = argv[3]; 14 | const OrtApi* g_ort = OrtGetApiBase()->GetApi(ORT_API_VERSION); 15 | OrtEnv* env; 16 | g_ort->CreateEnv(ORT_LOGGING_LEVEL_WARNING, "idcard", &env); 17 | IDCardOCR idcard(g_ort, env); 18 | idcard.InitModel(det_model_path, rec_model_path); 19 | cv::Mat image = cv::imread(image_path); 20 | std::vector> infos; 21 | idcard.ParseHead(image, infos); 22 | 23 | return 0; 24 | } -------------------------------------------------------------------------------- /src/rec/CMakeLists.txt: -------------------------------------------------------------------------------- 1 | include_directories(${CMAKE_CURRENT_DIR}) 2 | add_library(idcard_rec SHARED recognizer.cpp decode.cpp) 3 | target_link_libraries(idcard_rec ${OpenCV_LIBS} onnxruntime) 4 | 5 | add_executable(idcard_rec_test test.cpp) 6 | target_link_libraries(idcard_rec_test idcard_rec ${OpenCV_LIBS} onnxruntime) 7 | -------------------------------------------------------------------------------- /src/rec/decode.cpp: -------------------------------------------------------------------------------- 1 | // Copyright(c) TuYuAI authors.All rights reserved. 2 | // Licensed under the Apache-2.0 License. 3 | // 4 | 5 | #if defined(_MSC_VER) && (_MSC_VER >= 1900) 6 | # pragma execution_character_set("utf-8") 7 | #endif 8 | #include "decode.h" 9 | 10 | std::vector GreedyDecode(const std::vector &preds) { 11 | std::vector res; 12 | int preds_size = preds.size(); 13 | for (int i = 0; i < preds_size; i++) { 14 | if (preds[i] != 0 && !(i > 0 && preds[i - 1] == preds[i])) { 15 | res.push_back(preds[i]); 16 | } 17 | } 18 | return res; 19 | } 20 | 21 | std::vector alphabets = { 22 | " ", "!", "\"", "#", "$", "%", "&", "\'", "(", ")", "*", "+", 23 | ",", "-", ".", "/", "0", "1", "2", "3", "4", "5", "6", "7", 24 | "8", "9", ":", ";", "<", "=", ">", "?", "@", "A", "B", "C", 25 | "D", "E", "F", "G", "H", "I", "J", "K", "L", "M", "N", "O", 26 | "P", "Q", "R", "S", "T", "U", "V", "W", "X", "Y", "Z", "[", 27 | "\\", "]", "^", "_", "`", "a", "b", "c", "d", "e", "f", "g", 28 | "h", "i", "j", "k", "l", "m", "n", "o", "p", "q", "r", "s", 29 | "t", "u", "v", "w", "x", "y", "z", "{", "|", "}", "~", "°", 30 | "±", "·", "×", "à", "é", "÷", "ü", "α", "β", "О", "П", "Р", 31 | "–", "—", "―", "‘", "’", "“", "”", "…", "‰", "′", "※", "℃", 32 | "Ⅰ", "Ⅱ", "Ⅲ", "Ⅳ", "→", "↓", "∈", "√", "∩", "∵", "∶", "≠", 33 | "≤", "≥", "①", "②", "③", "④", "⑤", "⑥", "⑦", "⑧", "⑨", "⑩", 34 | "⑴", "⑵", "⑶", "⑾", "⑿", "⒀", "⒂", "⒃", "⒄", "⒅", "⒆", "⒈", 35 | "⒉", "⒊", "─", "━", "│", "┌", "┐", "╱", "■", "□", "▲", "△", 36 | "◆", "◇", "○", "◎", "●", "★", "☆", " ", "、", "。", "〇", "〈", 37 | "〉", "《", "》", "「", "」", "『", "』", "【", "】", "〔", "〕", "て", 38 | "な", "㘰", "㘵", "㙍", "㙟", "䓣", "䶮", "一", "丁", "七", "万", "丈", 39 | "三", "上", "下", "不", "与", "丐", "丑", "专", "且", "丕", "世", "丘", 40 | "丙", "业", "丛", "东", "丝", "丞", "丢", "两", "严", "丧", "个", "丫", 41 | "中", "丰", "串", "临", "丸", "丹", "为", "主", "丽", "举", "乂", "乃", 42 | "久", "么", "义", "之", "乌", "乍", "乎", "乏", "乐", "乒", "乓", "乔", 43 | "乖", "乘", "乙", "乜", "九", "乞", "也", "习", "乡", "书", "乩", "买", 44 | "乱", "乳", "乸", "乾", "了", "予", "争", "事", "二", "于", "亏", "云", 45 | "互", "亓", "五", "井", "亘", "亚", "些", "亟", "亡", "亢", "交", "亥", 46 | "亦", "产", "亨", "亩", "享", "京", "亭", "亮", "亲", "亳", "亵", "亶", 47 | "人", "亿", "什", "仁", "仂", "仃", "仄", "仅", "仆", "仇", "仉", "今", 48 | "介", "仍", "从", "仑", "仓", "仔", "仕", "他", "仗", "付", "仙", "仚", 49 | "仝", "仞", "仟", "仡", "代", "令", "以", "仨", "仪", "仫", "们", "仰", 50 | "仲", "仵", "件", "价", "任", "份", "仿", "企", "伉", "伊", "伍", "伎", 51 | "伏", "伐", "休", "伕", "众", "优", "伙", "会", "伛", "伞", "伟", "传", 52 | "伢", "伤", "伦", "伧", "伪", "伫", "伯", "估", "伲", "伴", "伶", "伸", 53 | "伺", "伻", "似", "伽", "伾", "佀", "佃", "但", "佈", "位", "低", "住", 54 | "佐", "佑", "体", "何", "佗", "佘", "余", "佚", "佛", "作", "佝", "佞", 55 | "佟", "你", "佣", "佤", "佧", "佩", "佬", "佯", "佰", "佳", "佴", "佶", 56 | "佺", "佻", "佼", "佾", "使", "侃", "侄", "侈", "侉", "例", "侍", "侏", 57 | "侑", "侔", "侗", "供", "依", "侠", "侣", "侥", "侦", "侧", "侨", "侩", 58 | "侪", "侬", "侮", "侯", "侴", "侵", "便", "促", "俄", "俅", "俊", "俍", 59 | "俎", "俏", "俐", "俑", "俗", "俘", "俚", "俛", "保", "俞", "俟", "信", 60 | "俣", "俤", "俦", "俨", "俩", "俪", "俬", "俭", "修", "俯", "俱", "俳", 61 | "俵", "俶", "俸", "俺", "俾", "倍", "倏", "倒", "倓", "倔", "倖", "倘", 62 | "候", "倚", "倜", "倞", "借", "倡", "倢", "倦", "倨", "倩", "倪", "倬", 63 | "倭", "倮", "倴", "债", "值", "倾", "偃", "假", "偈", "偌", "偎", "偏", 64 | "偕", "做", "停", "健", "偰", "偲", "偶", "偷", "偻", "偿", "傀", "傅", 65 | "傈", "傍", "傑", "傕", "傢", "傣", "傥", "傧", "储", "傩", "催", "傲", 66 | "傻", "像", "僖", "僚", "僦", "僧", "僭", "僮", "僰", "僳", "僵", "價", 67 | "僻", "儀", "億", "儆", "儇", "儋", "儒", "儡", "儿", "兀", "允", "元", 68 | "兄", "充", "兆", "先", "光", "克", "免", "兑", "兒", "兔", "兕", "兖", 69 | "党", "兜", "兢", "入", "內", "全", "八", "公", "六", "兮", "兰", "共", 70 | "关", "兴", "兵", "其", "具", "典", "兹", "养", "兼", "兽", "冀", "冁", 71 | "内", "冈", "冉", "册", "再", "冏", "冑", "冒", "冕", "冗", "写", "冚", 72 | "军", "农", "冠", "冢", "冤", "冥", "冬", "冮", "冯", "冰", "冲", "决", 73 | "况", "冶", "冷", "冻", "冼", "冽", "净", "凃", "凄", "准", "凇", "凉", 74 | "凋", "凌", "凎", "减", "凑", "凛", "凝", "几", "凡", "凤", "凫", "凭", 75 | "凯", "凰", "凱", "凳", "凶", "凸", "凹", "出", "击", "凼", "函", "凿", 76 | "刀", "刁", "刃", "分", "切", "刈", "刊", "刍", "刎", "刑", "划", "刓", 77 | "刖", "列", "刘", "则", "刚", "创", "初", "删", "判", "刨", "利", "别", 78 | "刭", "刮", "到", "刳", "制", "刷", "券", "刹", "刺", "刻", "刽", "剀", 79 | "剁", "剂", "剃", "剅", "削", "剌", "前", "剎", "剐", "剑", "剔", "剖", 80 | "剜", "剡", "剥", "剧", "剩", "剪", "副", "割", "剽", "剿", "劈", "劓", 81 | "劙", "力", "劝", "办", "功", "加", "务", "劢", "劣", "劦", "动", "助", 82 | "努", "劫", "劬", "劭", "励", "劲", "劳", "劵", "劼", "劾", "势", "勃", 83 | "勇", "勉", "勋", "勍", "勐", "勒", "勖", "勘", "募", "勤", "勰", "勺", 84 | "勾", "勿", "匀", "包", "匆", "匈", "匍", "匏", "匐", "匕", "化", "北", 85 | "匙", "匝", "匠", "匡", "匣", "匪", "匮", "匳", "匹", "区", "医", "匽", 86 | "匾", "匿", "區", "十", "千", "卅", "升", "午", "卉", "半", "华", "协", 87 | "卑", "卒", "卓", "单", "卖", "南", "博", "卜", "卞", "占", "卡", "卢", 88 | "卤", "卦", "卧", "卫", "卬", "卮", "卯", "印", "危", "即", "却", "卵", 89 | "卷", "卸", "卺", "卿", "厂", "厄", "厅", "历", "厉", "压", "厌", "厍", 90 | "厐", "厕", "厘", "厚", "厝", "原", "厢", "厥", "厦", "厨", "厩", "厮", 91 | "厶", "去", "县", "叁", "参", "又", "叉", "及", "友", "双", "反", "发", 92 | "叔", "取", "受", "变", "叙", "叛", "叟", "叠", "叡", "口", "古", "句", 93 | "另", "叨", "叩", "只", "叫", "召", "叭", "叮", "可", "台", "叱", "史", 94 | "右", "叵", "叶", "号", "司", "叹", "叻", "叼", "叽", "吁", "吃", "各", 95 | "吆", "合", "吉", "吊", "同", "名", "后", "吏", "吐", "向", "吒", "吓", 96 | "吕", "吗", "君", "吝", "吞", "吟", "吠", "否", "吧", "吨", "吩", "含", 97 | "听", "吭", "吮", "启", "吱", "吴", "吵", "吸", "吹", "吻", "吼", "吾", 98 | "呀", "呂", "呃", "呆", "呈", "告", "呐", "呓", "呕", "呗", "员", "呙", 99 | "呛", "呜", "呢", "呤", "呦", "周", "呱", "味", "呵", "呶", "呷", "呻", 100 | "呼", "命", "咀", "咂", "咄", "咆", "咋", "和", "咎", "咏", "咐", "咒", 101 | "咔", "咕", "咖", "咘", "咙", "咚", "咛", "咡", "咣", "咤", "咦", "咧", 102 | "咨", "咪", "咫", "咬", "咯", "咱", "咳", "咸", "咻", "咽", "咿", "哀", 103 | "品", "哂", "哄", "哆", "哇", "哈", "哉", "哌", "响", "哎", "哏", "哑", 104 | "哓", "哗", "哙", "哝", "哟", "哥", "哦", "哧", "哨", "哩", "哪", "哭", 105 | "哮", "哲", "哺", "哼", "哽", "哿", "唁", "唆", "唇", "唉", "唏", "唐", 106 | "唑", "唔", "唕", "唛", "唠", "唢", "唤", "唧", "唬", "售", "唯", "唱", 107 | "唳", "唶", "唸", "唾", "唿", "啁", "啃", "啄", "商", "啊", "啐", "啓", 108 | "啕", "啖", "啜", "啟", "啡", "啤", "啥", "啦", "啧", "啪", "啬", "啮", 109 | "啰", "啶", "啸", "啻", "啼", "啾", "喀", "喁", "喂", "喃", "善", "喆", 110 | "喇", "喉", "喊", "喋", "喏", "喑", "喔", "喘", "喙", "喜", "喝", "喟", 111 | "喧", "喱", "喳", "喷", "喹", "喻", "喽", "喾", "嗄", "嗅", "嗌", "嗑", 112 | "嗒", "嗓", "嗔", "嗖", "嗛", "嗜", "嗝", "嗟", "嗡", "嗣", "嗤", "嗥", 113 | "嗦", "嗨", "嗪", "嗫", "嗯", "嗲", "嗳", "嗷", "嗽", "嗾", "嘀", "嘈", 114 | "嘉", "嘌", "嘎", "嘘", "嘛", "嘟", "嘡", "嘤", "嘭", "嘱", "嘲", "嘴", 115 | "嘶", "嘹", "嘻", "嘿", "噉", "噌", "噎", "噔", "噗", "噙", "噜", "噢", 116 | "噤", "器", "噩", "噪", "噫", "噬", "噭", "噱", "噶", "噻", "嚅", "嚎", 117 | "嚏", "嚓", "嚣", "嚬", "嚷", "嚼", "囊", "囔", "囗", "囚", "四", "囝", 118 | "回", "因", "囡", "团", "囤", "囫", "园", "困", "囱", "围", "囵", "囷", 119 | "囹", "固", "国", "图", "囿", "圃", "圄", "圆", "圈", "圉", "國", "圐", 120 | "園", "圓", "團", "圙", "圜", "土", "圣", "在", "圩", "圪", "圭", "圯", 121 | "地", "圳", "圹", "场", "圻", "圾", "址", "坁", "坂", "均", "坉", "坊", 122 | "坌", "坍", "坎", "坏", "坐", "坑", "块", "坚", "坛", "坜", "坝", "坞", 123 | "坟", "坠", "坡", "坣", "坤", "坦", "坨", "坪", "坬", "坭", "坮", "坯", 124 | "坳", "坵", "坷", "坻", "坼", "坽", "垂", "垃", "垄", "垅", "垆", "垇", 125 | "垈", "型", "垌", "垒", "垓", "垕", "垚", "垛", "垝", "垞", "垟", "垠", 126 | "垡", "垢", "垣", "垤", "垦", "垧", "垩", "垫", "垭", "垮", "垯", "垱", 127 | "垲", "垴", "垵", "垸", "垾", "垿", "埂", "埃", "埇", "埋", "埌", "城", 128 | "埒", "埔", "埕", "埗", "埙", "埚", "埜", "埝", "域", "埠", "埡", "埤", 129 | "埩", "埪", "埫", "埭", "埸", "培", "基", "埼", "堀", "堂", "堃", "堆", 130 | "堇", "堉", "堋", "堌", "堍", "堎", "堑", "堕", "堘", "堙", "堞", "堠", 131 | "堡", "堤", "堪", "堯", "堰", "報", "場", "堵", "堼", "堽", "塄", "塅", 132 | "塆", "塌", "塍", "塑", "塔", "塘", "塚", "塝", "塞", "塥", "塨", "填", 133 | "塬", "塭", "塱", "塽", "塾", "墀", "墁", "境", "墅", "墈", "墉", "墓", 134 | "墕", "墘", "墙", "增", "墟", "墠", "墨", "墩", "壁", "壅", "壆", "壑", 135 | "壕", "壘", "壤", "士", "壬", "壮", "声", "壳", "壶", "壹", "壽", "处", 136 | "夆", "备", "复", "夏", "夔", "夕", "外", "夙", "多", "夜", "够", "夤", 137 | "夥", "大", "天", "太", "夫", "夭", "央", "夯", "失", "头", "夷", "夸", 138 | "夹", "夺", "夼", "奀", "奁", "奂", "奄", "奇", "奈", "奉", "奋", "奎", 139 | "奏", "契", "奓", "奔", "奕", "奖", "套", "奘", "奚", "奠", "奡", "奢", 140 | "奥", "奭", "女", "奴", "奶", "奸", "她", "好", "妁", "如", "妃", "妄", 141 | "妆", "妇", "妈", "妊", "妍", "妒", "妓", "妖", "妗", "妙", "妞", "妤", 142 | "妥", "妨", "妩", "妪", "妮", "妲", "妳", "妹", "妻", "妾", "姁", "姆", 143 | "姊", "始", "姐", "姑", "姒", "姓", "委", "姗", "姚", "姜", "姝", "姣", 144 | "姥", "姨", "姬", "姮", "姹", "姻", "姿", "威", "娃", "娄", "娅", "娆", 145 | "娇", "娈", "娉", "娌", "娑", "娒", "娓", "娘", "娜", "娟", "娠", "娡", 146 | "娣", "娥", "娩", "娱", "娲", "娴", "娶", "娼", "婀", "婆", "婉", "婊", 147 | "婍", "婕", "婚", "婢", "婧", "婪", "婳", "婴", "婵", "婶", "婷", "婺", 148 | "婻", "婼", "婿", "媒", "媖", "媗", "媚", "媛", "媞", "媪", "媲", "媳", 149 | "媵", "媸", "媾", "嫁", "嫂", "嫄", "嫉", "嫌", "嫒", "嫔", "嫖", "嫘", 150 | "嫚", "嫜", "嫡", "嫣", "嫦", "嫩", "嫪", "嫱", "嬉", "嬌", "嬖", "嬗", 151 | "嬛", "嬴", "嬿", "孀", "子", "孑", "孔", "孕", "字", "存", "孙", "孚", 152 | "孛", "孜", "孝", "孟", "孢", "季", "孤", "孥", "学", "孩", "孪", "孬", 153 | "孰", "孱", "孳", "孵", "孺", "孽", "宁", "它", "宅", "宇", "守", "安", 154 | "宋", "完", "宏", "宓", "宕", "宗", "官", "宙", "定", "宛", "宜", "宝", 155 | "实", "宠", "审", "客", "宣", "室", "宥", "宦", "宪", "宫", "宬", "宰", 156 | "害", "宴", "宵", "家", "宸", "容", "宽", "宾", "宿", "寀", "寂", "寄", 157 | "寅", "密", "寇", "富", "寐", "寒", "寓", "寖", "寘", "寝", "寞", "察", 158 | "寡", "寤", "寥", "寧", "寨", "寮", "寰", "寸", "对", "寺", "寻", "导", 159 | "寿", "封", "射", "将", "尉", "尊", "對", "小", "少", "尔", "尕", "尖", 160 | "尘", "尚", "尝", "尤", "尧", "尬", "就", "尴", "尸", "尹", "尺", "尻", 161 | "尼", "尽", "尾", "尿", "局", "屁", "层", "居", "屈", "屉", "届", "屋", 162 | "屎", "屏", "屐", "屑", "展", "属", "屠", "屡", "屣", "層", "履", "屦", 163 | "屯", "山", "屲", "屹", "屺", "屾", "屿", "岁", "岂", "岈", "岌", "岐", 164 | "岑", "岔", "岕", "岖", "岗", "岘", "岙", "岚", "岛", "岜", "岞", "岢", 165 | "岣", "岩", "岫", "岬", "岭", "岱", "岳", "岵", "岷", "岸", "岺", "岽", 166 | "岿", "峁", "峃", "峄", "峇", "峋", "峒", "峗", "峘", "峙", "峡", "峣", 167 | "峤", "峥", "峦", "峨", "峪", "峭", "峯", "峰", "峻", "崀", "崂", "崃", 168 | "崆", "崇", "崎", "崑", "崔", "崖", "崗", "崙", "崚", "崛", "崞", "崟", 169 | "崤", "崧", "崩", "崭", "崮", "崯", "崴", "崽", "崾", "嵅", "嵇", "嵊", 170 | "嵋", "嵌", "嵖", "嵘", "嵚", "嵛", "嵝", "嵩", "嵬", "嵯", "嵿", "嶂", 171 | "嶍", "嶒", "嶙", "嶚", "嶝", "嶷", "嶺", "巅", "巉", "巍", "川", "州", 172 | "巡", "巢", "工", "左", "巧", "巨", "巩", "巫", "差", "己", "已", "巳", 173 | "巴", "巷", "巽", "巾", "巿", "币", "市", "布", "帅", "帆", "师", "希", 174 | "帏", "帐", "帑", "帔", "帕", "帖", "帘", "帚", "帛", "帜", "帝", "带", 175 | "帧", "帨", "席", "帮", "帷", "常", "帼", "帽", "幂", "幄", "幅", "幌", 176 | "幔", "幕", "幛", "幞", "幡", "幢", "干", "平", "年", "并", "幸", "幺", 177 | "幻", "幼", "幽", "广", "庄", "庆", "庇", "床", "序", "庐", "庑", "库", 178 | "应", "底", "庖", "店", "庙", "庚", "府", "庞", "废", "庠", "庥", "度", 179 | "座", "庭", "庵", "庶", "康", "庸", "庹", "庾", "廉", "廊", "廒", "廓", 180 | "廖", "廛", "廣", "廨", "廪", "延", "廷", "建", "廿", "开", "弁", "异", 181 | "弃", "弄", "弇", "弈", "弊", "弋", "式", "弑", "弓", "引", "弗", "弘", 182 | "弛", "弟", "张", "弢", "弥", "弦", "弧", "弨", "弩", "弭", "弯", "弱", 183 | "張", "弶", "弹", "强", "弼", "彀", "彊", "彐", "归", "当", "录", "彗", 184 | "彘", "彝", "形", "彤", "彦", "彧", "彩", "彪", "彬", "彭", "彰", "影", 185 | "彷", "役", "彻", "彼", "彿", "往", "征", "徂", "径", "待", "徇", "很", 186 | "徉", "徊", "律", "後", "徐", "徒", "徕", "得", "徘", "徙", "徜", "御", 187 | "徨", "循", "徭", "微", "徳", "徵", "德", "徼", "徽", "心", "必", "忆", 188 | "忌", "忍", "忏", "忐", "忑", "忒", "忖", "志", "忘", "忙", "忝", "忞", 189 | "忠", "忡", "忤", "忧", "忪", "快", "忱", "念", "忻", "忽", "忿", "怀", 190 | "态", "怂", "怅", "怆", "怍", "怎", "怏", "怒", "怔", "怕", "怖", "怙", 191 | "怛", "怜", "思", "怠", "怡", "急", "怦", "性", "怨", "怪", "怫", "怯", 192 | "怵", "总", "怼", "怿", "恂", "恃", "恋", "恍", "恐", "恒", "恕", "恙", 193 | "恚", "恝", "恢", "恣", "恤", "恨", "恩", "恪", "恫", "恬", "恭", "息", 194 | "恰", "恳", "恶", "恸", "恹", "恺", "恻", "恼", "恽", "恿", "悃", "悄", 195 | "悅", "悉", "悌", "悍", "悒", "悔", "悖", "悚", "悝", "悞", "悟", "悠", 196 | "患", "悦", "您", "悫", "悬", "悭", "悯", "悲", "悴", "悸", "悻", "悼", 197 | "情", "惆", "惇", "惊", "惋", "惑", "惕", "惘", "惚", "惜", "惟", "惠", 198 | "惦", "惧", "惨", "惩", "惫", "惬", "惭", "惮", "惯", "惰", "想", "惴", 199 | "惶", "惹", "惺", "愀", "愁", "愆", "愈", "愉", "愍", "愎", "意", "愔", 200 | "愕", "愚", "愛", "感", "愠", "愣", "愤", "愦", "愧", "愫", "愬", "愷", 201 | "愿", "慈", "慊", "慌", "慎", "慑", "慕", "慙", "慜", "慢", "慧", "慨", 202 | "慰", "慵", "慷", "憋", "憎", "憔", "憧", "憨", "憩", "憬", "憶", "憾", 203 | "懂", "懈", "懊", "懋", "懑", "懒", "懜", "懦", "懵", "懿", "戆", "戈", 204 | "戊", "戌", "戍", "戎", "戏", "成", "我", "戒", "戕", "或", "戗", "战", 205 | "戚", "戛", "戟", "戡", "戢", "戥", "截", "戬", "戮", "戳", "戴", "户", 206 | "戾", "房", "所", "扁", "扃", "扆", "扇", "扈", "扉", "手", "扌", "才", 207 | "扎", "扑", "扒", "打", "扔", "托", "扛", "扞", "扢", "扣", "执", "扩", 208 | "扪", "扫", "扬", "扭", "扮", "扯", "扰", "扳", "扶", "批", "扼", "找", 209 | "承", "技", "抄", "抉", "把", "抑", "抒", "抓", "投", "抖", "抗", "折", 210 | "抚", "抛", "抟", "抠", "抡", "抢", "护", "报", "抨", "披", "抬", "抱", 211 | "抵", "抹", "抻", "押", "抽", "抿", "拂", "拄", "担", "拆", "拇", "拈", 212 | "拉", "拊", "拌", "拍", "拎", "拐", "拑", "拒", "拓", "拔", "拖", "拗", 213 | "拘", "拙", "拚", "招", "拜", "拟", "拢", "拣", "拥", "拦", "拧", "拨", 214 | "择", "括", "拭", "拮", "拯", "拱", "拳", "拴", "拷", "拼", "拽", "拾", 215 | "拿", "持", "挂", "指", "挈", "按", "挎", "挑", "挖", "挚", "挛", "挝", 216 | "挞", "挟", "挠", "挡", "挣", "挤", "挥", "挨", "挪", "挫", "振", "挹", 217 | "挺", "挽", "捂", "捅", "捆", "捉", "捋", "捌", "捍", "捎", "捏", "捐", 218 | "捕", "捞", "损", "捡", "换", "捣", "捧", "捭", "据", "捱", "捶", "捷", 219 | "捺", "捻", "捽", "掀", "掂", "掇", "授", "掉", "掌", "掏", "掐", "排", 220 | "掖", "掘", "掛", "掠", "探", "掣", "接", "控", "推", "掩", "措", "掬", 221 | "掰", "掳", "掷", "掺", "掼", "掾", "揄", "揆", "揉", "揍", "描", "提", 222 | "插", "揖", "揜", "握", "揣", "揩", "揪", "揭", "援", "揶", "揽", "搀", 223 | "搁", "搂", "搅", "搌", "搏", "搐", "搒", "搓", "搔", "搜", "搞", "搠", 224 | "搢", "搪", "搬", "搭", "搴", "携", "搽", "摁", "摄", "摆", "摇", "摈", 225 | "摊", "摒", "摔", "摘", "摞", "摧", "摩", "摸", "摹", "撂", "撇", "撑", 226 | "撒", "撕", "撖", "撝", "撞", "撤", "撩", "撬", "播", "撮", "撰", "撵", 227 | "撷", "撺", "撼", "擀", "擂", "擅", "操", "擎", "擒", "擘", "擞", "擢", 228 | "擦", "攀", "攒", "攘", "攥", "攫", "支", "收", "攸", "改", "攻", "放", 229 | "政", "故", "效", "敉", "敌", "敏", "救", "敕", "敖", "教", "敛", "敝", 230 | "敞", "敢", "散", "敦", "敬", "数", "敲", "整", "敷", "文", "斋", "斌", 231 | "斐", "斑", "斓", "斗", "料", "斛", "斜", "斟", "斡", "斤", "斥", "斧", 232 | "斩", "斫", "断", "斯", "新", "斲", "方", "於", "施", "旁", "旃", "旄", 233 | "旅", "旆", "旋", "旌", "旎", "族", "旒", "旖", "旗", "无", "既", "日", 234 | "旦", "旧", "旨", "早", "旬", "旭", "旮", "旯", "旱", "旴", "旵", "时", 235 | "旷", "旸", "旺", "旻", "旼", "昀", "昂", "昃", "昆", "昇", "昉", "昊", 236 | "昌", "明", "昏", "易", "昔", "昕", "昙", "昝", "昞", "星", "映", "春", 237 | "昧", "昨", "昫", "昭", "是", "昰", "昱", "昳", "昴", "昵", "昶", "昺", 238 | "昼", "显", "晁", "晃", "晅", "晉", "晋", "晌", "晏", "晒", "晓", "晔", 239 | "晕", "晖", "晗", "晚", "晛", "晞", "晟", "晡", "晢", "晤", "晦", "晧", 240 | "晨", "普", "景", "晰", "晴", "晶", "晸", "晹", "智", "晾", "暂", "暄", 241 | "暇", "暐", "暑", "暕", "暖", "暗", "暘", "暠", "暧", "暨", "暮", "暲", 242 | "暴", "暹", "暾", "曈", "曌", "曙", "曚", "曛", "曜", "曝", "曦", "曩", 243 | "曰", "曲", "曳", "更", "曷", "書", "曹", "曼", "曾", "替", "最", "朂", 244 | "會", "月", "有", "朋", "服", "朐", "朔", "朕", "朗", "望", "朝", "期", 245 | "朦", "木", "未", "末", "本", "札", "术", "朱", "朴", "朵", "机", "朽", 246 | "杀", "杂", "权", "杆", "杈", "杉", "杌", "李", "杏", "材", "村", "杓", 247 | "杖", "杜", "杞", "束", "杠", "条", "来", "杨", "杪", "杭", "杯", "杰", 248 | "東", "杲", "杳", "杵", "杷", "杼", "松", "板", "极", "构", "枇", "枉", 249 | "枋", "析", "枕", "林", "枘", "枚", "果", "枝", "枞", "枢", "枣", "枥", 250 | "枧", "枨", "枪", "枫", "枭", "枯", "枰", "枳", "枵", "架", "枷", "枸", 251 | "枹", "柃", "柄", "柈", "柏", "某", "柑", "柒", "染", "柔", "柘", "柚", 252 | "柜", "柞", "柠", "柢", "查", "柩", "柬", "柯", "柰", "柱", "柳", "柴", 253 | "柽", "柿", "栀", "栅", "标", "栈", "栉", "栊", "栋", "栎", "栏", "树", 254 | "栒", "栓", "栖", "栗", "栟", "校", "栢", "栩", "株", "栲", "栳", "样", 255 | "核", "根", "格", "栽", "栾", "栿", "桀", "桁", "桂", "桃", "桄", "桅", 256 | "框", "案", "桉", "桌", "桎", "桐", "桑", "桓", "桔", "桠", "桡", "桢", 257 | "档", "桤", "桥", "桦", "桧", "桨", "桩", "桶", "桷", "梁", "梃", "梅", 258 | "梆", "梏", "梓", "梗", "梢", "梦", "梧", "梨", "梭", "梯", "械", "梳", 259 | "梵", "梽", "检", "棂", "棉", "棋", "棍", "棐", "棒", "棓", "棕", "棘", 260 | "棚", "棟", "棠", "棡", "棣", "棪", "森", "棰", "棱", "棵", "棹", "棺", 261 | "棽", "椀", "椁", "椅", "椋", "植", "椎", "椐", "椑", "椒", "椟", "椭", 262 | "椰", "椴", "椹", "椽", "椿", "楂", "楊", "楔", "楗", "楚", "楝", "楞", 263 | "楠", "楣", "楦", "楫", "楮", "楷", "楸", "楹", "楼", "榀", "概", "榃", 264 | "榄", "榅", "榆", "榇", "榈", "榉", "榍", "榔", "榕", "榛", "榜", "榧", 265 | "榨", "榫", "榭", "榮", "榱", "榴", "榷", "榻", "榼", "槁", "槃", "槊", 266 | "槌", "槎", "槐", "槑", "槓", "槚", "槛", "槟", "槺", "槽", "槿", "樊", 267 | "樑", "樓", "樗", "樘", "標", "樟", "模", "樨", "横", "樯", "樱", "樵", 268 | "樽", "樾", "橄", "橇", "橋", "橐", "橘", "橙", "橡", "橦", "橫", "橱", 269 | "橹", "檀", "檄", "檐", "檑", "檔", "檗", "檟", "檠", "檢", "檩", "檬", 270 | "櫃", "欠", "次", "欢", "欣", "欤", "欧", "欲", "欷", "欺", "欻", "款", 271 | "歆", "歇", "歉", "歌", "歔", "歘", "歙", "止", "正", "此", "步", "武", 272 | "歧", "歪", "歹", "死", "歼", "殁", "殂", "殃", "殄", "殆", "殇", "殉", 273 | "殊", "残", "殒", "殓", "殖", "殚", "殛", "殡", "殪", "殳", "殴", "段", 274 | "殷", "殽", "殿", "毁", "毂", "毅", "毋", "母", "每", "毒", "毓", "比", 275 | "毕", "毗", "毙", "毛", "毡", "毫", "毯", "毳", "氅", "氆", "氇", "氏", 276 | "氐", "民", "氓", "气", "氖", "氙", "氛", "氟", "氡", "氢", "氤", "氦", 277 | "氧", "氨", "氩", "氮", "氯", "氰", "氲", "水", "永", "氹", "氽", "氾", 278 | "氿", "汀", "汁", "求", "汇", "汈", "汉", "汊", "汎", "汐", "汔", "汕", 279 | "汗", "汛", "汜", "汝", "汞", "江", "池", "污", "汤", "汨", "汩", "汪", 280 | "汫", "汭", "汰", "汲", "汴", "汶", "汹", "汽", "汾", "沁", "沂", "沃", 281 | "沄", "沅", "沆", "沈", "沉", "沌", "沐", "沓", "沔", "沙", "沚", "沛", 282 | "沟", "没", "沣", "沥", "沦", "沧", "沨", "沩", "沪", "沫", "沬", "沭", 283 | "沮", "沱", "河", "沸", "油", "沺", "治", "沼", "沽", "沾", "沿", "泂", 284 | "泃", "泄", "泅", "泉", "泊", "泌", "泐", "泒", "泓", "泔", "法", "泖", 285 | "泗", "泛", "泞", "泠", "泡", "波", "泣", "泥", "注", "泪", "泫", "泮", 286 | "泯", "泰", "泱", "泳", "泵", "泷", "泸", "泺", "泻", "泼", "泽", "泾", 287 | "洁", "洄", "洇", "洈", "洋", "洌", "洎", "洑", "洒", "洗", "洙", "洛", 288 | "洞", "洟", "洢", "洣", "津", "洧", "洩", "洪", "洮", "洱", "洲", "洳", 289 | "洴", "洵", "洸", "洹", "洺", "活", "洼", "洽", "派", "流", "浃", "浅", 290 | "浆", "浇", "浈", "浉", "浊", "测", "浍", "济", "浏", "浐", "浑", "浒", 291 | "浓", "浔", "浙", "浚", "浛", "浜", "浞", "浠", "浡", "浣", "浥", "浦", 292 | "浩", "浪", "浬", "浮", "浯", "浰", "浴", "海", "浸", "浼", "涂", "涅", 293 | "消", "涉", "涌", "涎", "涓", "涔", "涕", "涛", "涝", "涞", "涟", "涠", 294 | "涡", "涣", "涤", "润", "涧", "涨", "涩", "涪", "涮", "涯", "液", "涴", 295 | "涵", "涸", "涿", "淀", "淄", "淅", "淆", "淇", "淋", "淌", "淏", "淑", 296 | "淖", "淘", "淙", "淝", "淞", "淠", "淡", "淤", "淦", "淩", "淫", "淬", 297 | "淮", "淯", "淰", "深", "淳", "混", "淹", "添", "淼", "清", "渊", "渌", 298 | "渍", "渎", "渐", "渑", "渔", "渖", "渗", "渚", "渝", "渟", "渠", "渡", 299 | "渣", "渤", "渥", "温", "測", "渭", "港", "渲", "渴", "游", "渺", "湃", 300 | "湄", "湉", "湍", "湎", "湑", "湓", "湔", "湖", "湘", "湛", "湜", "湟", 301 | "湦", "湧", "湫", "湮", "湲", "湴", "湾", "湿", "溁", "溃", "溅", "溆", 302 | "溇", "溉", "溍", "溏", "源", "溜", "溟", "溢", "溥", "溦", "溧", "溪", 303 | "溫", "溯", "溱", "溲", "溴", "溶", "溷", "溺", "滁", "滂", "滆", "滇", 304 | "滈", "滉", "滋", "滏", "滑", "滓", "滔", "滕", "滘", "滚", "滞", "滟", 305 | "滠", "满", "滢", "滤", "滥", "滦", "滧", "滨", "滩", "滴", "滹", "漂", 306 | "漆", "漈", "漉", "漍", "漏", "漓", "演", "漕", "漖", "漠", "漩", "漪", 307 | "漫", "漭", "漯", "漱", "漳", "漴", "漷", "漹", "漾", "潆", "潇", "潋", 308 | "潍", "潘", "潜", "潞", "潢", "潤", "潦", "潭", "潮", "潴", "潸", "潺", 309 | "潼", "澄", "澈", "澉", "澋", "澌", "澍", "澎", "澔", "澛", "澜", "澡", 310 | "澥", "澧", "澳", "澹", "激", "濂", "濉", "濑", "濒", "濛", "濞", "濠", 311 | "濡", "濬", "濮", "濯", "濰", "瀍", "瀑", "瀚", "瀛", "瀹", "瀼", "灈", 312 | "灌", "灏", "灜", "灞", "火", "灭", "灯", "灰", "灵", "灶", "灸", "灼", 313 | "灾", "灿", "炀", "炅", "炆", "炉", "炊", "炎", "炒", "炔", "炕", "炖", 314 | "炘", "炙", "炜", "炟", "炤", "炫", "炬", "炭", "炮", "炯", "炳", "炷", 315 | "炸", "点", "炼", "炽", "烁", "烂", "烃", "烈", "烊", "烔", "烘", "烙", 316 | "烛", "烜", "烝", "烟", "烤", "烦", "烧", "烨", "烩", "烫", "烬", "热", 317 | "烯", "烷", "烹", "烺", "烽", "焉", "焊", "焌", "焓", "焕", "焖", "焘", 318 | "焙", "焚", "焜", "焦", "焮", "焯", "焰", "焱", "然", "煅", "煇", "煊", 319 | "煌", "煎", "煕", "煖", "煚", "煜", "煞", "煤", "煦", "照", "煨", "煬", 320 | "煮", "煲", "煽", "熀", "熄", "熇", "熊", "熏", "熔", "熖", "熙", "熟", 321 | "熠", "熨", "熬", "熳", "熹", "燃", "燊", "燎", "燏", "燔", "燕", "燚", 322 | "燠", "燥", "燧", "燮", "燹", "燿", "爆", "爇", "爔", "爨", "爪", "爬", 323 | "爰", "爱", "爲", "爵", "父", "爷", "爸", "爹", "爻", "爽", "爿", "片", 324 | "版", "牌", "牍", "牒", "牖", "牙", "牛", "牝", "牟", "牡", "牢", "牤", 325 | "牦", "牧", "物", "牮", "牯", "牲", "牵", "特", "牺", "犀", "犁", "犄", 326 | "犇", "犊", "犋", "犍", "犒", "犟", "犬", "犯", "犴", "状", "犷", "犹", 327 | "狁", "狂", "狃", "狄", "狈", "狎", "狐", "狒", "狗", "狙", "狝", "狞", 328 | "狠", "狡", "狩", "独", "狭", "狮", "狰", "狱", "狲", "狸", "狻", "狼", 329 | "猃", "猇", "猊", "猎", "猕", "猖", "猗", "猛", "猜", "猝", "猢", "猥", 330 | "猩", "猪", "猫", "猬", "献", "猱", "猴", "猷", "猾", "猿", "獐", "獗", 331 | "獠", "獬", "獭", "獾", "玄", "率", "玉", "王", "玎", "玏", "玑", "玓", 332 | "玕", "玖", "玘", "玙", "玚", "玛", "玟", "玠", "玢", "玥", "玦", "玩", 333 | "玫", "玭", "玮", "环", "现", "玲", "玳", "玷", "玺", "玻", "珀", "珂", 334 | "珅", "珈", "珉", "珊", "珍", "珏", "珐", "珑", "珖", "珙", "珞", "珠", 335 | "珣", "珥", "珧", "珩", "珪", "班", "珮", "珰", "珲", "珵", "珺", "珽", 336 | "球", "琅", "理", "琇", "琉", "琊", "琍", "琎", "琏", "琐", "琓", "琖", 337 | "琚", "琛", "琢", "琤", "琥", "琦", "琨", "琪", "琬", "琭", "琮", "琯", 338 | "琰", "琳", "琴", "琵", "琶", "琼", "瑀", "瑁", "瑄", "瑆", "瑊", "瑒", 339 | "瑕", "瑗", "瑙", "瑚", "瑛", "瑜", "瑞", "瑟", "瑢", "瑧", "瑨", "瑩", 340 | "瑭", "瑰", "瑱", "瑶", "瑷", "瑸", "瑾", "璀", "璁", "璃", "璆", "璇", 341 | "璋", "璎", "璐", "璘", "璜", "璞", "璟", "璠", "璧", "璨", "璩", "環", 342 | "璿", "瓅", "瓒", "瓛", "瓜", "瓠", "瓢", "瓣", "瓦", "瓮", "瓯", "瓴", 343 | "瓶", "瓷", "瓻", "甄", "甌", "甍", "甑", "甘", "甙", "甚", "甜", "生", 344 | "甡", "産", "甥", "甦", "用", "甩", "甪", "甫", "甬", "甭", "甯", "田", 345 | "由", "甲", "申", "电", "男", "甸", "町", "画", "甽", "甾", "畀", "畅", 346 | "畇", "畈", "畋", "界", "畎", "畏", "畑", "畓", "畔", "留", "畚", "畛", 347 | "畜", "畤", "略", "畦", "番", "畯", "畲", "畴", "畸", "畹", "畿", "疃", 348 | "疆", "疋", "疏", "疑", "疗", "疙", "疚", "疝", "疟", "疡", "疣", "疤", 349 | "疥", "疫", "疮", "疯", "疱", "疲", "疴", "疵", "疸", "疹", "疼", "疽", 350 | "疾", "痂", "病", "症", "痈", "痉", "痊", "痍", "痒", "痔", "痕", "痘", 351 | "痛", "痞", "痢", "痣", "痤", "痧", "痪", "痫", "痰", "痴", "痹", "痼", 352 | "痿", "瘀", "瘁", "瘐", "瘗", "瘘", "瘙", "瘟", "瘠", "瘢", "瘤", "瘥", 353 | "瘦", "瘩", "瘪", "瘫", "瘳", "瘴", "瘵", "瘸", "瘾", "癀", "療", "癌", 354 | "癔", "癖", "癜", "癞", "癣", "癫", "癸", "登", "發", "白", "百", "癿", 355 | "皁", "皂", "的", "皆", "皇", "皈", "皊", "皋", "皎", "皑", "皓", "皖", 356 | "皙", "皛", "皝", "皞", "皤", "皮", "皱", "皴", "皿", "盂", "盅", "盆", 357 | "盈", "益", "盍", "盎", "盏", "盐", "监", "盒", "盔", "盖", "盗", "盘", 358 | "盛", "盟", "盥", "目", "盯", "盱", "盲", "直", "相", "盹", "盼", "盾", 359 | "省", "眇", "眈", "眉", "看", "眙", "眛", "真", "眠", "眢", "眦", "眨", 360 | "眩", "眭", "眯", "眶", "眷", "眸", "眺", "眼", "着", "睁", "睆", "睇", 361 | "睐", "睑", "睒", "睚", "睛", "睡", "睢", "督", "睦", "睨", "睪", "睫", 362 | "睬", "睹", "睽", "睾", "睿", "瞀", "瞄", "瞅", "瞋", "瞌", "瞎", "瞑", 363 | "瞒", "瞟", "瞠", "瞥", "瞧", "瞩", "瞪", "瞬", "瞭", "瞰", "瞳", "瞻", 364 | "瞽", "瞾", "瞿", "矗", "矛", "矜", "矢", "矣", "知", "矦", "矩", "矫", 365 | "短", "矮", "石", "矶", "矽", "矾", "矿", "砀", "码", "砂", "砌", "砍", 366 | "砒", "研", "砖", "砚", "砝", "砟", "砣", "砥", "砦", "砧", "砩", "砬", 367 | "砭", "砰", "砲", "破", "砵", "砷", "砸", "砺", "砻", "砼", "砾", "础", 368 | "硃", "硅", "硇", "硋", "硍", "硎", "硐", "硒", "硕", "硖", "硗", "硙", 369 | "硚", "硝", "硫", "硬", "确", "硷", "硼", "碁", "碇", "碉", "碌", "碍", 370 | "碎", "碑", "碓", "碗", "碘", "碚", "碛", "碟", "碡", "碣", "碧", "碰", 371 | "碱", "碲", "碳", "碴", "碶", "碾", "磁", "磅", "磉", "磊", "磋", "磐", 372 | "磑", "磔", "磕", "磘", "磙", "磛", "磜", "磡", "磨", "磬", "磴", "磷", 373 | "磹", "磺", "磻", "磾", "礁", "礐", "礤", "礴", "示", "礻", "礼", "礽", 374 | "社", "祀", "祁", "祃", "祇", "祈", "祉", "祎", "祐", "祓", "祖", "祗", 375 | "祚", "祛", "祜", "祝", "神", "祟", "祠", "祥", "祧", "票", "祭", "祯", 376 | "祷", "祸", "祺", "禀", "禁", "禄", "禅", "禇", "福", "禕", "禚", "禛", 377 | "禤", "禧", "禳", "禹", "禺", "离", "禽", "禾", "秀", "私", "秃", "秉", 378 | "秋", "种", "秏", "科", "秒", "秘", "租", "秣", "秤", "秦", "秧", "秩", 379 | "秭", "积", "称", "秸", "移", "秽", "秾", "稀", "稂", "程", "稍", "税", 380 | "稔", "稗", "稚", "稞", "稠", "稣", "稳", "稷", "稹", "稻", "稼", "稽", 381 | "稿", "穂", "穆", "穑", "穗", "穰", "穴", "究", "穷", "穹", "空", "穿", 382 | "突", "窃", "窄", "窈", "窊", "窍", "窑", "窒", "窕", "窖", "窗", "窘", 383 | "窜", "窝", "窟", "窠", "窣", "窥", "窦", "窨", "窬", "窭", "窿", "立", 384 | "竑", "竖", "站", "竜", "竞", "竟", "章", "竣", "童", "竦", "竫", "竭", 385 | "端", "競", "竹", "竺", "竻", "竽", "竿", "笃", "笄", "笆", "笈", "笋", 386 | "笏", "笑", "笔", "笕", "笙", "笛", "笞", "笠", "笤", "笥", "符", "笨", 387 | "笪", "笫", "第", "笮", "笳", "笺", "笼", "筀", "等", "筋", "筌", "筏", 388 | "筐", "筑", "筒", "答", "策", "筛", "筜", "筝", "筠", "筮", "筱", "筲", 389 | "筵", "筷", "筹", "筼", "签", "简", "箍", "箐", "箔", "箕", "算", "管", 390 | "箦", "箧", "箩", "箫", "箬", "箭", "箱", "箴", "箸", "篁", "篆", "篇", 391 | "篑", "篓", "篙", "篝", "篡", "篢", "篦", "篪", "篮", "篱", "篷", "篾", 392 | "簇", "簋", "簌", "簏", "簕", "簖", "簧", "簪", "簸", "簿", "籁", "籍", 393 | "米", "类", "籼", "籽", "粄", "粉", "粑", "粒", "粕", "粗", "粘", "粜", 394 | "粝", "粟", "粤", "粥", "粦", "粪", "粮", "粱", "粲", "粳", "粹", "粼", 395 | "粽", "精", "粿", "糁", "糅", "糊", "糌", "糍", "糕", "糖", "糗", "糙", 396 | "糜", "糟", "糠", "糯", "系", "紊", "素", "索", "紧", "紫", "紬", "累", 397 | "細", "組", "絜", "絮", "統", "絷", "綦", "線", "縂", "縠", "縢", "縯", 398 | "縻", "總", "繁", "繇", "纂", "纔", "纠", "纡", "红", "纣", "纤", "纥", 399 | "约", "级", "纨", "纪", "纫", "纬", "纭", "纮", "纯", "纰", "纱", "纲", 400 | "纳", "纵", "纶", "纷", "纸", "纹", "纺", "纻", "纽", "纾", "线", "绀", 401 | "绁", "练", "组", "绅", "细", "织", "终", "绊", "绌", "绍", "绎", "经", 402 | "绐", "绑", "绒", "结", "绔", "绕", "绖", "绘", "给", "绚", "绛", "络", 403 | "绝", "绞", "统", "绠", "绡", "绢", "绣", "绥", "绦", "继", "绨", "绩", 404 | "绪", "绫", "续", "绮", "绯", "绰", "绳", "维", "绵", "绶", "绷", "绸", 405 | "绺", "绻", "综", "绽", "绾", "绿", "缀", "缁", "缄", "缅", "缆", "缇", 406 | "缈", "缉", "缎", "缐", "缑", "缒", "缓", "缔", "缕", "编", "缗", "缘", 407 | "缙", "缚", "缜", "缝", "缞", "缟", "缠", "缢", "缤", "缥", "缦", "缧", 408 | "缨", "缩", "缪", "缫", "缭", "缮", "缯", "缰", "缱", "缳", "缴", "缵", 409 | "缶", "缸", "缺", "罂", "罄", "罅", "罐", "网", "罔", "罕", "罗", "罘", 410 | "罚", "罡", "罢", "罥", "罩", "罪", "置", "署", "罳", "罴", "罹", "罾", 411 | "羁", "羊", "羌", "美", "羑", "羔", "羖", "羚", "羞", "羟", "羡", "群", 412 | "義", "羯", "羲", "羸", "羹", "羽", "羿", "翀", "翁", "翃", "翅", "翊", 413 | "翌", "翎", "翔", "翕", "翘", "翚", "翛", "翟", "翠", "翡", "翥", "翦", 414 | "翩", "翮", "翯", "翰", "翱", "翳", "翻", "翼", "翾", "耀", "老", "考", 415 | "耄", "者", "耆", "耇", "耋", "而", "耍", "耎", "耐", "耑", "耒", "耕", 416 | "耗", "耘", "耙", "耜", "耦", "耨", "耩", "耳", "耶", "耸", "耻", "耽", 417 | "耿", "聂", "聃", "聆", "聊", "聋", "职", "聒", "联", "聖", "聘", "聚", 418 | "聩", "聪", "聯", "聲", "聿", "肃", "肄", "肆", "肇", "肉", "肋", "肌", 419 | "肓", "肖", "肘", "肚", "肛", "肜", "肝", "肠", "股", "肢", "肤", "肥", 420 | "肩", "肪", "肮", "肯", "肱", "育", "肴", "肸", "肺", "肽", "肾", "肿", 421 | "胀", "胁", "胃", "胄", "胆", "背", "胎", "胖", "胙", "胚", "胜", "胝", 422 | "胞", "胡", "胤", "胥", "胧", "胪", "胫", "胭", "胯", "胰", "胱", "胳", 423 | "胶", "胸", "胺", "能", "胾", "脁", "脂", "脆", "脉", "脊", "脍", "脏", 424 | "脐", "脑", "脓", "脔", "脖", "脚", "脯", "脱", "脲", "脸", "脾", "腆", 425 | "腈", "腉", "腊", "腋", "腌", "腐", "腑", "腓", "腔", "腕", "腥", "腩", 426 | "腭", "腮", "腰", "腱", "腴", "腹", "腺", "腻", "腼", "腾", "腿", "膀", 427 | "膈", "膊", "膏", "膑", "膘", "膛", "膜", "膝", "膦", "膨", "膳", "膺", 428 | "膻", "臀", "臂", "臃", "臆", "臊", "臛", "臣", "臧", "自", "臬", "臭", 429 | "至", "致", "臻", "臼", "臾", "舀", "舁", "舂", "舄", "舅", "舆", "舌", 430 | "舍", "舐", "舒", "舔", "舛", "舜", "舞", "舟", "舡", "舢", "舨", "航", 431 | "舫", "般", "舰", "舱", "舲", "舵", "舶", "舷", "舸", "船", "艇", "艘", 432 | "艚", "艞", "艮", "良", "艰", "色", "艳", "艺", "艻", "艽", "艾", "节", 433 | "芃", "芈", "芊", "芋", "芍", "芎", "芒", "芗", "芘", "芙", "芜", "芝", 434 | "芡", "芥", "芦", "芩", "芪", "芫", "芬", "芭", "芮", "芯", "芰", "花", 435 | "芳", "芶", "芷", "芸", "芹", "芽", "芾", "苁", "苄", "苇", "苈", "苋", 436 | "苌", "苍", "苎", "苏", "苑", "苒", "苓", "苔", "苕", "苗", "苛", "苜", 437 | "苞", "苟", "苠", "苡", "苣", "若", "苦", "苧", "苫", "苯", "英", "苳", 438 | "苴", "苷", "苹", "苻", "苾", "苿", "茀", "茁", "茂", "范", "茄", "茅", 439 | "茆", "茈", "茉", "茌", "茎", "茏", "茔", "茕", "茗", "茘", "茜", "茤", 440 | "茧", "茨", "茫", "茬", "茭", "茯", "茱", "茳", "茴", "茵", "茶", "茸", 441 | "茹", "茼", "荀", "荃", "荄", "荆", "荇", "草", "荏", "荐", "荒", "荔", 442 | "荘", "荚", "荜", "荞", "荟", "荠", "荡", "荣", "荤", "荥", "荦", "荧", 443 | "荩", "荪", "荫", "荭", "药", "荷", "荸", "荻", "荼", "荿", "莅", "莆", 444 | "莉", "莊", "莎", "莒", "莓", "莘", "莙", "莛", "莜", "莞", "莠", "莨", 445 | "莩", "莪", "莫", "莱", "莲", "莳", "莴", "获", "莹", "莺", "莼", "莽", 446 | "菀", "菁", "菂", "菅", "菇", "菉", "菊", "菌", "菏", "菑", "菓", "菖", 447 | "菘", "菜", "菟", "菠", "菡", "菥", "菩", "華", "菰", "菱", "菲", "菴", 448 | "菻", "菽", "萁", "萃", "萄", "萊", "萌", "萍", "萎", "萏", "萝", "萤", 449 | "营", "萦", "萧", "萨", "萩", "萬", "萱", "萸", "萼", "落", "葆", "葑", 450 | "著", "葛", "葡", "董", "葩", "葫", "葬", "葭", "葱", "葳", "葵", "葶", 451 | "葸", "葺", "蒂", "蒉", "蒋", "蒌", "蒗", "蒙", "蒜", "蒨", "蒯", "蒲", 452 | "蒴", "蒸", "蒺", "蒽", "蒿", "蓁", "蓄", "蓆", "蓉", "蓍", "蓐", "蓓", 453 | "蓖", "蓝", "蓟", "蓠", "蓢", "蓥", "蓦", "蓬", "蓼", "蓿", "蔈", "蔑", 454 | "蔓", "蔗", "蔚", "蔡", "蔫", "蔬", "蔷", "蔸", "蔺", "蔼", "蔽", "蕃", 455 | "蕉", "蕊", "蕗", "蕙", "蕤", "蕨", "蕰", "蕲", "蕴", "蕺", "蕻", "蕾", 456 | "薄", "薇", "薏", "薛", "薜", "薤", "薨", "薪", "薮", "薯", "薰", "薷", 457 | "藁", "藉", "藍", "藏", "藐", "藓", "藕", "藜", "藠", "藤", "藩", "藻", 458 | "藿", "蘅", "蘋", "蘑", "蘧", "蘭", "蘸", "虎", "虏", "虐", "虑", "虒", 459 | "虓", "虔", "虚", "虞", "號", "虢", "虫", "虬", "虮", "虱", "虹", "虺", 460 | "虽", "虾", "蚀", "蚁", "蚂", "蚊", "蚌", "蚓", "蚕", "蚝", "蚡", "蚣", 461 | "蚤", "蚧", "蚩", "蚬", "蚯", "蚰", "蚶", "蚺", "蛀", "蛆", "蛇", "蛊", 462 | "蛋", "蛎", "蛐", "蛔", "蛙", "蛛", "蛟", "蛤", "蛩", "蛭", "蛮", "蛰", 463 | "蛲", "蛳", "蛹", "蛾", "蜀", "蜂", "蜃", "蜇", "蜈", "蜊", "蜍", "蜒", 464 | "蜓", "蜕", "蜗", "蜘", "蜚", "蜜", "蜞", "蜡", "蜢", "蜥", "蜴", "蜷", 465 | "蜻", "蜿", "蝇", "蝉", "蝎", "蝗", "蝙", "蝠", "蝮", "蝴", "蝶", "蝼", 466 | "螂", "螃", "螅", "融", "螨", "螫", "螭", "螳", "螺", "蟆", "蟋", "蟒", 467 | "蟠", "蟥", "蟭", "蟹", "蟾", "蠕", "蠡", "蠢", "蠹", "血", "衅", "行", 468 | "衍", "衎", "衔", "衖", "街", "衙", "衡", "衢", "衣", "补", "表", "衩", 469 | "衫", "衬", "衮", "衰", "衲", "衷", "衽", "衾", "衿", "袁", "袂", "袄", 470 | "袅", "袆", "袈", "袋", "袍", "袒", "袖", "袜", "袤", "被", "袭", "袱", 471 | "袴", "裀", "裁", "裂", "装", "裆", "裔", "裕", "裘", "裙", "裝", "裟", 472 | "裡", "裢", "裤", "裨", "裰", "裱", "裳", "裴", "裸", "裹", "裾", "褂", 473 | "褊", "褐", "褒", "褓", "褔", "褚", "褛", "褡", "褥", "褪", "褫", "褴", 474 | "褶", "襁", "襄", "襆", "襟", "襦", "西", "要", "覃", "覆", "視", "见", 475 | "观", "规", "觅", "视", "觇", "览", "觉", "觊", "觌", "觎", "觐", "觑", 476 | "角", "觖", "觚", "觜", "觞", "解", "觥", "触", "觳", "言", "訇", "計", 477 | "訚", "設", "訸", "訾", "詈", "詠", "詹", "誉", "誌", "誓", "諸", "謦", 478 | "譞", "警", "譬", "讙", "计", "订", "讣", "认", "讥", "讦", "讧", "讨", 479 | "让", "讪", "讫", "训", "议", "讯", "记", "讲", "讳", "讴", "讵", "讶", 480 | "讷", "许", "讹", "论", "讼", "讽", "设", "访", "诀", "证", "诂", "诃", 481 | "评", "诅", "识", "诈", "诉", "诊", "诋", "词", "诎", "诏", "译", "诒", 482 | "试", "诗", "诘", "诙", "诚", "诛", "诜", "话", "诞", "诟", "诠", "诡", 483 | "询", "诣", "诤", "该", "详", "诧", "诨", "诩", "诫", "诬", "语", "诮", 484 | "误", "诰", "诱", "诲", "诳", "说", "诵", "请", "诸", "诺", "读", "诽", 485 | "课", "诿", "谀", "谁", "调", "谄", "谅", "谆", "谇", "谈", "谊", "谋", 486 | "谌", "谍", "谎", "谏", "谐", "谑", "谒", "谓", "谔", "谕", "谗", "谙", 487 | "谚", "谛", "谜", "谞", "谟", "谡", "谢", "谣", "谤", "谥", "谦", "谧", 488 | "谨", "谩", "谪", "谬", "谭", "谮", "谯", "谰", "谱", "谲", "谳", "谴", 489 | "谶", "谷", "谿", "豁", "豆", "豇", "豉", "豌", "豐", "豕", "豚", "象", 490 | "豢", "豨", "豪", "豫", "豭", "豳", "豸", "豹", "豺", "貂", "貉", "貌", 491 | "貔", "貟", "資", "贇", "贝", "贞", "负", "贠", "贡", "财", "责", "贤", 492 | "败", "账", "货", "质", "贩", "贪", "贫", "贬", "购", "贮", "贯", "贰", 493 | "贱", "贲", "贳", "贴", "贵", "贶", "贷", "贸", "费", "贺", "贻", "贼", 494 | "贽", "贾", "贿", "赀", "赁", "赂", "赃", "资", "赅", "赇", "赈", "赉", 495 | "赊", "赋", "赌", "赍", "赎", "赏", "赐", "赓", "赔", "赖", "赘", "赚", 496 | "赛", "赜", "赝", "赞", "赟", "赠", "赡", "赢", "赣", "赤", "赦", "赧", 497 | "赪", "赫", "赭", "走", "赳", "赴", "赵", "赶", "起", "趁", "趄", "超", 498 | "越", "趋", "趟", "趣", "趱", "足", "趴", "趵", "趸", "趹", "趺", "趾", 499 | "跂", "跃", "跄", "跆", "跋", "跌", "跑", "跖", "跚", "跛", "距", "跞", 500 | "跟", "跣", "跤", "跨", "跪", "跬", "路", "跳", "践", "跷", "跸", "跹", 501 | "跺", "跻", "跽", "踅", "踉", "踊", "踌", "踏", "踔", "踝", "踞", "踟", 502 | "踢", "踣", "踧", "踩", "踪", "踯", "踰", "踱", "踵", "踹", "踽", "蹀", 503 | "蹂", "蹄", "蹇", "蹈", "蹉", "蹊", "蹋", "蹑", "蹒", "蹙", "蹦", "蹩", 504 | "蹬", "蹭", "蹰", "蹲", "蹴", "蹶", "蹻", "蹿", "躁", "躅", "躇", "躏", 505 | "躞", "身", "躬", "躯", "躲", "躺", "車", "軍", "輋", "车", "轧", "轨", 506 | "轩", "轫", "转", "轭", "轮", "软", "轰", "轲", "轳", "轴", "轵", "轶", 507 | "轸", "轺", "轻", "轼", "载", "轾", "轿", "辂", "较", "辄", "辅", "辆", 508 | "辇", "辈", "辉", "辊", "辋", "辍", "辎", "辐", "辑", "输", "辔", "辕", 509 | "辖", "辗", "辘", "辙", "辛", "辜", "辞", "辟", "辣", "辨", "辩", "辫", 510 | "辰", "辱", "辶", "边", "辽", "达", "迁", "迂", "迄", "迅", "过", "迈", 511 | "迎", "运", "近", "迓", "返", "迕", "还", "这", "进", "远", "违", "连", 512 | "迟", "迢", "迤", "迥", "迦", "迨", "迩", "迪", "迫", "迭", "迮", "述", 513 | "迳", "迴", "迷", "迸", "迹", "迺", "追", "退", "送", "适", "逃", "逄", 514 | "逅", "逆", "选", "逊", "逋", "逍", "透", "逐", "逑", "递", "途", "逖", 515 | "逗", "通", "逛", "逝", "逞", "速", "造", "逡", "逢", "逦", "逮", "逯", 516 | "逵", "逶", "逸", "逻", "逼", "逾", "遁", "遂", "遄", "遆", "遇", "遊", 517 | "遍", "遏", "遐", "遑", "遒", "道", "遗", "遘", "遛", "遢", "遣", "遥", 518 | "遨", "遫", "遭", "遮", "遴", "遵", "遶", "遹", "遽", "避", "邀", "邂", 519 | "邃", "邈", "邋", "邑", "邓", "邕", "邗", "邙", "邛", "邝", "邠", "邡", 520 | "邢", "那", "邦", "邨", "邪", "邬", "邮", "邯", "邰", "邱", "邳", "邴", 521 | "邵", "邸", "邹", "邺", "邻", "邾", "郁", "郄", "郅", "郇", "郊", "郎", 522 | "郏", "郐", "郑", "郓", "郗", "郚", "郜", "郝", "郞", "郡", "郢", "郤", 523 | "郦", "郧", "部", "郪", "郫", "郭", "郯", "郴", "郸", "都", "郾", "郿", 524 | "鄂", "鄄", "鄌", "鄙", "鄚", "鄜", "鄞", "鄠", "鄢", "鄣", "鄯", "鄱", 525 | "酂", "酃", "酄", "酆", "酇", "酉", "酊", "酋", "酌", "配", "酎", "酒", 526 | "酗", "酚", "酝", "酞", "酡", "酢", "酣", "酤", "酥", "酩", "酪", "酬", 527 | "酮", "酯", "酰", "酱", "酵", "酶", "酷", "酸", "酹", "酿", "醇", "醉", 528 | "醋", "醍", "醐", "醒", "醚", "醛", "醪", "醮", "醴", "醵", "醺", "釂", 529 | "采", "釉", "释", "里", "重", "野", "量", "釐", "金", "釜", "鈇", "鈜", 530 | "鈺", "鉏", "鉥", "鉴", "鉿", "銀", "銘", "銮", "鋆", "錡", "録", "錾", 531 | "鎏", "鎔", "鎮", "鏊", "鏐", "鏖", "鐘", "鐾", "鑑", "鑙", "鑫", "钇", 532 | "针", "钉", "钊", "钎", "钏", "钐", "钒", "钓", "钕", "钖", "钗", "钘", 533 | "钙", "钚", "钛", "钜", "钝", "钞", "钟", "钠", "钡", "钢", "钣", "钤", 534 | "钥", "钦", "钧", "钨", "钩", "钫", "钬", "钭", "钮", "钯", "钰", "钱", 535 | "钲", "钳", "钴", "钵", "钶", "钹", "钺", "钻", "钼", "钾", "钿", "铀", 536 | "铁", "铂", "铃", "铄", "铅", "铆", "铈", "铉", "铋", "铌", "铎", "铐", 537 | "铕", "铖", "铙", "铚", "铛", "铜", "铝", "铞", "铟", "铠", "铢", "铣", 538 | "铤", "铧", "铨", "铩", "铫", "铬", "铭", "铮", "铱", "铲", "铳", "铵", 539 | "银", "铷", "铸", "铺", "铼", "链", "铿", "销", "锁", "锂", "锃", "锄", 540 | "锅", "锆", "锈", "锉", "锋", "锌", "锎", "锏", "锐", "锑", "锒", "锖", 541 | "锗", "锘", "错", "锚", "锜", "锞", "锟", "锠", "锡", "锢", "锣", "锤", 542 | "锥", "锦", "锨", "锫", "锬", "锭", "键", "锯", "锰", "锱", "锲", "锴", 543 | "锵", "锶", "锷", "锹", "锺", "锻", "锽", "锾", "镀", "镁", "镂", "镇", 544 | "镈", "镉", "镊", "镌", "镍", "镐", "镑", "镒", "镓", "镔", "镕", "镖", 545 | "镗", "镘", "镛", "镜", "镝", "镞", "镠", "镡", "镣", "镤", "镧", "镨", 546 | "镪", "镫", "镬", "镭", "镯", "镰", "镱", "镳", "镵", "镶", "长", "開", 547 | "閒", "間", "閤", "闇", "闟", "门", "闩", "闪", "闫", "闭", "问", "闯", 548 | "闰", "闱", "闲", "闳", "间", "闵", "闷", "闸", "闹", "闺", "闻", "闼", 549 | "闽", "闾", "阀", "阁", "阂", "阃", "阅", "阆", "阈", "阉", "阊", "阍", 550 | "阎", "阏", "阐", "阑", "阔", "阕", "阖", "阗", "阙", "阚", "阜", "队", 551 | "阡", "阪", "阮", "阱", "防", "阳", "阴", "阵", "阶", "阻", "阾", "阿", 552 | "陀", "陂", "附", "际", "陆", "陇", "陈", "陉", "陋", "陌", "降", "限", 553 | "陔", "陕", "陛", "陞", "陟", "陡", "院", "除", "陨", "险", "陪", "陬", 554 | "陲", "陵", "陶", "陷", "隅", "隆", "隈", "隋", "隍", "随", "隐", "隔", 555 | "隗", "隘", "隙", "際", "障", "隧", "隰", "隳", "隶", "隹", "隼", "隽", 556 | "难", "雀", "雁", "雄", "雅", "集", "雇", "雉", "雌", "雍", "雎", "雏", 557 | "雒", "雕", "雨", "雩", "雪", "雯", "雲", "雳", "零", "雷", "雹", "電", 558 | "雾", "需", "霁", "霂", "霄", "霆", "震", "霈", "霉", "霍", "霎", "霏", 559 | "霓", "霖", "霜", "霞", "霣", "霭", "霰", "露", "霸", "霹", "霾", "青", 560 | "靓", "靖", "静", "靛", "非", "靠", "靡", "面", "靥", "革", "靳", "靴", 561 | "靶", "鞅", "鞋", "鞍", "鞑", "鞘", "鞚", "鞠", "鞣", "鞫", "鞭", "韂", 562 | "韓", "韡", "韦", "韧", "韩", "韪", "韫", "韬", "韭", "音", "韵", "韶", 563 | "韻", "頔", "頫", "頲", "顏", "顒", "页", "顶", "顷", "项", "顺", "须", 564 | "顼", "顽", "顾", "顿", "颀", "颁", "颂", "预", "颅", "领", "颇", "颈", 565 | "颉", "颊", "颋", "颌", "颍", "颐", "频", "颓", "颔", "颖", "颗", "题", 566 | "颚", "颛", "颜", "额", "颠", "颡", "颢", "颤", "颦", "颧", "风", "飏", 567 | "飒", "飓", "飔", "飕", "飘", "飙", "飚", "飞", "食", "飧", "飨", "餍", 568 | "餐", "餮", "饕", "饥", "饦", "饪", "饬", "饭", "饮", "饯", "饰", "饱", 569 | "饲", "饴", "饵", "饶", "饷", "饺", "饼", "饽", "饿", "馀", "馁", "馅", 570 | "馆", "馈", "馋", "馍", "馎", "馏", "馐", "馒", "馓", "馔", "馕", "首", 571 | "馗", "香", "馥", "馨", "驩", "马", "驭", "驮", "驯", "驰", "驱", "驳", 572 | "驴", "驶", "驷", "驸", "驹", "驺", "驻", "驼", "驽", "驾", "驿", "骀", 573 | "骁", "骂", "骄", "骅", "骆", "骇", "骈", "骉", "骊", "骋", "验", "骍", 574 | "骎", "骏", "骐", "骑", "骕", "骖", "骗", "骘", "骙", "骚", "骛", "骜", 575 | "骝", "骞", "骠", "骡", "骢", "骤", "骥", "骧", "骨", "骰", "骶", "骷", 576 | "骸", "骼", "髀", "髃", "髅", "髋", "髑", "髓", "髕", "高", "髡", "髦", 577 | "髫", "髭", "髯", "髻", "鬃", "鬓", "鬟", "鬣", "鬲", "鬻", "鬼", "魁", 578 | "魂", "魄", "魅", "魇", "魉", "魍", "魏", "魑", "魔", "鮀", "鱼", "鱿", 579 | "鲁", "鲅", "鲇", "鲈", "鲋", "鲍", "鲐", "鲑", "鲔", "鲖", "鲛", "鲜", 580 | "鲟", "鲠", "鲡", "鲤", "鲧", "鲨", "鲰", "鲲", "鲸", "鳃", "鳄", "鳊", 581 | "鳌", "鳍", "鳏", "鳖", "鳗", "鳜", "鳝", "鳞", "鴻", "鵾", "鶴", "鸟", 582 | "鸠", "鸡", "鸢", "鸣", "鸥", "鸦", "鸨", "鸩", "鸪", "鸫", "鸬", "鸭", 583 | "鸮", "鸯", "鸱", "鸳", "鸵", "鸶", "鸷", "鸹", "鸽", "鸾", "鸿", "鹁", 584 | "鹂", "鹃", "鹄", "鹅", "鹆", "鹉", "鹊", "鹌", "鹍", "鹏", "鹑", "鹗", 585 | "鹘", "鹚", "鹜", "鹞", "鹣", "鹤", "鹦", "鹧", "鹫", "鹭", "鹰", "鹳", 586 | "鹿", "麂", "麋", "麒", "麓", "麗", "麝", "麟", "麦", "麻", "麽", "麾", 587 | "黄", "黉", "黍", "黎", "黏", "黑", "黔", "默", "黛", "黜", "黝", "黟", 588 | "黠", "黯", "黾", "鼋", "鼍", "鼎", "鼐", "鼓", "鼠", "鼻", "鼾", "齐", 589 | "齿", "龁", "龄", "龈", "龊", "龋", "龌", "龍", "龑", "龙", "龚", "龛", 590 | "龟", "", "︰", "﹐", "﹑", "﹒", "﹔", "﹖", "﹟", "#", "(", ")", 591 | "+", ",", "-", ".", "/", "0", "1", "2", "4", "5", "6", "7", 592 | "9", ":", "B", "~", "¥"}; -------------------------------------------------------------------------------- /src/rec/decode.h: -------------------------------------------------------------------------------- 1 | // Copyright(c) TuYuAI authors.All rights reserved. 2 | // Licensed under the Apache-2.0 License. 3 | // 4 | 5 | #pragma once 6 | 7 | #include 8 | #include 9 | extern std::vector alphabets; 10 | std::vector GreedyDecode(const std::vector &preds); 11 | -------------------------------------------------------------------------------- /src/rec/recognizer.cpp: -------------------------------------------------------------------------------- 1 | // Copyright(c) TuYuAI authors.All rights reserved. 2 | // Licensed under the Apache-2.0 License. 3 | // 4 | 5 | #include "recognizer.h" 6 | #include 7 | #include 8 | #include 9 | #include 10 | #include "spdlog/spdlog.h" 11 | 12 | #define ORT_ABORT_ON_ERROR(expr) \ 13 | do { \ 14 | OrtStatus* onnx_status = (expr); \ 15 | if (onnx_status != NULL) { \ 16 | const char* msg = ort_api_->GetErrorMessage(onnx_status); \ 17 | fprintf(stderr, "%s\n", msg); \ 18 | ort_api_->ReleaseStatus(onnx_status); \ 19 | abort(); \ 20 | } \ 21 | } while (0); 22 | 23 | Recognizer::~Recognizer() { 24 | if (session_ != nullptr) { 25 | ort_api_->ReleaseSession(session_); 26 | } 27 | if (session_options_ != nullptr) { 28 | ort_api_->ReleaseSessionOptions(session_options_); 29 | } 30 | } 31 | 32 | void Recognizer::InitModel(const std::string& model_path) { 33 | ORT_ABORT_ON_ERROR(ort_api_->CreateSessionOptions(&session_options_)); 34 | ort_api_->SetIntraOpNumThreads(session_options_, 1); 35 | ort_api_->SetSessionGraphOptimizationLevel(session_options_, ORT_ENABLE_ALL); 36 | 37 | 38 | #if defined(WIN32) || defined(_WIN32) || defined(__WIN32__) || defined(__NT__) 39 | std::wstring w_model_path; 40 | ORT_ABORT_ON_ERROR(ort_api_->CreateSession(env_, w_model_path.c_str(), 41 | session_options_, &session_)); 42 | #else 43 | ORT_ABORT_ON_ERROR(ort_api_->CreateSession(env_, model_path.c_str(), 44 | session_options_, &session_)); 45 | #endif 46 | } 47 | 48 | std::string Recognizer::Predict(const cv::Mat& image) { 49 | auto start_time = std::chrono::high_resolution_clock::now(); 50 | 51 | cv::Mat out; 52 | Preprocess(image, out); 53 | 54 | int image_width = out.cols; 55 | int image_height = out.rows; 56 | int image_channels = out.channels(); 57 | 58 | std::vector input_node_dims = {1, image_channels, image_height, 59 | image_width}; 60 | size_t input_tensor_size = image_width * image_height * image_channels; 61 | std::vector input_tensor_values(input_tensor_size); 62 | 63 | float* input_data = input_tensor_values.data(); 64 | 65 | for (int h = 0; h < image_height; ++h) { 66 | for (int w = 0; w < image_width; ++w) { 67 | int idx0 = h * image_width + w; 68 | int idx1 = image_height * image_width + idx0; 69 | int idx2 = 2 * image_height * image_width + idx0; 70 | cv::Vec3f d = out.at(h, w); 71 | input_tensor_values[idx0] = d[0]; 72 | input_tensor_values[idx1] = d[1]; 73 | input_tensor_values[idx2] = d[2]; 74 | } 75 | } 76 | 77 | // create input tensor object from data values 78 | OrtMemoryInfo* allocator_info; 79 | ORT_ABORT_ON_ERROR(ort_api_->CreateCpuMemoryInfo( 80 | OrtArenaAllocator, OrtMemTypeDefault, &allocator_info)); 81 | OrtValue* input_tensor = NULL; 82 | ORT_ABORT_ON_ERROR(ort_api_->CreateTensorWithDataAsOrtValue( 83 | allocator_info, input_tensor_values.data(), 84 | input_tensor_size * sizeof(float), input_node_dims.data(), 4, 85 | ONNX_TENSOR_ELEMENT_DATA_TYPE_FLOAT, &input_tensor)); 86 | int is_tensor; 87 | ORT_ABORT_ON_ERROR(ort_api_->IsTensor(input_tensor, &is_tensor)); 88 | assert(is_tensor); 89 | 90 | OrtStatus* status; 91 | const char* input_names[] = {"image"}; 92 | const char* output_names[] = {"output"}; 93 | 94 | OrtValue* output_tensor = NULL; 95 | ORT_ABORT_ON_ERROR(ort_api_->Run(this->session_, NULL, input_names, 96 | (const OrtValue* const*)&input_tensor, 1, 97 | output_names, 1, &output_tensor)); 98 | assert(output_tensor != NULL); 99 | ORT_ABORT_ON_ERROR(ort_api_->IsTensor(output_tensor, &is_tensor)); 100 | assert(is_tensor); 101 | 102 | float* out_array; 103 | ORT_ABORT_ON_ERROR( 104 | ort_api_->GetTensorMutableData(output_tensor, (void**)&out_array)); 105 | 106 | OrtTensorTypeAndShapeInfo* output_tensor_info; 107 | ORT_ABORT_ON_ERROR( 108 | ort_api_->GetTensorTypeAndShape(output_tensor, &output_tensor_info)); 109 | 110 | size_t out_num_dims; 111 | ORT_ABORT_ON_ERROR( 112 | ort_api_->GetDimensionsCount(output_tensor_info, &out_num_dims)); 113 | std::vector output_node_dims; 114 | output_node_dims.resize(out_num_dims); 115 | ORT_ABORT_ON_ERROR(ort_api_->GetDimensions( 116 | output_tensor_info, (int64_t*)output_node_dims.data(), out_num_dims)); 117 | 118 | int64_t T = output_node_dims[0]; 119 | int64_t N = output_node_dims[1]; 120 | int64_t C = output_node_dims[2]; 121 | SPDLOG_DEBUG("T = {}, N= {}, C={}\n", T, N, C); 122 | std::vector preds; 123 | for (int t = 0; t < T; t++) { 124 | int idx = 0; 125 | float max_value = -10000000000.0f; 126 | for (int c = 0; c < C; c++) { 127 | if (out_array[t * C + c] > max_value) { 128 | max_value = out_array[t * C + c]; 129 | idx = c; 130 | } 131 | } 132 | preds.emplace_back(idx); 133 | SPDLOG_DEBUG("preds is {}\n", idx); 134 | } 135 | 136 | std::vector result = GreedyDecode(preds); 137 | 138 | ort_api_->ReleaseTensorTypeAndShapeInfo(output_tensor_info); 139 | ort_api_->ReleaseValue(output_tensor); 140 | ort_api_->ReleaseValue(input_tensor); 141 | ort_api_->ReleaseMemoryInfo(allocator_info); 142 | 143 | std::string ret_result; 144 | for (int i = 0; i < result.size(); i++) { 145 | int idx = result[i]; 146 | ret_result += alphabets[idx - 1]; 147 | } 148 | 149 | return ret_result; 150 | } 151 | 152 | void Recognizer::Preprocess(const cv::Mat& image, cv::Mat& out) { 153 | cv::cvtColor(image, image, cv::COLOR_BGR2RGB); 154 | int image_width = image.cols; 155 | int image_height = image.rows; 156 | int param_w = 200; 157 | int param_h = 32; 158 | float ratio = float(param_w) / float(param_h); 159 | float h_major_ratio = float(image_height) / float(param_h); 160 | int new_h = int(image_height / h_major_ratio); 161 | int new_w = int(image_width / h_major_ratio); 162 | SPDLOG_DEBUG("new_h = {}, new_w = {}", new_h, new_w); 163 | cv::Mat resize_image; 164 | cv::resize(image, resize_image, cv::Size(new_w, new_h)); 165 | if (((float)image_width / image_height) < ratio) { 166 | int top = (param_h - new_h) / 2; 167 | int left = (param_w - new_w) / 2; 168 | cv::Mat pad_image(cv::Size(param_w, param_h), image.type()); 169 | pad_image.setTo(cv::Scalar(255, 255, 255)); 170 | cv::Mat roi_image = pad_image(cv::Rect(left, top, new_w, new_h)); 171 | resize_image.copyTo(roi_image); 172 | cv::resize(pad_image, resize_image, cv::Size(param_w, param_h)); 173 | } 174 | cv::Mat out_float; 175 | resize_image.convertTo(out_float, CV_32FC3); 176 | out = out_float / 255.0f; 177 | out = (out - 0.5) / 0.5; 178 | } 179 | -------------------------------------------------------------------------------- /src/rec/recognizer.h: -------------------------------------------------------------------------------- 1 | // Copyright(c) TuYuAI authors.All rights reserved. 2 | // Licensed under the Apache-2.0 License. 3 | // 4 | 5 | #ifndef RECOGNIZER_H_ 6 | #define RECOGNIZER_H_ 7 | #include "common/common.h" 8 | #include 9 | #include 10 | #include "decode.h" 11 | #include "onnxruntime_c_api.h" 12 | 13 | class TUYUIDCARD_API Recognizer { 14 | public: 15 | Recognizer(const OrtApi* ort_api, OrtEnv* env) 16 | : ort_api_(ort_api), 17 | env_(env), 18 | session_(nullptr), 19 | session_options_(nullptr) {} 20 | ~Recognizer(); 21 | std::string Predict(const cv::Mat& image); 22 | 23 | void Preprocess(const cv::Mat& image, cv::Mat& out); 24 | void InitModel(const std::string& onnx_model_name); 25 | 26 | private: 27 | const OrtApi* ort_api_; 28 | OrtEnv* env_; 29 | OrtSessionOptions* session_options_; 30 | OrtSession* session_; 31 | }; 32 | 33 | #endif -------------------------------------------------------------------------------- /src/rec/test.cpp: -------------------------------------------------------------------------------- 1 | // Copyright(c) TuYuAI authors.All rights reserved. 2 | // Licensed under the Apache-2.0 License. 3 | // 4 | 5 | #include 6 | #include "onnxruntime_c_api.h" 7 | #include "recognizer.h" 8 | 9 | int main(int argc, char* argv[]) { 10 | if (argc < 3) { 11 | std::cout << "idcard_rec_test model_path image_path" << std::endl; 12 | return 0; 13 | } 14 | std::string model_path = argv[1]; 15 | std::string image_path = argv[2]; 16 | const OrtApi* g_ort = OrtGetApiBase()->GetApi(ORT_API_VERSION); 17 | OrtEnv* env; 18 | g_ort->CreateEnv(ORT_LOGGING_LEVEL_WARNING, "idcard", &env); 19 | Recognizer recognizer(g_ort, env); 20 | recognizer.InitModel(model_path); 21 | cv::Mat image = cv::imread(image_path); 22 | std::string res = recognizer.Predict(image); 23 | std::cout << res << std::endl; 24 | return 0; 25 | } --------------------------------------------------------------------------------