https://huggingface.co/nvidia/nemotron-ocr-v1/tree/main
#8
by Eklavya214 - opened
- LICENSE +0 -243
- README.md +1 -2
- config.json +0 -1
- example.py +1 -1
- nemotron-ocr/cpp/non_maximal_suppression/cuda_non_maximal_suppression.cu +118 -67
- nemotron-ocr/pyproject.toml +0 -1
- nemotron-ocr/src/nemotron_ocr/inference/pipeline.py +28 -52
LICENSE
DELETED
|
@@ -1,243 +0,0 @@
|
|
| 1 |
-
All binary model files are licensed under NVIDIA Open Model License Agreement.
|
| 2 |
-
All source code files are licensed under the Apache 2.0 License.
|
| 3 |
-
|
| 4 |
-
------------
|
| 5 |
-
NVIDIA Open Model License Agreement
|
| 6 |
-
Last Modified: October 24, 2025
|
| 7 |
-
https://www.nvidia.com/en-us/agreements/enterprise-software/nvidia-open-model-license/
|
| 8 |
-
|
| 9 |
-
This NVIDIA Open Model License Agreement (the “Agreement”) is a legal agreement between the Legal Entity You represent, or if no entity is identified, You and NVIDIA Corporation and its Affiliates (“NVIDIA”) and governs Your use of the Models that NVIDIA provides to You under this Agreement. NVIDIA and You are each a “party” and collectively the “parties.”
|
| 10 |
-
|
| 11 |
-
NVIDIA models released under this Agreement are intended to be used permissively and enable the further development of AI technologies. Subject to the terms of this Agreement, NVIDIA confirms that:
|
| 12 |
-
|
| 13 |
-
- Models are commercially usable.
|
| 14 |
-
- You are free to create and distribute Derivative Models.
|
| 15 |
-
- NVIDIA does not claim ownership to any outputs generated using the Models or Derivative Models.
|
| 16 |
-
By using, reproducing, modifying, distributing, performing or displaying any portion or element of the Model or Derivative Model, or otherwise accepting the terms of this Agreement, you agree to be bound by this Agreement.
|
| 17 |
-
|
| 18 |
-
1. Definitions. The following definitions apply to this Agreement:
|
| 19 |
-
|
| 20 |
-
1.1 "Derivative Model" means all (a) modifications to the Model, (b) works based on the Model, and (c) any other derivative works of the Model. An output is not a Derivative Model.
|
| 21 |
-
|
| 22 |
-
1.2 "Legal Entity" means the union of the acting entity and all other entities that control, are controlled by, or are under common control with that entity. For the purposes of this definition, "control" means (a) the power, direct or indirect, to cause the direction or management of such entity, whether by contract or otherwise, or (b) ownership of fifty percent (50%) or more of the outstanding shares, or (c) beneficial ownership of such entity.
|
| 23 |
-
|
| 24 |
-
1.3 “Model” means the machine learning model, software, checkpoints, learnt weights, algorithms, parameters, configuration files and documentation shared under this Agreement.
|
| 25 |
-
|
| 26 |
-
1.4 "NVIDIA Cosmos Model" means a multimodal Model shared under this Agreement
|
| 27 |
-
|
| 28 |
-
1.5 "Special-Purpose Model" means a Model that is only competent in a narrow set of purpose-specific tasks and should not be used for unintended or general-purpose applications
|
| 29 |
-
|
| 30 |
-
1.6 “You” or “Your” means an individual or Legal Entity exercising permissions granted by this Agreement.
|
| 31 |
-
|
| 32 |
-
2. Conditions for Use, License Grant, AI Ethics and IP Ownership.
|
| 33 |
-
|
| 34 |
-
2.1 Conditions for Use. The Model and any Derivative Model are subject to additional terms as described in Section 2 and Section 3 of this Agreement and govern Your use. If You institute copyright or patent litigation against any entity (including a cross-claim or counterclaim in a lawsuit) alleging that the Model or a Derivative Model constitutes direct or contributory copyright or patent infringement, then any licenses granted to You under this Agreement for that Model or Derivative Model will terminate as of the date such litigation is filed. If You bypass, disable, reduce the efficacy of, or circumvent any technical limitation, safety guardrail or associated safety guardrail hyperparameter, encryption, security, digital rights management, or authentication mechanism (collectively “Guardrail”) contained in the Model without a substantially similar Guardrail appropriate for your use case, your rights under this Agreement will automatically terminate. NVIDIA may indicate in relevant documentation that a Model is a Special-Purpose Model. NVIDIA may update this Agreement to comply with legal and regulatory requirements at any time and You agree to either comply with any updated license or cease Your copying, use, and distribution of the Model and any Derivative Model.
|
| 35 |
-
|
| 36 |
-
2.2 License Grant. The rights granted herein are explicitly conditioned on Your full compliance with the terms of this Agreement. Subject to the terms and conditions of this Agreement, NVIDIA hereby grants to You a perpetual, worldwide, non-exclusive, no-charge, royalty-free, revocable (as stated in Section 2.1) license to publicly perform, publicly display, reproduce, use, create derivative works of, make, have made, sell, offer for sale, distribute (through multiple tiers of distribution) and import the Model.
|
| 37 |
-
|
| 38 |
-
2.3 AI Ethics. Use of the Models under the Agreement must be consistent with NVIDIA’s Trustworthy AI terms found at https://www.nvidia.com/en-us/agreements/trustworthy-ai/terms/.
|
| 39 |
-
|
| 40 |
-
2.4 NVIDIA owns the Model and any Derivative Models created by NVIDIA. Subject to NVIDIA’s underlying ownership rights in the Model or its Derivative Models, You are and will be the owner of Your Derivative Models. NVIDIA claims no ownership rights in outputs. You are responsible for outputs and their subsequent uses. Except as expressly granted in this Agreement, (a) NVIDIA reserves all rights, interests and remedies in connection with the Model and (b) no other license or right is granted to you by implication, estoppel or otherwise.
|
| 41 |
-
|
| 42 |
-
3. Redistribution. You may reproduce and distribute copies of the Model or Derivative Models thereof in any medium, with or without modifications, provided that You meet the following conditions:
|
| 43 |
-
|
| 44 |
-
3.1 If you distribute the Model, You must give any other recipients of the Model a copy of this Agreement and include the following attribution notice within a “Notice” text file with such copies: “Licensed by NVIDIA Corporation under the NVIDIA Open Model License”;
|
| 45 |
-
|
| 46 |
-
3.2 If you distribute or make available a NVIDIA Cosmos Model, or a product or service (including an AI model) that contains or uses a NVIDIA Cosmos Model, use a NVIDIA Cosmos Model to create a Derivative Model, or use a NVIDIA Cosmos Model or its outputs to create, train, fine tune, or otherwise improve an AI model, you will include “Built on NVIDIA Cosmos” on a related website, user interface, blogpost, about page, or product documentation; and
|
| 47 |
-
|
| 48 |
-
3.3 You may add Your own copyright statement to Your modifications and may provide additional or different license terms and conditions for use, reproduction, or distribution of Your modifications, or for any such Derivative Models as a whole, provided Your use, reproduction, and distribution of the Model otherwise complies with the conditions stated in this Agreement.
|
| 49 |
-
|
| 50 |
-
4. Separate Components. The Models may include or be distributed with components provided with separate legal notices or terms that accompany the components, such as an Open Source Software License or other third-party license. The components are subject to the applicable other licenses, including any proprietary notices, disclaimers, requirements and extended use rights; except that this Agreement will prevail regarding the use of third-party Open Source Software License, unless a third-party Open Source Software License requires its license terms to prevail. “Open Source Software License” means any software, data or documentation subject to any license identified as an open source license by the Open Source Initiative (https://opensource.org), Free Software Foundation (https://www.fsf.org) or other similar open source organization or listed by the Software Package Data Exchange (SPDX) Workgroup under the Linux Foundation (https://www.spdx.org).
|
| 51 |
-
|
| 52 |
-
5. Trademarks. This Agreement does not grant permission to use the trade names, trademarks, service marks, or product names of NVIDIA, except as required for reasonable and customary use in describing the origin of the Model and reproducing the content of the “Notice” text file.
|
| 53 |
-
|
| 54 |
-
6. Disclaimer of Warranty. Unless required by applicable law or agreed to in writing, NVIDIA provides the Model on an “AS IS” BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied, including, without limitation, any warranties or conditions of TITLE, NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A PARTICULAR PURPOSE. You are solely responsible for reviewing Model documentation, including any Special-Purpose Model limitations, and determining the appropriateness of using or redistributing the Model, Derivative Models and outputs. You assume any risks associated with Your exercise of permissions under this Agreement.
|
| 55 |
-
|
| 56 |
-
7. Limitation of Liability. In no event and under no legal theory, whether in tort (including negligence), contract, or otherwise, unless required by applicable law (such as deliberate and grossly negligent acts) or agreed to in writing, will NVIDIA be liable to You for damages, including any direct, indirect, special, incidental, or consequential damages of any character arising as a result of this Agreement or out of the use or inability to use the Model, Derivative Models or outputs (including but not limited to damages for loss of goodwill, work stoppage, computer failure or malfunction, or any and all other commercial damages or losses), even if NVIDIA has been advised of the possibility of such damages.
|
| 57 |
-
|
| 58 |
-
8. Indemnity. You will indemnify and hold harmless NVIDIA from and against any claim by any third party arising out of or related to your use or distribution of the Model, Derivative Models or outputs.
|
| 59 |
-
|
| 60 |
-
9. Feedback. NVIDIA appreciates your feedback, and You agree that NVIDIA may use it without restriction or compensation to You.
|
| 61 |
-
|
| 62 |
-
10. Governing Law. This Agreement will be governed in all respects by the laws of the United States and the laws of the State of Delaware, without regard to conflict of laws principles or the United Nations Convention on Contracts for the International Sale of Goods. The state and federal courts residing in Santa Clara County, California will have exclusive jurisdiction over any dispute or claim arising out of or related to this Agreement, and the parties irrevocably consent to personal jurisdiction and venue in those courts; except that, either party may apply for injunctive remedies or an equivalent type of urgent legal relief in any jurisdiction.
|
| 63 |
-
|
| 64 |
-
11. Trade and Compliance. You agree to comply with all applicable export, import, trade and economic sanctions laws and regulations, as amended, including without limitation U.S. Export Administration Regulations and Office of Foreign Assets Control regulations. These laws include restrictions on destinations, end-users and end-use.
|
| 65 |
-
|
| 66 |
-
Version Release Date: October 24, 2025
|
| 67 |
-
|
| 68 |
-
-----------------
|
| 69 |
-
Apache License
|
| 70 |
-
Version 2.0, January 2004
|
| 71 |
-
http://www.apache.org/licenses/
|
| 72 |
-
TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION
|
| 73 |
-
1. Definitions.
|
| 74 |
-
"License" shall mean the terms and conditions for use, reproduction,
|
| 75 |
-
and distribution as defined by Sections 1 through 9 of this document.
|
| 76 |
-
"Licensor" shall mean the copyright owner or entity authorized by
|
| 77 |
-
the copyright owner that is granting the License.
|
| 78 |
-
"Legal Entity" shall mean the union of the acting entity and all
|
| 79 |
-
other entities that control, are controlled by, or are under common
|
| 80 |
-
control with that entity. For the purposes of this definition,
|
| 81 |
-
"control" means (i) the power, direct or indirect, to cause the
|
| 82 |
-
direction or management of such entity, whether by contract or
|
| 83 |
-
otherwise, or (ii) ownership of fifty percent (50%) or more of the
|
| 84 |
-
outstanding shares, or (iii) beneficial ownership of such entity.
|
| 85 |
-
"You" (or "Your") shall mean an individual or Legal Entity
|
| 86 |
-
exercising permissions granted by this License.
|
| 87 |
-
"Source" form shall mean the preferred form for making modifications,
|
| 88 |
-
including but not limited to software source code, documentation
|
| 89 |
-
source, and configuration files.
|
| 90 |
-
"Object" form shall mean any form resulting from mechanical
|
| 91 |
-
transformation or translation of a Source form, including but
|
| 92 |
-
not limited to compiled object code, generated documentation,
|
| 93 |
-
and conversions to other media types.
|
| 94 |
-
"Work" shall mean the work of authorship, whether in Source or
|
| 95 |
-
Object form, made available under the License, as indicated by a
|
| 96 |
-
copyright notice that is included in or attached to the work
|
| 97 |
-
(an example is provided in the Appendix below).
|
| 98 |
-
"Derivative Works" shall mean any work, whether in Source or Object
|
| 99 |
-
form, that is based on (or derived from) the Work and for which the
|
| 100 |
-
editorial revisions, annotations, elaborations, or other modifications
|
| 101 |
-
represent, as a whole, an original work of authorship. For the purposes
|
| 102 |
-
of this License, Derivative Works shall not include works that remain
|
| 103 |
-
separable from, or merely link (or bind by name) to the interfaces of,
|
| 104 |
-
the Work and Derivative Works thereof.
|
| 105 |
-
"Contribution" shall mean any work of authorship, including
|
| 106 |
-
the original version of the Work and any modifications or additions
|
| 107 |
-
to that Work or Derivative Works thereof, that is intentionally
|
| 108 |
-
submitted to Licensor for inclusion in the Work by the copyright owner
|
| 109 |
-
or by an individual or Legal Entity authorized to submit on behalf of
|
| 110 |
-
the copyright owner. For the purposes of this definition, "submitted"
|
| 111 |
-
means any form of electronic, verbal, or written communication sent
|
| 112 |
-
to the Licensor or its representatives, including but not limited to
|
| 113 |
-
communication on electronic mailing lists, source code control systems,
|
| 114 |
-
and issue tracking systems that are managed by, or on behalf of, the
|
| 115 |
-
Licensor for the purpose of discussing and improving the Work, but
|
| 116 |
-
excluding communication that is conspicuously marked or otherwise
|
| 117 |
-
designated in writing by the copyright owner as "Not a Contribution."
|
| 118 |
-
"Contributor" shall mean Licensor and any individual or Legal Entity
|
| 119 |
-
on behalf of whom a Contribution has been received by Licensor and
|
| 120 |
-
subsequently incorporated within the Work.
|
| 121 |
-
2. Grant of Copyright License. Subject to the terms and conditions of
|
| 122 |
-
this License, each Contributor hereby grants to You a perpetual,
|
| 123 |
-
worldwide, non-exclusive, no-charge, royalty-free, irrevocable
|
| 124 |
-
copyright license to reproduce, prepare Derivative Works of,
|
| 125 |
-
publicly display, publicly perform, sublicense, and distribute the
|
| 126 |
-
Work and such Derivative Works in Source or Object form.
|
| 127 |
-
3. Grant of Patent License. Subject to the terms and conditions of
|
| 128 |
-
this License, each Contributor hereby grants to You a perpetual,
|
| 129 |
-
worldwide, non-exclusive, no-charge, royalty-free, irrevocable
|
| 130 |
-
(except as stated in this section) patent license to make, have made,
|
| 131 |
-
use, offer to sell, sell, import, and otherwise transfer the Work,
|
| 132 |
-
where such license applies only to those patent claims licensable
|
| 133 |
-
by such Contributor that are necessarily infringed by their
|
| 134 |
-
Contribution(s) alone or by combination of their Contribution(s)
|
| 135 |
-
with the Work to which such Contribution(s) was submitted. If You
|
| 136 |
-
institute patent litigation against any entity (including a
|
| 137 |
-
cross-claim or counterclaim in a lawsuit) alleging that the Work
|
| 138 |
-
or a Contribution incorporated within the Work constitutes direct
|
| 139 |
-
or contributory patent infringement, then any patent licenses
|
| 140 |
-
granted to You under this License for that Work shall terminate
|
| 141 |
-
as of the date such litigation is filed.
|
| 142 |
-
4. Redistribution. You may reproduce and distribute copies of the
|
| 143 |
-
Work or Derivative Works thereof in any medium, with or without
|
| 144 |
-
modifications, and in Source or Object form, provided that You
|
| 145 |
-
meet the following conditions:
|
| 146 |
-
(a) You must give any other recipients of the Work or
|
| 147 |
-
Derivative Works a copy of this License; and
|
| 148 |
-
(b) You must cause any modified files to carry prominent notices
|
| 149 |
-
stating that You changed the files; and
|
| 150 |
-
(c) You must retain, in the Source form of any Derivative Works
|
| 151 |
-
that You distribute, all copyright, patent, trademark, and
|
| 152 |
-
attribution notices from the Source form of the Work,
|
| 153 |
-
excluding those notices that do not pertain to any part of
|
| 154 |
-
the Derivative Works; and
|
| 155 |
-
(d) If the Work includes a "NOTICE" text file as part of its
|
| 156 |
-
distribution, then any Derivative Works that You distribute must
|
| 157 |
-
include a readable copy of the attribution notices contained
|
| 158 |
-
within such NOTICE file, excluding those notices that do not
|
| 159 |
-
pertain to any part of the Derivative Works, in at least one
|
| 160 |
-
of the following places: within a NOTICE text file distributed
|
| 161 |
-
as part of the Derivative Works; within the Source form or
|
| 162 |
-
documentation, if provided along with the Derivative Works; or,
|
| 163 |
-
within a display generated by the Derivative Works, if and
|
| 164 |
-
wherever such third-party notices normally appear. The contents
|
| 165 |
-
of the NOTICE file are for informational purposes only and
|
| 166 |
-
do not modify the License. You may add Your own attribution
|
| 167 |
-
notices within Derivative Works that You distribute, alongside
|
| 168 |
-
or as an addendum to the NOTICE text from the Work, provided
|
| 169 |
-
that such additional attribution notices cannot be construed
|
| 170 |
-
as modifying the License.
|
| 171 |
-
You may add Your own copyright statement to Your modifications and
|
| 172 |
-
may provide additional or different license terms and conditions
|
| 173 |
-
for use, reproduction, or distribution of Your modifications, or
|
| 174 |
-
for any such Derivative Works as a whole, provided Your use,
|
| 175 |
-
reproduction, and distribution of the Work otherwise complies with
|
| 176 |
-
the conditions stated in this License.
|
| 177 |
-
5. Submission of Contributions. Unless You explicitly state otherwise,
|
| 178 |
-
any Contribution intentionally submitted for inclusion in the Work
|
| 179 |
-
by You to the Licensor shall be under the terms and conditions of
|
| 180 |
-
this License, without any additional terms or conditions.
|
| 181 |
-
Notwithstanding the above, nothing herein shall supersede or modify
|
| 182 |
-
the terms of any separate license agreement you may have executed
|
| 183 |
-
with Licensor regarding such Contributions.
|
| 184 |
-
6. Trademarks. This License does not grant permission to use the trade
|
| 185 |
-
names, trademarks, service marks, or product names of the Licensor,
|
| 186 |
-
except as required for reasonable and customary use in describing the
|
| 187 |
-
origin of the Work and reproducing the content of the NOTICE file.
|
| 188 |
-
7. Disclaimer of Warranty. Unless required by applicable law or
|
| 189 |
-
agreed to in writing, Licensor provides the Work (and each
|
| 190 |
-
Contributor provides its Contributions) on an "AS IS" BASIS,
|
| 191 |
-
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or
|
| 192 |
-
implied, including, without limitation, any warranties or conditions
|
| 193 |
-
of TITLE, NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A
|
| 194 |
-
PARTICULAR PURPOSE. You are solely responsible for determining the
|
| 195 |
-
appropriateness of using or redistributing the Work and assume any
|
| 196 |
-
risks associated with Your exercise of permissions under this License.
|
| 197 |
-
8. Limitation of Liability. In no event and under no legal theory,
|
| 198 |
-
whether in tort (including negligence), contract, or otherwise,
|
| 199 |
-
unless required by applicable law (such as deliberate and grossly
|
| 200 |
-
negligent acts) or agreed to in writing, shall any Contributor be
|
| 201 |
-
liable to You for damages, including any direct, indirect, special,
|
| 202 |
-
incidental, or consequential damages of any character arising as a
|
| 203 |
-
result of this License or out of the use or inability to use the
|
| 204 |
-
Work (including but not limited to damages for loss of goodwill,
|
| 205 |
-
work stoppage, computer failure or malfunction, or any and all
|
| 206 |
-
other commercial damages or losses), even if such Contributor
|
| 207 |
-
has been advised of the possibility of such damages.
|
| 208 |
-
9. Accepting Warranty or Additional Liability. While redistributing
|
| 209 |
-
the Work or Derivative Works thereof, You may choose to offer,
|
| 210 |
-
and charge a fee for, acceptance of support, warranty, indemnity,
|
| 211 |
-
or other liability obligations and/or rights consistent with this
|
| 212 |
-
License. However, in accepting such obligations, You may act only
|
| 213 |
-
on Your own behalf and on Your sole responsibility, not on behalf
|
| 214 |
-
of any other Contributor, and only if You agree to indemnify,
|
| 215 |
-
defend, and hold each Contributor harmless for any liability
|
| 216 |
-
incurred by, or claims asserted against, such Contributor by reason
|
| 217 |
-
of your accepting any such warranty or additional liability.
|
| 218 |
-
END OF TERMS AND CONDITIONS
|
| 219 |
-
APPENDIX: How to apply the Apache License to your work.
|
| 220 |
-
To apply the Apache License to your work, attach the following
|
| 221 |
-
boilerplate notice, with the fields enclosed by brackets "[]"
|
| 222 |
-
replaced with your own identifying information. (Don't include
|
| 223 |
-
the brackets!) The text should be enclosed in the appropriate
|
| 224 |
-
comment syntax for the file format. We also recommend that a
|
| 225 |
-
file or class name and description of purpose be included on the
|
| 226 |
-
same "printed page" as the copyright notice for easier
|
| 227 |
-
identification within third-party archives.
|
| 228 |
-
|
| 229 |
-
Copyright [yyyy] [name of copyright owner]
|
| 230 |
-
|
| 231 |
-
Licensed under the Apache License, Version 2.0 (the "License");
|
| 232 |
-
you may not use this file except in compliance with the License.
|
| 233 |
-
You may obtain a copy of the License at
|
| 234 |
-
|
| 235 |
-
http://www.apache.org/licenses/LICENSE-2.0
|
| 236 |
-
|
| 237 |
-
Unless required by applicable law or agreed to in writing, software
|
| 238 |
-
distributed under the License is distributed on an "AS IS" BASIS,
|
| 239 |
-
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
| 240 |
-
See the License for the specific language governing permissions and
|
| 241 |
-
limitations under the License.
|
| 242 |
-
|
| 243 |
-
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
README.md
CHANGED
|
@@ -14,7 +14,6 @@ tags:
|
|
| 14 |
- text recognition
|
| 15 |
- layout analysis
|
| 16 |
- ingestion
|
| 17 |
-
new_version: nvidia/nemotron-ocr-v2
|
| 18 |
---
|
| 19 |
|
| 20 |
# Nemotron OCR v1
|
|
@@ -346,4 +345,4 @@ Please report security vulnerabilities or NVIDIA AI Concerns [here](https://app.
|
|
| 346 |
| Model Application Field(s): | Text recognition and structured OCR for multimodal retrieval. Inputs can include natural scene images, scanned documents, charts, tables, and infographics. |
|
| 347 |
| Use Case Restrictions: | Abide by [NVIDIA Open Model License Agreement](https://www.nvidia.com/en-us/agreements/enterprise-software/nvidia-open-model-license/) and the use of the post-processing scripts are licensed under [Apache 2.0](https://www.apache.org/licenses/LICENSE-2.0.txt). |
|
| 348 |
| Model and dataset restrictions: | The principle of least privilege (PoLP) is applied, limiting access for dataset generation and model development. Restrictions enforce dataset access only during training, and all dataset license constraints are adhered to. |
|
| 349 |
-
| Describe the life critical impact (if present): | Not applicable. |
|
|
|
|
| 14 |
- text recognition
|
| 15 |
- layout analysis
|
| 16 |
- ingestion
|
|
|
|
| 17 |
---
|
| 18 |
|
| 19 |
# Nemotron OCR v1
|
|
|
|
| 345 |
| Model Application Field(s): | Text recognition and structured OCR for multimodal retrieval. Inputs can include natural scene images, scanned documents, charts, tables, and infographics. |
|
| 346 |
| Use Case Restrictions: | Abide by [NVIDIA Open Model License Agreement](https://www.nvidia.com/en-us/agreements/enterprise-software/nvidia-open-model-license/) and the use of the post-processing scripts are licensed under [Apache 2.0](https://www.apache.org/licenses/LICENSE-2.0.txt). |
|
| 347 |
| Model and dataset restrictions: | The principle of least privilege (PoLP) is applied, limiting access for dataset generation and model development. Restrictions enforce dataset access only during training, and all dataset license constraints are adhered to. |
|
| 348 |
+
| Describe the life critical impact (if present): | Not applicable. |
|
config.json
CHANGED
|
@@ -1 +0,0 @@
|
|
| 1 |
-
{}
|
|
|
|
|
|
example.py
CHANGED
|
@@ -8,7 +8,7 @@ from nemotron_ocr.inference.pipeline import NemotronOCR
|
|
| 8 |
|
| 9 |
|
| 10 |
def main(image_path, merge_level, no_visualize, model_dir):
|
| 11 |
-
ocr_pipeline = NemotronOCR(
|
| 12 |
|
| 13 |
predictions = ocr_pipeline(image_path, merge_level=merge_level, visualize=not no_visualize)
|
| 14 |
|
|
|
|
| 8 |
|
| 9 |
|
| 10 |
def main(image_path, merge_level, no_visualize, model_dir):
|
| 11 |
+
ocr_pipeline = NemotronOCR()
|
| 12 |
|
| 13 |
predictions = ocr_pipeline(image_path, merge_level=merge_level, visualize=not no_visualize)
|
| 14 |
|
nemotron-ocr/cpp/non_maximal_suppression/cuda_non_maximal_suppression.cu
CHANGED
|
@@ -157,8 +157,11 @@ void device_row_collapse(torch::PackedTensorAccessor64<T, 5> allQuads,
|
|
| 157 |
torch::PackedTensorAccessor64<T, 3> allConfs,
|
| 158 |
T confThreshold, T iouThreshold,
|
| 159 |
torch::PackedTensorAccessor64<int32_t, 1> allOutCounts,
|
| 160 |
-
torch::PackedTensorAccessor64<T, 3> allOutEmbedQuads
|
| 161 |
-
|
|
|
|
|
|
|
|
|
|
| 162 |
{
|
| 163 |
typedef InPlaceQuad_<T> Quadf;
|
| 164 |
static_assert(sizeof(Quadf) == sizeof(T) * 8, "Invalid QuadMem size!");
|
|
@@ -303,9 +306,11 @@ void device_row_collapse(torch::PackedTensorAccessor64<T, 5> allQuads,
|
|
| 303 |
}
|
| 304 |
|
| 305 |
write_embed_quad(outEmbedQuads, outQuad, storeOff + procLabel - 1);
|
|
|
|
| 306 |
if (threadRank == 0) {
|
| 307 |
allOutIds[b][storeOff + procLabel - 1] = r * 32 + startIdx;
|
| 308 |
}
|
|
|
|
| 309 |
}
|
| 310 |
|
| 311 |
if (threadRank == 0) {
|
|
@@ -316,9 +321,9 @@ void device_row_collapse(torch::PackedTensorAccessor64<T, 5> allQuads,
|
|
| 316 |
#undef threadRank
|
| 317 |
}
|
| 318 |
|
| 319 |
-
template<typename T>
|
| 320 |
__global__
|
| 321 |
-
void device_a2a_adjacency_sparse(const
|
| 322 |
T iouThreshold,
|
| 323 |
torch::PackedTensorAccessor64<T, 3> embedQuads,
|
| 324 |
torch::PackedTensorAccessor64<bool, 2> outIsStart,
|
|
@@ -327,11 +332,7 @@ void device_a2a_adjacency_sparse(const int32_t *ptrQuadCts,
|
|
| 327 |
{
|
| 328 |
const uint32_t b = blockIdx.y;
|
| 329 |
|
| 330 |
-
const int32_t quadCt =
|
| 331 |
-
|
| 332 |
-
if (quadCt == 0) {
|
| 333 |
-
return;
|
| 334 |
-
}
|
| 335 |
|
| 336 |
const int32_t jobIdx = blockIdx.x * blockDim.x + threadIdx.x;
|
| 337 |
const int32_t row = jobIdx / quadCt;
|
|
@@ -342,7 +343,7 @@ void device_a2a_adjacency_sparse(const int32_t *ptrQuadCts,
|
|
| 342 |
return;
|
| 343 |
}
|
| 344 |
|
| 345 |
-
T* exData = embedQuads[b].data();
|
| 346 |
|
| 347 |
const auto qRow = StridedEmbedQuad_<T>{ exData + row * embedQuads.stride(2), embedQuads.stride(1) }.Bounds(),
|
| 348 |
qCol = StridedEmbedQuad_<T>{ exData + col * embedQuads.stride(2), embedQuads.stride(1) }.Bounds();
|
|
@@ -404,9 +405,9 @@ void device_a2a_adjacency_sparse(const int32_t *ptrQuadCts,
|
|
| 404 |
}
|
| 405 |
}
|
| 406 |
|
| 407 |
-
template<uint32_t NumWarps, typename T, int32_t I_CELL_SIZE>
|
| 408 |
__global__
|
| 409 |
-
void device_a2a_adjacency_build_grid(const
|
| 410 |
torch::PackedTensorAccessor64<T, 3> embedQuads,
|
| 411 |
torch::PackedTensorAccessor64<int32_t, 4> outGridCells,
|
| 412 |
torch::PackedTensorAccessor64<int32_t, 3> outQuadCells)
|
|
@@ -422,10 +423,10 @@ void device_a2a_adjacency_build_grid(const int32_t *ptrQuadCts,
|
|
| 422 |
|
| 423 |
const uint32_t b = blockIdx.z;
|
| 424 |
|
| 425 |
-
const uint32_t quadCt =
|
| 426 |
const uint32_t quadIdx = blockIdx.y;
|
| 427 |
|
| 428 |
-
if (quadIdx >= quadCt) {
|
| 429 |
return;
|
| 430 |
}
|
| 431 |
|
|
@@ -484,9 +485,9 @@ void device_a2a_adjacency_build_grid(const int32_t *ptrQuadCts,
|
|
| 484 |
|
| 485 |
typedef uint8_t visit_mask_t;
|
| 486 |
|
| 487 |
-
template<uint32_t NumWarps, typename T>
|
| 488 |
__global__
|
| 489 |
-
void device_a2a_adjacency_with_grid(const
|
| 490 |
T iouThreshold,
|
| 491 |
torch::PackedTensorAccessor64<T, 3> allEmbedQuads,
|
| 492 |
torch::PackedTensorAccessor64<int32_t, 4> allCells,
|
|
@@ -502,10 +503,10 @@ void device_a2a_adjacency_with_grid(const int32_t *ptrQuadCts,
|
|
| 502 |
|
| 503 |
const uint32_t b = blockIdx.z;
|
| 504 |
|
| 505 |
-
const uint32_t quadCt =
|
| 506 |
const uint32_t quadIdx = blockIdx.y;
|
| 507 |
|
| 508 |
-
if (quadIdx >= quadCt) {
|
| 509 |
return;
|
| 510 |
}
|
| 511 |
|
|
@@ -534,7 +535,7 @@ void device_a2a_adjacency_with_grid(const int32_t *ptrQuadCts,
|
|
| 534 |
auto exAdjCounts = reinterpret_cast<uint32_t*>(outAdjCounts[b].data());
|
| 535 |
auto exAdjValues = outSparseAdj[b][quadIdx].data();
|
| 536 |
|
| 537 |
-
T *exData = allEmbedQuads[b].data();
|
| 538 |
|
| 539 |
const auto bdsAnchor = Quad_<T>{ s_quadVerts }.Bounds();
|
| 540 |
|
|
@@ -598,8 +599,9 @@ void device_a2a_adjacency_with_grid(const int32_t *ptrQuadCts,
|
|
| 598 |
}
|
| 599 |
}
|
| 600 |
|
|
|
|
| 601 |
__global__
|
| 602 |
-
void device_flatten_graph_iterative(const
|
| 603 |
torch::PackedTensorAccessor64<bool, 2> allIsStart,
|
| 604 |
volatile uint32_t *allAdjCounts,
|
| 605 |
volatile uint32_t *allAdjValues
|
|
@@ -620,12 +622,14 @@ void device_flatten_graph_iterative(const int32_t *ptrQuadCts,
|
|
| 620 |
const uint32_t b = blockIdx.z;
|
| 621 |
const uint32_t anchorRow = blockIdx.y;
|
| 622 |
|
| 623 |
-
const uint32_t quadCt =
|
| 624 |
|
| 625 |
// Only need to check this if there are multiple examples, since in the case of a single example,
|
| 626 |
// the grid is precisely sized to that quadCt
|
| 627 |
-
if
|
| 628 |
-
|
|
|
|
|
|
|
| 629 |
}
|
| 630 |
|
| 631 |
auto isStart = allIsStart[b].data();
|
|
@@ -686,13 +690,12 @@ void device_flatten_graph_iterative(const int32_t *ptrQuadCts,
|
|
| 686 |
visitStack[1] = anchorRow;
|
| 687 |
#ifndef NDEBUG
|
| 688 |
for (uint32_t i = 2; i < VISIT_STACK_SIZE; ++i) {
|
| 689 |
-
visitStack[i] =
|
| 690 |
}
|
| 691 |
#endif
|
| 692 |
int32_t visitPtr = 1;
|
| 693 |
|
| 694 |
-
|
| 695 |
-
for (uint32_t dfsIter = 0; true; ++dfsIter) {
|
| 696 |
#ifdef NMS_VERIFY_CORRECTNESS
|
| 697 |
assert(visitPtr >= 0 && visitPtr < VISIT_STACK_SIZE);
|
| 698 |
#endif
|
|
@@ -704,7 +707,7 @@ void device_flatten_graph_iterative(const int32_t *ptrQuadCts,
|
|
| 704 |
if (threadNextCol == warpNextCol) {
|
| 705 |
#ifndef NDEBUG
|
| 706 |
// This makes it easier to debug where the pointer is
|
| 707 |
-
visitStack[visitPtr] =
|
| 708 |
#endif
|
| 709 |
--visitPtr;
|
| 710 |
}
|
|
@@ -728,15 +731,12 @@ void device_flatten_graph_iterative(const int32_t *ptrQuadCts,
|
|
| 728 |
const uint32_t procAdjCount = adjCounts[procRow];
|
| 729 |
auto procAdjValues = adjValues + (procRow * maxExCount);
|
| 730 |
|
|
|
|
|
|
|
|
|
|
|
|
|
| 731 |
for (uint32_t i = threadRank; i < procAdjCount; i += WARP_SIZE) {
|
| 732 |
-
uint32_t adjCol = procAdjValues[i];
|
| 733 |
-
|
| 734 |
-
auto group = cg::coalesced_threads();
|
| 735 |
-
// Offsetting by the iteration number will help balance out the maximum depth of any stack in the warp.
|
| 736 |
-
// The reason behind this is due to how otherwise, warp-0 will always get a new element, warp-1 iff the adj graph
|
| 737 |
-
// has more than one element, warp-2 iff the adj graph has more than two elements, and so on. Basically,
|
| 738 |
-
// the warps have decreasing pressure. With the rotation mechanism, it helps to balance out stack usage.
|
| 739 |
-
adjCol = group.shfl(adjCol, (group.thread_rank() + dfsIter) % group.size());
|
| 740 |
|
| 741 |
// This will set the queued flag for this column, if it's not already set.
|
| 742 |
// It also returns the old state. In our case, we only want to add this value to the
|
|
@@ -748,6 +748,7 @@ void device_flatten_graph_iterative(const int32_t *ptrQuadCts,
|
|
| 748 |
|
| 749 |
bool alreadyAdded = oldMask & ADDED_MASK;
|
| 750 |
|
|
|
|
| 751 |
const uint32_t gThreadRank = group.thread_rank();
|
| 752 |
uint32_t notAddedBallot = group.ballot(!alreadyAdded);
|
| 753 |
if (notAddedBallot) {
|
|
@@ -824,7 +825,8 @@ void add_to_set(const torch::TensorAccessor<int32_t, 1>& adjCounts,
|
|
| 824 |
}
|
| 825 |
}
|
| 826 |
|
| 827 |
-
|
|
|
|
| 828 |
torch::Tensor isStartTensorGPU,
|
| 829 |
torch::Tensor adjCountsTensorGPU,
|
| 830 |
torch::Tensor adjValuesTensorGPU)
|
|
@@ -838,7 +840,7 @@ void cpu_flatten_graph(const int32_t *ptrQuadCts,
|
|
| 838 |
auto allAdjValues = adjValuesTensor.accessor<int32_t, 3>();
|
| 839 |
|
| 840 |
for (int32_t b = 0; b < allAdjCounts.size(0); ++b) {
|
| 841 |
-
const int32_t quadCt =
|
| 842 |
|
| 843 |
for (int32_t row = 0; row < quadCt; ++row) {
|
| 844 |
std::unordered_set<int32_t> fullAdjSet;
|
|
@@ -893,9 +895,9 @@ void device_a2a_adj_cleanup(const int32_t *counts,
|
|
| 893 |
}
|
| 894 |
}
|
| 895 |
|
| 896 |
-
template<uint32_t NumWarps, typename T>
|
| 897 |
__global__
|
| 898 |
-
void device_a2a_collapse(
|
| 899 |
torch::PackedTensorAccessor64<T, 3> allEmbedQuads,
|
| 900 |
torch::PackedTensorAccessor64<bool, 2> allIsLeadRow,
|
| 901 |
const int64_t *regionCounts,
|
|
@@ -915,14 +917,16 @@ void device_a2a_collapse(torch::PackedTensorAccessor64<int32_t, 1> quadCounts,
|
|
| 915 |
const uint32_t b = blockIdx.z;
|
| 916 |
const uint32_t row = blockIdx.y;
|
| 917 |
|
| 918 |
-
const int32_t quadCt =
|
| 919 |
|
| 920 |
-
if
|
| 921 |
-
|
|
|
|
|
|
|
| 922 |
}
|
| 923 |
|
| 924 |
// Only process the lead rows
|
| 925 |
-
const auto isLeadRow = allIsLeadRow[b].data();
|
| 926 |
if (!isLeadRow[row]) {
|
| 927 |
return;
|
| 928 |
}
|
|
@@ -941,7 +945,7 @@ void device_a2a_collapse(torch::PackedTensorAccessor64<int32_t, 1> quadCounts,
|
|
| 941 |
__syncthreads();
|
| 942 |
}
|
| 943 |
|
| 944 |
-
T *exData = allEmbedQuads[b].data();
|
| 945 |
|
| 946 |
const int32_t adjCount = allAdjCounts[b][row];
|
| 947 |
const int32_t *adjIdxs = allAdjValues[b][row].data();
|
|
@@ -982,12 +986,20 @@ void device_a2a_collapse(torch::PackedTensorAccessor64<int32_t, 1> quadCounts,
|
|
| 982 |
|
| 983 |
// Figure out the output position
|
| 984 |
uint32_t writePosition = 0;
|
| 985 |
-
|
| 986 |
-
|
|
|
|
|
|
|
| 987 |
}
|
| 988 |
|
|
|
|
| 989 |
const uint8_t *pCurrIsLeadRow = reinterpret_cast<const uint8_t*>(isLeadRow);
|
| 990 |
-
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 991 |
if (pCurrIsLeadRow[i]) {
|
| 992 |
++writePosition;
|
| 993 |
}
|
|
@@ -1063,9 +1075,13 @@ CollapseRowsResult collapse_rows(
|
|
| 1063 |
int64_t embedSize = sizeof(EmbedQuad_<scalar_t>) / sizeof(scalar_t);
|
| 1064 |
auto rowMergeTensor = torch::empty({ quads.size(0), embedSize, quads.size(1) * quads.size(2) }, quads.options());
|
| 1065 |
|
|
|
|
| 1066 |
auto idsTensor = torch::full({ quads.size(0), quads.size(1) * quads.size(2) },
|
| 1067 |
std::numeric_limits<int32_t>::max(),
|
| 1068 |
counts.options().dtype(torch::kInt32));
|
|
|
|
|
|
|
|
|
|
| 1069 |
|
| 1070 |
dim3 blockSize(32, 3, 1);
|
| 1071 |
dim3 gridSize(1,
|
|
@@ -1077,8 +1093,10 @@ CollapseRowsResult collapse_rows(
|
|
| 1077 |
probs.packed_accessor64<scalar_t, 3>(),
|
| 1078 |
probThreshold, iouThreshold,
|
| 1079 |
counts.packed_accessor64<int32_t, 1>(),
|
| 1080 |
-
rowMergeTensor.packed_accessor64<scalar_t, 3>()
|
| 1081 |
-
|
|
|
|
|
|
|
| 1082 |
);
|
| 1083 |
|
| 1084 |
#ifdef NMS_VERIFY_CORRECTNESS
|
|
@@ -1101,6 +1119,7 @@ CollapseRowsResult collapse_rows(
|
|
| 1101 |
|
| 1102 |
counts = counts.slice(/*dim=*/ 0, 0, counts.size(0) - 1);
|
| 1103 |
|
|
|
|
| 1104 |
int64_t maxExCount;
|
| 1105 |
if (counts.size(0) > 1) {
|
| 1106 |
maxExCount = counts.max().item<int32_t>();
|
|
@@ -1112,12 +1131,13 @@ CollapseRowsResult collapse_rows(
|
|
| 1112 |
|
| 1113 |
rowMergeTensor = rowMergeTensor.slice(2, 0, maxExCount);
|
| 1114 |
idsTensor = idsTensor.slice(1, 0, maxExCount);
|
| 1115 |
-
auto order = torch::argsort(idsTensor, /*dim=*/ 1, s_sortOrder);
|
| 1116 |
|
| 1117 |
auto embOrder = order.unsqueeze(1).expand_as(rowMergeTensor);
|
| 1118 |
|
| 1119 |
rowMergeTensor = torch::gather(rowMergeTensor, /*dim=*/ 2, embOrder);
|
| 1120 |
idsTensor = torch::gather(idsTensor, /*dim=*/ 1, order);
|
|
|
|
| 1121 |
|
| 1122 |
return { counts, rowMergeTensor, totalQuads, idsTensor, imageWidth, imageHeight };
|
| 1123 |
}
|
|
@@ -1157,8 +1177,8 @@ struct AdjacencyResult {
|
|
| 1157 |
int64_t MaxExCount;
|
| 1158 |
};
|
| 1159 |
|
| 1160 |
-
template<typename T>
|
| 1161 |
-
void cpu_a2a_adjacency_sparse(const
|
| 1162 |
const T iouThreshold,
|
| 1163 |
torch::Tensor embedQuadsTensor,
|
| 1164 |
torch::Tensor outIsStartTensorGPU,
|
|
@@ -1176,7 +1196,7 @@ void cpu_a2a_adjacency_sparse(const int32_t *ptrQuadCts,
|
|
| 1176 |
auto adjValues = outSparseAdjTensor.accessor<int32_t, 3>();
|
| 1177 |
|
| 1178 |
for (int32_t b = 0; b < embedQuadsTensor.size(0); ++b) {
|
| 1179 |
-
const int32_t quadCt =
|
| 1180 |
|
| 1181 |
T *exData = embedQuads[b].data();
|
| 1182 |
|
|
@@ -1264,6 +1284,13 @@ AdjacencyResult compute_all_to_all_adjacency(
|
|
| 1264 |
counts.options().dtype(torch::kInt32));
|
| 1265 |
#endif
|
| 1266 |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1267 |
#ifdef NMS_VERIFY_CORRECTNESS
|
| 1268 |
auto cpuAdjValuesTensor = adjValuesTensor.cpu();
|
| 1269 |
auto cpuAdjCountsTensor = adjCountsTensor.cpu();
|
|
@@ -1291,15 +1318,23 @@ AdjacencyResult compute_all_to_all_adjacency(
|
|
| 1291 |
//blockSize = dim3{ GRID_NUM_WARPS * 32, 1, 1 };
|
| 1292 |
//gridSize = dim3{ 1, static_cast<uint32_t>(maxExCount), static_cast<uint32_t>(counts.size(0)) };
|
| 1293 |
|
| 1294 |
-
//
|
| 1295 |
-
//
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1296 |
// collapseResult.StridedMergeQuads.packed_accessor64<scalar_t, 3>(),
|
| 1297 |
// gridCellsTensor.packed_accessor64<int32_t, 4>(),
|
| 1298 |
// quadCellExtentsTensor.packed_accessor64<int32_t, 3>()
|
| 1299 |
//);
|
| 1300 |
|
| 1301 |
-
//
|
| 1302 |
-
//
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1303 |
// iouThreshold,
|
| 1304 |
// collapseResult.StridedMergeQuads.packed_accessor64<scalar_t, 3>(),
|
| 1305 |
// gridCellsTensor.packed_accessor64<int32_t, 4>(),
|
|
@@ -1316,9 +1351,11 @@ AdjacencyResult compute_all_to_all_adjacency(
|
|
| 1316 |
gridSize = dim3{div_up(totalWork, blockSize.x),
|
| 1317 |
static_cast<uint32_t>(counts.size(0))};
|
| 1318 |
|
|
|
|
|
|
|
| 1319 |
// This algorithm is O(n^2) with n being the current number of quads
|
| 1320 |
-
|
| 1321 |
-
|
| 1322 |
iouThreshold,
|
| 1323 |
collapseResult.StridedMergeQuads.packed_accessor64<scalar_t, 3>(),
|
| 1324 |
isStartTensor.packed_accessor64<bool, 2>(),
|
|
@@ -1328,9 +1365,7 @@ AdjacencyResult compute_all_to_all_adjacency(
|
|
| 1328 |
|
| 1329 |
|
| 1330 |
#ifdef NMS_VERIFY_CORRECTNESS
|
| 1331 |
-
|
| 1332 |
-
|
| 1333 |
-
cpu_a2a_adjacency_sparse<scalar_t>(cpuCounts.data_ptr<int32_t>(), iouThreshold,
|
| 1334 |
collapseResult.StridedMergeQuads, cpuIsStartTensor, cpuAdjCountsTensor, cpuAdjValuesTensor);
|
| 1335 |
|
| 1336 |
adjValuesTensor = std::get<0>(torch::sort(adjValuesTensor, /*dim=*/ 2));
|
|
@@ -1345,12 +1380,16 @@ AdjacencyResult compute_all_to_all_adjacency(
|
|
| 1345 |
auto maxDepthTensor = torch::tensor(0, adjCountsTensor.options());
|
| 1346 |
#endif
|
| 1347 |
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1348 |
blockSize = dim3{ 128, 1, 1 };
|
| 1349 |
gridSize = dim3{ 1, static_cast<uint32_t>(maxExCount), static_cast<uint32_t>(counts.size(0)) };
|
| 1350 |
smemSize = div_up(maxExCount * sizeof(visit_mask_t), sizeof(uint32_t)) * sizeof(uint32_t);
|
| 1351 |
|
| 1352 |
-
|
| 1353 |
-
|
| 1354 |
isStartTensor.packed_accessor64<bool, 2>(),
|
| 1355 |
reinterpret_cast<uint32_t*>(adjCountsTensor.data_ptr<int32_t>()),
|
| 1356 |
reinterpret_cast<uint32_t*>(adjValuesTensor.data_ptr<int32_t>())
|
|
@@ -1360,7 +1399,7 @@ AdjacencyResult compute_all_to_all_adjacency(
|
|
| 1360 |
);
|
| 1361 |
|
| 1362 |
#ifdef NMS_VERIFY_CORRECTNESS
|
| 1363 |
-
cpu_flatten_graph
|
| 1364 |
|
| 1365 |
cpuAdjValuesTensor = std::get<0>(torch::sort(cpuAdjValuesTensor, /*dim=*/ 2));
|
| 1366 |
adjValuesTensor = std::get<0>(torch::sort(adjValuesTensor, /*dim=*/ 2));
|
|
@@ -1398,6 +1437,7 @@ AdjacencyResult compute_all_to_all_adjacency(
|
|
| 1398 |
cpuIsStartTensor = isStartTensor.cpu();
|
| 1399 |
cpuAdjCountsTensor = adjCountsTensor.cpu();
|
| 1400 |
cpuAdjValuesTensor = adjValuesTensor.cpu();
|
|
|
|
| 1401 |
auto cpuCollapseIds = collapseResult.QuadIds.cpu();
|
| 1402 |
|
| 1403 |
static std::vector<std::unordered_set<int32_t>> s_knownGroups;
|
|
@@ -1549,11 +1589,22 @@ nms_result_t
|
|
| 1549 |
dim3 blockSize(NUM_WARPS * 32, 1, 1);
|
| 1550 |
dim3 gridSize(1, adjResult.MaxExCount, counts.size(0));
|
| 1551 |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1552 |
torch::Tensor outQuads = torch::empty({ numOutQuads, 4, 2 }, embedQuads.options());
|
| 1553 |
torch::Tensor outConf = torch::empty({ numOutQuads }, embedQuads.options());
|
| 1554 |
|
| 1555 |
-
|
| 1556 |
-
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1557 |
embedQuads.packed_accessor64<scalar_t, 3>(),
|
| 1558 |
isLeadRow.packed_accessor64<bool, 2>(),
|
| 1559 |
regionCounts.data_ptr<int64_t>(),
|
|
|
|
| 157 |
torch::PackedTensorAccessor64<T, 3> allConfs,
|
| 158 |
T confThreshold, T iouThreshold,
|
| 159 |
torch::PackedTensorAccessor64<int32_t, 1> allOutCounts,
|
| 160 |
+
torch::PackedTensorAccessor64<T, 3> allOutEmbedQuads
|
| 161 |
+
#ifdef NMS_VERIFY_CORRECTNESS
|
| 162 |
+
, torch::PackedTensorAccessor64<int32_t, 2> allOutIds
|
| 163 |
+
#endif
|
| 164 |
+
)
|
| 165 |
{
|
| 166 |
typedef InPlaceQuad_<T> Quadf;
|
| 167 |
static_assert(sizeof(Quadf) == sizeof(T) * 8, "Invalid QuadMem size!");
|
|
|
|
| 306 |
}
|
| 307 |
|
| 308 |
write_embed_quad(outEmbedQuads, outQuad, storeOff + procLabel - 1);
|
| 309 |
+
#ifdef NMS_VERIFY_CORRECTNESS
|
| 310 |
if (threadRank == 0) {
|
| 311 |
allOutIds[b][storeOff + procLabel - 1] = r * 32 + startIdx;
|
| 312 |
}
|
| 313 |
+
#endif
|
| 314 |
}
|
| 315 |
|
| 316 |
if (threadRank == 0) {
|
|
|
|
| 321 |
#undef threadRank
|
| 322 |
}
|
| 323 |
|
| 324 |
+
template<bool IsSingleExample, typename T>
|
| 325 |
__global__
|
| 326 |
+
void device_a2a_adjacency_sparse(const uint64_t punCounts,
|
| 327 |
T iouThreshold,
|
| 328 |
torch::PackedTensorAccessor64<T, 3> embedQuads,
|
| 329 |
torch::PackedTensorAccessor64<bool, 2> outIsStart,
|
|
|
|
| 332 |
{
|
| 333 |
const uint32_t b = blockIdx.y;
|
| 334 |
|
| 335 |
+
const int32_t quadCt = IsSingleExample ? punCounts : reinterpret_cast<const int32_t*>(punCounts)[b];
|
|
|
|
|
|
|
|
|
|
|
|
|
| 336 |
|
| 337 |
const int32_t jobIdx = blockIdx.x * blockDim.x + threadIdx.x;
|
| 338 |
const int32_t row = jobIdx / quadCt;
|
|
|
|
| 343 |
return;
|
| 344 |
}
|
| 345 |
|
| 346 |
+
T* exData = IsSingleExample ? embedQuads.data() : embedQuads[b].data();
|
| 347 |
|
| 348 |
const auto qRow = StridedEmbedQuad_<T>{ exData + row * embedQuads.stride(2), embedQuads.stride(1) }.Bounds(),
|
| 349 |
qCol = StridedEmbedQuad_<T>{ exData + col * embedQuads.stride(2), embedQuads.stride(1) }.Bounds();
|
|
|
|
| 405 |
}
|
| 406 |
}
|
| 407 |
|
| 408 |
+
template<uint32_t NumWarps, bool IsSingleExample, typename T, int32_t I_CELL_SIZE>
|
| 409 |
__global__
|
| 410 |
+
void device_a2a_adjacency_build_grid(const uint64_t punCounts,
|
| 411 |
torch::PackedTensorAccessor64<T, 3> embedQuads,
|
| 412 |
torch::PackedTensorAccessor64<int32_t, 4> outGridCells,
|
| 413 |
torch::PackedTensorAccessor64<int32_t, 3> outQuadCells)
|
|
|
|
| 423 |
|
| 424 |
const uint32_t b = blockIdx.z;
|
| 425 |
|
| 426 |
+
const uint32_t quadCt = IsSingleExample ? punCounts : reinterpret_cast<const int32_t*>(punCounts)[b];
|
| 427 |
const uint32_t quadIdx = blockIdx.y;
|
| 428 |
|
| 429 |
+
if (!IsSingleExample && quadIdx >= quadCt) {
|
| 430 |
return;
|
| 431 |
}
|
| 432 |
|
|
|
|
| 485 |
|
| 486 |
typedef uint8_t visit_mask_t;
|
| 487 |
|
| 488 |
+
template<uint32_t NumWarps, bool IsSingleExample, typename T>
|
| 489 |
__global__
|
| 490 |
+
void device_a2a_adjacency_with_grid(const uint64_t punCounts,
|
| 491 |
T iouThreshold,
|
| 492 |
torch::PackedTensorAccessor64<T, 3> allEmbedQuads,
|
| 493 |
torch::PackedTensorAccessor64<int32_t, 4> allCells,
|
|
|
|
| 503 |
|
| 504 |
const uint32_t b = blockIdx.z;
|
| 505 |
|
| 506 |
+
const uint32_t quadCt = IsSingleExample ? punCounts : reinterpret_cast<const int32_t*>(punCounts)[b];
|
| 507 |
const uint32_t quadIdx = blockIdx.y;
|
| 508 |
|
| 509 |
+
if (!IsSingleExample && quadIdx >= quadCt) {
|
| 510 |
return;
|
| 511 |
}
|
| 512 |
|
|
|
|
| 535 |
auto exAdjCounts = reinterpret_cast<uint32_t*>(outAdjCounts[b].data());
|
| 536 |
auto exAdjValues = outSparseAdj[b][quadIdx].data();
|
| 537 |
|
| 538 |
+
T *exData = IsSingleExample ? allEmbedQuads.data() : allEmbedQuads[b].data();
|
| 539 |
|
| 540 |
const auto bdsAnchor = Quad_<T>{ s_quadVerts }.Bounds();
|
| 541 |
|
|
|
|
| 599 |
}
|
| 600 |
}
|
| 601 |
|
| 602 |
+
template<bool IsSingleExample>
|
| 603 |
__global__
|
| 604 |
+
void device_flatten_graph_iterative(const uint64_t punCounts,
|
| 605 |
torch::PackedTensorAccessor64<bool, 2> allIsStart,
|
| 606 |
volatile uint32_t *allAdjCounts,
|
| 607 |
volatile uint32_t *allAdjValues
|
|
|
|
| 622 |
const uint32_t b = blockIdx.z;
|
| 623 |
const uint32_t anchorRow = blockIdx.y;
|
| 624 |
|
| 625 |
+
const uint32_t quadCt = IsSingleExample ? punCounts : reinterpret_cast<const int32_t*>(punCounts)[b];
|
| 626 |
|
| 627 |
// Only need to check this if there are multiple examples, since in the case of a single example,
|
| 628 |
// the grid is precisely sized to that quadCt
|
| 629 |
+
if constexpr (!IsSingleExample) {
|
| 630 |
+
if (anchorRow >= quadCt) {
|
| 631 |
+
return;
|
| 632 |
+
}
|
| 633 |
}
|
| 634 |
|
| 635 |
auto isStart = allIsStart[b].data();
|
|
|
|
| 690 |
visitStack[1] = anchorRow;
|
| 691 |
#ifndef NDEBUG
|
| 692 |
for (uint32_t i = 2; i < VISIT_STACK_SIZE; ++i) {
|
| 693 |
+
visitStack[i] = -2;
|
| 694 |
}
|
| 695 |
#endif
|
| 696 |
int32_t visitPtr = 1;
|
| 697 |
|
| 698 |
+
while (true) {
|
|
|
|
| 699 |
#ifdef NMS_VERIFY_CORRECTNESS
|
| 700 |
assert(visitPtr >= 0 && visitPtr < VISIT_STACK_SIZE);
|
| 701 |
#endif
|
|
|
|
| 707 |
if (threadNextCol == warpNextCol) {
|
| 708 |
#ifndef NDEBUG
|
| 709 |
// This makes it easier to debug where the pointer is
|
| 710 |
+
visitStack[visitPtr] = -2;
|
| 711 |
#endif
|
| 712 |
--visitPtr;
|
| 713 |
}
|
|
|
|
| 731 |
const uint32_t procAdjCount = adjCounts[procRow];
|
| 732 |
auto procAdjValues = adjValues + (procRow * maxExCount);
|
| 733 |
|
| 734 |
+
// Offsetting by the iteration number will help balance out the maximum depth of any stack in the warp.
|
| 735 |
+
// The reason behind this is due to how otherwise, warp-0 will always get a new element, warp-1 iff the adj graph
|
| 736 |
+
// has more than one element, warp-2 iff the adj graph has more than two elements, and so on. Basically,
|
| 737 |
+
// the warps have decreasing pressure. With the rotation mechanism, it helps to balance out stack usage.
|
| 738 |
for (uint32_t i = threadRank; i < procAdjCount; i += WARP_SIZE) {
|
| 739 |
+
const uint32_t adjCol = procAdjValues[i];
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 740 |
|
| 741 |
// This will set the queued flag for this column, if it's not already set.
|
| 742 |
// It also returns the old state. In our case, we only want to add this value to the
|
|
|
|
| 748 |
|
| 749 |
bool alreadyAdded = oldMask & ADDED_MASK;
|
| 750 |
|
| 751 |
+
auto group = cg::coalesced_threads();
|
| 752 |
const uint32_t gThreadRank = group.thread_rank();
|
| 753 |
uint32_t notAddedBallot = group.ballot(!alreadyAdded);
|
| 754 |
if (notAddedBallot) {
|
|
|
|
| 825 |
}
|
| 826 |
}
|
| 827 |
|
| 828 |
+
template<bool IsSingleExample>
|
| 829 |
+
void cpu_flatten_graph(const uint64_t punCounts,
|
| 830 |
torch::Tensor isStartTensorGPU,
|
| 831 |
torch::Tensor adjCountsTensorGPU,
|
| 832 |
torch::Tensor adjValuesTensorGPU)
|
|
|
|
| 840 |
auto allAdjValues = adjValuesTensor.accessor<int32_t, 3>();
|
| 841 |
|
| 842 |
for (int32_t b = 0; b < allAdjCounts.size(0); ++b) {
|
| 843 |
+
const int32_t quadCt = IsSingleExample ? punCounts : reinterpret_cast<const int32_t*>(punCounts)[b];
|
| 844 |
|
| 845 |
for (int32_t row = 0; row < quadCt; ++row) {
|
| 846 |
std::unordered_set<int32_t> fullAdjSet;
|
|
|
|
| 895 |
}
|
| 896 |
}
|
| 897 |
|
| 898 |
+
template<uint32_t NumWarps, typename T, bool IsSingleExample>
|
| 899 |
__global__
|
| 900 |
+
void device_a2a_collapse(const uint64_t punCounts,
|
| 901 |
torch::PackedTensorAccessor64<T, 3> allEmbedQuads,
|
| 902 |
torch::PackedTensorAccessor64<bool, 2> allIsLeadRow,
|
| 903 |
const int64_t *regionCounts,
|
|
|
|
| 917 |
const uint32_t b = blockIdx.z;
|
| 918 |
const uint32_t row = blockIdx.y;
|
| 919 |
|
| 920 |
+
const int32_t quadCt = IsSingleExample ? punCounts : reinterpret_cast<const int32_t*>(punCounts)[b];
|
| 921 |
|
| 922 |
+
if constexpr (!IsSingleExample) {
|
| 923 |
+
if (row >= quadCt) {
|
| 924 |
+
return;
|
| 925 |
+
}
|
| 926 |
}
|
| 927 |
|
| 928 |
// Only process the lead rows
|
| 929 |
+
const auto isLeadRow = IsSingleExample ? allIsLeadRow.data() : allIsLeadRow[b].data();
|
| 930 |
if (!isLeadRow[row]) {
|
| 931 |
return;
|
| 932 |
}
|
|
|
|
| 945 |
__syncthreads();
|
| 946 |
}
|
| 947 |
|
| 948 |
+
T *exData = IsSingleExample ? allEmbedQuads.data() : allEmbedQuads[b].data();
|
| 949 |
|
| 950 |
const int32_t adjCount = allAdjCounts[b][row];
|
| 951 |
const int32_t *adjIdxs = allAdjValues[b][row].data();
|
|
|
|
| 986 |
|
| 987 |
// Figure out the output position
|
| 988 |
uint32_t writePosition = 0;
|
| 989 |
+
if constexpr (!IsSingleExample) {
|
| 990 |
+
for (int32_t i = threadRank; i < b; i += BLOCK_WIDTH) {
|
| 991 |
+
writePosition += regionCounts[i];
|
| 992 |
+
}
|
| 993 |
}
|
| 994 |
|
| 995 |
+
const int32_t numLongs = row >> 3; // Divide by 8
|
| 996 |
const uint8_t *pCurrIsLeadRow = reinterpret_cast<const uint8_t*>(isLeadRow);
|
| 997 |
+
const uint64_t *lpCurrIsLeadRow = reinterpret_cast<const uint64_t*>(pCurrIsLeadRow);
|
| 998 |
+
|
| 999 |
+
for (int32_t i = threadRank; i < numLongs; i += BLOCK_WIDTH) {
|
| 1000 |
+
writePosition += __popcll(lpCurrIsLeadRow[i]);
|
| 1001 |
+
}
|
| 1002 |
+
for (int32_t i = (numLongs * 8) + threadRank; i < row; i += BLOCK_WIDTH) {
|
| 1003 |
if (pCurrIsLeadRow[i]) {
|
| 1004 |
++writePosition;
|
| 1005 |
}
|
|
|
|
| 1075 |
int64_t embedSize = sizeof(EmbedQuad_<scalar_t>) / sizeof(scalar_t);
|
| 1076 |
auto rowMergeTensor = torch::empty({ quads.size(0), embedSize, quads.size(1) * quads.size(2) }, quads.options());
|
| 1077 |
|
| 1078 |
+
#ifdef NMS_VERIFY_CORRECTNESS
|
| 1079 |
auto idsTensor = torch::full({ quads.size(0), quads.size(1) * quads.size(2) },
|
| 1080 |
std::numeric_limits<int32_t>::max(),
|
| 1081 |
counts.options().dtype(torch::kInt32));
|
| 1082 |
+
#else
|
| 1083 |
+
torch::Tensor idsTensor;
|
| 1084 |
+
#endif
|
| 1085 |
|
| 1086 |
dim3 blockSize(32, 3, 1);
|
| 1087 |
dim3 gridSize(1,
|
|
|
|
| 1093 |
probs.packed_accessor64<scalar_t, 3>(),
|
| 1094 |
probThreshold, iouThreshold,
|
| 1095 |
counts.packed_accessor64<int32_t, 1>(),
|
| 1096 |
+
rowMergeTensor.packed_accessor64<scalar_t, 3>()
|
| 1097 |
+
#ifdef NMS_VERIFY_CORRECTNESS
|
| 1098 |
+
, idsTensor.packed_accessor64<int32_t, 2>()
|
| 1099 |
+
#endif
|
| 1100 |
);
|
| 1101 |
|
| 1102 |
#ifdef NMS_VERIFY_CORRECTNESS
|
|
|
|
| 1119 |
|
| 1120 |
counts = counts.slice(/*dim=*/ 0, 0, counts.size(0) - 1);
|
| 1121 |
|
| 1122 |
+
#ifdef NMS_VERIFY_CORRECTNESS
|
| 1123 |
int64_t maxExCount;
|
| 1124 |
if (counts.size(0) > 1) {
|
| 1125 |
maxExCount = counts.max().item<int32_t>();
|
|
|
|
| 1131 |
|
| 1132 |
rowMergeTensor = rowMergeTensor.slice(2, 0, maxExCount);
|
| 1133 |
idsTensor = idsTensor.slice(1, 0, maxExCount);
|
| 1134 |
+
auto order = torch::argsort(idsTensor, /*dim=*/ 1, s_sortOrder); s_sortOrder = !s_sortOrder;
|
| 1135 |
|
| 1136 |
auto embOrder = order.unsqueeze(1).expand_as(rowMergeTensor);
|
| 1137 |
|
| 1138 |
rowMergeTensor = torch::gather(rowMergeTensor, /*dim=*/ 2, embOrder);
|
| 1139 |
idsTensor = torch::gather(idsTensor, /*dim=*/ 1, order);
|
| 1140 |
+
#endif
|
| 1141 |
|
| 1142 |
return { counts, rowMergeTensor, totalQuads, idsTensor, imageWidth, imageHeight };
|
| 1143 |
}
|
|
|
|
| 1177 |
int64_t MaxExCount;
|
| 1178 |
};
|
| 1179 |
|
| 1180 |
+
template<bool IsSingleExample, typename T>
|
| 1181 |
+
void cpu_a2a_adjacency_sparse(const uint64_t punCounts,
|
| 1182 |
const T iouThreshold,
|
| 1183 |
torch::Tensor embedQuadsTensor,
|
| 1184 |
torch::Tensor outIsStartTensorGPU,
|
|
|
|
| 1196 |
auto adjValues = outSparseAdjTensor.accessor<int32_t, 3>();
|
| 1197 |
|
| 1198 |
for (int32_t b = 0; b < embedQuadsTensor.size(0); ++b) {
|
| 1199 |
+
const int32_t quadCt = IsSingleExample ? punCounts : reinterpret_cast<const int32_t*>(punCounts)[b];
|
| 1200 |
|
| 1201 |
T *exData = embedQuads[b].data();
|
| 1202 |
|
|
|
|
| 1284 |
counts.options().dtype(torch::kInt32));
|
| 1285 |
#endif
|
| 1286 |
|
| 1287 |
+
// If the batch is only a single example, instead of hitting global memory for the count, we can
|
| 1288 |
+
// just encode the count into the pointer instead
|
| 1289 |
+
uint64_t ptrCounts = reinterpret_cast<uint64_t>(counts.data_ptr<int32_t>());
|
| 1290 |
+
if (counts.size(0) == 1) {
|
| 1291 |
+
ptrCounts = maxExCount;
|
| 1292 |
+
}
|
| 1293 |
+
|
| 1294 |
#ifdef NMS_VERIFY_CORRECTNESS
|
| 1295 |
auto cpuAdjValuesTensor = adjValuesTensor.cpu();
|
| 1296 |
auto cpuAdjCountsTensor = adjCountsTensor.cpu();
|
|
|
|
| 1318 |
//blockSize = dim3{ GRID_NUM_WARPS * 32, 1, 1 };
|
| 1319 |
//gridSize = dim3{ 1, static_cast<uint32_t>(maxExCount), static_cast<uint32_t>(counts.size(0)) };
|
| 1320 |
|
| 1321 |
+
//auto buildGridFn = counts.size(0) == 1 ?
|
| 1322 |
+
// device_a2a_adjacency_build_grid<GRID_NUM_WARPS, true, scalar_t, CELL_SIZE> :
|
| 1323 |
+
// device_a2a_adjacency_build_grid<GRID_NUM_WARPS, false, scalar_t, CELL_SIZE>;
|
| 1324 |
+
|
| 1325 |
+
//buildGridFn KERNEL_ARG2(gridSize, blockSize) (
|
| 1326 |
+
// ptrCounts,
|
| 1327 |
// collapseResult.StridedMergeQuads.packed_accessor64<scalar_t, 3>(),
|
| 1328 |
// gridCellsTensor.packed_accessor64<int32_t, 4>(),
|
| 1329 |
// quadCellExtentsTensor.packed_accessor64<int32_t, 3>()
|
| 1330 |
//);
|
| 1331 |
|
| 1332 |
+
//auto adjGridFn = counts.size(0) == 1 ?
|
| 1333 |
+
// device_a2a_adjacency_with_grid<GRID_NUM_WARPS, true, scalar_t> :
|
| 1334 |
+
// device_a2a_adjacency_with_grid<GRID_NUM_WARPS, false, scalar_t>;
|
| 1335 |
+
|
| 1336 |
+
//adjGridFn KERNEL_ARG3(gridSize, blockSize, smemSize) (
|
| 1337 |
+
// ptrCounts,
|
| 1338 |
// iouThreshold,
|
| 1339 |
// collapseResult.StridedMergeQuads.packed_accessor64<scalar_t, 3>(),
|
| 1340 |
// gridCellsTensor.packed_accessor64<int32_t, 4>(),
|
|
|
|
| 1351 |
gridSize = dim3{div_up(totalWork, blockSize.x),
|
| 1352 |
static_cast<uint32_t>(counts.size(0))};
|
| 1353 |
|
| 1354 |
+
auto adjFn = counts.size(0) == 1 ? device_a2a_adjacency_sparse<true, scalar_t> : device_a2a_adjacency_sparse<false, scalar_t>;
|
| 1355 |
+
|
| 1356 |
// This algorithm is O(n^2) with n being the current number of quads
|
| 1357 |
+
adjFn KERNEL_ARG2(gridSize, blockSize) (
|
| 1358 |
+
ptrCounts,
|
| 1359 |
iouThreshold,
|
| 1360 |
collapseResult.StridedMergeQuads.packed_accessor64<scalar_t, 3>(),
|
| 1361 |
isStartTensor.packed_accessor64<bool, 2>(),
|
|
|
|
| 1365 |
|
| 1366 |
|
| 1367 |
#ifdef NMS_VERIFY_CORRECTNESS
|
| 1368 |
+
cpu_a2a_adjacency_sparse<true>(ptrCounts, iouThreshold,
|
|
|
|
|
|
|
| 1369 |
collapseResult.StridedMergeQuads, cpuIsStartTensor, cpuAdjCountsTensor, cpuAdjValuesTensor);
|
| 1370 |
|
| 1371 |
adjValuesTensor = std::get<0>(torch::sort(adjValuesTensor, /*dim=*/ 2));
|
|
|
|
| 1380 |
auto maxDepthTensor = torch::tensor(0, adjCountsTensor.options());
|
| 1381 |
#endif
|
| 1382 |
|
| 1383 |
+
auto traverseFn = counts.size(0) == 1 ?
|
| 1384 |
+
device_flatten_graph_iterative<true> :
|
| 1385 |
+
device_flatten_graph_iterative<false>;
|
| 1386 |
+
|
| 1387 |
blockSize = dim3{ 128, 1, 1 };
|
| 1388 |
gridSize = dim3{ 1, static_cast<uint32_t>(maxExCount), static_cast<uint32_t>(counts.size(0)) };
|
| 1389 |
smemSize = div_up(maxExCount * sizeof(visit_mask_t), sizeof(uint32_t)) * sizeof(uint32_t);
|
| 1390 |
|
| 1391 |
+
traverseFn KERNEL_ARG3(gridSize, blockSize, smemSize) (
|
| 1392 |
+
ptrCounts,
|
| 1393 |
isStartTensor.packed_accessor64<bool, 2>(),
|
| 1394 |
reinterpret_cast<uint32_t*>(adjCountsTensor.data_ptr<int32_t>()),
|
| 1395 |
reinterpret_cast<uint32_t*>(adjValuesTensor.data_ptr<int32_t>())
|
|
|
|
| 1399 |
);
|
| 1400 |
|
| 1401 |
#ifdef NMS_VERIFY_CORRECTNESS
|
| 1402 |
+
cpu_flatten_graph<true>(ptrCounts, cpuIsStartTensor, cpuAdjCountsTensor, cpuAdjValuesTensor);
|
| 1403 |
|
| 1404 |
cpuAdjValuesTensor = std::get<0>(torch::sort(cpuAdjValuesTensor, /*dim=*/ 2));
|
| 1405 |
adjValuesTensor = std::get<0>(torch::sort(adjValuesTensor, /*dim=*/ 2));
|
|
|
|
| 1437 |
cpuIsStartTensor = isStartTensor.cpu();
|
| 1438 |
cpuAdjCountsTensor = adjCountsTensor.cpu();
|
| 1439 |
cpuAdjValuesTensor = adjValuesTensor.cpu();
|
| 1440 |
+
auto cpuCounts = counts.cpu();
|
| 1441 |
auto cpuCollapseIds = collapseResult.QuadIds.cpu();
|
| 1442 |
|
| 1443 |
static std::vector<std::unordered_set<int32_t>> s_knownGroups;
|
|
|
|
| 1589 |
dim3 blockSize(NUM_WARPS * 32, 1, 1);
|
| 1590 |
dim3 gridSize(1, adjResult.MaxExCount, counts.size(0));
|
| 1591 |
|
| 1592 |
+
// If the batch is only a single example, instead of hitting global memory for the count, we can
|
| 1593 |
+
// just encode the count into the pointer instead
|
| 1594 |
+
uint64_t ptrCounts = reinterpret_cast<uint64_t>(counts.data_ptr<int32_t>());
|
| 1595 |
+
if (counts.size(0) == 1) {
|
| 1596 |
+
ptrCounts = adjResult.MaxExCount;
|
| 1597 |
+
}
|
| 1598 |
+
|
| 1599 |
torch::Tensor outQuads = torch::empty({ numOutQuads, 4, 2 }, embedQuads.options());
|
| 1600 |
torch::Tensor outConf = torch::empty({ numOutQuads }, embedQuads.options());
|
| 1601 |
|
| 1602 |
+
auto collapseFn = counts.size(0) == 1 ?
|
| 1603 |
+
device_a2a_collapse<NUM_WARPS, scalar_t, true> :
|
| 1604 |
+
device_a2a_collapse<NUM_WARPS, scalar_t, false>;
|
| 1605 |
+
|
| 1606 |
+
collapseFn KERNEL_ARG2(gridSize, blockSize) (
|
| 1607 |
+
ptrCounts,
|
| 1608 |
embedQuads.packed_accessor64<scalar_t, 3>(),
|
| 1609 |
isLeadRow.packed_accessor64<bool, 2>(),
|
| 1610 |
regionCounts.data_ptr<int64_t>(),
|
nemotron-ocr/pyproject.toml
CHANGED
|
@@ -5,7 +5,6 @@ description = "Nemoton OCR"
|
|
| 5 |
authors = [{ name = "NVIDIA Nemotron" }]
|
| 6 |
requires-python = ">=3.12,<3.13"
|
| 7 |
dependencies = [
|
| 8 |
-
"huggingface_hub>=0.20.0",
|
| 9 |
"pandas>=2.3.3",
|
| 10 |
"pillow>=12.0.0",
|
| 11 |
"scikit-learn>=1.7.2",
|
|
|
|
| 5 |
authors = [{ name = "NVIDIA Nemotron" }]
|
| 6 |
requires-python = ">=3.12,<3.13"
|
| 7 |
dependencies = [
|
|
|
|
| 8 |
"pandas>=2.3.3",
|
| 9 |
"pillow>=12.0.0",
|
| 10 |
"scikit-learn>=1.7.2",
|
nemotron-ocr/src/nemotron_ocr/inference/pipeline.py
CHANGED
|
@@ -6,7 +6,6 @@ import io
|
|
| 6 |
import json
|
| 7 |
import os
|
| 8 |
from pathlib import Path
|
| 9 |
-
from typing import Optional
|
| 10 |
|
| 11 |
import numpy as np
|
| 12 |
import torch
|
|
@@ -21,7 +20,6 @@ from nemotron_ocr.inference.post_processing.data.text_region import TextBlock
|
|
| 21 |
from nemotron_ocr.inference.post_processing.quad_rectify import QuadRectify
|
| 22 |
from nemotron_ocr.inference.post_processing.research_ops import parse_relational_results, reorder_boxes
|
| 23 |
from nemotron_ocr.inference.pre_processing import interpolate_and_pad, pad_to_square
|
| 24 |
-
from huggingface_hub import hf_hub_download
|
| 25 |
from nemotron_ocr_cpp import quad_non_maximal_suppression, region_counts_to_indices, rrect_to_quads
|
| 26 |
from PIL import Image, ImageDraw, ImageFont
|
| 27 |
from torch import amp
|
|
@@ -39,57 +37,25 @@ MERGE_LEVELS = {"word", "sentence", "paragraph"}
|
|
| 39 |
DEFAULT_MERGE_LEVEL = "paragraph"
|
| 40 |
|
| 41 |
|
| 42 |
-
# HuggingFace repository for downloading model weights
|
| 43 |
-
HF_REPO_ID = "nvidia/nemotron-ocr-v1"
|
| 44 |
-
CHECKPOINT_FILES = ["detector.pth", "recognizer.pth", "relational.pth", "charset.txt"]
|
| 45 |
-
|
| 46 |
-
|
| 47 |
class NemotronOCR:
|
| 48 |
"""
|
| 49 |
A high-level pipeline for performing OCR on images.
|
| 50 |
-
|
| 51 |
-
Model weights are automatically downloaded from Hugging Face Hub
|
| 52 |
-
(nvidia/nemotron-ocr-v1) if not found locally.
|
| 53 |
"""
|
| 54 |
|
| 55 |
-
def __init__(self, model_dir
|
| 56 |
-
|
| 57 |
-
if model_dir is not None:
|
| 58 |
-
local_path = Path(model_dir)
|
| 59 |
-
if all((local_path / f).is_file() for f in CHECKPOINT_FILES):
|
| 60 |
-
self._model_dir = local_path
|
| 61 |
-
else:
|
| 62 |
-
self._model_dir = self._download_checkpoints()
|
| 63 |
-
else:
|
| 64 |
-
self._model_dir = self._download_checkpoints()
|
| 65 |
|
| 66 |
self._load_models()
|
| 67 |
self._load_charset()
|
| 68 |
self._initialize_processors()
|
| 69 |
|
| 70 |
-
@staticmethod
|
| 71 |
-
def _download_checkpoints() -> Path:
|
| 72 |
-
"""Download model checkpoints from HuggingFace Hub (cached locally after first download)."""
|
| 73 |
-
downloaded_path = None
|
| 74 |
-
for filename in CHECKPOINT_FILES:
|
| 75 |
-
downloaded_path = hf_hub_download(
|
| 76 |
-
repo_id=HF_REPO_ID,
|
| 77 |
-
filename=f"checkpoints/{filename}",
|
| 78 |
-
)
|
| 79 |
-
# All checkpoint files are in the same directory
|
| 80 |
-
return Path(downloaded_path).parent
|
| 81 |
-
|
| 82 |
def _load_models(self):
|
| 83 |
"""Loads all necessary models into memory."""
|
| 84 |
self.detector = FOTSDetector(coordinate_mode="RBOX", backbone="regnet_y_8gf", verbose=False)
|
| 85 |
-
self.detector.load_state_dict(
|
| 86 |
-
torch.load(self._model_dir / "detector.pth", weights_only=True), strict=True
|
| 87 |
-
)
|
| 88 |
|
| 89 |
self.recognizer = TransformerRecognizer(nic=self.detector.num_features[-1], num_tokens=858, max_width=32)
|
| 90 |
-
self.recognizer.load_state_dict(
|
| 91 |
-
torch.load(self._model_dir / "recognizer.pth", weights_only=True), strict=True
|
| 92 |
-
)
|
| 93 |
|
| 94 |
self.relational = GlobalRelationalModel(
|
| 95 |
num_input_channels=self.detector.num_features,
|
|
@@ -98,9 +64,7 @@ class NemotronOCR:
|
|
| 98 |
k=16,
|
| 99 |
num_layers=4,
|
| 100 |
)
|
| 101 |
-
self.relational.load_state_dict(
|
| 102 |
-
torch.load(self._model_dir / "relational.pth", weights_only=True), strict=True
|
| 103 |
-
)
|
| 104 |
|
| 105 |
for model in (self.detector, self.recognizer, self.relational):
|
| 106 |
model = model.cuda()
|
|
@@ -217,17 +181,29 @@ class NemotronOCR:
|
|
| 217 |
e2e_det_conf = torch.sigmoid(det_conf)
|
| 218 |
e2e_det_coords = rrect_to_quads(det_rboxes.float(), DETECTOR_DOWNSAMPLE)
|
| 219 |
|
| 220 |
-
|
| 221 |
-
|
| 222 |
-
|
| 223 |
-
|
| 224 |
-
|
| 225 |
-
|
| 226 |
-
|
| 227 |
-
|
| 228 |
-
|
| 229 |
-
|
| 230 |
-
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 231 |
|
| 232 |
if quads.shape[0] == 0:
|
| 233 |
rec_rectified_quads = torch.empty(0, 128, 8, 32, dtype=torch.float32, device=padded_image.device)
|
|
|
|
| 6 |
import json
|
| 7 |
import os
|
| 8 |
from pathlib import Path
|
|
|
|
| 9 |
|
| 10 |
import numpy as np
|
| 11 |
import torch
|
|
|
|
| 20 |
from nemotron_ocr.inference.post_processing.quad_rectify import QuadRectify
|
| 21 |
from nemotron_ocr.inference.post_processing.research_ops import parse_relational_results, reorder_boxes
|
| 22 |
from nemotron_ocr.inference.pre_processing import interpolate_and_pad, pad_to_square
|
|
|
|
| 23 |
from nemotron_ocr_cpp import quad_non_maximal_suppression, region_counts_to_indices, rrect_to_quads
|
| 24 |
from PIL import Image, ImageDraw, ImageFont
|
| 25 |
from torch import amp
|
|
|
|
| 37 |
DEFAULT_MERGE_LEVEL = "paragraph"
|
| 38 |
|
| 39 |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 40 |
class NemotronOCR:
|
| 41 |
"""
|
| 42 |
A high-level pipeline for performing OCR on images.
|
|
|
|
|
|
|
|
|
|
| 43 |
"""
|
| 44 |
|
| 45 |
+
def __init__(self, model_dir="./checkpoints"):
|
| 46 |
+
self._model_dir = Path(model_dir)
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 47 |
|
| 48 |
self._load_models()
|
| 49 |
self._load_charset()
|
| 50 |
self._initialize_processors()
|
| 51 |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 52 |
def _load_models(self):
|
| 53 |
"""Loads all necessary models into memory."""
|
| 54 |
self.detector = FOTSDetector(coordinate_mode="RBOX", backbone="regnet_y_8gf", verbose=False)
|
| 55 |
+
self.detector.load_state_dict(torch.load(self._model_dir / "detector.pth"), strict=True)
|
|
|
|
|
|
|
| 56 |
|
| 57 |
self.recognizer = TransformerRecognizer(nic=self.detector.num_features[-1], num_tokens=858, max_width=32)
|
| 58 |
+
self.recognizer.load_state_dict(torch.load(self._model_dir / "recognizer.pth"), strict=True)
|
|
|
|
|
|
|
| 59 |
|
| 60 |
self.relational = GlobalRelationalModel(
|
| 61 |
num_input_channels=self.detector.num_features,
|
|
|
|
| 64 |
k=16,
|
| 65 |
num_layers=4,
|
| 66 |
)
|
| 67 |
+
self.relational.load_state_dict(torch.load(self._model_dir / "relational.pth"), strict=True)
|
|
|
|
|
|
|
| 68 |
|
| 69 |
for model in (self.detector, self.recognizer, self.relational):
|
| 70 |
model = model.cuda()
|
|
|
|
| 181 |
e2e_det_conf = torch.sigmoid(det_conf)
|
| 182 |
e2e_det_coords = rrect_to_quads(det_rboxes.float(), DETECTOR_DOWNSAMPLE)
|
| 183 |
|
| 184 |
+
# FIXME: quad_non_maximal_suppression fails with batch size > 1
|
| 185 |
+
all_quads = []
|
| 186 |
+
all_confidence = []
|
| 187 |
+
all_region_counts = []
|
| 188 |
+
|
| 189 |
+
for idx in range(e2e_det_coords.shape[0]):
|
| 190 |
+
quads, confidence, region_counts = quad_non_maximal_suppression(
|
| 191 |
+
e2e_det_coords[idx].unsqueeze(0),
|
| 192 |
+
e2e_det_conf[idx].unsqueeze(0),
|
| 193 |
+
prob_threshold=NMS_PROB_THRESHOLD,
|
| 194 |
+
iou_threshold=NMS_IOU_THRESHOLD,
|
| 195 |
+
kernel_height=2,
|
| 196 |
+
kernel_width=3,
|
| 197 |
+
max_regions=NMS_MAX_REGIONS,
|
| 198 |
+
verbose=False,
|
| 199 |
+
)[:3]
|
| 200 |
+
all_quads.append(quads)
|
| 201 |
+
all_confidence.append(confidence)
|
| 202 |
+
all_region_counts.append(region_counts)
|
| 203 |
+
|
| 204 |
+
quads = torch.cat(all_quads, dim=0)
|
| 205 |
+
confidence = torch.cat(all_confidence, dim=0)
|
| 206 |
+
region_counts = torch.cat(all_region_counts, dim=0)
|
| 207 |
|
| 208 |
if quads.shape[0] == 0:
|
| 209 |
rec_rectified_quads = torch.empty(0, 128, 8, 32, dtype=torch.float32, device=padded_image.device)
|