├── .gitignore ├── LICENSE ├── README.md ├── app.py ├── configs ├── __init__.py └── base.py ├── nets ├── __init__.py └── backbone │ ├── __init__.py │ ├── ddddocr │ ├── __init__.py │ └── ddddocrv1.py │ ├── effcientnet │ ├── __init__.py │ └── efficientnetv2.py │ └── mobilenet │ ├── __init__.py │ ├── mobilenetv2.py │ └── mobilenetv3.py ├── projects ├── __init__.py └── ddddocr │ ├── checkpoints │ └── checkpoint_ddddocr-2022-2-28_6_2090000.tar │ └── config.yaml ├── requirements.txt ├── tools └── __init__.py └── utils ├── __init__.py ├── cache_data.py ├── load_cache.py ├── project_manager.py └── train.py /.gitignore: -------------------------------------------------------------------------------- 1 | # Byte-compiled / optimized / DLL files 2 | __pycache__/ 3 | *.py[cod] 4 | *$py.class 5 | 6 | # C extensions 7 | *.so 8 | 9 | # Distribution / packaging 10 | .Python 11 | build/ 12 | develop-eggs/ 13 | dist/ 14 | downloads/ 15 | eggs/ 16 | .eggs/ 17 | lib/ 18 | lib64/ 19 | parts/ 20 | sdist/ 21 | var/ 22 | wheels/ 23 | pip-wheel-metadata/ 24 | share/python-wheels/ 25 | *.egg-info/ 26 | .installed.cfg 27 | *.egg 28 | MANIFEST 29 | 30 | # PyInstaller 31 | # Usually these files are written by a python script from a template 32 | # before PyInstaller builds the exe, so as to inject date/other infos into it. 33 | *.manifest 34 | *.spec 35 | 36 | # Installer logs 37 | pip-log.txt 38 | pip-delete-this-directory.txt 39 | 40 | # Unit test / coverage reports 41 | htmlcov/ 42 | .tox/ 43 | .nox/ 44 | .coverage 45 | .coverage.* 46 | .cache 47 | nosetests.xml 48 | coverage.xml 49 | *.cover 50 | *.py,cover 51 | .hypothesis/ 52 | .pytest_cache/ 53 | 54 | # Translations 55 | *.mo 56 | *.pot 57 | 58 | # Django stuff: 59 | *.log 60 | local_settings.py 61 | db.sqlite3 62 | db.sqlite3-journal 63 | 64 | # Flask stuff: 65 | instance/ 66 | .webassets-cache 67 | 68 | # Scrapy stuff: 69 | .scrapy 70 | 71 | # Sphinx documentation 72 | docs/_build/ 73 | 74 | # PyBuilder 75 | target/ 76 | 77 | # Jupyter Notebook 78 | .ipynb_checkpoints 79 | 80 | # IPython 81 | profile_default/ 82 | ipython_config.py 83 | 84 | # pyenv 85 | .python-version 86 | 87 | # pipenv 88 | # According to pypa/pipenv#598, it is recommended to include Pipfile.lock in version control. 89 | # However, in case of collaboration, if having platform-specific dependencies or dependencies 90 | # having no cross-platform support, pipenv may install dependencies that don't work, or not 91 | # install all needed dependencies. 92 | #Pipfile.lock 93 | 94 | # PEP 582; used by e.g. github.com/David-OConnor/pyflow 95 | __pypackages__/ 96 | 97 | # Celery stuff 98 | celerybeat-schedule 99 | celerybeat.pid 100 | 101 | # SageMath parsed files 102 | *.sage.py 103 | 104 | # Environments 105 | .env 106 | .venv 107 | env/ 108 | venv/ 109 | ENV/ 110 | env.bak/ 111 | venv.bak/ 112 | 113 | # Spyder project settings 114 | .spyderproject 115 | .spyproject 116 | 117 | # Rope project settings 118 | .ropeproject 119 | 120 | # mkdocs documentation 121 | /site 122 | 123 | # mypy 124 | .mypy_cache/ 125 | .dmypy.json 126 | dmypy.json 127 | 128 | # Pyre type checker 129 | .pyre/ 130 | -------------------------------------------------------------------------------- /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 [yyyy] [name of copyright owner] 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 | # dddd_trainer 带带弟弟OCR训练工具 (2025.01.18项目即将更新,将支持完善的可视化训练和新的训练策略) 2 | 3 | ### 带带弟弟OCR所用的训练工具今天正式开源啦! [ddddocr](https://github.com/sml2h3/ddddocr) 4 | 5 | ### 项目仅支持N卡训练,A卡或其他卡就先别看啦 6 | 7 | ### 项目基于Pytorch进行开发,支持cnn与crnn进行训练、断点恢复、自动导出onnx模型,并同时支持无缝使用[ddddocr](https://github.com/sml2h3/ddddocr) 与 [ocr_api_server](https://gitee.com/fkgeek/ocr_api_server) 的无缝部署 8 | 9 | ### 训练环境支持 10 | 11 | Windows/Linux 12 | 13 | Macos仅支持cpu训练 14 | 15 | ## 1、深度学习必备环境配置(非仅本项目要求,而是所有深度学习项目要求,cpu训练除外) 16 | 17 | ### 开始本教程前请先前往[pytorch](https://pytorch.org/get-started/locally/) 官网查看自己系统与硬件支持的pytorch版本,注意30系列之前的N卡,如2080Ti等请选择cuda11以下的版本(例:CUDA 10.2),如果为30系N卡,仅支持CUDA 11版本,请选择CUDA 11以上版本(例:CUDA 11.3),然后根据选择的条件显示的pytorch安装命令完成pytorch安装,由于pytorch的版本更新速度导致很多pypi源仅缓存了cpu版本,CUDA版本需要自己在官网安装。 18 | 19 | ### 安装CUDA和CUDNN 20 | 21 | 根据自己显卡型号与系统选择 22 | 23 | [cuda](https://developer.nvidia.com/cuda-downloads) 24 | 25 | [cudnn](https://developer.nvidia.com/zh-cn/cudnn) 26 | 27 | 注意cudnn支持的cuda版本号要与你安装的cuda版本号对应,不同版本的cuda支持的显卡不一样,20系无脑选择10.2版本cuda,30系无脑选择11.3版本cuda,这里有啥问题就百度吧,算是一个基础问题。 28 | 29 | ## 2、训练部分 30 | 31 | - 以下所有变量均以 {param} 格式代替,表示可根据自己需要修改,而使用时并不需要带上{},如步骤创建新的训练项目,使用时可以直接写 32 | 33 | `python app.py create test_project` 34 | 35 | - ### 1、Clone本项目到本地 36 | 37 | `git clone https://github.com/sml2h3/dddd_trainer.git` 38 | 39 | - ### 2、进入项目目录并安装本项目所需依赖 40 | 41 | `pip install -r requirements.txt -i https://pypi.douban.com/simple` 42 | 43 | - ### 3、创建新的训练项目 44 | 45 | `python app.py create {project_name}` 46 | 47 | 如果想要创建一个CNN的项目,则可以加上--single参数,CNN项目识别比如图片类是什么分类的情况,比如图片上只有一个字,识别这张图是什么字(图上有多个字的不要用CNN模式),又比如分辨图片里是狮子还是兔子用CNN模式比较合适,大多数OCR需求请不要使用--single 48 | 49 | `python app.py create {project_name} --single` 50 | 51 | project_name 为项目名称,尽量不要以特殊符号命名 52 | 53 | - ### 4、准备数据 54 | 55 | 项目支持两种形式的数据 56 | 57 | ### A、从文件名导入 58 | 59 | 图片均在同一个文件夹中,且命名为类似,其中/root/images_set为图片所在目录,可以为任意目录地址 60 | 61 | ``` 62 | /root/images_set/ 63 | |---- abcde_随机hash值.jpg 64 | |---- sdae_随机hash值.jpg 65 | |---- 酱闷肘子_随机hash值.jpg 66 | 67 | ``` 68 | 69 | 如下图所示 70 | 71 | ![image](https://cdn.wenanzhe.com/img/mkGu_000001d00f140741741ed9916240d8d5.jpg) 72 | 73 | 那么图片命名可以是 74 | 75 | `mkGu_000001d00f140741741ed9916240d8d5.jpg` 76 | 77 | ### 为考虑各种情况,dddd_trainer不会自动去处理大小写问题,如果想训练大小写,则在样本标注时就需要自己标注好大小写,如上面例子 78 | 79 | ### B、从文件中导入 80 | 81 | 受限于可能样本组织形式或者特殊字符,本项目支持从txt文档中导入数据,数据集目录必须包含有`labels.txt`文件和`images`文件夹, 其中/root/images_set为图片所在目录,可以为任意目录地址 82 | 83 | `labels.txt`文件中包含了所有在`/root/images_set/images`目录下基于`/root/images_set/images`的图片相对路径,`/root/images_set/images`下可以有目录。 84 | 85 | #### 当然,在这种模式下,图片的文件名随意,可以有具体label也可以没有,因为咱们不从这里获取图片的label 86 | 87 | 如下所示 88 | - 89 | a.images下无目录的形式 90 | 91 | ``` 92 | /root/images_set/ 93 | |---- labels.txt 94 | |---- images 95 | |---- 随机hash值.jpg 96 | |---- 随机hash值.jpg 97 | |---- 酱闷肘子_随机hash值.jpg 98 | 99 | labels.txt文件内容为(其中\t制表符为每行文件名与label的分隔符) 100 | 随机hash值.jpg\tabcd 101 | 随机hash值.jpg\tsdae 102 | 酱闷肘子_随机hash值.jpg\t酱闷肘子 103 | ``` 104 | b.images下有目录的形式 105 | ``` 106 | /root/images_set/ 107 | |---- labels.txt 108 | |---- images 109 | |---- aaaa 110 | |---- 随机hash值.jpg 111 | |---- 酱闷肘子_随机hash值.jpg 112 | 113 | labels.txt文件内容为(其中\t制表符为每行文件名与label的分隔符) 114 | aaaa/随机hash值.jpg\tabcd 115 | aaaa/随机hash值.jpg\tsdae 116 | 酱闷肘子_随机hash值.jpg\t酱闷肘子 117 | 118 | ``` 119 | 120 | ### 为了新手更好的理解本部分的内容,本项目也提供了两套基础数据集提供测试 121 | 122 | [数据集一](https://wwm.lanzoum.com/iUyYb0b5z3lg) 123 | [数据集二](https://wwm.lanzoum.com/itczd0b5z3yj) 124 | - ### 5、修改配置文件 125 | ```yaml 126 | Model: 127 | CharSet: [] # 字符集,不要动,会自动生成 128 | ImageChannel: 1 # 图片通道数,如果你想以灰度图进行训练,则设置为1,彩图,则设置为3。如果设置为1,数据集是彩图,项目会在训练的过程中自动在内存中将读取到的彩图转为灰度图,并不需要提前自己修改并且该设置不会修改本地图片 129 | ImageHeight: 64 # 图片自动缩放后的高度,单位为px,高度必须为16的倍数,会自动缩放图像 130 | ImageWidth: -1 # 图片自动缩放后的宽度,单位为px,本项若设置为-1,将自动根据情况调整 131 | Word: false # 是否为CNN模型,这里在创建项目的时候通过参数控制,不要自己修改 132 | System: 133 | Allow_Ext: [jpg, jpeg, png, bmp] # 支持的图片后缀,不满足的图片将会被自动忽略 134 | GPU: true # 是否启用GPU去训练,使用GPU训练需要参考步骤一安装好环境 135 | GPU_ID: 0 # GPU设备号,0为第一张显卡 136 | Path: '' # 数据集根目录,在缓存图片步骤会自动生成,不需要自己改,除非数据集地址改了 137 | Project: test # 项目名称 也就是{project_name} 138 | Val: 0.03 # 验证集的数据量比例,0.03就是3%,在缓存数据时,会自动选则3%的图片用作训练过程中的数据验证,修改本值之后需要重新缓存数据 139 | Train: 140 | BATCH_SIZE: 32 # 训练时每一个batch_size的大小,主要取决于你的显存或内存大小,可以根据自己的情况,多测试,一般为16的倍数,如16,32,64,128 141 | CNN: {NAME: ddddocr} # 特征提取的模型,目前支持的值为ddddocr,effnetv2_l,effnetv2_m,effnetv2_xl,effnetv2_s,mobilenetv2,mobilenetv3_s,mobilenetv3_l 142 | DROPOUT: 0.3 # 非专业人员不要动 143 | LR: 0.01 # 初始学习率 144 | OPTIMIZER: SGD # 优化器,不要动 145 | SAVE_CHECKPOINTS_STEP: 2000 # 每多少step保存一次模型 146 | TARGET: {Accuracy: 0.97, Cost: 0.05, Epoch: 20} # 训练结束的目标,同时满足时自动结束训练并保存onnx模型,Accuracy为需要满足的最小准确率,Cost为需要满足的最小损失,Epoch为需要满足的最小训练轮数 147 | TEST_BATCH_SIZE: 32 # 测试时每一个batch_size的大小,主要取决于你的显存或内存大小,可以根据自己的情况,多测试,一般为16的倍数,如16,32,64,128 148 | TEST_STEP: 1000 # 每多少step进行一次测试 149 | 150 | 151 | ``` 152 | 配置文件位于本项目根目录下`projects/{project_name}/config.yaml` 153 | 154 | - ### 6、缓存数据 155 | 156 | `python app.py cache {project_name} /root/images_set/` 157 | 158 | 如果是从labels.txt里面读取数据 159 | 160 | `python app.py cache {project_name} /root/images_set/ file` 161 | 162 | - ### 7、开始训练或恢复训练 163 | 164 | `python app.py train {project_name}` 165 | 166 | - ### 8、部署 167 | 168 | `你们先训练着,我去适配ddddocr和ocr_api_server了,适配完我再继续更新文档` 169 | 170 | - ### 9、备注 171 | 172 | 1. ddddocr crnn网络结构来自 https://github.com/meijieru/crnn.pytorch.git 173 | -------------------------------------------------------------------------------- /app.py: -------------------------------------------------------------------------------- 1 | import fire 2 | 3 | from loguru import logger 4 | from utils import project_manager 5 | from utils import cache_data 6 | from utils import train 7 | 8 | 9 | class App: 10 | 11 | def __init__(self): 12 | logger.info("\nHello baby~") 13 | 14 | def create(self, project_name: str, single: bool = False): 15 | logger.info("\nCreate Project ----> {}".format(project_name)) 16 | pm = project_manager.ProjectManager() 17 | pm.create_project(project_name, single) 18 | 19 | def cache(self, project_name: str, base_path: str, search_type: str = "name"): 20 | logger.info("\nCaching Data ----> {}\nPath ----> {}".format(project_name, base_path)) 21 | cache = cache_data.CacheData(project_name) 22 | cache.cache(base_path, search_type) 23 | pass 24 | 25 | def train(self, project_name: str): 26 | logger.info("\nStart Train ----> {}\n".format(project_name)) 27 | trainer = train.Train(project_name) 28 | trainer.start() 29 | 30 | 31 | 32 | if __name__ == '__main__': 33 | fire.Fire(App) 34 | -------------------------------------------------------------------------------- /configs/__init__.py: -------------------------------------------------------------------------------- 1 | from .base import * 2 | 3 | 4 | -------------------------------------------------------------------------------- /configs/base.py: -------------------------------------------------------------------------------- 1 | import os 2 | import json 3 | import yaml 4 | 5 | 6 | class Config(object): 7 | 8 | def __init__(self, project_name): 9 | self.project_name = project_name 10 | self.base_path = os.path.join(os.path.dirname(os.path.dirname(os.path.abspath(__file__))), "projects") 11 | self.config_dict = { 12 | "System": { 13 | "Project": None, 14 | "GPU": True, 15 | "GPU_ID": 0, 16 | "Allow_Ext": ["jpg", "jpeg", "png", "bmp"], 17 | "Path": "", 18 | "Val": 0.03 19 | }, 20 | "Model": { 21 | "ImageWidth": -1, 22 | "ImageHeight": 64, 23 | "ImageChannel": 1, 24 | "CharSet": [], 25 | "Word": False 26 | }, 27 | "Train": { 28 | "BATCH_SIZE": 32, 29 | "TEST_BATCH_SIZE": 32, 30 | 'CNN': { 31 | "NAME": "ddddocr", 32 | }, 33 | 'DROPOUT': 0.3, 34 | 'OPTIMIZER': 'SGD', 35 | "TEST_STEP": 1000, 36 | "SAVE_CHECKPOINTS_STEP": 2000, 37 | "TARGET": { 38 | "Accuracy": 0.97, 39 | "Epoch": 20, 40 | "Cost": 0.05 41 | }, 42 | "LR": 0.01 43 | } 44 | } 45 | 46 | 47 | def make_config(self, config_dict=None, single: bool = False): 48 | if not config_dict: 49 | config_dict = self.config_dict 50 | if single: 51 | config_dict['Model']['Word'] = True 52 | 53 | config_dict["System"]["Project"] = self.project_name 54 | config_path = os.path.join(self.base_path, self.project_name, "config.yaml") 55 | with open(config_path, 'w', encoding="utf-8") as f: 56 | yaml.dump(config_dict, f, allow_unicode=True, default_flow_style=None, indent=4) 57 | 58 | def load_config(self): 59 | config_path = os.path.join(self.base_path, self.project_name, "config.yaml") 60 | with open(config_path, 'r', encoding="utf-8") as f: 61 | config_dict = yaml.load(f, Loader=yaml.FullLoader) 62 | return config_dict 63 | -------------------------------------------------------------------------------- /nets/__init__.py: -------------------------------------------------------------------------------- 1 | import json 2 | 3 | from .backbone import * 4 | import torch 5 | 6 | torch.set_num_threads(1) 7 | 8 | import numpy as np 9 | 10 | np.random.seed(0) 11 | torch.manual_seed(0) 12 | 13 | 14 | class Net(torch.nn.Module): 15 | def __init__(self, conf, lr=None): 16 | super(Net, self).__init__() 17 | 18 | self.backbones_list = { 19 | "ddddocr": DdddOcr, 20 | "effnetv2_l": effnetv2_l, 21 | "effnetv2_m": effnetv2_m, 22 | "effnetv2_xl": effnetv2_xl, 23 | "effnetv2_s": effnetv2_s, 24 | "mobilenetv2": mobilenetv2, 25 | "mobilenetv3_s": MobileNetV3_Small, 26 | "mobilenetv3_l": MobileNetV3_Large 27 | } 28 | 29 | self.optimizers_list = { 30 | "SGD": torch.optim.SGD, 31 | "Adam": torch.optim.Adam, 32 | } 33 | self.conf = conf 34 | if self.conf['System']['GPU']: 35 | torch.cuda.manual_seed_all(0) 36 | self.image_channel = self.conf['Model']['ImageChannel'] 37 | self.resize = [int(self.conf['Model']['ImageWidth']), int(self.conf['Model']['ImageHeight'])] 38 | self.charset = self.conf['Model']['CharSet'] 39 | self.charset_len = len(self.charset) 40 | self.backbone = self.conf['Train']['CNN']['NAME'] 41 | self.paramters = [] 42 | self.word = self.conf['Model']['Word'] 43 | if self.backbone in self.backbones_list: 44 | test_cnn = self.backbones_list[self.backbone](nc=1) 45 | x = torch.randn(1, 1, self.resize[1], self.resize[1]) 46 | test_features = test_cnn(x) 47 | del x 48 | del test_cnn 49 | if self.word: 50 | self.out_size = test_features.size()[1] * test_features.size()[2] * test_features.size()[3] 51 | else: 52 | self.out_size = test_features.size()[1] * test_features.size()[2] 53 | self.cnn = self.backbones_list[self.backbone](nc=self.image_channel) 54 | else: 55 | raise Exception("{} is not found in backbones! backbone list : {}".format(self.backbone, json.dumps( 56 | list(self.backbones_list.keys())))) 57 | self.paramters.append({'params': self.cnn.parameters()}) 58 | 59 | 60 | if not self.word: 61 | self.dropout = self.conf['Train']['DROPOUT'] 62 | self.lstm = torch.nn.LSTM(input_size=self.out_size, hidden_size=self.out_size, bidirectional=True, 63 | num_layers=1, dropout=self.dropout) 64 | self.paramters.append({'params': self.lstm.parameters()}) 65 | 66 | self.loss = torch.nn.CTCLoss(blank=0, reduction='mean') 67 | self.fc = torch.nn.Linear(in_features=self.out_size * 2, out_features=self.charset_len) 68 | 69 | else: 70 | self.lstm = None 71 | self.loss = torch.nn.CrossEntropyLoss() 72 | self.fc = torch.nn.Linear(in_features=self.out_size, out_features=self.charset_len) 73 | 74 | self.paramters.append({'params': self.loss.parameters()}) 75 | 76 | self.paramters.append({'params': self.fc.parameters()}) 77 | 78 | if lr == None: 79 | self.lr = self.conf['Train']['LR'] 80 | else: 81 | self.lr = lr 82 | 83 | self.optim = self.conf['Train']['OPTIMIZER'] 84 | if self.optim in self.optimizers_list: 85 | if self.optim == "SGD": 86 | self.optimizer = self.optimizers_list[self.optim](self.paramters, lr=self.lr, momentum=0.9) 87 | else: 88 | self.optimizer = self.optimizers_list[self.optim](self.paramters, lr=self.lr, betas=(0.9, 0.99)) 89 | else: 90 | raise Exception("{} is not found in optimizers! optimizers list : {}".format(self.optim, json.dumps( 91 | list(self.optimizers_list.keys())))) 92 | 93 | self.scheduler = torch.optim.lr_scheduler.ExponentialLR(self.optimizer, gamma=0.98) 94 | 95 | 96 | def forward(self, inputs): 97 | predict = self.get_features(inputs) 98 | if self.word: 99 | outputs = predict.max(1) 100 | else: 101 | outputs = predict.max(2)[1].transpose(0, 1) 102 | return outputs 103 | 104 | def get_features(self, inputs): 105 | outputs = self.cnn(inputs) 106 | if not self.word: 107 | outputs = outputs.permute(3, 0, 1, 2) 108 | w, b, c, h = outputs.shape 109 | outputs = outputs.view(w, b, c * h) 110 | outputs, _ = self.lstm(outputs) 111 | time_step, batch_size, h = outputs.shape 112 | outputs = outputs.view(time_step * batch_size, h) 113 | outputs = self.fc(outputs) 114 | outputs = outputs.view(time_step, batch_size, -1) 115 | else: 116 | outputs = outputs.view(outputs.size(0), -1) 117 | outputs = self.fc(outputs) 118 | return outputs 119 | 120 | def trainer(self, inputs, labels, labels_length): 121 | outputs = self.get_features(inputs) 122 | loss, lr = self.get_loss(outputs, labels, labels_length) 123 | return loss, lr 124 | 125 | def tester(self, inputs, labels, labels_length): 126 | predict = self.get_features(inputs) 127 | pred_decode_labels = [] 128 | labels_list = [] 129 | correct_list = [] 130 | error_list = [] 131 | i = 0 132 | labels = labels.tolist() 133 | if self.word: 134 | outputs = predict.max(1)[1] 135 | for pred_labels in outputs: 136 | pred_decode_labels.append(pred_labels) 137 | else: 138 | outputs = predict.max(2)[1].transpose(0, 1) 139 | for pred_labels in outputs: 140 | decoded = [] 141 | last_item = 0 142 | for item in pred_labels: 143 | item = item.item() 144 | if item == last_item: 145 | continue 146 | else: 147 | last_item = item 148 | if item != 0: 149 | decoded.append(item) 150 | pred_decode_labels.append(decoded) 151 | 152 | for idx in labels_length.tolist(): 153 | labels_list.append(labels[i: i + idx]) 154 | i += idx 155 | if len(labels_list) != len(pred_decode_labels): 156 | raise Exception("origin labels length is {}, but pred labels length is {}".format( 157 | len(labels_list), len(pred_decode_labels))) 158 | for ids in range(len(labels_list)): 159 | if self.word: 160 | label_res = labels_list[ids][0] 161 | 162 | pred_res = pred_decode_labels[ids].item() 163 | else: 164 | label_res = labels_list[ids] 165 | 166 | pred_res = pred_decode_labels[ids] 167 | if label_res == pred_res: 168 | correct_list.append(ids) 169 | else: 170 | error_list.append(ids) 171 | return pred_decode_labels, labels_list, correct_list, error_list 172 | 173 | def get_loss(self, predict, labels, labels_length): 174 | labels = torch.autograd.Variable(labels) 175 | if self.word: 176 | loss = self.loss(predict, labels.long().cuda()) 177 | else: 178 | log_predict = predict.log_softmax(2) 179 | seq_len = torch.IntTensor([log_predict.shape[0]] * log_predict.shape[1]) 180 | loss = self.loss(log_predict.cpu(), labels, seq_len, labels_length) 181 | self.optimizer.zero_grad() 182 | loss.backward() 183 | self.optimizer.step() 184 | 185 | return loss.item(), self.scheduler.state_dict()['_last_lr'][-1] 186 | 187 | def save_model(self, path, net): 188 | torch.save(net, path) 189 | 190 | @staticmethod 191 | def get_device(gpu_id): 192 | if gpu_id == -1: 193 | device = torch.device('cpu'.format(str(gpu_id))) 194 | else: 195 | device = torch.device('cuda:{}'.format(str(gpu_id))) 196 | return device 197 | 198 | def variable_to_device(self, inputs, device): 199 | return torch.autograd.Variable(inputs).to(device) 200 | 201 | def get_random_tensor(self): 202 | width = self.resize[0] 203 | height = self.resize[1] 204 | if width == -1: 205 | if self.word: 206 | w = height 207 | else: 208 | w = 240 209 | h = height 210 | else: 211 | w = height 212 | h = height 213 | return torch.randn(1, self.image_channel, h, w, device='cpu') 214 | 215 | def export_onnx(self, net, dummy_input, graph_path, input_names, output_names, dynamic_ax): 216 | torch.onnx.export(net, dummy_input, graph_path, export_params=True, verbose=False, 217 | input_names=input_names, output_names=output_names, dynamic_axes=dynamic_ax, 218 | opset_version=12, do_constant_folding=True) 219 | 220 | 221 | @staticmethod 222 | def load_checkpoint(path, device): 223 | param = torch.load(path, map_location=device) 224 | state_dict = param['net'] 225 | optimizer = param['optimizer'] 226 | # self.lr = param['lr'] 227 | # self.reset_optimizer(param['epoch']) 228 | # self.load_state_dict(state_dict) 229 | # self.optimizer.load_state_dict(optimizer) 230 | # return param['epoch'], param['step'], param['lr'] 231 | return param, state_dict, optimizer 232 | -------------------------------------------------------------------------------- /nets/backbone/__init__.py: -------------------------------------------------------------------------------- 1 | from .ddddocr import * 2 | from .effcientnet import * 3 | from .mobilenet import * -------------------------------------------------------------------------------- /nets/backbone/ddddocr/__init__.py: -------------------------------------------------------------------------------- 1 | from .ddddocrv1 import DdddOcr -------------------------------------------------------------------------------- /nets/backbone/ddddocr/ddddocrv1.py: -------------------------------------------------------------------------------- 1 | ''' 2 | 不记得从哪套模型改的了,可能来自于部门mobildenetv2 3 | ''' 4 | import torch 5 | import torch.nn as nn 6 | 7 | 8 | class DdddOcr(nn.Module): 9 | def __init__(self, nc=3, leakyRelu=False): 10 | super(DdddOcr, self).__init__() 11 | # assert imgH % 16 == 0, 'imgH has to be a multiple of 16' 12 | 13 | ks = [3, 3, 3, 3, 3, 3, 2] 14 | ps = [1, 1, 1, 1, 1, 1, 0] 15 | ss = [1, 1, 1, 1, 1, 1, 1] 16 | nm = [16, 32, 64, 64, 128, 128, 128] 17 | 18 | cnn = nn.Sequential() 19 | 20 | def convRelu(i, batchNormalization=False): 21 | nIn = nc if i == 0 else nm[i - 1] 22 | nOut = nm[i] 23 | cnn.add_module('conv{0}'.format(i), 24 | nn.Conv2d(nIn, nOut, ks[i], ss[i], ps[i])) 25 | if batchNormalization: 26 | cnn.add_module('batchnorm{0}'.format(i), nn.BatchNorm2d(nOut)) 27 | if leakyRelu: 28 | cnn.add_module('relu{0}'.format(i), 29 | nn.LeakyReLU(0.2, inplace=True)) 30 | else: 31 | cnn.add_module('relu{0}'.format(i), nn.ReLU(True)) 32 | 33 | convRelu(0) 34 | cnn.add_module('pooling{0}'.format(0), nn.MaxPool2d(2, 2)) # 64x16x64 35 | convRelu(1) 36 | cnn.add_module('pooling{0}'.format(1), nn.MaxPool2d(2, 2)) # 128x8x32 37 | convRelu(2, True) 38 | convRelu(3) 39 | cnn.add_module('pooling{0}'.format(2), 40 | nn.MaxPool2d((2, 2), (2, 1), (0, 1))) # 256x4x16 41 | convRelu(4, True) 42 | convRelu(5) 43 | cnn.add_module('pooling{0}'.format(3), 44 | nn.MaxPool2d((2, 2), (2, 1), (0, 1))) # 512x2x16 45 | convRelu(6, True) # 512x1x16 46 | 47 | self.cnn = cnn 48 | 49 | def forward(self, input): 50 | return self.cnn(input) 51 | 52 | def test(): 53 | net = DdddOcr(1) 54 | x = torch.randn(1, 1, 128, 128) 55 | y = net(x) 56 | print(y.size()) 57 | 58 | if __name__ == '__main__': 59 | test() -------------------------------------------------------------------------------- /nets/backbone/effcientnet/__init__.py: -------------------------------------------------------------------------------- 1 | from .efficientnetv2 import effnetv2_l, effnetv2_m, effnetv2_xl, effnetv2_s -------------------------------------------------------------------------------- /nets/backbone/effcientnet/efficientnetv2.py: -------------------------------------------------------------------------------- 1 | """ 2 | Creates a EfficientNetV2 Model as defined in: 3 | Mingxing Tan, Quoc V. Le. (2021). 4 | EfficientNetV2: Smaller Models and Faster Training 5 | arXiv preprint arXiv:2104.00298. 6 | import from https://github.com/d-li14/mobilenetv2.pytorch 7 | """ 8 | 9 | import torch 10 | import torch.nn as nn 11 | import math 12 | 13 | __all__ = ['effnetv2_s', 'effnetv2_m', 'effnetv2_l', 'effnetv2_xl'] 14 | 15 | 16 | def _make_divisible(v, divisor, min_value=None): 17 | """ 18 | This function is taken from the original tf repo. 19 | It ensures that all layers have a channel number that is divisible by 8 20 | It can be seen here: 21 | https://github.com/tensorflow/models/blob/master/research/slim/nets/mobilenet/mobilenet.py 22 | :param v: 23 | :param divisor: 24 | :param min_value: 25 | :return: 26 | """ 27 | if min_value is None: 28 | min_value = divisor 29 | new_v = max(min_value, int(v + divisor / 2) // divisor * divisor) 30 | # Make sure that round down does not go down by more than 10%. 31 | if new_v < 0.9 * v: 32 | new_v += divisor 33 | return new_v 34 | 35 | 36 | # SiLU (Swish) activation function 37 | if hasattr(nn, 'SiLU'): 38 | SiLU = nn.SiLU 39 | else: 40 | # For compatibility with old PyTorch versions 41 | class SiLU(nn.Module): 42 | def forward(self, x): 43 | return x * torch.sigmoid(x) 44 | 45 | 46 | class SELayer(nn.Module): 47 | def __init__(self, inp, oup, reduction=4): 48 | super(SELayer, self).__init__() 49 | self.avg_pool = nn.AdaptiveAvgPool2d(1) 50 | self.fc = nn.Sequential( 51 | nn.Linear(oup, _make_divisible(inp // reduction, 8)), 52 | SiLU(), 53 | nn.Linear(_make_divisible(inp // reduction, 8), oup), 54 | nn.Sigmoid() 55 | ) 56 | 57 | def forward(self, x): 58 | b, c, _, _ = x.size() 59 | y = self.avg_pool(x).view(b, c) 60 | y = self.fc(y).view(b, c, 1, 1) 61 | return x * y 62 | 63 | 64 | def conv_3x3_bn(inp, oup, stride): 65 | return nn.Sequential( 66 | nn.Conv2d(inp, oup, 3, stride, 1, bias=False), 67 | nn.BatchNorm2d(oup), 68 | SiLU() 69 | ) 70 | 71 | 72 | def conv_1x1_bn(inp, oup): 73 | return nn.Sequential( 74 | nn.Conv2d(inp, oup, 1, 1, 0, bias=False), 75 | nn.BatchNorm2d(oup), 76 | SiLU() 77 | ) 78 | 79 | 80 | class MBConv(nn.Module): 81 | def __init__(self, inp, oup, stride, expand_ratio, use_se): 82 | super(MBConv, self).__init__() 83 | assert stride in [1, 2] 84 | 85 | hidden_dim = round(inp * expand_ratio) 86 | self.identity = stride == 1 and inp == oup 87 | if use_se: 88 | self.conv = nn.Sequential( 89 | # pw 90 | nn.Conv2d(inp, hidden_dim, 1, 1, 0, bias=False), 91 | nn.BatchNorm2d(hidden_dim), 92 | SiLU(), 93 | # dw 94 | nn.Conv2d(hidden_dim, hidden_dim, 3, stride, 1, groups=hidden_dim, bias=False), 95 | nn.BatchNorm2d(hidden_dim), 96 | SiLU(), 97 | SELayer(inp, hidden_dim), 98 | # pw-linear 99 | nn.Conv2d(hidden_dim, oup, 1, 1, 0, bias=False), 100 | nn.BatchNorm2d(oup), 101 | ) 102 | else: 103 | self.conv = nn.Sequential( 104 | # fused 105 | nn.Conv2d(inp, hidden_dim, 3, stride, 1, bias=False), 106 | nn.BatchNorm2d(hidden_dim), 107 | SiLU(), 108 | # pw-linear 109 | nn.Conv2d(hidden_dim, oup, 1, 1, 0, bias=False), 110 | nn.BatchNorm2d(oup), 111 | ) 112 | 113 | def forward(self, x): 114 | if self.identity: 115 | return x + self.conv(x) 116 | else: 117 | return self.conv(x) 118 | 119 | 120 | class EffNetV2(nn.Module): 121 | def __init__(self, cfgs, nc=3, width_mult=1.): 122 | super(EffNetV2, self).__init__() 123 | self.cfgs = cfgs 124 | 125 | # building first layer 126 | input_channel = _make_divisible(24 * width_mult, 8) 127 | layers = [conv_3x3_bn(nc, input_channel, 2)] 128 | # building inverted residual blocks 129 | block = MBConv 130 | for t, c, n, s, use_se in self.cfgs: 131 | output_channel = _make_divisible(c * width_mult, 8) 132 | for i in range(n): 133 | layers.append(block(input_channel, output_channel, s if i == 0 else 1, t, use_se)) 134 | input_channel = output_channel 135 | self.features = nn.Sequential(*layers) 136 | 137 | self._initialize_weights() 138 | 139 | def forward(self, x): 140 | x = self.features(x) 141 | return x 142 | 143 | def _initialize_weights(self): 144 | for m in self.modules(): 145 | if isinstance(m, nn.Conv2d): 146 | n = m.kernel_size[0] * m.kernel_size[1] * m.out_channels 147 | m.weight.data.normal_(0, math.sqrt(2. / n)) 148 | if m.bias is not None: 149 | m.bias.data.zero_() 150 | elif isinstance(m, nn.BatchNorm2d): 151 | m.weight.data.fill_(1) 152 | m.bias.data.zero_() 153 | elif isinstance(m, nn.Linear): 154 | m.weight.data.normal_(0, 0.001) 155 | m.bias.data.zero_() 156 | 157 | 158 | def effnetv2_s(**kwargs): 159 | """ 160 | Constructs a EfficientNetV2-S model 161 | """ 162 | cfgs = [ 163 | # t, c, n, s, SE 164 | [1, 24, 2, 1, 0], 165 | [4, 48, 4, 2, 0], 166 | [4, 64, 4, 2, 0], 167 | [4, 128, 6, 2, 1], 168 | [6, 160, 9, 1, 1], 169 | [6, 256, 15, 2, 1], 170 | ] 171 | return EffNetV2(cfgs, **kwargs) 172 | 173 | 174 | def effnetv2_m(**kwargs): 175 | """ 176 | Constructs a EfficientNetV2-M model 177 | """ 178 | cfgs = [ 179 | # t, c, n, s, SE 180 | [1, 24, 3, 1, 0], 181 | [4, 48, 5, 2, 0], 182 | [4, 80, 5, 2, 0], 183 | [4, 160, 7, 2, 1], 184 | [6, 176, 14, 1, 1], 185 | [6, 304, 18, 2, 1], 186 | [6, 512, 5, 1, 1], 187 | ] 188 | return EffNetV2(cfgs, **kwargs) 189 | 190 | 191 | def effnetv2_l(**kwargs): 192 | """ 193 | Constructs a EfficientNetV2-L model 194 | """ 195 | cfgs = [ 196 | # t, c, n, s, SE 197 | [1, 32, 4, 1, 0], 198 | [4, 64, 7, 2, 0], 199 | [4, 96, 7, 2, 0], 200 | [4, 192, 10, 2, 1], 201 | [6, 224, 19, 1, 1], 202 | [6, 384, 25, 2, 1], 203 | [6, 640, 7, 1, 1], 204 | ] 205 | return EffNetV2(cfgs, **kwargs) 206 | 207 | 208 | def effnetv2_xl(**kwargs): 209 | """ 210 | Constructs a EfficientNetV2-XL model 211 | """ 212 | cfgs = [ 213 | # t, c, n, s, SE 214 | [1, 32, 4, 1, 0], 215 | [4, 64, 8, 2, 0], 216 | [4, 96, 8, 2, 0], 217 | [4, 192, 16, 2, 1], 218 | [6, 256, 24, 1, 1], 219 | [6, 512, 32, 2, 1], 220 | [6, 640, 8, 1, 1], 221 | ] 222 | return EffNetV2(cfgs, **kwargs) 223 | 224 | def test(): 225 | net = effnetv2_s(nc=1) 226 | x = torch.randn(1, 1, 128, 128) 227 | y = net(x) 228 | print(y.size()) 229 | 230 | 231 | if __name__ == '__main__': 232 | test() -------------------------------------------------------------------------------- /nets/backbone/mobilenet/__init__.py: -------------------------------------------------------------------------------- 1 | from .mobilenetv2 import mobilenetv2 2 | from .mobilenetv3 import MobileNetV3_Small, MobileNetV3_Large -------------------------------------------------------------------------------- /nets/backbone/mobilenet/mobilenetv2.py: -------------------------------------------------------------------------------- 1 | """ 2 | Creates a MobileNetV2 Model as defined in: 3 | Mark Sandler, Andrew Howard, Menglong Zhu, Andrey Zhmoginov, Liang-Chieh Chen. (2018). 4 | MobileNetV2: Inverted Residuals and Linear Bottlenecks 5 | arXiv preprint arXiv:1801.04381. 6 | import from https://github.com/tonylins/pytorch-mobilenet-v2 7 | """ 8 | 9 | import torch.nn as nn 10 | import math 11 | 12 | __all__ = ['mobilenetv2'] 13 | 14 | 15 | def _make_divisible(v, divisor, min_value=None): 16 | """ 17 | This function is taken from the original tf repo. 18 | It ensures that all layers have a channel number that is divisible by 8 19 | It can be seen here: 20 | https://github.com/tensorflow/models/blob/master/research/slim/nets/mobilenet/mobilenet.py 21 | :param v: 22 | :param divisor: 23 | :param min_value: 24 | :return: 25 | """ 26 | if min_value is None: 27 | min_value = divisor 28 | new_v = max(min_value, int(v + divisor / 2) // divisor * divisor) 29 | # Make sure that round down does not go down by more than 10%. 30 | if new_v < 0.9 * v: 31 | new_v += divisor 32 | return new_v 33 | 34 | 35 | def conv_3x3_bn(inp, oup, stride): 36 | return nn.Sequential( 37 | nn.Conv2d(inp, oup, 3, stride, 1, bias=False), 38 | nn.BatchNorm2d(oup), 39 | nn.ReLU6(inplace=True) 40 | ) 41 | 42 | 43 | def conv_1x1_bn(inp, oup): 44 | return nn.Sequential( 45 | nn.Conv2d(inp, oup, 1, 1, 0, bias=False), 46 | nn.BatchNorm2d(oup), 47 | nn.ReLU6(inplace=True) 48 | ) 49 | 50 | 51 | class InvertedResidual(nn.Module): 52 | def __init__(self, inp, oup, stride, expand_ratio): 53 | super(InvertedResidual, self).__init__() 54 | assert stride in [1, 2] 55 | 56 | hidden_dim = round(inp * expand_ratio) 57 | self.identity = stride == 1 and inp == oup 58 | 59 | if expand_ratio == 1: 60 | self.conv = nn.Sequential( 61 | # dw 62 | nn.Conv2d(hidden_dim, hidden_dim, 3, stride, 1, groups=hidden_dim, bias=False), 63 | nn.BatchNorm2d(hidden_dim), 64 | nn.ReLU6(inplace=True), 65 | # pw-linear 66 | nn.Conv2d(hidden_dim, oup, 1, 1, 0, bias=False), 67 | nn.BatchNorm2d(oup), 68 | ) 69 | else: 70 | self.conv = nn.Sequential( 71 | # pw 72 | nn.Conv2d(inp, hidden_dim, 1, 1, 0, bias=False), 73 | nn.BatchNorm2d(hidden_dim), 74 | nn.ReLU6(inplace=True), 75 | # dw 76 | nn.Conv2d(hidden_dim, hidden_dim, 3, stride, 1, groups=hidden_dim, bias=False), 77 | nn.BatchNorm2d(hidden_dim), 78 | nn.ReLU6(inplace=True), 79 | # pw-linear 80 | nn.Conv2d(hidden_dim, oup, 1, 1, 0, bias=False), 81 | nn.BatchNorm2d(oup), 82 | ) 83 | 84 | def forward(self, x): 85 | if self.identity: 86 | return x + self.conv(x) 87 | else: 88 | return self.conv(x) 89 | 90 | 91 | class MobileNetV2(nn.Module): 92 | def __init__(self, nc=3, width_mult=1.): 93 | super(MobileNetV2, self).__init__() 94 | # setting of inverted residual blocks 95 | self.cfgs = [ 96 | # t, c, n, s 97 | [1, 16, 1, 1], 98 | [6, 24, 2, 2], 99 | [6, 32, 3, 2], 100 | [6, 64, 4, 2], 101 | [6, 96, 3, 1], 102 | [6, 160, 3, 2], 103 | [6, 320, 1, 1], 104 | ] 105 | 106 | # building first layer 107 | input_channel = _make_divisible(32 * width_mult, 4 if width_mult == 0.1 else 8) 108 | layers = [conv_3x3_bn(nc, input_channel, 2)] 109 | # building inverted residual blocks 110 | block = InvertedResidual 111 | for t, c, n, s in self.cfgs: 112 | output_channel = _make_divisible(c * width_mult, 4 if width_mult == 0.1 else 8) 113 | for i in range(n): 114 | layers.append(block(input_channel, output_channel, s if i == 0 else 1, t)) 115 | input_channel = output_channel 116 | self.features = nn.Sequential(*layers) 117 | 118 | self._initialize_weights() 119 | 120 | def forward(self, x): 121 | x = self.features(x) 122 | return x 123 | 124 | def _initialize_weights(self): 125 | for m in self.modules(): 126 | if isinstance(m, nn.Conv2d): 127 | n = m.kernel_size[0] * m.kernel_size[1] * m.out_channels 128 | m.weight.data.normal_(0, math.sqrt(2. / n)) 129 | if m.bias is not None: 130 | m.bias.data.zero_() 131 | elif isinstance(m, nn.BatchNorm2d): 132 | m.weight.data.fill_(1) 133 | m.bias.data.zero_() 134 | elif isinstance(m, nn.Linear): 135 | m.weight.data.normal_(0, 0.01) 136 | m.bias.data.zero_() 137 | 138 | def mobilenetv2(**kwargs): 139 | """ 140 | Constructs a MobileNet V2 model 141 | """ 142 | return MobileNetV2(**kwargs) 143 | -------------------------------------------------------------------------------- /nets/backbone/mobilenet/mobilenetv3.py: -------------------------------------------------------------------------------- 1 | '''MobileNetV3 in PyTorch. 2 | See the paper "Inverted Residuals and Linear Bottlenecks: 3 | Mobile Networks for Classification, Detection and Segmentation" for more details. 4 | 5 | import from https://github.com/xiaolai-sqlai/mobilenetv3/blob/master/mobilenetv3.py 6 | ''' 7 | import torch 8 | import torch.nn as nn 9 | import torch.nn.functional as F 10 | from torch.nn import init 11 | 12 | 13 | class hswish(nn.Module): 14 | def forward(self, x): 15 | out = x * F.relu6(x + 3, inplace=True) / 6 16 | return out 17 | 18 | 19 | class hsigmoid(nn.Module): 20 | def forward(self, x): 21 | out = F.relu6(x + 3, inplace=True) / 6 22 | return out 23 | 24 | 25 | class SeModule(nn.Module): 26 | def __init__(self, in_size, reduction=4): 27 | super(SeModule, self).__init__() 28 | self.se = nn.Sequential( 29 | nn.AdaptiveAvgPool2d(1), 30 | nn.Conv2d(in_size, in_size // reduction, kernel_size=1, stride=1, padding=0, bias=False), 31 | nn.BatchNorm2d(in_size // reduction), 32 | nn.ReLU(inplace=True), 33 | nn.Conv2d(in_size // reduction, in_size, kernel_size=1, stride=1, padding=0, bias=False), 34 | nn.BatchNorm2d(in_size), 35 | hsigmoid() 36 | ) 37 | 38 | def forward(self, x): 39 | return x * self.se(x) 40 | 41 | 42 | class Block(nn.Module): 43 | '''expand + depthwise + pointwise''' 44 | 45 | def __init__(self, kernel_size, in_size, expand_size, out_size, nolinear, semodule, stride): 46 | super(Block, self).__init__() 47 | self.stride = stride 48 | self.se = semodule 49 | 50 | self.conv1 = nn.Conv2d(in_size, expand_size, kernel_size=1, stride=1, padding=0, bias=False) 51 | self.bn1 = nn.BatchNorm2d(expand_size) 52 | self.nolinear1 = nolinear 53 | self.conv2 = nn.Conv2d(expand_size, expand_size, kernel_size=kernel_size, stride=stride, 54 | padding=kernel_size // 2, groups=expand_size, bias=False) 55 | self.bn2 = nn.BatchNorm2d(expand_size) 56 | self.nolinear2 = nolinear 57 | self.conv3 = nn.Conv2d(expand_size, out_size, kernel_size=1, stride=1, padding=0, bias=False) 58 | self.bn3 = nn.BatchNorm2d(out_size) 59 | 60 | self.shortcut = nn.Sequential() 61 | if stride == 1 and in_size != out_size: 62 | self.shortcut = nn.Sequential( 63 | nn.Conv2d(in_size, out_size, kernel_size=1, stride=1, padding=0, bias=False), 64 | nn.BatchNorm2d(out_size), 65 | ) 66 | 67 | def forward(self, x): 68 | out = self.nolinear1(self.bn1(self.conv1(x))) 69 | out = self.nolinear2(self.bn2(self.conv2(out))) 70 | out = self.bn3(self.conv3(out)) 71 | if self.se != None: 72 | out = self.se(out) 73 | out = out + self.shortcut(x) if self.stride == 1 else out 74 | return out 75 | 76 | 77 | class MobileNetV3_Large(nn.Module): 78 | def __init__(self, nc=3): 79 | super(MobileNetV3_Large, self).__init__() 80 | self.conv1 = nn.Conv2d(nc, 16, kernel_size=3, stride=2, padding=1, bias=False) 81 | self.bn1 = nn.BatchNorm2d(16) 82 | self.hs1 = hswish() 83 | 84 | self.bneck = nn.Sequential( 85 | Block(3, 16, 16, 16, nn.ReLU(inplace=True), None, 1), 86 | Block(3, 16, 64, 24, nn.ReLU(inplace=True), None, 2), 87 | Block(3, 24, 72, 24, nn.ReLU(inplace=True), None, 1), 88 | Block(5, 24, 72, 40, nn.ReLU(inplace=True), SeModule(40), 2), 89 | Block(5, 40, 120, 40, nn.ReLU(inplace=True), SeModule(40), 1), 90 | Block(5, 40, 120, 40, nn.ReLU(inplace=True), SeModule(40), 1), 91 | Block(3, 40, 240, 80, hswish(), None, 2), 92 | Block(3, 80, 200, 80, hswish(), None, 1), 93 | Block(3, 80, 184, 80, hswish(), None, 1), 94 | Block(3, 80, 184, 80, hswish(), None, 1), 95 | Block(3, 80, 480, 112, hswish(), SeModule(112), 1), 96 | Block(3, 112, 672, 112, hswish(), SeModule(112), 1), 97 | Block(5, 112, 672, 160, hswish(), SeModule(160), 1), 98 | Block(5, 160, 672, 160, hswish(), SeModule(160), 2), 99 | Block(5, 160, 960, 160, hswish(), SeModule(160), 1), 100 | ) 101 | 102 | self.conv2 = nn.Conv2d(160, 960, kernel_size=1, stride=1, padding=0, bias=False) 103 | self.bn2 = nn.BatchNorm2d(960) 104 | self.hs2 = hswish() 105 | self.linear3 = nn.Linear(960, 1280) 106 | self.bn3 = nn.BatchNorm1d(1280) 107 | self.init_params() 108 | 109 | def init_params(self): 110 | for m in self.modules(): 111 | if isinstance(m, nn.Conv2d): 112 | init.kaiming_normal_(m.weight, mode='fan_out') 113 | if m.bias is not None: 114 | init.constant_(m.bias, 0) 115 | elif isinstance(m, nn.BatchNorm2d): 116 | init.constant_(m.weight, 1) 117 | init.constant_(m.bias, 0) 118 | elif isinstance(m, nn.Linear): 119 | init.normal_(m.weight, std=0.001) 120 | if m.bias is not None: 121 | init.constant_(m.bias, 0) 122 | 123 | def forward(self, x): 124 | out = self.hs1(self.bn1(self.conv1(x))) 125 | out = self.bneck(out) 126 | out = self.hs2(self.bn2(self.conv2(out))) 127 | return out 128 | 129 | 130 | class MobileNetV3_Small(nn.Module): 131 | def __init__(self, nc=3): 132 | super(MobileNetV3_Small, self).__init__() 133 | self.conv1 = nn.Conv2d(nc, 16, kernel_size=3, stride=2, padding=1, bias=False) 134 | self.bn1 = nn.BatchNorm2d(16) 135 | self.hs1 = hswish() 136 | 137 | self.bneck = nn.Sequential( 138 | Block(3, 16, 16, 16, nn.ReLU(inplace=True), SeModule(16), 2), 139 | Block(3, 16, 72, 24, nn.ReLU(inplace=True), None, 2), 140 | Block(3, 24, 88, 24, nn.ReLU(inplace=True), None, 1), 141 | Block(5, 24, 96, 40, hswish(), SeModule(40), 2), 142 | Block(5, 40, 240, 40, hswish(), SeModule(40), 1), 143 | Block(5, 40, 240, 40, hswish(), SeModule(40), 1), 144 | Block(5, 40, 120, 48, hswish(), SeModule(48), 1), 145 | Block(5, 48, 144, 48, hswish(), SeModule(48), 1), 146 | Block(5, 48, 288, 96, hswish(), SeModule(96), 2), 147 | Block(5, 96, 576, 96, hswish(), SeModule(96), 1), 148 | Block(5, 96, 576, 96, hswish(), SeModule(96), 1), 149 | ) 150 | 151 | self.conv2 = nn.Conv2d(96, 576, kernel_size=1, stride=1, padding=0, bias=False) 152 | self.bn2 = nn.BatchNorm2d(576) 153 | self.hs2 = hswish() 154 | self.linear3 = nn.Linear(576, 1280) 155 | self.bn3 = nn.BatchNorm1d(1280) 156 | self.init_params() 157 | 158 | def init_params(self): 159 | for m in self.modules(): 160 | if isinstance(m, nn.Conv2d): 161 | init.kaiming_normal_(m.weight, mode='fan_out') 162 | if m.bias is not None: 163 | init.constant_(m.bias, 0) 164 | elif isinstance(m, nn.BatchNorm2d): 165 | init.constant_(m.weight, 1) 166 | init.constant_(m.bias, 0) 167 | elif isinstance(m, nn.Linear): 168 | init.normal_(m.weight, std=0.001) 169 | if m.bias is not None: 170 | init.constant_(m.bias, 0) 171 | 172 | def forward(self, x): 173 | out = self.hs1(self.bn1(self.conv1(x))) 174 | out = self.bneck(out) 175 | out = self.hs2(self.bn2(self.conv2(out))) 176 | 177 | return out 178 | 179 | 180 | def test(): 181 | net = MobileNetV3_Small() 182 | x = torch.randn(2, 3, 50, 224) 183 | y = net(x) 184 | print(y.size()) 185 | y = y.permute(3, 0, 1, 2) 186 | w, b, c, h = y.shape 187 | y = y.view(w, b, c * h) 188 | print(y.size()) 189 | 190 | if __name__ == '__main__': 191 | test() -------------------------------------------------------------------------------- /projects/__init__.py: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/sml2h3/dddd_trainer/5fd0d0b5bb83bf44a5692c9d253b7d928e05e673/projects/__init__.py -------------------------------------------------------------------------------- /projects/ddddocr/checkpoints/checkpoint_ddddocr-2022-2-28_6_2090000.tar: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/sml2h3/dddd_trainer/5fd0d0b5bb83bf44a5692c9d253b7d928e05e673/projects/ddddocr/checkpoints/checkpoint_ddddocr-2022-2-28_6_2090000.tar -------------------------------------------------------------------------------- /projects/ddddocr/config.yaml: -------------------------------------------------------------------------------- 1 | Model: 2 | CharSet: [' ', 猿, 灭, 鄗, 僤, 鞑, 孚, ─, 嘭, 阮, 婷, 搌, ‘, 镬, 莅, 朕, 溇, 砸, 蘊, 襁, 恻, 肢, 3 | 脚, 诿, 韬, 寕, 海, 勢, 嗵, 烔, 髪, 寅, 郎, 薮, 蝢, 岷, 队, 嗜, 簏, 順, 螫, 晥, 旋, 瑜, 犒, 趋, 膀, 4 | 鳎, 享, 禢, 扯, 餍, 穷, 铝, 麇, 癃, 彤, 茈, 9, 際, 倨, 渊, 臻, 杨, 抗, 伽, 汭, 迢, 棱, 屹, 伥, 芒, 5 | 臼, 薹, 缒, 靳, 夕, 叭, 铈, 颢, 萦, 逶, 秤, 撖, 翰, 伉, 駕, 镍, 硌, 鳙, 韓, 帑, 嵐, 您, 濞, 川, 麒, 6 | 鳯, 冑, 葱, 茱, 摘, 惕, 赶, 眍, 桧, 拜, 發, 桥, 棒, 止, 颗, 簃, 腑, 荫, 宠, '''', 髙, ä, 珥, 楊, 7 | 蝟, 閣, 阜, 欃, 毵, 疢, 篾, 俬, 众, 疯, 胙, 烂, 铮, 銭, 绌, 牛, 沂, 离, √, 岐, 沫, 鳞, 词, 絜, 镊, 8 | 毂, 唝, 苹, 啬, 苌, E, 颙, 脬, 牙, 宴, 秸, 楽, 袗, 梏, 绥, 垱, 挫, 摔, 劲, 亳, 电, 灯, 引, 花, 毙, 9 | 睪, 揽, 忾, 打, 從, 抠, 阼, 梓, 粉, 率, 熙, 命, 叹, 姮, 劙, 邦, 阇, 次, 姓, 槜, 陇, 搁, 藩, 苏, 倒, 10 | 菜, 绫, 掀, 扥, 廣, 恃, 儡, 唿, 憧, 皱, 珍, 跎, 鸶, 雕, 岖, 碣, 祘, 缤, 捧, 钀, 飨, 介, 箩, 囟, 徭, 11 | 陡, 揍, 岑, '=', 鞮, 腐, 拢, β, 澧, 婄, 主, 癖, 鈉, 糈, 谳, 买, 碹, 酏, 饟, 讣, 瞈, 临, 驩, 销, 12 | 审, 趱, 鮮, 饲, 髌, 遵, 庐, 焦, 洛, 哐, 甓, 虞, 萏, 岬, 妄, 杼, 规, 窣, 更, 慢, 掏, 娠, 匦, 莸, 撙, 13 | z, 霣, 籍, 措, 錎, 炜, 椒, 浡, 鳌, 子, 苫, 腠, 奠, 委, 猕, 挢, 鳈, 陛, 鲾, 膜, 圣, 浯, 在, 伞, 榟, 14 | …, 俇, 湨, 泣, 亹, 县, 倭, 皓, 芩, 瑛, 檗, 郚, 浜, 憾, 專, 丰, 葴, 狭, 恺, 砑, 襕, 慒, 霍, 狸, 郤, 15 | 颛, 斫, 柝, 铡, 遄, 赦, 赴, 影, 衅, 郊, 忐, 蹽, 褿, 蹐, 途, 绖, 笕, 块, 轹, 鳆, 蛭, 睐, 蜔, 涤, 馕, 16 | 鄭, 瓣, 源, 暖, 璋, 翔, 令, 扭, 糠, 刀, 榈, 羁, 血, 拽, 玖, 楱, 蚂, 曛, 兔, 丕, 森, 伷, 颼, 砉, 奘, 17 | 缳, 艮, 漉, 鉏, 蟋, 捽, 邈, 啡, 俄, 佞, 荀, 绽, 卺, +, 瑾, 餪, 蔚, 嫜, 恩, 错, 位, ', 昃, 嘧, 铪, 18 | 通, 泖, 侏, 妍, 镯, 帼, 薛, 保, 莠, 瑀, 枸, 茛, 嵛, 铊, 狒, 師, 圻, 罗, 龃, 萑, 畲, 骙, 窸, 風, 濑, 19 | 嗦, 意, 悛, 旃, 晾, 无, 黠, 脁, 紫, 蛘, 劈, 锄, 剐, 鄢, 丫, 渭, 湲, 髃, 炙, 艘, 牖, 裙, 辑, 荨, 跻, 20 | ´, 樇, 黉, '*', 绝, 遐, 幛, 偏, 卲, 林, 僭, 钉, 捻, 赪, 桫, 痢, 盈, 糇, 星, 施, 廿, 莒, 編, 小, 21 | 畹, 泺, 妥, 执, 坫, 吁, 鸨, 剿, 罩, 什, 冈, 铋, 肺, 咱, 翩, 媳, 腎, 萜, 腔, 脶, 呛, 揭, Р, 呦, 忤, 22 | à, 妁, ′, 嘻, 踱, 汰, 痍, 螵, 矧, 仇, 烯, 慊, 套, 嘏, 旻, 爱, 萁, 嚬, 器, 醚, 仅, 詈, 鹋, 钔, 弛, 23 | 用, 枋, 蜣, 籼, 泃, 善, 啖, 婤, 棣, 殚, 寡, 愣, 転, 愠, 汩, 葒, 噱, 靈, 榔, 椑, 潼, 量, 笞, 谬, 瘟, 24 | 猜, 火, 夏, 曩, 岘, 鉵, 蜕, 钯, 告, 毓, è, 谏, 笫, 祥, 犍, 盎, 猱, 偬, 蓑, 宵, 衰, 翘, #, ◇, 铨, 25 | 妪, 镒, 榕, 羖, 政, 脓, 包, 獘, 鲽, 痋, 铠, 帷, 戬, 烬, 绘, 鈴, 挎, ①, 囿, 惹, 趼, 蚋, 唶, 睬, 堋, 26 | 赡, 叫, 亏, 店, 芜, 馓, 搀, 锡, 泐, 庇, 酢, 酸, 癸, ~, 树, 稔, 藕, 扑, 苒, V, 馼, 咸, 刻, 涧, 镜, 27 | 漦, 建, 氨, 默, 栋, 畼, 姹, 钪, 炟, 吉, 选, ↓, 夀, 炒, 蹈, 缸, 飚, +, 妯, 軒, 訂, 徽, 牲, 从, 殻, 28 | 粞, 洹, 朰, 诅, 诸, 臧, 钿, 瞥, 衣, 牂, 蚺, 滓, 嘡, 第, 蚱, 势, 轻, 詹, 怏, 唾, 拚, 脏, 颁, 跹, 乳, 29 | 愀, 辇, 摅, 驾, 异, 沕, 沼, r, 沨, 装, 髭, 袍, 峁, 缂, 女, 猬, 合, 辈, 袴, 愛, 椎, 遶, 镲, 符, 韨, 30 | 鞭, 汊, 砷, 伾, 鲇, 怼, 播, 帙, 鄌, 呷, 硐, 纽, ⑨, 設, 達, 羧, 猛, ⒉, 滘, 帏, '2', 堆, 险, 髅, 31 | 阔, 匝, 畈, 戾, 溱, 莪, 哨, 痿, 鱿, 袁, 迟, 笥, 膚, 奚, 宜, 畛, 旮, 惫, 奋, 豊, 讯, 娡, 讠, 绕, 過, 32 | 醐, 邠, 匣, 劁, 咐, 祚, 隙, 娌, 禚, 舡, 荠, 啧, 贱, 皂, 捆, 嵋, 卉, 牝, 晚, 狩, 埘, 觫, 仂, 哦, 狨, 33 | 蛉, 鬻, 樣, 皎, 驽, 叔, 氽, 拍, 佶, 泯, 埃, 珂, 瞾, 似, 迈, 刈, 牵, 泌, 恚, 開, 罱, 赉, 糁, 蘖, 诟, 34 | 塘, 预, 塬, 挞, 糕, 淤, 苁, 咫, 焐, 潦, 滥, 仕, 骄, 讵, 季, 復, 呸, 躯, 吮, 蓄, 上, 窗, 饯, 恙, 佴, 35 | 納, 毖, 塍, 濟, 鲎, 恂, 涛, 汝, 鄺, 旌, 篼, 屋, 構, 圜, 食, 捱, é, 被, 痱, 烜, 豁, 译, 畫, 畋, 彘, 36 | 芄, 鲀, 澙, 坻, 纥, 蛄, 咻, 仁, 贴, 鎚, 红, 葓, 绹, 贸, 荬, 萌, 累, 嗝, 鯭, 镐, 趄, 丹, 涌, 綉, 爷, 37 | 梦, 面, 苤, 晖, 圭, 骇, 视, 柰, 渍, 鲑, 鸣, 杵, 槠, 财, 剅, 明, 佘, 懂, 莘, 飕, 捐, 醉, 渌, 氍, 盘, 38 | 菁, 涑, 酌, 欤, 笆, 馇, 裘, 戛, 圖, 骖, 瞀, 某, 戌, 桕, 痨, 拇, 蹲, 爝, 宄, 畑, 蔺, 跶, 翙, 喟, 颔, 39 | 团, 泵, 画, 唧, 返, 呑, 拯, 诖, 鹫, 黄, 逯, 镓, 筝, 儋, 蒺, 巷, 雙, 烤, 哌, 钲, 嶪, 邂, 卣, 偿, 拃, 40 | 轰, ﹔, 豫, 螨, 荒, 擀, 巧, 慨, 压, 顒, 晗, 笙, 醳, 李, 镄, 榫, 稼, 祷, 铫, 怡, 萨, 签, 虬, 3, 皙, 41 | 即, 蘘, 怙, 凭, 駡, 踌, 衫, J, 穂, 班, 纟, 蜃, 蜉, 唬, 咡, 馎, 码, 芦, 琰, 镨, 錦, 鸵, 碌, 间, 玑, 42 | 症, 矮, 局, て, 腧, 陀, 螟, 牢, 怃, 舫, 覆, 赈, 荷, O, 涿, 揿, 槚, 请, 商, 桐, 脾, 瞳, 入, 埔, 蔡, 43 | 柄, 垭, →, 湊, 铯, 鸯, _, 扪, 茠, 翮, 抿, 氡, 希, 騠, 斤, 嗓, 携, 琛, 娲, 妖, 赝, u, 厦, 盼, 赜, 44 | 眨, 況, 吸, 捃, 筣, ↑, 皖, 剧, 绠, 倥, 乘, 趹, 苘, 练, 兵, 鹧, 東, 赋, 瑄, 窅, 鋼, 嫔, 殣, 闽, 殂, 45 | 庠, 贡, 欸, 哀, 萧, 斓, 蜀, ,, 築, 闇, 驴, 戒, 偈, 婀, 舌, 偾, 蹇, 试, 狗, 论, 栽, 崭, 痉, 佛, 童, 46 | 汶, 翚, 石, 怨, 园, 霎, 均, 橋, 寒, 搴, 恍, 远, 歆, 遣, 滏, 樵, 淇, 艚, 蹬, 堉, 阆, 耆, 颓, 果, 断, 47 | 捯, 慕, 盲, 蕴, 匀, 鲺, 伐, 莺, 路, 掭, 抍, 變, 淡, 侍, 专, 鼩, 钘, 阙, 兑, 稀, 蚤, 性, 姚, 婕, 滔, 48 | 髁, 升, 启, 醊, 耰, 扈, 统, 讥, 憯, 硝, 山, 暄, 居, 簿, 獾, 噫, 犰, 颅, 流, 膈, 欷, 呙, 玄, 陳, 賀, 49 | 医, 米, 饰, 励, 郦, 倍, 蜢, 俞, 徵, 谕, 帱, 诫, 瀘, 枞, 饽, 爸, 獠, 卧, °, 哉, 芬, 孛, 田, 窖, 诬, 50 | 檩, 涪, 钴, 浙, ┐, 踅, 峇, 馁, 覺, 诹, 荐, 泫, 铭, 语, 锧, 痄, 篚, 谦, 白, 郄, 母, 箕, 僬, 衾, ❤, 51 | 刃, 苋, 呲, 爲, 颃, 鱾, 鹜, 莳, 蚪, &, 陧, 飱, 坩, 圄, 狺, 矩, 蟜, 榻, 筷, 锥, 鞣, 耿, 锽, 蝥, 峙, 52 | 膦, 音, 嗬, 扢, 忡, 幞, 旳, 洟, 驶, 柞, 纛, '~', 机, H, 说, 嶝, 鹦, 筏, 绺, ⒁, D, 贶, 烷, 玷, 53 | 枘, 扁, 缘, 昱, 厉, 谒, 簸, 荽, 钐, 究, 忑, 炭, 稽, 嫪, 赇, 菌, 墨, 袆, 淝, 骋, 羔, 俳, 虹, 沾, 黇, 54 | 耵, 皿, 嚷, 疖, 崇, 禾, 嘬, 这, 芼, 扩, 躬, 阎, 庼, 姑, 傀, 婵, 躁, 放, 滚, 鄯, 废, 沛, О, 鞨, 勃, 55 | 积, 好, 萎, 嫒, 巿, 弢, 添, 瞎, 登, 课, 隆, 钦, 喷, 旇, 瀌, 羅, 刷, 侦, 鲤, 鲚, 節, 还, 锎, 奭, 慭, 56 | 牧, 吒, 羞, 刨, 徳, 咭, 怂, 伛, 池, 湉, 我, 诠, 吼, 惟, 侣, 泪, 杻, 鹀, 甑, 淘, 歁, 躺, 尺, 但, 痦, 57 | 腱, 张, 蹉, 熘, 护, 挽, 菊, 如, 爪, 龉, 漾, 几, 茬, 肋, 滕, K, 甙, 瓴, 濆, 宣, 江, 聲, 垓, 秧, 瞰, 58 | 罹, 講, 亲, 倩, 寄, 妇, 傍, 仄, 蠋, 幂, 颚, 厂, 温, 潮, 姗, Ⅰ, 鲏, 佻, 稲, 笑, 唔, 個, 陌, 曺, 之, 59 | 蟹, 稣, 甸, 靰, 蚩, 恿, 侧, 竽, 痛, 慶, 踟, 策, 辅, 網, 顿, 乐, 制, 谁, 筼, 鲉, 鸿, 燠, 茯, 邑, 荘, 60 | 婴, 荧, 陔, 稇, 珲, 篪, 褛, 寨, 滁, の, 骁, 挡, 疋, 霹, 厮, 姆, 苷, 蠕, 朔, 挲, 缙, 擦, 茭, 姘, 喑, 61 | 立, 酪, 很, 遒, 氛, 春, 苴, 哕, 续, 頉, 诐, П, 井, 哞, 裥, 液, 嵎, 雏, 壳, 垩, 舊, 秆, 療, 霜, 迂, 62 | 犹, 派, 舉, 俏, 懑, 溏, 瘪, 喘, 宸, 绒, 郭, 祇, 靺, 伤, 口, 际, 裟, 翯, 吊, 狄, 溋, 夺, 铧, 丸, 饨, 63 | 墉, 式, 楂, 切, 啦, 傫, 儙, 嫄, 嘤, 靠, 鹡, 玲, 柔, 闾, 凼, 不, 哝, 酥, v, 利, 骆, (, 俪, 沏, 蒌, 64 | 殒, 谫, 兒, 沧, 甄, 暧, 搠, 批, 隰, 曾, 蝣, 裳, 衬, 乚, 黡, 嘀, 状, 卢, 柽, 葫, ”, 锈, 询, 捩, 烘, 65 | 剪, 莫, 坏, 允, 肝, 種, 塌, 嘴, 泸, 靶, 葚, 荦, 咖, 本, 埂, 跃, 厍, 铻, 浪, 照, 仟, 轾, 桅, 裆, 诺, 66 | 縠, 硖, 硭, 贻, 悱, 樾, 隍, 啭, 疤, a, 癌, 袭, 跟, 踣, 赛, 拧, G, 邕, 皴, 裱, 榼, 轱, 嵚, 夫, 狉, 67 | 拮, 文, 萊, 些, 悃, 官, 记, 蚵, 蚁, 仝, 鞴, 耀, 阡, ━, 腆, 梻, 棁, 枨, 偶, 谙, 砼, 豸, 黔, 汳, 腽, 68 | 诀, 毗, 壁, 歉, 钽, 皑, 儇, '#', 鏊, 宅, 缑, 仞, 鏂, 蝻, 馒, 轧, 啮, 枰, 栊, 簉, 磛, ∶, 琼, 啱, 69 | 湎, 瘭, 栌, 钷, 味, 徨, 歼, 萭, 慙, 萍, 渔, 鞠, 盗, 茳, P, 艇, 悔, 撕, 玙, j, 巢, 極, 岽, 玢, 煸, 70 | 旬, 睛, 扔, 踵, 屏, 稷, 釘, 菟, 涰, 粟, 仍, 煽, 掉, 庙, 蛞, 鳜, 古, 孝, 鉄, 骗, 琵, 庭, 戆, 骨, 琦, 71 | 港, 偻, 狞, 恒, 崔, 痫, 伯, 斄, 锆, 朓, 鹠, 匍, 菱, 恰, 噀, ⒄, 钩, 笺, 關, 踺, 资, 氙, 鹁, 侔, 梯, 72 | 罄, 塮, 出, 庡, 浓, 哽, 系, 聚, 荜, 沤, 鲢, 辋, 閥, 炽, 亢, 塔, ⑩, 邬, 淋, 薤, 抔, 诡, 槲, 許, ‰, 73 | 厕, 鲨, 雲, 兴, 胩, 後, 丢, 袄, 觎, 毪, 秷, 臁, 塄, 龅, 贪, 藏, 蒂, 忸, 簦, 踬, 弱, 樭, 储, 濱, 踮, 74 | 嗡, 耨, 绊, 鲭, 葹, 桞, 鷄, 涫, 兠, 鳕, 逞, 义, 伦, 箪, 诈, 编, 姬, 顼, 萘, 潴, 喁, 蛱, 貉, 榉, 捍, 75 | 唑, 绸, 败, 发, 酱, 衡, 冽, 侠, 妗, 幡, 帡, 铴, 唛, 旰, 杂, 甜, 貋, 攒, 嘣, 掣, 馔, 柚, 诲, 秋, 揠, 76 | 辗, 露, 冷, 筑, 嗑, 册, ’, 顇, 普, 娱, 翟, 乒, 虱, 説, 绯, 死, 襙, 钢, 抽, 圊, 寵, 骒, 旅, 擂, 乾, 77 | 湃, 税, 撬, 枕, 尧, 痳, 亿, 没, 慝, 铉, 迓, 舴, 蛇, 铃, 奸, 辆, 努, 讦, 碾, 褊, 芸, 哚, 唣, 轨, 痪, 78 | 托, 款, 篓, 烩, 刎, 踝, 祠, 提, 贮, 燏, 丑, 镪, 塥, 葸, 潸, 鸷, 啥, 毹, 炷, 溃, g, 缎, ―, 縯, 夤, 79 | 深, 薜, 酹, 污, 腰, 球, 誉, 实, 芟, 韫, 蚊, 嚎, 椽, 藉, 堃, 郧, 總, 召, 沩, 蒤, 碜, 帇, 洇, 庚, 7, 80 | 厘, 骰, 抱, 唇, 偆, 丨, 簖, 腴, 涩, 穴, 竑, 譄, 剀, 堕, 镔, 株, 莰, 館, 鲌, 幌, B, 雒, 事, 才, '?', 81 | 乍, 汐, 偕, 镠, 俅, 罐, 霆, 狮, 鏢, 雯, 赅, 终, 兖, 诘, 燃, 账, 摸, 蔹, 铲, 怪, 滞, 谆, 鉫, 郝, 酷, 82 | 廐, 挠, 祉, 窑, 耐, 鲠, 沲, 焉, 眵, 腾, 娓, 指, 缟, 痭, 習, 茑, 媵, 权, 盔, 恪, 喻, 扳, 腩, 褓, 稗, 83 | 招, 趾, 蟾, 靛, 狎, 氚, 臱, 嘞, 晋, 玮, 邾, 娟, 膄, 蹂, 菝, 忿, '|', 祟, 董, 倘, 乱, 楮, 枢, 燴, 84 | 躞, 彳, 私, 杪, 篮, 浠, 公, 煤, 2, 嗯, 苎, 棰, 珽, 捅, 蔌, 晃, 糯, 鸳, 擞, 铌, 缱, 挤, 螃, 碎, 魁, 85 | 蟮, 膠, 嘅, 垚, 氐, 梱, 燎, 脖, 蹊, 暴, 蘭, 戮, 每, 熹, 亁, 哈, 専, 鞯, 瞋, 纱, 聱, 彭, 鼱, 钣, 架, 86 | 篱, 醛, 庥, 戏, 孔, 洪, 铐, 久, 蒗, 域, 佃, 圩, 犊, 犋, 維, 蓬, 彻, 泛, 岈, 睃, 嘁, 铁, 蕞, 磴, 粼, 87 | 霉, 应, 铹, 4, 帧, 纪, 彀, 纨, 镫, 缴, 铵, 莞, 傲, 優, 绰, 送, 蜒, 恨, 薷, 瘴, 翥, '&', 圏, 共, 88 | 代, 孰, 恬, 曼, 攘, 氆, 吗, 钤, 囚, 飔, 皤, 髓, 彦, 桄, 椐, 瑭, 祆, 短, 麯, 若, 名, 鈇, 偉, 葭, 厥, 89 | 洺, 垌, 贰, 诱, 捂, 箅, 跄, 椰, 眷, 旺, 晡, 颀, 磔, 掾, 讴, 镗, 炊, 瓯, 为, 犁, 咿, 遮, 屎, 汀, 崆, 90 | 筛, 惊, 瀍, 彖, 钭, 睒, 鄏, 郫, 世, 驿, 推, 点, 榦, 噌, 茨, 甘, 炅, 捷, 興, 茁, 三, 罔, 哺, 襦, 骚, 91 | 殉, 喹, 镑, 骺, 接, 醪, 茧, 桌, 冥, 荇, 旒, 丶, 暝, 眊, 邋, 炖, 恁, 涮, 君, 俘, 棼, 蕾, 愫, 嫠, 燹, 92 | 腌, 秽, 贫, —, 苯, 控, 蠼, 裢, 箐, 钕, 悚, 壅, 贝, 揾, 鳟, 禳, 菼, ™, 宽, 铜, 脯, 衉, 簟, 刊, 男, 93 | Ⓡ, 沟, 扅, 轿, 台, 轵, 虑, 功, 掇, 弟, 煺, 揉, 胱, 散, 圬, 茗, 郇, 跺, 雀, 瞵, 佳, 璞, 札, 仡, 怎, 94 | 龏, 货, 裸, 胖, o, 䝉, 识, 芈, 彬, 搅, 雠, 已, 妞, △, 参, 氪, 鸰, 盡, 诙, 问, 萸, 觅, 务, 泊, 拄, 95 | 热, 焱, 陟, 柃, 珩, 胗, 骶, 職, 佩, 槌, 搦, 颜, 娣, 沦, 穈, 朦, 郛, 罽, 纮, 锰, 芮, 旁, 忙, 蝗, 纬, 96 | 湯, 苍, 乜, 痞, 渎, 澶, 疫, 怯, 襻, 黥, 缪, 扶, 裴, 锭, 创, 乎, 臊, 粪, 徜, 邽, 亊, 谯, 倾, 谈, 茎, 97 | b, 欣, 镂, 叡, 經, 铘, 坪, 昆, 杌, 秉, 勤, 媸, 望, 伸, 粒, 係, 艺, 饬, ×, 鬣, '"', 假, 奕, 配, 98 | 嬖, 鞲, 趺, 菤, 謇, 蓓, 铂, 朐, 購, 泳, 诽, 脉, 灶, 况, 镁, 崦, 繄, 驻, 阁, 炎, 竖, 蠹, 鹄, 痊, 濩, 99 | 隈, 笸, 腢, 窄, ②, 赤, 昙, 竹, 腒, 馅, 熟, 牟, 工, 漂, 厾, 敷, 貌, 哟, 倜, 連, 齁, 臾, 囡, 昏, 矽, 100 | 雎, 仨, 驊, 尹, 伪, 褚, 呖, I, 梽, 企, 蘇, 孫, 姒, 院, 蹄, 绐, 堍, 貢, 濯, 碥, 氅, 禊, 锴, 昇, 卞, 101 | 仃, 镥, 磲, 涴, 馃, 协, 乙, 僮, 靓, 餮, 寓, 窟, 嫰, Й, 颞, 绻, 悖, 炳, 屉, 姿, 鬼, 菲, 砗, 丽, 螠, 102 | 婞, 晴, 鸤, 炝, 阌, 樱, 喊, 摽, 干, 撼, 匙, 黼, 銮, 睥, 跤, 牮, 芪, 国, 湟, 現, 汉, 镦, 焚, 熥, 胸, 103 | 余, 种, 挣, 镕, 楗, 炗, 菽, 圆, 嗞, 觊, 嗖, 颈, 旚, 梢, 顧, 凯, 糟, 锻, 邘, 昭, 劝, 雷, ⇋, 窳, 呆, 104 | 鹈, 教, 笳, 紡, 睢, 尿, 畵, 呕, 奍, 杠, 铕, 游, 掬, 退, 臃, 醯, 搽, 驯, 鲩, 篁, 耗, 痒, 姊, 术, 潽, 105 | 辔, 逐, 噍, 蹯, 鳍, ±, 鲧, 醌, 糌, 逆, 虢, 辐, 黛, 会, 撤, ², 鲅, 杋, 辽, 笃, 嚚, 态, 鹇, 看, 猎, 106 | 疃, 屙, m, 跛, 崩, 抡, 鋈, 鲳, 矾, 苣, 梁, 浑, 盹, -, 樽, 臛, 宁, 葳, 逋, 瘩, 扒, 絡, 磋, 联, 辀, 107 | 谣, 汜, 褙, 貊, 缠, 汗, 惋, Ë, 癔, 屑, 扼, 络, 棓, 芤, 佯, 蘸, 蔸, 赔, 摩, 溴, 涘, 烟, 蠖, 鹌, 镈, 108 | 釣, 缛, 桁, 荔, 嗌, 氧, 蕉, 匜, 黝, 糗, 傣, 坌, 畸, 鳓, 枧, 厩, 籀, 茓, 赎, 跗, 钟, 板, 獗, 毒, 陴, 109 | 蝎, 鲪, 谄, 霭, 苠, 淏, 阐, 倆, 玃, 欢, 卓, 淅, 徇, 件, 涉, 蹿, 估, 赟, 磷, 飾, 妮, 爭, 汴, 蛎, 扎, 110 | 杧, 饶, 瀛, 摄, 滨, 嗨, 咏, 刑, 琐, 鸼, 疥, 涠, ⑥, 俦, 膪, 郡, 催, 暲, 牍, 忽, 楝, 惑, 嗉, 拶, 饥, 111 | 奏, 聪, 麟, 溍, 酬, 蓝, 潤, 礶, 環, 淀, 餘, 啾, 除, 匋, 垫, 邴, 篥, 痼, 膨, 洳, 怵, 朽, 麃, 挦, 筲, 112 | 墅, 罢, 畦, 征, 骟, 叻, 澜, 捏, 卑, 涸, 湫, 拭, 秘, 瞽, 熛, 嬉, 鸺, 敕, 总, 撂, 婢, 夬, 禹, 袯, 跖, 113 | 瓒, 免, 轶, 冼, 樯, ÷, 逮, 仑, 济, 旗, 恶, 豆, 帕, 萝, 胾, 馊, 波, 麸, 獬, 柆, 冯, 讹, 楼, 尘, 艋, 114 | 寘, 烽, 匯, 杭, 郏, 锔, 赳, 烨, 煳, 锶, 窦, 韪, 灾, 璀, 婥, 燮, 滹, 维, 骥, 履, 鲁, 绡, 鸻, 葬, 犟, 115 | 幔, 仲, 驹, 渣, 吠, 凹, 簇, 辛, 鳐, 鄱, 帐, 蔟, 称, ), 战, 混, ❋, 畀, 跂, 臭, 孺, 茔, 据, 硅, 凖, 116 | 荡, 惴, 揣, 遴, 庞, 祯, 蛩, 押, 酴, 柿, 撞, 縢, 榚, 逧, 交, 亰, 肸, 洎, 斝, 鲱, 诶, 赏, 溆, 觞, 阗, 117 | 番, 疭, 憨, 咙, 揜, 殖, 坤, 恝, ⑦, 铬, 九, 墈, 拂, 沆, 砻, 诏, 躔, 购, 疑, 荼, '7', 毋, 瞄, 郿, 118 | 度, 矫, 泩, 炼, 庄, 平, 麽, 圧, 侩, 鸡, 斟, 記, '!', 懦, 澳, 粱, 泚, 玊, 諾, 焰, 嗤, 裀, 作, 元, 119 | 隅, 橘, 褫, 電, 芳, 遗, 忠, 锚, 幽, 屈, 绁, 冱, 酩, 峰, 窎, 茜, 洴, 凸, 搭, 剕, 爹, 矜, 檐, 侯, 敞, 120 | 糅, 蔽, 少, 菖, 瘁, 劬, 灌, 碑, 蹒, 惚, 崚, 骧, 宾, 格, 兆, 桎, 愎, 瞠, 陪, 雍, 幻, 软, 馀, i, 肫, 121 | 芍, 唠, 圈, 铺, 長, 營, 媒, s, 滢, 崂, 䀲, 曝, 妨, 铼, 捶, 持, 铥, 篷, 漁, 鹗, 觀, 哇, 斡, 劳, 箫, 122 | 向, 宏, 閃, 蕙, 勿, 测, 兄, 役, 劭, 撴, 订, 娼, 砭, 瘼, 处, 纫, 润, 榜, 杈, 户, 缵, 骛, 筐, 锢, 咋, 123 | 产, 鸟, 羡, 揞, 偷, 岗, 琯, 证, 闲, 镢, 毐, 黑, 筌, 稞, 尃, 蜚, 硁, 桨, 姁, 鲈, 颖, 忝, 惧, •, 蝇, 124 | 蕖, 蕰, 8, 壴, 琇, 甫, 狙, 膘, 薇, 澌, 痃, 喔, 歇, 蚴, 躐, 荻, 栉, 蛮, 闻, 迤, 涓, 漆, 差, 敝, 衩, 125 | 薯, 葉, 玦, 乔, 镋, 晓, 单, 鞍, 鲣, 喜, 洁, 啃, 「, 叼, ?, 恼, 锦, 搏, 抄, 鳑, 喧, 穿, 嶶, 浆, c, 126 | 鳳, 趿, 绑, 啁, 鳘, 疡, 趑, 獻, 铤, 钌, 洽, 呻, 检, 場, 門, 愈, 旸, 爬, 赍, 彝, 匈, 壤, 腈, 蜘, 硫, 127 | 珺, 灊, 阿, 厌, 純, 篑, 杖, 拐, 锓, 瘳, 筰, 帽, 芎, 值, 镀, 漏, ⒂, 棋, 乓, ︰, 警, 醮, 冏, 阑, 帻, 128 | 缬, 蘼, 移, 宗, 垛, 薰, 醋, 谰, 镖, 亥, 洋, 攀, 剁, 鬲, 链, 畎, 漭, 支, 嘛, 依, 坚, 蹓, 枇, 燘, 王, 129 | 鼍, 鲯, 奄, 浐, 晰, 宦, 匿, 快, 酡, 機, 煋, 颇, 苩, 尼, 最, 糸, 哧, 豉, 沬, 走, 嵩, 煲, 碧, 椱, 獍, 130 | 罕, 捡, 孙, 刚, 瞢, 缆, 眸, 眦, 蜎, 疸, 穄, 膺, 柜, 美, 爵, 〇, 晶, 阍, 闳, 稁, 翼, 陈, 再, 锺, 玛, 131 | 付, 粜, 琪, 悝, 嗄, 勍, 挨, 逭, 朊, 龆, 棂, 缧, ╱, 阳, 垡, 鋪, 惆, 空, 馈, 备, 霪, 奖, 原, 膻, 掺, 132 | 贞, 璧, 判, 牯, 蒉, 剃, 觱, 雁, 孑, 颏, 朋, 职, 悟, 隘, 蠓, R, 唢, 严, 踶, 腹, 艴, 盒, 倻, 胰, 、, 133 | 瑢, 孩, 埕, 囤, 咬, 梭, 妤, W, 猞, 墟, 申, 菡, 栀, 粢, '`', 聩, 且, 砟, 盟, 睦, 瞍, 侗, 箨, 阚, 134 | 办, 圯, 馌, 洄, 瑗, 廖, 佰, 嗣, 砂, 骏, 亡, 行, 艾, 缏, 怊, 邙, /, 婪, 揩, 杅, 歹, 剟, 鲋, 壶, 紗, 135 | 鲊, 始, 旭, 浏, 蹼, 谎, ←, 祎, 殽, 亓, 邡, 薄, 注, 耠, 话, 哳, 脸, 庤, 犯, 琊, 唏, 蠡, 承, 埼, 呱, 136 | 对, 蕲, 莨, 惩, 戊, 便, 宝, 桢, 鲆, 铚, 缯, 璜, 孥, 穹, 龄, 旷, 摛, 植, 萋, 磉, 峻, 吴, 抛, y, 弩, 137 | 轷, 与, 珰, 岸, 噼, 稳, 芊, 霰, 碴, ◆, 沙, 蝈, 漫, '8', 鼋, 茀, 枣, 蓼, 含, 索, 釆, 渖, 矍, ':', 138 | 泅, 唼, 鮕, 怕, 璟, 轪, 〔, 潏, 蝶, 甾, 色, 授, 殿, 瞩, 邀, 瑟, 雹, 将, 肟, 窨, 拒, 寞, 蹙, 翱, 锝, 139 | 瘛, 胝, 檑, 駹, 刭, 芋, 贵, 鸱, 洮, 踞, 咯, 仉, 麂, 嗽, 辖, 唯, 葑, 晩, 抖, 跏, 净, 蜍, 刽, 鞁, 蚡, 140 | 缺, 杓, 娥, 柯, 偎, 盐, 淖, 秒, 筠, 胃, 摧, 琅, 藁, 誊, 疌, 漕, 祝, Θ, 楦, 苪, 猫, ⑿, 酺, 淫, 殃, 141 | 嗥, 檎, 孓,  , 禅, 潜, 滫, 鼹, 槁, 韩, 砍, 誓, 栝, 笪, 既, 蛐, 噎, 纣, 帔, 脞, 狍, 圉, 阶, 逻, 虻, 142 | 楯, 餐, 晁, 讓, 脍, 鸢, 跨, r, 鼒, 蒡, 闿, 戽, 肐, 鼾, M, 殇, 宙, 鳝, 兿, 捒, 猾, 仙, 眈, 耥, 谮, 143 | 斑, 夷, 辊, 耦, 念, 纭, 潍, 胧, 魅, ●, 嘟, 淄, 猴, 蛸, 洵, '4', 肉, 琉, 皈, Λ, 莛, 吩, 毽, 滈, 144 | 潘, 舀, 儿, 烃, 冒, 禽, 循, 诮, 嫌, 卸, 猸, 曚, 冬, 鞘, 滇, 街, 陆, 俭, 1, 稍, 片, 濡, 厢, 苦, 么, 145 | 伧, 息, 龋, α, 汔, 阖, 耪, 挈, ③, ∩, 榨, 堪, 扦, 溶, 齿, 栓, 坳, 潟, 藤, 觚, 侓, 臌, 组, 闵, 进, 146 | 玟, 埶, 棹, 钞, 衿, 互, 冤, 妈, 啼, 贲, 浿, Z, 杲, 杏, 描, 沉, 硒, 珣, 宇, 浃, 亻, 楹, 骞, h, 邓, 147 | 绿, 日, 墙, 奂, 抒, 玎, 轮, 貘, 铛, 圪, 铸, 娄, 淦, 蓐, 全, 顾, 裾, 窀, 匐, 熵, 谤, 昊, 哿, 昽, 勒, 148 | 琢, 疮, 徝, 脱, 鳖, 关, 淑, 媪, 萱, 浸, 眭, 嫦, 屿, 咨, 痴, 蜇, 鬟, 抚, 佗, 蛟, 蒇, 摒, 卜, 它, 慗, 149 | 辕, 穰, 鍪, 擅, 犄, 掰, 腼, 爍, 扉, 锣, 噪, 杰, 悬, 鄜, 墒, 客, 槑, 誜, 蜗, 氇, 榷, 距, 肿, 诔, 绛, 150 | 勋, 越, 疏, 桔, 穝, 梅, 舟, 怄, 韵, 揳, 窕, 铱, 魭, 腥, 到, 栳, 独, 褥, 鲶, 缮, 嵘, 怜, 澈, 嚅, 阈, 151 | 苶, 袋, 《, 砝, 怅, 。, 核, 占, 完, 博, 氤, 槊, 有, 欲, 汧, 嚭, 渚, N, 霞, ɔ, 肠, 慈, 睿, 嘲, 鸭, 152 | 楫, 蚍, 贯, 僜, 鄣, 杆, 鳏, 曦, 讫, 咕, 缡, 條, 羹, 悦, 变, 撺, 示, 敛, 镏, 赖, 栏, 揎, 土, 汈, 锅, 153 | 幢, 今, 羮, 想, 臣, 屦, 溠, 啜, 鴨, 朝, 驼, 惝, 逼, 舰, 帚, 兀, 霖, 锘, 颡, 庳, 闼, 瘿, 殍, 蜻, 翠, 154 | 莹, 堤, 墀, 溻, 篆, 钵, 勺, 辰, 剡, ⑾, 骘, 檵, 迪, 鹱, 蜷, 垒, 候, 闟, 惯, 茄, 獴, 读, 四, 湔, 也, 155 | 6, 愬, 趔, 夡, 郎, 俨, 吆, 徒, 够, 遑, 帘, 冢, 璈, 鞋, 仳, 溦, 牞, 翻, 钎, 枯, ⑶, 真, 堠, 鲂, 谊, 156 | 篝, 辙, 抉, 亟, 奈, 蜜, 缈, 奡, 骈, 砧, 钍, 裝, 雩, 甍, 首, 阀, 缕, 喬, 颠, 壹, 啄, 舔, 黟, 嶽, 澥, 157 | 陽, 徕, 瘫, 戈, な, 湄, 鴻, 嘹, 嘘, 迮, 铀, 砮, 锜, 常, 柴, 旆, 扣, 咝, 得, 膂, 闫, 蓍, 酚, 動, 恧, 158 | 膳, 瓠, 饧, 邗, 悢, 解, 顶, 讲, 眉, 镭, 筅, 熊, 胡, 紅, 喂, 窜, 臜, 蛹, 铽, 煎, 韶, 秩, 騳, 骷, 朘, 159 | 熣, 捋, 届, 酇, 考, 刹, 懿, 席, 弭, 蒜, 嚣, 墩, 抨, 岫, 宓, 葶, 尔, 连, 旯, 泠, 太, 泰, 螂, 臺, 折, 160 | 檀, 阱, 間, 衲, 辟, 速, 焘, 槃, 㧎, 界, 耳, 富, 缭, 婉, 就, 膏, 結, 骑, 邃, 怿, 笊, 妆, 缦, 5, 栟, 161 | 峥, 趟, 熰, 墓, 栩, 馉, 柠, 悉, 挻, 僻, 奢, 螓, 譬, 犴, 妻, 曈, 吏, 曆, 笈, 轴, 馬, 槽, l, 稆, 骃, 162 | 嫘, 皲, 缁, 霈, 眩, 彰, 肾, 牒, 邳, 健, 冰, ﹖, 茌, 瑙, 傢, 杳, 卿, 场, 佑, 寮, 蚧, 姨, 昨, 谨, 楸, 163 | 里, 梳, 漠, 蚜, 囫, 所, 槍, 馐, 滋, 宛, 份, 劓, 带, 庾, 冶, 遢, 永, 媛, 遫, 聢, 裔, 澄, 僦, 礌, 淩, 164 | 垞, 鐘, 枳, 啸, 驮, 蒟, 孕, 等, 迁, 湘, 绗, 缣, 钻, 歧, 忭, 椿, 标, 郓, 壽, 旖, 堑, 芏, 様, 剞, 恣, 165 | 阊, 佧, 粮, 嚼, 勘, 鏡, 兹, 颋, 鳁, 币, 志, 获, 錾, 戡, 纔, 尖, 映, 嫱, 胬, 赊, 椴, 瓤, 踊, 窍, 學, 166 | 搂, 麿, 潞, 馄, 锒, 茉, 驱, 蠃, 奧, 袪, 輸, 银, ┅, 曰, 改, k, 〈, 瓢, 观, 叙, 栅, 舍, 荑, 鹞, 垦, 167 | '%', 镘, 嫫, 蕃, 疗, 瑪, 俺, 簋, 佐, 瞒, 陲, 樘, 革, 诼, 亵, 滪, 撵, 蒈, 楚, 檠, 担, 懵, 烺, 纸, 168 | 陋, 徂, 恸, 礞, 伺, 座, 前, 瞟, 缐, 啐, 唉, 袼, 约, 愉, 湝, 竴, 抓, 阏, 俛, 匹, 曷, 攥, 眢, 聘, 曜, 169 | 鴈, 蚯, 胪, 西, 筋, 瓿, 窭, 垤, 中, 黹, 讙, 鲐, 乃, 绀, 攉, 袂, 锹, 鹎, 糺, 藔, 狂, 诞, 蠲, 酮, 筒, 170 | ⑵, 牾, 饩, 颟, 瘰, 抬, 脽, 因, 揶, 邸, 酋, 逝, 犾, 泄, 鐵, 桤, 闷, 渝, 澹, 一, 轳, 豐, 绉, 娴, 邰, 171 | 蹑, 鄳, 昼, 鐾, 氣, 妫, 倕, 劍, 起, 绅, 傅, 焊, 厝, 贾, 融, 懈, 裉, 闪, 虎, 谩, 蓮, 潔, 淳, 衔, 茚, 172 | 囹, 鹣, 鬶, 裰, 翅, 癣, ﹐, 』, 违, 窂, 娅, 箴, 蛣, 搐, 噻, 祲, 捎, 怫, 钱, 萤, 唱, 咔, 沚, 曙, 翳, 173 | Ⅲ, Q, 桼, 蝰, 螬, 茝, 训, 淸, 挹, 锫, 硼, 舨, 要, 義, 骓, 掠, 枫, 睇, 匡, 龍, 肇, 直, 爆, 环, 蹢, 174 | 鐀, 钫, 岂, 何, 顸, 垆, 0, 螣, 阃, 狲, 螗, 鹿, ·, 荃, 抑, 莙, 幕, 材, 醤, 梾, 邪, 结, 静, 法, 零, 175 | 章, 篦, 曳, 皇, 构, 晌, 撄, 删, 礤, 鸬, 毛, 墼, 灸, 饷, 欺, 車, 麋, 樗, 醭, 烫, 营, 睨, 抟, 遨, 聃, 176 | 紧, 萬, 郴, 戢, 渺, 邯, 崎, 鍊, 遭, 鄒, 媽, 濋, 供, 促, 徼, 由, 郕, 惗, 钝, 骠, 鹝, 璆, 吻, 憔, 鑙, 177 | 顏, 庋, 纴, 溢, 當, 魃, 叽, 煨, 銀, 舁, 侉, 廷, 篃, 饸, 瞿, 缄, 撷, 髯, 玩, 桂, 苊, 惠, 瀦, 戴, 僚, 178 | 鸽, 娩, 士, 黍, 饺, 拴, 蚝, 囱, 镵, 槿, '9', 腚, 拟, 析, 蓊, 祖, 橼, 蒿, 泓, 徊, 垄, 唤, 倌, 菰, 179 | 祼, 檄, 禁, ⑧, 畏, 铳, 鹭, 粳, 勖, 械, 摊, 劢, 書, '6', 谲, 聍, 廓, 嵬, 遂, '>', 堌, 轩, 廰, 哼, 180 | 膑, 框, 喀, 魚, 洲, 纶, 臆, 滃, 嬴, 瞧, 呶, 魔, 峒, 疬, 算, 生, 歙, 褒, 虼, 浚, 匠, 筱, 镅, 克, 溯, 181 | 芰, 沅, 刖, 赘, 攫, 随, 孟, 纩, 郅, 成, 坜, 逵, 俚, 脐, 瘵, 窥, 坶, 蕺, 跑, 靥, 罪, 扫, 把, Ⅱ, 州, 182 | 賣, 访, 砒, 硕, 辜, 祢, 缗, 朿, 锯, 搪, 拔, 酯, 敌, 诨, 罂, 献, 蚕, 洃, 髻, 麓, 蹭, 哂, 沄, 䘵, 鹨, 183 | 潆, 迅, 箧, 嘚, 床, 疵, 兮, 紊, 硂, 盯, 攰, 袅, 轸, 磐, 弋, 桡, 岚, 陒, 缫, 妃, 递, 荞, 浈, 噙, 漳, 184 | 耕, 洙, 屠, 釜, 靽, 尉, 拎, 暂, 參, 鲬, 醵, 草, 螈, 盆, 础, 艟, 鹚, 崤, 重, 饹, 氕, 螋, 鼐, [, 计, 185 | 嵫, 瞻, 斐, 蟑, 受, 闱, 栈, 凛, 藿, 跫, 髂, 嫡, 斥, 苜, 觖, 蚰, 逊, 㭗, 螅, 求, 钬, 拈, 堅, 历, ò, 186 | 『, 薅, 龂, 搡, 挚, 牚, 祁, 獯, 渴, 礓, 牌, 喽, 挛, 碗, 畜, 射, 梧, 咆, 舜, 惶, 鞥, 觉, 赧, 复, 狈, 187 | 匳, 偌, 戕, 峧, 淮, ;, ., 戋, 赠, 刍, 缃, 兜, 频, 庆, V, 致, 夥, 锵, 悌, 往, 跞, 郰, 壑, 籴, 鼢, 188 | 嬗, 蛴, 鸾, 搛, 揖, 椠, 遹, 觇, 驷, 飧, 恢, 狼, 鹛, 貔, 蒎, 碉, 骢, 話, 雉, 窠, 喵, 劫, 碟, 碁, 群, 189 | 锉, 桠, 怆, 緣, 荸, 犏, 勚, 署, 汾, 勐, 儣, 蒸, 阢, 黏, 蓿, 揪, L, 焌, 臀, 䨱, 猊, 枚, 寐, 豭, 乕, 190 | 房, 雌, 犬, 曹, 洨, 龚, 裣, 柙, 具, 学, 嗲, 瘘, 特, 啵, 谗, 耜, 眼, 脲, 沘, 濂, 汨, 禮, 棚, 磬, 沽, 191 | 钇, 肃, 遁, 阪, 摁, 卯, 跌, 岁, 筇, 敦, 挪, 枥, 鼯, 项, 咤, 【, 蔬, 外, 歩, №, 鞫, 蒹, 肛, 晏, 郑, 192 | 轲, 龙, 调, 铣, 颦, 踧, 炉, 锸, 媲, 体, 艄, 谖, 谜, 笠, 灣, 固, 馗, 祾, '[', '}', 颍, 稱, 失, 坯, 193 | 噤, 愿, 吱, 麈, 超, 吨, 逑, 抵, 靡, 辌, 脆, ¥, 嚰, 扇, 忖, 柬, 㾄, 團, 蚣, 罴, 噉, 浮, 竦, 忏, 哑, 194 | 砾, 桀, 拌, 俟, 術, 嘢, 感, 律, 榖, 萼, 凱, 渫, 崟, 悠, 疔, 斋, 触, 镳, 绪, 郞, 楡, 箜, 襟, 㛃, 箍, 195 | 狁, ), 俗, 蘧, 业, ※, 攝, 捕, 瑰, 浥, 磅, Z, 栗, 內, 隋, 氖, 腻, 戳, 敫, 飒, 囵, 肄, 磺, 茼, 村, 196 | 德, 螺, 留, 沿, 吕, 藥, 剥, 诗, 显, 蝮, 髦, 茅, 吲, 翌, 約, 泱, 榴, 耶, 嗷, 妩, 氰, 凝, 觌, 稚, 檇, 197 | 俣, 簧, 惭, 赑, 纵, 沐, 裤, 苞, 邒, 韭, 睫, 滿, 跋, 庒, 芝, 埠, 怦, 爰, 劐, 栂, 埽, 益, 箬, 糜, 隶, 198 | 酉, 笔, 纡, 挺, 龁, 羟, 腿, 伢, 虮, 者, 嵴, 辁, 蔫, 億, 柏, 凇, ⒃, 厣, 鬏, 蔑, 噬, 玫, 廊, 徙, 屡, 199 | 娈, 眆, 萩, 鳂, 举, 揸, 喱, 饫, 后, 谑, 查, 彥, 南, 锐, 巴, ☆, 琳, 辩, 鼗, 啤, 聊, 瞌, 戎, 怒, 囊, 200 | 噶, 勰, 歔, 素, 呔, 蝄, 跳, 運, 斯, 卡, 骅, 翡, 拘, 殄, 天, 麺, 靖, 貅, 浕, 綮, 赙, 比, 珏, 炀, 诚, 201 | 溘, 葺, 刿, 觜, 柩, 只, 鳢, 瑯, 魏, 岭, 锛, 民, 缝, 郜, 豕, 礳, 秭, 尥, 嚯, 缜, 误, 待, 鑫, 隽, 饪, 202 | 揄, 梗, 膊, 午, 争, 塆, 俯, 茸, e, 鼻, 末, 適, 匾, 诉, 泍, 活, 馑, 隔, 廒, 焒, 丝, 飗, 咧, 〉, 侨, 203 | 凰, 奥, 镱, 拉, 暌, 琮, 言, 汤, 嵝, 多, 醍, 馏, 稻, R, 弦, 骂, 逄, 舆, 鸮, 蚶, 矅, 钒, 族, 汇, 笼, 204 | 醫, 柑, 肈, !, 谢, 猩, 嗟, 撅, 佈, 桃, 莽, 辉, 鹐, 樟, 河, 另, 麦, 骼, 併, 鱽, 姽, 嬃, 焅, 监, 割, 205 | 蔻, 较, 谀, 鄠, 麾, 卦, 蚨, 踔, ℃, 璎, 齐, 桦, 瘊, 于, 己, 唳, 踪, \, 肣, 自, 堵, 逦, 蜂, ≥, 疙, 206 | 谌, 淙, 溥, 砰, 壖, 啪, 髡, 铙, 殁, 檔, 魉, 酦, 喾, 忆, 僵, 吹, 载, 替, 谇, 芷, 巩, 镎, 恽, 凬, 耲, 207 | 酲, 靸, 蓠, 齮, 趴, 勉, 鳗, 谶, 茵, 迫, 赫, 先, 蹾, 障, 坠, 瞓, 邱, 癫, 舅, 乞, 贼, 菪, 呜, 谡, 飙, 208 | 牀, 胯, 碛, 疟, 航, 茆, 砚, 辄, 镤, 兰, 磁, 鼠, 鼬, 硚, 鹬, 雪, 陨, 怠, 光, 品, 钾, 叉, 夯, 虾, 畔, 209 | 拿, 住, 舾, 俸, 忒, 芑, 箦, 奎, 鹘, 肤, 籽, 千, 汪, 槭, 俐, 眬, 應, 棻, 癯, 臬, 堇, 焼, 逍, 杀, 偃, 210 | 呋, 皮, 聿, 皁, 驭, 氓, 鹂, 鳄, 肥, 穗, 痘, U, 皆, 痹, 遠, 煦, 苻, 呵, 犷, 趵, 锤, 价, 蜴, 挟, 陬, 211 | 胳, 彷, 浩, 侵, 臂, 篇, 车, 俩, 婊, 摹, 突, 鞧, 逛, 噜, 鞚, 尜, 风, 伶, 汹, 镞, 闹, 新, 忌, 炱, 耙, 212 | 怖, 庹, 呐, 囝, 蜈, 涣, 瘅, 蝽, 酤, 瀚, 览, 锕, 擐, 竣, 橥, 哥, 嫉, 盛, 脊, 芴, S, 塞, 铢, 酊, 眛, 213 | 雄, 會, 壕, 豹, 月, 传, 黜, 鄙, 鹕, ⑤, 咣, 瞬, 褀, 搢, 坟, 敏, 择, 潇, 甏, 颂, 孤, 農, 瀵, 噩, 凉, 214 | 紬, 锱, 弗, 票, 需, 篌, 荆, 浔, 匮, 羑, 句, 邮, 猃, 廉, 辂, 逅, 娇, 函, 耧, 黢, 鲰, 岛, 蝓, 懋, 箔, 215 | 鸦, 碲, 纤, 玻, 渡, 巫, 鹟, 岌, 觋, 岿, 淬, 搤, 爽, 墦, 豳, 著, 暇, 见, 酿, 谷, 馆, 亘, 悫, 心, 鲼, 216 | ^, 初, 涂, 舣, 碓, 屃, 遥, 鸥, 珅, 腺, 炫, 涡, 农, 㺃, 盖, 乩, 倓, 戗, 迸, 莜, 哭, 蹦, 舻, 括, 轺, 217 | 屺, 砹, 毅, 镰, 綦, 腊, 周, 咎, 鼫, 笄, 高, 時, 莴, 绋, w, 勻, 当, 觯, 鹊, 禀, 醅, 罨, 欠, 腦, 弼, 218 | 圠, 廠, 忱, 蕻, 悸, 督, 恅, 咀, 氢, 劃, 掘, 咪, 椀, 寂, 檬, 技, 汕, 嗍, 卖, 驳, 襜, ④, 悴, 磙, 鸩, 219 | 飯, 蜱, 氩, 湍, 侮, 耔, 患, 耑, 鲞, 廋, 鼓, 烦, 點, 擤, 嘶, 價, 香, 睹, 蜊, 染, 钰, ■, 對, 咄, 菂, 220 | 激, 乏, 筴, 浦, 煊, 伎, 集, 嫚, 输, 额, 畴, 雅, 棉, 正, 滙, 阉, 殪, 锾, 屐, 癀, 迩, 胴, 铅, 梃, 岵, 221 | 慎, 逃, 渲, 敖, 仺, 翊, 社, 杷, 愕, 寶, 鲦, 知, 寿, 猥, 贩, 拣, 鯨, 洌, 砀, 晔, 喃, 倫, n, 剽, 碱, 222 | 情, 愍, 燫, 像, 叁, 限, 垂, 睑, 舱, 墁, 盾, 扛, 弹, 阽, 刓, 精, 盥, 纻, 僰, 左, 掳, 方, 硎, 撑, 殷, 223 | 阅, 嵌, 肓, 绣, 巉, 坐, 诧, 疱, 掷, 嗔, 瘌, 开, 茇, 议, 筢, 腉, 莎, 盏, 物, 锋, 薪, a, 卵, 谘, 殆, 224 | 弯, 厨, 听, 诩, 栒, 螯, 回, 琴, 忍, 弄, 肆, 荤, 㐂, 奁, 嗾, 狝, 谸, 搞, 颌, 万, 摺, 饳, 滤, 铄, 糨, 225 | 锬, 昶, 滩, 呓, 豇, 彧, 师, 嚒, 遯, 埌, 炔, 骣, 佬, 铰, 馋, 籁, 夙, 胎, 殳, 婳, 壮, 长, 撇, 碶, 坝, 226 | 婶, 贽, 巂, ',', 婆, 珈, 廙, 辘, 莩, 青, 湿, 竟, 赁, 惜, 坑, 麝, 寖, 半, 绵, 搓, 乇, 隧, 刘, 羰, 227 | 费, 疆, 仰, 插, 莉, 胁, 熳, 腙, 隹, 遇, 膲, 歌, 鲫, 爾, 题, 级, 缲, 股, 曪, 淠, 胶, 翕, 峪, 瘤, 宕, 228 | 阒, 巳, 缰, 吾, 澍, ']', 帝, 来, 觏, 戥, 侃, 桊, 痰, 菔, 穸, 汲, 陵, 碇, 撻, 拷, 竺, 號, 仆, 晟, 229 | 渗, 護, 葛, U, 懜, 》, 笮, 寻, 圾, 借, 嶓, 態, 筘, 娑, 箸, 饭, 伻, 笱, 苗, 姥, 镌, 蓉, 缀, 充, 滦, 230 | 摇, 笨, 島, 泡, 丈, 诒, 助, 熔, ★, 羝, 斛, 陕, 骎, 掊, 縻, 榛, 瀑, 摭, 侪, 弒, 罿, 沪, 鳊, 阂, 租, 231 | 聂, 蔯, 蹩, '3', 鹑, 惬, 據, 麻, 闩, 垲, 褂, 潵, 蓇, 瞭, 军, 思, 浣, 弓, 粘, 栾, 堂, 泾, 柳, 搋, 232 | 喏, 讧, 城, 竞, 潋, 呤, 暮, 舄, 道, 藍, 呈, 力, 汁, 闰, 伊, 溟, 睁, 拨, 取, 鵰, 珑, 丧, 的, 酐, 鸫, 233 | 罅, 琬, 寸, 栎, 寺, 旦, ⒅, 䄂, 坭, 类, 锩, 镶, 截, 蛊, 闸, 疹, 娜, 粕, Я, 绲, 泽, 哙, 峭, 芹, 崮, 234 | 啴, 脑, 鹲, 碡, o, 岣, 阵, 眄, 脔, 蒲, 奴, 剎, 掩, 俶, 威, 笤, 黎, 辞, 匆, 質, 镣, 虍, 谱, 殊, 身, 235 | 嘿, 鲲, 胤, 武, 背, 缚, 攸, 评, 阻, 葩, 羲, 旎, 儆, 耱, 徹, 苑, 期, 椭, 惺, 哄, 僊, 伋, 啉, 嬢, 蓟, 236 | 垧, 钛, 傕, 府, '0', 达, 咴, 繁, 暾, 鲡, 谧, 绍, 茫, 瓷, 帜, 谠, 養, 祛, 癜, 信, 涝, 囐, 蓅, 懲, 237 | 枭, 舵, 嗛, 卫, 拼, 瘠, 瓦, 简, 癞, 嶲, 酒, 倞, 雜, 耷, 谥, X, 缷, 育, 兽, 渥, 呯, 沥, 愔, 哲, 踰, 238 | 馍, 卟, 呢, 疼, 灿, 埤, 琨, 汞, 螾, 诊, 熠, 蹀, 瓘, 龟, 沭, 瑶, 矸, 醴, 鲟, 簌, 蛳, 娶, 妓, 痈, 敢, 239 | 衢, 讳, 烈, 郗, 鸲, 让, '1', 是, 煙, 蹰, 阋, 肭, 蛏, 勇, 藝, 根, 孪, 褴, 卅, 例, 漓, 暅, 讽, 涔, 240 | 熨, 梨, 诛, 埗, 侥, 瘗, 悄, 榄, ., 境, 砺, 倡, 扰, 枉, 目, 蹻, 钏, 绨, 傩, 纺, (, 闌, 亨, 蒙, 洞, 241 | 禨, 仿, 羊, 桑, 畤, 裂, 瘐, 洼, 遛, 莓, 钜, G, 衄, 以, 敬, 誠, 橛, 鄄, 赂, 遆, 滠, 鲹, 诜, 尽, 瀹, 242 | d, 养, 觐, Ω, 旄, 疠, 黾, 樊, ≠, 魂, 珙, 蟠, 苔, 敲, 椁, 莶, 剔, 藻, 盤, 浊, 列, 蝾, 纯, 掖, 钊, 243 | 妲, 柘, 尊, 灞, 蟊, 困, 氦, 焯, '{', 吇, 钅, 垟, 飑, 赞, 蠊, 梆, 蒋, 瓞, 讷, 糍, 劾, 鼎, 粤, 碍, 244 | 鲮, 佪, 恕, 迄, 囍, 袷, 狳, 冠, 瑞, 堰, 剂, 褐, 礴, 飛, 啻, 饼, 榭, 垅, 康, 谔, 龀, 惦, 帅, 骐, 锷, 245 | 芙, 怍, 愦, 荥, 锗, 琚, 仗, 烁, 毯, 埝, 狯, 形, 覃, i, 嬷, 埸, 韧, 嵕, 珮, 察, 你, 茴, F, 痧, 榮, 246 | 園, 缨, 巾, 袒, 粑, 蟛, 溊, 裈, 赣, 扌, 駆, 堙, 防, 去, 钋, 爼, 瑚, 鉰, 梵, 羽, 蟒, 奔, 管, 铷, 昌, 247 | 钚, 镝, 蝌, 饦, 吓, 婿, 蘑, 喝, 骸, 燥, 猪, 概, 惘, 翎, 汛, 糥, 按, 瞪, 觳, 胀, 门, 蜓, 罚, 鹃, 啊, 248 | 庛, 岙, 网, 捭, 兼, 舛, 硗, 乂, 跷, 泞, 袈, 羚, 攻, 計, 蟥, 鼙, $, 芾, 榞, 菠, 赢, 债, 鄞, 贤, 摞, 249 | 啶, 嚄, 髹, 莆, 疽, 号, 闯, 拗, 毫, 绞, 棽, 冗, 菇, 雳, 钨, 史, 菩, 澴, 聒, 磊, 募, 钹, 髫, 悞, 珐, 250 | 骡, 猷, 創, 杯, 孵, 缓, 振, 膝, 玺, c, 野, 蹁, 趣, 倔, 洧, 习, 锟, 粹, 荛, 靴, 祗, 奶, 糊, 彊, 导, 251 | 酗, 蛑, 峨, 笏, 踉, 鞒, 啟, 契, 址, 煌, 醒, 嘎, 弃, 驺, 鳃, 绶, 蹴, 龠, 肴, 圹, 尓, 嫁, 蕤, 筹, 谓, 252 | 矣, 陉, 夢, 桶, 涯, 渑, 弈, 蔗, 蜿, 酆, 漤, 肪, 祺, 塗, 鲍, 认, 肽, 腮, 鬯, 俎, 佟, 沖, 胞, 隐, 那, 253 | 易, 肌, 畚, 尤, 庶, 礅, 馥, 绩, 祊, 匕, 幪, 纷, 酽, 宪, 哏, 睾, 弘, 附, 蔼, 尚, 舳, 谪, 傧, 宋, 舞, 254 | 呀, 宬, 其, 蝙, 砥, 醾, 濉, 跪, 良, 橇, 铍, 锊, 佤, 虫, 貝, 剌, 妒, 埭, 筫, 伙, 鴿, 低, 湛, 睡, 酃, 255 | 砜, 使, ⒆, 福, 眚, 孽, 郢, 综, 区, 褰, 歡, 崞, 宰, 翏, 鷪, 監, 毡, 叟, 朵, 贷, 姻, 残, p, 吃, 瓮, 256 | 巽, 骤, 岢, 潲, 哔, 咅, 玚, 幄, 垯, 央, 嘌, 昝, 撸, 织, 痤, 疍, 绮, 谚, 焻, 验, 粥, 邝, 轘, │, 犨, 257 | 薨, 延, 佣, 硪, 造, 鸪, 哮, 甬, 蛰, 序, 訇, 酞, 佼, 盂, 蟭, 贇, 線, 仔, 八, 俊, 胲, 蹋, 夔, 禇, 郂, 258 | 搬, 彩, 扺, 坢, ®, 撮, 呃, 圮, 魈, 渐, 锨, 骝, 龌, 忧, 驸, 步, 锌, 勾, 㙟, 蟪, 亩, 漪, 頭, 忮, 悻, 259 | 祓, 排, 荪, 纳, 趸, 钓, 副, 揆, 捞, 嚏, 谭, 样, 纰, 室, 爨, 飞, 證, 嫣, 瘦, 舸, |, 躇, 荩, 矶, 捲, 260 | 簪, 躲, 宥, 絪, 鍋, 悺, 珊, 夭, 隳, 淞, 蛲, 骍, 蜮, 釉, 救, 祭, 任, 鋆, 狠, 置, 晨, 服, 砌, 蓣, 饱, 261 | 饕, 豨, 骀, 镩, 耖, 加, 坛, 坷, 補, 狴, 镡, 蔓, 油, 吣, 蕹, 恐, 蛛, 砣, 柁, 窃, 墠, 唐, 絮, 芘, 钧, 262 | 谛, 栲, 觥, 猁, 傻, 䧳, 姝, 病, 眠, 涟, 氏, 跣, 图, 淼, 智, 殡, 浍, 茹, 榧, 椅, 津, 痣, 葡, 侬, ﹑, 263 | 蓺, 绤, 炮, 二, 细, 娉, 筻, 飐, 襆, 窴, 蚬, 竭, 封, 跚, 濛, 岍, 跆, 聋, 侑, 强, 莊, 怔, 肘, 釂, 剜, 264 | 圳, 谂, 甪, 橄, 恫, 眙, 衷, '5', 幤, 镇, 課, 峋, 家, 鹺, 涅, 癍, 水, 虿, 魇, 神, 馘, 蟞, 芭, 踽, 265 | 急, 都, 媚, 疾, 档, 鑽, 诓, 萄, 详, 芥, 磨, 嘈, 玉, 彼, 欻, 缞, 礻, 页, 剑, 停, 誤, 搒, 甥, 媢, 焗, 266 | 痔, 爺, 橙, 妣, 讪, 罾, 仫, 訄, 庸, 嚆, 丐, 蒽, 洑, 大, 穆, 睽, 蛔, 叵, 辢, 哒, 耸, 蟆, 肼, 茶, 属, 267 | 晦, 哩, 洗, 蛀, 逡, 桓, 钮, 顽, 畺, –, 劂, 颧, 蘩, 遊, 奉, 艿, 遽, 讨, 线, 辣, 裁, 搎, 逹, 则, 猢, 268 | 鳅, 刳, 粽, 饮, 崖, 繻, 狻, 个, 擎, 增, 捌, 然, 噢, '-', 劵, 撩, 銎, 璨, 尴, 盱, 洱, 帛, 哎, 倚, 269 | 吐, 演, 亍, 敓, 濒, 鹤, 弁, 枷, 珪, 眺, 虺, 铗, 贺, 夼, 跽, 嶷, 吞, 聆, 岩, 柱, 肷, 扊, 魆, t, 玳, 270 | 孜, 责, ⒀, 傎, 惨, 鏾, 凌, 湧, 郁, 羸, 龛, 棬, 朴, 離, 钳, 湖, 貂, 蝼, 竿, 矛, 仓, 鈺, 裕, 肯, 又, 271 | 时, 燧, 瘢, 杉, 獒, ﹒, 襞, 璇, 匪, 鬈, 虏, 湮, 两, 荟, 臑, 杞, 该, 巍, 疴, 坡, 琤, 省, 埙, 蠢, 未, 272 | “, 能, 边, 六, 伟, 冉, 耄, 愤, 逗, 秾, 漱, 螽, 屣, 崛, 樂, 醜, 铎, 峄, 憎, 枵, 嗪, 华, 换, t, 蹅, 273 | 唆, 蟀, 渤, 慰, 璘, 鎮, 钗, 邢, 闺, 腋, 并, 摈, 鎖, 矬, 须, 嘱, 响, 倦, 稂, T, 负, 漩, 褡, 蹜, 牡, 274 | 牦, 叨, 呗, 彗, 衽, 虔, 莼, 蝠, 填, 沓, 涎, 缉, 織, 酰, 蜩, 堞, 十, 躜, 菘, 坼, 钙, 堡, 訾, 佚, 氯, 275 | 晤, 腭, 型, 昉, 泻, 丟, 拖, 鳀, 暹, 胺, 鹏, 筜, 绦, 顺, 探, 诣, 站, 菅, 礼, 絲, 胭, 秏, 窬, 蔷, 藜, 276 | 伍, 吧, 扮, 舷, 微, 纹, 踹, 篡, 亭, 颤, 耽, 沃, 溷, 祧, 赚, q, 劣, 蓂, 挂, 馨, 瘀, 朗, 库, 姣, 媾, 277 | 唅, 椋, 憬, 槎, 邶, 傥, 卮, 乌, 篙, 绳, 案, 菥, 甲, 坂, 谟, 卤, 叮, 抢, 鸈, 针, 雇, 覌, 碘, 榇, 烝, 278 | 撰, 秀, 齌, 吡, 尾, 司, 敉, И, 〕, 麤, 报, 笛, 萃, 迦, 绷, 柈, 修, 懷, 硷, 蕨, 幼, 御, 过, 苟, 麥, 279 | 喤, 金, 鞅, 年, 鄘, 乖, 安, 醺, 霁, 膙, 荣, 掂, 逖, 棘, 卒, 铞, 猄, 晷, 拤, 眯, 噭, 哆, 鹳, 丛, 憩, 280 | 咛, 鳇, A, 茕, 郐, 抃, 害, 鼷, 袱, Φ, 鲙, 徉, 岱, 潢, 伫, 舐, 晳, 憷, 溝, 昀, 槟, 昕, 嗈, 至, 迷, 281 | 喆, 裼, 盦, 鲜, 阕, 訓, 霸, 鏖, 虐, 早, 援, 槛, 蝉, 肊, 遏, 卩, 熄, 下, 拆, ▲, 鲷, 沁, 玥, 葜, 勁, 282 | 」, 胆, 浅, 遘, 鳚, 狰, 荭, 悲, 黩, 耢, 晞, 浒, 滂, 镚, 濮, 茡, 蠔, 蚀, 醦, 瘆, 藠, 窿, 鄂, 诃, 胨, 283 | 唷, 獨, 钺, 骊, 聖, 檫, 遍, 炸, 炬, 渠, 琖, 迭, 蘅, 秦, 矗, 鮼, 偲, 危, 茏, 穑, 龇, 范, 崃, 龈, 匏, 284 | 惰, 蛆, 郸, 桹, 俜, 猹, 觽, 實, 研, 寤, 祐, 疚, ▪, 鹪, 睚, 倏, 珠, 秕, 手, 释, 鲴, 觑, 亸, 谅, 椹, 285 | 鞡, 缌, 峂, 锪, 湜, 僡, 姜, 击, 庀, 褶, C, 妊, 榱, 哫, 们, 凍, 摆, 嫩, 追, 葖, 刮, 滟, 踯, 泜, 闶, 286 | 叩, 罃, 烧, 亮, 咳, 操, 芡, 现, 砘, 苇, 反, 踩, 舂, 嵇, 容, 拥, 否, 箢, 峡, 绂, 料, 治, 鄉, 坎, 寇, 287 | 鳉, 呼, 鄹, 镆, 涨, 忘, 啷, 汎, 稠, 镃, 瞅, 绚, 皋, 赭, 诋, 芗, 杜, 舶, 疰, 蒴, 廛, 妹, 撒, 無, 谵, 288 | 孳, 桺, 選, 庵, 胚, 蒯, 跬, I, 洯, 校, 悯, 耋, 黻, 孀, 絷, 垢, 玠, 鹅, 裨, 窈, 犀, 侈, 噔, 踢, ◎, 289 | 尝, 滑, 赃, 鹰, 兢, 辏, 碰, 烛, 鲻, 溲, 咒, 衮, 冚, 灼, 瑕, 搔, 佝, 泷, 璁, 蕈, 蛾, 瘸, 腓, 椤, 昵, 290 | 而, 楷, 榆, 凡, 楔, 掸, 曲, 耍, 甭, 秣, 賓, 喈, 黙, 涢, 垮, 他, 锿, 投, 诎, 表, 决, 桷, 磕, 夸, 澡, 291 | 五, 挝, ∈, 舖, 燁, 給, 橡, 拦, 劉, 胛, 蓥, 斲, 忲, 煜, 琏, 理, 氟, 橦, 轼, E, 蚌, 羼, 柒, 恳, 鹩, 292 | 寥, 沌, f, 昂, 矻, 灏, 衹, 拙, 质, 旘, 槔, 帮, 伴, 狐, 袖, 冻, 嚋, 可, 晕, 腕, 锲, 飘, 数, 氘, 卬, 293 | 嶂, 辫, 惮, 袢, 滗, 洒, 歪, 淆, 沮, 落, 逾, 粝, 珧, 许, 猡, 胼, 殴, 澉, 圃, 槐, 同, 胍, 存, 懒, 角, 294 | 窒, 髋, 嵖, 俵, 僖, 蜾, 罡, 咦, 沱, ;, 藐, 栖, 减, 衙, 烙, 豪, 晒, 凋, 醇, 罘, 蹶, 故, 叠, 禺, 塾, 295 | 破, 守, 慌, 厄, 噗, 毳, 谐, 迹, 吭, 華, 爿, 埴, 奇, 愧, 貴, 避, 云, 弧, 楣, 颉, 郾, 彪, 荚, T, 黧, 296 | 狷, 耩, 玡, 牺, 魍, 璱, 町, 锁, Ⅳ, 近, 胂, 员, 隼, 酵, 嶙, 束, 葆, 条, 擢, 暗, 豺, 扃, 甚, 焓, 颥, 297 | 娃, 茂, 邵, 猝, 纲, 楠, 诵, 谍, 肱, 豚, 畅, 榀, 禤, ㎡, 璪, 饔, D, 珞, 酾, 贳, 嬛, 嘃, 瘾, 鋫, 捣, 298 | 柫, 崴, 兕, 甩, 夜, 盉, x, 哪, 恋, 齉, 趙, 燔, 蜡, 芫, 峣, 娆, 苈, 炯, 烆, 设, 巯, 孬, 産, 英, 鹖, 299 | 准, 卷, 眶, 铑, 帨, 层, 书, 饻, 铩, 溧, 绔, 端, 雨, 苛, 冁, 嘉, 祈, 炻, 疝, 峤, 驰, 恭, 浇, 忻, 昴, 300 | 辎, 双, 坍, 攮, 肮, 幸, 羯, 般, 咽, 罥, 邺, 基, 捺, 耒, 浴, 漶, 践, 怩, 北, 庑, 欧, 痂, 耘, 泮, 魋, 301 | 挖, 爻, 键, 泉, 诌, B, 凑, 憋, 棕, 潭, 铆, 逸, 述, 碚, 涕, 鸠, 巡, 整, 典, 纾, 髑, 锃, 斧, 窋, 脒, 302 | 忪, 尬, 掮, 蝴, 醢, 啕, 粗, 鬓, 蓖, 艳, 菑, 胥, 啰, 归, 淹, 笋, 溉, 躏, 饿, 老, 杶, 却, 抻, 敵, 斩, 303 | 内, 悍, 仵, 矿, 沸, 鉴, 洣, 廪, 侄, 裎, 券, 扞, 慥, 莱, 缔, 泗, 滌, 辍, 垃, 冕, 钥, 船, 驵, 碳, 虚, 304 | 張, 鳔, 霏, 牁, 湾, 寝, 雾, 浼, 掌, 暑, 鳥, 迕, 煅, 烹, 透, 凉, 珉, 蒍, 横, 於, 龊, 苄, 胜, 硬, 菀, 305 | 璩, 釐, 漯, 脂, 鲸, 巅, 澎, 钡, 东, 予, 瓶, 叛, 党, 采, 祸, 瞑, 鼑, 瓜, 马, 震, 禧, 殛, 收, 纠, 悒, 306 | 喉, 邻, 娀, 陑, 酎, 齑, 擗, 崽, 坞, 糧, 宫, 窝, 赆, 蝤, ㆍ, 垠, 镉, 洫, 恹, 慑, 程, 傈, 芯, 粵, 杮, 307 | 继, 棠, 尻, 溜, 炕, 凫, 繇, 喇, 右, 拳, 寰, 涒, 橱, 體, 略, 赐, 泔, 妙, 慵, 熬, 棵, 畯, 潺, 亚, 愁, 308 | 視, 宿, 阘, 跸, 桴, 醑, 痕, 褪, 霾, 叶, 喳, 琶, 辱, 悭, 傒, 埒, 挑, 衍, 緃, 擒, 瓻, 肩, 讶, 砖, 氮, 309 | 筮, 纂, 掴, 祀, 佥, 燈, 冀, 爇, 祜, 婧, 坨, 暽, 溅, 唪, 汽, 涵, 犸, 煮, 缢, 袤, 饵, 屯, 凄, 坊, 酝, 310 | 颐, 刺, 蜞, 菹, 段, 桉, 硈, 襄, 盍, 弥, 贬, 箓, 鸹, 歘, 筵, 殓, 鎌, 瓅, 矢, 邛, 韦, 恤, 人, 模, 凤, 311 | 椟, 拾, 狃, 头, 乡, 靿, 垣, 蔁, 此, 着, 泼, <, 松, 亶, 荏, 璃, 礁, 幅, 臨, 稿, 頫, 及, 休, 给, 氵, 312 | 垸, 坦, 埯, 锑, 崧, 酶, 孱, 极, 苕, 抺, 吵, 声, 咩, 峦, 尸, 羌, 广, 脎, 气, 烀, 转, 獭, 見, 诳, 艽, 313 | 唁, 樓, 培, 鲵, 密, 務, 俾, 旨, 孃, 刁, 蓰, 鸸, 景, 羿, 擘, 秃, 溪, 掐, 丞, 褔, 鱼, 廨, 鬃, 畿, 姞, 314 | 嵊, 囔, 骜, 饴, 坉, 悼, 展, 毶, 埋, 僇, 节, 讼, 绾, 琎, 鳡, 帆, 赌, 菓, F, 耻, 趁, 圗, 】, 焙, 浉, 315 | 锂, 翁, 岔, 桩, 沣, 径, 缅, 弑, 頤, 各, 蛙, 锏, 郈, 嗫, 别, 隗, 蓁, 旧, 叱, 缇, 剖, 霓, 棍, 挥, 拓, 316 | □, 颊, 燕, 削, 定, 岳, :, 陂, 肖, 轭, 膛, 煞, 濠, 糙, 珀, 烊, 陶, 俱, 售, 牗, 扬, 鲃, 厚, 哗, 疲, 317 | 和, 棺, 化, 缩, 夹, 酣, 酔, 艨, 裹, 凳, 徐, 百, 厅, 舒, 馿, 俑, 铟, 亦, 進, 芨, 缶, 繪, 淌, 鹉, 妾, 318 | 虽, 埚, 版, 麗, 吝, 蓦, 友, 橹, 荙, 钼, 蛤, 磚, 辒, 塃, 嚓, 侞, 忉, 瘙, 胄, 豢, 赀, 凿, 披, 慷, 亞, 319 | W, 倪, 霄, 葵, 佾, 鲥, 锇, 芽, 靼, 魑, 划, 獐, 镴, 溽, 筚, 辨, 丁, 狱, 斗, 拊, 飓, 焜, 塑, 降, 舢, 320 | 疳, 弊, 愚, 粲, 缥, 肚, 瑁, 琥, 辚, 葙, ≤, 焖, 孖, 弇, 怛, 髀, 艰, 尕, 朱, 怀, 鳤, ü, 或, 捉, 咂, 321 | 绎, 相, 倬, 诂, 地, 瘃, 谝, Y, 陷, 滴, 写, 象, L, 樨, 满, 歃, 懊, 药, 帖, 昧, 踏, 找, 领, 仪, 丙, 322 | 暨, 牴, 秫, 唻, 薏, 猖, 垍, 钠, 枓, 屁, 橐, 动, 赓, 斌, 澼, A, 魄, 父, 邹, 躅, 豌, 阴, 玕, 昔, 科, 323 | 爡, 苡, 瘥, 娘, 徘, 枝, ∧, 柢, 鲔, 谴, 必, 祃, 木, 她, 硇, 镧, З, 郪, 字, 婺, 憝, 姐, 枪, 衖, 箱, 324 | 與, 贿, 撝, 氲, ¥, 斜, 螳, 壬, 钆, 惇, 嗅, 笾, 慧, 寳, 垝, 围, 七, 璠, 禄, 非, 勝, ∵, 藓, 脘, 轫, 325 | 鎏, ┌, 优, 僳, 儀, 喙, 铔, 庖, 螭, 莲, 妳, 戟, 媄, 愆, 闭, 業, 巨, 清, 僧, 菏, 蚓, 熏, 戚, 黯, 赵, 326 | 经, 锖, 橉, 犿, 沈, 幺, 灰, 瀣, 铿, 串, 掼, 消, 罟, 确, 毕, 嫂, ⑴, 糖, 胫, 京, 崋, 浞, 腄, 榍, H, 327 | 橪, 掎, 搜, 涞, 塈, 剩, 沔, 熜, 补, 燚, 戍, 市, 泥, 楞, 猗, 蜥, 鏠, 滍, 簠, 埏, 迨, 冲, 嗒, 诤, 旱, 328 | 握, 运, 顷, 分, 绢, 郯, C, 裒, ▏, 难, 盅, 做, 窘, 婚, 祙, 了, 畐, 足, 诰, 伏, 谋, 財, ‧, 咚, 喋, 329 | 洚, 吟, 溺, 眇, 逢, 苓, 鹮, 箭, 丘, 嗳, 毁, 狡, 嵯, 銘, 阄, 拱, 凶, 部, 灵, 迥, '@', 锞, 儒, 汆, 330 | 迎, 录, 孢, 鲛, 琫, 哓, 底, 疣, 嫖, 印, 布, 沇, 铒, 迳, 鹆, ○, 國, 袜, 璐, 翦, 弍, 摉, 顔, 蕊, 效, 331 | 焕, 滬, 抹, 答, 适, 镛, 峯, 耎, 损, 褲, 澝, 蛋, 阝, 韂] 332 | ImageChannel: 1 333 | ImageHeight: 64 334 | ImageWidth: -1 335 | Word: false 336 | System: 337 | Allow_Ext: [jpg, jpeg, png, bmp] 338 | GPU: true 339 | GPU_ID: 0 340 | Path: /datas/datasets/images 341 | Project: ddddocr-2022-2-28 342 | Val: 0.03 343 | Train: 344 | BATCH_SIZE: 32 345 | CNN: {NAME: ddddocr} 346 | DROPOUT: 0.3 347 | LR: 0.01 348 | OPTIMIZER: SGD 349 | SAVE_CHECKPOINTS_STEP: 10000 350 | TARGET: {Accuracy: 0.97, Cost: 0.05, Epoch: 20} 351 | TEST_BATCH_SIZE: 32 352 | TEST_STEP: 1000 353 | -------------------------------------------------------------------------------- /requirements.txt: -------------------------------------------------------------------------------- 1 | fire 2 | loguru 3 | pyyaml 4 | tqdm 5 | numpy<2 6 | pillow==9.5.0 -------------------------------------------------------------------------------- /tools/__init__.py: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/sml2h3/dddd_trainer/5fd0d0b5bb83bf44a5692c9d253b7d928e05e673/tools/__init__.py -------------------------------------------------------------------------------- /utils/__init__.py: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/sml2h3/dddd_trainer/5fd0d0b5bb83bf44a5692c9d253b7d928e05e673/utils/__init__.py -------------------------------------------------------------------------------- /utils/cache_data.py: -------------------------------------------------------------------------------- 1 | import json 2 | import os 3 | import random 4 | 5 | import tqdm 6 | 7 | from configs import Config 8 | from loguru import logger 9 | 10 | 11 | class CacheData: 12 | def __init__(self, project_name: str): 13 | self.project_name = project_name 14 | self.project_path = os.path.join(os.path.dirname(os.path.dirname(os.path.abspath(__file__))), "projects", 15 | project_name) 16 | if os.path.exists(self.project_path): 17 | self.cache_path = os.path.join(self.project_path, "cache") 18 | else: 19 | logger.error("Project {} is not exists!".format(project_name)) 20 | exit() 21 | self.config = Config(project_name) 22 | self.conf = self.config.load_config() 23 | self.bath_path = self.conf['System']['Path'] 24 | self.allow_ext = [] 25 | 26 | def cache(self, base_path: str, search_type="name"): 27 | self.bath_path = base_path 28 | self.allow_ext = self.conf["System"]["Allow_Ext"] 29 | if search_type == "name": 30 | self.__get_label_from_name(base_path=base_path) 31 | else: 32 | self.__get_label_from_file(base_path=base_path) 33 | 34 | def __get_label_from_name(self, base_path: str): 35 | files = os.listdir(base_path) 36 | logger.info("\nFiles number is {}.".format(len(files))) 37 | self.__collect_data(files, base_path, []) 38 | 39 | def __get_label_from_file(self, base_path: str): 40 | labels_path = os.path.join(base_path, "labels.txt") 41 | images_path = os.path.join(base_path, "images") 42 | if not os.path.exists(labels_path): 43 | logger.error("\nThe file labels.txt not found in path ----> {}".format(base_path)) 44 | exit() 45 | if not os.path.exists(images_path) or not os.path.isdir(images_path): 46 | logger.error("\nThe dir {} not found in path ----> {}".format(images_path, base_path)) 47 | exit() 48 | files = os.listdir(images_path) 49 | logger.info("\nFiles number is {}.".format(len(files))) 50 | with open(labels_path, "r", encoding="utf-8") as f: 51 | labels_lines = f.readlines() 52 | labels_lines = [line.replace("\r", "").replace("\n", "") for line in labels_lines] 53 | labels_filename_lines = [line.split("\t")[0] for line in labels_lines] 54 | logger.info("\nLabels number is {}.".format(len(labels_lines))) 55 | logger.info("\nChecking labels.txt ...") 56 | error_files = set(labels_filename_lines).difference(set(files)) 57 | logger.info("\nCheck labels.txt end! {} errors!".format(len(error_files))) 58 | del files 59 | self.__collect_data(labels_lines, images_path, error_files, is_file=True) 60 | 61 | def __collect_data(self, lines, base_path, error_files, is_file=False): 62 | labels = [] 63 | caches = [] 64 | 65 | for file in tqdm.tqdm(lines): 66 | if is_file: 67 | line_list = file.split('\t') 68 | filename = line_list[0] 69 | label = line_list[1] 70 | else: 71 | filename = file 72 | label = "_".join(filename.split("_")[:-1]) 73 | if filename in error_files: 74 | continue 75 | label = label.replace(" ", "") 76 | if filename.split('.')[-1] in self.allow_ext: 77 | if " " in filename: 78 | logger.warning("The {} has black. We will remove it!".format(filename)) 79 | continue 80 | caches.append('\t'.join([filename, label])) 81 | if not self.conf['Model']['Word']: 82 | label = list(label) 83 | labels.extend(label) 84 | else: 85 | labels.append(label) 86 | 87 | else: 88 | logger.warning("\nFile({}) has a suffix that is not allowed! We will remove it!".format(file)) 89 | labels = list(set(labels)) 90 | if not self.conf['Model']['Word']: 91 | labels.insert(0, " ") 92 | logger.info("\nCoolect labels is {}".format(json.dumps(labels, ensure_ascii=False))) 93 | self.conf['System']['Path'] = base_path 94 | self.conf['Model']['CharSet'] = labels 95 | self.config.make_config(config_dict=self.conf, single=self.conf['Model']['Word']) 96 | logger.info("\nWriting Cache Data!") 97 | del lines 98 | logger.info("\nCache Data Number is {}".format(len(caches))) 99 | logger.info("\nWriting Train and Val File.".format(len(caches))) 100 | val = self.conf['System']['Val'] 101 | if 0 < val < 1: 102 | val_num = int(len(caches) * val) 103 | elif 1 < val < len(caches): 104 | val_num = int(val) 105 | else: 106 | logger.error("val setting vaild!") 107 | exit() 108 | random.shuffle(caches) 109 | train_set = caches[val_num:] 110 | val_set = caches[:val_num] 111 | del caches 112 | with open(os.path.join(self.cache_path, "cache.train.tmp"), 'w', encoding="utf-8") as f: 113 | f.write("\n".join(train_set)) 114 | with open(os.path.join(self.cache_path, "cache.val.tmp"), 'w', encoding="utf-8") as f: 115 | f.write("\n".join(val_set)) 116 | logger.info("\nTrain Data Number is {}".format(len(train_set))) 117 | logger.info("\nVal Data Number is {}".format(len(val_set))) 118 | -------------------------------------------------------------------------------- /utils/load_cache.py: -------------------------------------------------------------------------------- 1 | import json 2 | import os 3 | 4 | import torch 5 | 6 | from configs import Config 7 | from loguru import logger 8 | 9 | import torchvision 10 | from PIL import Image, ImageFile 11 | from torch.utils.data import DataLoader, Dataset, TensorDataset 12 | 13 | ImageFile.LOAD_TRUNCATED_IMAGES = True 14 | 15 | 16 | class LoadCache(Dataset): 17 | def __init__(self, cache_path: str, path: str, word: bool, image_channel: int, resize: list, charset: list): 18 | self.cache_path = cache_path 19 | self.path = path 20 | self.word = word 21 | self.ImageChannel = image_channel 22 | self.resize = resize 23 | self.charset = charset 24 | self.caches = [] 25 | logger.info("\nReading Cache File... ----> {}".format(self.cache_path)) 26 | 27 | with open(self.cache_path, 'r', encoding='utf-8') as f: 28 | self.caches = f.readlines() 29 | self.caches_num = len(self.caches) 30 | logger.info("\nRead Cache File End! Caches Num is {}.".format(self.caches_num)) 31 | 32 | def __len__(self): 33 | return self.caches_num 34 | 35 | def __getitem__(self, idx): 36 | try: 37 | data = self.caches[idx] 38 | data = data.replace("\r", "").replace("\n", "").split("\t") 39 | image_name = data[0] 40 | image_label = data[1] 41 | image_path = os.path.join(self.path, image_name) 42 | if not self.word: 43 | image_label = list(image_label) 44 | else: 45 | image_label = [image_label] 46 | if self.ImageChannel == 1: 47 | mode = "L" 48 | else: 49 | mode = "RGB" 50 | image = Image.open(image_path).convert(mode) # shape c, h, w 51 | image_shape = image.size 52 | image_height = image_shape[1] 53 | image_width = image_shape[0] 54 | width = self.resize[0] 55 | height = self.resize[1] 56 | if self.resize[0] == -1: 57 | if self.word: 58 | image = image.resize((height, height)) 59 | else: 60 | image = image.resize((int(image_width * (height / image_height)), height)) 61 | else: 62 | image = image.resize((width, height)) 63 | label = [int(self.charset.index(item)) for item in list(image_label)] 64 | return image, label 65 | 66 | except Exception as e: 67 | logger.error("\nError: {}, File: {}".format(str(e), self.caches[idx].split("\t")[0])) 68 | return None, None 69 | 70 | 71 | class GetLoader: 72 | def __init__(self, project_name: str): 73 | self.project_name = project_name 74 | self.project_path = os.path.join(os.path.dirname(os.path.dirname(os.path.abspath(__file__))), "projects", 75 | project_name) 76 | if os.path.exists(self.project_path): 77 | self.cache_path = os.path.join(self.project_path, "cache") 78 | if os.path.exists(self.cache_path): 79 | self.cache_train_path = os.path.join(self.cache_path, "cache.train.tmp") 80 | self.cache_val_path = os.path.join(self.cache_path, "cache.val.tmp") 81 | 82 | if not os.path.exists(self.cache_train_path): 83 | logger.error("\nCache Train File {} is not exists!".format(self.cache_train_path)) 84 | exit() 85 | if not os.path.exists(self.cache_val_path): 86 | logger.error("\nCache Val File {} is not exists!".format(self.cache_val_path)) 87 | exit() 88 | 89 | else: 90 | logger.error("\nCache dir {} is not exists!".format(self.cache_path)) 91 | exit() 92 | else: 93 | logger.error("\nProject {} is not exists!".format(project_name)) 94 | exit() 95 | 96 | self.config = Config(project_name) 97 | 98 | self.conf = self.config.load_config() 99 | 100 | self.charset = self.conf['Model']['CharSet'] 101 | 102 | logger.info("\nCharsets is {}".format(json.dumps(self.charset, ensure_ascii=False))) 103 | 104 | self.resize = [int(self.conf['Model']['ImageWidth']), int(self.conf['Model']['ImageHeight'])] 105 | 106 | logger.info("\nImage Resize is {}".format(json.dumps(self.resize))) 107 | 108 | self.ImageChannel = self.conf['Model']['ImageChannel'] 109 | 110 | self.word = self.conf['Model']['Word'] 111 | 112 | self.path = self.conf['System']['Path'] 113 | 114 | self.batch_size = self.conf['Train']['BATCH_SIZE'] 115 | 116 | self.val_batch_size = self.conf['Train']['TEST_BATCH_SIZE'] 117 | 118 | logger.info("\nImage Path is {}".format(self.path)) 119 | 120 | self.transform_list = [] 121 | self.transform_list.append(torchvision.transforms.ToTensor()) 122 | if self.ImageChannel == 1: 123 | self.transform_list.append(torchvision.transforms.Normalize(mean=[0.456], 124 | std=[0.224])) 125 | else: 126 | if self.ImageChannel != 3: 127 | logger.error("ImageChannel must be 1 or 3!") 128 | exit() 129 | self.transform_list.append(torchvision.transforms.Normalize(mean=[0.485, 0.456, 0.406], 130 | std=[0.229, 0.224, 0.225])) 131 | self.transform = torchvision.transforms.Compose(self.transform_list) 132 | train_loader = LoadCache(self.cache_train_path, self.path, self.word, self.ImageChannel, self.resize, 133 | self.charset) 134 | if len(train_loader) < self.batch_size: 135 | self.batch_size = len(train_loader) 136 | val_loader = LoadCache(self.cache_val_path, self.path, self.word, self.ImageChannel, self.resize, self.charset) 137 | if len(val_loader) < self.batch_size: 138 | self.val_batch_size = len(val_loader) 139 | self.loaders = { 140 | 'train': DataLoader(dataset=train_loader, batch_size=self.batch_size, shuffle=True, drop_last=True, 141 | num_workers=0, collate_fn=self.collate_to_sparse), 142 | 'val': DataLoader(dataset=val_loader, batch_size=self.val_batch_size, shuffle=True, drop_last=True, 143 | num_workers=0, collate_fn=self.collate_to_sparse), 144 | } 145 | del val_loader 146 | del train_loader 147 | 148 | def collate_to_sparse(self, batch): 149 | values = [] 150 | images = [] 151 | shapes = [] 152 | max_width = 0 153 | for n, (img, seq) in enumerate(batch): 154 | if img is None or seq is None: 155 | continue 156 | if len(seq) == 0: continue 157 | if max_width < img.size[0]: 158 | max_width = img.size[0] 159 | values.extend(seq) 160 | images.append(img) 161 | shapes.append(len(seq)) 162 | images_pad = [] 163 | for img in images: 164 | img = torchvision.transforms.Pad((0, 0, int(max_width - img.size[0]), 0))(img) 165 | if self.transform is not None: 166 | img = self.transform(img) 167 | images_pad.append(img) 168 | images_pad = torch.stack(images_pad, dim=0) 169 | return [images_pad, torch.FloatTensor(values), torch.IntTensor(shapes)] 170 | -------------------------------------------------------------------------------- /utils/project_manager.py: -------------------------------------------------------------------------------- 1 | import os 2 | from configs import Config 3 | from loguru import logger 4 | 5 | 6 | class ProjectManager: 7 | 8 | def __init__(self): 9 | self.base_path = os.path.join(os.path.dirname(os.path.dirname(os.path.abspath(__file__))), "projects") 10 | 11 | def create_project(self, project_name: str, single: bool = False): 12 | project_base_path = os.path.join(self.base_path, project_name) 13 | logger.info("Creating Directory... ----> {}".format(project_base_path)) 14 | if not os.path.exists(project_base_path): 15 | os.mkdir(project_base_path) 16 | if not os.path.exists(project_base_path): 17 | logger.error("Directory create failed! ----> {}".format(project_base_path)) 18 | return False 19 | models_path = os.path.join(project_base_path, "models") 20 | logger.info("Creating Directory... ----> {}".format(models_path)) 21 | os.mkdir(models_path) 22 | 23 | cache_path = os.path.join(project_base_path, "cache") 24 | logger.info("Creating Directory... ----> {}".format(cache_path)) 25 | os.mkdir(cache_path) 26 | 27 | checkpoints_path = os.path.join(project_base_path, "checkpoints") 28 | logger.info("Creating Directory... ----> {}".format(checkpoints_path)) 29 | os.mkdir(checkpoints_path) 30 | 31 | config_path = os.path.join(os.path.join(project_base_path, "config.yaml")) 32 | logger.info("Creating {} Config File... ----> {}".format("CNN" if single else "CRNN", config_path)) 33 | conf = Config(project_name) 34 | conf.make_config(single=single) 35 | 36 | logger.info("Create Project Success! ----> {}".format(project_name)) 37 | else: 38 | logger.error("Directory already exists! ----> {}".format(project_base_path)) 39 | return False 40 | -------------------------------------------------------------------------------- /utils/train.py: -------------------------------------------------------------------------------- 1 | import json 2 | import os 3 | import random 4 | import time 5 | 6 | import tqdm 7 | 8 | from configs import Config 9 | from loguru import logger 10 | from utils import load_cache 11 | from nets import Net 12 | 13 | 14 | class Train: 15 | def __init__(self, project_name: str): 16 | self.project_name = project_name 17 | self.project_path = os.path.join(os.path.dirname(os.path.dirname(os.path.abspath(__file__))), "projects", 18 | project_name) 19 | self.checkpoints_path = os.path.join(self.project_path, "checkpoints") 20 | self.models_path = os.path.join(self.project_path, "models") 21 | self.epoch = 0 22 | self.step = 0 23 | self.lr = None 24 | self.state_dict = None 25 | self.optimizer = None 26 | self.config = Config(project_name) 27 | self.conf = self.config.load_config() 28 | 29 | self.test_step = self.conf['Train']['TEST_STEP'] 30 | self.save_checkpoints_step = self.conf['Train']['SAVE_CHECKPOINTS_STEP'] 31 | 32 | self.target = self.conf['Train']['TARGET'] 33 | self.target_acc = self.target['Accuracy'] 34 | self.min_epoch = self.target['Epoch'] 35 | self.max_loss = self.target['Cost'] 36 | 37 | self.resize = [int(self.conf['Model']['ImageWidth']), int(self.conf['Model']['ImageHeight'])] 38 | self.word = self.conf['Model']['Word'] 39 | self.ImageChannel = self.conf['Model']['ImageChannel'] 40 | logger.info("\nTaget:\nmin_Accuracy: {}\nmin_Epoch: {}\nmax_Loss: {}".format(self.target_acc, self.min_epoch, 41 | self.max_loss)) 42 | self.use_gpu = self.conf['System']['GPU'] 43 | if self.use_gpu: 44 | self.gpu_id = self.conf['System']['GPU_ID'] 45 | logger.info("\nUSE GPU ----> {}".format(self.gpu_id)) 46 | self.device = Net.get_device(self.gpu_id) 47 | 48 | else: 49 | self.gpu_id = -1 50 | self.device = Net.get_device(self.gpu_id) 51 | logger.info("\nUSE CPU".format(self.gpu_id)) 52 | logger.info("\nSearch for history checkpoints...") 53 | history_checkpoints = os.listdir(self.checkpoints_path) 54 | if len(history_checkpoints) > 0: 55 | history_step = 0 56 | newer_checkpoint = None 57 | for checkpoint in history_checkpoints: 58 | checkpoint_name = checkpoint.split(".")[0].split("_") 59 | if int(checkpoint_name[3]) > history_step: 60 | newer_checkpoint = checkpoint 61 | history_step = int(checkpoint_name[3]) 62 | param, self.state_dict, self.optimizer= Net.load_checkpoint( 63 | os.path.join(self.checkpoints_path, newer_checkpoint), self.device) 64 | self.epoch, self.step, self.lr = param['epoch'], param['step'], param['lr'] 65 | self.epoch += 1 66 | self.step += 1 67 | 68 | else: 69 | logger.info("\nEmpty history checkpoints") 70 | 71 | logger.info("\nBuilding Net...") 72 | self.net = Net(self.conf, self.lr) 73 | if self.state_dict: 74 | self.net.load_state_dict(self.state_dict) 75 | logger.info(self.net) 76 | logger.info("\nBuilding End") 77 | 78 | 79 | 80 | self.net = self.net.to(self.device) 81 | logger.info("\nGet Data Loader...") 82 | 83 | loaders = load_cache.GetLoader(project_name) 84 | self.train = loaders.loaders['train'] 85 | self.val = loaders.loaders['val'] 86 | del loaders 87 | logger.info("\nGet Data Loader End!") 88 | 89 | self.loss = 0 90 | self.avg_loss = 0 91 | self.start_time = time.time() 92 | self.now_time = time.time() 93 | 94 | def start(self): 95 | val_iter = iter(self.val) 96 | while True: 97 | for idx, (inputs, labels, labels_length) in enumerate(self.train): 98 | self.now_time = time.time() 99 | inputs = self.net.variable_to_device(inputs, device=self.device) 100 | 101 | loss, lr = self.net.trainer(inputs, labels, labels_length) 102 | 103 | self.avg_loss += loss 104 | 105 | self.step += 1 106 | 107 | if self.step % 100 == 0 and self.step % self.test_step != 0: 108 | logger.info("{}\tEpoch: {}\tStep: {}\tLastLoss: {}\tAvgLoss: {}\tLr: {}".format( 109 | time.strftime("[%Y-%m-%d-%H_%M_%S]", time.localtime(self.now_time)), self.epoch, self.step, 110 | str(loss), str(self.avg_loss / 100), lr 111 | )) 112 | self.avg_loss = 0 113 | if self.step % self.save_checkpoints_step == 0 and self.step != 0: 114 | model_path = os.path.join(self.checkpoints_path, "checkpoint_{}_{}_{}.tar".format( 115 | self.project_name, self.epoch, self.step, 116 | )) 117 | self.net.scheduler.step() 118 | self.net.save_model(model_path, 119 | {"net": self.net.state_dict(), "optimizer": self.net.optimizer.state_dict(), 120 | "epoch": self.epoch, "step": self.step, "lr": lr}) 121 | 122 | if self.step % self.test_step == 0: 123 | try: 124 | test_inputs, test_labels, test_labels_length = next(val_iter) 125 | except Exception: 126 | del val_iter 127 | val_iter = iter(self.val) 128 | test_inputs, test_labels, test_labels_length = next(val_iter) 129 | if test_inputs.shape[0] < 5: 130 | continue 131 | test_inputs = self.net.variable_to_device(test_inputs, self.device) 132 | self.net = self.net.train(False) 133 | pred_labels, labels_list, correct_list, error_list = self.net.tester(test_inputs, test_labels, 134 | test_labels_length) 135 | self.net = self.net.train() 136 | accuracy = len(correct_list) / test_inputs.shape[0] 137 | logger.info("{}\tEpoch: {}\tStep: {}\tLastLoss: {}\tAvgLoss: {}\tLr: {}\tAcc: {}".format( 138 | time.strftime("[%Y-%m-%d-%H_%M_%S]", time.localtime(self.now_time)), self.epoch, self.step, 139 | str(loss), str(self.avg_loss / 100), lr, accuracy 140 | )) 141 | self.avg_loss = 0 142 | if accuracy > self.target_acc and self.epoch > self.min_epoch and self.avg_loss < self.max_loss: 143 | logger.info("\nTraining Finished!Exporting Model...") 144 | dummy_input = self.net.get_random_tensor() 145 | input_names = ["input1"] 146 | output_names = ["output"] 147 | 148 | if self.net.backbone.startswith("effnet"): 149 | self.net.cnn.set_swish(memory_efficient=False) 150 | self.net = self.net.eval().cpu() 151 | dynamic_ax = {'input1': {3: 'image_wdith'}, "output": {1: 'seq'}} 152 | self.net.export_onnx(self.net, dummy_input, 153 | os.path.join(self.models_path, "{}_{}_{}_{}_{}.onnx".format( 154 | self.project_name, str(accuracy), self.epoch, self.step, 155 | time.strftime("%Y-%m-%d-%H-%M-%S", time.localtime(self.now_time)))) 156 | , input_names, output_names, dynamic_ax) 157 | with open(os.path.join(self.models_path, "charsets.json"), 'w', encoding="utf-8") as f: 158 | f.write(json.dumps({"charset": self.net.charset, "image": self.resize, "word": self.word, 'channel': self.ImageChannel}, ensure_ascii=False)) 159 | logger.info("\nExport Finished!Using Time: {}min".format( 160 | str(int(int(self.now_time) - int(self.start_time)) / 60))) 161 | exit() 162 | 163 | self.epoch += 1 164 | 165 | 166 | if __name__ == '__main__': 167 | Train("test1") 168 | --------------------------------------------------------------------------------