Add files using upload-large-folder tool
Browse filesThis view is limited to 50 files because it contains too many changes. See raw diff
- .gitattributes +2 -0
- my_container_sandbox/workspace/anaconda3/lib/python3.8/site-packages/SimpleITK/__pycache__/__init__.cpython-38.pyc +0 -0
- my_container_sandbox/workspace/anaconda3/lib/python3.8/site-packages/SimpleITK/__pycache__/extra.cpython-38.pyc +0 -0
- my_container_sandbox/workspace/anaconda3/lib/python3.8/site-packages/SimpleITK/docs/LICENSE +202 -0
- my_container_sandbox/workspace/anaconda3/lib/python3.8/site-packages/SimpleITK/docs/NOTICE +9 -0
- my_container_sandbox/workspace/anaconda3/lib/python3.8/site-packages/SimpleITK/docs/Readme.md +87 -0
- my_container_sandbox/workspace/anaconda3/lib/python3.8/site-packages/matplotlib/mpl-data/fonts/ttf/DejaVuSerif-BoldItalic.ttf +3 -0
- my_container_sandbox/workspace/anaconda3/lib/python3.8/site-packages/matplotlib/mpl-data/fonts/ttf/DejaVuSerif.ttf +3 -0
- my_container_sandbox/workspace/anaconda3/lib/python3.8/site-packages/past/__pycache__/__init__.cpython-38.pyc +0 -0
- my_container_sandbox/workspace/anaconda3/lib/python3.8/site-packages/past/builtins/__init__.py +72 -0
- my_container_sandbox/workspace/anaconda3/lib/python3.8/site-packages/past/builtins/misc.py +94 -0
- my_container_sandbox/workspace/anaconda3/lib/python3.8/site-packages/past/builtins/noniterators.py +272 -0
- my_container_sandbox/workspace/anaconda3/lib/python3.8/site-packages/past/translation/__pycache__/__init__.cpython-38.pyc +0 -0
- my_container_sandbox/workspace/anaconda3/lib/python3.8/site-packages/past/types/__init__.py +29 -0
- my_container_sandbox/workspace/anaconda3/lib/python3.8/site-packages/past/types/olddict.py +96 -0
- my_container_sandbox/workspace/anaconda3/lib/python3.8/site-packages/past/types/oldstr.py +135 -0
- my_container_sandbox/workspace/anaconda3/lib/python3.8/site-packages/past/utils/__init__.py +97 -0
- my_container_sandbox/workspace/anaconda3/lib/python3.8/site-packages/torchvision/io/__pycache__/__init__.cpython-38.pyc +0 -0
- my_container_sandbox/workspace/anaconda3/lib/python3.8/site-packages/torchvision/io/__pycache__/_load_gpu_decoder.cpython-38.pyc +0 -0
- my_container_sandbox/workspace/anaconda3/lib/python3.8/site-packages/torchvision/io/__pycache__/_video_opt.cpython-38.pyc +0 -0
- my_container_sandbox/workspace/anaconda3/lib/python3.8/site-packages/torchvision/io/__pycache__/image.cpython-38.pyc +0 -0
- my_container_sandbox/workspace/anaconda3/lib/python3.8/site-packages/torchvision/io/__pycache__/video.cpython-38.pyc +0 -0
- my_container_sandbox/workspace/anaconda3/lib/python3.8/site-packages/torchvision/io/__pycache__/video_reader.cpython-38.pyc +0 -0
- my_container_sandbox/workspace/anaconda3/lib/python3.8/site-packages/torchvision/io/_video_opt.py +505 -0
- my_container_sandbox/workspace/anaconda3/lib/python3.8/site-packages/torchvision/io/image.py +257 -0
- my_container_sandbox/workspace/anaconda3/lib/python3.8/site-packages/torchvision/io/video.py +415 -0
- my_container_sandbox/workspace/anaconda3/lib/python3.8/site-packages/torchvision/models/__pycache__/_meta.cpython-38.pyc +0 -0
- my_container_sandbox/workspace/anaconda3/lib/python3.8/site-packages/torchvision/models/__pycache__/alexnet.cpython-38.pyc +0 -0
- my_container_sandbox/workspace/anaconda3/lib/python3.8/site-packages/torchvision/models/__pycache__/feature_extraction.cpython-38.pyc +0 -0
- my_container_sandbox/workspace/anaconda3/lib/python3.8/site-packages/torchvision/models/__pycache__/mobilenetv2.cpython-38.pyc +0 -0
- my_container_sandbox/workspace/anaconda3/lib/python3.8/site-packages/torchvision/models/detection/_utils.py +538 -0
- my_container_sandbox/workspace/anaconda3/lib/python3.8/site-packages/torchvision/models/detection/anchor_utils.py +268 -0
- my_container_sandbox/workspace/anaconda3/lib/python3.8/site-packages/torchvision/models/detection/generalized_rcnn.py +118 -0
- my_container_sandbox/workspace/anaconda3/lib/python3.8/site-packages/torchvision/models/detection/ssdlite.py +339 -0
- my_container_sandbox/workspace/anaconda3/lib/python3.8/site-packages/torchvision/models/detection/transform.py +309 -0
- my_container_sandbox/workspace/anaconda3/lib/python3.8/site-packages/torchvision/models/optical_flow/__pycache__/__init__.cpython-38.pyc +0 -0
- my_container_sandbox/workspace/anaconda3/lib/python3.8/site-packages/torchvision/models/optical_flow/__pycache__/_utils.cpython-38.pyc +0 -0
- my_container_sandbox/workspace/anaconda3/lib/python3.8/site-packages/torchvision/models/optical_flow/__pycache__/raft.cpython-38.pyc +0 -0
- my_container_sandbox/workspace/anaconda3/lib/python3.8/site-packages/torchvision/models/quantization/__pycache__/__init__.cpython-38.pyc +0 -0
- my_container_sandbox/workspace/anaconda3/lib/python3.8/site-packages/torchvision/models/quantization/__pycache__/googlenet.cpython-38.pyc +0 -0
- my_container_sandbox/workspace/anaconda3/lib/python3.8/site-packages/torchvision/models/quantization/__pycache__/inception.cpython-38.pyc +0 -0
- my_container_sandbox/workspace/anaconda3/lib/python3.8/site-packages/torchvision/models/quantization/__pycache__/mobilenet.cpython-38.pyc +0 -0
- my_container_sandbox/workspace/anaconda3/lib/python3.8/site-packages/torchvision/models/quantization/__pycache__/mobilenetv2.cpython-38.pyc +0 -0
- my_container_sandbox/workspace/anaconda3/lib/python3.8/site-packages/torchvision/models/quantization/__pycache__/mobilenetv3.cpython-38.pyc +0 -0
- my_container_sandbox/workspace/anaconda3/lib/python3.8/site-packages/torchvision/models/segmentation/__pycache__/__init__.cpython-38.pyc +0 -0
- my_container_sandbox/workspace/anaconda3/lib/python3.8/site-packages/torchvision/models/segmentation/__pycache__/_utils.cpython-38.pyc +0 -0
- my_container_sandbox/workspace/anaconda3/lib/python3.8/site-packages/torchvision/models/segmentation/__pycache__/deeplabv3.cpython-38.pyc +0 -0
- my_container_sandbox/workspace/anaconda3/lib/python3.8/site-packages/torchvision/models/segmentation/__pycache__/fcn.cpython-38.pyc +0 -0
- my_container_sandbox/workspace/anaconda3/lib/python3.8/site-packages/torchvision/models/segmentation/__pycache__/lraspp.cpython-38.pyc +0 -0
- my_container_sandbox/workspace/anaconda3/lib/python3.8/site-packages/torchvision/models/segmentation/__pycache__/segmentation.cpython-38.pyc +0 -0
.gitattributes
CHANGED
|
@@ -390,3 +390,5 @@ my_container_sandbox/workspace/anaconda3/lib/python3.8/site-packages/matplotlib/
|
|
| 390 |
my_container_sandbox/workspace/anaconda3/lib/python3.8/site-packages/matplotlib/mpl-data/fonts/ttf/DejaVuSans-Bold.ttf filter=lfs diff=lfs merge=lfs -text
|
| 391 |
my_container_sandbox/workspace/anaconda3/lib/python3.8/site-packages/matplotlib/mpl-data/fonts/ttf/STIXGeneral.ttf filter=lfs diff=lfs merge=lfs -text
|
| 392 |
my_container_sandbox/workspace/anaconda3/lib/python3.8/site-packages/matplotlib/mpl-data/fonts/ttf/DejaVuSansMono-Bold.ttf filter=lfs diff=lfs merge=lfs -text
|
|
|
|
|
|
|
|
|
| 390 |
my_container_sandbox/workspace/anaconda3/lib/python3.8/site-packages/matplotlib/mpl-data/fonts/ttf/DejaVuSans-Bold.ttf filter=lfs diff=lfs merge=lfs -text
|
| 391 |
my_container_sandbox/workspace/anaconda3/lib/python3.8/site-packages/matplotlib/mpl-data/fonts/ttf/STIXGeneral.ttf filter=lfs diff=lfs merge=lfs -text
|
| 392 |
my_container_sandbox/workspace/anaconda3/lib/python3.8/site-packages/matplotlib/mpl-data/fonts/ttf/DejaVuSansMono-Bold.ttf filter=lfs diff=lfs merge=lfs -text
|
| 393 |
+
my_container_sandbox/workspace/anaconda3/lib/python3.8/site-packages/matplotlib/mpl-data/fonts/ttf/DejaVuSerif-BoldItalic.ttf filter=lfs diff=lfs merge=lfs -text
|
| 394 |
+
my_container_sandbox/workspace/anaconda3/lib/python3.8/site-packages/matplotlib/mpl-data/fonts/ttf/DejaVuSerif.ttf filter=lfs diff=lfs merge=lfs -text
|
my_container_sandbox/workspace/anaconda3/lib/python3.8/site-packages/SimpleITK/__pycache__/__init__.cpython-38.pyc
ADDED
|
Binary file (278 Bytes). View file
|
|
|
my_container_sandbox/workspace/anaconda3/lib/python3.8/site-packages/SimpleITK/__pycache__/extra.cpython-38.pyc
ADDED
|
Binary file (12.4 kB). View file
|
|
|
my_container_sandbox/workspace/anaconda3/lib/python3.8/site-packages/SimpleITK/docs/LICENSE
ADDED
|
@@ -0,0 +1,202 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
|
| 2 |
+
Apache License
|
| 3 |
+
Version 2.0, January 2004
|
| 4 |
+
http://www.apache.org/licenses/
|
| 5 |
+
|
| 6 |
+
TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION
|
| 7 |
+
|
| 8 |
+
1. Definitions.
|
| 9 |
+
|
| 10 |
+
"License" shall mean the terms and conditions for use, reproduction,
|
| 11 |
+
and distribution as defined by Sections 1 through 9 of this document.
|
| 12 |
+
|
| 13 |
+
"Licensor" shall mean the copyright owner or entity authorized by
|
| 14 |
+
the copyright owner that is granting the License.
|
| 15 |
+
|
| 16 |
+
"Legal Entity" shall mean the union of the acting entity and all
|
| 17 |
+
other entities that control, are controlled by, or are under common
|
| 18 |
+
control with that entity. For the purposes of this definition,
|
| 19 |
+
"control" means (i) the power, direct or indirect, to cause the
|
| 20 |
+
direction or management of such entity, whether by contract or
|
| 21 |
+
otherwise, or (ii) ownership of fifty percent (50%) or more of the
|
| 22 |
+
outstanding shares, or (iii) beneficial ownership of such entity.
|
| 23 |
+
|
| 24 |
+
"You" (or "Your") shall mean an individual or Legal Entity
|
| 25 |
+
exercising permissions granted by this License.
|
| 26 |
+
|
| 27 |
+
"Source" form shall mean the preferred form for making modifications,
|
| 28 |
+
including but not limited to software source code, documentation
|
| 29 |
+
source, and configuration files.
|
| 30 |
+
|
| 31 |
+
"Object" form shall mean any form resulting from mechanical
|
| 32 |
+
transformation or translation of a Source form, including but
|
| 33 |
+
not limited to compiled object code, generated documentation,
|
| 34 |
+
and conversions to other media types.
|
| 35 |
+
|
| 36 |
+
"Work" shall mean the work of authorship, whether in Source or
|
| 37 |
+
Object form, made available under the License, as indicated by a
|
| 38 |
+
copyright notice that is included in or attached to the work
|
| 39 |
+
(an example is provided in the Appendix below).
|
| 40 |
+
|
| 41 |
+
"Derivative Works" shall mean any work, whether in Source or Object
|
| 42 |
+
form, that is based on (or derived from) the Work and for which the
|
| 43 |
+
editorial revisions, annotations, elaborations, or other modifications
|
| 44 |
+
represent, as a whole, an original work of authorship. For the purposes
|
| 45 |
+
of this License, Derivative Works shall not include works that remain
|
| 46 |
+
separable from, or merely link (or bind by name) to the interfaces of,
|
| 47 |
+
the Work and Derivative Works thereof.
|
| 48 |
+
|
| 49 |
+
"Contribution" shall mean any work of authorship, including
|
| 50 |
+
the original version of the Work and any modifications or additions
|
| 51 |
+
to that Work or Derivative Works thereof, that is intentionally
|
| 52 |
+
submitted to Licensor for inclusion in the Work by the copyright owner
|
| 53 |
+
or by an individual or Legal Entity authorized to submit on behalf of
|
| 54 |
+
the copyright owner. For the purposes of this definition, "submitted"
|
| 55 |
+
means any form of electronic, verbal, or written communication sent
|
| 56 |
+
to the Licensor or its representatives, including but not limited to
|
| 57 |
+
communication on electronic mailing lists, source code control systems,
|
| 58 |
+
and issue tracking systems that are managed by, or on behalf of, the
|
| 59 |
+
Licensor for the purpose of discussing and improving the Work, but
|
| 60 |
+
excluding communication that is conspicuously marked or otherwise
|
| 61 |
+
designated in writing by the copyright owner as "Not a Contribution."
|
| 62 |
+
|
| 63 |
+
"Contributor" shall mean Licensor and any individual or Legal Entity
|
| 64 |
+
on behalf of whom a Contribution has been received by Licensor and
|
| 65 |
+
subsequently incorporated within the Work.
|
| 66 |
+
|
| 67 |
+
2. Grant of Copyright License. Subject to the terms and conditions of
|
| 68 |
+
this License, each Contributor hereby grants to You a perpetual,
|
| 69 |
+
worldwide, non-exclusive, no-charge, royalty-free, irrevocable
|
| 70 |
+
copyright license to reproduce, prepare Derivative Works of,
|
| 71 |
+
publicly display, publicly perform, sublicense, and distribute the
|
| 72 |
+
Work and such Derivative Works in Source or Object form.
|
| 73 |
+
|
| 74 |
+
3. Grant of Patent License. Subject to the terms and conditions of
|
| 75 |
+
this License, each Contributor hereby grants to You a perpetual,
|
| 76 |
+
worldwide, non-exclusive, no-charge, royalty-free, irrevocable
|
| 77 |
+
(except as stated in this section) patent license to make, have made,
|
| 78 |
+
use, offer to sell, sell, import, and otherwise transfer the Work,
|
| 79 |
+
where such license applies only to those patent claims licensable
|
| 80 |
+
by such Contributor that are necessarily infringed by their
|
| 81 |
+
Contribution(s) alone or by combination of their Contribution(s)
|
| 82 |
+
with the Work to which such Contribution(s) was submitted. If You
|
| 83 |
+
institute patent litigation against any entity (including a
|
| 84 |
+
cross-claim or counterclaim in a lawsuit) alleging that the Work
|
| 85 |
+
or a Contribution incorporated within the Work constitutes direct
|
| 86 |
+
or contributory patent infringement, then any patent licenses
|
| 87 |
+
granted to You under this License for that Work shall terminate
|
| 88 |
+
as of the date such litigation is filed.
|
| 89 |
+
|
| 90 |
+
4. Redistribution. You may reproduce and distribute copies of the
|
| 91 |
+
Work or Derivative Works thereof in any medium, with or without
|
| 92 |
+
modifications, and in Source or Object form, provided that You
|
| 93 |
+
meet the following conditions:
|
| 94 |
+
|
| 95 |
+
(a) You must give any other recipients of the Work or
|
| 96 |
+
Derivative Works a copy of this License; and
|
| 97 |
+
|
| 98 |
+
(b) You must cause any modified files to carry prominent notices
|
| 99 |
+
stating that You changed the files; and
|
| 100 |
+
|
| 101 |
+
(c) You must retain, in the Source form of any Derivative Works
|
| 102 |
+
that You distribute, all copyright, patent, trademark, and
|
| 103 |
+
attribution notices from the Source form of the Work,
|
| 104 |
+
excluding those notices that do not pertain to any part of
|
| 105 |
+
the Derivative Works; and
|
| 106 |
+
|
| 107 |
+
(d) If the Work includes a "NOTICE" text file as part of its
|
| 108 |
+
distribution, then any Derivative Works that You distribute must
|
| 109 |
+
include a readable copy of the attribution notices contained
|
| 110 |
+
within such NOTICE file, excluding those notices that do not
|
| 111 |
+
pertain to any part of the Derivative Works, in at least one
|
| 112 |
+
of the following places: within a NOTICE text file distributed
|
| 113 |
+
as part of the Derivative Works; within the Source form or
|
| 114 |
+
documentation, if provided along with the Derivative Works; or,
|
| 115 |
+
within a display generated by the Derivative Works, if and
|
| 116 |
+
wherever such third-party notices normally appear. The contents
|
| 117 |
+
of the NOTICE file are for informational purposes only and
|
| 118 |
+
do not modify the License. You may add Your own attribution
|
| 119 |
+
notices within Derivative Works that You distribute, alongside
|
| 120 |
+
or as an addendum to the NOTICE text from the Work, provided
|
| 121 |
+
that such additional attribution notices cannot be construed
|
| 122 |
+
as modifying the License.
|
| 123 |
+
|
| 124 |
+
You may add Your own copyright statement to Your modifications and
|
| 125 |
+
may provide additional or different license terms and conditions
|
| 126 |
+
for use, reproduction, or distribution of Your modifications, or
|
| 127 |
+
for any such Derivative Works as a whole, provided Your use,
|
| 128 |
+
reproduction, and distribution of the Work otherwise complies with
|
| 129 |
+
the conditions stated in this License.
|
| 130 |
+
|
| 131 |
+
5. Submission of Contributions. Unless You explicitly state otherwise,
|
| 132 |
+
any Contribution intentionally submitted for inclusion in the Work
|
| 133 |
+
by You to the Licensor shall be under the terms and conditions of
|
| 134 |
+
this License, without any additional terms or conditions.
|
| 135 |
+
Notwithstanding the above, nothing herein shall supersede or modify
|
| 136 |
+
the terms of any separate license agreement you may have executed
|
| 137 |
+
with Licensor regarding such Contributions.
|
| 138 |
+
|
| 139 |
+
6. Trademarks. This License does not grant permission to use the trade
|
| 140 |
+
names, trademarks, service marks, or product names of the Licensor,
|
| 141 |
+
except as required for reasonable and customary use in describing the
|
| 142 |
+
origin of the Work and reproducing the content of the NOTICE file.
|
| 143 |
+
|
| 144 |
+
7. Disclaimer of Warranty. Unless required by applicable law or
|
| 145 |
+
agreed to in writing, Licensor provides the Work (and each
|
| 146 |
+
Contributor provides its Contributions) on an "AS IS" BASIS,
|
| 147 |
+
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or
|
| 148 |
+
implied, including, without limitation, any warranties or conditions
|
| 149 |
+
of TITLE, NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A
|
| 150 |
+
PARTICULAR PURPOSE. You are solely responsible for determining the
|
| 151 |
+
appropriateness of using or redistributing the Work and assume any
|
| 152 |
+
risks associated with Your exercise of permissions under this License.
|
| 153 |
+
|
| 154 |
+
8. Limitation of Liability. In no event and under no legal theory,
|
| 155 |
+
whether in tort (including negligence), contract, or otherwise,
|
| 156 |
+
unless required by applicable law (such as deliberate and grossly
|
| 157 |
+
negligent acts) or agreed to in writing, shall any Contributor be
|
| 158 |
+
liable to You for damages, including any direct, indirect, special,
|
| 159 |
+
incidental, or consequential damages of any character arising as a
|
| 160 |
+
result of this License or out of the use or inability to use the
|
| 161 |
+
Work (including but not limited to damages for loss of goodwill,
|
| 162 |
+
work stoppage, computer failure or malfunction, or any and all
|
| 163 |
+
other commercial damages or losses), even if such Contributor
|
| 164 |
+
has been advised of the possibility of such damages.
|
| 165 |
+
|
| 166 |
+
9. Accepting Warranty or Additional Liability. While redistributing
|
| 167 |
+
the Work or Derivative Works thereof, You may choose to offer,
|
| 168 |
+
and charge a fee for, acceptance of support, warranty, indemnity,
|
| 169 |
+
or other liability obligations and/or rights consistent with this
|
| 170 |
+
License. However, in accepting such obligations, You may act only
|
| 171 |
+
on Your own behalf and on Your sole responsibility, not on behalf
|
| 172 |
+
of any other Contributor, and only if You agree to indemnify,
|
| 173 |
+
defend, and hold each Contributor harmless for any liability
|
| 174 |
+
incurred by, or claims asserted against, such Contributor by reason
|
| 175 |
+
of your accepting any such warranty or additional liability.
|
| 176 |
+
|
| 177 |
+
END OF TERMS AND CONDITIONS
|
| 178 |
+
|
| 179 |
+
APPENDIX: How to apply the Apache License to your work.
|
| 180 |
+
|
| 181 |
+
To apply the Apache License to your work, attach the following
|
| 182 |
+
boilerplate notice, with the fields enclosed by brackets "[]"
|
| 183 |
+
replaced with your own identifying information. (Don't include
|
| 184 |
+
the brackets!) The text should be enclosed in the appropriate
|
| 185 |
+
comment syntax for the file format. We also recommend that a
|
| 186 |
+
file or class name and description of purpose be included on the
|
| 187 |
+
same "printed page" as the copyright notice for easier
|
| 188 |
+
identification within third-party archives.
|
| 189 |
+
|
| 190 |
+
Copyright [yyyy] [name of copyright owner]
|
| 191 |
+
|
| 192 |
+
Licensed under the Apache License, Version 2.0 (the "License");
|
| 193 |
+
you may not use this file except in compliance with the License.
|
| 194 |
+
You may obtain a copy of the License at
|
| 195 |
+
|
| 196 |
+
http://www.apache.org/licenses/LICENSE-2.0
|
| 197 |
+
|
| 198 |
+
Unless required by applicable law or agreed to in writing, software
|
| 199 |
+
distributed under the License is distributed on an "AS IS" BASIS,
|
| 200 |
+
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
| 201 |
+
See the License for the specific language governing permissions and
|
| 202 |
+
limitations under the License.
|
my_container_sandbox/workspace/anaconda3/lib/python3.8/site-packages/SimpleITK/docs/NOTICE
ADDED
|
@@ -0,0 +1,9 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
SimpleITK
|
| 2 |
+
|
| 3 |
+
Copyright 2010-2019 Insight Software Consortium
|
| 4 |
+
Copyright 2020 NumFOCUS
|
| 5 |
+
|
| 6 |
+
This software is distributed under the
|
| 7 |
+
Apache 2.0 License.
|
| 8 |
+
|
| 9 |
+
See LICENSE file for details.
|
my_container_sandbox/workspace/anaconda3/lib/python3.8/site-packages/SimpleITK/docs/Readme.md
ADDED
|
@@ -0,0 +1,87 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
SimpleITK
|
| 2 |
+
=========
|
| 3 |
+
|
| 4 |
+
| | CircleCI | ReadTheDocs | AzurePipelines |
|
| 5 |
+
|:-------:|:---------:|:-------------:|:-------------:|
|
| 6 |
+
| release | [](https://circleci.com/gh/SimpleITK/SimpleITK/tree/release) | [](http://simpleitk.readthedocs.io/en/release/) | [](https://dev.azure.com/SimpleITK-DevOps/SimpleITK/_build/latest?definitionId=2&branchName=release) |
|
| 7 |
+
| master | [](https://circleci.com/gh/SimpleITK/SimpleITK/tree/master) | [](http://simpleitk.readthedocs.io/en/master/) | [](https://dev.azure.com/SimpleITK-DevOps/SimpleITK/_build/latest?definitionId=2&branchName=master) |
|
| 8 |
+
|
| 9 |
+
|
| 10 |
+
SimpleITK is an image analysis toolkit with a large number of components supporting general filtering operations, image segmentation and registration. It is built on top of the Insight Segmentation and Registration Toolkit [ITK](https://www.itk.org) with the intent of providing a simplified interface to ITK. SimpleITK itself is written in C++ but is available for a large number of programming languages. Currently these include:
|
| 11 |
+
|
| 12 |
+
* [Python](http://www.python.org)
|
| 13 |
+
* [R](https://www.r-project.org)
|
| 14 |
+
* [Java](http://www.java.com)
|
| 15 |
+
* [C#](http://msdn.microsoft.com/en-us/vcsharp/default.aspx)
|
| 16 |
+
* [Lua](http://www.lua.org)
|
| 17 |
+
* [TCL](https://www.tcl.tk/)
|
| 18 |
+
* [Ruby](https://www.ruby-lang.org/en/)
|
| 19 |
+
|
| 20 |
+
|
| 21 |
+
Wrapping of the C++ code is accomplished through [SWIG](http://www.swig.org), in principle, any language wrapped by SWIG should be applicable to SimpleITK.
|
| 22 |
+
|
| 23 |
+
Unlike ITK's support of n-dimensional spatio-temporal images, SimpleITK supports 2D, 3D and 4D images. The dimensionality refers to spatio-temporal dimensions, the voxels can be n-dimensional vectors.
|
| 24 |
+
|
| 25 |
+
SimpleITK is licensed under the [Apache License](http://www.opensource.org/licenses/apache2.0.php).
|
| 26 |
+
|
| 27 |
+
Acknowledgments
|
| 28 |
+
--------------
|
| 29 |
+
SimpleITK development has been supported by:
|
| 30 |
+
|
| 31 |
+
* The Intramural Research Program of the National Institutes of Health, National Institute of Allergy and Infectious Diseases.
|
| 32 |
+
|
| 33 |
+
* The US National Library of Medicine under the American Recovery and Reinvestment Act (ARRA) and under the Intramural Research Program of the U.S. National Institutes of Health.
|
| 34 |
+
|
| 35 |
+
* The Insight Software Consortium and the ITK user and developer communities.
|
| 36 |
+
|
| 37 |
+
|
| 38 |
+
License and Copyright
|
| 39 |
+
---------------------
|
| 40 |
+
|
| 41 |
+
The SimpleITK project is part of the [Insight Software Consortium](https://www.insightsoftwareconsortium.org/)(ISC) a non-profit educational consortium dedicated to promoting and maintaining open-source, freely available software for bio-medical image analysis. The copyright is held by [NumFOCUS](https://numfocus.org/). The SimpleITK software is distributed under the [Apache License 2.0](https://github.com/SimpleITK/SimpleITK/blob/master/LICENSE).
|
| 42 |
+
|
| 43 |
+
|
| 44 |
+
How to Cite
|
| 45 |
+
--------
|
| 46 |
+
|
| 47 |
+
If you found SimpleITK useful in your research, support our efforts by citing
|
| 48 |
+
the relevant publication(s):
|
| 49 |
+
|
| 50 |
+
R. Beare, B. C. Lowekamp, Z. Yaniv, "Image Segmentation, Registration and
|
| 51 |
+
Characterization in R with SimpleITK", *J Stat Softw*, 86(8), https://doi.org/10.18637/jss.v086.i08, 2018.
|
| 52 |
+
|
| 53 |
+
Z. Yaniv, B. C. Lowekamp, H. J. Johnson, R. Beare, "SimpleITK Image-Analysis Notebooks: a Collaborative Environment for Education and Reproducible Research", *J Digit Imaging.*, 31(3): 290-303, https://doi.org/10.1007/s10278-017-0037-8, 2018.
|
| 54 |
+
|
| 55 |
+
B. C. Lowekamp, D. T. Chen, L. Ibáñez, D. Blezek, "The Design of SimpleITK", *Front. Neuroinform.*, 7:45. https://doi.org/10.3389/fninf.2013.00045, 2013.
|
| 56 |
+
|
| 57 |
+
Documentation
|
| 58 |
+
-------------
|
| 59 |
+
With massive libraries like SimpleITK, good documentation is a must. The documentation for SimpleITK is split up into multiple levels:
|
| 60 |
+
1. [API Documentation](https://simpleitk.org/doxygen/latest/html/) - This contains class and function documentation. The descriptions for functions and classes are primarily borrowed from the original ITK C++ classes.
|
| 61 |
+
2. [SimpleITK Documentation](http://simpleitk.readthedocs.io/en/master/) - This site contains high-level guides (fundamental SimpleITK concepts, common conventions, etc.), details with respect to the toolkit's binary distributions, instructions for building the toolkit, as well as SimpleITK examples in all supported programming languages. [This site replaced the
|
| 62 |
+
[SimpleITK Wiki](https://itk.org/Wiki/SimpleITK) which is mostly of interest for historical reasons.]
|
| 63 |
+
3. [Juypyter Notebook Repository](http://insightsoftwareconsortium.github.io/SimpleITK-Notebooks/) - This repository contains a collection of Jupyter Notebooks illustrating the use of SimpleITK for educational and research activities. The notebooks demonstrate the use of SimpleITK for interactive image analysis using the Python and R programming languages. Recommended reading, if you are starting with SimpleITK.
|
| 64 |
+
|
| 65 |
+
|
| 66 |
+
Support
|
| 67 |
+
-------
|
| 68 |
+
|
| 69 |
+
SimpleITK provides access to most of the ITK components, but not all. If you are looking for something specific and can't find it, open an issue on GitHub or ask for support on the [forum](https://discourse.itk.org).
|
| 70 |
+
|
| 71 |
+
Report an Issue
|
| 72 |
+
---------------
|
| 73 |
+
|
| 74 |
+
Help us improve SimpleITK by reporting issues you encounter. When you report an error, you allow us to address your specific problem, but more importantly you are helping all of the SimpleITK community.
|
| 75 |
+
|
| 76 |
+
**Thank you for helping making SimpleITK better!**
|
| 77 |
+
|
| 78 |
+
All issues are reported and managed on the [project's GitHub issue tracker](https://github.com/SimpleITK/SimpleITK/issues). When reporting an issue, please provide as much information as possible to enable us to reproduce the problem.
|
| 79 |
+
|
| 80 |
+
The following information will allow us to address your issue in a timely manner:
|
| 81 |
+
1. Error message (copy & pasted) and focused description of the problem.
|
| 82 |
+
2. Operating system, and version (e.g. OSX 10.11.6).
|
| 83 |
+
3. Programming language, and version (e.g. Python 2.7.14, R 3.2.3).
|
| 84 |
+
4. Version of SimpleITK (e.g. 1.1.0), just invoke the SimpleITK Version() function.
|
| 85 |
+
5. How did you install SimpleITK, binary distribution (e.g. conda install -c simpleitk simpleitk), or built it from source (e.g. devtools::install_github("SimpleITK/SimpleITKRInstaller")).
|
| 86 |
+
6. A minimal working example which causes the error.
|
| 87 |
+
7. If your code requires input, possibly point to a minimal sized input image.
|
my_container_sandbox/workspace/anaconda3/lib/python3.8/site-packages/matplotlib/mpl-data/fonts/ttf/DejaVuSerif-BoldItalic.ttf
ADDED
|
@@ -0,0 +1,3 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
version https://git-lfs.github.com/spec/v1
|
| 2 |
+
oid sha256:d93efec7a9d2e826768d1a2ee95b95870e15e29599a84f3484af1de1cec2e181
|
| 3 |
+
size 347064
|
my_container_sandbox/workspace/anaconda3/lib/python3.8/site-packages/matplotlib/mpl-data/fonts/ttf/DejaVuSerif.ttf
ADDED
|
@@ -0,0 +1,3 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
version https://git-lfs.github.com/spec/v1
|
| 2 |
+
oid sha256:107244956e9962b9e96faccdc551825e0ae0898ae13737133e1b921a2fd35ffa
|
| 3 |
+
size 379740
|
my_container_sandbox/workspace/anaconda3/lib/python3.8/site-packages/past/__pycache__/__init__.cpython-38.pyc
ADDED
|
Binary file (3.1 kB). View file
|
|
|
my_container_sandbox/workspace/anaconda3/lib/python3.8/site-packages/past/builtins/__init__.py
ADDED
|
@@ -0,0 +1,72 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
"""
|
| 2 |
+
A resurrection of some old functions from Python 2 for use in Python 3. These
|
| 3 |
+
should be used sparingly, to help with porting efforts, since code using them
|
| 4 |
+
is no longer standard Python 3 code.
|
| 5 |
+
|
| 6 |
+
This module provides the following:
|
| 7 |
+
|
| 8 |
+
1. Implementations of these builtin functions which have no equivalent on Py3:
|
| 9 |
+
|
| 10 |
+
- apply
|
| 11 |
+
- chr
|
| 12 |
+
- cmp
|
| 13 |
+
- execfile
|
| 14 |
+
|
| 15 |
+
2. Aliases:
|
| 16 |
+
|
| 17 |
+
- intern <- sys.intern
|
| 18 |
+
- raw_input <- input
|
| 19 |
+
- reduce <- functools.reduce
|
| 20 |
+
- reload <- imp.reload
|
| 21 |
+
- unichr <- chr
|
| 22 |
+
- unicode <- str
|
| 23 |
+
- xrange <- range
|
| 24 |
+
|
| 25 |
+
3. List-producing versions of the corresponding Python 3 iterator-producing functions:
|
| 26 |
+
|
| 27 |
+
- filter
|
| 28 |
+
- map
|
| 29 |
+
- range
|
| 30 |
+
- zip
|
| 31 |
+
|
| 32 |
+
4. Forward-ported Py2 types:
|
| 33 |
+
|
| 34 |
+
- basestring
|
| 35 |
+
- dict
|
| 36 |
+
- str
|
| 37 |
+
- long
|
| 38 |
+
- unicode
|
| 39 |
+
|
| 40 |
+
"""
|
| 41 |
+
|
| 42 |
+
from future.utils import PY3
|
| 43 |
+
from past.builtins.noniterators import (filter, map, range, reduce, zip)
|
| 44 |
+
# from past.builtins.misc import (ascii, hex, input, oct, open)
|
| 45 |
+
if PY3:
|
| 46 |
+
from past.types import (basestring,
|
| 47 |
+
olddict as dict,
|
| 48 |
+
oldstr as str,
|
| 49 |
+
long,
|
| 50 |
+
unicode)
|
| 51 |
+
else:
|
| 52 |
+
from __builtin__ import (basestring, dict, str, long, unicode)
|
| 53 |
+
|
| 54 |
+
from past.builtins.misc import (apply, chr, cmp, execfile, intern, oct,
|
| 55 |
+
raw_input, reload, unichr, unicode, xrange)
|
| 56 |
+
from past import utils
|
| 57 |
+
|
| 58 |
+
|
| 59 |
+
if utils.PY3:
|
| 60 |
+
# We only import names that shadow the builtins on Py3. No other namespace
|
| 61 |
+
# pollution on Py3.
|
| 62 |
+
|
| 63 |
+
# Only shadow builtins on Py3; no new names
|
| 64 |
+
__all__ = ['filter', 'map', 'range', 'reduce', 'zip',
|
| 65 |
+
'basestring', 'dict', 'str', 'long', 'unicode',
|
| 66 |
+
'apply', 'chr', 'cmp', 'execfile', 'intern', 'raw_input',
|
| 67 |
+
'reload', 'unichr', 'xrange'
|
| 68 |
+
]
|
| 69 |
+
|
| 70 |
+
else:
|
| 71 |
+
# No namespace pollution on Py2
|
| 72 |
+
__all__ = []
|
my_container_sandbox/workspace/anaconda3/lib/python3.8/site-packages/past/builtins/misc.py
ADDED
|
@@ -0,0 +1,94 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
from __future__ import unicode_literals
|
| 2 |
+
|
| 3 |
+
import inspect
|
| 4 |
+
|
| 5 |
+
from future.utils import PY2, PY3, exec_
|
| 6 |
+
|
| 7 |
+
if PY2:
|
| 8 |
+
from collections import Mapping
|
| 9 |
+
else:
|
| 10 |
+
from collections.abc import Mapping
|
| 11 |
+
|
| 12 |
+
if PY3:
|
| 13 |
+
import builtins
|
| 14 |
+
from collections.abc import Mapping
|
| 15 |
+
|
| 16 |
+
def apply(f, *args, **kw):
|
| 17 |
+
return f(*args, **kw)
|
| 18 |
+
|
| 19 |
+
from past.builtins import str as oldstr
|
| 20 |
+
|
| 21 |
+
def chr(i):
|
| 22 |
+
"""
|
| 23 |
+
Return a byte-string of one character with ordinal i; 0 <= i <= 256
|
| 24 |
+
"""
|
| 25 |
+
return oldstr(bytes((i,)))
|
| 26 |
+
|
| 27 |
+
def cmp(x, y):
|
| 28 |
+
"""
|
| 29 |
+
cmp(x, y) -> integer
|
| 30 |
+
|
| 31 |
+
Return negative if x<y, zero if x==y, positive if x>y.
|
| 32 |
+
"""
|
| 33 |
+
return (x > y) - (x < y)
|
| 34 |
+
|
| 35 |
+
from sys import intern
|
| 36 |
+
|
| 37 |
+
def oct(number):
|
| 38 |
+
"""oct(number) -> string
|
| 39 |
+
|
| 40 |
+
Return the octal representation of an integer
|
| 41 |
+
"""
|
| 42 |
+
return '0' + builtins.oct(number)[2:]
|
| 43 |
+
|
| 44 |
+
raw_input = input
|
| 45 |
+
from imp import reload
|
| 46 |
+
unicode = str
|
| 47 |
+
unichr = chr
|
| 48 |
+
xrange = range
|
| 49 |
+
else:
|
| 50 |
+
import __builtin__
|
| 51 |
+
from collections import Mapping
|
| 52 |
+
apply = __builtin__.apply
|
| 53 |
+
chr = __builtin__.chr
|
| 54 |
+
cmp = __builtin__.cmp
|
| 55 |
+
execfile = __builtin__.execfile
|
| 56 |
+
intern = __builtin__.intern
|
| 57 |
+
oct = __builtin__.oct
|
| 58 |
+
raw_input = __builtin__.raw_input
|
| 59 |
+
reload = __builtin__.reload
|
| 60 |
+
unicode = __builtin__.unicode
|
| 61 |
+
unichr = __builtin__.unichr
|
| 62 |
+
xrange = __builtin__.xrange
|
| 63 |
+
|
| 64 |
+
|
| 65 |
+
if PY3:
|
| 66 |
+
def execfile(filename, myglobals=None, mylocals=None):
|
| 67 |
+
"""
|
| 68 |
+
Read and execute a Python script from a file in the given namespaces.
|
| 69 |
+
The globals and locals are dictionaries, defaulting to the current
|
| 70 |
+
globals and locals. If only globals is given, locals defaults to it.
|
| 71 |
+
"""
|
| 72 |
+
if myglobals is None:
|
| 73 |
+
# There seems to be no alternative to frame hacking here.
|
| 74 |
+
caller_frame = inspect.stack()[1]
|
| 75 |
+
myglobals = caller_frame[0].f_globals
|
| 76 |
+
mylocals = caller_frame[0].f_locals
|
| 77 |
+
elif mylocals is None:
|
| 78 |
+
# Only if myglobals is given do we set mylocals to it.
|
| 79 |
+
mylocals = myglobals
|
| 80 |
+
if not isinstance(myglobals, Mapping):
|
| 81 |
+
raise TypeError('globals must be a mapping')
|
| 82 |
+
if not isinstance(mylocals, Mapping):
|
| 83 |
+
raise TypeError('locals must be a mapping')
|
| 84 |
+
with open(filename, "rb") as fin:
|
| 85 |
+
source = fin.read()
|
| 86 |
+
code = compile(source, filename, "exec")
|
| 87 |
+
exec_(code, myglobals, mylocals)
|
| 88 |
+
|
| 89 |
+
|
| 90 |
+
if PY3:
|
| 91 |
+
__all__ = ['apply', 'chr', 'cmp', 'execfile', 'intern', 'raw_input',
|
| 92 |
+
'reload', 'unichr', 'unicode', 'xrange']
|
| 93 |
+
else:
|
| 94 |
+
__all__ = []
|
my_container_sandbox/workspace/anaconda3/lib/python3.8/site-packages/past/builtins/noniterators.py
ADDED
|
@@ -0,0 +1,272 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
"""
|
| 2 |
+
This module is designed to be used as follows::
|
| 3 |
+
|
| 4 |
+
from past.builtins.noniterators import filter, map, range, reduce, zip
|
| 5 |
+
|
| 6 |
+
And then, for example::
|
| 7 |
+
|
| 8 |
+
assert isinstance(range(5), list)
|
| 9 |
+
|
| 10 |
+
The list-producing functions this brings in are::
|
| 11 |
+
|
| 12 |
+
- ``filter``
|
| 13 |
+
- ``map``
|
| 14 |
+
- ``range``
|
| 15 |
+
- ``reduce``
|
| 16 |
+
- ``zip``
|
| 17 |
+
|
| 18 |
+
"""
|
| 19 |
+
|
| 20 |
+
from __future__ import division, absolute_import, print_function
|
| 21 |
+
|
| 22 |
+
from itertools import chain, starmap
|
| 23 |
+
import itertools # since zip_longest doesn't exist on Py2
|
| 24 |
+
from past.types import basestring
|
| 25 |
+
from past.utils import PY3
|
| 26 |
+
|
| 27 |
+
|
| 28 |
+
def flatmap(f, items):
|
| 29 |
+
return chain.from_iterable(map(f, items))
|
| 30 |
+
|
| 31 |
+
|
| 32 |
+
if PY3:
|
| 33 |
+
import builtins
|
| 34 |
+
|
| 35 |
+
# list-producing versions of the major Python iterating functions
|
| 36 |
+
def oldfilter(*args):
|
| 37 |
+
"""
|
| 38 |
+
filter(function or None, sequence) -> list, tuple, or string
|
| 39 |
+
|
| 40 |
+
Return those items of sequence for which function(item) is true.
|
| 41 |
+
If function is None, return the items that are true. If sequence
|
| 42 |
+
is a tuple or string, return the same type, else return a list.
|
| 43 |
+
"""
|
| 44 |
+
mytype = type(args[1])
|
| 45 |
+
if isinstance(args[1], basestring):
|
| 46 |
+
return mytype().join(builtins.filter(*args))
|
| 47 |
+
elif isinstance(args[1], (tuple, list)):
|
| 48 |
+
return mytype(builtins.filter(*args))
|
| 49 |
+
else:
|
| 50 |
+
# Fall back to list. Is this the right thing to do?
|
| 51 |
+
return list(builtins.filter(*args))
|
| 52 |
+
|
| 53 |
+
# This is surprisingly difficult to get right. For example, the
|
| 54 |
+
# solutions here fail with the test cases in the docstring below:
|
| 55 |
+
# http://stackoverflow.com/questions/8072755/
|
| 56 |
+
def oldmap(func, *iterables):
|
| 57 |
+
"""
|
| 58 |
+
map(function, sequence[, sequence, ...]) -> list
|
| 59 |
+
|
| 60 |
+
Return a list of the results of applying the function to the
|
| 61 |
+
items of the argument sequence(s). If more than one sequence is
|
| 62 |
+
given, the function is called with an argument list consisting of
|
| 63 |
+
the corresponding item of each sequence, substituting None for
|
| 64 |
+
missing values when not all sequences have the same length. If
|
| 65 |
+
the function is None, return a list of the items of the sequence
|
| 66 |
+
(or a list of tuples if more than one sequence).
|
| 67 |
+
|
| 68 |
+
Test cases:
|
| 69 |
+
>>> oldmap(None, 'hello world')
|
| 70 |
+
['h', 'e', 'l', 'l', 'o', ' ', 'w', 'o', 'r', 'l', 'd']
|
| 71 |
+
|
| 72 |
+
>>> oldmap(None, range(4))
|
| 73 |
+
[0, 1, 2, 3]
|
| 74 |
+
|
| 75 |
+
More test cases are in test_past.test_builtins.
|
| 76 |
+
"""
|
| 77 |
+
zipped = itertools.zip_longest(*iterables)
|
| 78 |
+
l = list(zipped)
|
| 79 |
+
if len(l) == 0:
|
| 80 |
+
return []
|
| 81 |
+
if func is None:
|
| 82 |
+
result = l
|
| 83 |
+
else:
|
| 84 |
+
result = list(starmap(func, l))
|
| 85 |
+
|
| 86 |
+
# Inspect to see whether it's a simple sequence of tuples
|
| 87 |
+
try:
|
| 88 |
+
if max([len(item) for item in result]) == 1:
|
| 89 |
+
return list(chain.from_iterable(result))
|
| 90 |
+
# return list(flatmap(func, result))
|
| 91 |
+
except TypeError as e:
|
| 92 |
+
# Simple objects like ints have no len()
|
| 93 |
+
pass
|
| 94 |
+
return result
|
| 95 |
+
|
| 96 |
+
############################
|
| 97 |
+
### For reference, the source code for Py2.7 map function:
|
| 98 |
+
# static PyObject *
|
| 99 |
+
# builtin_map(PyObject *self, PyObject *args)
|
| 100 |
+
# {
|
| 101 |
+
# typedef struct {
|
| 102 |
+
# PyObject *it; /* the iterator object */
|
| 103 |
+
# int saw_StopIteration; /* bool: did the iterator end? */
|
| 104 |
+
# } sequence;
|
| 105 |
+
#
|
| 106 |
+
# PyObject *func, *result;
|
| 107 |
+
# sequence *seqs = NULL, *sqp;
|
| 108 |
+
# Py_ssize_t n, len;
|
| 109 |
+
# register int i, j;
|
| 110 |
+
#
|
| 111 |
+
# n = PyTuple_Size(args);
|
| 112 |
+
# if (n < 2) {
|
| 113 |
+
# PyErr_SetString(PyExc_TypeError,
|
| 114 |
+
# "map() requires at least two args");
|
| 115 |
+
# return NULL;
|
| 116 |
+
# }
|
| 117 |
+
#
|
| 118 |
+
# func = PyTuple_GetItem(args, 0);
|
| 119 |
+
# n--;
|
| 120 |
+
#
|
| 121 |
+
# if (func == Py_None) {
|
| 122 |
+
# if (PyErr_WarnPy3k("map(None, ...) not supported in 3.x; "
|
| 123 |
+
# "use list(...)", 1) < 0)
|
| 124 |
+
# return NULL;
|
| 125 |
+
# if (n == 1) {
|
| 126 |
+
# /* map(None, S) is the same as list(S). */
|
| 127 |
+
# return PySequence_List(PyTuple_GetItem(args, 1));
|
| 128 |
+
# }
|
| 129 |
+
# }
|
| 130 |
+
#
|
| 131 |
+
# /* Get space for sequence descriptors. Must NULL out the iterator
|
| 132 |
+
# * pointers so that jumping to Fail_2 later doesn't see trash.
|
| 133 |
+
# */
|
| 134 |
+
# if ((seqs = PyMem_NEW(sequence, n)) == NULL) {
|
| 135 |
+
# PyErr_NoMemory();
|
| 136 |
+
# return NULL;
|
| 137 |
+
# }
|
| 138 |
+
# for (i = 0; i < n; ++i) {
|
| 139 |
+
# seqs[i].it = (PyObject*)NULL;
|
| 140 |
+
# seqs[i].saw_StopIteration = 0;
|
| 141 |
+
# }
|
| 142 |
+
#
|
| 143 |
+
# /* Do a first pass to obtain iterators for the arguments, and set len
|
| 144 |
+
# * to the largest of their lengths.
|
| 145 |
+
# */
|
| 146 |
+
# len = 0;
|
| 147 |
+
# for (i = 0, sqp = seqs; i < n; ++i, ++sqp) {
|
| 148 |
+
# PyObject *curseq;
|
| 149 |
+
# Py_ssize_t curlen;
|
| 150 |
+
#
|
| 151 |
+
# /* Get iterator. */
|
| 152 |
+
# curseq = PyTuple_GetItem(args, i+1);
|
| 153 |
+
# sqp->it = PyObject_GetIter(curseq);
|
| 154 |
+
# if (sqp->it == NULL) {
|
| 155 |
+
# static char errmsg[] =
|
| 156 |
+
# "argument %d to map() must support iteration";
|
| 157 |
+
# char errbuf[sizeof(errmsg) + 25];
|
| 158 |
+
# PyOS_snprintf(errbuf, sizeof(errbuf), errmsg, i+2);
|
| 159 |
+
# PyErr_SetString(PyExc_TypeError, errbuf);
|
| 160 |
+
# goto Fail_2;
|
| 161 |
+
# }
|
| 162 |
+
#
|
| 163 |
+
# /* Update len. */
|
| 164 |
+
# curlen = _PyObject_LengthHint(curseq, 8);
|
| 165 |
+
# if (curlen > len)
|
| 166 |
+
# len = curlen;
|
| 167 |
+
# }
|
| 168 |
+
#
|
| 169 |
+
# /* Get space for the result list. */
|
| 170 |
+
# if ((result = (PyObject *) PyList_New(len)) == NULL)
|
| 171 |
+
# goto Fail_2;
|
| 172 |
+
#
|
| 173 |
+
# /* Iterate over the sequences until all have stopped. */
|
| 174 |
+
# for (i = 0; ; ++i) {
|
| 175 |
+
# PyObject *alist, *item=NULL, *value;
|
| 176 |
+
# int numactive = 0;
|
| 177 |
+
#
|
| 178 |
+
# if (func == Py_None && n == 1)
|
| 179 |
+
# alist = NULL;
|
| 180 |
+
# else if ((alist = PyTuple_New(n)) == NULL)
|
| 181 |
+
# goto Fail_1;
|
| 182 |
+
#
|
| 183 |
+
# for (j = 0, sqp = seqs; j < n; ++j, ++sqp) {
|
| 184 |
+
# if (sqp->saw_StopIteration) {
|
| 185 |
+
# Py_INCREF(Py_None);
|
| 186 |
+
# item = Py_None;
|
| 187 |
+
# }
|
| 188 |
+
# else {
|
| 189 |
+
# item = PyIter_Next(sqp->it);
|
| 190 |
+
# if (item)
|
| 191 |
+
# ++numactive;
|
| 192 |
+
# else {
|
| 193 |
+
# if (PyErr_Occurred()) {
|
| 194 |
+
# Py_XDECREF(alist);
|
| 195 |
+
# goto Fail_1;
|
| 196 |
+
# }
|
| 197 |
+
# Py_INCREF(Py_None);
|
| 198 |
+
# item = Py_None;
|
| 199 |
+
# sqp->saw_StopIteration = 1;
|
| 200 |
+
# }
|
| 201 |
+
# }
|
| 202 |
+
# if (alist)
|
| 203 |
+
# PyTuple_SET_ITEM(alist, j, item);
|
| 204 |
+
# else
|
| 205 |
+
# break;
|
| 206 |
+
# }
|
| 207 |
+
#
|
| 208 |
+
# if (!alist)
|
| 209 |
+
# alist = item;
|
| 210 |
+
#
|
| 211 |
+
# if (numactive == 0) {
|
| 212 |
+
# Py_DECREF(alist);
|
| 213 |
+
# break;
|
| 214 |
+
# }
|
| 215 |
+
#
|
| 216 |
+
# if (func == Py_None)
|
| 217 |
+
# value = alist;
|
| 218 |
+
# else {
|
| 219 |
+
# value = PyEval_CallObject(func, alist);
|
| 220 |
+
# Py_DECREF(alist);
|
| 221 |
+
# if (value == NULL)
|
| 222 |
+
# goto Fail_1;
|
| 223 |
+
# }
|
| 224 |
+
# if (i >= len) {
|
| 225 |
+
# int status = PyList_Append(result, value);
|
| 226 |
+
# Py_DECREF(value);
|
| 227 |
+
# if (status < 0)
|
| 228 |
+
# goto Fail_1;
|
| 229 |
+
# }
|
| 230 |
+
# else if (PyList_SetItem(result, i, value) < 0)
|
| 231 |
+
# goto Fail_1;
|
| 232 |
+
# }
|
| 233 |
+
#
|
| 234 |
+
# if (i < len && PyList_SetSlice(result, i, len, NULL) < 0)
|
| 235 |
+
# goto Fail_1;
|
| 236 |
+
#
|
| 237 |
+
# goto Succeed;
|
| 238 |
+
#
|
| 239 |
+
# Fail_1:
|
| 240 |
+
# Py_DECREF(result);
|
| 241 |
+
# Fail_2:
|
| 242 |
+
# result = NULL;
|
| 243 |
+
# Succeed:
|
| 244 |
+
# assert(seqs);
|
| 245 |
+
# for (i = 0; i < n; ++i)
|
| 246 |
+
# Py_XDECREF(seqs[i].it);
|
| 247 |
+
# PyMem_DEL(seqs);
|
| 248 |
+
# return result;
|
| 249 |
+
# }
|
| 250 |
+
|
| 251 |
+
def oldrange(*args, **kwargs):
|
| 252 |
+
return list(builtins.range(*args, **kwargs))
|
| 253 |
+
|
| 254 |
+
def oldzip(*args, **kwargs):
|
| 255 |
+
return list(builtins.zip(*args, **kwargs))
|
| 256 |
+
|
| 257 |
+
filter = oldfilter
|
| 258 |
+
map = oldmap
|
| 259 |
+
range = oldrange
|
| 260 |
+
from functools import reduce
|
| 261 |
+
zip = oldzip
|
| 262 |
+
__all__ = ['filter', 'map', 'range', 'reduce', 'zip']
|
| 263 |
+
|
| 264 |
+
else:
|
| 265 |
+
import __builtin__
|
| 266 |
+
# Python 2-builtin ranges produce lists
|
| 267 |
+
filter = __builtin__.filter
|
| 268 |
+
map = __builtin__.map
|
| 269 |
+
range = __builtin__.range
|
| 270 |
+
reduce = __builtin__.reduce
|
| 271 |
+
zip = __builtin__.zip
|
| 272 |
+
__all__ = []
|
my_container_sandbox/workspace/anaconda3/lib/python3.8/site-packages/past/translation/__pycache__/__init__.cpython-38.pyc
ADDED
|
Binary file (11.2 kB). View file
|
|
|
my_container_sandbox/workspace/anaconda3/lib/python3.8/site-packages/past/types/__init__.py
ADDED
|
@@ -0,0 +1,29 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
"""
|
| 2 |
+
Forward-ports of types from Python 2 for use with Python 3:
|
| 3 |
+
|
| 4 |
+
- ``basestring``: equivalent to ``(str, bytes)`` in ``isinstance`` checks
|
| 5 |
+
- ``dict``: with list-producing .keys() etc. methods
|
| 6 |
+
- ``str``: bytes-like, but iterating over them doesn't product integers
|
| 7 |
+
- ``long``: alias of Py3 int with ``L`` suffix in the ``repr``
|
| 8 |
+
- ``unicode``: alias of Py3 str with ``u`` prefix in the ``repr``
|
| 9 |
+
|
| 10 |
+
"""
|
| 11 |
+
|
| 12 |
+
from past import utils
|
| 13 |
+
|
| 14 |
+
if utils.PY2:
|
| 15 |
+
import __builtin__
|
| 16 |
+
basestring = __builtin__.basestring
|
| 17 |
+
dict = __builtin__.dict
|
| 18 |
+
str = __builtin__.str
|
| 19 |
+
long = __builtin__.long
|
| 20 |
+
unicode = __builtin__.unicode
|
| 21 |
+
__all__ = []
|
| 22 |
+
else:
|
| 23 |
+
from .basestring import basestring
|
| 24 |
+
from .olddict import olddict
|
| 25 |
+
from .oldstr import oldstr
|
| 26 |
+
long = int
|
| 27 |
+
unicode = str
|
| 28 |
+
# from .unicode import unicode
|
| 29 |
+
__all__ = ['basestring', 'olddict', 'oldstr', 'long', 'unicode']
|
my_container_sandbox/workspace/anaconda3/lib/python3.8/site-packages/past/types/olddict.py
ADDED
|
@@ -0,0 +1,96 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
"""
|
| 2 |
+
A dict subclass for Python 3 that behaves like Python 2's dict
|
| 3 |
+
|
| 4 |
+
Example use:
|
| 5 |
+
|
| 6 |
+
>>> from past.builtins import dict
|
| 7 |
+
>>> d1 = dict() # instead of {} for an empty dict
|
| 8 |
+
>>> d2 = dict(key1='value1', key2='value2')
|
| 9 |
+
|
| 10 |
+
The keys, values and items methods now return lists on Python 3.x and there are
|
| 11 |
+
methods for iterkeys, itervalues, iteritems, and viewkeys etc.
|
| 12 |
+
|
| 13 |
+
>>> for d in (d1, d2):
|
| 14 |
+
... assert isinstance(d.keys(), list)
|
| 15 |
+
... assert isinstance(d.values(), list)
|
| 16 |
+
... assert isinstance(d.items(), list)
|
| 17 |
+
"""
|
| 18 |
+
|
| 19 |
+
import sys
|
| 20 |
+
|
| 21 |
+
from past.utils import with_metaclass
|
| 22 |
+
|
| 23 |
+
|
| 24 |
+
_builtin_dict = dict
|
| 25 |
+
ver = sys.version_info[:2]
|
| 26 |
+
|
| 27 |
+
|
| 28 |
+
class BaseOldDict(type):
|
| 29 |
+
def __instancecheck__(cls, instance):
|
| 30 |
+
return isinstance(instance, _builtin_dict)
|
| 31 |
+
|
| 32 |
+
|
| 33 |
+
class olddict(with_metaclass(BaseOldDict, _builtin_dict)):
|
| 34 |
+
"""
|
| 35 |
+
A backport of the Python 3 dict object to Py2
|
| 36 |
+
"""
|
| 37 |
+
iterkeys = _builtin_dict.keys
|
| 38 |
+
viewkeys = _builtin_dict.keys
|
| 39 |
+
|
| 40 |
+
def keys(self):
|
| 41 |
+
return list(super(olddict, self).keys())
|
| 42 |
+
|
| 43 |
+
itervalues = _builtin_dict.values
|
| 44 |
+
viewvalues = _builtin_dict.values
|
| 45 |
+
|
| 46 |
+
def values(self):
|
| 47 |
+
return list(super(olddict, self).values())
|
| 48 |
+
|
| 49 |
+
iteritems = _builtin_dict.items
|
| 50 |
+
viewitems = _builtin_dict.items
|
| 51 |
+
|
| 52 |
+
def items(self):
|
| 53 |
+
return list(super(olddict, self).items())
|
| 54 |
+
|
| 55 |
+
def has_key(self, k):
|
| 56 |
+
"""
|
| 57 |
+
D.has_key(k) -> True if D has a key k, else False
|
| 58 |
+
"""
|
| 59 |
+
return k in self
|
| 60 |
+
|
| 61 |
+
# def __new__(cls, *args, **kwargs):
|
| 62 |
+
# """
|
| 63 |
+
# dict() -> new empty dictionary
|
| 64 |
+
# dict(mapping) -> new dictionary initialized from a mapping object's
|
| 65 |
+
# (key, value) pairs
|
| 66 |
+
# dict(iterable) -> new dictionary initialized as if via:
|
| 67 |
+
# d = {}
|
| 68 |
+
# for k, v in iterable:
|
| 69 |
+
# d[k] = v
|
| 70 |
+
# dict(**kwargs) -> new dictionary initialized with the name=value pairs
|
| 71 |
+
# in the keyword argument list. For example: dict(one=1, two=2)
|
| 72 |
+
|
| 73 |
+
# """
|
| 74 |
+
#
|
| 75 |
+
# if len(args) == 0:
|
| 76 |
+
# return super(olddict, cls).__new__(cls)
|
| 77 |
+
# # Was: elif isinstance(args[0], newbytes):
|
| 78 |
+
# # We use type() instead of the above because we're redefining
|
| 79 |
+
# # this to be True for all unicode string subclasses. Warning:
|
| 80 |
+
# # This may render newstr un-subclassable.
|
| 81 |
+
# elif type(args[0]) == olddict:
|
| 82 |
+
# return args[0]
|
| 83 |
+
# # elif isinstance(args[0], _builtin_dict):
|
| 84 |
+
# # value = args[0]
|
| 85 |
+
# else:
|
| 86 |
+
# value = args[0]
|
| 87 |
+
# return super(olddict, cls).__new__(cls, value)
|
| 88 |
+
|
| 89 |
+
def __native__(self):
|
| 90 |
+
"""
|
| 91 |
+
Hook for the past.utils.native() function
|
| 92 |
+
"""
|
| 93 |
+
return super(oldbytes, self)
|
| 94 |
+
|
| 95 |
+
|
| 96 |
+
__all__ = ['olddict']
|
my_container_sandbox/workspace/anaconda3/lib/python3.8/site-packages/past/types/oldstr.py
ADDED
|
@@ -0,0 +1,135 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
"""
|
| 2 |
+
Pure-Python implementation of a Python 2-like str object for Python 3.
|
| 3 |
+
"""
|
| 4 |
+
|
| 5 |
+
from numbers import Integral
|
| 6 |
+
|
| 7 |
+
from past.utils import PY2, with_metaclass
|
| 8 |
+
|
| 9 |
+
if PY2:
|
| 10 |
+
from collections import Iterable
|
| 11 |
+
else:
|
| 12 |
+
from collections.abc import Iterable
|
| 13 |
+
|
| 14 |
+
_builtin_bytes = bytes
|
| 15 |
+
|
| 16 |
+
|
| 17 |
+
class BaseOldStr(type):
|
| 18 |
+
def __instancecheck__(cls, instance):
|
| 19 |
+
return isinstance(instance, _builtin_bytes)
|
| 20 |
+
|
| 21 |
+
|
| 22 |
+
def unescape(s):
|
| 23 |
+
"""
|
| 24 |
+
Interprets strings with escape sequences
|
| 25 |
+
|
| 26 |
+
Example:
|
| 27 |
+
>>> s = unescape(r'abc\\def') # i.e. 'abc\\\\def'
|
| 28 |
+
>>> print(s)
|
| 29 |
+
'abc\def'
|
| 30 |
+
>>> s2 = unescape('abc\\ndef')
|
| 31 |
+
>>> len(s2)
|
| 32 |
+
8
|
| 33 |
+
>>> print(s2)
|
| 34 |
+
abc
|
| 35 |
+
def
|
| 36 |
+
"""
|
| 37 |
+
return s.encode().decode('unicode_escape')
|
| 38 |
+
|
| 39 |
+
|
| 40 |
+
class oldstr(with_metaclass(BaseOldStr, _builtin_bytes)):
|
| 41 |
+
"""
|
| 42 |
+
A forward port of the Python 2 8-bit string object to Py3
|
| 43 |
+
"""
|
| 44 |
+
# Python 2 strings have no __iter__ method:
|
| 45 |
+
@property
|
| 46 |
+
def __iter__(self):
|
| 47 |
+
raise AttributeError
|
| 48 |
+
|
| 49 |
+
def __dir__(self):
|
| 50 |
+
return [thing for thing in dir(_builtin_bytes) if thing != '__iter__']
|
| 51 |
+
|
| 52 |
+
# def __new__(cls, *args, **kwargs):
|
| 53 |
+
# """
|
| 54 |
+
# From the Py3 bytes docstring:
|
| 55 |
+
|
| 56 |
+
# bytes(iterable_of_ints) -> bytes
|
| 57 |
+
# bytes(string, encoding[, errors]) -> bytes
|
| 58 |
+
# bytes(bytes_or_buffer) -> immutable copy of bytes_or_buffer
|
| 59 |
+
# bytes(int) -> bytes object of size given by the parameter initialized with null bytes
|
| 60 |
+
# bytes() -> empty bytes object
|
| 61 |
+
#
|
| 62 |
+
# Construct an immutable array of bytes from:
|
| 63 |
+
# - an iterable yielding integers in range(256)
|
| 64 |
+
# - a text string encoded using the specified encoding
|
| 65 |
+
# - any object implementing the buffer API.
|
| 66 |
+
# - an integer
|
| 67 |
+
# """
|
| 68 |
+
#
|
| 69 |
+
# if len(args) == 0:
|
| 70 |
+
# return super(newbytes, cls).__new__(cls)
|
| 71 |
+
# # Was: elif isinstance(args[0], newbytes):
|
| 72 |
+
# # We use type() instead of the above because we're redefining
|
| 73 |
+
# # this to be True for all unicode string subclasses. Warning:
|
| 74 |
+
# # This may render newstr un-subclassable.
|
| 75 |
+
# elif type(args[0]) == newbytes:
|
| 76 |
+
# return args[0]
|
| 77 |
+
# elif isinstance(args[0], _builtin_bytes):
|
| 78 |
+
# value = args[0]
|
| 79 |
+
# elif isinstance(args[0], unicode):
|
| 80 |
+
# if 'encoding' not in kwargs:
|
| 81 |
+
# raise TypeError('unicode string argument without an encoding')
|
| 82 |
+
# ###
|
| 83 |
+
# # Was: value = args[0].encode(**kwargs)
|
| 84 |
+
# # Python 2.6 string encode() method doesn't take kwargs:
|
| 85 |
+
# # Use this instead:
|
| 86 |
+
# newargs = [kwargs['encoding']]
|
| 87 |
+
# if 'errors' in kwargs:
|
| 88 |
+
# newargs.append(kwargs['errors'])
|
| 89 |
+
# value = args[0].encode(*newargs)
|
| 90 |
+
# ###
|
| 91 |
+
# elif isinstance(args[0], Iterable):
|
| 92 |
+
# if len(args[0]) == 0:
|
| 93 |
+
# # What is this?
|
| 94 |
+
# raise ValueError('unknown argument type')
|
| 95 |
+
# elif len(args[0]) > 0 and isinstance(args[0][0], Integral):
|
| 96 |
+
# # It's a list of integers
|
| 97 |
+
# value = b''.join([chr(x) for x in args[0]])
|
| 98 |
+
# else:
|
| 99 |
+
# raise ValueError('item cannot be interpreted as an integer')
|
| 100 |
+
# elif isinstance(args[0], Integral):
|
| 101 |
+
# if args[0] < 0:
|
| 102 |
+
# raise ValueError('negative count')
|
| 103 |
+
# value = b'\x00' * args[0]
|
| 104 |
+
# else:
|
| 105 |
+
# value = args[0]
|
| 106 |
+
# return super(newbytes, cls).__new__(cls, value)
|
| 107 |
+
|
| 108 |
+
def __repr__(self):
|
| 109 |
+
s = super(oldstr, self).__repr__() # e.g. b'abc' on Py3, b'abc' on Py3
|
| 110 |
+
return s[1:]
|
| 111 |
+
|
| 112 |
+
def __str__(self):
|
| 113 |
+
s = super(oldstr, self).__str__() # e.g. "b'abc'" or "b'abc\\ndef'
|
| 114 |
+
# TODO: fix this:
|
| 115 |
+
assert s[:2] == "b'" and s[-1] == "'"
|
| 116 |
+
return unescape(s[2:-1]) # e.g. 'abc' or 'abc\ndef'
|
| 117 |
+
|
| 118 |
+
def __getitem__(self, y):
|
| 119 |
+
if isinstance(y, Integral):
|
| 120 |
+
return super(oldstr, self).__getitem__(slice(y, y+1))
|
| 121 |
+
else:
|
| 122 |
+
return super(oldstr, self).__getitem__(y)
|
| 123 |
+
|
| 124 |
+
def __getslice__(self, *args):
|
| 125 |
+
return self.__getitem__(slice(*args))
|
| 126 |
+
|
| 127 |
+
def __contains__(self, key):
|
| 128 |
+
if isinstance(key, int):
|
| 129 |
+
return False
|
| 130 |
+
|
| 131 |
+
def __native__(self):
|
| 132 |
+
return bytes(self)
|
| 133 |
+
|
| 134 |
+
|
| 135 |
+
__all__ = ['oldstr']
|
my_container_sandbox/workspace/anaconda3/lib/python3.8/site-packages/past/utils/__init__.py
ADDED
|
@@ -0,0 +1,97 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
"""
|
| 2 |
+
Various non-built-in utility functions and definitions for Py2
|
| 3 |
+
compatibility in Py3.
|
| 4 |
+
|
| 5 |
+
For example:
|
| 6 |
+
|
| 7 |
+
>>> # The old_div() function behaves like Python 2's / operator
|
| 8 |
+
>>> # without "from __future__ import division"
|
| 9 |
+
>>> from past.utils import old_div
|
| 10 |
+
>>> old_div(3, 2) # like 3/2 in Py2
|
| 11 |
+
0
|
| 12 |
+
>>> old_div(3, 2.0) # like 3/2.0 in Py2
|
| 13 |
+
1.5
|
| 14 |
+
"""
|
| 15 |
+
|
| 16 |
+
import sys
|
| 17 |
+
import numbers
|
| 18 |
+
|
| 19 |
+
PY3 = sys.version_info[0] >= 3
|
| 20 |
+
PY2 = sys.version_info[0] == 2
|
| 21 |
+
PYPY = hasattr(sys, 'pypy_translation_info')
|
| 22 |
+
|
| 23 |
+
|
| 24 |
+
def with_metaclass(meta, *bases):
|
| 25 |
+
"""
|
| 26 |
+
Function from jinja2/_compat.py. License: BSD.
|
| 27 |
+
|
| 28 |
+
Use it like this::
|
| 29 |
+
|
| 30 |
+
class BaseForm(object):
|
| 31 |
+
pass
|
| 32 |
+
|
| 33 |
+
class FormType(type):
|
| 34 |
+
pass
|
| 35 |
+
|
| 36 |
+
class Form(with_metaclass(FormType, BaseForm)):
|
| 37 |
+
pass
|
| 38 |
+
|
| 39 |
+
This requires a bit of explanation: the basic idea is to make a
|
| 40 |
+
dummy metaclass for one level of class instantiation that replaces
|
| 41 |
+
itself with the actual metaclass. Because of internal type checks
|
| 42 |
+
we also need to make sure that we downgrade the custom metaclass
|
| 43 |
+
for one level to something closer to type (that's why __call__ and
|
| 44 |
+
__init__ comes back from type etc.).
|
| 45 |
+
|
| 46 |
+
This has the advantage over six.with_metaclass of not introducing
|
| 47 |
+
dummy classes into the final MRO.
|
| 48 |
+
"""
|
| 49 |
+
class metaclass(meta):
|
| 50 |
+
__call__ = type.__call__
|
| 51 |
+
__init__ = type.__init__
|
| 52 |
+
def __new__(cls, name, this_bases, d):
|
| 53 |
+
if this_bases is None:
|
| 54 |
+
return type.__new__(cls, name, (), d)
|
| 55 |
+
return meta(name, bases, d)
|
| 56 |
+
return metaclass('temporary_class', None, {})
|
| 57 |
+
|
| 58 |
+
|
| 59 |
+
def native(obj):
|
| 60 |
+
"""
|
| 61 |
+
On Py2, this is a no-op: native(obj) -> obj
|
| 62 |
+
|
| 63 |
+
On Py3, returns the corresponding native Py3 types that are
|
| 64 |
+
superclasses for forward-ported objects from Py2:
|
| 65 |
+
|
| 66 |
+
>>> from past.builtins import str, dict
|
| 67 |
+
|
| 68 |
+
>>> native(str(b'ABC')) # Output on Py3 follows. On Py2, output is 'ABC'
|
| 69 |
+
b'ABC'
|
| 70 |
+
>>> type(native(str(b'ABC')))
|
| 71 |
+
bytes
|
| 72 |
+
|
| 73 |
+
Existing native types on Py3 will be returned unchanged:
|
| 74 |
+
|
| 75 |
+
>>> type(native(b'ABC'))
|
| 76 |
+
bytes
|
| 77 |
+
"""
|
| 78 |
+
if hasattr(obj, '__native__'):
|
| 79 |
+
return obj.__native__()
|
| 80 |
+
else:
|
| 81 |
+
return obj
|
| 82 |
+
|
| 83 |
+
|
| 84 |
+
# An alias for future.utils.old_div():
|
| 85 |
+
def old_div(a, b):
|
| 86 |
+
"""
|
| 87 |
+
Equivalent to ``a / b`` on Python 2 without ``from __future__ import
|
| 88 |
+
division``.
|
| 89 |
+
|
| 90 |
+
TODO: generalize this to other objects (like arrays etc.)
|
| 91 |
+
"""
|
| 92 |
+
if isinstance(a, numbers.Integral) and isinstance(b, numbers.Integral):
|
| 93 |
+
return a // b
|
| 94 |
+
else:
|
| 95 |
+
return a / b
|
| 96 |
+
|
| 97 |
+
__all__ = ['PY3', 'PY2', 'PYPY', 'with_metaclass', 'native', 'old_div']
|
my_container_sandbox/workspace/anaconda3/lib/python3.8/site-packages/torchvision/io/__pycache__/__init__.cpython-38.pyc
ADDED
|
Binary file (1.46 kB). View file
|
|
|
my_container_sandbox/workspace/anaconda3/lib/python3.8/site-packages/torchvision/io/__pycache__/_load_gpu_decoder.cpython-38.pyc
ADDED
|
Binary file (335 Bytes). View file
|
|
|
my_container_sandbox/workspace/anaconda3/lib/python3.8/site-packages/torchvision/io/__pycache__/_video_opt.cpython-38.pyc
ADDED
|
Binary file (15.5 kB). View file
|
|
|
my_container_sandbox/workspace/anaconda3/lib/python3.8/site-packages/torchvision/io/__pycache__/image.cpython-38.pyc
ADDED
|
Binary file (9.53 kB). View file
|
|
|
my_container_sandbox/workspace/anaconda3/lib/python3.8/site-packages/torchvision/io/__pycache__/video.cpython-38.pyc
ADDED
|
Binary file (11.6 kB). View file
|
|
|
my_container_sandbox/workspace/anaconda3/lib/python3.8/site-packages/torchvision/io/__pycache__/video_reader.cpython-38.pyc
ADDED
|
Binary file (7.08 kB). View file
|
|
|
my_container_sandbox/workspace/anaconda3/lib/python3.8/site-packages/torchvision/io/_video_opt.py
ADDED
|
@@ -0,0 +1,505 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
import math
|
| 2 |
+
import warnings
|
| 3 |
+
from fractions import Fraction
|
| 4 |
+
from typing import List, Tuple, Dict, Optional, Union
|
| 5 |
+
|
| 6 |
+
import torch
|
| 7 |
+
|
| 8 |
+
from ..extension import _load_library
|
| 9 |
+
|
| 10 |
+
|
| 11 |
+
try:
|
| 12 |
+
_load_library("video_reader")
|
| 13 |
+
_HAS_VIDEO_OPT = True
|
| 14 |
+
except (ImportError, OSError):
|
| 15 |
+
_HAS_VIDEO_OPT = False
|
| 16 |
+
|
| 17 |
+
default_timebase = Fraction(0, 1)
|
| 18 |
+
|
| 19 |
+
|
| 20 |
+
# simple class for torch scripting
|
| 21 |
+
# the complex Fraction class from fractions module is not scriptable
|
| 22 |
+
class Timebase:
|
| 23 |
+
__annotations__ = {"numerator": int, "denominator": int}
|
| 24 |
+
__slots__ = ["numerator", "denominator"]
|
| 25 |
+
|
| 26 |
+
def __init__(
|
| 27 |
+
self,
|
| 28 |
+
numerator: int,
|
| 29 |
+
denominator: int,
|
| 30 |
+
) -> None:
|
| 31 |
+
self.numerator = numerator
|
| 32 |
+
self.denominator = denominator
|
| 33 |
+
|
| 34 |
+
|
| 35 |
+
class VideoMetaData:
|
| 36 |
+
__annotations__ = {
|
| 37 |
+
"has_video": bool,
|
| 38 |
+
"video_timebase": Timebase,
|
| 39 |
+
"video_duration": float,
|
| 40 |
+
"video_fps": float,
|
| 41 |
+
"has_audio": bool,
|
| 42 |
+
"audio_timebase": Timebase,
|
| 43 |
+
"audio_duration": float,
|
| 44 |
+
"audio_sample_rate": float,
|
| 45 |
+
}
|
| 46 |
+
__slots__ = [
|
| 47 |
+
"has_video",
|
| 48 |
+
"video_timebase",
|
| 49 |
+
"video_duration",
|
| 50 |
+
"video_fps",
|
| 51 |
+
"has_audio",
|
| 52 |
+
"audio_timebase",
|
| 53 |
+
"audio_duration",
|
| 54 |
+
"audio_sample_rate",
|
| 55 |
+
]
|
| 56 |
+
|
| 57 |
+
def __init__(self) -> None:
|
| 58 |
+
self.has_video = False
|
| 59 |
+
self.video_timebase = Timebase(0, 1)
|
| 60 |
+
self.video_duration = 0.0
|
| 61 |
+
self.video_fps = 0.0
|
| 62 |
+
self.has_audio = False
|
| 63 |
+
self.audio_timebase = Timebase(0, 1)
|
| 64 |
+
self.audio_duration = 0.0
|
| 65 |
+
self.audio_sample_rate = 0.0
|
| 66 |
+
|
| 67 |
+
|
| 68 |
+
def _validate_pts(pts_range: Tuple[int, int]) -> None:
|
| 69 |
+
|
| 70 |
+
if pts_range[0] > pts_range[1] > 0:
|
| 71 |
+
raise ValueError(
|
| 72 |
+
f"Start pts should not be smaller than end pts, got start pts: {pts_range[0]} and end pts: {pts_range[1]}"
|
| 73 |
+
)
|
| 74 |
+
|
| 75 |
+
|
| 76 |
+
def _fill_info(
|
| 77 |
+
vtimebase: torch.Tensor,
|
| 78 |
+
vfps: torch.Tensor,
|
| 79 |
+
vduration: torch.Tensor,
|
| 80 |
+
atimebase: torch.Tensor,
|
| 81 |
+
asample_rate: torch.Tensor,
|
| 82 |
+
aduration: torch.Tensor,
|
| 83 |
+
) -> VideoMetaData:
|
| 84 |
+
"""
|
| 85 |
+
Build update VideoMetaData struct with info about the video
|
| 86 |
+
"""
|
| 87 |
+
meta = VideoMetaData()
|
| 88 |
+
if vtimebase.numel() > 0:
|
| 89 |
+
meta.video_timebase = Timebase(int(vtimebase[0].item()), int(vtimebase[1].item()))
|
| 90 |
+
timebase = vtimebase[0].item() / float(vtimebase[1].item())
|
| 91 |
+
if vduration.numel() > 0:
|
| 92 |
+
meta.has_video = True
|
| 93 |
+
meta.video_duration = float(vduration.item()) * timebase
|
| 94 |
+
if vfps.numel() > 0:
|
| 95 |
+
meta.video_fps = float(vfps.item())
|
| 96 |
+
if atimebase.numel() > 0:
|
| 97 |
+
meta.audio_timebase = Timebase(int(atimebase[0].item()), int(atimebase[1].item()))
|
| 98 |
+
timebase = atimebase[0].item() / float(atimebase[1].item())
|
| 99 |
+
if aduration.numel() > 0:
|
| 100 |
+
meta.has_audio = True
|
| 101 |
+
meta.audio_duration = float(aduration.item()) * timebase
|
| 102 |
+
if asample_rate.numel() > 0:
|
| 103 |
+
meta.audio_sample_rate = float(asample_rate.item())
|
| 104 |
+
|
| 105 |
+
return meta
|
| 106 |
+
|
| 107 |
+
|
| 108 |
+
def _align_audio_frames(
|
| 109 |
+
aframes: torch.Tensor, aframe_pts: torch.Tensor, audio_pts_range: Tuple[int, int]
|
| 110 |
+
) -> torch.Tensor:
|
| 111 |
+
start, end = aframe_pts[0], aframe_pts[-1]
|
| 112 |
+
num_samples = aframes.size(0)
|
| 113 |
+
step_per_aframe = float(end - start + 1) / float(num_samples)
|
| 114 |
+
s_idx = 0
|
| 115 |
+
e_idx = num_samples
|
| 116 |
+
if start < audio_pts_range[0]:
|
| 117 |
+
s_idx = int((audio_pts_range[0] - start) / step_per_aframe)
|
| 118 |
+
if audio_pts_range[1] != -1 and end > audio_pts_range[1]:
|
| 119 |
+
e_idx = int((audio_pts_range[1] - end) / step_per_aframe)
|
| 120 |
+
return aframes[s_idx:e_idx, :]
|
| 121 |
+
|
| 122 |
+
|
| 123 |
+
def _read_video_from_file(
|
| 124 |
+
filename: str,
|
| 125 |
+
seek_frame_margin: float = 0.25,
|
| 126 |
+
read_video_stream: bool = True,
|
| 127 |
+
video_width: int = 0,
|
| 128 |
+
video_height: int = 0,
|
| 129 |
+
video_min_dimension: int = 0,
|
| 130 |
+
video_max_dimension: int = 0,
|
| 131 |
+
video_pts_range: Tuple[int, int] = (0, -1),
|
| 132 |
+
video_timebase: Fraction = default_timebase,
|
| 133 |
+
read_audio_stream: bool = True,
|
| 134 |
+
audio_samples: int = 0,
|
| 135 |
+
audio_channels: int = 0,
|
| 136 |
+
audio_pts_range: Tuple[int, int] = (0, -1),
|
| 137 |
+
audio_timebase: Fraction = default_timebase,
|
| 138 |
+
) -> Tuple[torch.Tensor, torch.Tensor, VideoMetaData]:
|
| 139 |
+
"""
|
| 140 |
+
Reads a video from a file, returning both the video frames as well as
|
| 141 |
+
the audio frames
|
| 142 |
+
|
| 143 |
+
Args:
|
| 144 |
+
filename (str): path to the video file
|
| 145 |
+
seek_frame_margin (double, optional): seeking frame in the stream is imprecise. Thus,
|
| 146 |
+
when video_start_pts is specified, we seek the pts earlier by seek_frame_margin seconds
|
| 147 |
+
read_video_stream (int, optional): whether read video stream. If yes, set to 1. Otherwise, 0
|
| 148 |
+
video_width/video_height/video_min_dimension/video_max_dimension (int): together decide
|
| 149 |
+
the size of decoded frames:
|
| 150 |
+
|
| 151 |
+
- When video_width = 0, video_height = 0, video_min_dimension = 0,
|
| 152 |
+
and video_max_dimension = 0, keep the original frame resolution
|
| 153 |
+
- When video_width = 0, video_height = 0, video_min_dimension != 0,
|
| 154 |
+
and video_max_dimension = 0, keep the aspect ratio and resize the
|
| 155 |
+
frame so that shorter edge size is video_min_dimension
|
| 156 |
+
- When video_width = 0, video_height = 0, video_min_dimension = 0,
|
| 157 |
+
and video_max_dimension != 0, keep the aspect ratio and resize
|
| 158 |
+
the frame so that longer edge size is video_max_dimension
|
| 159 |
+
- When video_width = 0, video_height = 0, video_min_dimension != 0,
|
| 160 |
+
and video_max_dimension != 0, resize the frame so that shorter
|
| 161 |
+
edge size is video_min_dimension, and longer edge size is
|
| 162 |
+
video_max_dimension. The aspect ratio may not be preserved
|
| 163 |
+
- When video_width = 0, video_height != 0, video_min_dimension = 0,
|
| 164 |
+
and video_max_dimension = 0, keep the aspect ratio and resize
|
| 165 |
+
the frame so that frame video_height is $video_height
|
| 166 |
+
- When video_width != 0, video_height == 0, video_min_dimension = 0,
|
| 167 |
+
and video_max_dimension = 0, keep the aspect ratio and resize
|
| 168 |
+
the frame so that frame video_width is $video_width
|
| 169 |
+
- When video_width != 0, video_height != 0, video_min_dimension = 0,
|
| 170 |
+
and video_max_dimension = 0, resize the frame so that frame
|
| 171 |
+
video_width and video_height are set to $video_width and
|
| 172 |
+
$video_height, respectively
|
| 173 |
+
video_pts_range (list(int), optional): the start and end presentation timestamp of video stream
|
| 174 |
+
video_timebase (Fraction, optional): a Fraction rational number which denotes timebase in video stream
|
| 175 |
+
read_audio_stream (int, optional): whether read audio stream. If yes, set to 1. Otherwise, 0
|
| 176 |
+
audio_samples (int, optional): audio sampling rate
|
| 177 |
+
audio_channels (int optional): audio channels
|
| 178 |
+
audio_pts_range (list(int), optional): the start and end presentation timestamp of audio stream
|
| 179 |
+
audio_timebase (Fraction, optional): a Fraction rational number which denotes time base in audio stream
|
| 180 |
+
|
| 181 |
+
Returns
|
| 182 |
+
vframes (Tensor[T, H, W, C]): the `T` video frames
|
| 183 |
+
aframes (Tensor[L, K]): the audio frames, where `L` is the number of points and
|
| 184 |
+
`K` is the number of audio_channels
|
| 185 |
+
info (Dict): metadata for the video and audio. Can contain the fields video_fps (float)
|
| 186 |
+
and audio_fps (int)
|
| 187 |
+
"""
|
| 188 |
+
_validate_pts(video_pts_range)
|
| 189 |
+
_validate_pts(audio_pts_range)
|
| 190 |
+
|
| 191 |
+
result = torch.ops.video_reader.read_video_from_file(
|
| 192 |
+
filename,
|
| 193 |
+
seek_frame_margin,
|
| 194 |
+
0, # getPtsOnly
|
| 195 |
+
read_video_stream,
|
| 196 |
+
video_width,
|
| 197 |
+
video_height,
|
| 198 |
+
video_min_dimension,
|
| 199 |
+
video_max_dimension,
|
| 200 |
+
video_pts_range[0],
|
| 201 |
+
video_pts_range[1],
|
| 202 |
+
video_timebase.numerator,
|
| 203 |
+
video_timebase.denominator,
|
| 204 |
+
read_audio_stream,
|
| 205 |
+
audio_samples,
|
| 206 |
+
audio_channels,
|
| 207 |
+
audio_pts_range[0],
|
| 208 |
+
audio_pts_range[1],
|
| 209 |
+
audio_timebase.numerator,
|
| 210 |
+
audio_timebase.denominator,
|
| 211 |
+
)
|
| 212 |
+
vframes, _vframe_pts, vtimebase, vfps, vduration, aframes, aframe_pts, atimebase, asample_rate, aduration = result
|
| 213 |
+
info = _fill_info(vtimebase, vfps, vduration, atimebase, asample_rate, aduration)
|
| 214 |
+
if aframes.numel() > 0:
|
| 215 |
+
# when audio stream is found
|
| 216 |
+
aframes = _align_audio_frames(aframes, aframe_pts, audio_pts_range)
|
| 217 |
+
return vframes, aframes, info
|
| 218 |
+
|
| 219 |
+
|
| 220 |
+
def _read_video_timestamps_from_file(filename: str) -> Tuple[List[int], List[int], VideoMetaData]:
|
| 221 |
+
"""
|
| 222 |
+
Decode all video- and audio frames in the video. Only pts
|
| 223 |
+
(presentation timestamp) is returned. The actual frame pixel data is not
|
| 224 |
+
copied. Thus, it is much faster than read_video(...)
|
| 225 |
+
"""
|
| 226 |
+
result = torch.ops.video_reader.read_video_from_file(
|
| 227 |
+
filename,
|
| 228 |
+
0, # seek_frame_margin
|
| 229 |
+
1, # getPtsOnly
|
| 230 |
+
1, # read_video_stream
|
| 231 |
+
0, # video_width
|
| 232 |
+
0, # video_height
|
| 233 |
+
0, # video_min_dimension
|
| 234 |
+
0, # video_max_dimension
|
| 235 |
+
0, # video_start_pts
|
| 236 |
+
-1, # video_end_pts
|
| 237 |
+
0, # video_timebase_num
|
| 238 |
+
1, # video_timebase_den
|
| 239 |
+
1, # read_audio_stream
|
| 240 |
+
0, # audio_samples
|
| 241 |
+
0, # audio_channels
|
| 242 |
+
0, # audio_start_pts
|
| 243 |
+
-1, # audio_end_pts
|
| 244 |
+
0, # audio_timebase_num
|
| 245 |
+
1, # audio_timebase_den
|
| 246 |
+
)
|
| 247 |
+
_vframes, vframe_pts, vtimebase, vfps, vduration, _aframes, aframe_pts, atimebase, asample_rate, aduration = result
|
| 248 |
+
info = _fill_info(vtimebase, vfps, vduration, atimebase, asample_rate, aduration)
|
| 249 |
+
|
| 250 |
+
vframe_pts = vframe_pts.numpy().tolist()
|
| 251 |
+
aframe_pts = aframe_pts.numpy().tolist()
|
| 252 |
+
return vframe_pts, aframe_pts, info
|
| 253 |
+
|
| 254 |
+
|
| 255 |
+
def _probe_video_from_file(filename: str) -> VideoMetaData:
|
| 256 |
+
"""
|
| 257 |
+
Probe a video file and return VideoMetaData with info about the video
|
| 258 |
+
"""
|
| 259 |
+
result = torch.ops.video_reader.probe_video_from_file(filename)
|
| 260 |
+
vtimebase, vfps, vduration, atimebase, asample_rate, aduration = result
|
| 261 |
+
info = _fill_info(vtimebase, vfps, vduration, atimebase, asample_rate, aduration)
|
| 262 |
+
return info
|
| 263 |
+
|
| 264 |
+
|
| 265 |
+
def _read_video_from_memory(
|
| 266 |
+
video_data: torch.Tensor,
|
| 267 |
+
seek_frame_margin: float = 0.25,
|
| 268 |
+
read_video_stream: int = 1,
|
| 269 |
+
video_width: int = 0,
|
| 270 |
+
video_height: int = 0,
|
| 271 |
+
video_min_dimension: int = 0,
|
| 272 |
+
video_max_dimension: int = 0,
|
| 273 |
+
video_pts_range: Tuple[int, int] = (0, -1),
|
| 274 |
+
video_timebase_numerator: int = 0,
|
| 275 |
+
video_timebase_denominator: int = 1,
|
| 276 |
+
read_audio_stream: int = 1,
|
| 277 |
+
audio_samples: int = 0,
|
| 278 |
+
audio_channels: int = 0,
|
| 279 |
+
audio_pts_range: Tuple[int, int] = (0, -1),
|
| 280 |
+
audio_timebase_numerator: int = 0,
|
| 281 |
+
audio_timebase_denominator: int = 1,
|
| 282 |
+
) -> Tuple[torch.Tensor, torch.Tensor]:
|
| 283 |
+
"""
|
| 284 |
+
Reads a video from memory, returning both the video frames as well as
|
| 285 |
+
the audio frames
|
| 286 |
+
This function is torchscriptable.
|
| 287 |
+
|
| 288 |
+
Args:
|
| 289 |
+
video_data (data type could be 1) torch.Tensor, dtype=torch.int8 or 2) python bytes):
|
| 290 |
+
compressed video content stored in either 1) torch.Tensor 2) python bytes
|
| 291 |
+
seek_frame_margin (double, optional): seeking frame in the stream is imprecise.
|
| 292 |
+
Thus, when video_start_pts is specified, we seek the pts earlier by seek_frame_margin seconds
|
| 293 |
+
read_video_stream (int, optional): whether read video stream. If yes, set to 1. Otherwise, 0
|
| 294 |
+
video_width/video_height/video_min_dimension/video_max_dimension (int): together decide
|
| 295 |
+
the size of decoded frames:
|
| 296 |
+
|
| 297 |
+
- When video_width = 0, video_height = 0, video_min_dimension = 0,
|
| 298 |
+
and video_max_dimension = 0, keep the original frame resolution
|
| 299 |
+
- When video_width = 0, video_height = 0, video_min_dimension != 0,
|
| 300 |
+
and video_max_dimension = 0, keep the aspect ratio and resize the
|
| 301 |
+
frame so that shorter edge size is video_min_dimension
|
| 302 |
+
- When video_width = 0, video_height = 0, video_min_dimension = 0,
|
| 303 |
+
and video_max_dimension != 0, keep the aspect ratio and resize
|
| 304 |
+
the frame so that longer edge size is video_max_dimension
|
| 305 |
+
- When video_width = 0, video_height = 0, video_min_dimension != 0,
|
| 306 |
+
and video_max_dimension != 0, resize the frame so that shorter
|
| 307 |
+
edge size is video_min_dimension, and longer edge size is
|
| 308 |
+
video_max_dimension. The aspect ratio may not be preserved
|
| 309 |
+
- When video_width = 0, video_height != 0, video_min_dimension = 0,
|
| 310 |
+
and video_max_dimension = 0, keep the aspect ratio and resize
|
| 311 |
+
the frame so that frame video_height is $video_height
|
| 312 |
+
- When video_width != 0, video_height == 0, video_min_dimension = 0,
|
| 313 |
+
and video_max_dimension = 0, keep the aspect ratio and resize
|
| 314 |
+
the frame so that frame video_width is $video_width
|
| 315 |
+
- When video_width != 0, video_height != 0, video_min_dimension = 0,
|
| 316 |
+
and video_max_dimension = 0, resize the frame so that frame
|
| 317 |
+
video_width and video_height are set to $video_width and
|
| 318 |
+
$video_height, respectively
|
| 319 |
+
video_pts_range (list(int), optional): the start and end presentation timestamp of video stream
|
| 320 |
+
video_timebase_numerator / video_timebase_denominator (float, optional): a rational
|
| 321 |
+
number which denotes timebase in video stream
|
| 322 |
+
read_audio_stream (int, optional): whether read audio stream. If yes, set to 1. Otherwise, 0
|
| 323 |
+
audio_samples (int, optional): audio sampling rate
|
| 324 |
+
audio_channels (int optional): audio audio_channels
|
| 325 |
+
audio_pts_range (list(int), optional): the start and end presentation timestamp of audio stream
|
| 326 |
+
audio_timebase_numerator / audio_timebase_denominator (float, optional):
|
| 327 |
+
a rational number which denotes time base in audio stream
|
| 328 |
+
|
| 329 |
+
Returns:
|
| 330 |
+
vframes (Tensor[T, H, W, C]): the `T` video frames
|
| 331 |
+
aframes (Tensor[L, K]): the audio frames, where `L` is the number of points and
|
| 332 |
+
`K` is the number of channels
|
| 333 |
+
"""
|
| 334 |
+
|
| 335 |
+
_validate_pts(video_pts_range)
|
| 336 |
+
_validate_pts(audio_pts_range)
|
| 337 |
+
|
| 338 |
+
if not isinstance(video_data, torch.Tensor):
|
| 339 |
+
video_data = torch.frombuffer(video_data, dtype=torch.uint8)
|
| 340 |
+
|
| 341 |
+
result = torch.ops.video_reader.read_video_from_memory(
|
| 342 |
+
video_data,
|
| 343 |
+
seek_frame_margin,
|
| 344 |
+
0, # getPtsOnly
|
| 345 |
+
read_video_stream,
|
| 346 |
+
video_width,
|
| 347 |
+
video_height,
|
| 348 |
+
video_min_dimension,
|
| 349 |
+
video_max_dimension,
|
| 350 |
+
video_pts_range[0],
|
| 351 |
+
video_pts_range[1],
|
| 352 |
+
video_timebase_numerator,
|
| 353 |
+
video_timebase_denominator,
|
| 354 |
+
read_audio_stream,
|
| 355 |
+
audio_samples,
|
| 356 |
+
audio_channels,
|
| 357 |
+
audio_pts_range[0],
|
| 358 |
+
audio_pts_range[1],
|
| 359 |
+
audio_timebase_numerator,
|
| 360 |
+
audio_timebase_denominator,
|
| 361 |
+
)
|
| 362 |
+
|
| 363 |
+
vframes, _vframe_pts, vtimebase, vfps, vduration, aframes, aframe_pts, atimebase, asample_rate, aduration = result
|
| 364 |
+
|
| 365 |
+
if aframes.numel() > 0:
|
| 366 |
+
# when audio stream is found
|
| 367 |
+
aframes = _align_audio_frames(aframes, aframe_pts, audio_pts_range)
|
| 368 |
+
|
| 369 |
+
return vframes, aframes
|
| 370 |
+
|
| 371 |
+
|
| 372 |
+
def _read_video_timestamps_from_memory(
|
| 373 |
+
video_data: torch.Tensor,
|
| 374 |
+
) -> Tuple[List[int], List[int], VideoMetaData]:
|
| 375 |
+
"""
|
| 376 |
+
Decode all frames in the video. Only pts (presentation timestamp) is returned.
|
| 377 |
+
The actual frame pixel data is not copied. Thus, read_video_timestamps(...)
|
| 378 |
+
is much faster than read_video(...)
|
| 379 |
+
"""
|
| 380 |
+
if not isinstance(video_data, torch.Tensor):
|
| 381 |
+
video_data = torch.frombuffer(video_data, dtype=torch.uint8)
|
| 382 |
+
result = torch.ops.video_reader.read_video_from_memory(
|
| 383 |
+
video_data,
|
| 384 |
+
0, # seek_frame_margin
|
| 385 |
+
1, # getPtsOnly
|
| 386 |
+
1, # read_video_stream
|
| 387 |
+
0, # video_width
|
| 388 |
+
0, # video_height
|
| 389 |
+
0, # video_min_dimension
|
| 390 |
+
0, # video_max_dimension
|
| 391 |
+
0, # video_start_pts
|
| 392 |
+
-1, # video_end_pts
|
| 393 |
+
0, # video_timebase_num
|
| 394 |
+
1, # video_timebase_den
|
| 395 |
+
1, # read_audio_stream
|
| 396 |
+
0, # audio_samples
|
| 397 |
+
0, # audio_channels
|
| 398 |
+
0, # audio_start_pts
|
| 399 |
+
-1, # audio_end_pts
|
| 400 |
+
0, # audio_timebase_num
|
| 401 |
+
1, # audio_timebase_den
|
| 402 |
+
)
|
| 403 |
+
_vframes, vframe_pts, vtimebase, vfps, vduration, _aframes, aframe_pts, atimebase, asample_rate, aduration = result
|
| 404 |
+
info = _fill_info(vtimebase, vfps, vduration, atimebase, asample_rate, aduration)
|
| 405 |
+
|
| 406 |
+
vframe_pts = vframe_pts.numpy().tolist()
|
| 407 |
+
aframe_pts = aframe_pts.numpy().tolist()
|
| 408 |
+
return vframe_pts, aframe_pts, info
|
| 409 |
+
|
| 410 |
+
|
| 411 |
+
def _probe_video_from_memory(
|
| 412 |
+
video_data: torch.Tensor,
|
| 413 |
+
) -> VideoMetaData:
|
| 414 |
+
"""
|
| 415 |
+
Probe a video in memory and return VideoMetaData with info about the video
|
| 416 |
+
This function is torchscriptable
|
| 417 |
+
"""
|
| 418 |
+
if not isinstance(video_data, torch.Tensor):
|
| 419 |
+
video_data = torch.frombuffer(video_data, dtype=torch.uint8)
|
| 420 |
+
result = torch.ops.video_reader.probe_video_from_memory(video_data)
|
| 421 |
+
vtimebase, vfps, vduration, atimebase, asample_rate, aduration = result
|
| 422 |
+
info = _fill_info(vtimebase, vfps, vduration, atimebase, asample_rate, aduration)
|
| 423 |
+
return info
|
| 424 |
+
|
| 425 |
+
|
| 426 |
+
def _read_video(
|
| 427 |
+
filename: str,
|
| 428 |
+
start_pts: Union[float, Fraction] = 0,
|
| 429 |
+
end_pts: Optional[Union[float, Fraction]] = None,
|
| 430 |
+
pts_unit: str = "pts",
|
| 431 |
+
) -> Tuple[torch.Tensor, torch.Tensor, Dict[str, float]]:
|
| 432 |
+
if end_pts is None:
|
| 433 |
+
end_pts = float("inf")
|
| 434 |
+
|
| 435 |
+
if pts_unit == "pts":
|
| 436 |
+
warnings.warn(
|
| 437 |
+
"The pts_unit 'pts' gives wrong results and will be removed in a "
|
| 438 |
+
+ "follow-up version. Please use pts_unit 'sec'."
|
| 439 |
+
)
|
| 440 |
+
|
| 441 |
+
info = _probe_video_from_file(filename)
|
| 442 |
+
|
| 443 |
+
has_video = info.has_video
|
| 444 |
+
has_audio = info.has_audio
|
| 445 |
+
|
| 446 |
+
def get_pts(time_base):
|
| 447 |
+
start_offset = start_pts
|
| 448 |
+
end_offset = end_pts
|
| 449 |
+
if pts_unit == "sec":
|
| 450 |
+
start_offset = int(math.floor(start_pts * (1 / time_base)))
|
| 451 |
+
if end_offset != float("inf"):
|
| 452 |
+
end_offset = int(math.ceil(end_pts * (1 / time_base)))
|
| 453 |
+
if end_offset == float("inf"):
|
| 454 |
+
end_offset = -1
|
| 455 |
+
return start_offset, end_offset
|
| 456 |
+
|
| 457 |
+
video_pts_range = (0, -1)
|
| 458 |
+
video_timebase = default_timebase
|
| 459 |
+
if has_video:
|
| 460 |
+
video_timebase = Fraction(info.video_timebase.numerator, info.video_timebase.denominator)
|
| 461 |
+
video_pts_range = get_pts(video_timebase)
|
| 462 |
+
|
| 463 |
+
audio_pts_range = (0, -1)
|
| 464 |
+
audio_timebase = default_timebase
|
| 465 |
+
if has_audio:
|
| 466 |
+
audio_timebase = Fraction(info.audio_timebase.numerator, info.audio_timebase.denominator)
|
| 467 |
+
audio_pts_range = get_pts(audio_timebase)
|
| 468 |
+
|
| 469 |
+
vframes, aframes, info = _read_video_from_file(
|
| 470 |
+
filename,
|
| 471 |
+
read_video_stream=True,
|
| 472 |
+
video_pts_range=video_pts_range,
|
| 473 |
+
video_timebase=video_timebase,
|
| 474 |
+
read_audio_stream=True,
|
| 475 |
+
audio_pts_range=audio_pts_range,
|
| 476 |
+
audio_timebase=audio_timebase,
|
| 477 |
+
)
|
| 478 |
+
_info = {}
|
| 479 |
+
if has_video:
|
| 480 |
+
_info["video_fps"] = info.video_fps
|
| 481 |
+
if has_audio:
|
| 482 |
+
_info["audio_fps"] = info.audio_sample_rate
|
| 483 |
+
|
| 484 |
+
return vframes, aframes, _info
|
| 485 |
+
|
| 486 |
+
|
| 487 |
+
def _read_video_timestamps(
|
| 488 |
+
filename: str, pts_unit: str = "pts"
|
| 489 |
+
) -> Tuple[Union[List[int], List[Fraction]], Optional[float]]:
|
| 490 |
+
if pts_unit == "pts":
|
| 491 |
+
warnings.warn(
|
| 492 |
+
"The pts_unit 'pts' gives wrong results and will be removed in a "
|
| 493 |
+
+ "follow-up version. Please use pts_unit 'sec'."
|
| 494 |
+
)
|
| 495 |
+
|
| 496 |
+
pts: Union[List[int], List[Fraction]]
|
| 497 |
+
pts, _, info = _read_video_timestamps_from_file(filename)
|
| 498 |
+
|
| 499 |
+
if pts_unit == "sec":
|
| 500 |
+
video_time_base = Fraction(info.video_timebase.numerator, info.video_timebase.denominator)
|
| 501 |
+
pts = [x * video_time_base for x in pts]
|
| 502 |
+
|
| 503 |
+
video_fps = info.video_fps if info.has_video else None
|
| 504 |
+
|
| 505 |
+
return pts, video_fps
|
my_container_sandbox/workspace/anaconda3/lib/python3.8/site-packages/torchvision/io/image.py
ADDED
|
@@ -0,0 +1,257 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
from enum import Enum
|
| 2 |
+
from warnings import warn
|
| 3 |
+
|
| 4 |
+
import torch
|
| 5 |
+
|
| 6 |
+
from ..extension import _load_library
|
| 7 |
+
from ..utils import _log_api_usage_once
|
| 8 |
+
|
| 9 |
+
|
| 10 |
+
try:
|
| 11 |
+
_load_library("image")
|
| 12 |
+
except (ImportError, OSError) as e:
|
| 13 |
+
warn(f"Failed to load image Python extension: {e}")
|
| 14 |
+
|
| 15 |
+
|
| 16 |
+
class ImageReadMode(Enum):
|
| 17 |
+
"""
|
| 18 |
+
Support for various modes while reading images.
|
| 19 |
+
|
| 20 |
+
Use ``ImageReadMode.UNCHANGED`` for loading the image as-is,
|
| 21 |
+
``ImageReadMode.GRAY`` for converting to grayscale,
|
| 22 |
+
``ImageReadMode.GRAY_ALPHA`` for grayscale with transparency,
|
| 23 |
+
``ImageReadMode.RGB`` for RGB and ``ImageReadMode.RGB_ALPHA`` for
|
| 24 |
+
RGB with transparency.
|
| 25 |
+
"""
|
| 26 |
+
|
| 27 |
+
UNCHANGED = 0
|
| 28 |
+
GRAY = 1
|
| 29 |
+
GRAY_ALPHA = 2
|
| 30 |
+
RGB = 3
|
| 31 |
+
RGB_ALPHA = 4
|
| 32 |
+
|
| 33 |
+
|
| 34 |
+
def read_file(path: str) -> torch.Tensor:
|
| 35 |
+
"""
|
| 36 |
+
Reads and outputs the bytes contents of a file as a uint8 Tensor
|
| 37 |
+
with one dimension.
|
| 38 |
+
|
| 39 |
+
Args:
|
| 40 |
+
path (str): the path to the file to be read
|
| 41 |
+
|
| 42 |
+
Returns:
|
| 43 |
+
data (Tensor)
|
| 44 |
+
"""
|
| 45 |
+
if not torch.jit.is_scripting() and not torch.jit.is_tracing():
|
| 46 |
+
_log_api_usage_once(read_file)
|
| 47 |
+
data = torch.ops.image.read_file(path)
|
| 48 |
+
return data
|
| 49 |
+
|
| 50 |
+
|
| 51 |
+
def write_file(filename: str, data: torch.Tensor) -> None:
|
| 52 |
+
"""
|
| 53 |
+
Writes the contents of a uint8 tensor with one dimension to a
|
| 54 |
+
file.
|
| 55 |
+
|
| 56 |
+
Args:
|
| 57 |
+
filename (str): the path to the file to be written
|
| 58 |
+
data (Tensor): the contents to be written to the output file
|
| 59 |
+
"""
|
| 60 |
+
if not torch.jit.is_scripting() and not torch.jit.is_tracing():
|
| 61 |
+
_log_api_usage_once(write_file)
|
| 62 |
+
torch.ops.image.write_file(filename, data)
|
| 63 |
+
|
| 64 |
+
|
| 65 |
+
def decode_png(input: torch.Tensor, mode: ImageReadMode = ImageReadMode.UNCHANGED) -> torch.Tensor:
|
| 66 |
+
"""
|
| 67 |
+
Decodes a PNG image into a 3 dimensional RGB or grayscale Tensor.
|
| 68 |
+
Optionally converts the image to the desired format.
|
| 69 |
+
The values of the output tensor are uint8 in [0, 255].
|
| 70 |
+
|
| 71 |
+
Args:
|
| 72 |
+
input (Tensor[1]): a one dimensional uint8 tensor containing
|
| 73 |
+
the raw bytes of the PNG image.
|
| 74 |
+
mode (ImageReadMode): the read mode used for optionally
|
| 75 |
+
converting the image. Default: ``ImageReadMode.UNCHANGED``.
|
| 76 |
+
See `ImageReadMode` class for more information on various
|
| 77 |
+
available modes.
|
| 78 |
+
|
| 79 |
+
Returns:
|
| 80 |
+
output (Tensor[image_channels, image_height, image_width])
|
| 81 |
+
"""
|
| 82 |
+
if not torch.jit.is_scripting() and not torch.jit.is_tracing():
|
| 83 |
+
_log_api_usage_once(decode_png)
|
| 84 |
+
output = torch.ops.image.decode_png(input, mode.value, False)
|
| 85 |
+
return output
|
| 86 |
+
|
| 87 |
+
|
| 88 |
+
def encode_png(input: torch.Tensor, compression_level: int = 6) -> torch.Tensor:
|
| 89 |
+
"""
|
| 90 |
+
Takes an input tensor in CHW layout and returns a buffer with the contents
|
| 91 |
+
of its corresponding PNG file.
|
| 92 |
+
|
| 93 |
+
Args:
|
| 94 |
+
input (Tensor[channels, image_height, image_width]): int8 image tensor of
|
| 95 |
+
``c`` channels, where ``c`` must 3 or 1.
|
| 96 |
+
compression_level (int): Compression factor for the resulting file, it must be a number
|
| 97 |
+
between 0 and 9. Default: 6
|
| 98 |
+
|
| 99 |
+
Returns:
|
| 100 |
+
Tensor[1]: A one dimensional int8 tensor that contains the raw bytes of the
|
| 101 |
+
PNG file.
|
| 102 |
+
"""
|
| 103 |
+
if not torch.jit.is_scripting() and not torch.jit.is_tracing():
|
| 104 |
+
_log_api_usage_once(encode_png)
|
| 105 |
+
output = torch.ops.image.encode_png(input, compression_level)
|
| 106 |
+
return output
|
| 107 |
+
|
| 108 |
+
|
| 109 |
+
def write_png(input: torch.Tensor, filename: str, compression_level: int = 6):
|
| 110 |
+
"""
|
| 111 |
+
Takes an input tensor in CHW layout (or HW in the case of grayscale images)
|
| 112 |
+
and saves it in a PNG file.
|
| 113 |
+
|
| 114 |
+
Args:
|
| 115 |
+
input (Tensor[channels, image_height, image_width]): int8 image tensor of
|
| 116 |
+
``c`` channels, where ``c`` must be 1 or 3.
|
| 117 |
+
filename (str): Path to save the image.
|
| 118 |
+
compression_level (int): Compression factor for the resulting file, it must be a number
|
| 119 |
+
between 0 and 9. Default: 6
|
| 120 |
+
"""
|
| 121 |
+
if not torch.jit.is_scripting() and not torch.jit.is_tracing():
|
| 122 |
+
_log_api_usage_once(write_png)
|
| 123 |
+
output = encode_png(input, compression_level)
|
| 124 |
+
write_file(filename, output)
|
| 125 |
+
|
| 126 |
+
|
| 127 |
+
def decode_jpeg(
|
| 128 |
+
input: torch.Tensor, mode: ImageReadMode = ImageReadMode.UNCHANGED, device: str = "cpu"
|
| 129 |
+
) -> torch.Tensor:
|
| 130 |
+
"""
|
| 131 |
+
Decodes a JPEG image into a 3 dimensional RGB or grayscale Tensor.
|
| 132 |
+
Optionally converts the image to the desired format.
|
| 133 |
+
The values of the output tensor are uint8 between 0 and 255.
|
| 134 |
+
|
| 135 |
+
Args:
|
| 136 |
+
input (Tensor[1]): a one dimensional uint8 tensor containing
|
| 137 |
+
the raw bytes of the JPEG image. This tensor must be on CPU,
|
| 138 |
+
regardless of the ``device`` parameter.
|
| 139 |
+
mode (ImageReadMode): the read mode used for optionally
|
| 140 |
+
converting the image. Default: ``ImageReadMode.UNCHANGED``.
|
| 141 |
+
See ``ImageReadMode`` class for more information on various
|
| 142 |
+
available modes.
|
| 143 |
+
device (str or torch.device): The device on which the decoded image will
|
| 144 |
+
be stored. If a cuda device is specified, the image will be decoded
|
| 145 |
+
with `nvjpeg <https://developer.nvidia.com/nvjpeg>`_. This is only
|
| 146 |
+
supported for CUDA version >= 10.1
|
| 147 |
+
|
| 148 |
+
.. betastatus:: device parameter
|
| 149 |
+
|
| 150 |
+
.. warning::
|
| 151 |
+
There is a memory leak in the nvjpeg library for CUDA versions < 11.6.
|
| 152 |
+
Make sure to rely on CUDA 11.6 or above before using ``device="cuda"``.
|
| 153 |
+
|
| 154 |
+
Returns:
|
| 155 |
+
output (Tensor[image_channels, image_height, image_width])
|
| 156 |
+
"""
|
| 157 |
+
if not torch.jit.is_scripting() and not torch.jit.is_tracing():
|
| 158 |
+
_log_api_usage_once(decode_jpeg)
|
| 159 |
+
device = torch.device(device)
|
| 160 |
+
if device.type == "cuda":
|
| 161 |
+
output = torch.ops.image.decode_jpeg_cuda(input, mode.value, device)
|
| 162 |
+
else:
|
| 163 |
+
output = torch.ops.image.decode_jpeg(input, mode.value)
|
| 164 |
+
return output
|
| 165 |
+
|
| 166 |
+
|
| 167 |
+
def encode_jpeg(input: torch.Tensor, quality: int = 75) -> torch.Tensor:
|
| 168 |
+
"""
|
| 169 |
+
Takes an input tensor in CHW layout and returns a buffer with the contents
|
| 170 |
+
of its corresponding JPEG file.
|
| 171 |
+
|
| 172 |
+
Args:
|
| 173 |
+
input (Tensor[channels, image_height, image_width])): int8 image tensor of
|
| 174 |
+
``c`` channels, where ``c`` must be 1 or 3.
|
| 175 |
+
quality (int): Quality of the resulting JPEG file, it must be a number between
|
| 176 |
+
1 and 100. Default: 75
|
| 177 |
+
|
| 178 |
+
Returns:
|
| 179 |
+
output (Tensor[1]): A one dimensional int8 tensor that contains the raw bytes of the
|
| 180 |
+
JPEG file.
|
| 181 |
+
"""
|
| 182 |
+
if not torch.jit.is_scripting() and not torch.jit.is_tracing():
|
| 183 |
+
_log_api_usage_once(encode_jpeg)
|
| 184 |
+
if quality < 1 or quality > 100:
|
| 185 |
+
raise ValueError("Image quality should be a positive number between 1 and 100")
|
| 186 |
+
|
| 187 |
+
output = torch.ops.image.encode_jpeg(input, quality)
|
| 188 |
+
return output
|
| 189 |
+
|
| 190 |
+
|
| 191 |
+
def write_jpeg(input: torch.Tensor, filename: str, quality: int = 75):
|
| 192 |
+
"""
|
| 193 |
+
Takes an input tensor in CHW layout and saves it in a JPEG file.
|
| 194 |
+
|
| 195 |
+
Args:
|
| 196 |
+
input (Tensor[channels, image_height, image_width]): int8 image tensor of ``c``
|
| 197 |
+
channels, where ``c`` must be 1 or 3.
|
| 198 |
+
filename (str): Path to save the image.
|
| 199 |
+
quality (int): Quality of the resulting JPEG file, it must be a number
|
| 200 |
+
between 1 and 100. Default: 75
|
| 201 |
+
"""
|
| 202 |
+
if not torch.jit.is_scripting() and not torch.jit.is_tracing():
|
| 203 |
+
_log_api_usage_once(write_jpeg)
|
| 204 |
+
output = encode_jpeg(input, quality)
|
| 205 |
+
write_file(filename, output)
|
| 206 |
+
|
| 207 |
+
|
| 208 |
+
def decode_image(input: torch.Tensor, mode: ImageReadMode = ImageReadMode.UNCHANGED) -> torch.Tensor:
|
| 209 |
+
"""
|
| 210 |
+
Detects whether an image is a JPEG or PNG and performs the appropriate
|
| 211 |
+
operation to decode the image into a 3 dimensional RGB or grayscale Tensor.
|
| 212 |
+
|
| 213 |
+
Optionally converts the image to the desired format.
|
| 214 |
+
The values of the output tensor are uint8 in [0, 255].
|
| 215 |
+
|
| 216 |
+
Args:
|
| 217 |
+
input (Tensor): a one dimensional uint8 tensor containing the raw bytes of the
|
| 218 |
+
PNG or JPEG image.
|
| 219 |
+
mode (ImageReadMode): the read mode used for optionally converting the image.
|
| 220 |
+
Default: ``ImageReadMode.UNCHANGED``.
|
| 221 |
+
See ``ImageReadMode`` class for more information on various
|
| 222 |
+
available modes.
|
| 223 |
+
|
| 224 |
+
Returns:
|
| 225 |
+
output (Tensor[image_channels, image_height, image_width])
|
| 226 |
+
"""
|
| 227 |
+
if not torch.jit.is_scripting() and not torch.jit.is_tracing():
|
| 228 |
+
_log_api_usage_once(decode_image)
|
| 229 |
+
output = torch.ops.image.decode_image(input, mode.value)
|
| 230 |
+
return output
|
| 231 |
+
|
| 232 |
+
|
| 233 |
+
def read_image(path: str, mode: ImageReadMode = ImageReadMode.UNCHANGED) -> torch.Tensor:
|
| 234 |
+
"""
|
| 235 |
+
Reads a JPEG or PNG image into a 3 dimensional RGB or grayscale Tensor.
|
| 236 |
+
Optionally converts the image to the desired format.
|
| 237 |
+
The values of the output tensor are uint8 in [0, 255].
|
| 238 |
+
|
| 239 |
+
Args:
|
| 240 |
+
path (str): path of the JPEG or PNG image.
|
| 241 |
+
mode (ImageReadMode): the read mode used for optionally converting the image.
|
| 242 |
+
Default: ``ImageReadMode.UNCHANGED``.
|
| 243 |
+
See ``ImageReadMode`` class for more information on various
|
| 244 |
+
available modes.
|
| 245 |
+
|
| 246 |
+
Returns:
|
| 247 |
+
output (Tensor[image_channels, image_height, image_width])
|
| 248 |
+
"""
|
| 249 |
+
if not torch.jit.is_scripting() and not torch.jit.is_tracing():
|
| 250 |
+
_log_api_usage_once(read_image)
|
| 251 |
+
data = read_file(path)
|
| 252 |
+
return decode_image(data, mode)
|
| 253 |
+
|
| 254 |
+
|
| 255 |
+
def _read_png_16(path: str, mode: ImageReadMode = ImageReadMode.UNCHANGED) -> torch.Tensor:
|
| 256 |
+
data = read_file(path)
|
| 257 |
+
return torch.ops.image.decode_png(data, mode.value, True)
|
my_container_sandbox/workspace/anaconda3/lib/python3.8/site-packages/torchvision/io/video.py
ADDED
|
@@ -0,0 +1,415 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
import gc
|
| 2 |
+
import math
|
| 3 |
+
import os
|
| 4 |
+
import re
|
| 5 |
+
import warnings
|
| 6 |
+
from fractions import Fraction
|
| 7 |
+
from typing import Any, Dict, List, Optional, Tuple, Union
|
| 8 |
+
|
| 9 |
+
import numpy as np
|
| 10 |
+
import torch
|
| 11 |
+
|
| 12 |
+
from ..utils import _log_api_usage_once
|
| 13 |
+
from . import _video_opt
|
| 14 |
+
|
| 15 |
+
|
| 16 |
+
try:
|
| 17 |
+
import av
|
| 18 |
+
|
| 19 |
+
av.logging.set_level(av.logging.ERROR)
|
| 20 |
+
if not hasattr(av.video.frame.VideoFrame, "pict_type"):
|
| 21 |
+
av = ImportError(
|
| 22 |
+
"""\
|
| 23 |
+
Your version of PyAV is too old for the necessary video operations in torchvision.
|
| 24 |
+
If you are on Python 3.5, you will have to build from source (the conda-forge
|
| 25 |
+
packages are not up-to-date). See
|
| 26 |
+
https://github.com/mikeboers/PyAV#installation for instructions on how to
|
| 27 |
+
install PyAV on your system.
|
| 28 |
+
"""
|
| 29 |
+
)
|
| 30 |
+
except ImportError:
|
| 31 |
+
av = ImportError(
|
| 32 |
+
"""\
|
| 33 |
+
PyAV is not installed, and is necessary for the video operations in torchvision.
|
| 34 |
+
See https://github.com/mikeboers/PyAV#installation for instructions on how to
|
| 35 |
+
install PyAV on your system.
|
| 36 |
+
"""
|
| 37 |
+
)
|
| 38 |
+
|
| 39 |
+
|
| 40 |
+
def _check_av_available() -> None:
|
| 41 |
+
if isinstance(av, Exception):
|
| 42 |
+
raise av
|
| 43 |
+
|
| 44 |
+
|
| 45 |
+
def _av_available() -> bool:
|
| 46 |
+
return not isinstance(av, Exception)
|
| 47 |
+
|
| 48 |
+
|
| 49 |
+
# PyAV has some reference cycles
|
| 50 |
+
_CALLED_TIMES = 0
|
| 51 |
+
_GC_COLLECTION_INTERVAL = 10
|
| 52 |
+
|
| 53 |
+
|
| 54 |
+
def write_video(
|
| 55 |
+
filename: str,
|
| 56 |
+
video_array: torch.Tensor,
|
| 57 |
+
fps: float,
|
| 58 |
+
video_codec: str = "libx264",
|
| 59 |
+
options: Optional[Dict[str, Any]] = None,
|
| 60 |
+
audio_array: Optional[torch.Tensor] = None,
|
| 61 |
+
audio_fps: Optional[float] = None,
|
| 62 |
+
audio_codec: Optional[str] = None,
|
| 63 |
+
audio_options: Optional[Dict[str, Any]] = None,
|
| 64 |
+
) -> None:
|
| 65 |
+
"""
|
| 66 |
+
Writes a 4d tensor in [T, H, W, C] format in a video file
|
| 67 |
+
|
| 68 |
+
Args:
|
| 69 |
+
filename (str): path where the video will be saved
|
| 70 |
+
video_array (Tensor[T, H, W, C]): tensor containing the individual frames,
|
| 71 |
+
as a uint8 tensor in [T, H, W, C] format
|
| 72 |
+
fps (Number): video frames per second
|
| 73 |
+
video_codec (str): the name of the video codec, i.e. "libx264", "h264", etc.
|
| 74 |
+
options (Dict): dictionary containing options to be passed into the PyAV video stream
|
| 75 |
+
audio_array (Tensor[C, N]): tensor containing the audio, where C is the number of channels
|
| 76 |
+
and N is the number of samples
|
| 77 |
+
audio_fps (Number): audio sample rate, typically 44100 or 48000
|
| 78 |
+
audio_codec (str): the name of the audio codec, i.e. "mp3", "aac", etc.
|
| 79 |
+
audio_options (Dict): dictionary containing options to be passed into the PyAV audio stream
|
| 80 |
+
"""
|
| 81 |
+
if not torch.jit.is_scripting() and not torch.jit.is_tracing():
|
| 82 |
+
_log_api_usage_once(write_video)
|
| 83 |
+
_check_av_available()
|
| 84 |
+
video_array = torch.as_tensor(video_array, dtype=torch.uint8).numpy()
|
| 85 |
+
|
| 86 |
+
# PyAV does not support floating point numbers with decimal point
|
| 87 |
+
# and will throw OverflowException in case this is not the case
|
| 88 |
+
if isinstance(fps, float):
|
| 89 |
+
fps = np.round(fps)
|
| 90 |
+
|
| 91 |
+
with av.open(filename, mode="w") as container:
|
| 92 |
+
stream = container.add_stream(video_codec, rate=fps)
|
| 93 |
+
stream.width = video_array.shape[2]
|
| 94 |
+
stream.height = video_array.shape[1]
|
| 95 |
+
stream.pix_fmt = "yuv420p" if video_codec != "libx264rgb" else "rgb24"
|
| 96 |
+
stream.options = options or {}
|
| 97 |
+
|
| 98 |
+
if audio_array is not None:
|
| 99 |
+
audio_format_dtypes = {
|
| 100 |
+
"dbl": "<f8",
|
| 101 |
+
"dblp": "<f8",
|
| 102 |
+
"flt": "<f4",
|
| 103 |
+
"fltp": "<f4",
|
| 104 |
+
"s16": "<i2",
|
| 105 |
+
"s16p": "<i2",
|
| 106 |
+
"s32": "<i4",
|
| 107 |
+
"s32p": "<i4",
|
| 108 |
+
"u8": "u1",
|
| 109 |
+
"u8p": "u1",
|
| 110 |
+
}
|
| 111 |
+
a_stream = container.add_stream(audio_codec, rate=audio_fps)
|
| 112 |
+
a_stream.options = audio_options or {}
|
| 113 |
+
|
| 114 |
+
num_channels = audio_array.shape[0]
|
| 115 |
+
audio_layout = "stereo" if num_channels > 1 else "mono"
|
| 116 |
+
audio_sample_fmt = container.streams.audio[0].format.name
|
| 117 |
+
|
| 118 |
+
format_dtype = np.dtype(audio_format_dtypes[audio_sample_fmt])
|
| 119 |
+
audio_array = torch.as_tensor(audio_array).numpy().astype(format_dtype)
|
| 120 |
+
|
| 121 |
+
frame = av.AudioFrame.from_ndarray(audio_array, format=audio_sample_fmt, layout=audio_layout)
|
| 122 |
+
|
| 123 |
+
frame.sample_rate = audio_fps
|
| 124 |
+
|
| 125 |
+
for packet in a_stream.encode(frame):
|
| 126 |
+
container.mux(packet)
|
| 127 |
+
|
| 128 |
+
for packet in a_stream.encode():
|
| 129 |
+
container.mux(packet)
|
| 130 |
+
|
| 131 |
+
for img in video_array:
|
| 132 |
+
frame = av.VideoFrame.from_ndarray(img, format="rgb24")
|
| 133 |
+
frame.pict_type = "NONE"
|
| 134 |
+
for packet in stream.encode(frame):
|
| 135 |
+
container.mux(packet)
|
| 136 |
+
|
| 137 |
+
# Flush stream
|
| 138 |
+
for packet in stream.encode():
|
| 139 |
+
container.mux(packet)
|
| 140 |
+
|
| 141 |
+
|
| 142 |
+
def _read_from_stream(
|
| 143 |
+
container: "av.container.Container",
|
| 144 |
+
start_offset: float,
|
| 145 |
+
end_offset: float,
|
| 146 |
+
pts_unit: str,
|
| 147 |
+
stream: "av.stream.Stream",
|
| 148 |
+
stream_name: Dict[str, Optional[Union[int, Tuple[int, ...], List[int]]]],
|
| 149 |
+
) -> List["av.frame.Frame"]:
|
| 150 |
+
global _CALLED_TIMES, _GC_COLLECTION_INTERVAL
|
| 151 |
+
_CALLED_TIMES += 1
|
| 152 |
+
if _CALLED_TIMES % _GC_COLLECTION_INTERVAL == _GC_COLLECTION_INTERVAL - 1:
|
| 153 |
+
gc.collect()
|
| 154 |
+
|
| 155 |
+
if pts_unit == "sec":
|
| 156 |
+
# TODO: we should change all of this from ground up to simply take
|
| 157 |
+
# sec and convert to MS in C++
|
| 158 |
+
start_offset = int(math.floor(start_offset * (1 / stream.time_base)))
|
| 159 |
+
if end_offset != float("inf"):
|
| 160 |
+
end_offset = int(math.ceil(end_offset * (1 / stream.time_base)))
|
| 161 |
+
else:
|
| 162 |
+
warnings.warn("The pts_unit 'pts' gives wrong results. Please use pts_unit 'sec'.")
|
| 163 |
+
|
| 164 |
+
frames = {}
|
| 165 |
+
should_buffer = True
|
| 166 |
+
max_buffer_size = 5
|
| 167 |
+
if stream.type == "video":
|
| 168 |
+
# DivX-style packed B-frames can have out-of-order pts (2 frames in a single pkt)
|
| 169 |
+
# so need to buffer some extra frames to sort everything
|
| 170 |
+
# properly
|
| 171 |
+
extradata = stream.codec_context.extradata
|
| 172 |
+
# overly complicated way of finding if `divx_packed` is set, following
|
| 173 |
+
# https://github.com/FFmpeg/FFmpeg/commit/d5a21172283572af587b3d939eba0091484d3263
|
| 174 |
+
if extradata and b"DivX" in extradata:
|
| 175 |
+
# can't use regex directly because of some weird characters sometimes...
|
| 176 |
+
pos = extradata.find(b"DivX")
|
| 177 |
+
d = extradata[pos:]
|
| 178 |
+
o = re.search(rb"DivX(\d+)Build(\d+)(\w)", d)
|
| 179 |
+
if o is None:
|
| 180 |
+
o = re.search(rb"DivX(\d+)b(\d+)(\w)", d)
|
| 181 |
+
if o is not None:
|
| 182 |
+
should_buffer = o.group(3) == b"p"
|
| 183 |
+
seek_offset = start_offset
|
| 184 |
+
# some files don't seek to the right location, so better be safe here
|
| 185 |
+
seek_offset = max(seek_offset - 1, 0)
|
| 186 |
+
if should_buffer:
|
| 187 |
+
# FIXME this is kind of a hack, but we will jump to the previous keyframe
|
| 188 |
+
# so this will be safe
|
| 189 |
+
seek_offset = max(seek_offset - max_buffer_size, 0)
|
| 190 |
+
try:
|
| 191 |
+
# TODO check if stream needs to always be the video stream here or not
|
| 192 |
+
container.seek(seek_offset, any_frame=False, backward=True, stream=stream)
|
| 193 |
+
except av.AVError:
|
| 194 |
+
# TODO add some warnings in this case
|
| 195 |
+
# print("Corrupted file?", container.name)
|
| 196 |
+
return []
|
| 197 |
+
buffer_count = 0
|
| 198 |
+
try:
|
| 199 |
+
for _idx, frame in enumerate(container.decode(**stream_name)):
|
| 200 |
+
frames[frame.pts] = frame
|
| 201 |
+
if frame.pts >= end_offset:
|
| 202 |
+
if should_buffer and buffer_count < max_buffer_size:
|
| 203 |
+
buffer_count += 1
|
| 204 |
+
continue
|
| 205 |
+
break
|
| 206 |
+
except av.AVError:
|
| 207 |
+
# TODO add a warning
|
| 208 |
+
pass
|
| 209 |
+
# ensure that the results are sorted wrt the pts
|
| 210 |
+
result = [frames[i] for i in sorted(frames) if start_offset <= frames[i].pts <= end_offset]
|
| 211 |
+
if len(frames) > 0 and start_offset > 0 and start_offset not in frames:
|
| 212 |
+
# if there is no frame that exactly matches the pts of start_offset
|
| 213 |
+
# add the last frame smaller than start_offset, to guarantee that
|
| 214 |
+
# we will have all the necessary data. This is most useful for audio
|
| 215 |
+
preceding_frames = [i for i in frames if i < start_offset]
|
| 216 |
+
if len(preceding_frames) > 0:
|
| 217 |
+
first_frame_pts = max(preceding_frames)
|
| 218 |
+
result.insert(0, frames[first_frame_pts])
|
| 219 |
+
return result
|
| 220 |
+
|
| 221 |
+
|
| 222 |
+
def _align_audio_frames(
|
| 223 |
+
aframes: torch.Tensor, audio_frames: List["av.frame.Frame"], ref_start: int, ref_end: float
|
| 224 |
+
) -> torch.Tensor:
|
| 225 |
+
start, end = audio_frames[0].pts, audio_frames[-1].pts
|
| 226 |
+
total_aframes = aframes.shape[1]
|
| 227 |
+
step_per_aframe = (end - start + 1) / total_aframes
|
| 228 |
+
s_idx = 0
|
| 229 |
+
e_idx = total_aframes
|
| 230 |
+
if start < ref_start:
|
| 231 |
+
s_idx = int((ref_start - start) / step_per_aframe)
|
| 232 |
+
if end > ref_end:
|
| 233 |
+
e_idx = int((ref_end - end) / step_per_aframe)
|
| 234 |
+
return aframes[:, s_idx:e_idx]
|
| 235 |
+
|
| 236 |
+
|
| 237 |
+
def read_video(
|
| 238 |
+
filename: str,
|
| 239 |
+
start_pts: Union[float, Fraction] = 0,
|
| 240 |
+
end_pts: Optional[Union[float, Fraction]] = None,
|
| 241 |
+
pts_unit: str = "pts",
|
| 242 |
+
output_format: str = "THWC",
|
| 243 |
+
) -> Tuple[torch.Tensor, torch.Tensor, Dict[str, Any]]:
|
| 244 |
+
"""
|
| 245 |
+
Reads a video from a file, returning both the video frames as well as
|
| 246 |
+
the audio frames
|
| 247 |
+
|
| 248 |
+
Args:
|
| 249 |
+
filename (str): path to the video file
|
| 250 |
+
start_pts (int if pts_unit = 'pts', float / Fraction if pts_unit = 'sec', optional):
|
| 251 |
+
The start presentation time of the video
|
| 252 |
+
end_pts (int if pts_unit = 'pts', float / Fraction if pts_unit = 'sec', optional):
|
| 253 |
+
The end presentation time
|
| 254 |
+
pts_unit (str, optional): unit in which start_pts and end_pts values will be interpreted,
|
| 255 |
+
either 'pts' or 'sec'. Defaults to 'pts'.
|
| 256 |
+
output_format (str, optional): The format of the output video tensors. Can be either "THWC" (default) or "TCHW".
|
| 257 |
+
|
| 258 |
+
Returns:
|
| 259 |
+
vframes (Tensor[T, H, W, C] or Tensor[T, C, H, W]): the `T` video frames
|
| 260 |
+
aframes (Tensor[K, L]): the audio frames, where `K` is the number of channels and `L` is the number of points
|
| 261 |
+
info (Dict): metadata for the video and audio. Can contain the fields video_fps (float) and audio_fps (int)
|
| 262 |
+
"""
|
| 263 |
+
if not torch.jit.is_scripting() and not torch.jit.is_tracing():
|
| 264 |
+
_log_api_usage_once(read_video)
|
| 265 |
+
|
| 266 |
+
output_format = output_format.upper()
|
| 267 |
+
if output_format not in ("THWC", "TCHW"):
|
| 268 |
+
raise ValueError(f"output_format should be either 'THWC' or 'TCHW', got {output_format}.")
|
| 269 |
+
|
| 270 |
+
from torchvision import get_video_backend
|
| 271 |
+
|
| 272 |
+
if not os.path.exists(filename):
|
| 273 |
+
raise RuntimeError(f"File not found: {filename}")
|
| 274 |
+
|
| 275 |
+
if get_video_backend() != "pyav":
|
| 276 |
+
return _video_opt._read_video(filename, start_pts, end_pts, pts_unit)
|
| 277 |
+
|
| 278 |
+
_check_av_available()
|
| 279 |
+
|
| 280 |
+
if end_pts is None:
|
| 281 |
+
end_pts = float("inf")
|
| 282 |
+
|
| 283 |
+
if end_pts < start_pts:
|
| 284 |
+
raise ValueError(f"end_pts should be larger than start_pts, got start_pts={start_pts} and end_pts={end_pts}")
|
| 285 |
+
|
| 286 |
+
info = {}
|
| 287 |
+
video_frames = []
|
| 288 |
+
audio_frames = []
|
| 289 |
+
audio_timebase = _video_opt.default_timebase
|
| 290 |
+
|
| 291 |
+
try:
|
| 292 |
+
with av.open(filename, metadata_errors="ignore") as container:
|
| 293 |
+
if container.streams.audio:
|
| 294 |
+
audio_timebase = container.streams.audio[0].time_base
|
| 295 |
+
if container.streams.video:
|
| 296 |
+
video_frames = _read_from_stream(
|
| 297 |
+
container,
|
| 298 |
+
start_pts,
|
| 299 |
+
end_pts,
|
| 300 |
+
pts_unit,
|
| 301 |
+
container.streams.video[0],
|
| 302 |
+
{"video": 0},
|
| 303 |
+
)
|
| 304 |
+
video_fps = container.streams.video[0].average_rate
|
| 305 |
+
# guard against potentially corrupted files
|
| 306 |
+
if video_fps is not None:
|
| 307 |
+
info["video_fps"] = float(video_fps)
|
| 308 |
+
|
| 309 |
+
if container.streams.audio:
|
| 310 |
+
audio_frames = _read_from_stream(
|
| 311 |
+
container,
|
| 312 |
+
start_pts,
|
| 313 |
+
end_pts,
|
| 314 |
+
pts_unit,
|
| 315 |
+
container.streams.audio[0],
|
| 316 |
+
{"audio": 0},
|
| 317 |
+
)
|
| 318 |
+
info["audio_fps"] = container.streams.audio[0].rate
|
| 319 |
+
|
| 320 |
+
except av.AVError:
|
| 321 |
+
# TODO raise a warning?
|
| 322 |
+
pass
|
| 323 |
+
|
| 324 |
+
vframes_list = [frame.to_rgb().to_ndarray() for frame in video_frames]
|
| 325 |
+
aframes_list = [frame.to_ndarray() for frame in audio_frames]
|
| 326 |
+
|
| 327 |
+
if vframes_list:
|
| 328 |
+
vframes = torch.as_tensor(np.stack(vframes_list))
|
| 329 |
+
else:
|
| 330 |
+
vframes = torch.empty((0, 1, 1, 3), dtype=torch.uint8)
|
| 331 |
+
|
| 332 |
+
if aframes_list:
|
| 333 |
+
aframes = np.concatenate(aframes_list, 1)
|
| 334 |
+
aframes = torch.as_tensor(aframes)
|
| 335 |
+
if pts_unit == "sec":
|
| 336 |
+
start_pts = int(math.floor(start_pts * (1 / audio_timebase)))
|
| 337 |
+
if end_pts != float("inf"):
|
| 338 |
+
end_pts = int(math.ceil(end_pts * (1 / audio_timebase)))
|
| 339 |
+
aframes = _align_audio_frames(aframes, audio_frames, start_pts, end_pts)
|
| 340 |
+
else:
|
| 341 |
+
aframes = torch.empty((1, 0), dtype=torch.float32)
|
| 342 |
+
|
| 343 |
+
if output_format == "TCHW":
|
| 344 |
+
# [T,H,W,C] --> [T,C,H,W]
|
| 345 |
+
vframes = vframes.permute(0, 3, 1, 2)
|
| 346 |
+
|
| 347 |
+
return vframes, aframes, info
|
| 348 |
+
|
| 349 |
+
|
| 350 |
+
def _can_read_timestamps_from_packets(container: "av.container.Container") -> bool:
|
| 351 |
+
extradata = container.streams[0].codec_context.extradata
|
| 352 |
+
if extradata is None:
|
| 353 |
+
return False
|
| 354 |
+
if b"Lavc" in extradata:
|
| 355 |
+
return True
|
| 356 |
+
return False
|
| 357 |
+
|
| 358 |
+
|
| 359 |
+
def _decode_video_timestamps(container: "av.container.Container") -> List[int]:
|
| 360 |
+
if _can_read_timestamps_from_packets(container):
|
| 361 |
+
# fast path
|
| 362 |
+
return [x.pts for x in container.demux(video=0) if x.pts is not None]
|
| 363 |
+
else:
|
| 364 |
+
return [x.pts for x in container.decode(video=0) if x.pts is not None]
|
| 365 |
+
|
| 366 |
+
|
| 367 |
+
def read_video_timestamps(filename: str, pts_unit: str = "pts") -> Tuple[List[int], Optional[float]]:
|
| 368 |
+
"""
|
| 369 |
+
List the video frames timestamps.
|
| 370 |
+
|
| 371 |
+
Note that the function decodes the whole video frame-by-frame.
|
| 372 |
+
|
| 373 |
+
Args:
|
| 374 |
+
filename (str): path to the video file
|
| 375 |
+
pts_unit (str, optional): unit in which timestamp values will be returned
|
| 376 |
+
either 'pts' or 'sec'. Defaults to 'pts'.
|
| 377 |
+
|
| 378 |
+
Returns:
|
| 379 |
+
pts (List[int] if pts_unit = 'pts', List[Fraction] if pts_unit = 'sec'):
|
| 380 |
+
presentation timestamps for each one of the frames in the video.
|
| 381 |
+
video_fps (float, optional): the frame rate for the video
|
| 382 |
+
|
| 383 |
+
"""
|
| 384 |
+
if not torch.jit.is_scripting() and not torch.jit.is_tracing():
|
| 385 |
+
_log_api_usage_once(read_video_timestamps)
|
| 386 |
+
from torchvision import get_video_backend
|
| 387 |
+
|
| 388 |
+
if get_video_backend() != "pyav":
|
| 389 |
+
return _video_opt._read_video_timestamps(filename, pts_unit)
|
| 390 |
+
|
| 391 |
+
_check_av_available()
|
| 392 |
+
|
| 393 |
+
video_fps = None
|
| 394 |
+
pts = []
|
| 395 |
+
|
| 396 |
+
try:
|
| 397 |
+
with av.open(filename, metadata_errors="ignore") as container:
|
| 398 |
+
if container.streams.video:
|
| 399 |
+
video_stream = container.streams.video[0]
|
| 400 |
+
video_time_base = video_stream.time_base
|
| 401 |
+
try:
|
| 402 |
+
pts = _decode_video_timestamps(container)
|
| 403 |
+
except av.AVError:
|
| 404 |
+
warnings.warn(f"Failed decoding frames for file {filename}")
|
| 405 |
+
video_fps = float(video_stream.average_rate)
|
| 406 |
+
except av.AVError as e:
|
| 407 |
+
msg = f"Failed to open container for {filename}; Caught error: {e}"
|
| 408 |
+
warnings.warn(msg, RuntimeWarning)
|
| 409 |
+
|
| 410 |
+
pts.sort()
|
| 411 |
+
|
| 412 |
+
if pts_unit == "sec":
|
| 413 |
+
pts = [x * video_time_base for x in pts]
|
| 414 |
+
|
| 415 |
+
return pts, video_fps
|
my_container_sandbox/workspace/anaconda3/lib/python3.8/site-packages/torchvision/models/__pycache__/_meta.cpython-38.pyc
ADDED
|
Binary file (27.9 kB). View file
|
|
|
my_container_sandbox/workspace/anaconda3/lib/python3.8/site-packages/torchvision/models/__pycache__/alexnet.cpython-38.pyc
ADDED
|
Binary file (4.34 kB). View file
|
|
|
my_container_sandbox/workspace/anaconda3/lib/python3.8/site-packages/torchvision/models/__pycache__/feature_extraction.cpython-38.pyc
ADDED
|
Binary file (20.8 kB). View file
|
|
|
my_container_sandbox/workspace/anaconda3/lib/python3.8/site-packages/torchvision/models/__pycache__/mobilenetv2.cpython-38.pyc
ADDED
|
Binary file (8.23 kB). View file
|
|
|
my_container_sandbox/workspace/anaconda3/lib/python3.8/site-packages/torchvision/models/detection/_utils.py
ADDED
|
@@ -0,0 +1,538 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
import math
|
| 2 |
+
from collections import OrderedDict
|
| 3 |
+
from typing import Dict, List, Optional, Tuple
|
| 4 |
+
|
| 5 |
+
import torch
|
| 6 |
+
from torch import Tensor, nn
|
| 7 |
+
from torch.nn import functional as F
|
| 8 |
+
from torchvision.ops import FrozenBatchNorm2d, complete_box_iou_loss, distance_box_iou_loss, generalized_box_iou_loss
|
| 9 |
+
|
| 10 |
+
|
| 11 |
+
class BalancedPositiveNegativeSampler:
|
| 12 |
+
"""
|
| 13 |
+
This class samples batches, ensuring that they contain a fixed proportion of positives
|
| 14 |
+
"""
|
| 15 |
+
|
| 16 |
+
def __init__(self, batch_size_per_image: int, positive_fraction: float) -> None:
|
| 17 |
+
"""
|
| 18 |
+
Args:
|
| 19 |
+
batch_size_per_image (int): number of elements to be selected per image
|
| 20 |
+
positive_fraction (float): percentage of positive elements per batch
|
| 21 |
+
"""
|
| 22 |
+
self.batch_size_per_image = batch_size_per_image
|
| 23 |
+
self.positive_fraction = positive_fraction
|
| 24 |
+
|
| 25 |
+
def __call__(self, matched_idxs: List[Tensor]) -> Tuple[List[Tensor], List[Tensor]]:
|
| 26 |
+
"""
|
| 27 |
+
Args:
|
| 28 |
+
matched idxs: list of tensors containing -1, 0 or positive values.
|
| 29 |
+
Each tensor corresponds to a specific image.
|
| 30 |
+
-1 values are ignored, 0 are considered as negatives and > 0 as
|
| 31 |
+
positives.
|
| 32 |
+
|
| 33 |
+
Returns:
|
| 34 |
+
pos_idx (list[tensor])
|
| 35 |
+
neg_idx (list[tensor])
|
| 36 |
+
|
| 37 |
+
Returns two lists of binary masks for each image.
|
| 38 |
+
The first list contains the positive elements that were selected,
|
| 39 |
+
and the second list the negative example.
|
| 40 |
+
"""
|
| 41 |
+
pos_idx = []
|
| 42 |
+
neg_idx = []
|
| 43 |
+
for matched_idxs_per_image in matched_idxs:
|
| 44 |
+
positive = torch.where(matched_idxs_per_image >= 1)[0]
|
| 45 |
+
negative = torch.where(matched_idxs_per_image == 0)[0]
|
| 46 |
+
|
| 47 |
+
num_pos = int(self.batch_size_per_image * self.positive_fraction)
|
| 48 |
+
# protect against not enough positive examples
|
| 49 |
+
num_pos = min(positive.numel(), num_pos)
|
| 50 |
+
num_neg = self.batch_size_per_image - num_pos
|
| 51 |
+
# protect against not enough negative examples
|
| 52 |
+
num_neg = min(negative.numel(), num_neg)
|
| 53 |
+
|
| 54 |
+
# randomly select positive and negative examples
|
| 55 |
+
perm1 = torch.randperm(positive.numel(), device=positive.device)[:num_pos]
|
| 56 |
+
perm2 = torch.randperm(negative.numel(), device=negative.device)[:num_neg]
|
| 57 |
+
|
| 58 |
+
pos_idx_per_image = positive[perm1]
|
| 59 |
+
neg_idx_per_image = negative[perm2]
|
| 60 |
+
|
| 61 |
+
# create binary mask from indices
|
| 62 |
+
pos_idx_per_image_mask = torch.zeros_like(matched_idxs_per_image, dtype=torch.uint8)
|
| 63 |
+
neg_idx_per_image_mask = torch.zeros_like(matched_idxs_per_image, dtype=torch.uint8)
|
| 64 |
+
|
| 65 |
+
pos_idx_per_image_mask[pos_idx_per_image] = 1
|
| 66 |
+
neg_idx_per_image_mask[neg_idx_per_image] = 1
|
| 67 |
+
|
| 68 |
+
pos_idx.append(pos_idx_per_image_mask)
|
| 69 |
+
neg_idx.append(neg_idx_per_image_mask)
|
| 70 |
+
|
| 71 |
+
return pos_idx, neg_idx
|
| 72 |
+
|
| 73 |
+
|
| 74 |
+
@torch.jit._script_if_tracing
|
| 75 |
+
def encode_boxes(reference_boxes: Tensor, proposals: Tensor, weights: Tensor) -> Tensor:
|
| 76 |
+
"""
|
| 77 |
+
Encode a set of proposals with respect to some
|
| 78 |
+
reference boxes
|
| 79 |
+
|
| 80 |
+
Args:
|
| 81 |
+
reference_boxes (Tensor): reference boxes
|
| 82 |
+
proposals (Tensor): boxes to be encoded
|
| 83 |
+
weights (Tensor[4]): the weights for ``(x, y, w, h)``
|
| 84 |
+
"""
|
| 85 |
+
|
| 86 |
+
# perform some unpacking to make it JIT-fusion friendly
|
| 87 |
+
wx = weights[0]
|
| 88 |
+
wy = weights[1]
|
| 89 |
+
ww = weights[2]
|
| 90 |
+
wh = weights[3]
|
| 91 |
+
|
| 92 |
+
proposals_x1 = proposals[:, 0].unsqueeze(1)
|
| 93 |
+
proposals_y1 = proposals[:, 1].unsqueeze(1)
|
| 94 |
+
proposals_x2 = proposals[:, 2].unsqueeze(1)
|
| 95 |
+
proposals_y2 = proposals[:, 3].unsqueeze(1)
|
| 96 |
+
|
| 97 |
+
reference_boxes_x1 = reference_boxes[:, 0].unsqueeze(1)
|
| 98 |
+
reference_boxes_y1 = reference_boxes[:, 1].unsqueeze(1)
|
| 99 |
+
reference_boxes_x2 = reference_boxes[:, 2].unsqueeze(1)
|
| 100 |
+
reference_boxes_y2 = reference_boxes[:, 3].unsqueeze(1)
|
| 101 |
+
|
| 102 |
+
# implementation starts here
|
| 103 |
+
ex_widths = proposals_x2 - proposals_x1
|
| 104 |
+
ex_heights = proposals_y2 - proposals_y1
|
| 105 |
+
ex_ctr_x = proposals_x1 + 0.5 * ex_widths
|
| 106 |
+
ex_ctr_y = proposals_y1 + 0.5 * ex_heights
|
| 107 |
+
|
| 108 |
+
gt_widths = reference_boxes_x2 - reference_boxes_x1
|
| 109 |
+
gt_heights = reference_boxes_y2 - reference_boxes_y1
|
| 110 |
+
gt_ctr_x = reference_boxes_x1 + 0.5 * gt_widths
|
| 111 |
+
gt_ctr_y = reference_boxes_y1 + 0.5 * gt_heights
|
| 112 |
+
|
| 113 |
+
targets_dx = wx * (gt_ctr_x - ex_ctr_x) / ex_widths
|
| 114 |
+
targets_dy = wy * (gt_ctr_y - ex_ctr_y) / ex_heights
|
| 115 |
+
targets_dw = ww * torch.log(gt_widths / ex_widths)
|
| 116 |
+
targets_dh = wh * torch.log(gt_heights / ex_heights)
|
| 117 |
+
|
| 118 |
+
targets = torch.cat((targets_dx, targets_dy, targets_dw, targets_dh), dim=1)
|
| 119 |
+
return targets
|
| 120 |
+
|
| 121 |
+
|
| 122 |
+
class BoxCoder:
|
| 123 |
+
"""
|
| 124 |
+
This class encodes and decodes a set of bounding boxes into
|
| 125 |
+
the representation used for training the regressors.
|
| 126 |
+
"""
|
| 127 |
+
|
| 128 |
+
def __init__(
|
| 129 |
+
self, weights: Tuple[float, float, float, float], bbox_xform_clip: float = math.log(1000.0 / 16)
|
| 130 |
+
) -> None:
|
| 131 |
+
"""
|
| 132 |
+
Args:
|
| 133 |
+
weights (4-element tuple)
|
| 134 |
+
bbox_xform_clip (float)
|
| 135 |
+
"""
|
| 136 |
+
self.weights = weights
|
| 137 |
+
self.bbox_xform_clip = bbox_xform_clip
|
| 138 |
+
|
| 139 |
+
def encode(self, reference_boxes: List[Tensor], proposals: List[Tensor]) -> List[Tensor]:
|
| 140 |
+
boxes_per_image = [len(b) for b in reference_boxes]
|
| 141 |
+
reference_boxes = torch.cat(reference_boxes, dim=0)
|
| 142 |
+
proposals = torch.cat(proposals, dim=0)
|
| 143 |
+
targets = self.encode_single(reference_boxes, proposals)
|
| 144 |
+
return targets.split(boxes_per_image, 0)
|
| 145 |
+
|
| 146 |
+
def encode_single(self, reference_boxes: Tensor, proposals: Tensor) -> Tensor:
|
| 147 |
+
"""
|
| 148 |
+
Encode a set of proposals with respect to some
|
| 149 |
+
reference boxes
|
| 150 |
+
|
| 151 |
+
Args:
|
| 152 |
+
reference_boxes (Tensor): reference boxes
|
| 153 |
+
proposals (Tensor): boxes to be encoded
|
| 154 |
+
"""
|
| 155 |
+
dtype = reference_boxes.dtype
|
| 156 |
+
device = reference_boxes.device
|
| 157 |
+
weights = torch.as_tensor(self.weights, dtype=dtype, device=device)
|
| 158 |
+
targets = encode_boxes(reference_boxes, proposals, weights)
|
| 159 |
+
|
| 160 |
+
return targets
|
| 161 |
+
|
| 162 |
+
def decode(self, rel_codes: Tensor, boxes: List[Tensor]) -> Tensor:
|
| 163 |
+
torch._assert(
|
| 164 |
+
isinstance(boxes, (list, tuple)),
|
| 165 |
+
"This function expects boxes of type list or tuple.",
|
| 166 |
+
)
|
| 167 |
+
torch._assert(
|
| 168 |
+
isinstance(rel_codes, torch.Tensor),
|
| 169 |
+
"This function expects rel_codes of type torch.Tensor.",
|
| 170 |
+
)
|
| 171 |
+
boxes_per_image = [b.size(0) for b in boxes]
|
| 172 |
+
concat_boxes = torch.cat(boxes, dim=0)
|
| 173 |
+
box_sum = 0
|
| 174 |
+
for val in boxes_per_image:
|
| 175 |
+
box_sum += val
|
| 176 |
+
if box_sum > 0:
|
| 177 |
+
rel_codes = rel_codes.reshape(box_sum, -1)
|
| 178 |
+
pred_boxes = self.decode_single(rel_codes, concat_boxes)
|
| 179 |
+
if box_sum > 0:
|
| 180 |
+
pred_boxes = pred_boxes.reshape(box_sum, -1, 4)
|
| 181 |
+
return pred_boxes
|
| 182 |
+
|
| 183 |
+
def decode_single(self, rel_codes: Tensor, boxes: Tensor) -> Tensor:
|
| 184 |
+
"""
|
| 185 |
+
From a set of original boxes and encoded relative box offsets,
|
| 186 |
+
get the decoded boxes.
|
| 187 |
+
|
| 188 |
+
Args:
|
| 189 |
+
rel_codes (Tensor): encoded boxes
|
| 190 |
+
boxes (Tensor): reference boxes.
|
| 191 |
+
"""
|
| 192 |
+
|
| 193 |
+
boxes = boxes.to(rel_codes.dtype)
|
| 194 |
+
|
| 195 |
+
widths = boxes[:, 2] - boxes[:, 0]
|
| 196 |
+
heights = boxes[:, 3] - boxes[:, 1]
|
| 197 |
+
ctr_x = boxes[:, 0] + 0.5 * widths
|
| 198 |
+
ctr_y = boxes[:, 1] + 0.5 * heights
|
| 199 |
+
|
| 200 |
+
wx, wy, ww, wh = self.weights
|
| 201 |
+
dx = rel_codes[:, 0::4] / wx
|
| 202 |
+
dy = rel_codes[:, 1::4] / wy
|
| 203 |
+
dw = rel_codes[:, 2::4] / ww
|
| 204 |
+
dh = rel_codes[:, 3::4] / wh
|
| 205 |
+
|
| 206 |
+
# Prevent sending too large values into torch.exp()
|
| 207 |
+
dw = torch.clamp(dw, max=self.bbox_xform_clip)
|
| 208 |
+
dh = torch.clamp(dh, max=self.bbox_xform_clip)
|
| 209 |
+
|
| 210 |
+
pred_ctr_x = dx * widths[:, None] + ctr_x[:, None]
|
| 211 |
+
pred_ctr_y = dy * heights[:, None] + ctr_y[:, None]
|
| 212 |
+
pred_w = torch.exp(dw) * widths[:, None]
|
| 213 |
+
pred_h = torch.exp(dh) * heights[:, None]
|
| 214 |
+
|
| 215 |
+
# Distance from center to box's corner.
|
| 216 |
+
c_to_c_h = torch.tensor(0.5, dtype=pred_ctr_y.dtype, device=pred_h.device) * pred_h
|
| 217 |
+
c_to_c_w = torch.tensor(0.5, dtype=pred_ctr_x.dtype, device=pred_w.device) * pred_w
|
| 218 |
+
|
| 219 |
+
pred_boxes1 = pred_ctr_x - c_to_c_w
|
| 220 |
+
pred_boxes2 = pred_ctr_y - c_to_c_h
|
| 221 |
+
pred_boxes3 = pred_ctr_x + c_to_c_w
|
| 222 |
+
pred_boxes4 = pred_ctr_y + c_to_c_h
|
| 223 |
+
pred_boxes = torch.stack((pred_boxes1, pred_boxes2, pred_boxes3, pred_boxes4), dim=2).flatten(1)
|
| 224 |
+
return pred_boxes
|
| 225 |
+
|
| 226 |
+
|
| 227 |
+
class BoxLinearCoder:
|
| 228 |
+
"""
|
| 229 |
+
The linear box-to-box transform defined in FCOS. The transformation is parameterized
|
| 230 |
+
by the distance from the center of (square) src box to 4 edges of the target box.
|
| 231 |
+
"""
|
| 232 |
+
|
| 233 |
+
def __init__(self, normalize_by_size: bool = True) -> None:
|
| 234 |
+
"""
|
| 235 |
+
Args:
|
| 236 |
+
normalize_by_size (bool): normalize deltas by the size of src (anchor) boxes.
|
| 237 |
+
"""
|
| 238 |
+
self.normalize_by_size = normalize_by_size
|
| 239 |
+
|
| 240 |
+
def encode_single(self, reference_boxes: Tensor, proposals: Tensor) -> Tensor:
|
| 241 |
+
"""
|
| 242 |
+
Encode a set of proposals with respect to some reference boxes
|
| 243 |
+
|
| 244 |
+
Args:
|
| 245 |
+
reference_boxes (Tensor): reference boxes
|
| 246 |
+
proposals (Tensor): boxes to be encoded
|
| 247 |
+
|
| 248 |
+
Returns:
|
| 249 |
+
Tensor: the encoded relative box offsets that can be used to
|
| 250 |
+
decode the boxes.
|
| 251 |
+
"""
|
| 252 |
+
# get the center of reference_boxes
|
| 253 |
+
reference_boxes_ctr_x = 0.5 * (reference_boxes[:, 0] + reference_boxes[:, 2])
|
| 254 |
+
reference_boxes_ctr_y = 0.5 * (reference_boxes[:, 1] + reference_boxes[:, 3])
|
| 255 |
+
|
| 256 |
+
# get box regression transformation deltas
|
| 257 |
+
target_l = reference_boxes_ctr_x - proposals[:, 0]
|
| 258 |
+
target_t = reference_boxes_ctr_y - proposals[:, 1]
|
| 259 |
+
target_r = proposals[:, 2] - reference_boxes_ctr_x
|
| 260 |
+
target_b = proposals[:, 3] - reference_boxes_ctr_y
|
| 261 |
+
|
| 262 |
+
targets = torch.stack((target_l, target_t, target_r, target_b), dim=1)
|
| 263 |
+
if self.normalize_by_size:
|
| 264 |
+
reference_boxes_w = reference_boxes[:, 2] - reference_boxes[:, 0]
|
| 265 |
+
reference_boxes_h = reference_boxes[:, 3] - reference_boxes[:, 1]
|
| 266 |
+
reference_boxes_size = torch.stack(
|
| 267 |
+
(reference_boxes_w, reference_boxes_h, reference_boxes_w, reference_boxes_h), dim=1
|
| 268 |
+
)
|
| 269 |
+
targets = targets / reference_boxes_size
|
| 270 |
+
|
| 271 |
+
return targets
|
| 272 |
+
|
| 273 |
+
def decode_single(self, rel_codes: Tensor, boxes: Tensor) -> Tensor:
|
| 274 |
+
"""
|
| 275 |
+
From a set of original boxes and encoded relative box offsets,
|
| 276 |
+
get the decoded boxes.
|
| 277 |
+
|
| 278 |
+
Args:
|
| 279 |
+
rel_codes (Tensor): encoded boxes
|
| 280 |
+
boxes (Tensor): reference boxes.
|
| 281 |
+
|
| 282 |
+
Returns:
|
| 283 |
+
Tensor: the predicted boxes with the encoded relative box offsets.
|
| 284 |
+
"""
|
| 285 |
+
|
| 286 |
+
boxes = boxes.to(rel_codes.dtype)
|
| 287 |
+
|
| 288 |
+
ctr_x = 0.5 * (boxes[:, 0] + boxes[:, 2])
|
| 289 |
+
ctr_y = 0.5 * (boxes[:, 1] + boxes[:, 3])
|
| 290 |
+
if self.normalize_by_size:
|
| 291 |
+
boxes_w = boxes[:, 2] - boxes[:, 0]
|
| 292 |
+
boxes_h = boxes[:, 3] - boxes[:, 1]
|
| 293 |
+
boxes_size = torch.stack((boxes_w, boxes_h, boxes_w, boxes_h), dim=1)
|
| 294 |
+
rel_codes = rel_codes * boxes_size
|
| 295 |
+
|
| 296 |
+
pred_boxes1 = ctr_x - rel_codes[:, 0]
|
| 297 |
+
pred_boxes2 = ctr_y - rel_codes[:, 1]
|
| 298 |
+
pred_boxes3 = ctr_x + rel_codes[:, 2]
|
| 299 |
+
pred_boxes4 = ctr_y + rel_codes[:, 3]
|
| 300 |
+
pred_boxes = torch.stack((pred_boxes1, pred_boxes2, pred_boxes3, pred_boxes4), dim=1)
|
| 301 |
+
return pred_boxes
|
| 302 |
+
|
| 303 |
+
|
| 304 |
+
class Matcher:
|
| 305 |
+
"""
|
| 306 |
+
This class assigns to each predicted "element" (e.g., a box) a ground-truth
|
| 307 |
+
element. Each predicted element will have exactly zero or one matches; each
|
| 308 |
+
ground-truth element may be assigned to zero or more predicted elements.
|
| 309 |
+
|
| 310 |
+
Matching is based on the MxN match_quality_matrix, that characterizes how well
|
| 311 |
+
each (ground-truth, predicted)-pair match. For example, if the elements are
|
| 312 |
+
boxes, the matrix may contain box IoU overlap values.
|
| 313 |
+
|
| 314 |
+
The matcher returns a tensor of size N containing the index of the ground-truth
|
| 315 |
+
element m that matches to prediction n. If there is no match, a negative value
|
| 316 |
+
is returned.
|
| 317 |
+
"""
|
| 318 |
+
|
| 319 |
+
BELOW_LOW_THRESHOLD = -1
|
| 320 |
+
BETWEEN_THRESHOLDS = -2
|
| 321 |
+
|
| 322 |
+
__annotations__ = {
|
| 323 |
+
"BELOW_LOW_THRESHOLD": int,
|
| 324 |
+
"BETWEEN_THRESHOLDS": int,
|
| 325 |
+
}
|
| 326 |
+
|
| 327 |
+
def __init__(self, high_threshold: float, low_threshold: float, allow_low_quality_matches: bool = False) -> None:
|
| 328 |
+
"""
|
| 329 |
+
Args:
|
| 330 |
+
high_threshold (float): quality values greater than or equal to
|
| 331 |
+
this value are candidate matches.
|
| 332 |
+
low_threshold (float): a lower quality threshold used to stratify
|
| 333 |
+
matches into three levels:
|
| 334 |
+
1) matches >= high_threshold
|
| 335 |
+
2) BETWEEN_THRESHOLDS matches in [low_threshold, high_threshold)
|
| 336 |
+
3) BELOW_LOW_THRESHOLD matches in [0, low_threshold)
|
| 337 |
+
allow_low_quality_matches (bool): if True, produce additional matches
|
| 338 |
+
for predictions that have only low-quality match candidates. See
|
| 339 |
+
set_low_quality_matches_ for more details.
|
| 340 |
+
"""
|
| 341 |
+
self.BELOW_LOW_THRESHOLD = -1
|
| 342 |
+
self.BETWEEN_THRESHOLDS = -2
|
| 343 |
+
torch._assert(low_threshold <= high_threshold, "low_threshold should be <= high_threshold")
|
| 344 |
+
self.high_threshold = high_threshold
|
| 345 |
+
self.low_threshold = low_threshold
|
| 346 |
+
self.allow_low_quality_matches = allow_low_quality_matches
|
| 347 |
+
|
| 348 |
+
def __call__(self, match_quality_matrix: Tensor) -> Tensor:
|
| 349 |
+
"""
|
| 350 |
+
Args:
|
| 351 |
+
match_quality_matrix (Tensor[float]): an MxN tensor, containing the
|
| 352 |
+
pairwise quality between M ground-truth elements and N predicted elements.
|
| 353 |
+
|
| 354 |
+
Returns:
|
| 355 |
+
matches (Tensor[int64]): an N tensor where N[i] is a matched gt in
|
| 356 |
+
[0, M - 1] or a negative value indicating that prediction i could not
|
| 357 |
+
be matched.
|
| 358 |
+
"""
|
| 359 |
+
if match_quality_matrix.numel() == 0:
|
| 360 |
+
# empty targets or proposals not supported during training
|
| 361 |
+
if match_quality_matrix.shape[0] == 0:
|
| 362 |
+
raise ValueError("No ground-truth boxes available for one of the images during training")
|
| 363 |
+
else:
|
| 364 |
+
raise ValueError("No proposal boxes available for one of the images during training")
|
| 365 |
+
|
| 366 |
+
# match_quality_matrix is M (gt) x N (predicted)
|
| 367 |
+
# Max over gt elements (dim 0) to find best gt candidate for each prediction
|
| 368 |
+
matched_vals, matches = match_quality_matrix.max(dim=0)
|
| 369 |
+
if self.allow_low_quality_matches:
|
| 370 |
+
all_matches = matches.clone()
|
| 371 |
+
else:
|
| 372 |
+
all_matches = None # type: ignore[assignment]
|
| 373 |
+
|
| 374 |
+
# Assign candidate matches with low quality to negative (unassigned) values
|
| 375 |
+
below_low_threshold = matched_vals < self.low_threshold
|
| 376 |
+
between_thresholds = (matched_vals >= self.low_threshold) & (matched_vals < self.high_threshold)
|
| 377 |
+
matches[below_low_threshold] = self.BELOW_LOW_THRESHOLD
|
| 378 |
+
matches[between_thresholds] = self.BETWEEN_THRESHOLDS
|
| 379 |
+
|
| 380 |
+
if self.allow_low_quality_matches:
|
| 381 |
+
if all_matches is None:
|
| 382 |
+
torch._assert(False, "all_matches should not be None")
|
| 383 |
+
else:
|
| 384 |
+
self.set_low_quality_matches_(matches, all_matches, match_quality_matrix)
|
| 385 |
+
|
| 386 |
+
return matches
|
| 387 |
+
|
| 388 |
+
def set_low_quality_matches_(self, matches: Tensor, all_matches: Tensor, match_quality_matrix: Tensor) -> None:
|
| 389 |
+
"""
|
| 390 |
+
Produce additional matches for predictions that have only low-quality matches.
|
| 391 |
+
Specifically, for each ground-truth find the set of predictions that have
|
| 392 |
+
maximum overlap with it (including ties); for each prediction in that set, if
|
| 393 |
+
it is unmatched, then match it to the ground-truth with which it has the highest
|
| 394 |
+
quality value.
|
| 395 |
+
"""
|
| 396 |
+
# For each gt, find the prediction with which it has highest quality
|
| 397 |
+
highest_quality_foreach_gt, _ = match_quality_matrix.max(dim=1)
|
| 398 |
+
# Find highest quality match available, even if it is low, including ties
|
| 399 |
+
gt_pred_pairs_of_highest_quality = torch.where(match_quality_matrix == highest_quality_foreach_gt[:, None])
|
| 400 |
+
# Example gt_pred_pairs_of_highest_quality:
|
| 401 |
+
# tensor([[ 0, 39796],
|
| 402 |
+
# [ 1, 32055],
|
| 403 |
+
# [ 1, 32070],
|
| 404 |
+
# [ 2, 39190],
|
| 405 |
+
# [ 2, 40255],
|
| 406 |
+
# [ 3, 40390],
|
| 407 |
+
# [ 3, 41455],
|
| 408 |
+
# [ 4, 45470],
|
| 409 |
+
# [ 5, 45325],
|
| 410 |
+
# [ 5, 46390]])
|
| 411 |
+
# Each row is a (gt index, prediction index)
|
| 412 |
+
# Note how gt items 1, 2, 3, and 5 each have two ties
|
| 413 |
+
|
| 414 |
+
pred_inds_to_update = gt_pred_pairs_of_highest_quality[1]
|
| 415 |
+
matches[pred_inds_to_update] = all_matches[pred_inds_to_update]
|
| 416 |
+
|
| 417 |
+
|
| 418 |
+
class SSDMatcher(Matcher):
|
| 419 |
+
def __init__(self, threshold: float) -> None:
|
| 420 |
+
super().__init__(threshold, threshold, allow_low_quality_matches=False)
|
| 421 |
+
|
| 422 |
+
def __call__(self, match_quality_matrix: Tensor) -> Tensor:
|
| 423 |
+
matches = super().__call__(match_quality_matrix)
|
| 424 |
+
|
| 425 |
+
# For each gt, find the prediction with which it has the highest quality
|
| 426 |
+
_, highest_quality_pred_foreach_gt = match_quality_matrix.max(dim=1)
|
| 427 |
+
matches[highest_quality_pred_foreach_gt] = torch.arange(
|
| 428 |
+
highest_quality_pred_foreach_gt.size(0), dtype=torch.int64, device=highest_quality_pred_foreach_gt.device
|
| 429 |
+
)
|
| 430 |
+
|
| 431 |
+
return matches
|
| 432 |
+
|
| 433 |
+
|
| 434 |
+
def overwrite_eps(model: nn.Module, eps: float) -> None:
|
| 435 |
+
"""
|
| 436 |
+
This method overwrites the default eps values of all the
|
| 437 |
+
FrozenBatchNorm2d layers of the model with the provided value.
|
| 438 |
+
This is necessary to address the BC-breaking change introduced
|
| 439 |
+
by the bug-fix at pytorch/vision#2933. The overwrite is applied
|
| 440 |
+
only when the pretrained weights are loaded to maintain compatibility
|
| 441 |
+
with previous versions.
|
| 442 |
+
|
| 443 |
+
Args:
|
| 444 |
+
model (nn.Module): The model on which we perform the overwrite.
|
| 445 |
+
eps (float): The new value of eps.
|
| 446 |
+
"""
|
| 447 |
+
for module in model.modules():
|
| 448 |
+
if isinstance(module, FrozenBatchNorm2d):
|
| 449 |
+
module.eps = eps
|
| 450 |
+
|
| 451 |
+
|
| 452 |
+
def retrieve_out_channels(model: nn.Module, size: Tuple[int, int]) -> List[int]:
|
| 453 |
+
"""
|
| 454 |
+
This method retrieves the number of output channels of a specific model.
|
| 455 |
+
|
| 456 |
+
Args:
|
| 457 |
+
model (nn.Module): The model for which we estimate the out_channels.
|
| 458 |
+
It should return a single Tensor or an OrderedDict[Tensor].
|
| 459 |
+
size (Tuple[int, int]): The size (wxh) of the input.
|
| 460 |
+
|
| 461 |
+
Returns:
|
| 462 |
+
out_channels (List[int]): A list of the output channels of the model.
|
| 463 |
+
"""
|
| 464 |
+
in_training = model.training
|
| 465 |
+
model.eval()
|
| 466 |
+
|
| 467 |
+
with torch.no_grad():
|
| 468 |
+
# Use dummy data to retrieve the feature map sizes to avoid hard-coding their values
|
| 469 |
+
device = next(model.parameters()).device
|
| 470 |
+
tmp_img = torch.zeros((1, 3, size[1], size[0]), device=device)
|
| 471 |
+
features = model(tmp_img)
|
| 472 |
+
if isinstance(features, torch.Tensor):
|
| 473 |
+
features = OrderedDict([("0", features)])
|
| 474 |
+
out_channels = [x.size(1) for x in features.values()]
|
| 475 |
+
|
| 476 |
+
if in_training:
|
| 477 |
+
model.train()
|
| 478 |
+
|
| 479 |
+
return out_channels
|
| 480 |
+
|
| 481 |
+
|
| 482 |
+
@torch.jit.unused
|
| 483 |
+
def _fake_cast_onnx(v: Tensor) -> int:
|
| 484 |
+
return v # type: ignore[return-value]
|
| 485 |
+
|
| 486 |
+
|
| 487 |
+
def _topk_min(input: Tensor, orig_kval: int, axis: int) -> int:
|
| 488 |
+
"""
|
| 489 |
+
ONNX spec requires the k-value to be less than or equal to the number of inputs along
|
| 490 |
+
provided dim. Certain models use the number of elements along a particular axis instead of K
|
| 491 |
+
if K exceeds the number of elements along that axis. Previously, python's min() function was
|
| 492 |
+
used to determine whether to use the provided k-value or the specified dim axis value.
|
| 493 |
+
|
| 494 |
+
However in cases where the model is being exported in tracing mode, python min() is
|
| 495 |
+
static causing the model to be traced incorrectly and eventually fail at the topk node.
|
| 496 |
+
In order to avoid this situation, in tracing mode, torch.min() is used instead.
|
| 497 |
+
|
| 498 |
+
Args:
|
| 499 |
+
input (Tensor): The orignal input tensor.
|
| 500 |
+
orig_kval (int): The provided k-value.
|
| 501 |
+
axis(int): Axis along which we retreive the input size.
|
| 502 |
+
|
| 503 |
+
Returns:
|
| 504 |
+
min_kval (int): Appropriately selected k-value.
|
| 505 |
+
"""
|
| 506 |
+
if not torch.jit.is_tracing():
|
| 507 |
+
return min(orig_kval, input.size(axis))
|
| 508 |
+
axis_dim_val = torch._shape_as_tensor(input)[axis].unsqueeze(0)
|
| 509 |
+
min_kval = torch.min(torch.cat((torch.tensor([orig_kval], dtype=axis_dim_val.dtype), axis_dim_val), 0))
|
| 510 |
+
return _fake_cast_onnx(min_kval)
|
| 511 |
+
|
| 512 |
+
|
| 513 |
+
def _box_loss(
|
| 514 |
+
type: str,
|
| 515 |
+
box_coder: BoxCoder,
|
| 516 |
+
anchors_per_image: Tensor,
|
| 517 |
+
matched_gt_boxes_per_image: Tensor,
|
| 518 |
+
bbox_regression_per_image: Tensor,
|
| 519 |
+
cnf: Optional[Dict[str, float]] = None,
|
| 520 |
+
) -> Tensor:
|
| 521 |
+
torch._assert(type in ["l1", "smooth_l1", "ciou", "diou", "giou"], f"Unsupported loss: {type}")
|
| 522 |
+
|
| 523 |
+
if type == "l1":
|
| 524 |
+
target_regression = box_coder.encode_single(matched_gt_boxes_per_image, anchors_per_image)
|
| 525 |
+
return F.l1_loss(bbox_regression_per_image, target_regression, reduction="sum")
|
| 526 |
+
elif type == "smooth_l1":
|
| 527 |
+
target_regression = box_coder.encode_single(matched_gt_boxes_per_image, anchors_per_image)
|
| 528 |
+
beta = cnf["beta"] if cnf is not None and "beta" in cnf else 1.0
|
| 529 |
+
return F.smooth_l1_loss(bbox_regression_per_image, target_regression, reduction="sum", beta=beta)
|
| 530 |
+
else:
|
| 531 |
+
bbox_per_image = box_coder.decode_single(bbox_regression_per_image, anchors_per_image)
|
| 532 |
+
eps = cnf["eps"] if cnf is not None and "eps" in cnf else 1e-7
|
| 533 |
+
if type == "ciou":
|
| 534 |
+
return complete_box_iou_loss(bbox_per_image, matched_gt_boxes_per_image, reduction="sum", eps=eps)
|
| 535 |
+
if type == "diou":
|
| 536 |
+
return distance_box_iou_loss(bbox_per_image, matched_gt_boxes_per_image, reduction="sum", eps=eps)
|
| 537 |
+
# otherwise giou
|
| 538 |
+
return generalized_box_iou_loss(bbox_per_image, matched_gt_boxes_per_image, reduction="sum", eps=eps)
|
my_container_sandbox/workspace/anaconda3/lib/python3.8/site-packages/torchvision/models/detection/anchor_utils.py
ADDED
|
@@ -0,0 +1,268 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
import math
|
| 2 |
+
from typing import List, Optional
|
| 3 |
+
|
| 4 |
+
import torch
|
| 5 |
+
from torch import nn, Tensor
|
| 6 |
+
|
| 7 |
+
from .image_list import ImageList
|
| 8 |
+
|
| 9 |
+
|
| 10 |
+
class AnchorGenerator(nn.Module):
|
| 11 |
+
"""
|
| 12 |
+
Module that generates anchors for a set of feature maps and
|
| 13 |
+
image sizes.
|
| 14 |
+
|
| 15 |
+
The module support computing anchors at multiple sizes and aspect ratios
|
| 16 |
+
per feature map. This module assumes aspect ratio = height / width for
|
| 17 |
+
each anchor.
|
| 18 |
+
|
| 19 |
+
sizes and aspect_ratios should have the same number of elements, and it should
|
| 20 |
+
correspond to the number of feature maps.
|
| 21 |
+
|
| 22 |
+
sizes[i] and aspect_ratios[i] can have an arbitrary number of elements,
|
| 23 |
+
and AnchorGenerator will output a set of sizes[i] * aspect_ratios[i] anchors
|
| 24 |
+
per spatial location for feature map i.
|
| 25 |
+
|
| 26 |
+
Args:
|
| 27 |
+
sizes (Tuple[Tuple[int]]):
|
| 28 |
+
aspect_ratios (Tuple[Tuple[float]]):
|
| 29 |
+
"""
|
| 30 |
+
|
| 31 |
+
__annotations__ = {
|
| 32 |
+
"cell_anchors": List[torch.Tensor],
|
| 33 |
+
}
|
| 34 |
+
|
| 35 |
+
def __init__(
|
| 36 |
+
self,
|
| 37 |
+
sizes=((128, 256, 512),),
|
| 38 |
+
aspect_ratios=((0.5, 1.0, 2.0),),
|
| 39 |
+
):
|
| 40 |
+
super().__init__()
|
| 41 |
+
|
| 42 |
+
if not isinstance(sizes[0], (list, tuple)):
|
| 43 |
+
# TODO change this
|
| 44 |
+
sizes = tuple((s,) for s in sizes)
|
| 45 |
+
if not isinstance(aspect_ratios[0], (list, tuple)):
|
| 46 |
+
aspect_ratios = (aspect_ratios,) * len(sizes)
|
| 47 |
+
|
| 48 |
+
self.sizes = sizes
|
| 49 |
+
self.aspect_ratios = aspect_ratios
|
| 50 |
+
self.cell_anchors = [
|
| 51 |
+
self.generate_anchors(size, aspect_ratio) for size, aspect_ratio in zip(sizes, aspect_ratios)
|
| 52 |
+
]
|
| 53 |
+
|
| 54 |
+
# TODO: https://github.com/pytorch/pytorch/issues/26792
|
| 55 |
+
# For every (aspect_ratios, scales) combination, output a zero-centered anchor with those values.
|
| 56 |
+
# (scales, aspect_ratios) are usually an element of zip(self.scales, self.aspect_ratios)
|
| 57 |
+
# This method assumes aspect ratio = height / width for an anchor.
|
| 58 |
+
def generate_anchors(
|
| 59 |
+
self,
|
| 60 |
+
scales: List[int],
|
| 61 |
+
aspect_ratios: List[float],
|
| 62 |
+
dtype: torch.dtype = torch.float32,
|
| 63 |
+
device: torch.device = torch.device("cpu"),
|
| 64 |
+
):
|
| 65 |
+
scales = torch.as_tensor(scales, dtype=dtype, device=device)
|
| 66 |
+
aspect_ratios = torch.as_tensor(aspect_ratios, dtype=dtype, device=device)
|
| 67 |
+
h_ratios = torch.sqrt(aspect_ratios)
|
| 68 |
+
w_ratios = 1 / h_ratios
|
| 69 |
+
|
| 70 |
+
ws = (w_ratios[:, None] * scales[None, :]).view(-1)
|
| 71 |
+
hs = (h_ratios[:, None] * scales[None, :]).view(-1)
|
| 72 |
+
|
| 73 |
+
base_anchors = torch.stack([-ws, -hs, ws, hs], dim=1) / 2
|
| 74 |
+
return base_anchors.round()
|
| 75 |
+
|
| 76 |
+
def set_cell_anchors(self, dtype: torch.dtype, device: torch.device):
|
| 77 |
+
self.cell_anchors = [cell_anchor.to(dtype=dtype, device=device) for cell_anchor in self.cell_anchors]
|
| 78 |
+
|
| 79 |
+
def num_anchors_per_location(self):
|
| 80 |
+
return [len(s) * len(a) for s, a in zip(self.sizes, self.aspect_ratios)]
|
| 81 |
+
|
| 82 |
+
# For every combination of (a, (g, s), i) in (self.cell_anchors, zip(grid_sizes, strides), 0:2),
|
| 83 |
+
# output g[i] anchors that are s[i] distance apart in direction i, with the same dimensions as a.
|
| 84 |
+
def grid_anchors(self, grid_sizes: List[List[int]], strides: List[List[Tensor]]) -> List[Tensor]:
|
| 85 |
+
anchors = []
|
| 86 |
+
cell_anchors = self.cell_anchors
|
| 87 |
+
torch._assert(cell_anchors is not None, "cell_anchors should not be None")
|
| 88 |
+
torch._assert(
|
| 89 |
+
len(grid_sizes) == len(strides) == len(cell_anchors),
|
| 90 |
+
"Anchors should be Tuple[Tuple[int]] because each feature "
|
| 91 |
+
"map could potentially have different sizes and aspect ratios. "
|
| 92 |
+
"There needs to be a match between the number of "
|
| 93 |
+
"feature maps passed and the number of sizes / aspect ratios specified.",
|
| 94 |
+
)
|
| 95 |
+
|
| 96 |
+
for size, stride, base_anchors in zip(grid_sizes, strides, cell_anchors):
|
| 97 |
+
grid_height, grid_width = size
|
| 98 |
+
stride_height, stride_width = stride
|
| 99 |
+
device = base_anchors.device
|
| 100 |
+
|
| 101 |
+
# For output anchor, compute [x_center, y_center, x_center, y_center]
|
| 102 |
+
shifts_x = torch.arange(0, grid_width, dtype=torch.int32, device=device) * stride_width
|
| 103 |
+
shifts_y = torch.arange(0, grid_height, dtype=torch.int32, device=device) * stride_height
|
| 104 |
+
shift_y, shift_x = torch.meshgrid(shifts_y, shifts_x, indexing="ij")
|
| 105 |
+
shift_x = shift_x.reshape(-1)
|
| 106 |
+
shift_y = shift_y.reshape(-1)
|
| 107 |
+
shifts = torch.stack((shift_x, shift_y, shift_x, shift_y), dim=1)
|
| 108 |
+
|
| 109 |
+
# For every (base anchor, output anchor) pair,
|
| 110 |
+
# offset each zero-centered base anchor by the center of the output anchor.
|
| 111 |
+
anchors.append((shifts.view(-1, 1, 4) + base_anchors.view(1, -1, 4)).reshape(-1, 4))
|
| 112 |
+
|
| 113 |
+
return anchors
|
| 114 |
+
|
| 115 |
+
def forward(self, image_list: ImageList, feature_maps: List[Tensor]) -> List[Tensor]:
|
| 116 |
+
grid_sizes = [feature_map.shape[-2:] for feature_map in feature_maps]
|
| 117 |
+
image_size = image_list.tensors.shape[-2:]
|
| 118 |
+
dtype, device = feature_maps[0].dtype, feature_maps[0].device
|
| 119 |
+
strides = [
|
| 120 |
+
[
|
| 121 |
+
torch.empty((), dtype=torch.int64, device=device).fill_(image_size[0] // g[0]),
|
| 122 |
+
torch.empty((), dtype=torch.int64, device=device).fill_(image_size[1] // g[1]),
|
| 123 |
+
]
|
| 124 |
+
for g in grid_sizes
|
| 125 |
+
]
|
| 126 |
+
self.set_cell_anchors(dtype, device)
|
| 127 |
+
anchors_over_all_feature_maps = self.grid_anchors(grid_sizes, strides)
|
| 128 |
+
anchors: List[List[torch.Tensor]] = []
|
| 129 |
+
for _ in range(len(image_list.image_sizes)):
|
| 130 |
+
anchors_in_image = [anchors_per_feature_map for anchors_per_feature_map in anchors_over_all_feature_maps]
|
| 131 |
+
anchors.append(anchors_in_image)
|
| 132 |
+
anchors = [torch.cat(anchors_per_image) for anchors_per_image in anchors]
|
| 133 |
+
return anchors
|
| 134 |
+
|
| 135 |
+
|
| 136 |
+
class DefaultBoxGenerator(nn.Module):
|
| 137 |
+
"""
|
| 138 |
+
This module generates the default boxes of SSD for a set of feature maps and image sizes.
|
| 139 |
+
|
| 140 |
+
Args:
|
| 141 |
+
aspect_ratios (List[List[int]]): A list with all the aspect ratios used in each feature map.
|
| 142 |
+
min_ratio (float): The minimum scale :math:`\text{s}_{\text{min}}` of the default boxes used in the estimation
|
| 143 |
+
of the scales of each feature map. It is used only if the ``scales`` parameter is not provided.
|
| 144 |
+
max_ratio (float): The maximum scale :math:`\text{s}_{\text{max}}` of the default boxes used in the estimation
|
| 145 |
+
of the scales of each feature map. It is used only if the ``scales`` parameter is not provided.
|
| 146 |
+
scales (List[float]], optional): The scales of the default boxes. If not provided it will be estimated using
|
| 147 |
+
the ``min_ratio`` and ``max_ratio`` parameters.
|
| 148 |
+
steps (List[int]], optional): It's a hyper-parameter that affects the tiling of defalt boxes. If not provided
|
| 149 |
+
it will be estimated from the data.
|
| 150 |
+
clip (bool): Whether the standardized values of default boxes should be clipped between 0 and 1. The clipping
|
| 151 |
+
is applied while the boxes are encoded in format ``(cx, cy, w, h)``.
|
| 152 |
+
"""
|
| 153 |
+
|
| 154 |
+
def __init__(
|
| 155 |
+
self,
|
| 156 |
+
aspect_ratios: List[List[int]],
|
| 157 |
+
min_ratio: float = 0.15,
|
| 158 |
+
max_ratio: float = 0.9,
|
| 159 |
+
scales: Optional[List[float]] = None,
|
| 160 |
+
steps: Optional[List[int]] = None,
|
| 161 |
+
clip: bool = True,
|
| 162 |
+
):
|
| 163 |
+
super().__init__()
|
| 164 |
+
if steps is not None and len(aspect_ratios) != len(steps):
|
| 165 |
+
raise ValueError("aspect_ratios and steps should have the same length")
|
| 166 |
+
self.aspect_ratios = aspect_ratios
|
| 167 |
+
self.steps = steps
|
| 168 |
+
self.clip = clip
|
| 169 |
+
num_outputs = len(aspect_ratios)
|
| 170 |
+
|
| 171 |
+
# Estimation of default boxes scales
|
| 172 |
+
if scales is None:
|
| 173 |
+
if num_outputs > 1:
|
| 174 |
+
range_ratio = max_ratio - min_ratio
|
| 175 |
+
self.scales = [min_ratio + range_ratio * k / (num_outputs - 1.0) for k in range(num_outputs)]
|
| 176 |
+
self.scales.append(1.0)
|
| 177 |
+
else:
|
| 178 |
+
self.scales = [min_ratio, max_ratio]
|
| 179 |
+
else:
|
| 180 |
+
self.scales = scales
|
| 181 |
+
|
| 182 |
+
self._wh_pairs = self._generate_wh_pairs(num_outputs)
|
| 183 |
+
|
| 184 |
+
def _generate_wh_pairs(
|
| 185 |
+
self, num_outputs: int, dtype: torch.dtype = torch.float32, device: torch.device = torch.device("cpu")
|
| 186 |
+
) -> List[Tensor]:
|
| 187 |
+
_wh_pairs: List[Tensor] = []
|
| 188 |
+
for k in range(num_outputs):
|
| 189 |
+
# Adding the 2 default width-height pairs for aspect ratio 1 and scale s'k
|
| 190 |
+
s_k = self.scales[k]
|
| 191 |
+
s_prime_k = math.sqrt(self.scales[k] * self.scales[k + 1])
|
| 192 |
+
wh_pairs = [[s_k, s_k], [s_prime_k, s_prime_k]]
|
| 193 |
+
|
| 194 |
+
# Adding 2 pairs for each aspect ratio of the feature map k
|
| 195 |
+
for ar in self.aspect_ratios[k]:
|
| 196 |
+
sq_ar = math.sqrt(ar)
|
| 197 |
+
w = self.scales[k] * sq_ar
|
| 198 |
+
h = self.scales[k] / sq_ar
|
| 199 |
+
wh_pairs.extend([[w, h], [h, w]])
|
| 200 |
+
|
| 201 |
+
_wh_pairs.append(torch.as_tensor(wh_pairs, dtype=dtype, device=device))
|
| 202 |
+
return _wh_pairs
|
| 203 |
+
|
| 204 |
+
def num_anchors_per_location(self):
|
| 205 |
+
# Estimate num of anchors based on aspect ratios: 2 default boxes + 2 * ratios of feaure map.
|
| 206 |
+
return [2 + 2 * len(r) for r in self.aspect_ratios]
|
| 207 |
+
|
| 208 |
+
# Default Boxes calculation based on page 6 of SSD paper
|
| 209 |
+
def _grid_default_boxes(
|
| 210 |
+
self, grid_sizes: List[List[int]], image_size: List[int], dtype: torch.dtype = torch.float32
|
| 211 |
+
) -> Tensor:
|
| 212 |
+
default_boxes = []
|
| 213 |
+
for k, f_k in enumerate(grid_sizes):
|
| 214 |
+
# Now add the default boxes for each width-height pair
|
| 215 |
+
if self.steps is not None:
|
| 216 |
+
x_f_k = image_size[0] / self.steps[k]
|
| 217 |
+
y_f_k = image_size[1] / self.steps[k]
|
| 218 |
+
else:
|
| 219 |
+
y_f_k, x_f_k = f_k
|
| 220 |
+
|
| 221 |
+
shifts_x = ((torch.arange(0, f_k[1]) + 0.5) / x_f_k).to(dtype=dtype)
|
| 222 |
+
shifts_y = ((torch.arange(0, f_k[0]) + 0.5) / y_f_k).to(dtype=dtype)
|
| 223 |
+
shift_y, shift_x = torch.meshgrid(shifts_y, shifts_x, indexing="ij")
|
| 224 |
+
shift_x = shift_x.reshape(-1)
|
| 225 |
+
shift_y = shift_y.reshape(-1)
|
| 226 |
+
|
| 227 |
+
shifts = torch.stack((shift_x, shift_y) * len(self._wh_pairs[k]), dim=-1).reshape(-1, 2)
|
| 228 |
+
# Clipping the default boxes while the boxes are encoded in format (cx, cy, w, h)
|
| 229 |
+
_wh_pair = self._wh_pairs[k].clamp(min=0, max=1) if self.clip else self._wh_pairs[k]
|
| 230 |
+
wh_pairs = _wh_pair.repeat((f_k[0] * f_k[1]), 1)
|
| 231 |
+
|
| 232 |
+
default_box = torch.cat((shifts, wh_pairs), dim=1)
|
| 233 |
+
|
| 234 |
+
default_boxes.append(default_box)
|
| 235 |
+
|
| 236 |
+
return torch.cat(default_boxes, dim=0)
|
| 237 |
+
|
| 238 |
+
def __repr__(self) -> str:
|
| 239 |
+
s = (
|
| 240 |
+
f"{self.__class__.__name__}("
|
| 241 |
+
f"aspect_ratios={self.aspect_ratios}"
|
| 242 |
+
f", clip={self.clip}"
|
| 243 |
+
f", scales={self.scales}"
|
| 244 |
+
f", steps={self.steps}"
|
| 245 |
+
")"
|
| 246 |
+
)
|
| 247 |
+
return s
|
| 248 |
+
|
| 249 |
+
def forward(self, image_list: ImageList, feature_maps: List[Tensor]) -> List[Tensor]:
|
| 250 |
+
grid_sizes = [feature_map.shape[-2:] for feature_map in feature_maps]
|
| 251 |
+
image_size = image_list.tensors.shape[-2:]
|
| 252 |
+
dtype, device = feature_maps[0].dtype, feature_maps[0].device
|
| 253 |
+
default_boxes = self._grid_default_boxes(grid_sizes, image_size, dtype=dtype)
|
| 254 |
+
default_boxes = default_boxes.to(device)
|
| 255 |
+
|
| 256 |
+
dboxes = []
|
| 257 |
+
x_y_size = torch.tensor([image_size[1], image_size[0]], device=default_boxes.device)
|
| 258 |
+
for _ in image_list.image_sizes:
|
| 259 |
+
dboxes_in_image = default_boxes
|
| 260 |
+
dboxes_in_image = torch.cat(
|
| 261 |
+
[
|
| 262 |
+
(dboxes_in_image[:, :2] - 0.5 * dboxes_in_image[:, 2:]) * x_y_size,
|
| 263 |
+
(dboxes_in_image[:, :2] + 0.5 * dboxes_in_image[:, 2:]) * x_y_size,
|
| 264 |
+
],
|
| 265 |
+
-1,
|
| 266 |
+
)
|
| 267 |
+
dboxes.append(dboxes_in_image)
|
| 268 |
+
return dboxes
|
my_container_sandbox/workspace/anaconda3/lib/python3.8/site-packages/torchvision/models/detection/generalized_rcnn.py
ADDED
|
@@ -0,0 +1,118 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
"""
|
| 2 |
+
Implements the Generalized R-CNN framework
|
| 3 |
+
"""
|
| 4 |
+
|
| 5 |
+
import warnings
|
| 6 |
+
from collections import OrderedDict
|
| 7 |
+
from typing import Tuple, List, Dict, Optional, Union
|
| 8 |
+
|
| 9 |
+
import torch
|
| 10 |
+
from torch import nn, Tensor
|
| 11 |
+
|
| 12 |
+
from ...utils import _log_api_usage_once
|
| 13 |
+
|
| 14 |
+
|
| 15 |
+
class GeneralizedRCNN(nn.Module):
|
| 16 |
+
"""
|
| 17 |
+
Main class for Generalized R-CNN.
|
| 18 |
+
|
| 19 |
+
Args:
|
| 20 |
+
backbone (nn.Module):
|
| 21 |
+
rpn (nn.Module):
|
| 22 |
+
roi_heads (nn.Module): takes the features + the proposals from the RPN and computes
|
| 23 |
+
detections / masks from it.
|
| 24 |
+
transform (nn.Module): performs the data transformation from the inputs to feed into
|
| 25 |
+
the model
|
| 26 |
+
"""
|
| 27 |
+
|
| 28 |
+
def __init__(self, backbone: nn.Module, rpn: nn.Module, roi_heads: nn.Module, transform: nn.Module) -> None:
|
| 29 |
+
super().__init__()
|
| 30 |
+
_log_api_usage_once(self)
|
| 31 |
+
self.transform = transform
|
| 32 |
+
self.backbone = backbone
|
| 33 |
+
self.rpn = rpn
|
| 34 |
+
self.roi_heads = roi_heads
|
| 35 |
+
# used only on torchscript mode
|
| 36 |
+
self._has_warned = False
|
| 37 |
+
|
| 38 |
+
@torch.jit.unused
|
| 39 |
+
def eager_outputs(self, losses, detections):
|
| 40 |
+
# type: (Dict[str, Tensor], List[Dict[str, Tensor]]) -> Union[Dict[str, Tensor], List[Dict[str, Tensor]]]
|
| 41 |
+
if self.training:
|
| 42 |
+
return losses
|
| 43 |
+
|
| 44 |
+
return detections
|
| 45 |
+
|
| 46 |
+
def forward(self, images, targets=None):
|
| 47 |
+
# type: (List[Tensor], Optional[List[Dict[str, Tensor]]]) -> Tuple[Dict[str, Tensor], List[Dict[str, Tensor]]]
|
| 48 |
+
"""
|
| 49 |
+
Args:
|
| 50 |
+
images (list[Tensor]): images to be processed
|
| 51 |
+
targets (list[Dict[str, Tensor]]): ground-truth boxes present in the image (optional)
|
| 52 |
+
|
| 53 |
+
Returns:
|
| 54 |
+
result (list[BoxList] or dict[Tensor]): the output from the model.
|
| 55 |
+
During training, it returns a dict[Tensor] which contains the losses.
|
| 56 |
+
During testing, it returns list[BoxList] contains additional fields
|
| 57 |
+
like `scores`, `labels` and `mask` (for Mask R-CNN models).
|
| 58 |
+
|
| 59 |
+
"""
|
| 60 |
+
if self.training:
|
| 61 |
+
if targets is None:
|
| 62 |
+
torch._assert(False, "targets should not be none when in training mode")
|
| 63 |
+
else:
|
| 64 |
+
for target in targets:
|
| 65 |
+
boxes = target["boxes"]
|
| 66 |
+
if isinstance(boxes, torch.Tensor):
|
| 67 |
+
torch._assert(
|
| 68 |
+
len(boxes.shape) == 2 and boxes.shape[-1] == 4,
|
| 69 |
+
f"Expected target boxes to be a tensor of shape [N, 4], got {boxes.shape}.",
|
| 70 |
+
)
|
| 71 |
+
else:
|
| 72 |
+
torch._assert(False, f"Expected target boxes to be of type Tensor, got {type(boxes)}.")
|
| 73 |
+
|
| 74 |
+
original_image_sizes: List[Tuple[int, int]] = []
|
| 75 |
+
for img in images:
|
| 76 |
+
val = img.shape[-2:]
|
| 77 |
+
torch._assert(
|
| 78 |
+
len(val) == 2,
|
| 79 |
+
f"expecting the last two dimensions of the Tensor to be H and W instead got {img.shape[-2:]}",
|
| 80 |
+
)
|
| 81 |
+
original_image_sizes.append((val[0], val[1]))
|
| 82 |
+
|
| 83 |
+
images, targets = self.transform(images, targets)
|
| 84 |
+
|
| 85 |
+
# Check for degenerate boxes
|
| 86 |
+
# TODO: Move this to a function
|
| 87 |
+
if targets is not None:
|
| 88 |
+
for target_idx, target in enumerate(targets):
|
| 89 |
+
boxes = target["boxes"]
|
| 90 |
+
degenerate_boxes = boxes[:, 2:] <= boxes[:, :2]
|
| 91 |
+
if degenerate_boxes.any():
|
| 92 |
+
# print the first degenerate box
|
| 93 |
+
bb_idx = torch.where(degenerate_boxes.any(dim=1))[0][0]
|
| 94 |
+
degen_bb: List[float] = boxes[bb_idx].tolist()
|
| 95 |
+
torch._assert(
|
| 96 |
+
False,
|
| 97 |
+
"All bounding boxes should have positive height and width."
|
| 98 |
+
f" Found invalid box {degen_bb} for target at index {target_idx}.",
|
| 99 |
+
)
|
| 100 |
+
|
| 101 |
+
features = self.backbone(images.tensors)
|
| 102 |
+
if isinstance(features, torch.Tensor):
|
| 103 |
+
features = OrderedDict([("0", features)])
|
| 104 |
+
proposals, proposal_losses = self.rpn(images, features, targets)
|
| 105 |
+
detections, detector_losses = self.roi_heads(features, proposals, images.image_sizes, targets)
|
| 106 |
+
detections = self.transform.postprocess(detections, images.image_sizes, original_image_sizes) # type: ignore[operator]
|
| 107 |
+
|
| 108 |
+
losses = {}
|
| 109 |
+
losses.update(detector_losses)
|
| 110 |
+
losses.update(proposal_losses)
|
| 111 |
+
|
| 112 |
+
if torch.jit.is_scripting():
|
| 113 |
+
if not self._has_warned:
|
| 114 |
+
warnings.warn("RCNN always returns a (Losses, Detections) tuple in scripting")
|
| 115 |
+
self._has_warned = True
|
| 116 |
+
return losses, detections
|
| 117 |
+
else:
|
| 118 |
+
return self.eager_outputs(losses, detections)
|
my_container_sandbox/workspace/anaconda3/lib/python3.8/site-packages/torchvision/models/detection/ssdlite.py
ADDED
|
@@ -0,0 +1,339 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
import warnings
|
| 2 |
+
from collections import OrderedDict
|
| 3 |
+
from functools import partial
|
| 4 |
+
from typing import Any, Callable, Dict, List, Optional, Union
|
| 5 |
+
|
| 6 |
+
import torch
|
| 7 |
+
from torch import nn, Tensor
|
| 8 |
+
|
| 9 |
+
from ...ops.misc import Conv2dNormActivation
|
| 10 |
+
from ...transforms._presets import ObjectDetection
|
| 11 |
+
from ...utils import _log_api_usage_once
|
| 12 |
+
from .. import mobilenet
|
| 13 |
+
from .._api import WeightsEnum, Weights
|
| 14 |
+
from .._meta import _COCO_CATEGORIES
|
| 15 |
+
from .._utils import handle_legacy_interface, _ovewrite_value_param
|
| 16 |
+
from ..mobilenetv3 import MobileNet_V3_Large_Weights, mobilenet_v3_large
|
| 17 |
+
from . import _utils as det_utils
|
| 18 |
+
from .anchor_utils import DefaultBoxGenerator
|
| 19 |
+
from .backbone_utils import _validate_trainable_layers
|
| 20 |
+
from .ssd import SSD, SSDScoringHead
|
| 21 |
+
|
| 22 |
+
|
| 23 |
+
__all__ = [
|
| 24 |
+
"SSDLite320_MobileNet_V3_Large_Weights",
|
| 25 |
+
"ssdlite320_mobilenet_v3_large",
|
| 26 |
+
]
|
| 27 |
+
|
| 28 |
+
|
| 29 |
+
# Building blocks of SSDlite as described in section 6.2 of MobileNetV2 paper
|
| 30 |
+
def _prediction_block(
|
| 31 |
+
in_channels: int, out_channels: int, kernel_size: int, norm_layer: Callable[..., nn.Module]
|
| 32 |
+
) -> nn.Sequential:
|
| 33 |
+
return nn.Sequential(
|
| 34 |
+
# 3x3 depthwise with stride 1 and padding 1
|
| 35 |
+
Conv2dNormActivation(
|
| 36 |
+
in_channels,
|
| 37 |
+
in_channels,
|
| 38 |
+
kernel_size=kernel_size,
|
| 39 |
+
groups=in_channels,
|
| 40 |
+
norm_layer=norm_layer,
|
| 41 |
+
activation_layer=nn.ReLU6,
|
| 42 |
+
),
|
| 43 |
+
# 1x1 projetion to output channels
|
| 44 |
+
nn.Conv2d(in_channels, out_channels, 1),
|
| 45 |
+
)
|
| 46 |
+
|
| 47 |
+
|
| 48 |
+
def _extra_block(in_channels: int, out_channels: int, norm_layer: Callable[..., nn.Module]) -> nn.Sequential:
|
| 49 |
+
activation = nn.ReLU6
|
| 50 |
+
intermediate_channels = out_channels // 2
|
| 51 |
+
return nn.Sequential(
|
| 52 |
+
# 1x1 projection to half output channels
|
| 53 |
+
Conv2dNormActivation(
|
| 54 |
+
in_channels, intermediate_channels, kernel_size=1, norm_layer=norm_layer, activation_layer=activation
|
| 55 |
+
),
|
| 56 |
+
# 3x3 depthwise with stride 2 and padding 1
|
| 57 |
+
Conv2dNormActivation(
|
| 58 |
+
intermediate_channels,
|
| 59 |
+
intermediate_channels,
|
| 60 |
+
kernel_size=3,
|
| 61 |
+
stride=2,
|
| 62 |
+
groups=intermediate_channels,
|
| 63 |
+
norm_layer=norm_layer,
|
| 64 |
+
activation_layer=activation,
|
| 65 |
+
),
|
| 66 |
+
# 1x1 projetion to output channels
|
| 67 |
+
Conv2dNormActivation(
|
| 68 |
+
intermediate_channels, out_channels, kernel_size=1, norm_layer=norm_layer, activation_layer=activation
|
| 69 |
+
),
|
| 70 |
+
)
|
| 71 |
+
|
| 72 |
+
|
| 73 |
+
def _normal_init(conv: nn.Module):
|
| 74 |
+
for layer in conv.modules():
|
| 75 |
+
if isinstance(layer, nn.Conv2d):
|
| 76 |
+
torch.nn.init.normal_(layer.weight, mean=0.0, std=0.03)
|
| 77 |
+
if layer.bias is not None:
|
| 78 |
+
torch.nn.init.constant_(layer.bias, 0.0)
|
| 79 |
+
|
| 80 |
+
|
| 81 |
+
class SSDLiteHead(nn.Module):
|
| 82 |
+
def __init__(
|
| 83 |
+
self, in_channels: List[int], num_anchors: List[int], num_classes: int, norm_layer: Callable[..., nn.Module]
|
| 84 |
+
):
|
| 85 |
+
super().__init__()
|
| 86 |
+
self.classification_head = SSDLiteClassificationHead(in_channels, num_anchors, num_classes, norm_layer)
|
| 87 |
+
self.regression_head = SSDLiteRegressionHead(in_channels, num_anchors, norm_layer)
|
| 88 |
+
|
| 89 |
+
def forward(self, x: List[Tensor]) -> Dict[str, Tensor]:
|
| 90 |
+
return {
|
| 91 |
+
"bbox_regression": self.regression_head(x),
|
| 92 |
+
"cls_logits": self.classification_head(x),
|
| 93 |
+
}
|
| 94 |
+
|
| 95 |
+
|
| 96 |
+
class SSDLiteClassificationHead(SSDScoringHead):
|
| 97 |
+
def __init__(
|
| 98 |
+
self, in_channels: List[int], num_anchors: List[int], num_classes: int, norm_layer: Callable[..., nn.Module]
|
| 99 |
+
):
|
| 100 |
+
cls_logits = nn.ModuleList()
|
| 101 |
+
for channels, anchors in zip(in_channels, num_anchors):
|
| 102 |
+
cls_logits.append(_prediction_block(channels, num_classes * anchors, 3, norm_layer))
|
| 103 |
+
_normal_init(cls_logits)
|
| 104 |
+
super().__init__(cls_logits, num_classes)
|
| 105 |
+
|
| 106 |
+
|
| 107 |
+
class SSDLiteRegressionHead(SSDScoringHead):
|
| 108 |
+
def __init__(self, in_channels: List[int], num_anchors: List[int], norm_layer: Callable[..., nn.Module]):
|
| 109 |
+
bbox_reg = nn.ModuleList()
|
| 110 |
+
for channels, anchors in zip(in_channels, num_anchors):
|
| 111 |
+
bbox_reg.append(_prediction_block(channels, 4 * anchors, 3, norm_layer))
|
| 112 |
+
_normal_init(bbox_reg)
|
| 113 |
+
super().__init__(bbox_reg, 4)
|
| 114 |
+
|
| 115 |
+
|
| 116 |
+
class SSDLiteFeatureExtractorMobileNet(nn.Module):
|
| 117 |
+
def __init__(
|
| 118 |
+
self,
|
| 119 |
+
backbone: nn.Module,
|
| 120 |
+
c4_pos: int,
|
| 121 |
+
norm_layer: Callable[..., nn.Module],
|
| 122 |
+
width_mult: float = 1.0,
|
| 123 |
+
min_depth: int = 16,
|
| 124 |
+
):
|
| 125 |
+
super().__init__()
|
| 126 |
+
_log_api_usage_once(self)
|
| 127 |
+
|
| 128 |
+
if backbone[c4_pos].use_res_connect:
|
| 129 |
+
raise ValueError("backbone[c4_pos].use_res_connect should be False")
|
| 130 |
+
|
| 131 |
+
self.features = nn.Sequential(
|
| 132 |
+
# As described in section 6.3 of MobileNetV3 paper
|
| 133 |
+
nn.Sequential(*backbone[:c4_pos], backbone[c4_pos].block[0]), # from start until C4 expansion layer
|
| 134 |
+
nn.Sequential(backbone[c4_pos].block[1:], *backbone[c4_pos + 1 :]), # from C4 depthwise until end
|
| 135 |
+
)
|
| 136 |
+
|
| 137 |
+
get_depth = lambda d: max(min_depth, int(d * width_mult)) # noqa: E731
|
| 138 |
+
extra = nn.ModuleList(
|
| 139 |
+
[
|
| 140 |
+
_extra_block(backbone[-1].out_channels, get_depth(512), norm_layer),
|
| 141 |
+
_extra_block(get_depth(512), get_depth(256), norm_layer),
|
| 142 |
+
_extra_block(get_depth(256), get_depth(256), norm_layer),
|
| 143 |
+
_extra_block(get_depth(256), get_depth(128), norm_layer),
|
| 144 |
+
]
|
| 145 |
+
)
|
| 146 |
+
_normal_init(extra)
|
| 147 |
+
|
| 148 |
+
self.extra = extra
|
| 149 |
+
|
| 150 |
+
def forward(self, x: Tensor) -> Dict[str, Tensor]:
|
| 151 |
+
# Get feature maps from backbone and extra. Can't be refactored due to JIT limitations.
|
| 152 |
+
output = []
|
| 153 |
+
for block in self.features:
|
| 154 |
+
x = block(x)
|
| 155 |
+
output.append(x)
|
| 156 |
+
|
| 157 |
+
for block in self.extra:
|
| 158 |
+
x = block(x)
|
| 159 |
+
output.append(x)
|
| 160 |
+
|
| 161 |
+
return OrderedDict([(str(i), v) for i, v in enumerate(output)])
|
| 162 |
+
|
| 163 |
+
|
| 164 |
+
def _mobilenet_extractor(
|
| 165 |
+
backbone: Union[mobilenet.MobileNetV2, mobilenet.MobileNetV3],
|
| 166 |
+
trainable_layers: int,
|
| 167 |
+
norm_layer: Callable[..., nn.Module],
|
| 168 |
+
):
|
| 169 |
+
backbone = backbone.features
|
| 170 |
+
# Gather the indices of blocks which are strided. These are the locations of C1, ..., Cn-1 blocks.
|
| 171 |
+
# The first and last blocks are always included because they are the C0 (conv1) and Cn.
|
| 172 |
+
stage_indices = [0] + [i for i, b in enumerate(backbone) if getattr(b, "_is_cn", False)] + [len(backbone) - 1]
|
| 173 |
+
num_stages = len(stage_indices)
|
| 174 |
+
|
| 175 |
+
# find the index of the layer from which we wont freeze
|
| 176 |
+
if not 0 <= trainable_layers <= num_stages:
|
| 177 |
+
raise ValueError("trainable_layers should be in the range [0, {num_stages}], instead got {trainable_layers}")
|
| 178 |
+
freeze_before = len(backbone) if trainable_layers == 0 else stage_indices[num_stages - trainable_layers]
|
| 179 |
+
|
| 180 |
+
for b in backbone[:freeze_before]:
|
| 181 |
+
for parameter in b.parameters():
|
| 182 |
+
parameter.requires_grad_(False)
|
| 183 |
+
|
| 184 |
+
return SSDLiteFeatureExtractorMobileNet(backbone, stage_indices[-2], norm_layer)
|
| 185 |
+
|
| 186 |
+
|
| 187 |
+
class SSDLite320_MobileNet_V3_Large_Weights(WeightsEnum):
|
| 188 |
+
COCO_V1 = Weights(
|
| 189 |
+
url="https://download.pytorch.org/models/ssdlite320_mobilenet_v3_large_coco-a79551df.pth",
|
| 190 |
+
transforms=ObjectDetection,
|
| 191 |
+
meta={
|
| 192 |
+
"num_params": 3440060,
|
| 193 |
+
"categories": _COCO_CATEGORIES,
|
| 194 |
+
"min_size": (1, 1),
|
| 195 |
+
"recipe": "https://github.com/pytorch/vision/tree/main/references/detection#ssdlite320-mobilenetv3-large",
|
| 196 |
+
"_metrics": {
|
| 197 |
+
"COCO-val2017": {
|
| 198 |
+
"box_map": 21.3,
|
| 199 |
+
}
|
| 200 |
+
},
|
| 201 |
+
"_docs": """These weights were produced by following a similar training recipe as on the paper.""",
|
| 202 |
+
},
|
| 203 |
+
)
|
| 204 |
+
DEFAULT = COCO_V1
|
| 205 |
+
|
| 206 |
+
|
| 207 |
+
@handle_legacy_interface(
|
| 208 |
+
weights=("pretrained", SSDLite320_MobileNet_V3_Large_Weights.COCO_V1),
|
| 209 |
+
weights_backbone=("pretrained_backbone", MobileNet_V3_Large_Weights.IMAGENET1K_V1),
|
| 210 |
+
)
|
| 211 |
+
def ssdlite320_mobilenet_v3_large(
|
| 212 |
+
*,
|
| 213 |
+
weights: Optional[SSDLite320_MobileNet_V3_Large_Weights] = None,
|
| 214 |
+
progress: bool = True,
|
| 215 |
+
num_classes: Optional[int] = None,
|
| 216 |
+
weights_backbone: Optional[MobileNet_V3_Large_Weights] = MobileNet_V3_Large_Weights.IMAGENET1K_V1,
|
| 217 |
+
trainable_backbone_layers: Optional[int] = None,
|
| 218 |
+
norm_layer: Optional[Callable[..., nn.Module]] = None,
|
| 219 |
+
**kwargs: Any,
|
| 220 |
+
) -> SSD:
|
| 221 |
+
"""SSDlite model architecture with input size 320x320 and a MobileNetV3 Large backbone, as
|
| 222 |
+
described at `Searching for MobileNetV3 <https://arxiv.org/abs/1905.02244>`__ and
|
| 223 |
+
`MobileNetV2: Inverted Residuals and Linear Bottlenecks <https://arxiv.org/abs/1801.04381>`__.
|
| 224 |
+
|
| 225 |
+
.. betastatus:: detection module
|
| 226 |
+
|
| 227 |
+
See :func:`~torchvision.models.detection.ssd300_vgg16` for more details.
|
| 228 |
+
|
| 229 |
+
Example:
|
| 230 |
+
|
| 231 |
+
>>> model = torchvision.models.detection.ssdlite320_mobilenet_v3_large(weights=SSDLite320_MobileNet_V3_Large_Weights.DEFAULT)
|
| 232 |
+
>>> model.eval()
|
| 233 |
+
>>> x = [torch.rand(3, 320, 320), torch.rand(3, 500, 400)]
|
| 234 |
+
>>> predictions = model(x)
|
| 235 |
+
|
| 236 |
+
Args:
|
| 237 |
+
weights (:class:`~torchvision.models.detection.SSDLite320_MobileNet_V3_Large_Weights`, optional): The
|
| 238 |
+
pretrained weights to use. See
|
| 239 |
+
:class:`~torchvision.models.detection.SSDLite320_MobileNet_V3_Large_Weights` below for
|
| 240 |
+
more details, and possible values. By default, no pre-trained
|
| 241 |
+
weights are used.
|
| 242 |
+
progress (bool, optional): If True, displays a progress bar of the
|
| 243 |
+
download to stderr. Default is True.
|
| 244 |
+
num_classes (int, optional): number of output classes of the model
|
| 245 |
+
(including the background).
|
| 246 |
+
weights_backbone (:class:`~torchvision.models.MobileNet_V3_Large_Weights`, optional): The pretrained
|
| 247 |
+
weights for the backbone.
|
| 248 |
+
trainable_backbone_layers (int, optional): number of trainable (not frozen) layers
|
| 249 |
+
starting from final block. Valid values are between 0 and 6, with 6 meaning all
|
| 250 |
+
backbone layers are trainable. If ``None`` is passed (the default) this value is
|
| 251 |
+
set to 6.
|
| 252 |
+
norm_layer (callable, optional): Module specifying the normalization layer to use.
|
| 253 |
+
**kwargs: parameters passed to the ``torchvision.models.detection.ssd.SSD``
|
| 254 |
+
base class. Please refer to the `source code
|
| 255 |
+
<https://github.com/pytorch/vision/blob/main/torchvision/models/detection/ssd.py>`_
|
| 256 |
+
for more details about this class.
|
| 257 |
+
|
| 258 |
+
.. autoclass:: torchvision.models.detection.SSDLite320_MobileNet_V3_Large_Weights
|
| 259 |
+
:members:
|
| 260 |
+
"""
|
| 261 |
+
|
| 262 |
+
weights = SSDLite320_MobileNet_V3_Large_Weights.verify(weights)
|
| 263 |
+
weights_backbone = MobileNet_V3_Large_Weights.verify(weights_backbone)
|
| 264 |
+
|
| 265 |
+
if "size" in kwargs:
|
| 266 |
+
warnings.warn("The size of the model is already fixed; ignoring the parameter.")
|
| 267 |
+
|
| 268 |
+
if weights is not None:
|
| 269 |
+
weights_backbone = None
|
| 270 |
+
num_classes = _ovewrite_value_param(num_classes, len(weights.meta["categories"]))
|
| 271 |
+
elif num_classes is None:
|
| 272 |
+
num_classes = 91
|
| 273 |
+
|
| 274 |
+
trainable_backbone_layers = _validate_trainable_layers(
|
| 275 |
+
weights is not None or weights_backbone is not None, trainable_backbone_layers, 6, 6
|
| 276 |
+
)
|
| 277 |
+
|
| 278 |
+
# Enable reduced tail if no pretrained backbone is selected. See Table 6 of MobileNetV3 paper.
|
| 279 |
+
reduce_tail = weights_backbone is None
|
| 280 |
+
|
| 281 |
+
if norm_layer is None:
|
| 282 |
+
norm_layer = partial(nn.BatchNorm2d, eps=0.001, momentum=0.03)
|
| 283 |
+
|
| 284 |
+
backbone = mobilenet_v3_large(
|
| 285 |
+
weights=weights_backbone, progress=progress, norm_layer=norm_layer, reduced_tail=reduce_tail, **kwargs
|
| 286 |
+
)
|
| 287 |
+
if weights_backbone is None:
|
| 288 |
+
# Change the default initialization scheme if not pretrained
|
| 289 |
+
_normal_init(backbone)
|
| 290 |
+
backbone = _mobilenet_extractor(
|
| 291 |
+
backbone,
|
| 292 |
+
trainable_backbone_layers,
|
| 293 |
+
norm_layer,
|
| 294 |
+
)
|
| 295 |
+
|
| 296 |
+
size = (320, 320)
|
| 297 |
+
anchor_generator = DefaultBoxGenerator([[2, 3] for _ in range(6)], min_ratio=0.2, max_ratio=0.95)
|
| 298 |
+
out_channels = det_utils.retrieve_out_channels(backbone, size)
|
| 299 |
+
num_anchors = anchor_generator.num_anchors_per_location()
|
| 300 |
+
if len(out_channels) != len(anchor_generator.aspect_ratios):
|
| 301 |
+
raise ValueError(
|
| 302 |
+
f"The length of the output channels from the backbone {len(out_channels)} do not match the length of the anchor generator aspect ratios {len(anchor_generator.aspect_ratios)}"
|
| 303 |
+
)
|
| 304 |
+
|
| 305 |
+
defaults = {
|
| 306 |
+
"score_thresh": 0.001,
|
| 307 |
+
"nms_thresh": 0.55,
|
| 308 |
+
"detections_per_img": 300,
|
| 309 |
+
"topk_candidates": 300,
|
| 310 |
+
# Rescale the input in a way compatible to the backbone:
|
| 311 |
+
# The following mean/std rescale the data from [0, 1] to [-1, 1]
|
| 312 |
+
"image_mean": [0.5, 0.5, 0.5],
|
| 313 |
+
"image_std": [0.5, 0.5, 0.5],
|
| 314 |
+
}
|
| 315 |
+
kwargs: Any = {**defaults, **kwargs}
|
| 316 |
+
model = SSD(
|
| 317 |
+
backbone,
|
| 318 |
+
anchor_generator,
|
| 319 |
+
size,
|
| 320 |
+
num_classes,
|
| 321 |
+
head=SSDLiteHead(out_channels, num_anchors, num_classes, norm_layer),
|
| 322 |
+
**kwargs,
|
| 323 |
+
)
|
| 324 |
+
|
| 325 |
+
if weights is not None:
|
| 326 |
+
model.load_state_dict(weights.get_state_dict(progress=progress))
|
| 327 |
+
|
| 328 |
+
return model
|
| 329 |
+
|
| 330 |
+
|
| 331 |
+
# The dictionary below is internal implementation detail and will be removed in v0.15
|
| 332 |
+
from .._utils import _ModelURLs
|
| 333 |
+
|
| 334 |
+
|
| 335 |
+
model_urls = _ModelURLs(
|
| 336 |
+
{
|
| 337 |
+
"ssdlite320_mobilenet_v3_large_coco": SSDLite320_MobileNet_V3_Large_Weights.COCO_V1.url,
|
| 338 |
+
}
|
| 339 |
+
)
|
my_container_sandbox/workspace/anaconda3/lib/python3.8/site-packages/torchvision/models/detection/transform.py
ADDED
|
@@ -0,0 +1,309 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
import math
|
| 2 |
+
from typing import List, Tuple, Dict, Optional, Any
|
| 3 |
+
|
| 4 |
+
import torch
|
| 5 |
+
import torchvision
|
| 6 |
+
from torch import nn, Tensor
|
| 7 |
+
|
| 8 |
+
from .image_list import ImageList
|
| 9 |
+
from .roi_heads import paste_masks_in_image
|
| 10 |
+
|
| 11 |
+
|
| 12 |
+
@torch.jit.unused
|
| 13 |
+
def _get_shape_onnx(image: Tensor) -> Tensor:
|
| 14 |
+
from torch.onnx import operators
|
| 15 |
+
|
| 16 |
+
return operators.shape_as_tensor(image)[-2:]
|
| 17 |
+
|
| 18 |
+
|
| 19 |
+
@torch.jit.unused
|
| 20 |
+
def _fake_cast_onnx(v: Tensor) -> float:
|
| 21 |
+
# ONNX requires a tensor but here we fake its type for JIT.
|
| 22 |
+
return v
|
| 23 |
+
|
| 24 |
+
|
| 25 |
+
def _resize_image_and_masks(
|
| 26 |
+
image: Tensor,
|
| 27 |
+
self_min_size: float,
|
| 28 |
+
self_max_size: float,
|
| 29 |
+
target: Optional[Dict[str, Tensor]] = None,
|
| 30 |
+
fixed_size: Optional[Tuple[int, int]] = None,
|
| 31 |
+
) -> Tuple[Tensor, Optional[Dict[str, Tensor]]]:
|
| 32 |
+
if torchvision._is_tracing():
|
| 33 |
+
im_shape = _get_shape_onnx(image)
|
| 34 |
+
else:
|
| 35 |
+
im_shape = torch.tensor(image.shape[-2:])
|
| 36 |
+
|
| 37 |
+
size: Optional[List[int]] = None
|
| 38 |
+
scale_factor: Optional[float] = None
|
| 39 |
+
recompute_scale_factor: Optional[bool] = None
|
| 40 |
+
if fixed_size is not None:
|
| 41 |
+
size = [fixed_size[1], fixed_size[0]]
|
| 42 |
+
else:
|
| 43 |
+
min_size = torch.min(im_shape).to(dtype=torch.float32)
|
| 44 |
+
max_size = torch.max(im_shape).to(dtype=torch.float32)
|
| 45 |
+
scale = torch.min(self_min_size / min_size, self_max_size / max_size)
|
| 46 |
+
|
| 47 |
+
if torchvision._is_tracing():
|
| 48 |
+
scale_factor = _fake_cast_onnx(scale)
|
| 49 |
+
else:
|
| 50 |
+
scale_factor = scale.item()
|
| 51 |
+
recompute_scale_factor = True
|
| 52 |
+
|
| 53 |
+
image = torch.nn.functional.interpolate(
|
| 54 |
+
image[None],
|
| 55 |
+
size=size,
|
| 56 |
+
scale_factor=scale_factor,
|
| 57 |
+
mode="bilinear",
|
| 58 |
+
recompute_scale_factor=recompute_scale_factor,
|
| 59 |
+
align_corners=False,
|
| 60 |
+
)[0]
|
| 61 |
+
|
| 62 |
+
if target is None:
|
| 63 |
+
return image, target
|
| 64 |
+
|
| 65 |
+
if "masks" in target:
|
| 66 |
+
mask = target["masks"]
|
| 67 |
+
mask = torch.nn.functional.interpolate(
|
| 68 |
+
mask[:, None].float(), size=size, scale_factor=scale_factor, recompute_scale_factor=recompute_scale_factor
|
| 69 |
+
)[:, 0].byte()
|
| 70 |
+
target["masks"] = mask
|
| 71 |
+
return image, target
|
| 72 |
+
|
| 73 |
+
|
| 74 |
+
class GeneralizedRCNNTransform(nn.Module):
|
| 75 |
+
"""
|
| 76 |
+
Performs input / target transformation before feeding the data to a GeneralizedRCNN
|
| 77 |
+
model.
|
| 78 |
+
|
| 79 |
+
The transformations it perform are:
|
| 80 |
+
- input normalization (mean subtraction and std division)
|
| 81 |
+
- input / target resizing to match min_size / max_size
|
| 82 |
+
|
| 83 |
+
It returns a ImageList for the inputs, and a List[Dict[Tensor]] for the targets
|
| 84 |
+
"""
|
| 85 |
+
|
| 86 |
+
def __init__(
|
| 87 |
+
self,
|
| 88 |
+
min_size: int,
|
| 89 |
+
max_size: int,
|
| 90 |
+
image_mean: List[float],
|
| 91 |
+
image_std: List[float],
|
| 92 |
+
size_divisible: int = 32,
|
| 93 |
+
fixed_size: Optional[Tuple[int, int]] = None,
|
| 94 |
+
**kwargs: Any,
|
| 95 |
+
):
|
| 96 |
+
super().__init__()
|
| 97 |
+
if not isinstance(min_size, (list, tuple)):
|
| 98 |
+
min_size = (min_size,)
|
| 99 |
+
self.min_size = min_size
|
| 100 |
+
self.max_size = max_size
|
| 101 |
+
self.image_mean = image_mean
|
| 102 |
+
self.image_std = image_std
|
| 103 |
+
self.size_divisible = size_divisible
|
| 104 |
+
self.fixed_size = fixed_size
|
| 105 |
+
self._skip_resize = kwargs.pop("_skip_resize", False)
|
| 106 |
+
|
| 107 |
+
def forward(
|
| 108 |
+
self, images: List[Tensor], targets: Optional[List[Dict[str, Tensor]]] = None
|
| 109 |
+
) -> Tuple[ImageList, Optional[List[Dict[str, Tensor]]]]:
|
| 110 |
+
images = [img for img in images]
|
| 111 |
+
if targets is not None:
|
| 112 |
+
# make a copy of targets to avoid modifying it in-place
|
| 113 |
+
# once torchscript supports dict comprehension
|
| 114 |
+
# this can be simplified as follows
|
| 115 |
+
# targets = [{k: v for k,v in t.items()} for t in targets]
|
| 116 |
+
targets_copy: List[Dict[str, Tensor]] = []
|
| 117 |
+
for t in targets:
|
| 118 |
+
data: Dict[str, Tensor] = {}
|
| 119 |
+
for k, v in t.items():
|
| 120 |
+
data[k] = v
|
| 121 |
+
targets_copy.append(data)
|
| 122 |
+
targets = targets_copy
|
| 123 |
+
for i in range(len(images)):
|
| 124 |
+
image = images[i]
|
| 125 |
+
target_index = targets[i] if targets is not None else None
|
| 126 |
+
|
| 127 |
+
if image.dim() != 3:
|
| 128 |
+
raise ValueError(f"images is expected to be a list of 3d tensors of shape [C, H, W], got {image.shape}")
|
| 129 |
+
image = self.normalize(image)
|
| 130 |
+
image, target_index = self.resize(image, target_index)
|
| 131 |
+
images[i] = image
|
| 132 |
+
if targets is not None and target_index is not None:
|
| 133 |
+
targets[i] = target_index
|
| 134 |
+
|
| 135 |
+
image_sizes = [img.shape[-2:] for img in images]
|
| 136 |
+
images = self.batch_images(images, size_divisible=self.size_divisible)
|
| 137 |
+
image_sizes_list: List[Tuple[int, int]] = []
|
| 138 |
+
for image_size in image_sizes:
|
| 139 |
+
torch._assert(
|
| 140 |
+
len(image_size) == 2,
|
| 141 |
+
f"Input tensors expected to have in the last two elements H and W, instead got {image_size}",
|
| 142 |
+
)
|
| 143 |
+
image_sizes_list.append((image_size[0], image_size[1]))
|
| 144 |
+
|
| 145 |
+
image_list = ImageList(images, image_sizes_list)
|
| 146 |
+
return image_list, targets
|
| 147 |
+
|
| 148 |
+
def normalize(self, image: Tensor) -> Tensor:
|
| 149 |
+
if not image.is_floating_point():
|
| 150 |
+
raise TypeError(
|
| 151 |
+
f"Expected input images to be of floating type (in range [0, 1]), "
|
| 152 |
+
f"but found type {image.dtype} instead"
|
| 153 |
+
)
|
| 154 |
+
dtype, device = image.dtype, image.device
|
| 155 |
+
mean = torch.as_tensor(self.image_mean, dtype=dtype, device=device)
|
| 156 |
+
std = torch.as_tensor(self.image_std, dtype=dtype, device=device)
|
| 157 |
+
return (image - mean[:, None, None]) / std[:, None, None]
|
| 158 |
+
|
| 159 |
+
def torch_choice(self, k: List[int]) -> int:
|
| 160 |
+
"""
|
| 161 |
+
Implements `random.choice` via torch ops so it can be compiled with
|
| 162 |
+
TorchScript. Remove if https://github.com/pytorch/pytorch/issues/25803
|
| 163 |
+
is fixed.
|
| 164 |
+
"""
|
| 165 |
+
index = int(torch.empty(1).uniform_(0.0, float(len(k))).item())
|
| 166 |
+
return k[index]
|
| 167 |
+
|
| 168 |
+
def resize(
|
| 169 |
+
self,
|
| 170 |
+
image: Tensor,
|
| 171 |
+
target: Optional[Dict[str, Tensor]] = None,
|
| 172 |
+
) -> Tuple[Tensor, Optional[Dict[str, Tensor]]]:
|
| 173 |
+
h, w = image.shape[-2:]
|
| 174 |
+
if self.training:
|
| 175 |
+
if self._skip_resize:
|
| 176 |
+
return image, target
|
| 177 |
+
size = float(self.torch_choice(self.min_size))
|
| 178 |
+
else:
|
| 179 |
+
# FIXME assume for now that testing uses the largest scale
|
| 180 |
+
size = float(self.min_size[-1])
|
| 181 |
+
image, target = _resize_image_and_masks(image, size, float(self.max_size), target, self.fixed_size)
|
| 182 |
+
|
| 183 |
+
if target is None:
|
| 184 |
+
return image, target
|
| 185 |
+
|
| 186 |
+
bbox = target["boxes"]
|
| 187 |
+
bbox = resize_boxes(bbox, (h, w), image.shape[-2:])
|
| 188 |
+
target["boxes"] = bbox
|
| 189 |
+
|
| 190 |
+
if "keypoints" in target:
|
| 191 |
+
keypoints = target["keypoints"]
|
| 192 |
+
keypoints = resize_keypoints(keypoints, (h, w), image.shape[-2:])
|
| 193 |
+
target["keypoints"] = keypoints
|
| 194 |
+
return image, target
|
| 195 |
+
|
| 196 |
+
# _onnx_batch_images() is an implementation of
|
| 197 |
+
# batch_images() that is supported by ONNX tracing.
|
| 198 |
+
@torch.jit.unused
|
| 199 |
+
def _onnx_batch_images(self, images: List[Tensor], size_divisible: int = 32) -> Tensor:
|
| 200 |
+
max_size = []
|
| 201 |
+
for i in range(images[0].dim()):
|
| 202 |
+
max_size_i = torch.max(torch.stack([img.shape[i] for img in images]).to(torch.float32)).to(torch.int64)
|
| 203 |
+
max_size.append(max_size_i)
|
| 204 |
+
stride = size_divisible
|
| 205 |
+
max_size[1] = (torch.ceil((max_size[1].to(torch.float32)) / stride) * stride).to(torch.int64)
|
| 206 |
+
max_size[2] = (torch.ceil((max_size[2].to(torch.float32)) / stride) * stride).to(torch.int64)
|
| 207 |
+
max_size = tuple(max_size)
|
| 208 |
+
|
| 209 |
+
# work around for
|
| 210 |
+
# pad_img[: img.shape[0], : img.shape[1], : img.shape[2]].copy_(img)
|
| 211 |
+
# which is not yet supported in onnx
|
| 212 |
+
padded_imgs = []
|
| 213 |
+
for img in images:
|
| 214 |
+
padding = [(s1 - s2) for s1, s2 in zip(max_size, tuple(img.shape))]
|
| 215 |
+
padded_img = torch.nn.functional.pad(img, (0, padding[2], 0, padding[1], 0, padding[0]))
|
| 216 |
+
padded_imgs.append(padded_img)
|
| 217 |
+
|
| 218 |
+
return torch.stack(padded_imgs)
|
| 219 |
+
|
| 220 |
+
def max_by_axis(self, the_list: List[List[int]]) -> List[int]:
|
| 221 |
+
maxes = the_list[0]
|
| 222 |
+
for sublist in the_list[1:]:
|
| 223 |
+
for index, item in enumerate(sublist):
|
| 224 |
+
maxes[index] = max(maxes[index], item)
|
| 225 |
+
return maxes
|
| 226 |
+
|
| 227 |
+
def batch_images(self, images: List[Tensor], size_divisible: int = 32) -> Tensor:
|
| 228 |
+
if torchvision._is_tracing():
|
| 229 |
+
# batch_images() does not export well to ONNX
|
| 230 |
+
# call _onnx_batch_images() instead
|
| 231 |
+
return self._onnx_batch_images(images, size_divisible)
|
| 232 |
+
|
| 233 |
+
max_size = self.max_by_axis([list(img.shape) for img in images])
|
| 234 |
+
stride = float(size_divisible)
|
| 235 |
+
max_size = list(max_size)
|
| 236 |
+
max_size[1] = int(math.ceil(float(max_size[1]) / stride) * stride)
|
| 237 |
+
max_size[2] = int(math.ceil(float(max_size[2]) / stride) * stride)
|
| 238 |
+
|
| 239 |
+
batch_shape = [len(images)] + max_size
|
| 240 |
+
batched_imgs = images[0].new_full(batch_shape, 0)
|
| 241 |
+
for i in range(batched_imgs.shape[0]):
|
| 242 |
+
img = images[i]
|
| 243 |
+
batched_imgs[i, : img.shape[0], : img.shape[1], : img.shape[2]].copy_(img)
|
| 244 |
+
|
| 245 |
+
return batched_imgs
|
| 246 |
+
|
| 247 |
+
def postprocess(
|
| 248 |
+
self,
|
| 249 |
+
result: List[Dict[str, Tensor]],
|
| 250 |
+
image_shapes: List[Tuple[int, int]],
|
| 251 |
+
original_image_sizes: List[Tuple[int, int]],
|
| 252 |
+
) -> List[Dict[str, Tensor]]:
|
| 253 |
+
if self.training:
|
| 254 |
+
return result
|
| 255 |
+
for i, (pred, im_s, o_im_s) in enumerate(zip(result, image_shapes, original_image_sizes)):
|
| 256 |
+
boxes = pred["boxes"]
|
| 257 |
+
boxes = resize_boxes(boxes, im_s, o_im_s)
|
| 258 |
+
result[i]["boxes"] = boxes
|
| 259 |
+
if "masks" in pred:
|
| 260 |
+
masks = pred["masks"]
|
| 261 |
+
masks = paste_masks_in_image(masks, boxes, o_im_s)
|
| 262 |
+
result[i]["masks"] = masks
|
| 263 |
+
if "keypoints" in pred:
|
| 264 |
+
keypoints = pred["keypoints"]
|
| 265 |
+
keypoints = resize_keypoints(keypoints, im_s, o_im_s)
|
| 266 |
+
result[i]["keypoints"] = keypoints
|
| 267 |
+
return result
|
| 268 |
+
|
| 269 |
+
def __repr__(self) -> str:
|
| 270 |
+
format_string = f"{self.__class__.__name__}("
|
| 271 |
+
_indent = "\n "
|
| 272 |
+
format_string += f"{_indent}Normalize(mean={self.image_mean}, std={self.image_std})"
|
| 273 |
+
format_string += f"{_indent}Resize(min_size={self.min_size}, max_size={self.max_size}, mode='bilinear')"
|
| 274 |
+
format_string += "\n)"
|
| 275 |
+
return format_string
|
| 276 |
+
|
| 277 |
+
|
| 278 |
+
def resize_keypoints(keypoints: Tensor, original_size: List[int], new_size: List[int]) -> Tensor:
|
| 279 |
+
ratios = [
|
| 280 |
+
torch.tensor(s, dtype=torch.float32, device=keypoints.device)
|
| 281 |
+
/ torch.tensor(s_orig, dtype=torch.float32, device=keypoints.device)
|
| 282 |
+
for s, s_orig in zip(new_size, original_size)
|
| 283 |
+
]
|
| 284 |
+
ratio_h, ratio_w = ratios
|
| 285 |
+
resized_data = keypoints.clone()
|
| 286 |
+
if torch._C._get_tracing_state():
|
| 287 |
+
resized_data_0 = resized_data[:, :, 0] * ratio_w
|
| 288 |
+
resized_data_1 = resized_data[:, :, 1] * ratio_h
|
| 289 |
+
resized_data = torch.stack((resized_data_0, resized_data_1, resized_data[:, :, 2]), dim=2)
|
| 290 |
+
else:
|
| 291 |
+
resized_data[..., 0] *= ratio_w
|
| 292 |
+
resized_data[..., 1] *= ratio_h
|
| 293 |
+
return resized_data
|
| 294 |
+
|
| 295 |
+
|
| 296 |
+
def resize_boxes(boxes: Tensor, original_size: List[int], new_size: List[int]) -> Tensor:
|
| 297 |
+
ratios = [
|
| 298 |
+
torch.tensor(s, dtype=torch.float32, device=boxes.device)
|
| 299 |
+
/ torch.tensor(s_orig, dtype=torch.float32, device=boxes.device)
|
| 300 |
+
for s, s_orig in zip(new_size, original_size)
|
| 301 |
+
]
|
| 302 |
+
ratio_height, ratio_width = ratios
|
| 303 |
+
xmin, ymin, xmax, ymax = boxes.unbind(1)
|
| 304 |
+
|
| 305 |
+
xmin = xmin * ratio_width
|
| 306 |
+
xmax = xmax * ratio_width
|
| 307 |
+
ymin = ymin * ratio_height
|
| 308 |
+
ymax = ymax * ratio_height
|
| 309 |
+
return torch.stack((xmin, ymin, xmax, ymax), dim=1)
|
my_container_sandbox/workspace/anaconda3/lib/python3.8/site-packages/torchvision/models/optical_flow/__pycache__/__init__.cpython-38.pyc
ADDED
|
Binary file (204 Bytes). View file
|
|
|
my_container_sandbox/workspace/anaconda3/lib/python3.8/site-packages/torchvision/models/optical_flow/__pycache__/_utils.cpython-38.pyc
ADDED
|
Binary file (2.04 kB). View file
|
|
|
my_container_sandbox/workspace/anaconda3/lib/python3.8/site-packages/torchvision/models/optical_flow/__pycache__/raft.cpython-38.pyc
ADDED
|
Binary file (27 kB). View file
|
|
|
my_container_sandbox/workspace/anaconda3/lib/python3.8/site-packages/torchvision/models/quantization/__pycache__/__init__.cpython-38.pyc
ADDED
|
Binary file (293 Bytes). View file
|
|
|
my_container_sandbox/workspace/anaconda3/lib/python3.8/site-packages/torchvision/models/quantization/__pycache__/googlenet.cpython-38.pyc
ADDED
|
Binary file (8.06 kB). View file
|
|
|
my_container_sandbox/workspace/anaconda3/lib/python3.8/site-packages/torchvision/models/quantization/__pycache__/inception.cpython-38.pyc
ADDED
|
Binary file (10.8 kB). View file
|
|
|
my_container_sandbox/workspace/anaconda3/lib/python3.8/site-packages/torchvision/models/quantization/__pycache__/mobilenet.cpython-38.pyc
ADDED
|
Binary file (307 Bytes). View file
|
|
|
my_container_sandbox/workspace/anaconda3/lib/python3.8/site-packages/torchvision/models/quantization/__pycache__/mobilenetv2.cpython-38.pyc
ADDED
|
Binary file (6.25 kB). View file
|
|
|
my_container_sandbox/workspace/anaconda3/lib/python3.8/site-packages/torchvision/models/quantization/__pycache__/mobilenetv3.cpython-38.pyc
ADDED
|
Binary file (8.53 kB). View file
|
|
|
my_container_sandbox/workspace/anaconda3/lib/python3.8/site-packages/torchvision/models/segmentation/__pycache__/__init__.cpython-38.pyc
ADDED
|
Binary file (242 Bytes). View file
|
|
|
my_container_sandbox/workspace/anaconda3/lib/python3.8/site-packages/torchvision/models/segmentation/__pycache__/_utils.cpython-38.pyc
ADDED
|
Binary file (1.44 kB). View file
|
|
|
my_container_sandbox/workspace/anaconda3/lib/python3.8/site-packages/torchvision/models/segmentation/__pycache__/deeplabv3.cpython-38.pyc
ADDED
|
Binary file (12.4 kB). View file
|
|
|
my_container_sandbox/workspace/anaconda3/lib/python3.8/site-packages/torchvision/models/segmentation/__pycache__/fcn.cpython-38.pyc
ADDED
|
Binary file (7.79 kB). View file
|
|
|
my_container_sandbox/workspace/anaconda3/lib/python3.8/site-packages/torchvision/models/segmentation/__pycache__/lraspp.cpython-38.pyc
ADDED
|
Binary file (7.26 kB). View file
|
|
|
my_container_sandbox/workspace/anaconda3/lib/python3.8/site-packages/torchvision/models/segmentation/__pycache__/segmentation.cpython-38.pyc
ADDED
|
Binary file (431 Bytes). View file
|
|
|