jarjoura commited on
Commit
23e686a
·
verified ·
1 Parent(s): b846e48

Keep weights only: runtime lives in mlx-vlm; refresh card and validation

Browse files
README.md CHANGED
@@ -18,65 +18,50 @@ tags:
18
 
19
  # SAM 3D Objects — MLX BF16
20
 
21
- Frozen inference port of Meta SAM 3D Objects, including the upstream MoGe-v1
22
- depth estimator. Runtime and conversion require **MLX and Python's standard
23
- library only**. This is an unofficial derivative. SAM components retain the
24
- SAM License; MoGe and FlexiCubes retain Apache-2.0.
 
 
25
 
26
- The bundle includes 3,709 BF16 tensors, strict loading, MLX sparse convolutions,
27
- windowed attention, Gaussian and colored-mesh decoding, `mx.async_eval`, and
28
- synchronous/asynchronous streaming APIs. Both Gaussian heads are included.
29
- It generates geometry and pose; it has no text or tool-calling interface.
 
30
 
31
- ## Install the included runtime
32
 
33
- Download this repository, then install its small runtime package:
 
34
 
35
  ```sh
 
36
  hf download mlx-community/sam-3d-objects-bf16 --local-dir sam-3d-objects-bf16
37
- python -m pip install ./sam-3d-objects-bf16
38
- sam3d-mlx --model ./sam-3d-objects-bf16 \
39
- --image image.png --mask mask.png --output ./output \
40
- --formats gaussian mesh
41
  ```
42
 
43
- `hf` is an optional download tool; it is not a runtime dependency. The package
44
- can also be imported directly from the downloaded directory. The same model
45
- implementation lives under `mlx_vlm/models/sam3d_objects` in the port checkout.
46
-
47
  ```python
48
- from sam3d_objects_mlx.io import read_png, read_mask
49
- from sam3d_objects_mlx.pipeline import Pipeline, Request
50
 
51
  pipeline = Pipeline.from_pretrained("sam-3d-objects-bf16")
52
  image, mask = read_png("image.png")[..., :3], read_mask("mask.png")
53
  result = pipeline.generate(image, mask, seed=42)
54
-
55
- async def reconstruct(requests):
56
- async for event in pipeline.astream(requests):
57
- if event.stage == "complete":
58
- consume(event.data)
59
  ```
60
 
61
- `requests` is an iterable or async iterable of `Request` values. Input is
62
- consumed one request at a time with backpressure. Events preserve IDs and
63
- order and include every sampling step plus decoded outputs. The event loop
64
- remains responsive while one worker submits MLX work. Early `aclose()` or
65
- cancellation stops submissions and drains the worker. Arrays may still be
66
- evaluating on the GPU when yielded; use `mx.eval` before host consumption.
67
-
68
- Inputs are HWC RGB/RGBA MLX arrays and HW masks. The included file reader
69
- supports non-interlaced 8-bit PNG, including RGBA alpha masks. Callers can use
70
- other decoders and pass arrays directly. Automatic MoGe depth is included;
71
- external HWC point maps use +X left, +Y up, +Z forward, with NaN invalid values.
72
- Set `estimate_depth=False` to use the trained point-map dropout instead.
73
-
74
  Results include local-space Gaussians, mesh vertices/faces/colors, latent
75
  features, occupied voxels, and a separate pose (rotation, translation, scale).
76
- CLI outputs standard Gaussian PLY, colored OBJ, and raw safetensors.
77
- `--jsonl` consumes stdin request lines and emits ordered progress records.
78
- Training, latent training encoders, rendering, texture baking, mesh
79
- simplification, and iterative layout optimization are not included.
80
 
81
  ## Conversion and validation
82
 
@@ -94,20 +79,21 @@ FlexiCubes. Maximum absolute error was below 0.000002 on these fixtures;
94
  mesh face indices matched exactly. This is not a claim of bitwise BF16
95
  end-to-end parity or a reconstruction-quality evaluation.
96
 
97
- See `validation.json`, `conversion.json`, and the included source for details.
98
- The original sources are [SAM 3D Objects](https://github.com/facebookresearch/sam-3d-objects)
99
- and [MoGe](https://github.com/microsoft/MoGe).
100
 
101
  ### Local performance
102
 
103
- Hardware: Apple M5 Max, 128 GiB unified memory. Input: 448×672 RGB plus object mask; automatic MoGe
104
- depth, 25 structure steps and 25 latent steps, both Gaussian heads and mesh.
105
- One warmup was excluded; the same seed produced identical latent outputs.
 
106
 
107
  | Metric | Result |
108
  | --- | ---: |
109
- | Median latency (3 runs) | 20.65 s |
110
- | Maximum peak memory | 10.56 GB |
111
 
112
  Two additional async requests matched the synchronous latent output while
113
  the event-loop heartbeat continued. These measurements describe one input.
 
18
 
19
  # SAM 3D Objects — MLX BF16
20
 
21
+ BF16 weights of Meta SAM 3D Objects, including the upstream MoGe-v1 depth
22
+ estimator, converted for the MLX runtime in
23
+ [mlx-vlm](https://github.com/Blaizzy/mlx-vlm) (`mlx_vlm/models/sam3d_objects`).
24
+ This repository holds weights and metadata only; the inference code lives in
25
+ mlx-vlm. This is an unofficial derivative. SAM components retain the SAM
26
+ License; MoGe retains MIT; FlexiCubes retains Apache-2.0.
27
 
28
+ The 3,709 BF16 tensors across seven safetensors files cover the structure and
29
+ pose flow model, the sparse-latent flow model, image and point-map
30
+ conditioners, the occupancy decoder, both Gaussian decoders (32 or 4 Gaussians
31
+ per voxel), the mesh decoder, and MoGe-v1. The model generates geometry and
32
+ pose; it has no text or tool-calling interface.
33
 
34
+ ## Usage
35
 
36
+ Install an mlx-vlm version that includes `sam3d_objects`, download this
37
+ repository, and run the CLI or the Python API:
38
 
39
  ```sh
40
+ pip install mlx-vlm
41
  hf download mlx-community/sam-3d-objects-bf16 --local-dir sam-3d-objects-bf16
42
+ python -m mlx_vlm.models.sam3d_objects.generate \
43
+ --model sam-3d-objects-bf16 --image image.png --mask mask.png \
44
+ --output output --formats gaussian mesh
 
45
  ```
46
 
 
 
 
 
47
  ```python
48
+ from mlx_vlm.models.sam3d_objects.io import read_mask, read_png
49
+ from mlx_vlm.models.sam3d_objects.pipeline import Pipeline
50
 
51
  pipeline = Pipeline.from_pretrained("sam-3d-objects-bf16")
52
  image, mask = read_png("image.png")[..., :3], read_mask("mask.png")
53
  result = pipeline.generate(image, mask, seed=42)
 
 
 
 
 
54
  ```
55
 
56
+ `pipeline.astream(requests)` streams ordered events for an iterable or async
57
+ iterable of `Request` values, one request at a time with backpressure; the
58
+ same weights also load through `mlx_vlm.utils.load_model`. Inputs are HWC
59
+ RGB/RGBA MLX arrays and HW masks. Automatic MoGe depth is included; external
60
+ HWC point maps use +X left, +Y up, +Z forward with NaN for invalid points.
 
 
 
 
 
 
 
 
61
  Results include local-space Gaussians, mesh vertices/faces/colors, latent
62
  features, occupied voxels, and a separate pose (rotation, translation, scale).
63
+ The mlx-vlm model README documents the full API, CLI, JSONL streaming, and
64
+ the inference optimizations.
 
 
65
 
66
  ## Conversion and validation
67
 
 
79
  mesh face indices matched exactly. This is not a claim of bitwise BF16
80
  end-to-end parity or a reconstruction-quality evaluation.
81
 
82
+ See `validation.json` and `conversion.json` for details. The original sources
83
+ are [SAM 3D Objects](https://github.com/facebookresearch/sam-3d-objects) and
84
+ [MoGe](https://github.com/microsoft/MoGe).
85
 
86
  ### Local performance
87
 
88
+ Hardware: Apple M5 Max, 128 GiB unified memory. Input: 448×672 RGB plus object mask; automatic
89
+ MoGe depth, 25 structure steps and 25 latent steps, both Gaussian heads
90
+ and mesh. One warmup was excluded; the same seed produced identical latent
91
+ outputs.
92
 
93
  | Metric | Result |
94
  | --- | ---: |
95
+ | Median latency (3 runs) | 15.68 s |
96
+ | Maximum peak memory | 10.98 GB |
97
 
98
  Two additional async requests matched the synchronous latent output while
99
  the event-loop heartbeat continued. These measurements describe one input.
pyproject.toml DELETED
@@ -1,20 +0,0 @@
1
- [build-system]
2
- requires = ["setuptools>=61"]
3
- build-backend = "setuptools.build_meta"
4
-
5
- [project]
6
- name = "sam3d-objects-mlx-inference"
7
- version = "0.1.0"
8
- description = "Frozen SAM 3D Objects inference using MLX and the Python standard library"
9
- requires-python = ">=3.10"
10
- dependencies = ["mlx>=0.32.2"]
11
- license = {text = "SAM License; Apache-2.0 for MoGe/FlexiCubes"}
12
-
13
- [project.scripts]
14
- sam3d-mlx = "sam3d_objects_mlx.generate:main"
15
-
16
- [tool.setuptools.packages.find]
17
- include = ["sam3d_objects_mlx*"]
18
-
19
- [tool.setuptools.package-data]
20
- sam3d_objects_mlx = ["LICENSE", "LICENSE-APACHE", "README.md"]
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
sam3d_objects_mlx/LICENSE DELETED
@@ -1,52 +0,0 @@
1
- SAM License
2
- Last Updated: November 19, 2025
3
-
4
- “Agreement” means the terms and conditions for use, reproduction, distribution and modification of the SAM Materials set forth herein.
5
-
6
- “SAM Materials” means, collectively, Documentation and the models, software and algorithms, including machine-learning model code, trained model weights, inference-enabling code, training-enabling code, fine-tuning enabling code, and other elements of the foregoing distributed by Meta and made available under this Agreement.
7
-
8
- “Documentation” means the specifications, manuals and documentation accompanying
9
- SAM Materials distributed by Meta.
10
-
11
- “Licensee” or “you” means you, or your employer or any other person or entity (if you are entering into this Agreement on such person or entity’s behalf), of the age required under applicable laws, rules or regulations to provide legal consent and that has legal authority to bind your employer or such other person or entity if you are entering in this Agreement on their behalf.
12
-
13
- “Meta” or “we” means Meta Platforms Ireland Limited (if you are located in or, if you are an entity, your principal place of business is in the EEA or Switzerland) or Meta Platforms, Inc. (if you are located outside of the EEA or Switzerland).
14
-
15
- “Sanctions” means any economic or trade sanctions or restrictions administered or enforced by the United States (including the Office of Foreign Assets Control of the U.S. Department of the Treasury (“OFAC”), the U.S. Department of State and the U.S. Department of Commerce), the United Nations, the European Union, or the United Kingdom.
16
-
17
- “Trade Controls” means any of the following: Sanctions and applicable export and import controls.
18
-
19
- By using or distributing any portion or element of the SAM Materials, you agree to be bound by this Agreement.
20
-
21
- 1. License Rights and Redistribution.
22
-
23
- a. Grant of Rights. You are granted a non-exclusive, worldwide, non-transferable and royalty-free limited license under Meta’s intellectual property or other rights owned by Meta embodied in the SAM Materials to use, reproduce, distribute, copy, create derivative works of, and make modifications to the SAM Materials.
24
-
25
- i. Grant of Patent License. Subject to the terms and conditions of this License, you are granted a perpetual, worldwide, non-exclusive, no-charge, royalty-free, irrevocable (except as stated in this section) patent license to make, have made, use, offer to sell, sell, import, and otherwise transfer the Work, where such license applies only to those patent claims licensable by Meta that are necessarily infringed alone or by combination of their contribution(s) with the SAM 3 Materials. If you institute patent litigation against any entity (including a cross-claim or counterclaim in a lawsuit) alleging that the SAM 3 Materials incorporated within the work constitutes direct or contributory patent infringement, then any patent licenses granted to you under this License for that work shall terminate as of the date such litigation is filed.
26
-
27
- b. Redistribution and Use.
28
-
29
- i. Distribution of SAM Materials, and any derivative works thereof, are subject to the terms of this Agreement. If you distribute or make the SAM Materials, or any derivative works thereof, available to a third party, you may only do so under the terms of this Agreement and you shall provide a copy of this Agreement with any such SAM Materials.
30
-
31
- ii. If you submit for publication the results of research you perform on, using, or otherwise in connection with SAM Materials, you must acknowledge the use of SAM Materials in your publication.
32
-
33
- iii. Your use of the SAM Materials must comply with applicable laws and regulations, including Trade Control Laws and applicable privacy and data protection laws.
34
- iv. Your use of the SAM Materials will not involve or encourage others to reverse engineer, decompile or discover the underlying components of the SAM Materials.
35
- v. You are not the target of Trade Controls and your use of SAM Materials must comply with Trade Controls. You agree not to use, or permit others to use, SAM Materials for any activities subject to the International Traffic in Arms Regulations (ITAR) or end uses prohibited by Trade Controls, including those related to military or warfare purposes, nuclear industries or applications, espionage, or the development or use of guns or illegal weapons.
36
- 2. User Support. Your use of the SAM Materials is done at your own discretion; Meta does not process any information nor provide any service in relation to such use. Meta is under no obligation to provide any support services for the SAM Materials. Any support provided is “as is”, “with all faults”, and without warranty of any kind.
37
-
38
- 3. Disclaimer of Warranty. UNLESS REQUIRED BY APPLICABLE LAW, THE SAM MATERIALS AND ANY OUTPUT AND RESULTS THEREFROM ARE PROVIDED ON AN “AS IS” BASIS, WITHOUT WARRANTIES OF ANY KIND, AND META DISCLAIMS ALL WARRANTIES OF ANY KIND, BOTH EXPRESS AND IMPLIED, INCLUDING, WITHOUT LIMITATION, ANY WARRANTIES OF TITLE, NON-INFRINGEMENT, MERCHANTABILITY, OR FITNESS FOR A PARTICULAR PURPOSE. YOU ARE SOLELY RESPONSIBLE FOR DETERMINING THE APPROPRIATENESS OF USING OR REDISTRIBUTING THE SAM MATERIALS AND ASSUME ANY RISKS ASSOCIATED WITH YOUR USE OF THE SAM MATERIALS AND ANY OUTPUT AND RESULTS.
39
-
40
- 4. Limitation of Liability. IN NO EVENT WILL META OR ITS AFFILIATES BE LIABLE UNDER ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, TORT, NEGLIGENCE, PRODUCTS LIABILITY, OR OTHERWISE, ARISING OUT OF THIS AGREEMENT, FOR ANY LOST PROFITS OR ANY DIRECT OR INDIRECT, SPECIAL, CONSEQUENTIAL, INCIDENTAL, EXEMPLARY OR PUNITIVE DAMAGES, EVEN IF META OR ITS AFFILIATES HAVE BEEN ADVISED OF THE POSSIBILITY OF ANY OF THE FOREGOING.
41
-
42
- 5. Intellectual Property.
43
-
44
- a. Subject to Meta’s ownership of SAM Materials and derivatives made by or for Meta, with respect to any derivative works and modifications of the SAM Materials that are made by you, as between you and Meta, you are and will be the owner of such derivative works and modifications.
45
-
46
- b. If you institute litigation or other proceedings against Meta or any entity (including a cross-claim or counterclaim in a lawsuit) alleging that the SAM Materials, outputs or results, or any portion of any of the foregoing, constitutes infringement of intellectual property or other rights owned or licensable by you, then any licenses granted to you under this Agreement shall terminate as of the date such litigation or claim is filed or instituted. You will indemnify and hold harmless Meta from and against any claim by any third party arising out of or related to your use or distribution of the SAM Materials.
47
-
48
- 6. Term and Termination. The term of this Agreement will commence upon your acceptance of this Agreement or access to the SAM Materials and will continue in full force and effect until terminated in accordance with the terms and conditions herein. Meta may terminate this Agreement if you are in breach of any term or condition of this Agreement. Upon termination of this Agreement, you shall delete and cease use of the SAM Materials. Sections 3, 4 and 7 shall survive the termination of this Agreement.
49
-
50
- 7. Governing Law and Jurisdiction. This Agreement will be governed and construed under the laws of the State of California without regard to choice of law principles, and the UN Convention on Contracts for the International Sale of Goods does not apply to this Agreement. The courts of California shall have exclusive jurisdiction of any dispute arising out of this Agreement.
51
-
52
- 8. Modifications and Amendments. Meta may modify this Agreement from time to time; provided that they are similar in spirit to the current version of the Agreement, but may differ in detail to address new problems or concerns. All such changes will be effective immediately. Your continued use of the SAM Materials after any modification to this Agreement constitutes your agreement to such modification. Except as provided in this Agreement, no modification or addition to any provision of this Agreement will be binding unless it is in writing and signed by an authorized representative of both you and Meta.
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
sam3d_objects_mlx/LICENSE-APACHE DELETED
@@ -1,224 +0,0 @@
1
- MIT License
2
-
3
- Copyright (c) Microsoft Corporation.
4
-
5
- Permission is hereby granted, free of charge, to any person obtaining a copy
6
- of this software and associated documentation files (the "Software"), to deal
7
- in the Software without restriction, including without limitation the rights
8
- to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
9
- copies of the Software, and to permit persons to whom the Software is
10
- furnished to do so, subject to the following conditions:
11
-
12
- The above copyright notice and this permission notice shall be included in all
13
- copies or substantial portions of the Software.
14
-
15
- THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
16
- IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
17
- FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
18
- AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
19
- LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
20
- OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
21
- SOFTWARE
22
-
23
-
24
- Apache License
25
- Version 2.0, January 2004
26
- http://www.apache.org/licenses/
27
-
28
- TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION
29
-
30
- 1. Definitions.
31
-
32
- "License" shall mean the terms and conditions for use, reproduction,
33
- and distribution as defined by Sections 1 through 9 of this document.
34
-
35
- "Licensor" shall mean the copyright owner or entity authorized by
36
- the copyright owner that is granting the License.
37
-
38
- "Legal Entity" shall mean the union of the acting entity and all
39
- other entities that control, are controlled by, or are under common
40
- control with that entity. For the purposes of this definition,
41
- "control" means (i) the power, direct or indirect, to cause the
42
- direction or management of such entity, whether by contract or
43
- otherwise, or (ii) ownership of fifty percent (50%) or more of the
44
- outstanding shares, or (iii) beneficial ownership of such entity.
45
-
46
- "You" (or "Your") shall mean an individual or Legal Entity
47
- exercising permissions granted by this License.
48
-
49
- "Source" form shall mean the preferred form for making modifications,
50
- including but not limited to software source code, documentation
51
- source, and configuration files.
52
-
53
- "Object" form shall mean any form resulting from mechanical
54
- transformation or translation of a Source form, including but
55
- not limited to compiled object code, generated documentation,
56
- and conversions to other media types.
57
-
58
- "Work" shall mean the work of authorship, whether in Source or
59
- Object form, made available under the License, as indicated by a
60
- copyright notice that is included in or attached to the work
61
- (an example is provided in the Appendix below).
62
-
63
- "Derivative Works" shall mean any work, whether in Source or Object
64
- form, that is based on (or derived from) the Work and for which the
65
- editorial revisions, annotations, elaborations, or other modifications
66
- represent, as a whole, an original work of authorship. For the purposes
67
- of this License, Derivative Works shall not include works that remain
68
- separable from, or merely link (or bind by name) to the interfaces of,
69
- the Work and Derivative Works thereof.
70
-
71
- "Contribution" shall mean any work of authorship, including
72
- the original version of the Work and any modifications or additions
73
- to that Work or Derivative Works thereof, that is intentionally
74
- submitted to Licensor for inclusion in the Work by the copyright owner
75
- or by an individual or Legal Entity authorized to submit on behalf of
76
- the copyright owner. For the purposes of this definition, "submitted"
77
- means any form of electronic, verbal, or written communication sent
78
- to the Licensor or its representatives, including but not limited to
79
- communication on electronic mailing lists, source code control systems,
80
- and issue tracking systems that are managed by, or on behalf of, the
81
- Licensor for the purpose of discussing and improving the Work, but
82
- excluding communication that is conspicuously marked or otherwise
83
- designated in writing by the copyright owner as "Not a Contribution."
84
-
85
- "Contributor" shall mean Licensor and any individual or Legal Entity
86
- on behalf of whom a Contribution has been received by Licensor and
87
- subsequently incorporated within the Work.
88
-
89
- 2. Grant of Copyright License. Subject to the terms and conditions of
90
- this License, each Contributor hereby grants to You a perpetual,
91
- worldwide, non-exclusive, no-charge, royalty-free, irrevocable
92
- copyright license to reproduce, prepare Derivative Works of,
93
- publicly display, publicly perform, sublicense, and distribute the
94
- Work and such Derivative Works in Source or Object form.
95
-
96
- 3. Grant of Patent License. Subject to the terms and conditions of
97
- this License, each Contributor hereby grants to You a perpetual,
98
- worldwide, non-exclusive, no-charge, royalty-free, irrevocable
99
- (except as stated in this section) patent license to make, have made,
100
- use, offer to sell, sell, import, and otherwise transfer the Work,
101
- where such license applies only to those patent claims licensable
102
- by such Contributor that are necessarily infringed by their
103
- Contribution(s) alone or by combination of their Contribution(s)
104
- with the Work to which such Contribution(s) was submitted. If You
105
- institute patent litigation against any entity (including a
106
- cross-claim or counterclaim in a lawsuit) alleging that the Work
107
- or a Contribution incorporated within the Work constitutes direct
108
- or contributory patent infringement, then any patent licenses
109
- granted to You under this License for that Work shall terminate
110
- as of the date such litigation is filed.
111
-
112
- 4. Redistribution. You may reproduce and distribute copies of the
113
- Work or Derivative Works thereof in any medium, with or without
114
- modifications, and in Source or Object form, provided that You
115
- meet the following conditions:
116
-
117
- (a) You must give any other recipients of the Work or
118
- Derivative Works a copy of this License; and
119
-
120
- (b) You must cause any modified files to carry prominent notices
121
- stating that You changed the files; and
122
-
123
- (c) You must retain, in the Source form of any Derivative Works
124
- that You distribute, all copyright, patent, trademark, and
125
- attribution notices from the Source form of the Work,
126
- excluding those notices that do not pertain to any part of
127
- the Derivative Works; and
128
-
129
- (d) If the Work includes a "NOTICE" text file as part of its
130
- distribution, then any Derivative Works that You distribute must
131
- include a readable copy of the attribution notices contained
132
- within such NOTICE file, excluding those notices that do not
133
- pertain to any part of the Derivative Works, in at least one
134
- of the following places: within a NOTICE text file distributed
135
- as part of the Derivative Works; within the Source form or
136
- documentation, if provided along with the Derivative Works; or,
137
- within a display generated by the Derivative Works, if and
138
- wherever such third-party notices normally appear. The contents
139
- of the NOTICE file are for informational purposes only and
140
- do not modify the License. You may add Your own attribution
141
- notices within Derivative Works that You distribute, alongside
142
- or as an addendum to the NOTICE text from the Work, provided
143
- that such additional attribution notices cannot be construed
144
- as modifying the License.
145
-
146
- You may add Your own copyright statement to Your modifications and
147
- may provide additional or different license terms and conditions
148
- for use, reproduction, or distribution of Your modifications, or
149
- for any such Derivative Works as a whole, provided Your use,
150
- reproduction, and distribution of the Work otherwise complies with
151
- the conditions stated in this License.
152
-
153
- 5. Submission of Contributions. Unless You explicitly state otherwise,
154
- any Contribution intentionally submitted for inclusion in the Work
155
- by You to the Licensor shall be under the terms and conditions of
156
- this License, without any additional terms or conditions.
157
- Notwithstanding the above, nothing herein shall supersede or modify
158
- the terms of any separate license agreement you may have executed
159
- with Licensor regarding such Contributions.
160
-
161
- 6. Trademarks. This License does not grant permission to use the trade
162
- names, trademarks, service marks, or product names of the Licensor,
163
- except as required for reasonable and customary use in describing the
164
- origin of the Work and reproducing the content of the NOTICE file.
165
-
166
- 7. Disclaimer of Warranty. Unless required by applicable law or
167
- agreed to in writing, Licensor provides the Work (and each
168
- Contributor provides its Contributions) on an "AS IS" BASIS,
169
- WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or
170
- implied, including, without limitation, any warranties or conditions
171
- of TITLE, NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A
172
- PARTICULAR PURPOSE. You are solely responsible for determining the
173
- appropriateness of using or redistributing the Work and assume any
174
- risks associated with Your exercise of permissions under this License.
175
-
176
- 8. Limitation of Liability. In no event and under no legal theory,
177
- whether in tort (including negligence), contract, or otherwise,
178
- unless required by applicable law (such as deliberate and grossly
179
- negligent acts) or agreed to in writing, shall any Contributor be
180
- liable to You for damages, including any direct, indirect, special,
181
- incidental, or consequential damages of any character arising as a
182
- result of this License or out of the use or inability to use the
183
- Work (including but not limited to damages for loss of goodwill,
184
- work stoppage, computer failure or malfunction, or any and all
185
- other commercial damages or losses), even if such Contributor
186
- has been advised of the possibility of such damages.
187
-
188
- 9. Accepting Warranty or Additional Liability. While redistributing
189
- the Work or Derivative Works thereof, You may choose to offer,
190
- and charge a fee for, acceptance of support, warranty, indemnity,
191
- or other liability obligations and/or rights consistent with this
192
- License. However, in accepting such obligations, You may act only
193
- on Your own behalf and on Your sole responsibility, not on behalf
194
- of any other Contributor, and only if You agree to indemnify,
195
- defend, and hold each Contributor harmless for any liability
196
- incurred by, or claims asserted against, such Contributor by reason
197
- of your accepting any such warranty or additional liability.
198
-
199
- END OF TERMS AND CONDITIONS
200
-
201
- APPENDIX: How to apply the Apache License to your work.
202
-
203
- To apply the Apache License to your work, attach the following
204
- boilerplate notice, with the fields enclosed by brackets "[]"
205
- replaced with your own identifying information. (Don't include
206
- the brackets!) The text should be enclosed in the appropriate
207
- comment syntax for the file format. We also recommend that a
208
- file or class name and description of purpose be included on the
209
- same "printed page" as the copyright notice for easier
210
- identification within third-party archives.
211
-
212
- Copyright [yyyy] [name of copyright owner]
213
-
214
- Licensed under the Apache License, Version 2.0 (the "License");
215
- you may not use this file except in compliance with the License.
216
- You may obtain a copy of the License at
217
-
218
- http://www.apache.org/licenses/LICENSE-2.0
219
-
220
- Unless required by applicable law or agreed to in writing, software
221
- distributed under the License is distributed on an "AS IS" BASIS,
222
- WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
223
- See the License for the specific language governing permissions and
224
- limitations under the License.
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
sam3d_objects_mlx/README.md DELETED
@@ -1,174 +0,0 @@
1
- # SAM 3D Objects for MLX
2
-
3
- Inference-only port of [Meta SAM 3D Objects](https://github.com/facebookresearch/sam-3d-objects).
4
- The runtime and checkpoint converter use **MLX and Python's standard library**.
5
- All parameters are frozen and every model runs in evaluation mode.
6
-
7
- The BF16 bundle is published as
8
- [`mlx-community/sam-3d-objects-bf16`](https://huggingface.co/mlx-community/sam-3d-objects-bf16).
9
- It includes the structure/pose flow model, sparse-latent flow model, image and
10
- point-map conditioners, occupancy decoder, both Gaussian decoders (32 or 4
11
- Gaussians per voxel), mesh decoder, and the MoGe-v1 depth estimator used by
12
- the upstream pipeline. The learned model outputs are decoded with MLX
13
- FlexiCubes into colored triangle meshes.
14
-
15
- Training, latent encoders used for training, differentiable renderers,
16
- texture baking, mesh simplification, and iterative layout optimization are
17
- outside this inference port. This model has no text or tool-calling protocol.
18
-
19
- ## Load and generate
20
-
21
- Download the bundle with your preferred Hugging Face client. Hub transfer is
22
- separate from the inference runtime; the example below uses the optional `hf`
23
- command-line tool:
24
-
25
- ```sh
26
- hf download mlx-community/sam-3d-objects-bf16 --local-dir models/sam-3d-objects-bf16
27
- ```
28
-
29
- Use a checkout containing this port. Installing the entire mlx-vlm project
30
- also installs its other model dependencies; this model itself only needs MLX:
31
-
32
- ```sh
33
- python -m pip install mlx
34
- ```
35
-
36
- From the repository root:
37
-
38
- ```python
39
- from mlx_vlm.models.sam3d_objects.io import read_mask, read_png, write_gaussians, write_obj
40
- from mlx_vlm.models.sam3d_objects.pipeline import Pipeline
41
-
42
- pipeline = Pipeline.from_pretrained("models/sam-3d-objects-bf16")
43
- image = read_png("image.png")[..., :3]
44
- mask = read_mask("mask.png")
45
- result = pipeline.generate(image, mask, seed=42)
46
- write_gaussians("object.ply", result["gaussian"])
47
- write_obj("object.obj", result["mesh"])
48
- ```
49
-
50
- Inputs are HWC RGB/RGBA MLX arrays and an HW object mask. Images may be
51
- `uint8` or floating values in `[0, 1]`; masks may be boolean, 0/1, or 0/255.
52
- The small stdlib image reader supports non-interlaced, 8-bit PNG images.
53
- Other codecs can be decoded by the caller and passed as MLX arrays.
54
-
55
- Automatic point maps come from the bundled MoGe model. To use a camera or
56
- an existing depth pipeline, supply an HWC `pointmap` in the upstream camera
57
- convention: +X left, +Y up, +Z forward. Invalid points use NaN. Setting
58
- `estimate_depth=False` without a point map uses the trained point-map dropout;
59
- that path cannot establish scene-relative layout from observed depth.
60
-
61
- Outputs include `pose` (quaternion WXYZ, rotation matrix, translation, scale),
62
- voxel `coords`, denormalized `latents`, `gaussian`, and `mesh`. Geometry is in
63
- object-local coordinates; pose is returned separately. Gaussian fields are
64
- `positions`, `sh_dc`, `scales`, `rotations`, and `opacities`. Mesh fields are
65
- `vertices`, `faces`, and six-channel `vertex_colors` (RGB plus learned attributes).
66
- PLY uses standard Gaussian-splat fields; OBJ includes RGB vertex colors.
67
-
68
- ## Streaming
69
-
70
- ```python
71
- from mlx_vlm.models.sam3d_objects.pipeline import Request
72
-
73
- async def requests(camera):
74
- async for frame, mask in camera:
75
- yield Request(frame, mask, request_id="object-1", seed=42)
76
-
77
- async def reconstruct(camera):
78
- async for event in pipeline.astream(requests(camera)):
79
- print(event.request_id, event.stage, event.step, event.total_steps)
80
- if event.stage == "complete":
81
- consume(event.data)
82
- ```
83
-
84
- `astream` accepts synchronous or asynchronous input iterables. It consumes
85
- one request at a time, preserves request order, and applies backpressure at
86
- each yield. One worker submits MLX operations so preprocessing and sparse
87
- topology work do not block the caller's event loop. Close the async generator
88
- with `aclose()` when stopping early, or use `contextlib.aclosing`.
89
-
90
- Events cover depth, conditioning, every structure/pose and sparse-latent
91
- flow step, occupancy, each selected decoder, and the final result. Arrays are
92
- scheduled with `mx.async_eval`; GPU work may still be in flight when yielded.
93
- Use `mx.eval(event.data)` if host-side code needs completed values. The
94
- blocking `generate` convenience method evaluates its final result.
95
- Sparse topology needs scalar synchronization to determine variable output
96
- sizes. Cancellation stops further submissions and drains already submitted
97
- work before releasing the worker stream. Each pipeline permits one active
98
- stream; use separate instances for independent streams.
99
-
100
- ## CLI
101
-
102
- ```sh
103
- python -m mlx_vlm.models.sam3d_objects.generate \
104
- --model models/sam-3d-objects-bf16 \
105
- --image image.png --mask mask.png --output outputs/object \
106
- --formats gaussian mesh --ss-steps 25 --slat-steps 25
107
- ```
108
-
109
- After installing mlx-vlm, the same command is available as `mlx_vlm.sam3d`.
110
- For JSONL streaming, pass `--jsonl` and provide one request per stdin line:
111
-
112
- ```json
113
- {"id":"frame-1","image":"frame-1.png","mask":"mask-1.png","seed":42}
114
- {"id":"frame-2","image":"frame-2.png","mask":"mask-2.png","seed":42}
115
- ```
116
-
117
- Progress is emitted as JSONL. Results go into numbered subdirectories under
118
- `--output`, each containing `result.safetensors` and the requested PLY/OBJ
119
- files. Input IDs are preserved as labels and never used as filesystem paths.
120
- For externally computed points, `--pointmap` accepts a safetensors file
121
- containing a `points` tensor.
122
-
123
- ## Conversion
124
-
125
- Obtain the original checkpoints from `facebook/sam-3d-objects` and
126
- `Ruicheng/moge-vitl`, then run:
127
-
128
- ```sh
129
- python -m mlx_vlm.models.sam3d_objects.convert \
130
- --source /path/to/sam-3d-objects/checkpoints \
131
- --moge-checkpoint /path/to/moge-vitl/model.pt \
132
- --output models/sam-3d-objects-bf16
133
- ```
134
-
135
- The converter reads tensor-only PyTorch ZIP archives through a restricted
136
- stdlib unpickler, streams their original storage bytes to safetensors, maps
137
- the inference weights to MLX layouts, and casts floating tensors to BF16.
138
- It does not import PyTorch, NumPy, or the safetensors Python library.
139
- Unknown pickle globals and unsupported storage layouts are rejected.
140
- The duplicate occupancy decoder embedded in the structure checkpoint is
141
- omitted in favor of its dedicated released checkpoint. Loading is strict:
142
- missing, extra, and incorrectly shaped model parameters are errors.
143
-
144
- The full bundle contains 3,709 tensors across seven safetensors files.
145
- There are no tokenizer or chat-template files because this is a geometry model.
146
-
147
- ## Validation
148
-
149
- ```sh
150
- python -m unittest mlx_vlm.tests.test_models.TestSAM3DObjects -v
151
- ```
152
-
153
- Tests cover frozen BF16 checkpoint round trips, sparse convolution versus
154
- dense convolution, reference pooling semantics, window attention versus
155
- masked full attention, Euler/CFG scheduling, closed mesh topology, async
156
- backpressure/early close/reuse, and restricted checkpoint deserialization.
157
-
158
- Additional local comparisons against the original PyTorch source used FP32
159
- MLX CPU to isolate equation parity from accelerated Metal precision. Attention,
160
- both flow networks, occupancy decoding, interpolation, pretrained MoGe, and
161
- FlexiCubes matched within 0.000002 maximum absolute error on those fixtures;
162
- mesh face indices matched exactly. These are component checks, not a claim of
163
- bitwise BF16 end-to-end parity or a reconstruction quality benchmark.
164
-
165
- The actual BF16 Metal pipeline was also exercised on an upstream example
166
- image with automatic depth and both 25-step sampling stages. See the Hub
167
- model card's validation report for measured timings and memory.
168
-
169
- ## Licenses
170
-
171
- SAM-derived code and weights retain the [SAM License](LICENSE).
172
- MoGe and FlexiCubes components retain Apache-2.0; see
173
- [LICENSE-APACHE](LICENSE-APACHE) and source copyright notices.
174
- This is an unofficial MLX port.
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
sam3d_objects_mlx/__init__.py DELETED
@@ -1,6 +0,0 @@
1
- """SAM 3D Objects inference on Apple Silicon, using MLX and the stdlib."""
2
-
3
- from .config import ModelConfig
4
- from .sam3d_objects import Model
5
-
6
- __all__ = ["Model", "ModelConfig"]
 
 
 
 
 
 
 
sam3d_objects_mlx/bundle.py DELETED
@@ -1,171 +0,0 @@
1
- """Package the inference code beside the converted Hub weights."""
2
-
3
- import argparse
4
- import json
5
- import shutil
6
- from pathlib import Path
7
-
8
-
9
- def package(destination):
10
- root = Path(destination)
11
- source = Path(__file__).parent
12
- package_root = root / "sam3d_objects_mlx"
13
- package_root.mkdir(parents=True, exist_ok=True)
14
- for path in source.glob("*.py"):
15
- shutil.copyfile(path, package_root / path.name)
16
- for name in ("LICENSE", "LICENSE-APACHE", "README.md"):
17
- shutil.copyfile(source / name, package_root / name)
18
- (root / "LICENSE").write_text((source / "LICENSE").read_text())
19
- (root / "LICENSE-APACHE").write_text((source / "LICENSE-APACHE").read_text())
20
- (root / "pyproject.toml").write_text("""[build-system]
21
- requires = ["setuptools>=61"]
22
- build-backend = "setuptools.build_meta"
23
-
24
- [project]
25
- name = "sam3d-objects-mlx-inference"
26
- version = "0.1.0"
27
- description = "Frozen SAM 3D Objects inference using MLX and the Python standard library"
28
- requires-python = ">=3.10"
29
- dependencies = ["mlx>=0.32.2"]
30
- license = {text = "SAM License; Apache-2.0 for MoGe/FlexiCubes"}
31
-
32
- [project.scripts]
33
- sam3d-mlx = "sam3d_objects_mlx.generate:main"
34
-
35
- [tool.setuptools.packages.find]
36
- include = ["sam3d_objects_mlx*"]
37
-
38
- [tool.setuptools.package-data]
39
- sam3d_objects_mlx = ["LICENSE", "LICENSE-APACHE", "README.md"]
40
- """)
41
- card = """---
42
- library_name: mlx
43
- pipeline_tag: image-to-3d
44
- license: other
45
- license_name: sam-license
46
- license_link: LICENSE
47
- base_model:
48
- - facebook/sam-3d-objects
49
- - Ruicheng/moge-vitl
50
- tags:
51
- - mlx
52
- - apple-silicon
53
- - bfloat16
54
- - image-to-3d
55
- - gaussian-splatting
56
- - inference
57
- ---
58
-
59
- # SAM 3D Objects — MLX BF16
60
-
61
- Frozen inference port of Meta SAM 3D Objects, including the upstream MoGe-v1
62
- depth estimator. Runtime and conversion require **MLX and Python's standard
63
- library only**. This is an unofficial derivative. SAM components retain the
64
- SAM License; MoGe and FlexiCubes retain Apache-2.0.
65
-
66
- The bundle includes 3,709 BF16 tensors, strict loading, MLX sparse convolutions,
67
- windowed attention, Gaussian and colored-mesh decoding, `mx.async_eval`, and
68
- synchronous/asynchronous streaming APIs. Both Gaussian heads are included.
69
- It generates geometry and pose; it has no text or tool-calling interface.
70
-
71
- ## Install the included runtime
72
-
73
- Download this repository, then install its small runtime package:
74
-
75
- ```sh
76
- hf download mlx-community/sam-3d-objects-bf16 --local-dir sam-3d-objects-bf16
77
- python -m pip install ./sam-3d-objects-bf16
78
- sam3d-mlx --model ./sam-3d-objects-bf16 \\
79
- --image image.png --mask mask.png --output ./output \\
80
- --formats gaussian mesh
81
- ```
82
-
83
- `hf` is an optional download tool; it is not a runtime dependency. The package
84
- can also be imported directly from the downloaded directory. The same model
85
- implementation lives under `mlx_vlm/models/sam3d_objects` in the port checkout.
86
-
87
- ```python
88
- from sam3d_objects_mlx.io import read_png, read_mask
89
- from sam3d_objects_mlx.pipeline import Pipeline, Request
90
-
91
- pipeline = Pipeline.from_pretrained("sam-3d-objects-bf16")
92
- image, mask = read_png("image.png")[..., :3], read_mask("mask.png")
93
- result = pipeline.generate(image, mask, seed=42)
94
-
95
- async def reconstruct(requests):
96
- async for event in pipeline.astream(requests):
97
- if event.stage == "complete":
98
- consume(event.data)
99
- ```
100
-
101
- `requests` is an iterable or async iterable of `Request` values. Input is
102
- consumed one request at a time with backpressure. Events preserve IDs and
103
- order and include every sampling step plus decoded outputs. The event loop
104
- remains responsive while one worker submits MLX work. Early `aclose()` or
105
- cancellation stops submissions and drains the worker. Arrays may still be
106
- evaluating on the GPU when yielded; use `mx.eval` before host consumption.
107
-
108
- Inputs are HWC RGB/RGBA MLX arrays and HW masks. The included file reader
109
- supports non-interlaced 8-bit PNG, including RGBA alpha masks. Callers can use
110
- other decoders and pass arrays directly. Automatic MoGe depth is included;
111
- external HWC point maps use +X left, +Y up, +Z forward, with NaN invalid values.
112
- Set `estimate_depth=False` to use the trained point-map dropout instead.
113
-
114
- Results include local-space Gaussians, mesh vertices/faces/colors, latent
115
- features, occupied voxels, and a separate pose (rotation, translation, scale).
116
- CLI outputs standard Gaussian PLY, colored OBJ, and raw safetensors.
117
- `--jsonl` consumes stdin request lines and emits ordered progress records.
118
- Training, latent training encoders, rendering, texture baking, mesh
119
- simplification, and iterative layout optimization are not included.
120
-
121
- ## Conversion and validation
122
-
123
- SAM weights were converted directly from the official tensor-only PyTorch
124
- ZIP checkpoints through a restricted stdlib reader. Tensor bytes are first
125
- preserved in safetensors, then mapped to MLX layouts and cast to BF16. The
126
- duplicate occupancy decoder is removed. MoGe uses its original safetensors
127
- export. No PyTorch or NumPy is imported by conversion or inference.
128
-
129
- All parameter names/shapes were checked with strict loading, every floating
130
- tensor is BF16, and the loaded model has zero trainable parameters. Component
131
- checks against the original reference in FP32 MLX CPU cover attention, both
132
- flow models, occupancy decoding, interpolation, pretrained MoGe, and
133
- FlexiCubes. Maximum absolute error was below 0.000002 on these fixtures;
134
- mesh face indices matched exactly. This is not a claim of bitwise BF16
135
- end-to-end parity or a reconstruction-quality evaluation.
136
-
137
- See `validation.json`, `conversion.json`, and the included source for details.
138
- The original sources are [SAM 3D Objects](https://github.com/facebookresearch/sam-3d-objects)
139
- and [MoGe](https://github.com/microsoft/MoGe).
140
- """
141
- validation = root / "validation.json"
142
- if validation.exists():
143
- report = json.loads(validation.read_text())
144
- if "benchmark" in report:
145
- b = report["benchmark"]
146
- card += f"""
147
- ### Local performance
148
-
149
- Hardware: {b["hardware"]}. Input: 448×672 RGB plus object mask; automatic MoGe
150
- depth, 25 structure steps and 25 latent steps, both Gaussian heads and mesh.
151
- One warmup was excluded; the same seed produced identical latent outputs.
152
-
153
- | Metric | Result |
154
- | --- | ---: |
155
- | Median latency ({len(b["samples"])} runs) | {b["median_seconds"]:.2f} s |
156
- | Maximum peak memory | {b["peak_memory_gb"]:.2f} GB |
157
-
158
- Two additional async requests matched the synchronous latent output while
159
- the event-loop heartbeat continued. These measurements describe one input.
160
- """
161
- (root / "README.md").write_text(card)
162
-
163
-
164
- def main():
165
- parser = argparse.ArgumentParser(description=__doc__)
166
- parser.add_argument("destination")
167
- package(parser.parse_args().destination)
168
-
169
-
170
- if __name__ == "__main__":
171
- main()
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
sam3d_objects_mlx/checkpoint.py DELETED
@@ -1,136 +0,0 @@
1
- """Read tensor-only torch ZIP checkpoints without importing torch or NumPy.
2
-
3
- The restricted unpickler creates metadata, never source classes. Only dense,
4
- contiguous tensor storages and OrderedDict are accepted. The intermediate
5
- safetensors file preserves source bytes; MLX handles BF16 conversion later.
6
- """
7
-
8
- import collections
9
- import io
10
- import json
11
- import math
12
- import pickle
13
- import struct
14
- import zipfile
15
- from dataclasses import dataclass
16
-
17
- DTYPES = {
18
- "FloatStorage": ("F32", 4),
19
- "HalfStorage": ("F16", 2),
20
- "BFloat16Storage": ("BF16", 2),
21
- "DoubleStorage": ("F64", 8),
22
- "LongStorage": ("I64", 8),
23
- "IntStorage": ("I32", 4),
24
- "BoolStorage": ("BOOL", 1),
25
- "ByteStorage": ("U8", 1),
26
- }
27
-
28
-
29
- @dataclass
30
- class Storage:
31
- kind: str
32
- key: str
33
- size: int
34
-
35
-
36
- @dataclass
37
- class Tensor:
38
- storage: Storage
39
- offset: int
40
- shape: tuple
41
- stride: tuple
42
-
43
-
44
- def _tensor(storage, offset, shape, stride, *unused):
45
- return Tensor(storage, offset, shape, stride)
46
-
47
-
48
- class TensorUnpickler(pickle.Unpickler):
49
- def find_class(self, module, name):
50
- if module == "collections" and name == "OrderedDict":
51
- return collections.OrderedDict
52
- if module == "torch" and name in DTYPES:
53
- return name
54
- if module == "torch._utils" and name in (
55
- "_rebuild_tensor_v2",
56
- "_rebuild_tensor",
57
- ):
58
- return _tensor
59
- if module == "torch._utils" and name == "_rebuild_parameter":
60
- return lambda tensor, *args: tensor
61
- raise pickle.UnpicklingError(f"Unsupported checkpoint global: {module}.{name}")
62
-
63
- def persistent_load(self, value):
64
- if (
65
- not isinstance(value, tuple)
66
- or len(value) != 5
67
- or value[0] != "storage"
68
- or value[1] not in DTYPES
69
- ):
70
- raise pickle.UnpicklingError("Unsupported checkpoint storage")
71
- return Storage(value[1], str(value[2]), value[4])
72
-
73
-
74
- def read_metadata(archive):
75
- names = archive.namelist()
76
- candidates = [n for n in names if n.endswith("/data.pkl")]
77
- if len(candidates) != 1:
78
- raise ValueError("Expected a tensor ZIP checkpoint with one data.pkl")
79
- prefix = candidates[0].removesuffix("data.pkl")
80
- if (
81
- prefix + "byteorder" in names
82
- and archive.read(prefix + "byteorder") != b"little"
83
- ):
84
- raise ValueError("Only little-endian checkpoints are supported")
85
- data = TensorUnpickler(io.BytesIO(archive.read(candidates[0]))).load()
86
- if "state_dict" in data:
87
- data = data["state_dict"]
88
- elif "model" in data and "model_config" in data:
89
- data = data["model"]
90
- # The released structure generator contains non-tensor training metadata.
91
- data.pop("__mixhavior__", None)
92
- if not isinstance(data, dict) or not all(
93
- isinstance(v, Tensor) for v in data.values()
94
- ):
95
- raise ValueError("Checkpoint must contain only named dense tensors")
96
- return prefix, data
97
-
98
-
99
- def to_safetensors(source, destination):
100
- """Stream a tensor-only checkpoint to a byte-exact safetensors file."""
101
- with zipfile.ZipFile(source) as archive:
102
- prefix, tensors = read_metadata(archive)
103
- header, offset = {}, 0
104
- for name, t in tensors.items():
105
- stride = 1
106
- for size, actual in zip(reversed(t.shape), reversed(t.stride)):
107
- if size > 1 and actual != stride:
108
- raise ValueError(f"Non-contiguous tensor: {name}")
109
- stride *= size
110
- dtype, width = DTYPES[t.storage.kind]
111
- length = math.prod(t.shape) * width
112
- if t.offset < 0 or t.offset + math.prod(t.shape) > t.storage.size:
113
- raise ValueError(f"Tensor outside storage: {name}")
114
- header[name] = {
115
- "dtype": dtype,
116
- "shape": list(t.shape),
117
- "data_offsets": [offset, offset + length],
118
- }
119
- offset += length
120
- encoded = json.dumps(header, separators=(",", ":")).encode()
121
- encoded += b" " * (-len(encoded) % 8)
122
- with open(destination, "wb") as out:
123
- out.write(struct.pack("<Q", len(encoded)))
124
- out.write(encoded)
125
- for name, t in tensors.items():
126
- width = DTYPES[t.storage.kind][1]
127
- remaining = math.prod(t.shape) * width
128
- with archive.open(prefix + "data/" + t.storage.key) as raw:
129
- raw.seek(t.offset * width)
130
- while remaining:
131
- chunk = raw.read(min(remaining, 8 << 20))
132
- if not chunk:
133
- raise ValueError(f"Truncated storage: {name}")
134
- out.write(chunk)
135
- remaining -= len(chunk)
136
- return len(tensors)
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
sam3d_objects_mlx/config.py DELETED
@@ -1,75 +0,0 @@
1
- """Inference configuration for Meta SAM 3D Objects."""
2
-
3
- from dataclasses import asdict, dataclass, field
4
-
5
-
6
- @dataclass
7
- class ModelConfig:
8
- model_type: str = "sam3d_objects"
9
- dtype: str = "bfloat16"
10
- hidden_size: int = 1024
11
- num_heads: int = 16
12
- num_blocks: int = 24
13
- cond_channels: int = 1024
14
- latent_channels: int = 8
15
- latent_resolution: int = 16
16
- resolution: int = 64
17
- io_channels: int = 128
18
- decoder_channels: int = 768
19
- decoder_heads: int = 12
20
- decoder_blocks: int = 12
21
- window_size: int = 8
22
- structure_channels: list = field(default_factory=lambda: [512, 128, 32])
23
- structure_res_blocks: int = 2
24
- dino_hidden_size: int = 1024
25
- dino_heads: int = 16
26
- dino_layers: int = 24
27
- dino_image_size: int = 518
28
- image_size: int = 518
29
- point_size: int = 256
30
- point_patch_size: int = 8
31
- point_channels: int = 512
32
- point_heads: int = 16
33
- # Enable the bundled MoGe-v1 inference model when its weights are present.
34
- depth_model: bool = False
35
- depth_num_tokens: int = 2551
36
- ss_steps: int = 25
37
- slat_steps: int = 25
38
- ss_guidance: float = 7.0
39
- slat_guidance: float = 1.0
40
- ss_rescale_t: float = 3.0
41
- slat_rescale_t: float = 1.0
42
- # Match the released inference pipeline's occupancy pruning.
43
- downsample_ss_dist: int = 1
44
- slat_mean: list = field(
45
- default_factory=lambda: [
46
- 0.12211431,
47
- 0.37204156,
48
- -1.26521907,
49
- -2.05276058,
50
- -3.10432536,
51
- -0.11294304,
52
- -0.85146744,
53
- 0.45506954,
54
- ]
55
- )
56
- slat_std: list = field(
57
- default_factory=lambda: [
58
- 2.37326008,
59
- 2.13174402,
60
- 2.2413953,
61
- 2.30589401,
62
- 2.1191894,
63
- 1.8969511,
64
- 2.41684989,
65
- 2.08374642,
66
- ]
67
- )
68
-
69
- @classmethod
70
- def from_dict(cls, values):
71
- names = cls.__dataclass_fields__
72
- return cls(**{k: v for k, v in values.items() if k in names})
73
-
74
- def to_dict(self):
75
- return asdict(self)
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
sam3d_objects_mlx/convert.py DELETED
@@ -1,184 +0,0 @@
1
- """Convert released inference checkpoints with MLX and the standard library."""
2
-
3
- import argparse
4
- import json
5
- import tempfile
6
- from pathlib import Path
7
-
8
- import mlx.core as mx
9
-
10
- from .checkpoint import to_safetensors
11
- from .config import ModelConfig
12
-
13
- COMPONENTS = (
14
- "ss_generator",
15
- "slat_generator",
16
- "ss_decoder",
17
- "slat_decoder_gs",
18
- "slat_decoder_gs_4",
19
- "slat_decoder_mesh",
20
- )
21
-
22
-
23
- def map_tensor(component, name, tensor):
24
- if component.endswith("generator"):
25
- if name.startswith("_base_models.generator.reverse_fn.backbone."):
26
- name = (
27
- component
28
- + "."
29
- + name.removeprefix("_base_models.generator.reverse_fn.backbone.")
30
- )
31
- elif name.startswith("_base_models.condition_embedder."):
32
- name = (
33
- component.replace("generator", "condition_embedder")
34
- + "."
35
- + name.removeprefix("_base_models.condition_embedder.")
36
- )
37
- elif name.startswith("_base_models.decoder."):
38
- # Duplicate occupancy decoder bundled with the structure generator.
39
- return None
40
- else:
41
- raise ValueError(f"Unmapped inference tensor: {name}")
42
- else:
43
- name = component + "." + name
44
- if name.startswith("ss_decoder.") and tensor.ndim == 5:
45
- tensor = tensor.transpose(0, 2, 3, 4, 1)
46
- elif name.endswith("patch_embed.proj.weight"):
47
- tensor = tensor.transpose(0, 2, 3, 1)
48
- if mx.issubdtype(tensor.dtype, mx.floating):
49
- tensor = tensor.astype(mx.bfloat16)
50
- return name, tensor
51
-
52
-
53
- def convert(source, destination):
54
- source, destination = Path(source), Path(destination)
55
- if (source / "checkpoints").is_dir():
56
- source = source / "checkpoints"
57
- destination.mkdir(parents=True, exist_ok=True)
58
- index, total_size, report = {}, 0, {}
59
- for i, component in enumerate(COMPONENTS, 1):
60
- checkpoint = source / f"{component}.ckpt"
61
- safe = source / f"{component}.safetensors"
62
- with tempfile.TemporaryDirectory(prefix="sam3d-convert-") as scratch:
63
- if checkpoint.exists():
64
- safe = Path(scratch) / "source.safetensors"
65
- count = to_safetensors(checkpoint, safe)
66
- elif safe.exists():
67
- count = None
68
- else:
69
- raise FileNotFoundError(checkpoint)
70
- weights = {}
71
- for name, tensor in mx.load(str(safe)).items():
72
- mapped = map_tensor(component, name, tensor)
73
- if mapped is not None:
74
- key, value = mapped
75
- weights[key] = value
76
- mx.async_eval(weights)
77
- shard = f"model-{i:05d}-of-{len(COMPONENTS):05d}.safetensors"
78
- mx.save_safetensors(
79
- str(destination / shard),
80
- weights,
81
- metadata={"format": "mlx", "dtype": "bfloat16"},
82
- )
83
- index.update({k: shard for k in weights})
84
- total_size += sum(v.nbytes for v in weights.values())
85
- report[component] = {
86
- "source_tensors": count,
87
- "converted_tensors": len(weights),
88
- "shard": shard,
89
- }
90
- print(f"{component}: {len(weights)} tensors -> {shard}", flush=True)
91
- del weights
92
- mx.clear_cache()
93
- (destination / "config.json").write_text(
94
- json.dumps(ModelConfig().to_dict(), indent=2) + "\n"
95
- )
96
- (destination / "model.safetensors.index.json").write_text(
97
- json.dumps(
98
- {"metadata": {"total_size": total_size}, "weight_map": index}, indent=2
99
- )
100
- + "\n"
101
- )
102
- (destination / "conversion.json").write_text(
103
- json.dumps(
104
- {
105
- "source": "facebook/sam-3d-objects",
106
- "dtype": "bfloat16",
107
- "components": report,
108
- },
109
- indent=2,
110
- )
111
- + "\n"
112
- )
113
- (destination / "LICENSE").write_text(
114
- (Path(__file__).parent / "LICENSE").read_text()
115
- )
116
- return destination
117
-
118
-
119
- def add_depth(source, destination):
120
- """Add the released Ruicheng/moge-vitl inference checkpoint to a bundle."""
121
- from .depth import DepthModel
122
-
123
- source, destination = Path(source), Path(destination)
124
- with tempfile.TemporaryDirectory(prefix="moge-convert-") as scratch:
125
- if source.suffix != ".safetensors":
126
- raw = Path(scratch) / "moge.safetensors"
127
- to_safetensors(source, raw)
128
- else:
129
- raw = source
130
- weights = {}
131
- for name, value in mx.load(str(raw)).items():
132
- if value.ndim == 4 and name.endswith("weight"):
133
- if name.startswith("head.upsample_blocks.") and ".0.0.weight" in name:
134
- value = value.transpose(1, 2, 3, 0)
135
- else:
136
- value = value.transpose(0, 2, 3, 1)
137
- weights[name] = (
138
- value.astype(mx.bfloat16)
139
- if mx.issubdtype(value.dtype, mx.floating)
140
- else value
141
- )
142
- model = DepthModel()
143
- model.load_weights(list(weights.items()), strict=True)
144
- weights = {"depth_model." + k: v for k, v in weights.items()}
145
- mx.save_safetensors(str(destination / "moge.safetensors"), weights)
146
- index_path = destination / "model.safetensors.index.json"
147
- index = json.loads(index_path.read_text())
148
- index["weight_map"].update({k: "moge.safetensors" for k in weights})
149
- index["metadata"]["total_size"] = sum(
150
- p.stat().st_size for p in destination.glob("*.safetensors")
151
- )
152
- index_path.write_text(json.dumps(index, indent=2) + "\n")
153
- path = destination / "config.json"
154
- config = json.loads(path.read_text())
155
- config["depth_model"] = True
156
- path.write_text(json.dumps(config, indent=2) + "\n")
157
- (destination / "LICENSE-APACHE").write_text(
158
- (Path(__file__).parent / "LICENSE-APACHE").read_text()
159
- )
160
- print(f"MoGe: {len(weights)} BF16 tensors added", flush=True)
161
-
162
-
163
- def main():
164
- parser = argparse.ArgumentParser(description=__doc__)
165
- parser.add_argument("--source")
166
- parser.add_argument("--output", required=True)
167
- parser.add_argument(
168
- "--moge-checkpoint",
169
- help="Optional Ruicheng/moge-vitl model.pt or source safetensors",
170
- )
171
- args = parser.parse_args()
172
- if args.source:
173
- convert(args.source, args.output)
174
- if args.moge_checkpoint:
175
- add_depth(args.moge_checkpoint, args.output)
176
- if not args.source and not args.moge_checkpoint:
177
- parser.error("Provide --source and/or --moge-checkpoint")
178
- from .bundle import package
179
-
180
- package(args.output)
181
-
182
-
183
- if __name__ == "__main__":
184
- main()
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
sam3d_objects_mlx/decoders.py DELETED
@@ -1,182 +0,0 @@
1
- # Adapted from Meta SAM 3D Objects. See LICENSE in this directory.
2
- """Occupancy, Gaussian, and mesh-feature inference decoders."""
3
-
4
- import math
5
-
6
- import mlx.core as mx
7
- import mlx.nn as nn
8
-
9
- from .layers import (
10
- Attention,
11
- FeedForward,
12
- LayerNorm,
13
- Sequential,
14
- layer_norm,
15
- position_embedding,
16
- )
17
- from .sparse import SparseConv, SparseGroupNorm, window_attention
18
-
19
-
20
- class DenseResBlock(nn.Module):
21
- def __init__(self, channels):
22
- super().__init__()
23
- self.norm1 = LayerNorm(channels)
24
- self.norm2 = LayerNorm(channels)
25
- self.conv1 = nn.Conv3d(channels, channels, 3, padding=1)
26
- self.conv2 = nn.Conv3d(channels, channels, 3, padding=1)
27
-
28
- def __call__(self, x):
29
- return x + self.conv2(nn.silu(self.norm2(self.conv1(nn.silu(self.norm1(x))))))
30
-
31
-
32
- class DenseUpsample(nn.Module):
33
- def __init__(self, inputs, outputs):
34
- super().__init__()
35
- self.conv = nn.Conv3d(inputs, outputs * 8, 3, padding=1)
36
-
37
- def __call__(self, x):
38
- x = self.conv(x)
39
- b, d, h, w, c = x.shape
40
- x = x.reshape(b, d, h, w, c // 8, 2, 2, 2)
41
- return x.transpose(0, 1, 5, 2, 6, 3, 7, 4).reshape(
42
- b, d * 2, h * 2, w * 2, c // 8
43
- )
44
-
45
-
46
- class StructureDecoder(nn.Module):
47
- def __init__(self, config):
48
- super().__init__()
49
- channels = config.structure_channels
50
- self.input_layer = nn.Conv3d(config.latent_channels, channels[0], 3, padding=1)
51
- self.middle_block = Sequential(
52
- *(DenseResBlock(channels[0]) for _ in range(config.structure_res_blocks))
53
- )
54
- self.blocks = []
55
- for i, c in enumerate(channels):
56
- self.blocks.extend(
57
- DenseResBlock(c) for _ in range(config.structure_res_blocks)
58
- )
59
- if i + 1 < len(channels):
60
- self.blocks.append(DenseUpsample(c, channels[i + 1]))
61
- self.out_layer = Sequential(
62
- LayerNorm(channels[-1]), nn.SiLU(), nn.Conv3d(channels[-1], 1, 3, padding=1)
63
- )
64
-
65
- def __call__(self, x):
66
- side = round(x.shape[1] ** (1 / 3))
67
- x = x.reshape(x.shape[0], side, side, side, x.shape[-1])
68
- x = self.middle_block(self.input_layer(x.astype(self.input_layer.weight.dtype)))
69
- for block in self.blocks:
70
- x = block(x)
71
- return self.out_layer(x)
72
-
73
-
74
- class WindowBlock(nn.Module):
75
- def __init__(self, channels, heads, window, shift):
76
- super().__init__()
77
- self.attn = Attention(channels, heads)
78
- self.mlp = FeedForward(channels)
79
- self._window, self._shift, self._heads = window, shift, heads
80
-
81
- def __call__(self, x, grid):
82
- h = self.attn.to_qkv(layer_norm(x, 1e-6)).reshape(
83
- x.shape[0], 3, self._heads, -1
84
- )
85
- h = window_attention(h, grid, self._window, self._shift)
86
- x = x + self.attn.to_out(h)
87
- return x + self.mlp(layer_norm(x, 1e-6))
88
-
89
-
90
- class DecoderBase(nn.Module):
91
- def __init__(self, config):
92
- super().__init__()
93
- c = config.decoder_channels
94
- self.input_layer = nn.Linear(config.latent_channels, c)
95
- self.blocks = [
96
- WindowBlock(
97
- c,
98
- config.decoder_heads,
99
- config.window_size,
100
- config.window_size // 2 * (i % 2),
101
- )
102
- for i in range(config.decoder_blocks)
103
- ]
104
-
105
- def encode(self, x, grid):
106
- x = self.input_layer(x.astype(self.input_layer.weight.dtype))
107
- x = x + position_embedding(grid.coords[:, 1:], x.shape[-1]).astype(x.dtype)
108
- for block in self.blocks:
109
- x = block(x, grid)
110
- return x
111
-
112
-
113
- class GaussianDecoder(DecoderBase):
114
- def __init__(self, config, count=32):
115
- super().__init__(config)
116
- self.out_layer = nn.Linear(config.decoder_channels, 14 * count)
117
- self.offset_perturbation = mx.zeros((count, 3))
118
- self._count = count
119
-
120
- def __call__(self, x, grid):
121
- h = self.out_layer(layer_norm(self.encode(x, grid))).astype(mx.float32)
122
- n = self._count
123
- xyz, color, scaling, rotation, opacity = mx.split(
124
- h, [n * 3, n * 6, n * 9, n * 13], axis=-1
125
- )
126
- xyz = (
127
- grid.coords[:, None, 1:].astype(mx.float32) + 0.5
128
- ) / grid.resolution + mx.tanh(
129
- xyz.reshape(-1, n, 3) + self.offset_perturbation
130
- ) * (
131
- 0.75 / grid.resolution
132
- )
133
- scale = nn.softplus(scaling.reshape(-1, 3) + math.log(math.expm1(0.004)))
134
- scale = mx.sqrt(scale * scale + 0.0009**2)
135
- rotation = rotation.reshape(-1, 4) * 0.1 + mx.array([1, 0, 0, 0])
136
- rotation = rotation * mx.rsqrt(
137
- mx.maximum(mx.sum(rotation * rotation, axis=-1, keepdims=True), 1e-24)
138
- )
139
- opacity = mx.sigmoid(opacity.reshape(-1, 1) + math.log(0.1 / 0.9))
140
- return {
141
- "positions": xyz.reshape(-1, 3) - 0.5,
142
- "sh_dc": color.reshape(-1, 3),
143
- "scales": scale,
144
- "rotations": rotation,
145
- "opacities": opacity,
146
- }
147
-
148
-
149
- class SubdivideBlock(nn.Module):
150
- def __init__(self, input_channels, channels):
151
- super().__init__()
152
- self.act_layers = Sequential(SparseGroupNorm(input_channels), nn.SiLU())
153
- self.out_layers = Sequential(
154
- SparseConv(input_channels, channels),
155
- SparseGroupNorm(channels),
156
- nn.SiLU(),
157
- SparseConv(channels, channels),
158
- )
159
- self.skip_connection = SparseConv(input_channels, channels, kernel=1)
160
-
161
- def __call__(self, x, grid):
162
- h = mx.repeat(self.act_layers(x), 8, axis=0)
163
- x = mx.repeat(x, 8, axis=0)
164
- grid = grid.subdivide()
165
- h = self.out_layers["0"](h, grid)
166
- h = self.out_layers["2"](self.out_layers["1"](h))
167
- h = self.out_layers["3"](h, grid)
168
- return h + self.skip_connection(x, grid), grid
169
-
170
-
171
- class MeshDecoder(DecoderBase):
172
- def __init__(self, config):
173
- super().__init__(config)
174
- c = config.decoder_channels
175
- self.upsample = [SubdivideBlock(c, c // 4), SubdivideBlock(c // 4, c // 8)]
176
- self.out_layer = nn.Linear(c // 8, 101)
177
-
178
- def __call__(self, x, grid):
179
- x = self.encode(x, grid)
180
- for block in self.upsample:
181
- x, grid = block(x, grid)
182
- return self.out_layer(x), grid
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
sam3d_objects_mlx/depth.py DELETED
@@ -1,210 +0,0 @@
1
- # Adapted from microsoft/MoGe (Apache-2.0). See LICENSE-APACHE in this directory.
2
- """Frozen MoGe v1 inference used by the released SAM 3D pipeline."""
3
-
4
- import math
5
- from types import SimpleNamespace
6
-
7
- import mlx.core as mx
8
- import mlx.nn as nn
9
-
10
- from .layers import Sequential
11
- from .resize import resize, resize_bicubic
12
- from .sparse import nonzero
13
- from .vision import DINOv2
14
-
15
-
16
- def view_plane(height, width, aspect=None):
17
- aspect = width / height if aspect is None else aspect
18
- x = aspect / math.sqrt(1 + aspect * aspect)
19
- y = 1 / math.sqrt(1 + aspect * aspect)
20
- u = mx.linspace(-x * (width - 1) / width, x * (width - 1) / width, width)
21
- v = mx.linspace(-y * (height - 1) / height, y * (height - 1) / height, height)
22
- xx, yy = mx.meshgrid(u, v, indexing="xy")
23
- return mx.stack([xx, yy], axis=-1)
24
-
25
-
26
- class GroupNorm(nn.Module):
27
- def __init__(self, channels, groups):
28
- super().__init__()
29
- self.weight = mx.ones(channels)
30
- self.bias = mx.zeros(channels)
31
- self._groups = groups
32
-
33
- def __call__(self, x):
34
- h = x.astype(mx.float32).reshape(
35
- x.shape[0], -1, self._groups, x.shape[-1] // self._groups
36
- )
37
- mean = h.mean(axis=(1, 3), keepdims=True)
38
- var = ((h - mean) ** 2).mean(axis=(1, 3), keepdims=True)
39
- return (
40
- ((h - mean) * mx.rsqrt(var + 1e-5)).reshape(x.shape) * self.weight
41
- + self.bias
42
- ).astype(x.dtype)
43
-
44
-
45
- class Conv(nn.Conv2d):
46
- def __call__(self, x):
47
- p = self.weight.shape[1] // 2
48
- if p:
49
- x = mx.pad(x, [(0, 0), (p, p), (p, p), (0, 0)], mode="edge")
50
- return mx.conv2d(x, self.weight) + self.bias
51
-
52
-
53
- class Residual(nn.Module):
54
- def __init__(self, channels, multiplier=2):
55
- super().__init__()
56
- hidden = channels * multiplier
57
- self.layers = Sequential(
58
- GroupNorm(channels, 1),
59
- nn.ReLU(),
60
- Conv(channels, hidden, 3),
61
- GroupNorm(hidden, hidden // 32),
62
- nn.ReLU(),
63
- Conv(hidden, channels, 3),
64
- )
65
-
66
- def __call__(self, x):
67
- return x + self.layers(x)
68
-
69
-
70
- class Head(nn.Module):
71
- def __init__(self):
72
- super().__init__()
73
- self.projects = [nn.Conv2d(1024, 512, 1) for _ in range(4)]
74
- self.upsample_blocks = [
75
- Sequential(
76
- Sequential(nn.ConvTranspose2d(i + 2, o, 2, stride=2), Conv(o, o, 3)),
77
- Residual(o),
78
- Residual(o),
79
- )
80
- for i, o in zip((512, 256, 128), (256, 128, 64))
81
- ]
82
- self.output_block = [
83
- Sequential(Conv(66, 32, 3), nn.ReLU(), Conv(32, out, 1)) for out in (3, 1)
84
- ]
85
-
86
- def __call__(self, features, height, width):
87
- x = sum(proj(f) for proj, f in zip(self.projects, features))
88
- aspect = width / height
89
- for block in self.upsample_blocks:
90
- uv = view_plane(x.shape[1], x.shape[2], aspect)[None].astype(x.dtype)
91
- x = block(mx.concatenate([x, uv], axis=-1))
92
- x = resize(x, height, width)
93
- x = mx.concatenate(
94
- [x, view_plane(height, width, aspect)[None].astype(x.dtype)], axis=-1
95
- )
96
- return [block(x) for block in self.output_block]
97
-
98
-
99
- class Backbone(DINOv2):
100
- def __init__(self):
101
- super().__init__(
102
- SimpleNamespace(
103
- dino_hidden_size=1024,
104
- dino_image_size=518,
105
- dino_heads=16,
106
- dino_layers=24,
107
- )
108
- )
109
- self.register_tokens = None
110
-
111
- def __call__(self, image):
112
- h, w = image.shape[1] // 14, image.shape[2] // 14
113
- x = self.patch_embed(image)
114
- pos = self.pos_embed[:, 1:].astype(mx.float32).reshape(1, 37, 37, 1024)
115
- if h != 37 or w != 37:
116
- pos = resize_bicubic(pos, h, w, offset=0.1)
117
- pos = mx.concatenate([self.pos_embed[:, :1], pos.reshape(1, -1, 1024)], axis=1)
118
- x = mx.concatenate([self.cls_token, x], axis=1) + pos.astype(x.dtype)
119
- outputs = []
120
- for i, block in enumerate(self.blocks):
121
- x = block(x)
122
- if i >= 20:
123
- outputs.append(self.norm(x)[:, 1:].reshape(1, h, w, 1024))
124
- return outputs
125
-
126
-
127
- def recover_focal_shift(points, valid):
128
- """One-dimensional damped least squares, with an analytic MLX Jacobian."""
129
- h, w = points.shape[:2]
130
- selected = nonzero(
131
- resize(valid[None, ..., None].astype(mx.float32), 64, 64, nearest=True)[
132
- 0, ..., 0
133
- ]
134
- > 0
135
- )
136
- if selected.size < 2:
137
- return mx.array(1.0), mx.array(0.0)
138
- xyz = resize(points[None], 64, 64, nearest=True)[0].reshape(-1, 3)[selected]
139
- uv = resize(view_plane(h, w)[None], 64, 64, nearest=True)[0].reshape(-1, 2)[
140
- selected
141
- ]
142
-
143
- def residual(shift):
144
- z = xyz[:, 2:3] + shift
145
- p = xyz[:, :2] / z
146
- dp = -p / z
147
- a, b = mx.sum(p * uv), mx.sum(p * p)
148
- focal = a / b
149
- df = (mx.sum(dp * uv) * b - 2 * a * mx.sum(p * dp)) / (b * b)
150
- return focal * p - uv, df * p + focal * dp, focal
151
-
152
- shift, damping = mx.array(0.0), 1e-3
153
- error, jac, focal = residual(shift)
154
- cost = float(mx.sum(error * error).item())
155
- for _ in range(100):
156
- a, g = mx.sum(jac * jac), mx.sum(jac * error)
157
- improved = False
158
- for _ in range(20):
159
- candidate = shift - g / (a * (1 + damping) + 1e-12)
160
- new_error, new_jac, new_focal = residual(candidate)
161
- new_cost = float(mx.sum(new_error * new_error).item())
162
- if math.isfinite(new_cost) and new_cost < cost:
163
- shift, error, jac, focal = candidate, new_error, new_jac, new_focal
164
- if cost - new_cost < 1e-3 * max(cost, 1e-12):
165
- return focal, shift
166
- cost, damping, improved = new_cost, max(damping / 10, 1e-12), True
167
- break
168
- damping *= 10
169
- if not improved:
170
- break
171
- return focal, shift
172
-
173
-
174
- class DepthModel(nn.Module):
175
- def __init__(self):
176
- super().__init__()
177
- self.backbone = Backbone()
178
- self.head = Head()
179
- self.image_mean = mx.array([0.485, 0.456, 0.406]).reshape(1, 3, 1, 1)
180
- self.image_std = mx.array([0.229, 0.224, 0.225]).reshape(1, 3, 1, 1)
181
-
182
- def forward(self, image, num_tokens=2551):
183
- h, w = image.shape[:2]
184
- factor = math.sqrt(num_tokens * 14 * 14 / (h * w))
185
- rh, rw = int(h * factor), int(w * factor)
186
- x = resize_bicubic(image[None].astype(mx.float32), rh, rw, antialias=True)
187
- x = (x - self.image_mean.transpose(0, 2, 3, 1)) / self.image_std.transpose(
188
- 0, 2, 3, 1
189
- )
190
- x14 = resize(x, rh // 14 * 14, rw // 14 * 14, antialias=True)
191
- features = self.backbone(x14.astype(self.backbone.cls_token.dtype))
192
- points, mask = self.head(features, rh, rw)
193
- points, mask = (
194
- resize(points.astype(mx.float32), h, w)[0],
195
- resize(mask.astype(mx.float32), h, w)[0, ..., 0],
196
- )
197
- z = mx.exp(points[..., 2:])
198
- points = mx.concatenate([points[..., :2] * z, z], axis=-1)
199
- return points, mask
200
-
201
- def __call__(self, image, num_tokens=2551):
202
- image = image[..., :3]
203
- if image.dtype == mx.uint8:
204
- image = image.astype(mx.float32) / 255
205
- points, mask = self.forward(image, num_tokens)
206
- valid = mask > 0.5
207
- focal, shift = recover_focal_shift(points, valid)
208
- points = points + mx.stack([mx.array(0.0), mx.array(0.0), shift])
209
- # MoGe/OpenCV to SAM's PyTorch3D camera convention.
210
- return mx.where(valid[..., None], points * mx.array([-1, -1, 1]), float("nan"))
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
sam3d_objects_mlx/flow.py DELETED
@@ -1,132 +0,0 @@
1
- # Adapted from Meta SAM 3D Objects. See LICENSE in this directory.
2
- """Structure/pose and sparse-latent flow networks."""
3
-
4
- import mlx.core as mx
5
- import mlx.nn as nn
6
-
7
- from .layers import (
8
- LayerNorm,
9
- ModulatedBlock,
10
- Sequential,
11
- TimestepEmbedder,
12
- layer_norm,
13
- position_embedding,
14
- )
15
- from .sparse import SparseConv, pool
16
-
17
- LATENTS = {
18
- "6drotation_normalized": 6,
19
- "scale": 3,
20
- "shape": 8,
21
- "translation": 3,
22
- "translation_scale": 1,
23
- }
24
- POSE_NAMES = ("6drotation_normalized", "translation", "scale", "translation_scale")
25
-
26
-
27
- class Latent(nn.Module):
28
- def __init__(self, input_channels, channels, length):
29
- super().__init__()
30
- self.input_layer = nn.Linear(input_channels, channels)
31
- self.out_layer = nn.Linear(channels, input_channels)
32
- self.pos_emb = mx.zeros((length, channels))
33
-
34
- def encode(self, x):
35
- return self.input_layer(x) + self.pos_emb[None]
36
-
37
- def decode(self, x):
38
- return self.out_layer(layer_norm(x))
39
-
40
-
41
- class StructureFlow(nn.Module):
42
- def __init__(self, config):
43
- super().__init__()
44
- c = config.hidden_size
45
- self.d_embedder = TimestepEmbedder(c)
46
- self.t_embedder = TimestepEmbedder(c)
47
- self.latent_mapping = {
48
- n: Latent(ch, c, config.latent_resolution**3 if n == "shape" else 1)
49
- for n, ch in LATENTS.items()
50
- }
51
- self.blocks = [
52
- ModulatedBlock(
53
- c,
54
- config.num_heads,
55
- config.cond_channels,
56
- names=("shape", "6drotation_normalized"),
57
- )
58
- for _ in range(config.num_blocks)
59
- ]
60
-
61
- def __call__(self, latents, time, condition, delta=None):
62
- emb = self.t_embedder(time)
63
- if delta is not None:
64
- emb = emb + self.d_embedder(delta)
65
- x = {
66
- n: m.encode(latents[n].astype(m.input_layer.weight.dtype))
67
- for n, m in self.latent_mapping.items()
68
- }
69
- h = {
70
- "6drotation_normalized": mx.concatenate([x[n] for n in POSE_NAMES], axis=1),
71
- "shape": x["shape"],
72
- }
73
- for block in self.blocks:
74
- h = block(h, emb, condition)
75
- outputs = {
76
- n: self.latent_mapping[n].decode(h["6drotation_normalized"][:, i : i + 1])
77
- for i, n in enumerate(POSE_NAMES)
78
- }
79
- outputs["shape"] = self.latent_mapping["shape"].decode(h["shape"])
80
- return outputs
81
-
82
-
83
- class SparseResBlock(nn.Module):
84
- def __init__(self, input_channels, channels, time_channels):
85
- super().__init__()
86
- self.norm1 = LayerNorm(input_channels, eps=1e-6)
87
- self.conv1 = SparseConv(input_channels, channels)
88
- self.conv2 = SparseConv(channels, channels)
89
- self.emb_layers = Sequential(nn.SiLU(), nn.Linear(time_channels, 2 * channels))
90
- self.skip_connection = (
91
- nn.Linear(input_channels, channels)
92
- if channels != input_channels
93
- else nn.Identity()
94
- )
95
-
96
- def __call__(self, x, grid, time):
97
- scale, shift = mx.split(self.emb_layers(time), 2, axis=-1)
98
- h = self.conv1(nn.silu(self.norm1(x)), grid)
99
- h = layer_norm(h, 1e-6) * (1 + scale) + shift
100
- return self.conv2(nn.silu(h), grid) + self.skip_connection(x)
101
-
102
-
103
- class LatentFlow(nn.Module):
104
- def __init__(self, config):
105
- super().__init__()
106
- c, io = config.hidden_size, config.io_channels
107
- self.t_embedder = TimestepEmbedder(c)
108
- self.input_layer = nn.Linear(config.latent_channels, io)
109
- self.input_blocks = [SparseResBlock(io, io, c), SparseResBlock(io, c, c)]
110
- self.blocks = [
111
- ModulatedBlock(c, config.num_heads, config.cond_channels)
112
- for _ in range(config.num_blocks)
113
- ]
114
- self.out_blocks = [SparseResBlock(2 * c, io, c), SparseResBlock(2 * io, io, c)]
115
- self.out_layer = nn.Linear(io, config.latent_channels)
116
-
117
- def __call__(self, x, grid, time, condition):
118
- x = x.astype(self.input_layer.weight.dtype)
119
- h = self.input_layer(x)
120
- emb = self.t_embedder(time)
121
- skip = self.input_blocks[0](h, grid, emb)
122
- h, child, parent = pool(skip, grid)
123
- h = self.input_blocks[1](h, child, emb)
124
- skip_low = h
125
- h = h + position_embedding(child.coords[:, 1:], h.shape[-1]).astype(h.dtype)
126
- h = h[None]
127
- for block in self.blocks:
128
- h = block(h, emb, condition)
129
- h = mx.concatenate([h[0], skip_low], axis=-1)[parent]
130
- h = self.out_blocks[0](h, grid, emb)
131
- h = self.out_blocks[1](mx.concatenate([h, skip], axis=-1), grid, emb)
132
- return self.out_layer(layer_norm(h))
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
sam3d_objects_mlx/generate.py DELETED
@@ -1,122 +0,0 @@
1
- """CLI for single requests or a JSONL stream of image-to-3D requests."""
2
-
3
- import argparse
4
- import asyncio
5
- import json
6
- import sys
7
- from pathlib import Path
8
-
9
- import mlx.core as mx
10
- from mlx.utils import tree_flatten
11
-
12
- from .io import read_mask, read_png, write_gaussians, write_obj
13
- from .pipeline import Pipeline, Request
14
-
15
-
16
- def _request(values, defaults):
17
- image = read_png(values["image"])
18
- mask = read_mask(values["mask"]) if values.get("mask") else None
19
- pointmap = mx.load(values["pointmap"])["points"] if values.get("pointmap") else None
20
- return Request(
21
- image,
22
- mask,
23
- pointmap,
24
- request_id=str(values.get("id", "0")),
25
- seed=int(values.get("seed", defaults.seed)),
26
- ss_steps=defaults.ss_steps,
27
- slat_steps=defaults.slat_steps,
28
- formats=tuple(defaults.formats),
29
- estimate_depth=not defaults.no_depth,
30
- )
31
-
32
-
33
- def _save(result, root):
34
- root.mkdir(parents=True, exist_ok=True)
35
- tensors = {k: v for k, v in tree_flatten(result) if isinstance(v, mx.array)}
36
- mx.save_safetensors(str(root / "result.safetensors"), tensors)
37
- for kind in ("gaussian", "gaussian_4"):
38
- if kind in result:
39
- write_gaussians(root / f"{kind}.ply", result[kind])
40
- if "mesh" in result:
41
- write_obj(root / "mesh.obj", result["mesh"])
42
-
43
-
44
- def main():
45
- parser = argparse.ArgumentParser(description=__doc__)
46
- parser.add_argument("--model", required=True, help="Local converted BF16 bundle")
47
- parser.add_argument("--image", help="PNG RGB or RGBA image")
48
- parser.add_argument("--mask", help="PNG object mask")
49
- parser.add_argument(
50
- "--pointmap", help="Safetensors file with an HWC 'points' array"
51
- )
52
- parser.add_argument(
53
- "--jsonl",
54
- action="store_true",
55
- help="Read JSON requests from stdin and stream JSON progress to stdout",
56
- )
57
- parser.add_argument("--output", required=True, help="Output directory")
58
- parser.add_argument("--seed", type=int, default=42)
59
- parser.add_argument("--ss-steps", type=int)
60
- parser.add_argument("--slat-steps", type=int)
61
- parser.add_argument(
62
- "--formats",
63
- nargs="+",
64
- choices=("gaussian", "gaussian_4", "mesh"),
65
- default=["gaussian", "mesh"],
66
- )
67
- parser.add_argument(
68
- "--no-depth",
69
- action="store_true",
70
- help="Use trained point-map dropout when no map is supplied",
71
- )
72
- args = parser.parse_args()
73
- if not args.jsonl and not args.image:
74
- parser.error("Provide --image or --jsonl")
75
- pipeline = Pipeline.from_pretrained(args.model)
76
- if not args.jsonl:
77
- request = _request(
78
- {"image": args.image, "mask": args.mask, "pointmap": args.pointmap}, args
79
- )
80
- for event in pipeline.stream(request):
81
- print(
82
- json.dumps(
83
- {
84
- "id": event.request_id,
85
- "stage": event.stage,
86
- "step": event.step,
87
- "total_steps": event.total_steps,
88
- }
89
- ),
90
- flush=True,
91
- )
92
- if event.stage == "complete":
93
- _save(event.data, Path(args.output))
94
- return
95
-
96
- async def run():
97
- async def requests():
98
- while line := await asyncio.to_thread(sys.stdin.readline):
99
- if line.strip():
100
- yield _request(json.loads(line), args)
101
-
102
- request_index = 0
103
- async for event in pipeline.astream(requests()):
104
- record = {
105
- "id": event.request_id,
106
- "stage": event.stage,
107
- "step": event.step,
108
- "total_steps": event.total_steps,
109
- }
110
- if event.stage == "complete":
111
- # Input IDs are labels, never filesystem paths.
112
- output = Path(args.output) / f"{request_index:06d}"
113
- await asyncio.to_thread(_save, event.data, output)
114
- record["output"] = str(output)
115
- request_index += 1
116
- print(json.dumps(record), flush=True)
117
-
118
- asyncio.run(run())
119
-
120
-
121
- if __name__ == "__main__":
122
- main()
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
sam3d_objects_mlx/io.py DELETED
@@ -1,154 +0,0 @@
1
- """PNG input and binary PLY/OBJ output using only the standard library."""
2
-
3
- import struct
4
- import zlib
5
- from pathlib import Path
6
-
7
- import mlx.core as mx
8
-
9
-
10
- def read_png(path):
11
- """Read non-interlaced 8-bit grayscale, RGB, RGBA, or indexed PNG."""
12
- raw = Path(path).read_bytes()
13
- if raw[:8] != b"\x89PNG\r\n\x1a\n":
14
- raise ValueError("Expected a PNG image; other codecs need an external decoder")
15
- offset, compressed, palette, transparency = 8, bytearray(), None, None
16
- header = None
17
- while offset + 12 <= len(raw):
18
- length = struct.unpack_from(">I", raw, offset)[0]
19
- kind = raw[offset + 4 : offset + 8]
20
- data = raw[offset + 8 : offset + 8 + length]
21
- if len(data) != length or offset + 12 + length > len(raw):
22
- raise ValueError("Truncated PNG chunk")
23
- crc = struct.unpack_from(">I", raw, offset + 8 + length)[0]
24
- if zlib.crc32(kind + data) & 0xFFFFFFFF != crc:
25
- raise ValueError("PNG checksum mismatch")
26
- if kind == b"IHDR":
27
- header = struct.unpack(">IIBBBBB", data)
28
- elif kind == b"IDAT":
29
- compressed.extend(data)
30
- elif kind == b"PLTE":
31
- palette = data
32
- elif kind == b"tRNS":
33
- transparency = data
34
- elif kind == b"IEND":
35
- break
36
- offset += length + 12
37
- if header is None:
38
- raise ValueError("PNG has no header")
39
- width, height, bits, color, compression, filtering, interlace = header
40
- channels = {0: 1, 2: 3, 3: 1, 4: 2, 6: 4}.get(color)
41
- if bits != 8 or channels is None or compression or filtering or interlace:
42
- raise ValueError("Only non-interlaced 8-bit PNG images are supported")
43
- stride = width * channels
44
- data = zlib.decompress(compressed)
45
- if len(data) != height * (stride + 1):
46
- raise ValueError("PNG pixel data has an invalid size")
47
- output = bytearray(height * stride)
48
- for y in range(height):
49
- mode = data[y * (stride + 1)]
50
- if mode > 4:
51
- raise ValueError("Invalid PNG filter")
52
- for x in range(stride):
53
- value = data[y * (stride + 1) + x + 1]
54
- a = output[y * stride + x - channels] if x >= channels else 0
55
- b = output[(y - 1) * stride + x] if y else 0
56
- c = output[(y - 1) * stride + x - channels] if y and x >= channels else 0
57
- if mode == 1:
58
- value += a
59
- elif mode == 2:
60
- value += b
61
- elif mode == 3:
62
- value += (a + b) // 2
63
- elif mode == 4:
64
- p = a + b - c
65
- pa, pb, pc = abs(p - a), abs(p - b), abs(p - c)
66
- value += a if pa <= pb and pa <= pc else b if pb <= pc else c
67
- output[y * stride + x] = value & 255
68
- pixels = mx.array(memoryview(output)).reshape(height, width, channels)
69
- if color == 3:
70
- if palette is None or len(palette) % 3:
71
- raise ValueError("Invalid PNG palette")
72
- colors = mx.array(memoryview(palette)).reshape(-1, 3)
73
- indices = pixels[..., 0].astype(mx.int32)
74
- if int(indices.max().item()) >= colors.shape[0]:
75
- raise ValueError("PNG palette index out of bounds")
76
- pixels = colors[indices]
77
- if transparency is not None:
78
- alpha = mx.array(
79
- list(transparency) + [255] * (colors.shape[0] - len(transparency)),
80
- mx.uint8,
81
- )
82
- pixels = mx.concatenate([pixels, alpha[indices, None]], axis=-1)
83
- return pixels
84
-
85
-
86
- def read_mask(path):
87
- """Read grayscale masks or the alpha channel of RGBA object cutouts."""
88
- pixels = read_png(path)
89
- channel = -1 if pixels.shape[-1] in (2, 4) else 0
90
- return pixels[..., channel] > 0
91
-
92
-
93
- def write_gaussians(path, gaussian):
94
- """Write the standard Gaussian-splat PLY fields (log-scales/logit opacity)."""
95
- xyz, scale, opacity = (
96
- gaussian["positions"],
97
- gaussian["scales"],
98
- gaussian["opacities"],
99
- )
100
- opacity = mx.clip(opacity, 1e-7, 1 - 1e-7)
101
- values = mx.concatenate(
102
- [
103
- xyz,
104
- mx.zeros_like(xyz),
105
- gaussian["sh_dc"],
106
- mx.log(opacity / (1 - opacity)),
107
- mx.log(scale),
108
- gaussian["rotations"],
109
- ],
110
- axis=-1,
111
- ).astype(mx.float32)
112
- fields = [
113
- "x",
114
- "y",
115
- "z",
116
- "nx",
117
- "ny",
118
- "nz",
119
- "f_dc_0",
120
- "f_dc_1",
121
- "f_dc_2",
122
- "opacity",
123
- "scale_0",
124
- "scale_1",
125
- "scale_2",
126
- "rot_0",
127
- "rot_1",
128
- "rot_2",
129
- "rot_3",
130
- ]
131
- header = (
132
- "ply\nformat binary_little_endian 1.0\n"
133
- + f"element vertex {values.shape[0]}\n"
134
- + "".join(f"property float {f}\n" for f in fields)
135
- + "end_header\n"
136
- )
137
- with open(path, "wb") as out:
138
- out.write(header.encode())
139
- for start in range(0, values.shape[0], 16384):
140
- for row in values[start : start + 16384].tolist():
141
- out.write(struct.pack("<17f", *row))
142
-
143
-
144
- def write_obj(path, mesh):
145
- with open(path, "w") as out:
146
- vertices = mx.concatenate(
147
- [mesh["vertices"], mesh["vertex_colors"][:, :3]], axis=-1
148
- )
149
- for start in range(0, vertices.shape[0], 16384):
150
- for row in vertices[start : start + 16384].tolist():
151
- out.write("v " + " ".join(f"{v:.8g}" for v in row) + "\n")
152
- for start in range(0, mesh["faces"].shape[0], 16384):
153
- for row in mesh["faces"][start : start + 16384].tolist():
154
- out.write("f " + " ".join(str(v + 1) for v in row) + "\n")
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
sam3d_objects_mlx/layers.py DELETED
@@ -1,194 +0,0 @@
1
- # Adapted from Meta SAM 3D Objects. See LICENSE in this directory.
2
- """Dense inference layers; all tensors and computation use MLX."""
3
-
4
- import math
5
-
6
- import mlx.core as mx
7
- import mlx.nn as nn
8
-
9
-
10
- class Sequential(nn.Module):
11
- """Keep the upstream numbered state-dict names."""
12
-
13
- def __init__(self, *layers):
14
- super().__init__()
15
- for i, layer in enumerate(layers):
16
- self[str(i)] = layer
17
-
18
- def __call__(self, x):
19
- for layer in self.values():
20
- x = layer(x)
21
- return x
22
-
23
- def __getitem__(self, key):
24
- return super().__getitem__(str(key) if isinstance(key, int) else key)
25
-
26
- def __setitem__(self, key, value):
27
- super().__setitem__(str(key) if isinstance(key, int) else key, value)
28
-
29
-
30
- def layer_norm(x, eps=1e-5):
31
- return mx.fast.layer_norm(x, None, None, eps)
32
-
33
-
34
- class LayerNorm(nn.LayerNorm):
35
- def __call__(self, x):
36
- y = super().__call__(x.astype(mx.float32))
37
- return y.astype(x.dtype)
38
-
39
-
40
- def position_embedding(coords, channels):
41
- dim = channels // 6
42
- freq = mx.exp(-math.log(10000) * mx.arange(dim, dtype=mx.float32) / dim)
43
- phase = coords.astype(mx.float32)[..., None] * freq
44
- out = mx.concatenate([mx.sin(phase), mx.cos(phase)], axis=-1)
45
- out = out.reshape(coords.shape[0], -1)
46
- return mx.pad(out, [(0, 0), (0, channels - out.shape[-1])])
47
-
48
-
49
- class TimestepEmbedder(nn.Module):
50
- def __init__(self, channels):
51
- super().__init__()
52
- self.mlp = Sequential(
53
- nn.Linear(256, channels), nn.SiLU(), nn.Linear(channels, channels)
54
- )
55
-
56
- def __call__(self, t):
57
- phase = t.reshape(-1, 1).astype(mx.float32) * mx.exp(
58
- -math.log(10000) * mx.arange(128, dtype=mx.float32) / 128
59
- )
60
- x = mx.concatenate([mx.cos(phase), mx.sin(phase)], axis=-1)
61
- return self.mlp(x.astype(self.mlp["0"].weight.dtype))
62
-
63
-
64
- class FeedForward(nn.Module):
65
- def __init__(self, channels, ratio=4):
66
- super().__init__()
67
- self.mlp = Sequential(
68
- nn.Linear(channels, int(channels * ratio)),
69
- nn.GELU(approx="tanh"),
70
- nn.Linear(int(channels * ratio), channels),
71
- )
72
-
73
- def __call__(self, x):
74
- return self.mlp(x)
75
-
76
-
77
- class HeadNorm(nn.Module):
78
- def __init__(self, heads, dim):
79
- super().__init__()
80
- self.gamma = mx.ones((heads, dim))
81
-
82
- def __call__(self, x):
83
- xf = x.astype(mx.float32)
84
- y = xf * mx.rsqrt(mx.maximum(mx.sum(xf * xf, axis=-1, keepdims=True), 1e-24))
85
- return (y * self.gamma * math.sqrt(x.shape[-1])).astype(x.dtype)
86
-
87
-
88
- def attend(q, k, v, mask=None):
89
- y = mx.fast.scaled_dot_product_attention(
90
- q.transpose(0, 2, 1, 3),
91
- k.transpose(0, 2, 1, 3),
92
- v.transpose(0, 2, 1, 3),
93
- scale=q.shape[-1] ** -0.5,
94
- mask=mask,
95
- )
96
- return y.transpose(0, 2, 1, 3).reshape(q.shape[0], q.shape[1], -1)
97
-
98
-
99
- class Attention(nn.Module):
100
- def __init__(self, channels, heads, context_channels=None, qk_norm=False):
101
- super().__init__()
102
- self.heads = heads
103
- if context_channels is None:
104
- self.to_qkv = nn.Linear(channels, channels * 3)
105
- else:
106
- self.to_q = nn.Linear(channels, channels)
107
- self.to_kv = nn.Linear(context_channels, channels * 2)
108
- if qk_norm:
109
- self.q_rms_norm = HeadNorm(heads, channels // heads)
110
- self.k_rms_norm = HeadNorm(heads, channels // heads)
111
- self.to_out = nn.Linear(channels, channels)
112
-
113
- def __call__(self, x, context=None, mask=None):
114
- b, n, c = x.shape
115
- if context is None:
116
- qkv = self.to_qkv(x).reshape(b, n, 3, self.heads, -1)
117
- q, k, v = (qkv[:, :, i] for i in range(3))
118
- else:
119
- q = self.to_q(x).reshape(b, n, self.heads, -1)
120
- kv = self.to_kv(context).reshape(b, -1, 2, self.heads, c // self.heads)
121
- k, v = kv[:, :, 0], kv[:, :, 1]
122
- if "q_rms_norm" in self:
123
- q, k = self.q_rms_norm(q), self.k_rms_norm(k)
124
- return self.to_out(attend(q, k, v, mask))
125
-
126
-
127
- class MOTAttention(nn.Module):
128
- def __init__(self, channels, heads, names):
129
- super().__init__()
130
- self.heads = heads
131
- self.to_qkv = {n: nn.Linear(channels, 3 * channels) for n in names}
132
- self.to_out = {n: nn.Linear(channels, channels) for n in names}
133
- self.q_rms_norm = {n: HeadNorm(heads, channels // heads) for n in names}
134
- self.k_rms_norm = {n: HeadNorm(heads, channels // heads) for n in names}
135
-
136
- def __call__(self, x):
137
- q, k, v = {}, {}, {}
138
- for name, value in x.items():
139
- qkv = self.to_qkv[name](value).reshape(*value.shape[:2], 3, self.heads, -1)
140
- q[name] = self.q_rms_norm[name](qkv[:, :, 0])
141
- k[name] = self.k_rms_norm[name](qkv[:, :, 1])
142
- v[name] = qkv[:, :, 2]
143
- out = {
144
- "shape": self.to_out["shape"](attend(q["shape"], k["shape"], v["shape"]))
145
- }
146
- others = [n for n in x if n != "shape"]
147
- keys = mx.concatenate([k[n] for n in others] + [k["shape"]], axis=1)
148
- vals = mx.concatenate([v[n] for n in others] + [v["shape"]], axis=1)
149
- for n in others:
150
- out[n] = self.to_out[n](attend(q[n], keys, vals))
151
- return out
152
-
153
-
154
- class ModulatedBlock(nn.Module):
155
- def __init__(self, channels, heads, context_channels, names=None):
156
- super().__init__()
157
- self.adaLN_modulation = Sequential(nn.SiLU(), nn.Linear(channels, 6 * channels))
158
- if names is None:
159
- self.norm2 = LayerNorm(channels, eps=1e-6)
160
- self.self_attn = Attention(channels, heads, qk_norm=True)
161
- self.cross_attn = Attention(channels, heads, context_channels)
162
- self.mlp = FeedForward(channels)
163
- else:
164
- self.norm2 = {n: LayerNorm(channels, eps=1e-6) for n in names}
165
- self.self_attn = MOTAttention(channels, heads, names)
166
- self.cross_attn = {
167
- n: Attention(channels, heads, context_channels) for n in names
168
- }
169
- self.mlp = {n: FeedForward(channels) for n in names}
170
-
171
- def __call__(self, x, time, context):
172
- shift, scale, gate, shift_mlp, scale_mlp, gate_mlp = [
173
- v[:, None] for v in mx.split(self.adaLN_modulation(time), 6, axis=-1)
174
- ]
175
- if isinstance(x, dict):
176
- h = self.self_attn(
177
- {n: layer_norm(v, 1e-6) * (1 + scale) + shift for n, v in x.items()}
178
- )
179
- x = {n: v + h[n] * gate for n, v in x.items()}
180
- x = {
181
- n: v + self.cross_attn[n](self.norm2[n](v), context)
182
- for n, v in x.items()
183
- }
184
- return {
185
- n: v
186
- + self.mlp[n](layer_norm(v, 1e-6) * (1 + scale_mlp) + shift_mlp)
187
- * gate_mlp
188
- for n, v in x.items()
189
- }
190
- x = x + self.self_attn(layer_norm(x, 1e-6) * (1 + scale) + shift) * gate
191
- x = x + self.cross_attn(self.norm2(x), context)
192
- return (
193
- x + self.mlp(layer_norm(x, 1e-6) * (1 + scale_mlp) + shift_mlp) * gate_mlp
194
- )
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
sam3d_objects_mlx/mesh.py DELETED
@@ -1,223 +0,0 @@
1
- # Copyright (c) Meta Platforms, Inc. and affiliates.
2
- # Copyright (c) 2025 NVIDIA CORPORATION & AFFILIATES.
3
- # FlexiCubes portions adapted under Apache-2.0; see mesh_tables.py.
4
- """Inference-only FlexiCubes extraction on a sparse MLX grid."""
5
-
6
- import mlx.core as mx
7
-
8
- from .mesh_tables import check_table, dmc_table, num_vd_table
9
- from .sparse import nonzero
10
-
11
- CORNERS = (
12
- (0, 0, 0),
13
- (1, 0, 0),
14
- (0, 1, 0),
15
- (1, 1, 0),
16
- (0, 0, 1),
17
- (1, 0, 1),
18
- (0, 1, 1),
19
- (1, 1, 1),
20
- )
21
- EDGES = (0, 1, 1, 5, 4, 5, 0, 4, 2, 3, 3, 7, 6, 7, 2, 6, 2, 0, 3, 1, 7, 5, 6, 4)
22
-
23
-
24
- def _keys(x, resolution):
25
- return (x[..., 0] * resolution + x[..., 1]) * resolution + x[..., 2]
26
-
27
-
28
- def _decode(keys, resolution):
29
- return mx.stack(
30
- [
31
- keys // (resolution * resolution),
32
- keys // resolution % resolution,
33
- keys % resolution,
34
- ],
35
- axis=-1,
36
- )
37
-
38
-
39
- def unique(keys):
40
- """Sorted unique keys and inverse indices, without NumPy."""
41
- order = mx.argsort(keys)
42
- sorted_keys = keys[order]
43
- start = mx.concatenate([mx.array([True]), sorted_keys[1:] != sorted_keys[:-1]])
44
- first = nonzero(start)
45
- groups = mx.cumsum(start.astype(mx.int32)) - 1
46
- inverse = mx.zeros(keys.size, mx.int32).at[order].add(groups)
47
- return sorted_keys[first], inverse
48
-
49
-
50
- def _lookup(keys, query):
51
- idx = mx.minimum(mx.searchsorted(keys, query.reshape(-1)), keys.size - 1).reshape(
52
- query.shape
53
- )
54
- return mx.where(keys[idx] == query, idx, keys.size)
55
-
56
-
57
- def _empty():
58
- return {
59
- "vertices": mx.zeros((0, 3)),
60
- "faces": mx.zeros((0, 3), mx.int32),
61
- "vertex_colors": mx.zeros((0, 6)),
62
- }
63
-
64
-
65
- def extract_mesh(features, grid):
66
- """Decode all learned SDF, deformation, alpha/beta/gamma and color values.
67
-
68
- Candidate cubes include neighbors of predicted vertices. This matches
69
- the dense reference's default +1 SDF outside the sparse prediction.
70
- """
71
- if not features.shape[0]:
72
- return _empty()
73
- r = grid.resolution
74
- coords = grid.coords[:, 1:]
75
- f = features.astype(mx.float32)
76
- corner = mx.array(CORNERS, dtype=mx.int32)
77
- vkeys, inverse = unique(_keys(coords[:, None] + corner[None], r + 1).reshape(-1))
78
- values = mx.concatenate(
79
- [
80
- f[:, :8, None] - 1 / r,
81
- f[:, 8:32].reshape(-1, 8, 3),
82
- f[:, 53:].reshape(-1, 8, 6),
83
- ],
84
- axis=-1,
85
- ).reshape(-1, 10)
86
- sums = mx.zeros((vkeys.size, 10)).at[inverse].add(values)
87
- counts = mx.zeros((vkeys.size, 1)).at[inverse].add(mx.ones((inverse.size, 1)))
88
- attrs = sums / counts
89
- vcoords = _decode(vkeys, r + 1)
90
- candidates = (vcoords[:, None] - corner[None]).reshape(-1, 3)
91
- valid = nonzero(mx.all((candidates >= 0) & (candidates < r), axis=-1))
92
- ckeys, _ = unique(_keys(candidates[valid], r))
93
- ccoords = _decode(ckeys, r)
94
- corners_keys = _keys(ccoords[:, None] + corner[None], r + 1)
95
- lookup = _lookup(vkeys, corners_keys)
96
- sdf = mx.concatenate([attrs[:, 0], mx.ones(1)])
97
- occupancy = sdf[lookup] < 0
98
- sums_occ = occupancy.sum(axis=-1)
99
- surface = nonzero((sums_occ > 0) & (sums_occ < 8))
100
- if not surface.size:
101
- return _empty()
102
- ccoords, ckeys = ccoords[surface], ckeys[surface]
103
- corners_keys = corners_keys[surface]
104
- cases = mx.sum(
105
- occupancy[surface] * mx.array([1, 2, 4, 8, 16, 32, 64, 128]), axis=-1
106
- )
107
- # Resolve the ambiguous C16/C19 cases against adjacent surface cubes.
108
- checks = mx.array(check_table, mx.int32)[cases]
109
- adjacent = ccoords + checks[:, 1:4]
110
- adjacent_index = _lookup(ckeys, _keys(adjacent, r))
111
- problem_flags = mx.concatenate([checks[:, 0], mx.zeros(1, mx.int32)])
112
- invert = (
113
- (checks[:, 0] == 1)
114
- & mx.all((adjacent >= 0) & (adjacent < r), axis=-1)
115
- & (problem_flags[adjacent_index] == 1)
116
- )
117
- cases = mx.where(invert, checks[:, -1], cases)
118
-
119
- # Retain the dense reference's vertex IDs, including default vertices.
120
- all_vkeys, cube_idx = unique(corners_keys.reshape(-1))
121
- cube_idx = cube_idx.reshape(-1, 8)
122
- attr_indices = _lookup(vkeys, all_vkeys)
123
- default_attr = mx.array([[1.0] + [0.0] * 9])
124
- attrs = mx.concatenate([attrs, default_attr])[attr_indices]
125
- vertices = (
126
- _decode(all_vkeys, r + 1).astype(mx.float32) / r
127
- - 0.5
128
- + mx.tanh(attrs[:, 1:4]) * ((1 - 1e-8) / (r * 2))
129
- )
130
- sdf, colors = attrs[:, 0], mx.sigmoid(attrs[:, 4:])
131
- source_keys = _keys(coords, r)
132
- source_order = mx.argsort(source_keys)
133
- weight_indices = _lookup(source_keys[source_order], ckeys)
134
- weights = mx.concatenate([f[source_order, 32:53], mx.zeros((1, 21))])[
135
- weight_indices
136
- ]
137
- beta, alpha = 1 + 0.99 * mx.tanh(weights[:, :12]), 1 + 0.99 * mx.tanh(
138
- weights[:, 12:20]
139
- )
140
- gamma = 0.99 * mx.sigmoid(weights[:, 20]) + 0.005
141
- edges = mx.array(EDGES, mx.int32)
142
- all_edges = cube_idx[:, edges].reshape(-1, 2)
143
- edge_keys = all_edges[:, 0].astype(mx.int64) * all_vkeys.size + all_edges[:, 1]
144
- unique_edge_keys, edge_inverse = unique(edge_keys)
145
- unique_edges = mx.stack(
146
- [unique_edge_keys // all_vkeys.size, unique_edge_keys % all_vkeys.size], axis=-1
147
- ).astype(mx.int32)
148
- crossing = mx.sum(sdf[unique_edges] < 0, axis=-1) == 1
149
- cross_ids = nonzero(crossing)
150
- surface_edges = unique_edges[cross_ids]
151
- mapping = mx.full((unique_edges.shape[0],), -1, mx.int32)
152
- mapping[cross_ids] = mx.arange(cross_ids.size)
153
- idx_map = mapping[edge_inverse].reshape(-1, 12)
154
- edge_counts = (
155
- mx.zeros((unique_edges.shape[0],), mx.int32)
156
- .at[edge_inverse]
157
- .add(mx.ones(edge_inverse.size, mx.int32))[edge_inverse]
158
- )
159
-
160
- # Each topology emits one to four dual vertices; seven edges per group.
161
- table = mx.array(dmc_table, mx.int32)
162
- numbers = mx.array(num_vd_table, mx.int32)[cases]
163
- edge_group, group_vertex, group_cube, gamma_parts = [], [], [], []
164
- total = 0
165
- for number in range(1, 5):
166
- cubes = nonzero(numbers == number)
167
- if not cubes.size:
168
- continue
169
- group = table[cases[cubes], :number].reshape(-1, 7)
170
- valid = nonzero(group >= 0)
171
- edge_group.append(group.reshape(-1)[valid])
172
- group_vertex.append(mx.repeat(mx.arange(group.shape[0]) + total, 7)[valid])
173
- group_cube.append(mx.repeat(cubes, number * 7)[valid])
174
- gamma_parts.append(mx.repeat(gamma[cubes], number))
175
- total += group.shape[0]
176
- edge_group, group_vertex, group_cube = (
177
- mx.concatenate(x) for x in (edge_group, group_vertex, group_cube)
178
- )
179
- vd_gamma = mx.concatenate(gamma_parts)
180
- selected = idx_map[group_cube, edge_group]
181
- pair = surface_edges[selected]
182
- alpha_pair = alpha[:, edges].reshape(-1, 12, 2)[group_cube, edge_group]
183
- signed = sdf[pair] * alpha_pair
184
- interp = mx.stack([signed[:, 1], -signed[:, 0]], axis=-1)
185
- interp = interp / interp.sum(axis=-1, keepdims=True)
186
- crossing_vertices = mx.sum(vertices[pair] * interp[..., None], axis=1)
187
- crossing_colors = mx.sum(colors[pair] * interp[..., None], axis=1)
188
- weight = beta[group_cube, edge_group, None]
189
- denominator = mx.zeros((total, 1)).at[group_vertex].add(weight)
190
- vd = (
191
- mx.zeros((total, 3)).at[group_vertex].add(crossing_vertices * weight)
192
- / denominator
193
- )
194
- vd_colors = (
195
- mx.zeros((total, colors.shape[-1]))
196
- .at[group_vertex]
197
- .add(crossing_colors * weight)
198
- / denominator
199
- )
200
- vd_idx = mx.zeros((cases.size * 12,), mx.int32)
201
- vd_idx[group_cube * 12 + edge_group] = group_vertex
202
-
203
- group_mask = nonzero((edge_counts == 4) & crossing[edge_inverse])
204
- group = idx_map.reshape(-1)[group_mask]
205
- # Explicit tie breaker gives the stable edge order used by the reference.
206
- order = mx.argsort(group.astype(mx.int64) * group.size + mx.arange(group.size))
207
- quads = vd_idx[group_mask][order].reshape(-1, 4)
208
- edge_ids = group[order].reshape(-1, 4)[:, 0]
209
- flip = sdf[surface_edges[edge_ids, 0]] > 0
210
- quads = mx.concatenate(
211
- [
212
- quads[nonzero(flip)][:, mx.array([0, 1, 3, 2])],
213
- quads[nonzero(~flip)][:, mx.array([2, 3, 1, 0])],
214
- ]
215
- )
216
- g = vd_gamma[quads]
217
- split = g[:, 0] * g[:, 2] > g[:, 1] * g[:, 3]
218
- faces = mx.where(
219
- split[:, None],
220
- quads[:, mx.array([0, 1, 2, 0, 2, 3])],
221
- quads[:, mx.array([0, 1, 3, 3, 1, 2])],
222
- ).reshape(-1, 3)
223
- return {"vertices": vd, "faces": faces.astype(mx.int32), "vertex_colors": vd_colors}
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
sam3d_objects_mlx/mesh_tables.py DELETED
@@ -1,2327 +0,0 @@
1
- # Copyright (c) Meta Platforms, Inc. and affiliates.
2
- # Copyright (c) 2025 NVIDIA CORPORATION & AFFILIATES.
3
- # All rights reserved.
4
- #
5
- # Licensed under the Apache License, Version 2.0 (the "License");
6
- # you may not use this file except in compliance with the License.
7
- # You may obtain a copy of the License at
8
- #
9
- # http://www.apache.org/licenses/LICENSE-2.0
10
- #
11
- # Unless required by applicable law or agreed to in writing, software
12
- # distributed under the License is distributed on an "AS IS" BASIS,
13
- # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
14
- # See the License for the specific language governing permissions and
15
- # limitations under the License.
16
- dmc_table = [
17
- [
18
- [-1, -1, -1, -1, -1, -1, -1],
19
- [-1, -1, -1, -1, -1, -1, -1],
20
- [-1, -1, -1, -1, -1, -1, -1],
21
- [-1, -1, -1, -1, -1, -1, -1],
22
- ],
23
- [
24
- [0, 3, 8, -1, -1, -1, -1],
25
- [-1, -1, -1, -1, -1, -1, -1],
26
- [-1, -1, -1, -1, -1, -1, -1],
27
- [-1, -1, -1, -1, -1, -1, -1],
28
- ],
29
- [
30
- [0, 1, 9, -1, -1, -1, -1],
31
- [-1, -1, -1, -1, -1, -1, -1],
32
- [-1, -1, -1, -1, -1, -1, -1],
33
- [-1, -1, -1, -1, -1, -1, -1],
34
- ],
35
- [
36
- [1, 3, 8, 9, -1, -1, -1],
37
- [-1, -1, -1, -1, -1, -1, -1],
38
- [-1, -1, -1, -1, -1, -1, -1],
39
- [-1, -1, -1, -1, -1, -1, -1],
40
- ],
41
- [
42
- [4, 7, 8, -1, -1, -1, -1],
43
- [-1, -1, -1, -1, -1, -1, -1],
44
- [-1, -1, -1, -1, -1, -1, -1],
45
- [-1, -1, -1, -1, -1, -1, -1],
46
- ],
47
- [
48
- [0, 3, 4, 7, -1, -1, -1],
49
- [-1, -1, -1, -1, -1, -1, -1],
50
- [-1, -1, -1, -1, -1, -1, -1],
51
- [-1, -1, -1, -1, -1, -1, -1],
52
- ],
53
- [
54
- [0, 1, 9, -1, -1, -1, -1],
55
- [4, 7, 8, -1, -1, -1, -1],
56
- [-1, -1, -1, -1, -1, -1, -1],
57
- [-1, -1, -1, -1, -1, -1, -1],
58
- ],
59
- [
60
- [1, 3, 4, 7, 9, -1, -1],
61
- [-1, -1, -1, -1, -1, -1, -1],
62
- [-1, -1, -1, -1, -1, -1, -1],
63
- [-1, -1, -1, -1, -1, -1, -1],
64
- ],
65
- [
66
- [4, 5, 9, -1, -1, -1, -1],
67
- [-1, -1, -1, -1, -1, -1, -1],
68
- [-1, -1, -1, -1, -1, -1, -1],
69
- [-1, -1, -1, -1, -1, -1, -1],
70
- ],
71
- [
72
- [0, 3, 8, -1, -1, -1, -1],
73
- [4, 5, 9, -1, -1, -1, -1],
74
- [-1, -1, -1, -1, -1, -1, -1],
75
- [-1, -1, -1, -1, -1, -1, -1],
76
- ],
77
- [
78
- [0, 1, 4, 5, -1, -1, -1],
79
- [-1, -1, -1, -1, -1, -1, -1],
80
- [-1, -1, -1, -1, -1, -1, -1],
81
- [-1, -1, -1, -1, -1, -1, -1],
82
- ],
83
- [
84
- [1, 3, 4, 5, 8, -1, -1],
85
- [-1, -1, -1, -1, -1, -1, -1],
86
- [-1, -1, -1, -1, -1, -1, -1],
87
- [-1, -1, -1, -1, -1, -1, -1],
88
- ],
89
- [
90
- [5, 7, 8, 9, -1, -1, -1],
91
- [-1, -1, -1, -1, -1, -1, -1],
92
- [-1, -1, -1, -1, -1, -1, -1],
93
- [-1, -1, -1, -1, -1, -1, -1],
94
- ],
95
- [
96
- [0, 3, 5, 7, 9, -1, -1],
97
- [-1, -1, -1, -1, -1, -1, -1],
98
- [-1, -1, -1, -1, -1, -1, -1],
99
- [-1, -1, -1, -1, -1, -1, -1],
100
- ],
101
- [
102
- [0, 1, 5, 7, 8, -1, -1],
103
- [-1, -1, -1, -1, -1, -1, -1],
104
- [-1, -1, -1, -1, -1, -1, -1],
105
- [-1, -1, -1, -1, -1, -1, -1],
106
- ],
107
- [
108
- [1, 3, 5, 7, -1, -1, -1],
109
- [-1, -1, -1, -1, -1, -1, -1],
110
- [-1, -1, -1, -1, -1, -1, -1],
111
- [-1, -1, -1, -1, -1, -1, -1],
112
- ],
113
- [
114
- [2, 3, 11, -1, -1, -1, -1],
115
- [-1, -1, -1, -1, -1, -1, -1],
116
- [-1, -1, -1, -1, -1, -1, -1],
117
- [-1, -1, -1, -1, -1, -1, -1],
118
- ],
119
- [
120
- [0, 2, 8, 11, -1, -1, -1],
121
- [-1, -1, -1, -1, -1, -1, -1],
122
- [-1, -1, -1, -1, -1, -1, -1],
123
- [-1, -1, -1, -1, -1, -1, -1],
124
- ],
125
- [
126
- [0, 1, 9, -1, -1, -1, -1],
127
- [2, 3, 11, -1, -1, -1, -1],
128
- [-1, -1, -1, -1, -1, -1, -1],
129
- [-1, -1, -1, -1, -1, -1, -1],
130
- ],
131
- [
132
- [1, 2, 8, 9, 11, -1, -1],
133
- [-1, -1, -1, -1, -1, -1, -1],
134
- [-1, -1, -1, -1, -1, -1, -1],
135
- [-1, -1, -1, -1, -1, -1, -1],
136
- ],
137
- [
138
- [4, 7, 8, -1, -1, -1, -1],
139
- [2, 3, 11, -1, -1, -1, -1],
140
- [-1, -1, -1, -1, -1, -1, -1],
141
- [-1, -1, -1, -1, -1, -1, -1],
142
- ],
143
- [
144
- [0, 2, 4, 7, 11, -1, -1],
145
- [-1, -1, -1, -1, -1, -1, -1],
146
- [-1, -1, -1, -1, -1, -1, -1],
147
- [-1, -1, -1, -1, -1, -1, -1],
148
- ],
149
- [
150
- [0, 1, 9, -1, -1, -1, -1],
151
- [4, 7, 8, -1, -1, -1, -1],
152
- [2, 3, 11, -1, -1, -1, -1],
153
- [-1, -1, -1, -1, -1, -1, -1],
154
- ],
155
- [
156
- [1, 2, 4, 7, 9, 11, -1],
157
- [-1, -1, -1, -1, -1, -1, -1],
158
- [-1, -1, -1, -1, -1, -1, -1],
159
- [-1, -1, -1, -1, -1, -1, -1],
160
- ],
161
- [
162
- [4, 5, 9, -1, -1, -1, -1],
163
- [2, 3, 11, -1, -1, -1, -1],
164
- [-1, -1, -1, -1, -1, -1, -1],
165
- [-1, -1, -1, -1, -1, -1, -1],
166
- ],
167
- [
168
- [0, 2, 8, 11, -1, -1, -1],
169
- [4, 5, 9, -1, -1, -1, -1],
170
- [-1, -1, -1, -1, -1, -1, -1],
171
- [-1, -1, -1, -1, -1, -1, -1],
172
- ],
173
- [
174
- [0, 1, 4, 5, -1, -1, -1],
175
- [2, 3, 11, -1, -1, -1, -1],
176
- [-1, -1, -1, -1, -1, -1, -1],
177
- [-1, -1, -1, -1, -1, -1, -1],
178
- ],
179
- [
180
- [1, 2, 4, 5, 8, 11, -1],
181
- [-1, -1, -1, -1, -1, -1, -1],
182
- [-1, -1, -1, -1, -1, -1, -1],
183
- [-1, -1, -1, -1, -1, -1, -1],
184
- ],
185
- [
186
- [5, 7, 8, 9, -1, -1, -1],
187
- [2, 3, 11, -1, -1, -1, -1],
188
- [-1, -1, -1, -1, -1, -1, -1],
189
- [-1, -1, -1, -1, -1, -1, -1],
190
- ],
191
- [
192
- [0, 2, 5, 7, 9, 11, -1],
193
- [-1, -1, -1, -1, -1, -1, -1],
194
- [-1, -1, -1, -1, -1, -1, -1],
195
- [-1, -1, -1, -1, -1, -1, -1],
196
- ],
197
- [
198
- [0, 1, 5, 7, 8, -1, -1],
199
- [2, 3, 11, -1, -1, -1, -1],
200
- [-1, -1, -1, -1, -1, -1, -1],
201
- [-1, -1, -1, -1, -1, -1, -1],
202
- ],
203
- [
204
- [1, 2, 5, 7, 11, -1, -1],
205
- [-1, -1, -1, -1, -1, -1, -1],
206
- [-1, -1, -1, -1, -1, -1, -1],
207
- [-1, -1, -1, -1, -1, -1, -1],
208
- ],
209
- [
210
- [1, 2, 10, -1, -1, -1, -1],
211
- [-1, -1, -1, -1, -1, -1, -1],
212
- [-1, -1, -1, -1, -1, -1, -1],
213
- [-1, -1, -1, -1, -1, -1, -1],
214
- ],
215
- [
216
- [0, 3, 8, -1, -1, -1, -1],
217
- [1, 2, 10, -1, -1, -1, -1],
218
- [-1, -1, -1, -1, -1, -1, -1],
219
- [-1, -1, -1, -1, -1, -1, -1],
220
- ],
221
- [
222
- [0, 2, 9, 10, -1, -1, -1],
223
- [-1, -1, -1, -1, -1, -1, -1],
224
- [-1, -1, -1, -1, -1, -1, -1],
225
- [-1, -1, -1, -1, -1, -1, -1],
226
- ],
227
- [
228
- [2, 3, 8, 9, 10, -1, -1],
229
- [-1, -1, -1, -1, -1, -1, -1],
230
- [-1, -1, -1, -1, -1, -1, -1],
231
- [-1, -1, -1, -1, -1, -1, -1],
232
- ],
233
- [
234
- [4, 7, 8, -1, -1, -1, -1],
235
- [1, 2, 10, -1, -1, -1, -1],
236
- [-1, -1, -1, -1, -1, -1, -1],
237
- [-1, -1, -1, -1, -1, -1, -1],
238
- ],
239
- [
240
- [0, 3, 4, 7, -1, -1, -1],
241
- [1, 2, 10, -1, -1, -1, -1],
242
- [-1, -1, -1, -1, -1, -1, -1],
243
- [-1, -1, -1, -1, -1, -1, -1],
244
- ],
245
- [
246
- [0, 2, 9, 10, -1, -1, -1],
247
- [4, 7, 8, -1, -1, -1, -1],
248
- [-1, -1, -1, -1, -1, -1, -1],
249
- [-1, -1, -1, -1, -1, -1, -1],
250
- ],
251
- [
252
- [2, 3, 4, 7, 9, 10, -1],
253
- [-1, -1, -1, -1, -1, -1, -1],
254
- [-1, -1, -1, -1, -1, -1, -1],
255
- [-1, -1, -1, -1, -1, -1, -1],
256
- ],
257
- [
258
- [4, 5, 9, -1, -1, -1, -1],
259
- [1, 2, 10, -1, -1, -1, -1],
260
- [-1, -1, -1, -1, -1, -1, -1],
261
- [-1, -1, -1, -1, -1, -1, -1],
262
- ],
263
- [
264
- [0, 3, 8, -1, -1, -1, -1],
265
- [4, 5, 9, -1, -1, -1, -1],
266
- [1, 2, 10, -1, -1, -1, -1],
267
- [-1, -1, -1, -1, -1, -1, -1],
268
- ],
269
- [
270
- [0, 2, 4, 5, 10, -1, -1],
271
- [-1, -1, -1, -1, -1, -1, -1],
272
- [-1, -1, -1, -1, -1, -1, -1],
273
- [-1, -1, -1, -1, -1, -1, -1],
274
- ],
275
- [
276
- [2, 3, 4, 5, 8, 10, -1],
277
- [-1, -1, -1, -1, -1, -1, -1],
278
- [-1, -1, -1, -1, -1, -1, -1],
279
- [-1, -1, -1, -1, -1, -1, -1],
280
- ],
281
- [
282
- [5, 7, 8, 9, -1, -1, -1],
283
- [1, 2, 10, -1, -1, -1, -1],
284
- [-1, -1, -1, -1, -1, -1, -1],
285
- [-1, -1, -1, -1, -1, -1, -1],
286
- ],
287
- [
288
- [0, 3, 5, 7, 9, -1, -1],
289
- [1, 2, 10, -1, -1, -1, -1],
290
- [-1, -1, -1, -1, -1, -1, -1],
291
- [-1, -1, -1, -1, -1, -1, -1],
292
- ],
293
- [
294
- [0, 2, 5, 7, 8, 10, -1],
295
- [-1, -1, -1, -1, -1, -1, -1],
296
- [-1, -1, -1, -1, -1, -1, -1],
297
- [-1, -1, -1, -1, -1, -1, -1],
298
- ],
299
- [
300
- [2, 3, 5, 7, 10, -1, -1],
301
- [-1, -1, -1, -1, -1, -1, -1],
302
- [-1, -1, -1, -1, -1, -1, -1],
303
- [-1, -1, -1, -1, -1, -1, -1],
304
- ],
305
- [
306
- [1, 3, 10, 11, -1, -1, -1],
307
- [-1, -1, -1, -1, -1, -1, -1],
308
- [-1, -1, -1, -1, -1, -1, -1],
309
- [-1, -1, -1, -1, -1, -1, -1],
310
- ],
311
- [
312
- [0, 1, 8, 10, 11, -1, -1],
313
- [-1, -1, -1, -1, -1, -1, -1],
314
- [-1, -1, -1, -1, -1, -1, -1],
315
- [-1, -1, -1, -1, -1, -1, -1],
316
- ],
317
- [
318
- [0, 3, 9, 10, 11, -1, -1],
319
- [-1, -1, -1, -1, -1, -1, -1],
320
- [-1, -1, -1, -1, -1, -1, -1],
321
- [-1, -1, -1, -1, -1, -1, -1],
322
- ],
323
- [
324
- [8, 9, 10, 11, -1, -1, -1],
325
- [-1, -1, -1, -1, -1, -1, -1],
326
- [-1, -1, -1, -1, -1, -1, -1],
327
- [-1, -1, -1, -1, -1, -1, -1],
328
- ],
329
- [
330
- [4, 7, 8, -1, -1, -1, -1],
331
- [1, 3, 10, 11, -1, -1, -1],
332
- [-1, -1, -1, -1, -1, -1, -1],
333
- [-1, -1, -1, -1, -1, -1, -1],
334
- ],
335
- [
336
- [0, 1, 4, 7, 10, 11, -1],
337
- [-1, -1, -1, -1, -1, -1, -1],
338
- [-1, -1, -1, -1, -1, -1, -1],
339
- [-1, -1, -1, -1, -1, -1, -1],
340
- ],
341
- [
342
- [0, 3, 9, 10, 11, -1, -1],
343
- [4, 7, 8, -1, -1, -1, -1],
344
- [-1, -1, -1, -1, -1, -1, -1],
345
- [-1, -1, -1, -1, -1, -1, -1],
346
- ],
347
- [
348
- [4, 7, 9, 10, 11, -1, -1],
349
- [-1, -1, -1, -1, -1, -1, -1],
350
- [-1, -1, -1, -1, -1, -1, -1],
351
- [-1, -1, -1, -1, -1, -1, -1],
352
- ],
353
- [
354
- [4, 5, 9, -1, -1, -1, -1],
355
- [1, 3, 10, 11, -1, -1, -1],
356
- [-1, -1, -1, -1, -1, -1, -1],
357
- [-1, -1, -1, -1, -1, -1, -1],
358
- ],
359
- [
360
- [0, 1, 8, 10, 11, -1, -1],
361
- [4, 5, 9, -1, -1, -1, -1],
362
- [-1, -1, -1, -1, -1, -1, -1],
363
- [-1, -1, -1, -1, -1, -1, -1],
364
- ],
365
- [
366
- [0, 3, 4, 5, 10, 11, -1],
367
- [-1, -1, -1, -1, -1, -1, -1],
368
- [-1, -1, -1, -1, -1, -1, -1],
369
- [-1, -1, -1, -1, -1, -1, -1],
370
- ],
371
- [
372
- [4, 5, 8, 10, 11, -1, -1],
373
- [-1, -1, -1, -1, -1, -1, -1],
374
- [-1, -1, -1, -1, -1, -1, -1],
375
- [-1, -1, -1, -1, -1, -1, -1],
376
- ],
377
- [
378
- [5, 7, 8, 9, -1, -1, -1],
379
- [1, 3, 10, 11, -1, -1, -1],
380
- [-1, -1, -1, -1, -1, -1, -1],
381
- [-1, -1, -1, -1, -1, -1, -1],
382
- ],
383
- [
384
- [0, 1, 5, 7, 9, 10, 11],
385
- [-1, -1, -1, -1, -1, -1, -1],
386
- [-1, -1, -1, -1, -1, -1, -1],
387
- [-1, -1, -1, -1, -1, -1, -1],
388
- ],
389
- [
390
- [0, 3, 5, 7, 8, 10, 11],
391
- [-1, -1, -1, -1, -1, -1, -1],
392
- [-1, -1, -1, -1, -1, -1, -1],
393
- [-1, -1, -1, -1, -1, -1, -1],
394
- ],
395
- [
396
- [5, 7, 10, 11, -1, -1, -1],
397
- [-1, -1, -1, -1, -1, -1, -1],
398
- [-1, -1, -1, -1, -1, -1, -1],
399
- [-1, -1, -1, -1, -1, -1, -1],
400
- ],
401
- [
402
- [6, 7, 11, -1, -1, -1, -1],
403
- [-1, -1, -1, -1, -1, -1, -1],
404
- [-1, -1, -1, -1, -1, -1, -1],
405
- [-1, -1, -1, -1, -1, -1, -1],
406
- ],
407
- [
408
- [0, 3, 8, -1, -1, -1, -1],
409
- [6, 7, 11, -1, -1, -1, -1],
410
- [-1, -1, -1, -1, -1, -1, -1],
411
- [-1, -1, -1, -1, -1, -1, -1],
412
- ],
413
- [
414
- [0, 1, 9, -1, -1, -1, -1],
415
- [6, 7, 11, -1, -1, -1, -1],
416
- [-1, -1, -1, -1, -1, -1, -1],
417
- [-1, -1, -1, -1, -1, -1, -1],
418
- ],
419
- [
420
- [1, 3, 8, 9, -1, -1, -1],
421
- [6, 7, 11, -1, -1, -1, -1],
422
- [-1, -1, -1, -1, -1, -1, -1],
423
- [-1, -1, -1, -1, -1, -1, -1],
424
- ],
425
- [
426
- [4, 6, 8, 11, -1, -1, -1],
427
- [-1, -1, -1, -1, -1, -1, -1],
428
- [-1, -1, -1, -1, -1, -1, -1],
429
- [-1, -1, -1, -1, -1, -1, -1],
430
- ],
431
- [
432
- [0, 3, 4, 6, 11, -1, -1],
433
- [-1, -1, -1, -1, -1, -1, -1],
434
- [-1, -1, -1, -1, -1, -1, -1],
435
- [-1, -1, -1, -1, -1, -1, -1],
436
- ],
437
- [
438
- [0, 1, 9, -1, -1, -1, -1],
439
- [4, 6, 8, 11, -1, -1, -1],
440
- [-1, -1, -1, -1, -1, -1, -1],
441
- [-1, -1, -1, -1, -1, -1, -1],
442
- ],
443
- [
444
- [1, 3, 4, 6, 9, 11, -1],
445
- [-1, -1, -1, -1, -1, -1, -1],
446
- [-1, -1, -1, -1, -1, -1, -1],
447
- [-1, -1, -1, -1, -1, -1, -1],
448
- ],
449
- [
450
- [4, 5, 9, -1, -1, -1, -1],
451
- [6, 7, 11, -1, -1, -1, -1],
452
- [-1, -1, -1, -1, -1, -1, -1],
453
- [-1, -1, -1, -1, -1, -1, -1],
454
- ],
455
- [
456
- [0, 3, 8, -1, -1, -1, -1],
457
- [4, 5, 9, -1, -1, -1, -1],
458
- [6, 7, 11, -1, -1, -1, -1],
459
- [-1, -1, -1, -1, -1, -1, -1],
460
- ],
461
- [
462
- [0, 1, 4, 5, -1, -1, -1],
463
- [6, 7, 11, -1, -1, -1, -1],
464
- [-1, -1, -1, -1, -1, -1, -1],
465
- [-1, -1, -1, -1, -1, -1, -1],
466
- ],
467
- [
468
- [1, 3, 4, 5, 8, -1, -1],
469
- [6, 7, 11, -1, -1, -1, -1],
470
- [-1, -1, -1, -1, -1, -1, -1],
471
- [-1, -1, -1, -1, -1, -1, -1],
472
- ],
473
- [
474
- [5, 6, 8, 9, 11, -1, -1],
475
- [-1, -1, -1, -1, -1, -1, -1],
476
- [-1, -1, -1, -1, -1, -1, -1],
477
- [-1, -1, -1, -1, -1, -1, -1],
478
- ],
479
- [
480
- [0, 3, 5, 6, 9, 11, -1],
481
- [-1, -1, -1, -1, -1, -1, -1],
482
- [-1, -1, -1, -1, -1, -1, -1],
483
- [-1, -1, -1, -1, -1, -1, -1],
484
- ],
485
- [
486
- [0, 1, 5, 6, 8, 11, -1],
487
- [-1, -1, -1, -1, -1, -1, -1],
488
- [-1, -1, -1, -1, -1, -1, -1],
489
- [-1, -1, -1, -1, -1, -1, -1],
490
- ],
491
- [
492
- [1, 3, 5, 6, 11, -1, -1],
493
- [-1, -1, -1, -1, -1, -1, -1],
494
- [-1, -1, -1, -1, -1, -1, -1],
495
- [-1, -1, -1, -1, -1, -1, -1],
496
- ],
497
- [
498
- [2, 3, 6, 7, -1, -1, -1],
499
- [-1, -1, -1, -1, -1, -1, -1],
500
- [-1, -1, -1, -1, -1, -1, -1],
501
- [-1, -1, -1, -1, -1, -1, -1],
502
- ],
503
- [
504
- [0, 2, 6, 7, 8, -1, -1],
505
- [-1, -1, -1, -1, -1, -1, -1],
506
- [-1, -1, -1, -1, -1, -1, -1],
507
- [-1, -1, -1, -1, -1, -1, -1],
508
- ],
509
- [
510
- [0, 1, 9, -1, -1, -1, -1],
511
- [2, 3, 6, 7, -1, -1, -1],
512
- [-1, -1, -1, -1, -1, -1, -1],
513
- [-1, -1, -1, -1, -1, -1, -1],
514
- ],
515
- [
516
- [1, 2, 6, 7, 8, 9, -1],
517
- [-1, -1, -1, -1, -1, -1, -1],
518
- [-1, -1, -1, -1, -1, -1, -1],
519
- [-1, -1, -1, -1, -1, -1, -1],
520
- ],
521
- [
522
- [2, 3, 4, 6, 8, -1, -1],
523
- [-1, -1, -1, -1, -1, -1, -1],
524
- [-1, -1, -1, -1, -1, -1, -1],
525
- [-1, -1, -1, -1, -1, -1, -1],
526
- ],
527
- [
528
- [0, 2, 4, 6, -1, -1, -1],
529
- [-1, -1, -1, -1, -1, -1, -1],
530
- [-1, -1, -1, -1, -1, -1, -1],
531
- [-1, -1, -1, -1, -1, -1, -1],
532
- ],
533
- [
534
- [0, 1, 9, -1, -1, -1, -1],
535
- [2, 3, 4, 6, 8, -1, -1],
536
- [-1, -1, -1, -1, -1, -1, -1],
537
- [-1, -1, -1, -1, -1, -1, -1],
538
- ],
539
- [
540
- [1, 2, 4, 6, 9, -1, -1],
541
- [-1, -1, -1, -1, -1, -1, -1],
542
- [-1, -1, -1, -1, -1, -1, -1],
543
- [-1, -1, -1, -1, -1, -1, -1],
544
- ],
545
- [
546
- [4, 5, 9, -1, -1, -1, -1],
547
- [2, 3, 6, 7, -1, -1, -1],
548
- [-1, -1, -1, -1, -1, -1, -1],
549
- [-1, -1, -1, -1, -1, -1, -1],
550
- ],
551
- [
552
- [0, 2, 6, 7, 8, -1, -1],
553
- [4, 5, 9, -1, -1, -1, -1],
554
- [-1, -1, -1, -1, -1, -1, -1],
555
- [-1, -1, -1, -1, -1, -1, -1],
556
- ],
557
- [
558
- [0, 1, 4, 5, -1, -1, -1],
559
- [2, 3, 6, 7, -1, -1, -1],
560
- [-1, -1, -1, -1, -1, -1, -1],
561
- [-1, -1, -1, -1, -1, -1, -1],
562
- ],
563
- [
564
- [1, 2, 4, 5, 6, 7, 8],
565
- [-1, -1, -1, -1, -1, -1, -1],
566
- [-1, -1, -1, -1, -1, -1, -1],
567
- [-1, -1, -1, -1, -1, -1, -1],
568
- ],
569
- [
570
- [2, 3, 5, 6, 8, 9, -1],
571
- [-1, -1, -1, -1, -1, -1, -1],
572
- [-1, -1, -1, -1, -1, -1, -1],
573
- [-1, -1, -1, -1, -1, -1, -1],
574
- ],
575
- [
576
- [0, 2, 5, 6, 9, -1, -1],
577
- [-1, -1, -1, -1, -1, -1, -1],
578
- [-1, -1, -1, -1, -1, -1, -1],
579
- [-1, -1, -1, -1, -1, -1, -1],
580
- ],
581
- [
582
- [0, 1, 2, 3, 5, 6, 8],
583
- [-1, -1, -1, -1, -1, -1, -1],
584
- [-1, -1, -1, -1, -1, -1, -1],
585
- [-1, -1, -1, -1, -1, -1, -1],
586
- ],
587
- [
588
- [1, 2, 5, 6, -1, -1, -1],
589
- [-1, -1, -1, -1, -1, -1, -1],
590
- [-1, -1, -1, -1, -1, -1, -1],
591
- [-1, -1, -1, -1, -1, -1, -1],
592
- ],
593
- [
594
- [1, 2, 10, -1, -1, -1, -1],
595
- [6, 7, 11, -1, -1, -1, -1],
596
- [-1, -1, -1, -1, -1, -1, -1],
597
- [-1, -1, -1, -1, -1, -1, -1],
598
- ],
599
- [
600
- [0, 3, 8, -1, -1, -1, -1],
601
- [1, 2, 10, -1, -1, -1, -1],
602
- [6, 7, 11, -1, -1, -1, -1],
603
- [-1, -1, -1, -1, -1, -1, -1],
604
- ],
605
- [
606
- [0, 2, 9, 10, -1, -1, -1],
607
- [6, 7, 11, -1, -1, -1, -1],
608
- [-1, -1, -1, -1, -1, -1, -1],
609
- [-1, -1, -1, -1, -1, -1, -1],
610
- ],
611
- [
612
- [2, 3, 8, 9, 10, -1, -1],
613
- [6, 7, 11, -1, -1, -1, -1],
614
- [-1, -1, -1, -1, -1, -1, -1],
615
- [-1, -1, -1, -1, -1, -1, -1],
616
- ],
617
- [
618
- [4, 6, 8, 11, -1, -1, -1],
619
- [1, 2, 10, -1, -1, -1, -1],
620
- [-1, -1, -1, -1, -1, -1, -1],
621
- [-1, -1, -1, -1, -1, -1, -1],
622
- ],
623
- [
624
- [0, 3, 4, 6, 11, -1, -1],
625
- [1, 2, 10, -1, -1, -1, -1],
626
- [-1, -1, -1, -1, -1, -1, -1],
627
- [-1, -1, -1, -1, -1, -1, -1],
628
- ],
629
- [
630
- [0, 2, 9, 10, -1, -1, -1],
631
- [4, 6, 8, 11, -1, -1, -1],
632
- [-1, -1, -1, -1, -1, -1, -1],
633
- [-1, -1, -1, -1, -1, -1, -1],
634
- ],
635
- [
636
- [2, 3, 4, 6, 9, 10, 11],
637
- [-1, -1, -1, -1, -1, -1, -1],
638
- [-1, -1, -1, -1, -1, -1, -1],
639
- [-1, -1, -1, -1, -1, -1, -1],
640
- ],
641
- [
642
- [4, 5, 9, -1, -1, -1, -1],
643
- [1, 2, 10, -1, -1, -1, -1],
644
- [6, 7, 11, -1, -1, -1, -1],
645
- [-1, -1, -1, -1, -1, -1, -1],
646
- ],
647
- [
648
- [0, 3, 8, -1, -1, -1, -1],
649
- [4, 5, 9, -1, -1, -1, -1],
650
- [1, 2, 10, -1, -1, -1, -1],
651
- [6, 7, 11, -1, -1, -1, -1],
652
- ],
653
- [
654
- [0, 2, 4, 5, 10, -1, -1],
655
- [6, 7, 11, -1, -1, -1, -1],
656
- [-1, -1, -1, -1, -1, -1, -1],
657
- [-1, -1, -1, -1, -1, -1, -1],
658
- ],
659
- [
660
- [2, 3, 4, 5, 8, 10, -1],
661
- [6, 7, 11, -1, -1, -1, -1],
662
- [-1, -1, -1, -1, -1, -1, -1],
663
- [-1, -1, -1, -1, -1, -1, -1],
664
- ],
665
- [
666
- [5, 6, 8, 9, 11, -1, -1],
667
- [1, 2, 10, -1, -1, -1, -1],
668
- [-1, -1, -1, -1, -1, -1, -1],
669
- [-1, -1, -1, -1, -1, -1, -1],
670
- ],
671
- [
672
- [0, 3, 5, 6, 9, 11, -1],
673
- [1, 2, 10, -1, -1, -1, -1],
674
- [-1, -1, -1, -1, -1, -1, -1],
675
- [-1, -1, -1, -1, -1, -1, -1],
676
- ],
677
- [
678
- [0, 2, 5, 6, 8, 10, 11],
679
- [-1, -1, -1, -1, -1, -1, -1],
680
- [-1, -1, -1, -1, -1, -1, -1],
681
- [-1, -1, -1, -1, -1, -1, -1],
682
- ],
683
- [
684
- [2, 3, 5, 6, 10, 11, -1],
685
- [-1, -1, -1, -1, -1, -1, -1],
686
- [-1, -1, -1, -1, -1, -1, -1],
687
- [-1, -1, -1, -1, -1, -1, -1],
688
- ],
689
- [
690
- [1, 3, 6, 7, 10, -1, -1],
691
- [-1, -1, -1, -1, -1, -1, -1],
692
- [-1, -1, -1, -1, -1, -1, -1],
693
- [-1, -1, -1, -1, -1, -1, -1],
694
- ],
695
- [
696
- [0, 1, 6, 7, 8, 10, -1],
697
- [-1, -1, -1, -1, -1, -1, -1],
698
- [-1, -1, -1, -1, -1, -1, -1],
699
- [-1, -1, -1, -1, -1, -1, -1],
700
- ],
701
- [
702
- [0, 3, 6, 7, 9, 10, -1],
703
- [-1, -1, -1, -1, -1, -1, -1],
704
- [-1, -1, -1, -1, -1, -1, -1],
705
- [-1, -1, -1, -1, -1, -1, -1],
706
- ],
707
- [
708
- [6, 7, 8, 9, 10, -1, -1],
709
- [-1, -1, -1, -1, -1, -1, -1],
710
- [-1, -1, -1, -1, -1, -1, -1],
711
- [-1, -1, -1, -1, -1, -1, -1],
712
- ],
713
- [
714
- [1, 3, 4, 6, 8, 10, -1],
715
- [-1, -1, -1, -1, -1, -1, -1],
716
- [-1, -1, -1, -1, -1, -1, -1],
717
- [-1, -1, -1, -1, -1, -1, -1],
718
- ],
719
- [
720
- [0, 1, 4, 6, 10, -1, -1],
721
- [-1, -1, -1, -1, -1, -1, -1],
722
- [-1, -1, -1, -1, -1, -1, -1],
723
- [-1, -1, -1, -1, -1, -1, -1],
724
- ],
725
- [
726
- [0, 3, 4, 6, 8, 9, 10],
727
- [-1, -1, -1, -1, -1, -1, -1],
728
- [-1, -1, -1, -1, -1, -1, -1],
729
- [-1, -1, -1, -1, -1, -1, -1],
730
- ],
731
- [
732
- [4, 6, 9, 10, -1, -1, -1],
733
- [-1, -1, -1, -1, -1, -1, -1],
734
- [-1, -1, -1, -1, -1, -1, -1],
735
- [-1, -1, -1, -1, -1, -1, -1],
736
- ],
737
- [
738
- [4, 5, 9, -1, -1, -1, -1],
739
- [1, 3, 6, 7, 10, -1, -1],
740
- [-1, -1, -1, -1, -1, -1, -1],
741
- [-1, -1, -1, -1, -1, -1, -1],
742
- ],
743
- [
744
- [0, 1, 6, 7, 8, 10, -1],
745
- [4, 5, 9, -1, -1, -1, -1],
746
- [-1, -1, -1, -1, -1, -1, -1],
747
- [-1, -1, -1, -1, -1, -1, -1],
748
- ],
749
- [
750
- [0, 3, 4, 5, 6, 7, 10],
751
- [-1, -1, -1, -1, -1, -1, -1],
752
- [-1, -1, -1, -1, -1, -1, -1],
753
- [-1, -1, -1, -1, -1, -1, -1],
754
- ],
755
- [
756
- [4, 5, 6, 7, 8, 10, -1],
757
- [-1, -1, -1, -1, -1, -1, -1],
758
- [-1, -1, -1, -1, -1, -1, -1],
759
- [-1, -1, -1, -1, -1, -1, -1],
760
- ],
761
- [
762
- [1, 3, 5, 6, 8, 9, 10],
763
- [-1, -1, -1, -1, -1, -1, -1],
764
- [-1, -1, -1, -1, -1, -1, -1],
765
- [-1, -1, -1, -1, -1, -1, -1],
766
- ],
767
- [
768
- [0, 1, 5, 6, 9, 10, -1],
769
- [-1, -1, -1, -1, -1, -1, -1],
770
- [-1, -1, -1, -1, -1, -1, -1],
771
- [-1, -1, -1, -1, -1, -1, -1],
772
- ],
773
- [
774
- [0, 3, 8, -1, -1, -1, -1],
775
- [5, 6, 10, -1, -1, -1, -1],
776
- [-1, -1, -1, -1, -1, -1, -1],
777
- [-1, -1, -1, -1, -1, -1, -1],
778
- ],
779
- [
780
- [5, 6, 10, -1, -1, -1, -1],
781
- [-1, -1, -1, -1, -1, -1, -1],
782
- [-1, -1, -1, -1, -1, -1, -1],
783
- [-1, -1, -1, -1, -1, -1, -1],
784
- ],
785
- [
786
- [5, 6, 10, -1, -1, -1, -1],
787
- [-1, -1, -1, -1, -1, -1, -1],
788
- [-1, -1, -1, -1, -1, -1, -1],
789
- [-1, -1, -1, -1, -1, -1, -1],
790
- ],
791
- [
792
- [0, 3, 8, -1, -1, -1, -1],
793
- [5, 6, 10, -1, -1, -1, -1],
794
- [-1, -1, -1, -1, -1, -1, -1],
795
- [-1, -1, -1, -1, -1, -1, -1],
796
- ],
797
- [
798
- [0, 1, 9, -1, -1, -1, -1],
799
- [5, 6, 10, -1, -1, -1, -1],
800
- [-1, -1, -1, -1, -1, -1, -1],
801
- [-1, -1, -1, -1, -1, -1, -1],
802
- ],
803
- [
804
- [1, 3, 8, 9, -1, -1, -1],
805
- [5, 6, 10, -1, -1, -1, -1],
806
- [-1, -1, -1, -1, -1, -1, -1],
807
- [-1, -1, -1, -1, -1, -1, -1],
808
- ],
809
- [
810
- [4, 7, 8, -1, -1, -1, -1],
811
- [5, 6, 10, -1, -1, -1, -1],
812
- [-1, -1, -1, -1, -1, -1, -1],
813
- [-1, -1, -1, -1, -1, -1, -1],
814
- ],
815
- [
816
- [0, 3, 4, 7, -1, -1, -1],
817
- [5, 6, 10, -1, -1, -1, -1],
818
- [-1, -1, -1, -1, -1, -1, -1],
819
- [-1, -1, -1, -1, -1, -1, -1],
820
- ],
821
- [
822
- [0, 1, 9, -1, -1, -1, -1],
823
- [4, 7, 8, -1, -1, -1, -1],
824
- [5, 6, 10, -1, -1, -1, -1],
825
- [-1, -1, -1, -1, -1, -1, -1],
826
- ],
827
- [
828
- [1, 3, 4, 7, 9, -1, -1],
829
- [5, 6, 10, -1, -1, -1, -1],
830
- [-1, -1, -1, -1, -1, -1, -1],
831
- [-1, -1, -1, -1, -1, -1, -1],
832
- ],
833
- [
834
- [4, 6, 9, 10, -1, -1, -1],
835
- [-1, -1, -1, -1, -1, -1, -1],
836
- [-1, -1, -1, -1, -1, -1, -1],
837
- [-1, -1, -1, -1, -1, -1, -1],
838
- ],
839
- [
840
- [0, 3, 8, -1, -1, -1, -1],
841
- [4, 6, 9, 10, -1, -1, -1],
842
- [-1, -1, -1, -1, -1, -1, -1],
843
- [-1, -1, -1, -1, -1, -1, -1],
844
- ],
845
- [
846
- [0, 1, 4, 6, 10, -1, -1],
847
- [-1, -1, -1, -1, -1, -1, -1],
848
- [-1, -1, -1, -1, -1, -1, -1],
849
- [-1, -1, -1, -1, -1, -1, -1],
850
- ],
851
- [
852
- [1, 3, 4, 6, 8, 10, -1],
853
- [-1, -1, -1, -1, -1, -1, -1],
854
- [-1, -1, -1, -1, -1, -1, -1],
855
- [-1, -1, -1, -1, -1, -1, -1],
856
- ],
857
- [
858
- [6, 7, 8, 9, 10, -1, -1],
859
- [-1, -1, -1, -1, -1, -1, -1],
860
- [-1, -1, -1, -1, -1, -1, -1],
861
- [-1, -1, -1, -1, -1, -1, -1],
862
- ],
863
- [
864
- [0, 3, 6, 7, 9, 10, -1],
865
- [-1, -1, -1, -1, -1, -1, -1],
866
- [-1, -1, -1, -1, -1, -1, -1],
867
- [-1, -1, -1, -1, -1, -1, -1],
868
- ],
869
- [
870
- [0, 1, 6, 7, 8, 10, -1],
871
- [-1, -1, -1, -1, -1, -1, -1],
872
- [-1, -1, -1, -1, -1, -1, -1],
873
- [-1, -1, -1, -1, -1, -1, -1],
874
- ],
875
- [
876
- [1, 3, 6, 7, 10, -1, -1],
877
- [-1, -1, -1, -1, -1, -1, -1],
878
- [-1, -1, -1, -1, -1, -1, -1],
879
- [-1, -1, -1, -1, -1, -1, -1],
880
- ],
881
- [
882
- [2, 3, 11, -1, -1, -1, -1],
883
- [5, 6, 10, -1, -1, -1, -1],
884
- [-1, -1, -1, -1, -1, -1, -1],
885
- [-1, -1, -1, -1, -1, -1, -1],
886
- ],
887
- [
888
- [0, 2, 8, 11, -1, -1, -1],
889
- [5, 6, 10, -1, -1, -1, -1],
890
- [-1, -1, -1, -1, -1, -1, -1],
891
- [-1, -1, -1, -1, -1, -1, -1],
892
- ],
893
- [
894
- [0, 1, 9, -1, -1, -1, -1],
895
- [2, 3, 11, -1, -1, -1, -1],
896
- [5, 6, 10, -1, -1, -1, -1],
897
- [-1, -1, -1, -1, -1, -1, -1],
898
- ],
899
- [
900
- [1, 2, 8, 9, 11, -1, -1],
901
- [5, 6, 10, -1, -1, -1, -1],
902
- [-1, -1, -1, -1, -1, -1, -1],
903
- [-1, -1, -1, -1, -1, -1, -1],
904
- ],
905
- [
906
- [4, 7, 8, -1, -1, -1, -1],
907
- [2, 3, 11, -1, -1, -1, -1],
908
- [5, 6, 10, -1, -1, -1, -1],
909
- [-1, -1, -1, -1, -1, -1, -1],
910
- ],
911
- [
912
- [0, 2, 4, 7, 11, -1, -1],
913
- [5, 6, 10, -1, -1, -1, -1],
914
- [-1, -1, -1, -1, -1, -1, -1],
915
- [-1, -1, -1, -1, -1, -1, -1],
916
- ],
917
- [
918
- [0, 1, 9, -1, -1, -1, -1],
919
- [4, 7, 8, -1, -1, -1, -1],
920
- [2, 3, 11, -1, -1, -1, -1],
921
- [5, 6, 10, -1, -1, -1, -1],
922
- ],
923
- [
924
- [1, 2, 4, 7, 9, 11, -1],
925
- [5, 6, 10, -1, -1, -1, -1],
926
- [-1, -1, -1, -1, -1, -1, -1],
927
- [-1, -1, -1, -1, -1, -1, -1],
928
- ],
929
- [
930
- [4, 6, 9, 10, -1, -1, -1],
931
- [2, 3, 11, -1, -1, -1, -1],
932
- [-1, -1, -1, -1, -1, -1, -1],
933
- [-1, -1, -1, -1, -1, -1, -1],
934
- ],
935
- [
936
- [0, 2, 8, 11, -1, -1, -1],
937
- [4, 6, 9, 10, -1, -1, -1],
938
- [-1, -1, -1, -1, -1, -1, -1],
939
- [-1, -1, -1, -1, -1, -1, -1],
940
- ],
941
- [
942
- [0, 1, 4, 6, 10, -1, -1],
943
- [2, 3, 11, -1, -1, -1, -1],
944
- [-1, -1, -1, -1, -1, -1, -1],
945
- [-1, -1, -1, -1, -1, -1, -1],
946
- ],
947
- [
948
- [1, 2, 4, 6, 8, 10, 11],
949
- [-1, -1, -1, -1, -1, -1, -1],
950
- [-1, -1, -1, -1, -1, -1, -1],
951
- [-1, -1, -1, -1, -1, -1, -1],
952
- ],
953
- [
954
- [6, 7, 8, 9, 10, -1, -1],
955
- [2, 3, 11, -1, -1, -1, -1],
956
- [-1, -1, -1, -1, -1, -1, -1],
957
- [-1, -1, -1, -1, -1, -1, -1],
958
- ],
959
- [
960
- [0, 2, 6, 7, 9, 10, 11],
961
- [-1, -1, -1, -1, -1, -1, -1],
962
- [-1, -1, -1, -1, -1, -1, -1],
963
- [-1, -1, -1, -1, -1, -1, -1],
964
- ],
965
- [
966
- [0, 1, 6, 7, 8, 10, -1],
967
- [2, 3, 11, -1, -1, -1, -1],
968
- [-1, -1, -1, -1, -1, -1, -1],
969
- [-1, -1, -1, -1, -1, -1, -1],
970
- ],
971
- [
972
- [1, 2, 6, 7, 10, 11, -1],
973
- [-1, -1, -1, -1, -1, -1, -1],
974
- [-1, -1, -1, -1, -1, -1, -1],
975
- [-1, -1, -1, -1, -1, -1, -1],
976
- ],
977
- [
978
- [1, 2, 5, 6, -1, -1, -1],
979
- [-1, -1, -1, -1, -1, -1, -1],
980
- [-1, -1, -1, -1, -1, -1, -1],
981
- [-1, -1, -1, -1, -1, -1, -1],
982
- ],
983
- [
984
- [0, 3, 8, -1, -1, -1, -1],
985
- [1, 2, 5, 6, -1, -1, -1],
986
- [-1, -1, -1, -1, -1, -1, -1],
987
- [-1, -1, -1, -1, -1, -1, -1],
988
- ],
989
- [
990
- [0, 2, 5, 6, 9, -1, -1],
991
- [-1, -1, -1, -1, -1, -1, -1],
992
- [-1, -1, -1, -1, -1, -1, -1],
993
- [-1, -1, -1, -1, -1, -1, -1],
994
- ],
995
- [
996
- [2, 3, 5, 6, 8, 9, -1],
997
- [-1, -1, -1, -1, -1, -1, -1],
998
- [-1, -1, -1, -1, -1, -1, -1],
999
- [-1, -1, -1, -1, -1, -1, -1],
1000
- ],
1001
- [
1002
- [4, 7, 8, -1, -1, -1, -1],
1003
- [1, 2, 5, 6, -1, -1, -1],
1004
- [-1, -1, -1, -1, -1, -1, -1],
1005
- [-1, -1, -1, -1, -1, -1, -1],
1006
- ],
1007
- [
1008
- [0, 3, 4, 7, -1, -1, -1],
1009
- [1, 2, 5, 6, -1, -1, -1],
1010
- [-1, -1, -1, -1, -1, -1, -1],
1011
- [-1, -1, -1, -1, -1, -1, -1],
1012
- ],
1013
- [
1014
- [0, 2, 5, 6, 9, -1, -1],
1015
- [4, 7, 8, -1, -1, -1, -1],
1016
- [-1, -1, -1, -1, -1, -1, -1],
1017
- [-1, -1, -1, -1, -1, -1, -1],
1018
- ],
1019
- [
1020
- [2, 3, 4, 5, 6, 7, 9],
1021
- [-1, -1, -1, -1, -1, -1, -1],
1022
- [-1, -1, -1, -1, -1, -1, -1],
1023
- [-1, -1, -1, -1, -1, -1, -1],
1024
- ],
1025
- [
1026
- [1, 2, 4, 6, 9, -1, -1],
1027
- [-1, -1, -1, -1, -1, -1, -1],
1028
- [-1, -1, -1, -1, -1, -1, -1],
1029
- [-1, -1, -1, -1, -1, -1, -1],
1030
- ],
1031
- [
1032
- [0, 3, 8, -1, -1, -1, -1],
1033
- [1, 2, 4, 6, 9, -1, -1],
1034
- [-1, -1, -1, -1, -1, -1, -1],
1035
- [-1, -1, -1, -1, -1, -1, -1],
1036
- ],
1037
- [
1038
- [0, 2, 4, 6, -1, -1, -1],
1039
- [-1, -1, -1, -1, -1, -1, -1],
1040
- [-1, -1, -1, -1, -1, -1, -1],
1041
- [-1, -1, -1, -1, -1, -1, -1],
1042
- ],
1043
- [
1044
- [2, 3, 4, 6, 8, -1, -1],
1045
- [-1, -1, -1, -1, -1, -1, -1],
1046
- [-1, -1, -1, -1, -1, -1, -1],
1047
- [-1, -1, -1, -1, -1, -1, -1],
1048
- ],
1049
- [
1050
- [1, 2, 6, 7, 8, 9, -1],
1051
- [-1, -1, -1, -1, -1, -1, -1],
1052
- [-1, -1, -1, -1, -1, -1, -1],
1053
- [-1, -1, -1, -1, -1, -1, -1],
1054
- ],
1055
- [
1056
- [0, 1, 2, 3, 6, 7, 9],
1057
- [-1, -1, -1, -1, -1, -1, -1],
1058
- [-1, -1, -1, -1, -1, -1, -1],
1059
- [-1, -1, -1, -1, -1, -1, -1],
1060
- ],
1061
- [
1062
- [0, 2, 6, 7, 8, -1, -1],
1063
- [-1, -1, -1, -1, -1, -1, -1],
1064
- [-1, -1, -1, -1, -1, -1, -1],
1065
- [-1, -1, -1, -1, -1, -1, -1],
1066
- ],
1067
- [
1068
- [2, 3, 6, 7, -1, -1, -1],
1069
- [-1, -1, -1, -1, -1, -1, -1],
1070
- [-1, -1, -1, -1, -1, -1, -1],
1071
- [-1, -1, -1, -1, -1, -1, -1],
1072
- ],
1073
- [
1074
- [1, 3, 5, 6, 11, -1, -1],
1075
- [-1, -1, -1, -1, -1, -1, -1],
1076
- [-1, -1, -1, -1, -1, -1, -1],
1077
- [-1, -1, -1, -1, -1, -1, -1],
1078
- ],
1079
- [
1080
- [0, 1, 5, 6, 8, 11, -1],
1081
- [-1, -1, -1, -1, -1, -1, -1],
1082
- [-1, -1, -1, -1, -1, -1, -1],
1083
- [-1, -1, -1, -1, -1, -1, -1],
1084
- ],
1085
- [
1086
- [0, 3, 5, 6, 9, 11, -1],
1087
- [-1, -1, -1, -1, -1, -1, -1],
1088
- [-1, -1, -1, -1, -1, -1, -1],
1089
- [-1, -1, -1, -1, -1, -1, -1],
1090
- ],
1091
- [
1092
- [5, 6, 8, 9, 11, -1, -1],
1093
- [-1, -1, -1, -1, -1, -1, -1],
1094
- [-1, -1, -1, -1, -1, -1, -1],
1095
- [-1, -1, -1, -1, -1, -1, -1],
1096
- ],
1097
- [
1098
- [4, 7, 8, -1, -1, -1, -1],
1099
- [1, 3, 5, 6, 11, -1, -1],
1100
- [-1, -1, -1, -1, -1, -1, -1],
1101
- [-1, -1, -1, -1, -1, -1, -1],
1102
- ],
1103
- [
1104
- [0, 1, 4, 5, 6, 7, 11],
1105
- [-1, -1, -1, -1, -1, -1, -1],
1106
- [-1, -1, -1, -1, -1, -1, -1],
1107
- [-1, -1, -1, -1, -1, -1, -1],
1108
- ],
1109
- [
1110
- [0, 3, 5, 6, 9, 11, -1],
1111
- [4, 7, 8, -1, -1, -1, -1],
1112
- [-1, -1, -1, -1, -1, -1, -1],
1113
- [-1, -1, -1, -1, -1, -1, -1],
1114
- ],
1115
- [
1116
- [4, 5, 6, 7, 9, 11, -1],
1117
- [-1, -1, -1, -1, -1, -1, -1],
1118
- [-1, -1, -1, -1, -1, -1, -1],
1119
- [-1, -1, -1, -1, -1, -1, -1],
1120
- ],
1121
- [
1122
- [1, 3, 4, 6, 9, 11, -1],
1123
- [-1, -1, -1, -1, -1, -1, -1],
1124
- [-1, -1, -1, -1, -1, -1, -1],
1125
- [-1, -1, -1, -1, -1, -1, -1],
1126
- ],
1127
- [
1128
- [0, 1, 4, 6, 8, 9, 11],
1129
- [-1, -1, -1, -1, -1, -1, -1],
1130
- [-1, -1, -1, -1, -1, -1, -1],
1131
- [-1, -1, -1, -1, -1, -1, -1],
1132
- ],
1133
- [
1134
- [0, 3, 4, 6, 11, -1, -1],
1135
- [-1, -1, -1, -1, -1, -1, -1],
1136
- [-1, -1, -1, -1, -1, -1, -1],
1137
- [-1, -1, -1, -1, -1, -1, -1],
1138
- ],
1139
- [
1140
- [4, 6, 8, 11, -1, -1, -1],
1141
- [-1, -1, -1, -1, -1, -1, -1],
1142
- [-1, -1, -1, -1, -1, -1, -1],
1143
- [-1, -1, -1, -1, -1, -1, -1],
1144
- ],
1145
- [
1146
- [1, 3, 6, 7, 8, 9, 11],
1147
- [-1, -1, -1, -1, -1, -1, -1],
1148
- [-1, -1, -1, -1, -1, -1, -1],
1149
- [-1, -1, -1, -1, -1, -1, -1],
1150
- ],
1151
- [
1152
- [0, 1, 9, -1, -1, -1, -1],
1153
- [6, 7, 11, -1, -1, -1, -1],
1154
- [-1, -1, -1, -1, -1, -1, -1],
1155
- [-1, -1, -1, -1, -1, -1, -1],
1156
- ],
1157
- [
1158
- [0, 3, 6, 7, 8, 11, -1],
1159
- [-1, -1, -1, -1, -1, -1, -1],
1160
- [-1, -1, -1, -1, -1, -1, -1],
1161
- [-1, -1, -1, -1, -1, -1, -1],
1162
- ],
1163
- [
1164
- [6, 7, 11, -1, -1, -1, -1],
1165
- [-1, -1, -1, -1, -1, -1, -1],
1166
- [-1, -1, -1, -1, -1, -1, -1],
1167
- [-1, -1, -1, -1, -1, -1, -1],
1168
- ],
1169
- [
1170
- [5, 7, 10, 11, -1, -1, -1],
1171
- [-1, -1, -1, -1, -1, -1, -1],
1172
- [-1, -1, -1, -1, -1, -1, -1],
1173
- [-1, -1, -1, -1, -1, -1, -1],
1174
- ],
1175
- [
1176
- [0, 3, 8, -1, -1, -1, -1],
1177
- [5, 7, 10, 11, -1, -1, -1],
1178
- [-1, -1, -1, -1, -1, -1, -1],
1179
- [-1, -1, -1, -1, -1, -1, -1],
1180
- ],
1181
- [
1182
- [0, 1, 9, -1, -1, -1, -1],
1183
- [5, 7, 10, 11, -1, -1, -1],
1184
- [-1, -1, -1, -1, -1, -1, -1],
1185
- [-1, -1, -1, -1, -1, -1, -1],
1186
- ],
1187
- [
1188
- [1, 3, 8, 9, -1, -1, -1],
1189
- [5, 7, 10, 11, -1, -1, -1],
1190
- [-1, -1, -1, -1, -1, -1, -1],
1191
- [-1, -1, -1, -1, -1, -1, -1],
1192
- ],
1193
- [
1194
- [4, 5, 8, 10, 11, -1, -1],
1195
- [-1, -1, -1, -1, -1, -1, -1],
1196
- [-1, -1, -1, -1, -1, -1, -1],
1197
- [-1, -1, -1, -1, -1, -1, -1],
1198
- ],
1199
- [
1200
- [0, 3, 4, 5, 10, 11, -1],
1201
- [-1, -1, -1, -1, -1, -1, -1],
1202
- [-1, -1, -1, -1, -1, -1, -1],
1203
- [-1, -1, -1, -1, -1, -1, -1],
1204
- ],
1205
- [
1206
- [0, 1, 9, -1, -1, -1, -1],
1207
- [4, 5, 8, 10, 11, -1, -1],
1208
- [-1, -1, -1, -1, -1, -1, -1],
1209
- [-1, -1, -1, -1, -1, -1, -1],
1210
- ],
1211
- [
1212
- [1, 3, 4, 5, 9, 10, 11],
1213
- [-1, -1, -1, -1, -1, -1, -1],
1214
- [-1, -1, -1, -1, -1, -1, -1],
1215
- [-1, -1, -1, -1, -1, -1, -1],
1216
- ],
1217
- [
1218
- [4, 7, 9, 10, 11, -1, -1],
1219
- [-1, -1, -1, -1, -1, -1, -1],
1220
- [-1, -1, -1, -1, -1, -1, -1],
1221
- [-1, -1, -1, -1, -1, -1, -1],
1222
- ],
1223
- [
1224
- [0, 3, 8, -1, -1, -1, -1],
1225
- [4, 7, 9, 10, 11, -1, -1],
1226
- [-1, -1, -1, -1, -1, -1, -1],
1227
- [-1, -1, -1, -1, -1, -1, -1],
1228
- ],
1229
- [
1230
- [0, 1, 4, 7, 10, 11, -1],
1231
- [-1, -1, -1, -1, -1, -1, -1],
1232
- [-1, -1, -1, -1, -1, -1, -1],
1233
- [-1, -1, -1, -1, -1, -1, -1],
1234
- ],
1235
- [
1236
- [1, 3, 4, 7, 8, 10, 11],
1237
- [-1, -1, -1, -1, -1, -1, -1],
1238
- [-1, -1, -1, -1, -1, -1, -1],
1239
- [-1, -1, -1, -1, -1, -1, -1],
1240
- ],
1241
- [
1242
- [8, 9, 10, 11, -1, -1, -1],
1243
- [-1, -1, -1, -1, -1, -1, -1],
1244
- [-1, -1, -1, -1, -1, -1, -1],
1245
- [-1, -1, -1, -1, -1, -1, -1],
1246
- ],
1247
- [
1248
- [0, 3, 9, 10, 11, -1, -1],
1249
- [-1, -1, -1, -1, -1, -1, -1],
1250
- [-1, -1, -1, -1, -1, -1, -1],
1251
- [-1, -1, -1, -1, -1, -1, -1],
1252
- ],
1253
- [
1254
- [0, 1, 8, 10, 11, -1, -1],
1255
- [-1, -1, -1, -1, -1, -1, -1],
1256
- [-1, -1, -1, -1, -1, -1, -1],
1257
- [-1, -1, -1, -1, -1, -1, -1],
1258
- ],
1259
- [
1260
- [1, 3, 10, 11, -1, -1, -1],
1261
- [-1, -1, -1, -1, -1, -1, -1],
1262
- [-1, -1, -1, -1, -1, -1, -1],
1263
- [-1, -1, -1, -1, -1, -1, -1],
1264
- ],
1265
- [
1266
- [2, 3, 5, 7, 10, -1, -1],
1267
- [-1, -1, -1, -1, -1, -1, -1],
1268
- [-1, -1, -1, -1, -1, -1, -1],
1269
- [-1, -1, -1, -1, -1, -1, -1],
1270
- ],
1271
- [
1272
- [0, 2, 5, 7, 8, 10, -1],
1273
- [-1, -1, -1, -1, -1, -1, -1],
1274
- [-1, -1, -1, -1, -1, -1, -1],
1275
- [-1, -1, -1, -1, -1, -1, -1],
1276
- ],
1277
- [
1278
- [0, 1, 9, -1, -1, -1, -1],
1279
- [2, 3, 5, 7, 10, -1, -1],
1280
- [-1, -1, -1, -1, -1, -1, -1],
1281
- [-1, -1, -1, -1, -1, -1, -1],
1282
- ],
1283
- [
1284
- [1, 2, 5, 7, 8, 9, 10],
1285
- [-1, -1, -1, -1, -1, -1, -1],
1286
- [-1, -1, -1, -1, -1, -1, -1],
1287
- [-1, -1, -1, -1, -1, -1, -1],
1288
- ],
1289
- [
1290
- [2, 3, 4, 5, 8, 10, -1],
1291
- [-1, -1, -1, -1, -1, -1, -1],
1292
- [-1, -1, -1, -1, -1, -1, -1],
1293
- [-1, -1, -1, -1, -1, -1, -1],
1294
- ],
1295
- [
1296
- [0, 2, 4, 5, 10, -1, -1],
1297
- [-1, -1, -1, -1, -1, -1, -1],
1298
- [-1, -1, -1, -1, -1, -1, -1],
1299
- [-1, -1, -1, -1, -1, -1, -1],
1300
- ],
1301
- [
1302
- [0, 1, 9, -1, -1, -1, -1],
1303
- [2, 3, 4, 5, 8, 10, -1],
1304
- [-1, -1, -1, -1, -1, -1, -1],
1305
- [-1, -1, -1, -1, -1, -1, -1],
1306
- ],
1307
- [
1308
- [1, 2, 4, 5, 9, 10, -1],
1309
- [-1, -1, -1, -1, -1, -1, -1],
1310
- [-1, -1, -1, -1, -1, -1, -1],
1311
- [-1, -1, -1, -1, -1, -1, -1],
1312
- ],
1313
- [
1314
- [2, 3, 4, 7, 9, 10, -1],
1315
- [-1, -1, -1, -1, -1, -1, -1],
1316
- [-1, -1, -1, -1, -1, -1, -1],
1317
- [-1, -1, -1, -1, -1, -1, -1],
1318
- ],
1319
- [
1320
- [0, 2, 4, 7, 8, 9, 10],
1321
- [-1, -1, -1, -1, -1, -1, -1],
1322
- [-1, -1, -1, -1, -1, -1, -1],
1323
- [-1, -1, -1, -1, -1, -1, -1],
1324
- ],
1325
- [
1326
- [0, 1, 2, 3, 4, 7, 10],
1327
- [-1, -1, -1, -1, -1, -1, -1],
1328
- [-1, -1, -1, -1, -1, -1, -1],
1329
- [-1, -1, -1, -1, -1, -1, -1],
1330
- ],
1331
- [
1332
- [4, 7, 8, -1, -1, -1, -1],
1333
- [1, 2, 10, -1, -1, -1, -1],
1334
- [-1, -1, -1, -1, -1, -1, -1],
1335
- [-1, -1, -1, -1, -1, -1, -1],
1336
- ],
1337
- [
1338
- [2, 3, 8, 9, 10, -1, -1],
1339
- [-1, -1, -1, -1, -1, -1, -1],
1340
- [-1, -1, -1, -1, -1, -1, -1],
1341
- [-1, -1, -1, -1, -1, -1, -1],
1342
- ],
1343
- [
1344
- [0, 2, 9, 10, -1, -1, -1],
1345
- [-1, -1, -1, -1, -1, -1, -1],
1346
- [-1, -1, -1, -1, -1, -1, -1],
1347
- [-1, -1, -1, -1, -1, -1, -1],
1348
- ],
1349
- [
1350
- [0, 1, 2, 3, 8, 10, -1],
1351
- [-1, -1, -1, -1, -1, -1, -1],
1352
- [-1, -1, -1, -1, -1, -1, -1],
1353
- [-1, -1, -1, -1, -1, -1, -1],
1354
- ],
1355
- [
1356
- [1, 2, 10, -1, -1, -1, -1],
1357
- [-1, -1, -1, -1, -1, -1, -1],
1358
- [-1, -1, -1, -1, -1, -1, -1],
1359
- [-1, -1, -1, -1, -1, -1, -1],
1360
- ],
1361
- [
1362
- [1, 2, 5, 7, 11, -1, -1],
1363
- [-1, -1, -1, -1, -1, -1, -1],
1364
- [-1, -1, -1, -1, -1, -1, -1],
1365
- [-1, -1, -1, -1, -1, -1, -1],
1366
- ],
1367
- [
1368
- [0, 3, 8, -1, -1, -1, -1],
1369
- [1, 2, 5, 7, 11, -1, -1],
1370
- [-1, -1, -1, -1, -1, -1, -1],
1371
- [-1, -1, -1, -1, -1, -1, -1],
1372
- ],
1373
- [
1374
- [0, 2, 5, 7, 9, 11, -1],
1375
- [-1, -1, -1, -1, -1, -1, -1],
1376
- [-1, -1, -1, -1, -1, -1, -1],
1377
- [-1, -1, -1, -1, -1, -1, -1],
1378
- ],
1379
- [
1380
- [2, 3, 5, 7, 8, 9, 11],
1381
- [-1, -1, -1, -1, -1, -1, -1],
1382
- [-1, -1, -1, -1, -1, -1, -1],
1383
- [-1, -1, -1, -1, -1, -1, -1],
1384
- ],
1385
- [
1386
- [1, 2, 4, 5, 8, 11, -1],
1387
- [-1, -1, -1, -1, -1, -1, -1],
1388
- [-1, -1, -1, -1, -1, -1, -1],
1389
- [-1, -1, -1, -1, -1, -1, -1],
1390
- ],
1391
- [
1392
- [0, 1, 2, 3, 4, 5, 11],
1393
- [-1, -1, -1, -1, -1, -1, -1],
1394
- [-1, -1, -1, -1, -1, -1, -1],
1395
- [-1, -1, -1, -1, -1, -1, -1],
1396
- ],
1397
- [
1398
- [0, 2, 4, 5, 8, 9, 11],
1399
- [-1, -1, -1, -1, -1, -1, -1],
1400
- [-1, -1, -1, -1, -1, -1, -1],
1401
- [-1, -1, -1, -1, -1, -1, -1],
1402
- ],
1403
- [
1404
- [4, 5, 9, -1, -1, -1, -1],
1405
- [2, 3, 11, -1, -1, -1, -1],
1406
- [-1, -1, -1, -1, -1, -1, -1],
1407
- [-1, -1, -1, -1, -1, -1, -1],
1408
- ],
1409
- [
1410
- [1, 2, 4, 7, 9, 11, -1],
1411
- [-1, -1, -1, -1, -1, -1, -1],
1412
- [-1, -1, -1, -1, -1, -1, -1],
1413
- [-1, -1, -1, -1, -1, -1, -1],
1414
- ],
1415
- [
1416
- [0, 3, 8, -1, -1, -1, -1],
1417
- [1, 2, 4, 7, 9, 11, -1],
1418
- [-1, -1, -1, -1, -1, -1, -1],
1419
- [-1, -1, -1, -1, -1, -1, -1],
1420
- ],
1421
- [
1422
- [0, 2, 4, 7, 11, -1, -1],
1423
- [-1, -1, -1, -1, -1, -1, -1],
1424
- [-1, -1, -1, -1, -1, -1, -1],
1425
- [-1, -1, -1, -1, -1, -1, -1],
1426
- ],
1427
- [
1428
- [2, 3, 4, 7, 8, 11, -1],
1429
- [-1, -1, -1, -1, -1, -1, -1],
1430
- [-1, -1, -1, -1, -1, -1, -1],
1431
- [-1, -1, -1, -1, -1, -1, -1],
1432
- ],
1433
- [
1434
- [1, 2, 8, 9, 11, -1, -1],
1435
- [-1, -1, -1, -1, -1, -1, -1],
1436
- [-1, -1, -1, -1, -1, -1, -1],
1437
- [-1, -1, -1, -1, -1, -1, -1],
1438
- ],
1439
- [
1440
- [0, 1, 2, 3, 9, 11, -1],
1441
- [-1, -1, -1, -1, -1, -1, -1],
1442
- [-1, -1, -1, -1, -1, -1, -1],
1443
- [-1, -1, -1, -1, -1, -1, -1],
1444
- ],
1445
- [
1446
- [0, 2, 8, 11, -1, -1, -1],
1447
- [-1, -1, -1, -1, -1, -1, -1],
1448
- [-1, -1, -1, -1, -1, -1, -1],
1449
- [-1, -1, -1, -1, -1, -1, -1],
1450
- ],
1451
- [
1452
- [2, 3, 11, -1, -1, -1, -1],
1453
- [-1, -1, -1, -1, -1, -1, -1],
1454
- [-1, -1, -1, -1, -1, -1, -1],
1455
- [-1, -1, -1, -1, -1, -1, -1],
1456
- ],
1457
- [
1458
- [1, 3, 5, 7, -1, -1, -1],
1459
- [-1, -1, -1, -1, -1, -1, -1],
1460
- [-1, -1, -1, -1, -1, -1, -1],
1461
- [-1, -1, -1, -1, -1, -1, -1],
1462
- ],
1463
- [
1464
- [0, 1, 5, 7, 8, -1, -1],
1465
- [-1, -1, -1, -1, -1, -1, -1],
1466
- [-1, -1, -1, -1, -1, -1, -1],
1467
- [-1, -1, -1, -1, -1, -1, -1],
1468
- ],
1469
- [
1470
- [0, 3, 5, 7, 9, -1, -1],
1471
- [-1, -1, -1, -1, -1, -1, -1],
1472
- [-1, -1, -1, -1, -1, -1, -1],
1473
- [-1, -1, -1, -1, -1, -1, -1],
1474
- ],
1475
- [
1476
- [5, 7, 8, 9, -1, -1, -1],
1477
- [-1, -1, -1, -1, -1, -1, -1],
1478
- [-1, -1, -1, -1, -1, -1, -1],
1479
- [-1, -1, -1, -1, -1, -1, -1],
1480
- ],
1481
- [
1482
- [1, 3, 4, 5, 8, -1, -1],
1483
- [-1, -1, -1, -1, -1, -1, -1],
1484
- [-1, -1, -1, -1, -1, -1, -1],
1485
- [-1, -1, -1, -1, -1, -1, -1],
1486
- ],
1487
- [
1488
- [0, 1, 4, 5, -1, -1, -1],
1489
- [-1, -1, -1, -1, -1, -1, -1],
1490
- [-1, -1, -1, -1, -1, -1, -1],
1491
- [-1, -1, -1, -1, -1, -1, -1],
1492
- ],
1493
- [
1494
- [0, 3, 4, 5, 8, 9, -1],
1495
- [-1, -1, -1, -1, -1, -1, -1],
1496
- [-1, -1, -1, -1, -1, -1, -1],
1497
- [-1, -1, -1, -1, -1, -1, -1],
1498
- ],
1499
- [
1500
- [4, 5, 9, -1, -1, -1, -1],
1501
- [-1, -1, -1, -1, -1, -1, -1],
1502
- [-1, -1, -1, -1, -1, -1, -1],
1503
- [-1, -1, -1, -1, -1, -1, -1],
1504
- ],
1505
- [
1506
- [1, 3, 4, 7, 9, -1, -1],
1507
- [-1, -1, -1, -1, -1, -1, -1],
1508
- [-1, -1, -1, -1, -1, -1, -1],
1509
- [-1, -1, -1, -1, -1, -1, -1],
1510
- ],
1511
- [
1512
- [0, 1, 4, 7, 8, 9, -1],
1513
- [-1, -1, -1, -1, -1, -1, -1],
1514
- [-1, -1, -1, -1, -1, -1, -1],
1515
- [-1, -1, -1, -1, -1, -1, -1],
1516
- ],
1517
- [
1518
- [0, 3, 4, 7, -1, -1, -1],
1519
- [-1, -1, -1, -1, -1, -1, -1],
1520
- [-1, -1, -1, -1, -1, -1, -1],
1521
- [-1, -1, -1, -1, -1, -1, -1],
1522
- ],
1523
- [
1524
- [4, 7, 8, -1, -1, -1, -1],
1525
- [-1, -1, -1, -1, -1, -1, -1],
1526
- [-1, -1, -1, -1, -1, -1, -1],
1527
- [-1, -1, -1, -1, -1, -1, -1],
1528
- ],
1529
- [
1530
- [1, 3, 8, 9, -1, -1, -1],
1531
- [-1, -1, -1, -1, -1, -1, -1],
1532
- [-1, -1, -1, -1, -1, -1, -1],
1533
- [-1, -1, -1, -1, -1, -1, -1],
1534
- ],
1535
- [
1536
- [0, 1, 9, -1, -1, -1, -1],
1537
- [-1, -1, -1, -1, -1, -1, -1],
1538
- [-1, -1, -1, -1, -1, -1, -1],
1539
- [-1, -1, -1, -1, -1, -1, -1],
1540
- ],
1541
- [
1542
- [0, 3, 8, -1, -1, -1, -1],
1543
- [-1, -1, -1, -1, -1, -1, -1],
1544
- [-1, -1, -1, -1, -1, -1, -1],
1545
- [-1, -1, -1, -1, -1, -1, -1],
1546
- ],
1547
- [
1548
- [-1, -1, -1, -1, -1, -1, -1],
1549
- [-1, -1, -1, -1, -1, -1, -1],
1550
- [-1, -1, -1, -1, -1, -1, -1],
1551
- [-1, -1, -1, -1, -1, -1, -1],
1552
- ],
1553
- ]
1554
- num_vd_table = [
1555
- 0,
1556
- 1,
1557
- 1,
1558
- 1,
1559
- 1,
1560
- 1,
1561
- 2,
1562
- 1,
1563
- 1,
1564
- 2,
1565
- 1,
1566
- 1,
1567
- 1,
1568
- 1,
1569
- 1,
1570
- 1,
1571
- 1,
1572
- 1,
1573
- 2,
1574
- 1,
1575
- 2,
1576
- 1,
1577
- 3,
1578
- 1,
1579
- 2,
1580
- 2,
1581
- 2,
1582
- 1,
1583
- 2,
1584
- 1,
1585
- 2,
1586
- 1,
1587
- 1,
1588
- 2,
1589
- 1,
1590
- 1,
1591
- 2,
1592
- 2,
1593
- 2,
1594
- 1,
1595
- 2,
1596
- 3,
1597
- 1,
1598
- 1,
1599
- 2,
1600
- 2,
1601
- 1,
1602
- 1,
1603
- 1,
1604
- 1,
1605
- 1,
1606
- 1,
1607
- 2,
1608
- 1,
1609
- 2,
1610
- 1,
1611
- 2,
1612
- 2,
1613
- 1,
1614
- 1,
1615
- 2,
1616
- 1,
1617
- 1,
1618
- 1,
1619
- 1,
1620
- 2,
1621
- 2,
1622
- 2,
1623
- 1,
1624
- 1,
1625
- 2,
1626
- 1,
1627
- 2,
1628
- 3,
1629
- 2,
1630
- 2,
1631
- 1,
1632
- 1,
1633
- 1,
1634
- 1,
1635
- 1,
1636
- 1,
1637
- 2,
1638
- 1,
1639
- 1,
1640
- 1,
1641
- 2,
1642
- 1,
1643
- 2,
1644
- 2,
1645
- 2,
1646
- 1,
1647
- 1,
1648
- 1,
1649
- 1,
1650
- 1,
1651
- 2,
1652
- 3,
1653
- 2,
1654
- 2,
1655
- 2,
1656
- 2,
1657
- 2,
1658
- 1,
1659
- 3,
1660
- 4,
1661
- 2,
1662
- 2,
1663
- 2,
1664
- 2,
1665
- 1,
1666
- 1,
1667
- 1,
1668
- 1,
1669
- 1,
1670
- 1,
1671
- 1,
1672
- 1,
1673
- 1,
1674
- 1,
1675
- 2,
1676
- 2,
1677
- 1,
1678
- 1,
1679
- 1,
1680
- 1,
1681
- 2,
1682
- 1,
1683
- 1,
1684
- 2,
1685
- 2,
1686
- 2,
1687
- 2,
1688
- 2,
1689
- 3,
1690
- 2,
1691
- 1,
1692
- 2,
1693
- 1,
1694
- 1,
1695
- 1,
1696
- 1,
1697
- 1,
1698
- 1,
1699
- 2,
1700
- 2,
1701
- 3,
1702
- 2,
1703
- 3,
1704
- 2,
1705
- 4,
1706
- 2,
1707
- 2,
1708
- 2,
1709
- 2,
1710
- 1,
1711
- 2,
1712
- 1,
1713
- 2,
1714
- 1,
1715
- 1,
1716
- 2,
1717
- 1,
1718
- 1,
1719
- 2,
1720
- 2,
1721
- 2,
1722
- 1,
1723
- 1,
1724
- 2,
1725
- 1,
1726
- 1,
1727
- 1,
1728
- 1,
1729
- 1,
1730
- 1,
1731
- 1,
1732
- 1,
1733
- 1,
1734
- 1,
1735
- 2,
1736
- 1,
1737
- 2,
1738
- 1,
1739
- 1,
1740
- 1,
1741
- 1,
1742
- 1,
1743
- 1,
1744
- 2,
1745
- 1,
1746
- 1,
1747
- 1,
1748
- 2,
1749
- 2,
1750
- 2,
1751
- 1,
1752
- 1,
1753
- 2,
1754
- 1,
1755
- 1,
1756
- 2,
1757
- 1,
1758
- 1,
1759
- 1,
1760
- 1,
1761
- 1,
1762
- 1,
1763
- 1,
1764
- 1,
1765
- 2,
1766
- 1,
1767
- 1,
1768
- 1,
1769
- 2,
1770
- 1,
1771
- 1,
1772
- 1,
1773
- 1,
1774
- 2,
1775
- 1,
1776
- 1,
1777
- 1,
1778
- 1,
1779
- 1,
1780
- 2,
1781
- 1,
1782
- 1,
1783
- 1,
1784
- 1,
1785
- 1,
1786
- 2,
1787
- 1,
1788
- 2,
1789
- 1,
1790
- 1,
1791
- 1,
1792
- 1,
1793
- 1,
1794
- 1,
1795
- 1,
1796
- 1,
1797
- 1,
1798
- 1,
1799
- 1,
1800
- 1,
1801
- 1,
1802
- 1,
1803
- 1,
1804
- 1,
1805
- 1,
1806
- 1,
1807
- 1,
1808
- 1,
1809
- 1,
1810
- 0,
1811
- ]
1812
- check_table = [
1813
- [0, 0, 0, 0, 0],
1814
- [0, 0, 0, 0, 0],
1815
- [0, 0, 0, 0, 0],
1816
- [0, 0, 0, 0, 0],
1817
- [0, 0, 0, 0, 0],
1818
- [0, 0, 0, 0, 0],
1819
- [0, 0, 0, 0, 0],
1820
- [0, 0, 0, 0, 0],
1821
- [0, 0, 0, 0, 0],
1822
- [0, 0, 0, 0, 0],
1823
- [0, 0, 0, 0, 0],
1824
- [0, 0, 0, 0, 0],
1825
- [0, 0, 0, 0, 0],
1826
- [0, 0, 0, 0, 0],
1827
- [0, 0, 0, 0, 0],
1828
- [0, 0, 0, 0, 0],
1829
- [0, 0, 0, 0, 0],
1830
- [0, 0, 0, 0, 0],
1831
- [0, 0, 0, 0, 0],
1832
- [0, 0, 0, 0, 0],
1833
- [0, 0, 0, 0, 0],
1834
- [0, 0, 0, 0, 0],
1835
- [0, 0, 0, 0, 0],
1836
- [0, 0, 0, 0, 0],
1837
- [0, 0, 0, 0, 0],
1838
- [0, 0, 0, 0, 0],
1839
- [0, 0, 0, 0, 0],
1840
- [0, 0, 0, 0, 0],
1841
- [0, 0, 0, 0, 0],
1842
- [0, 0, 0, 0, 0],
1843
- [0, 0, 0, 0, 0],
1844
- [0, 0, 0, 0, 0],
1845
- [0, 0, 0, 0, 0],
1846
- [0, 0, 0, 0, 0],
1847
- [0, 0, 0, 0, 0],
1848
- [0, 0, 0, 0, 0],
1849
- [0, 0, 0, 0, 0],
1850
- [0, 0, 0, 0, 0],
1851
- [0, 0, 0, 0, 0],
1852
- [0, 0, 0, 0, 0],
1853
- [0, 0, 0, 0, 0],
1854
- [0, 0, 0, 0, 0],
1855
- [0, 0, 0, 0, 0],
1856
- [0, 0, 0, 0, 0],
1857
- [0, 0, 0, 0, 0],
1858
- [0, 0, 0, 0, 0],
1859
- [0, 0, 0, 0, 0],
1860
- [0, 0, 0, 0, 0],
1861
- [0, 0, 0, 0, 0],
1862
- [0, 0, 0, 0, 0],
1863
- [0, 0, 0, 0, 0],
1864
- [0, 0, 0, 0, 0],
1865
- [0, 0, 0, 0, 0],
1866
- [0, 0, 0, 0, 0],
1867
- [0, 0, 0, 0, 0],
1868
- [0, 0, 0, 0, 0],
1869
- [0, 0, 0, 0, 0],
1870
- [0, 0, 0, 0, 0],
1871
- [0, 0, 0, 0, 0],
1872
- [0, 0, 0, 0, 0],
1873
- [0, 0, 0, 0, 0],
1874
- [1, 1, 0, 0, 194],
1875
- [1, -1, 0, 0, 193],
1876
- [0, 0, 0, 0, 0],
1877
- [0, 0, 0, 0, 0],
1878
- [0, 0, 0, 0, 0],
1879
- [0, 0, 0, 0, 0],
1880
- [0, 0, 0, 0, 0],
1881
- [0, 0, 0, 0, 0],
1882
- [0, 0, 0, 0, 0],
1883
- [0, 0, 0, 0, 0],
1884
- [0, 0, 0, 0, 0],
1885
- [0, 0, 0, 0, 0],
1886
- [0, 0, 0, 0, 0],
1887
- [0, 0, 0, 0, 0],
1888
- [0, 0, 0, 0, 0],
1889
- [0, 0, 0, 0, 0],
1890
- [0, 0, 0, 0, 0],
1891
- [0, 0, 0, 0, 0],
1892
- [0, 0, 0, 0, 0],
1893
- [0, 0, 0, 0, 0],
1894
- [0, 0, 0, 0, 0],
1895
- [0, 0, 0, 0, 0],
1896
- [0, 0, 0, 0, 0],
1897
- [0, 0, 0, 0, 0],
1898
- [0, 0, 0, 0, 0],
1899
- [0, 0, 0, 0, 0],
1900
- [0, 0, 0, 0, 0],
1901
- [0, 0, 0, 0, 0],
1902
- [0, 0, 0, 0, 0],
1903
- [0, 0, 0, 0, 0],
1904
- [1, 0, 1, 0, 164],
1905
- [0, 0, 0, 0, 0],
1906
- [0, 0, 0, 0, 0],
1907
- [1, 0, -1, 0, 161],
1908
- [0, 0, 0, 0, 0],
1909
- [0, 0, 0, 0, 0],
1910
- [0, 0, 0, 0, 0],
1911
- [0, 0, 0, 0, 0],
1912
- [0, 0, 0, 0, 0],
1913
- [0, 0, 0, 0, 0],
1914
- [0, 0, 0, 0, 0],
1915
- [0, 0, 0, 0, 0],
1916
- [1, 0, 0, 1, 152],
1917
- [0, 0, 0, 0, 0],
1918
- [0, 0, 0, 0, 0],
1919
- [0, 0, 0, 0, 0],
1920
- [0, 0, 0, 0, 0],
1921
- [0, 0, 0, 0, 0],
1922
- [0, 0, 0, 0, 0],
1923
- [1, 0, 0, 1, 145],
1924
- [1, 0, 0, 1, 144],
1925
- [0, 0, 0, 0, 0],
1926
- [0, 0, 0, 0, 0],
1927
- [0, 0, 0, 0, 0],
1928
- [0, 0, 0, 0, 0],
1929
- [0, 0, 0, 0, 0],
1930
- [0, 0, 0, 0, 0],
1931
- [1, 0, 0, -1, 137],
1932
- [0, 0, 0, 0, 0],
1933
- [0, 0, 0, 0, 0],
1934
- [0, 0, 0, 0, 0],
1935
- [1, 0, 1, 0, 133],
1936
- [1, 0, 1, 0, 132],
1937
- [1, 1, 0, 0, 131],
1938
- [1, 1, 0, 0, 130],
1939
- [0, 0, 0, 0, 0],
1940
- [0, 0, 0, 0, 0],
1941
- [0, 0, 0, 0, 0],
1942
- [0, 0, 0, 0, 0],
1943
- [0, 0, 0, 0, 0],
1944
- [0, 0, 0, 0, 0],
1945
- [0, 0, 0, 0, 0],
1946
- [0, 0, 0, 0, 0],
1947
- [0, 0, 0, 0, 0],
1948
- [0, 0, 0, 0, 0],
1949
- [0, 0, 0, 0, 0],
1950
- [0, 0, 0, 0, 0],
1951
- [0, 0, 0, 0, 0],
1952
- [0, 0, 0, 0, 0],
1953
- [0, 0, 0, 0, 0],
1954
- [0, 0, 0, 0, 0],
1955
- [0, 0, 0, 0, 0],
1956
- [0, 0, 0, 0, 0],
1957
- [0, 0, 0, 0, 0],
1958
- [0, 0, 0, 0, 0],
1959
- [0, 0, 0, 0, 0],
1960
- [0, 0, 0, 0, 0],
1961
- [0, 0, 0, 0, 0],
1962
- [0, 0, 0, 0, 0],
1963
- [0, 0, 0, 0, 0],
1964
- [0, 0, 0, 0, 0],
1965
- [0, 0, 0, 0, 0],
1966
- [0, 0, 0, 0, 0],
1967
- [0, 0, 0, 0, 0],
1968
- [1, 0, 0, 1, 100],
1969
- [0, 0, 0, 0, 0],
1970
- [1, 0, 0, 1, 98],
1971
- [0, 0, 0, 0, 0],
1972
- [1, 0, 0, 1, 96],
1973
- [0, 0, 0, 0, 0],
1974
- [0, 0, 0, 0, 0],
1975
- [0, 0, 0, 0, 0],
1976
- [0, 0, 0, 0, 0],
1977
- [0, 0, 0, 0, 0],
1978
- [0, 0, 0, 0, 0],
1979
- [0, 0, 0, 0, 0],
1980
- [1, 0, 1, 0, 88],
1981
- [0, 0, 0, 0, 0],
1982
- [0, 0, 0, 0, 0],
1983
- [0, 0, 0, 0, 0],
1984
- [0, 0, 0, 0, 0],
1985
- [0, 0, 0, 0, 0],
1986
- [1, 0, -1, 0, 82],
1987
- [0, 0, 0, 0, 0],
1988
- [0, 0, 0, 0, 0],
1989
- [0, 0, 0, 0, 0],
1990
- [0, 0, 0, 0, 0],
1991
- [0, 0, 0, 0, 0],
1992
- [0, 0, 0, 0, 0],
1993
- [0, 0, 0, 0, 0],
1994
- [1, 0, 1, 0, 74],
1995
- [0, 0, 0, 0, 0],
1996
- [1, 0, 1, 0, 72],
1997
- [0, 0, 0, 0, 0],
1998
- [1, 0, 0, -1, 70],
1999
- [0, 0, 0, 0, 0],
2000
- [0, 0, 0, 0, 0],
2001
- [1, -1, 0, 0, 67],
2002
- [0, 0, 0, 0, 0],
2003
- [1, -1, 0, 0, 65],
2004
- [0, 0, 0, 0, 0],
2005
- [0, 0, 0, 0, 0],
2006
- [0, 0, 0, 0, 0],
2007
- [0, 0, 0, 0, 0],
2008
- [0, 0, 0, 0, 0],
2009
- [0, 0, 0, 0, 0],
2010
- [0, 0, 0, 0, 0],
2011
- [0, 0, 0, 0, 0],
2012
- [1, 1, 0, 0, 56],
2013
- [0, 0, 0, 0, 0],
2014
- [0, 0, 0, 0, 0],
2015
- [0, 0, 0, 0, 0],
2016
- [1, -1, 0, 0, 52],
2017
- [0, 0, 0, 0, 0],
2018
- [0, 0, 0, 0, 0],
2019
- [0, 0, 0, 0, 0],
2020
- [0, 0, 0, 0, 0],
2021
- [0, 0, 0, 0, 0],
2022
- [0, 0, 0, 0, 0],
2023
- [0, 0, 0, 0, 0],
2024
- [1, 1, 0, 0, 44],
2025
- [0, 0, 0, 0, 0],
2026
- [0, 0, 0, 0, 0],
2027
- [0, 0, 0, 0, 0],
2028
- [1, 1, 0, 0, 40],
2029
- [0, 0, 0, 0, 0],
2030
- [1, 0, 0, -1, 38],
2031
- [1, 0, -1, 0, 37],
2032
- [0, 0, 0, 0, 0],
2033
- [0, 0, 0, 0, 0],
2034
- [0, 0, 0, 0, 0],
2035
- [1, 0, -1, 0, 33],
2036
- [0, 0, 0, 0, 0],
2037
- [0, 0, 0, 0, 0],
2038
- [0, 0, 0, 0, 0],
2039
- [0, 0, 0, 0, 0],
2040
- [1, -1, 0, 0, 28],
2041
- [0, 0, 0, 0, 0],
2042
- [1, 0, -1, 0, 26],
2043
- [1, 0, 0, -1, 25],
2044
- [0, 0, 0, 0, 0],
2045
- [0, 0, 0, 0, 0],
2046
- [0, 0, 0, 0, 0],
2047
- [0, 0, 0, 0, 0],
2048
- [1, -1, 0, 0, 20],
2049
- [0, 0, 0, 0, 0],
2050
- [1, 0, -1, 0, 18],
2051
- [0, 0, 0, 0, 0],
2052
- [0, 0, 0, 0, 0],
2053
- [0, 0, 0, 0, 0],
2054
- [0, 0, 0, 0, 0],
2055
- [0, 0, 0, 0, 0],
2056
- [0, 0, 0, 0, 0],
2057
- [0, 0, 0, 0, 0],
2058
- [0, 0, 0, 0, 0],
2059
- [1, 0, 0, -1, 9],
2060
- [0, 0, 0, 0, 0],
2061
- [0, 0, 0, 0, 0],
2062
- [1, 0, 0, -1, 6],
2063
- [0, 0, 0, 0, 0],
2064
- [0, 0, 0, 0, 0],
2065
- [0, 0, 0, 0, 0],
2066
- [0, 0, 0, 0, 0],
2067
- [0, 0, 0, 0, 0],
2068
- [0, 0, 0, 0, 0],
2069
- ]
2070
- tet_table = [
2071
- [-1, -1, -1, -1, -1, -1],
2072
- [0, 0, 0, 0, 0, 0],
2073
- [0, 0, 0, 0, 0, 0],
2074
- [1, 1, 1, 1, 1, 1],
2075
- [4, 4, 4, 4, 4, 4],
2076
- [0, 0, 0, 0, 0, 0],
2077
- [4, 0, 0, 4, 4, -1],
2078
- [1, 1, 1, 1, 1, 1],
2079
- [4, 4, 4, 4, 4, 4],
2080
- [0, 4, 0, 4, 4, -1],
2081
- [0, 0, 0, 0, 0, 0],
2082
- [1, 1, 1, 1, 1, 1],
2083
- [5, 5, 5, 5, 5, 5],
2084
- [0, 0, 0, 0, 0, 0],
2085
- [0, 0, 0, 0, 0, 0],
2086
- [1, 1, 1, 1, 1, 1],
2087
- [2, 2, 2, 2, 2, 2],
2088
- [0, 0, 0, 0, 0, 0],
2089
- [2, 0, 2, -1, 0, 2],
2090
- [1, 1, 1, 1, 1, 1],
2091
- [2, -1, 2, 4, 4, 2],
2092
- [0, 0, 0, 0, 0, 0],
2093
- [2, 0, 2, 4, 4, 2],
2094
- [1, 1, 1, 1, 1, 1],
2095
- [2, 4, 2, 4, 4, 2],
2096
- [0, 4, 0, 4, 4, 0],
2097
- [2, 0, 2, 0, 0, 2],
2098
- [1, 1, 1, 1, 1, 1],
2099
- [2, 5, 2, 5, 5, 2],
2100
- [0, 0, 0, 0, 0, 0],
2101
- [2, 0, 2, 0, 0, 2],
2102
- [1, 1, 1, 1, 1, 1],
2103
- [1, 1, 1, 1, 1, 1],
2104
- [0, 1, 1, -1, 0, 1],
2105
- [0, 0, 0, 0, 0, 0],
2106
- [2, 2, 2, 2, 2, 2],
2107
- [4, 1, 1, 4, 4, 1],
2108
- [0, 1, 1, 0, 0, 1],
2109
- [4, 0, 0, 4, 4, 0],
2110
- [2, 2, 2, 2, 2, 2],
2111
- [-1, 1, 1, 4, 4, 1],
2112
- [0, 1, 1, 4, 4, 1],
2113
- [0, 0, 0, 0, 0, 0],
2114
- [2, 2, 2, 2, 2, 2],
2115
- [5, 1, 1, 5, 5, 1],
2116
- [0, 1, 1, 0, 0, 1],
2117
- [0, 0, 0, 0, 0, 0],
2118
- [2, 2, 2, 2, 2, 2],
2119
- [1, 1, 1, 1, 1, 1],
2120
- [0, 0, 0, 0, 0, 0],
2121
- [0, 0, 0, 0, 0, 0],
2122
- [8, 8, 8, 8, 8, 8],
2123
- [1, 1, 1, 4, 4, 1],
2124
- [0, 0, 0, 0, 0, 0],
2125
- [4, 0, 0, 4, 4, 0],
2126
- [4, 4, 4, 4, 4, 4],
2127
- [1, 1, 1, 4, 4, 1],
2128
- [0, 4, 0, 4, 4, 0],
2129
- [0, 0, 0, 0, 0, 0],
2130
- [4, 4, 4, 4, 4, 4],
2131
- [1, 1, 1, 5, 5, 1],
2132
- [0, 0, 0, 0, 0, 0],
2133
- [0, 0, 0, 0, 0, 0],
2134
- [5, 5, 5, 5, 5, 5],
2135
- [6, 6, 6, 6, 6, 6],
2136
- [6, -1, 0, 6, 0, 6],
2137
- [6, 0, 0, 6, 0, 6],
2138
- [6, 1, 1, 6, 1, 6],
2139
- [4, 4, 4, 4, 4, 4],
2140
- [0, 0, 0, 0, 0, 0],
2141
- [4, 0, 0, 4, 4, 4],
2142
- [1, 1, 1, 1, 1, 1],
2143
- [6, 4, -1, 6, 4, 6],
2144
- [6, 4, 0, 6, 4, 6],
2145
- [6, 0, 0, 6, 0, 6],
2146
- [6, 1, 1, 6, 1, 6],
2147
- [5, 5, 5, 5, 5, 5],
2148
- [0, 0, 0, 0, 0, 0],
2149
- [0, 0, 0, 0, 0, 0],
2150
- [1, 1, 1, 1, 1, 1],
2151
- [2, 2, 2, 2, 2, 2],
2152
- [0, 0, 0, 0, 0, 0],
2153
- [2, 0, 2, 2, 0, 2],
2154
- [1, 1, 1, 1, 1, 1],
2155
- [2, 2, 2, 2, 2, 2],
2156
- [0, 0, 0, 0, 0, 0],
2157
- [2, 0, 2, 2, 2, 2],
2158
- [1, 1, 1, 1, 1, 1],
2159
- [2, 4, 2, 2, 4, 2],
2160
- [0, 4, 0, 4, 4, 0],
2161
- [2, 0, 2, 2, 0, 2],
2162
- [1, 1, 1, 1, 1, 1],
2163
- [2, 2, 2, 2, 2, 2],
2164
- [0, 0, 0, 0, 0, 0],
2165
- [0, 0, 0, 0, 0, 0],
2166
- [1, 1, 1, 1, 1, 1],
2167
- [6, 1, 1, 6, -1, 6],
2168
- [6, 1, 1, 6, 0, 6],
2169
- [6, 0, 0, 6, 0, 6],
2170
- [6, 2, 2, 6, 2, 6],
2171
- [4, 1, 1, 4, 4, 1],
2172
- [0, 1, 1, 0, 0, 1],
2173
- [4, 0, 0, 4, 4, 4],
2174
- [2, 2, 2, 2, 2, 2],
2175
- [6, 1, 1, 6, 4, 6],
2176
- [6, 1, 1, 6, 4, 6],
2177
- [6, 0, 0, 6, 0, 6],
2178
- [6, 2, 2, 6, 2, 6],
2179
- [5, 1, 1, 5, 5, 1],
2180
- [0, 1, 1, 0, 0, 1],
2181
- [0, 0, 0, 0, 0, 0],
2182
- [2, 2, 2, 2, 2, 2],
2183
- [1, 1, 1, 1, 1, 1],
2184
- [0, 0, 0, 0, 0, 0],
2185
- [0, 0, 0, 0, 0, 0],
2186
- [6, 6, 6, 6, 6, 6],
2187
- [1, 1, 1, 1, 1, 1],
2188
- [0, 0, 0, 0, 0, 0],
2189
- [0, 0, 0, 0, 0, 0],
2190
- [4, 4, 4, 4, 4, 4],
2191
- [1, 1, 1, 1, 4, 1],
2192
- [0, 4, 0, 4, 4, 0],
2193
- [0, 0, 0, 0, 0, 0],
2194
- [4, 4, 4, 4, 4, 4],
2195
- [1, 1, 1, 1, 1, 1],
2196
- [0, 0, 0, 0, 0, 0],
2197
- [0, 5, 0, 5, 0, 5],
2198
- [5, 5, 5, 5, 5, 5],
2199
- [5, 5, 5, 5, 5, 5],
2200
- [0, 5, 0, 5, 0, 5],
2201
- [-1, 5, 0, 5, 0, 5],
2202
- [1, 5, 1, 5, 1, 5],
2203
- [4, 5, -1, 5, 4, 5],
2204
- [0, 5, 0, 5, 0, 5],
2205
- [4, 5, 0, 5, 4, 5],
2206
- [1, 5, 1, 5, 1, 5],
2207
- [4, 4, 4, 4, 4, 4],
2208
- [0, 4, 0, 4, 4, 4],
2209
- [0, 0, 0, 0, 0, 0],
2210
- [1, 1, 1, 1, 1, 1],
2211
- [6, 6, 6, 6, 6, 6],
2212
- [0, 0, 0, 0, 0, 0],
2213
- [0, 0, 0, 0, 0, 0],
2214
- [1, 1, 1, 1, 1, 1],
2215
- [2, 5, 2, 5, -1, 5],
2216
- [0, 5, 0, 5, 0, 5],
2217
- [2, 5, 2, 5, 0, 5],
2218
- [1, 5, 1, 5, 1, 5],
2219
- [2, 5, 2, 5, 4, 5],
2220
- [0, 5, 0, 5, 0, 5],
2221
- [2, 5, 2, 5, 4, 5],
2222
- [1, 5, 1, 5, 1, 5],
2223
- [2, 4, 2, 4, 4, 2],
2224
- [0, 4, 0, 4, 4, 4],
2225
- [2, 0, 2, 0, 0, 2],
2226
- [1, 1, 1, 1, 1, 1],
2227
- [2, 6, 2, 6, 6, 2],
2228
- [0, 0, 0, 0, 0, 0],
2229
- [2, 0, 2, 0, 0, 2],
2230
- [1, 1, 1, 1, 1, 1],
2231
- [1, 1, 1, 1, 1, 1],
2232
- [0, 1, 1, 1, 0, 1],
2233
- [0, 0, 0, 0, 0, 0],
2234
- [2, 2, 2, 2, 2, 2],
2235
- [4, 1, 1, 1, 4, 1],
2236
- [0, 1, 1, 1, 0, 1],
2237
- [4, 0, 0, 4, 4, 0],
2238
- [2, 2, 2, 2, 2, 2],
2239
- [1, 1, 1, 1, 1, 1],
2240
- [0, 1, 1, 1, 1, 1],
2241
- [0, 0, 0, 0, 0, 0],
2242
- [2, 2, 2, 2, 2, 2],
2243
- [1, 1, 1, 1, 1, 1],
2244
- [0, 0, 0, 0, 0, 0],
2245
- [0, 0, 0, 0, 0, 0],
2246
- [2, 2, 2, 2, 2, 2],
2247
- [1, 1, 1, 1, 1, 1],
2248
- [0, 0, 0, 0, 0, 0],
2249
- [0, 0, 0, 0, 0, 0],
2250
- [5, 5, 5, 5, 5, 5],
2251
- [1, 1, 1, 1, 4, 1],
2252
- [0, 0, 0, 0, 0, 0],
2253
- [4, 0, 0, 4, 4, 0],
2254
- [4, 4, 4, 4, 4, 4],
2255
- [1, 1, 1, 1, 1, 1],
2256
- [0, 0, 0, 0, 0, 0],
2257
- [0, 0, 0, 0, 0, 0],
2258
- [4, 4, 4, 4, 4, 4],
2259
- [1, 1, 1, 1, 1, 1],
2260
- [6, 0, 0, 6, 0, 6],
2261
- [0, 0, 0, 0, 0, 0],
2262
- [6, 6, 6, 6, 6, 6],
2263
- [5, 5, 5, 5, 5, 5],
2264
- [5, 5, 0, 5, 0, 5],
2265
- [5, 5, 0, 5, 0, 5],
2266
- [5, 5, 1, 5, 1, 5],
2267
- [4, 4, 4, 4, 4, 4],
2268
- [0, 0, 0, 0, 0, 0],
2269
- [4, 4, 0, 4, 4, 4],
2270
- [1, 1, 1, 1, 1, 1],
2271
- [4, 4, 4, 4, 4, 4],
2272
- [4, 4, 0, 4, 4, 4],
2273
- [0, 0, 0, 0, 0, 0],
2274
- [1, 1, 1, 1, 1, 1],
2275
- [8, 8, 8, 8, 8, 8],
2276
- [0, 0, 0, 0, 0, 0],
2277
- [0, 0, 0, 0, 0, 0],
2278
- [1, 1, 1, 1, 1, 1],
2279
- [2, 2, 2, 2, 2, 2],
2280
- [0, 0, 0, 0, 0, 0],
2281
- [2, 2, 2, 2, 0, 2],
2282
- [1, 1, 1, 1, 1, 1],
2283
- [2, 2, 2, 2, 2, 2],
2284
- [0, 0, 0, 0, 0, 0],
2285
- [2, 2, 2, 2, 2, 2],
2286
- [1, 1, 1, 1, 1, 1],
2287
- [2, 2, 2, 2, 2, 2],
2288
- [0, 0, 0, 0, 0, 0],
2289
- [0, 0, 0, 0, 0, 0],
2290
- [4, 1, 1, 4, 4, 1],
2291
- [2, 2, 2, 2, 2, 2],
2292
- [0, 0, 0, 0, 0, 0],
2293
- [0, 0, 0, 0, 0, 0],
2294
- [1, 1, 1, 1, 1, 1],
2295
- [1, 1, 1, 1, 1, 1],
2296
- [1, 1, 1, 1, 0, 1],
2297
- [0, 0, 0, 0, 0, 0],
2298
- [2, 2, 2, 2, 2, 2],
2299
- [1, 1, 1, 1, 1, 1],
2300
- [0, 0, 0, 0, 0, 0],
2301
- [0, 0, 0, 0, 0, 0],
2302
- [2, 4, 2, 4, 4, 2],
2303
- [1, 1, 1, 1, 1, 1],
2304
- [1, 1, 1, 1, 1, 1],
2305
- [0, 0, 0, 0, 0, 0],
2306
- [2, 2, 2, 2, 2, 2],
2307
- [1, 1, 1, 1, 1, 1],
2308
- [0, 0, 0, 0, 0, 0],
2309
- [0, 0, 0, 0, 0, 0],
2310
- [2, 2, 2, 2, 2, 2],
2311
- [1, 1, 1, 1, 1, 1],
2312
- [0, 0, 0, 0, 0, 0],
2313
- [0, 0, 0, 0, 0, 0],
2314
- [5, 5, 5, 5, 5, 5],
2315
- [1, 1, 1, 1, 1, 1],
2316
- [0, 0, 0, 0, 0, 0],
2317
- [0, 0, 0, 0, 0, 0],
2318
- [4, 4, 4, 4, 4, 4],
2319
- [1, 1, 1, 1, 1, 1],
2320
- [0, 0, 0, 0, 0, 0],
2321
- [0, 0, 0, 0, 0, 0],
2322
- [4, 4, 4, 4, 4, 4],
2323
- [1, 1, 1, 1, 1, 1],
2324
- [0, 0, 0, 0, 0, 0],
2325
- [0, 0, 0, 0, 0, 0],
2326
- [12, 12, 12, 12, 12, 12],
2327
- ]
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
sam3d_objects_mlx/pipeline.py DELETED
@@ -1,311 +0,0 @@
1
- """Bounded streaming inference over image/mask/point-map requests."""
2
-
3
- import asyncio
4
- import threading
5
- from concurrent.futures import ThreadPoolExecutor
6
- from dataclasses import dataclass
7
- from typing import Optional
8
-
9
- import mlx.core as mx
10
-
11
- from .flow import LATENTS
12
- from .mesh import extract_mesh, unique
13
- from .processing import decode_pose, prepare_inputs
14
- from .sam3d_objects import Model
15
- from .sparse import Grid, nonzero
16
-
17
-
18
- @dataclass(frozen=True)
19
- class Request:
20
- image: mx.array
21
- mask: Optional[mx.array] = None
22
- pointmap: Optional[mx.array] = None
23
- request_id: str = ""
24
- seed: int = 42
25
- ss_steps: Optional[int] = None
26
- slat_steps: Optional[int] = None
27
- formats: tuple = ("gaussian", "mesh")
28
- estimate_depth: bool = True
29
-
30
-
31
- @dataclass(frozen=True)
32
- class Event:
33
- request_id: str
34
- stage: str
35
- step: int
36
- total_steps: int
37
- data: dict
38
-
39
-
40
- def schedule(steps, rescale):
41
- if not isinstance(steps, int) or isinstance(steps, bool) or steps < 1:
42
- raise ValueError("Inference step counts must be positive integers")
43
- return [
44
- (i / steps) / (1 + (rescale - 1) * (1 - i / steps)) for i in range(steps + 1)
45
- ]
46
-
47
-
48
- def sample(flow, state, steps, rescale, strength, condition, *, grid=None, cancel=None):
49
- """Euler flow with upstream's additive CFG on timestamps [0, 500]."""
50
- times = schedule(steps, rescale)
51
- for i, (t0, t1) in enumerate(zip(times, times[1:]), 1):
52
- if cancel is not None and cancel.is_set():
53
- return
54
- time = mx.array([t0 * 1000], mx.float32)
55
- if isinstance(state, dict):
56
- velocity = flow(state, time, condition, mx.zeros(1))
57
- if strength > 0 and t0 <= 0.5:
58
- uncond = flow(state, time, mx.zeros_like(condition), mx.zeros(1))
59
- velocity = {
60
- n: (1 + strength) * v.astype(mx.float32)
61
- - strength * uncond[n].astype(mx.float32)
62
- for n, v in velocity.items()
63
- }
64
- state = {
65
- n: x + (t1 - t0) * velocity[n].astype(mx.float32)
66
- for n, x in state.items()
67
- }
68
- else:
69
- velocity = flow(state, grid, time, condition)
70
- if strength > 0 and t0 <= 0.5:
71
- uncond = flow(state, grid, time, mx.zeros_like(condition))
72
- velocity = (1 + strength) * velocity.astype(
73
- mx.float32
74
- ) - strength * uncond.astype(mx.float32)
75
- state = state + (t1 - t0) * velocity.astype(mx.float32)
76
- mx.async_eval(state)
77
- yield i, state
78
-
79
-
80
- def occupancy_grid(logits, prune=1):
81
- r = logits.shape[1]
82
- active = nonzero(logits[0, ..., 0] > 0)
83
- if not active.size:
84
- raise RuntimeError("The structure decoder produced no occupied voxels")
85
- coords = mx.stack(
86
- [mx.zeros_like(active), active // (r * r), active // r % r, active % r], axis=-1
87
- ).astype(mx.int32)
88
- grid = Grid(coords, r)
89
- if prune:
90
- if prune != 1:
91
- raise ValueError("Only the released one-voxel pruning radius is supported")
92
- surface = nonzero(mx.any(grid.neighbors() == coords.shape[0], axis=-1))
93
- coords = coords[surface]
94
- factor = 1
95
- if coords.shape[0] > 42000:
96
- xyz = coords[:, 1:].astype(mx.float32)
97
- low, high = xyz.min(axis=0), xyz.max(axis=0)
98
- size = high - low + 1
99
- target_size = size / 2
100
- target_low = low + (size - target_size) / 2
101
- target_high = target_low + target_size - 1
102
- xyz = mx.round(
103
- (xyz - low) / mx.maximum(high - low, 1) * (target_size - 1) + target_low
104
- )
105
- xyz = mx.clip(
106
- xyz, target_low.astype(mx.int32), target_high.astype(mx.int32)
107
- ).astype(mx.int32)
108
- codes, _ = unique((xyz[:, 0] * r + xyz[:, 1]) * r + xyz[:, 2])
109
- coords = mx.stack(
110
- [mx.zeros_like(codes), codes // (r * r), codes // r % r, codes % r], axis=-1
111
- )
112
- if coords.shape[0] > 42000:
113
- raise RuntimeError(
114
- "Occupancy remains too large after upstream downsampling"
115
- )
116
- factor = 2
117
- return Grid(coords, r), factor
118
-
119
-
120
- class Pipeline:
121
- """One model, one active stream; input backpressure is one request.
122
-
123
- GPU work is submitted with async_eval. Dynamic sparse topology requires
124
- scalar synchronization. Async iteration runs submissions on one worker,
125
- leaving the caller's event loop responsive; it never buffers whole videos.
126
- """
127
-
128
- def __init__(self, model):
129
- self.model = model
130
- self._lock = threading.Lock()
131
-
132
- @classmethod
133
- def from_pretrained(cls, path):
134
- return cls(Model.from_pretrained(path))
135
-
136
- def _run(self, request, cancel):
137
- config, model = self.model.config, self.model
138
- formats = tuple(request.formats)
139
- if not formats or set(formats) - {"gaussian", "gaussian_4", "mesh"}:
140
- raise ValueError("formats must contain gaussian, gaussian_4, or mesh")
141
- ss_steps = config.ss_steps if request.ss_steps is None else request.ss_steps
142
- slat_steps = (
143
- config.slat_steps if request.slat_steps is None else request.slat_steps
144
- )
145
- schedule(ss_steps, config.ss_rescale_t)
146
- schedule(slat_steps, config.slat_rescale_t)
147
- pointmap = request.pointmap
148
- if pointmap is None and request.estimate_depth and config.depth_model:
149
- pointmap = model.depth_model(request.image, config.depth_num_tokens)
150
- mx.async_eval(pointmap)
151
- yield Event(request.request_id, "depth", 1, 1, {"pointmap": pointmap})
152
- inputs, metadata = prepare_inputs(
153
- request.image, request.mask, pointmap, size=config.image_size
154
- )
155
- condition = model.ss_condition_embedder(inputs)
156
- mx.async_eval(condition)
157
- yield Event(
158
- request.request_id,
159
- "conditioning",
160
- 1,
161
- 1,
162
- {"pointmap_conditioned": metadata["pointmap_conditioned"]},
163
- )
164
- keys = mx.random.split(mx.random.key(request.seed), len(LATENTS) + 1)
165
- state = {
166
- name: mx.random.normal(
167
- (1, config.latent_resolution**3 if name == "shape" else 1, channels),
168
- key=keys[i],
169
- )
170
- for i, (name, channels) in enumerate(LATENTS.items())
171
- }
172
- for step, state in sample(
173
- model.ss_generator,
174
- state,
175
- ss_steps,
176
- config.ss_rescale_t,
177
- config.ss_guidance,
178
- condition,
179
- cancel=cancel,
180
- ):
181
- yield Event(
182
- request.request_id, "structure", step, ss_steps, {"latents": state}
183
- )
184
- if cancel.is_set():
185
- return
186
- logits = model.ss_decoder(state["shape"])
187
- mx.async_eval(logits)
188
- grid, factor = occupancy_grid(logits, config.downsample_ss_dist)
189
- pose = decode_pose(state, metadata, factor)
190
- yield Event(
191
- request.request_id, "occupancy", 1, 1, {"coords": grid.coords, "pose": pose}
192
- )
193
- condition = model.slat_condition_embedder(inputs)
194
- mx.async_eval(condition)
195
- latent = mx.random.normal(
196
- (grid.coords.shape[0], config.latent_channels), key=keys[-1]
197
- )
198
- for step, latent in sample(
199
- model.slat_generator,
200
- latent,
201
- slat_steps,
202
- config.slat_rescale_t,
203
- config.slat_guidance,
204
- condition,
205
- grid=grid,
206
- cancel=cancel,
207
- ):
208
- yield Event(
209
- request.request_id,
210
- "latent",
211
- step,
212
- slat_steps,
213
- {"features": latent, "coords": grid.coords},
214
- )
215
- if cancel.is_set():
216
- return
217
- latent = latent * mx.array(config.slat_std) + mx.array(config.slat_mean)
218
- result = {
219
- "coords": grid.coords,
220
- "latents": latent,
221
- "pose": pose,
222
- "pointmap_conditioned": metadata["pointmap_conditioned"],
223
- }
224
- for index, kind in enumerate(formats, 1):
225
- if cancel.is_set():
226
- return
227
- if kind == "mesh":
228
- features, mesh_grid = model.slat_decoder_mesh(latent, grid)
229
- mx.async_eval(features)
230
- output = extract_mesh(features, mesh_grid)
231
- else:
232
- decoder = (
233
- model.slat_decoder_gs
234
- if kind == "gaussian"
235
- else model.slat_decoder_gs_4
236
- )
237
- output = decoder(latent, grid)
238
- mx.async_eval(output)
239
- result[kind] = output
240
- yield Event(request.request_id, kind, index, len(formats), output)
241
- mx.async_eval(result)
242
- yield Event(request.request_id, "complete", 1, 1, result)
243
-
244
- def stream(self, request):
245
- if not self._lock.acquire(blocking=False):
246
- raise RuntimeError("A stream is already using this pipeline")
247
- cancel = threading.Event()
248
- try:
249
- yield from self._run(request, cancel)
250
- finally:
251
- cancel.set()
252
- self._lock.release()
253
-
254
- def generate(self, image, mask=None, pointmap=None, **kwargs):
255
- for event in self.stream(Request(image, mask, pointmap, **kwargs)):
256
- if event.stage == "complete":
257
- mx.eval(event.data)
258
- return event.data
259
-
260
- async def astream(self, requests):
261
- """Consume an iterable or async iterable, yielding ordered Event objects.
262
-
263
- Cancellation stops further submissions and closes the active request.
264
- Already submitted GPU commands finish before worker stream cleanup.
265
- """
266
- if not self._lock.acquire(blocking=False):
267
- raise RuntimeError("A stream is already using this pipeline")
268
- executor = ThreadPoolExecutor(max_workers=1, thread_name_prefix="sam3d-mlx")
269
- loop = asyncio.get_running_loop()
270
- cancel = threading.Event()
271
- iterator = None
272
-
273
- def advance():
274
- return next(iterator, None)
275
-
276
- async def inputs():
277
- if hasattr(requests, "__aiter__"):
278
- async for item in requests:
279
- yield item
280
- else:
281
- for item in requests:
282
- yield item
283
-
284
- def cleanup():
285
- if iterator is not None:
286
- iterator.close()
287
- mx.synchronize()
288
- clear = getattr(mx, "clear_streams", None)
289
- if clear is not None:
290
- clear()
291
-
292
- try:
293
- async for request in inputs():
294
- iterator = self._run(request, cancel)
295
- while True:
296
- event = await loop.run_in_executor(executor, advance)
297
- if event is None:
298
- break
299
- yield event
300
- iterator = None
301
- finally:
302
- cancel.set()
303
- # Cleanup is queued behind any in-flight advance on the same worker.
304
- pending_cleanup = loop.run_in_executor(executor, cleanup)
305
-
306
- def release(_):
307
- executor.shutdown(wait=False)
308
- self._lock.release()
309
-
310
- pending_cleanup.add_done_callback(release)
311
- await asyncio.shield(pending_cleanup)
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
sam3d_objects_mlx/processing.py DELETED
@@ -1,213 +0,0 @@
1
- # Adapted from Meta SAM 3D Objects. See LICENSE in this directory.
2
- """MLX image/mask/point-map preprocessing and layout decoding."""
3
-
4
- import mlx.core as mx
5
-
6
- from .resize import resize
7
- from .sparse import nonzero
8
-
9
-
10
- def _square(x, fill=0):
11
- h, w = x.shape[:2]
12
- side = max(h, w)
13
- top, left = (side - h) // 2, (side - w) // 2
14
- return mx.pad(
15
- x,
16
- [(top, side - h - top), (left, side - w - left), (0, 0)],
17
- constant_values=fill,
18
- )
19
-
20
-
21
- def _crop(x, box):
22
- left, top, right, bottom = box
23
- h, w = x.shape[:2]
24
- pads = [
25
- (max(0, -top), max(0, bottom - h)),
26
- (max(0, -left), max(0, right - w)),
27
- (0, 0),
28
- ]
29
- x = mx.pad(x, pads)
30
- y, z = max(top, 0), max(left, 0)
31
- return x[y : y + bottom - top, z : z + right - left]
32
-
33
-
34
- def _median(x):
35
- x = x.reshape(-1)
36
- valid = ~mx.isnan(x)
37
- count = int(valid.sum().item())
38
- if not count:
39
- return mx.array(float("nan"))
40
- return mx.sort(mx.where(valid, x, float("inf")))[(count - 1) // 2]
41
-
42
-
43
- def prepare_inputs(image, mask, pointmap=None, *, size=518):
44
- """Accept HWC RGB/RGBA and HW masks; floating RGB must be in [0, 1].
45
-
46
- Point maps use the upstream PyTorch3D camera convention (+X left,
47
- +Y up, +Z forward). Omitted maps use the trained point-map dropout.
48
- """
49
- if image.ndim != 3 or image.shape[-1] not in (3, 4):
50
- raise ValueError("Expected an HWC RGB or RGBA image")
51
- if mask is None:
52
- if image.shape[-1] != 4:
53
- raise ValueError("Provide an object mask or an RGBA image")
54
- mask = image[..., 3]
55
- image = image[..., :3]
56
- if image.dtype == mx.uint8:
57
- image = image.astype(mx.float32) / 255
58
- else:
59
- image = image.astype(mx.float32)
60
- if not bool(mx.all(mx.isfinite(image) & (image >= 0) & (image <= 1)).item()):
61
- raise ValueError("Floating-point image values must be finite and in [0, 1]")
62
- if mask.ndim == 3 and mask.shape[-1] == 1:
63
- mask = mask[..., 0]
64
- if mask.shape != image.shape[:2]:
65
- raise ValueError("The mask must have the same height and width as the image")
66
- threshold = 127 if mask.dtype == mx.uint8 and int(mask.max().item()) > 1 else 0.5
67
- mask = (mask > threshold).astype(mx.float32)[..., None]
68
- ys, xs = nonzero(mx.any(mask[..., 0] > 0, axis=1)), nonzero(
69
- mx.any(mask[..., 0] > 0, axis=0)
70
- )
71
- if ys.size < 2 or xs.size < 2:
72
- raise ValueError("The object mask must span at least two rows and columns")
73
- x0, x1, y0, y1 = (int(v.item()) for v in (xs[0], xs[-1], ys[0], ys[-1]))
74
- side = int(max(x1 - x0, y1 - y0, 2) * 1.2)
75
- cx, cy = (x0 + x1) / 2, (y0 + y1) / 2
76
- box = (
77
- int(cx - side // 2),
78
- int(cy - side // 2),
79
- int(cx + side // 2),
80
- int(cy + side // 2),
81
- )
82
- result = {}
83
- for name, value in (("image", image), ("mask", mask)):
84
- for output, raw in (
85
- (name, _crop(value, box)),
86
- ("rgb_image" if name == "image" else "rgb_image_mask", value),
87
- ):
88
- result[output] = resize(
89
- _square(raw)[None],
90
- size,
91
- size,
92
- nearest=name == "mask",
93
- antialias=name == "image",
94
- )
95
- shift, scale = mx.zeros(3), mx.ones(3)
96
- if pointmap is not None:
97
- if pointmap.ndim != 3 or pointmap.shape[-1] != 3:
98
- raise ValueError("Expected an HWC point map with XYZ channels")
99
- pointmap = resize(
100
- pointmap[None].astype(mx.float32),
101
- image.shape[0],
102
- image.shape[1],
103
- nearest=True,
104
- )[0]
105
- selected = pointmap.reshape(-1, 3)[nonzero(mask[..., 0] > 0)]
106
- if not bool(mx.any(mx.isfinite(selected)).item()):
107
- raise ValueError("The object mask contains no finite point-map values")
108
- shift = mx.stack([_median(selected[:, c]) for c in range(3)])
109
- scale_value = _median(mx.max(mx.abs(pointmap - shift), axis=-1))
110
- if (
111
- not bool(mx.all(mx.isfinite(shift)).item())
112
- or not bool(mx.isfinite(scale_value).item())
113
- or float(scale_value.item()) <= 0
114
- ):
115
- raise ValueError("The point map has invalid or zero spatial scale")
116
- scale = mx.broadcast_to(scale_value, (3,))
117
- normalized = (pointmap - shift) / scale
118
- result["pointmap"] = resize(
119
- _square(_crop(normalized, box), float("nan"))[None],
120
- size,
121
- size,
122
- nearest=True,
123
- )
124
- result["rgb_pointmap"] = resize(
125
- _square(normalized)[None], size, size, nearest=True
126
- )
127
- else:
128
- result["pointmap"] = result["rgb_pointmap"] = None
129
- return result, {
130
- "pointmap_scale": scale,
131
- "pointmap_shift": shift,
132
- "pointmap_conditioned": pointmap is not None,
133
- }
134
-
135
-
136
- def _normalize(x):
137
- return x / mx.maximum(mx.sqrt(mx.sum(x * x, axis=-1, keepdims=True)), 1e-12)
138
-
139
-
140
- def _cross(a, b):
141
- return mx.stack(
142
- [
143
- a[..., 1] * b[..., 2] - a[..., 2] * b[..., 1],
144
- a[..., 2] * b[..., 0] - a[..., 0] * b[..., 2],
145
- a[..., 0] * b[..., 1] - a[..., 1] * b[..., 0],
146
- ],
147
- axis=-1,
148
- )
149
-
150
-
151
- def decode_pose(latents, metadata, downsample_factor=1):
152
- mean = mx.array(
153
- [
154
- -0.06366084883674913,
155
- 0.008438224692279752,
156
- 0.00017084786438302483,
157
- 0.0007126610473540038,
158
- -0.0030916726538816417,
159
- 0.5166093753457688,
160
- ]
161
- )
162
- std = mx.array(
163
- [
164
- 0.6656971967514863,
165
- 0.6787012271867754,
166
- 0.30345010594844524,
167
- 0.4394504420678794,
168
- 0.39817973931717104,
169
- 0.6176286868761914,
170
- ]
171
- )
172
- rot = latents["6drotation_normalized"].astype(mx.float32).reshape(6) * std + mean
173
- a = _normalize(rot[:3])
174
- b = _normalize(rot[3:] - mx.sum(a * rot[3:]) * a)
175
- matrix = mx.stack([a, b, _cross(a, b)], axis=-1)
176
- m = matrix
177
- qabs = mx.sqrt(
178
- mx.maximum(
179
- mx.array([1.0, 1.0, 1.0, 1.0])
180
- + mx.stack(
181
- [
182
- m[0, 0] + m[1, 1] + m[2, 2],
183
- m[0, 0] - m[1, 1] - m[2, 2],
184
- -m[0, 0] + m[1, 1] - m[2, 2],
185
- -m[0, 0] - m[1, 1] + m[2, 2],
186
- ]
187
- ),
188
- 0,
189
- )
190
- )
191
- qw, qx, qy, qz = qabs * qabs
192
- candidates = mx.stack(
193
- [
194
- mx.stack([qw, m[2, 1] - m[1, 2], m[0, 2] - m[2, 0], m[1, 0] - m[0, 1]]),
195
- mx.stack([m[2, 1] - m[1, 2], qx, m[1, 0] + m[0, 1], m[0, 2] + m[2, 0]]),
196
- mx.stack([m[0, 2] - m[2, 0], m[1, 0] + m[0, 1], qy, m[2, 1] + m[1, 2]]),
197
- mx.stack([m[1, 0] - m[0, 1], m[2, 0] + m[0, 2], m[2, 1] + m[1, 2], qz]),
198
- ]
199
- )
200
- quaternion = (candidates / (2 * mx.maximum(qabs[:, None], 0.1)))[mx.argmax(qabs)]
201
- quaternion = mx.where(quaternion[0] < 0, -quaternion, quaternion)
202
- scale = mx.exp(latents["scale"].reshape(3)) * metadata["pointmap_scale"]
203
- scale = mx.broadcast_to(mx.mean(scale) * downsample_factor, (3,))
204
- translation = (
205
- latents["translation"].reshape(3) * metadata["pointmap_scale"]
206
- + metadata["pointmap_shift"]
207
- )
208
- return {
209
- "rotation": quaternion,
210
- "rotation_matrix": matrix,
211
- "translation": translation,
212
- "scale": scale,
213
- }
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
sam3d_objects_mlx/resize.py DELETED
@@ -1,70 +0,0 @@
1
- """Channel-last resizing without image or array dependencies."""
2
-
3
- import mlx.core as mx
4
-
5
-
6
- def resize_bicubic(x, height, width, *, antialias=False, offset=0.0):
7
- """PyTorch bicubic coefficients, including the DINO scale-factor offset."""
8
- for axis, size in ((2, width), (1, height)):
9
- old = x.shape[axis]
10
- if old == size and not offset:
11
- continue
12
- scale = old / (size + offset)
13
- center = (mx.arange(size, dtype=mx.float32) + 0.5) * scale - 0.5
14
- support = 2 * max(scale, 1) if antialias else 2
15
- taps = int(support) + 1
16
- indices = mx.floor(center).astype(mx.int32)[:, None] + mx.arange(
17
- -taps, taps + 1
18
- )
19
- distance = mx.abs(indices - center[:, None]) / (
20
- max(scale, 1) if antialias else 1
21
- )
22
- a = -0.5 if antialias else -0.75
23
- weights = mx.where(
24
- distance < 1,
25
- ((a + 2) * distance - (a + 3)) * distance**2 + 1,
26
- mx.where(
27
- distance < 2,
28
- ((a * distance - 5 * a) * distance + 8 * a) * distance - 4 * a,
29
- 0,
30
- ),
31
- )
32
- if antialias:
33
- weights = mx.where((indices >= 0) & (indices < old), weights, 0)
34
- weights = weights / weights.sum(axis=-1, keepdims=True)
35
- gathered = mx.take(x, mx.clip(indices, 0, old - 1), axis=axis)
36
- shape = [1] * gathered.ndim
37
- shape[axis : axis + 2] = weights.shape
38
- x = mx.sum(gathered * weights.reshape(shape).astype(x.dtype), axis=axis + 1)
39
- return x
40
-
41
-
42
- def resize(x, height, width, *, nearest=False, antialias=False):
43
- """Match PyTorch interpolate with align_corners=False (NHWC)."""
44
- for axis, size in ((1, height), (2, width)):
45
- old = x.shape[axis]
46
- if old == size:
47
- continue
48
- if nearest:
49
- idx = mx.minimum(mx.arange(size) * old // size, old - 1)
50
- x = mx.take(x, idx, axis=axis)
51
- continue
52
- scale = old / size
53
- center = (mx.arange(size, dtype=mx.float32) + 0.5) * scale - 0.5
54
- if antialias and scale > 1:
55
- support = int(scale) + 1
56
- offsets = mx.arange(-support, support + 1)
57
- indices = mx.floor(center).astype(mx.int32)[:, None] + offsets
58
- weights = mx.maximum(1 - mx.abs(indices - center[:, None]) / scale, 0)
59
- weights = mx.where((indices >= 0) & (indices < old), weights, 0)
60
- weights = weights / weights.sum(axis=-1, keepdims=True)
61
- else:
62
- low = mx.floor(center).astype(mx.int32)
63
- indices = mx.stack([low, low + 1], axis=-1)
64
- f = center - low
65
- weights = mx.stack([1 - f, f], axis=-1)
66
- gathered = mx.take(x, mx.clip(indices, 0, old - 1), axis=axis)
67
- shape = [1] * gathered.ndim
68
- shape[axis : axis + 2] = weights.shape
69
- x = mx.sum(gathered * weights.reshape(shape).astype(x.dtype), axis=axis + 1)
70
- return x
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
sam3d_objects_mlx/sam3d_objects.py DELETED
@@ -1,72 +0,0 @@
1
- # Adapted from Meta SAM 3D Objects. See LICENSE in this directory.
2
- """Frozen inference models for SAM 3D Objects."""
3
-
4
- import json
5
- from pathlib import Path
6
-
7
- import mlx.core as mx
8
- import mlx.nn as nn
9
-
10
- from .config import ModelConfig
11
- from .decoders import GaussianDecoder, MeshDecoder, StructureDecoder
12
- from .flow import LatentFlow, StructureFlow
13
- from .vision import ConditionEncoder
14
-
15
-
16
- class Model(nn.Module):
17
- def __init__(self, config):
18
- super().__init__()
19
- self.config = config
20
- self.ss_generator = StructureFlow(config)
21
- self.slat_generator = LatentFlow(config)
22
- self.ss_condition_embedder = ConditionEncoder(config, pointmap=True)
23
- self.slat_condition_embedder = ConditionEncoder(config)
24
- self.ss_decoder = StructureDecoder(config)
25
- self.slat_decoder_gs = GaussianDecoder(config)
26
- self.slat_decoder_gs_4 = GaussianDecoder(config, count=4)
27
- self.slat_decoder_mesh = MeshDecoder(config)
28
- if config.depth_model:
29
- from .depth import DepthModel
30
-
31
- self.depth_model = DepthModel()
32
- self.eval()
33
- self.freeze()
34
-
35
- def train(self, mode=True):
36
- if mode:
37
- raise ValueError("SAM 3D Objects is an inference-only model")
38
- return super().train(False)
39
-
40
- @classmethod
41
- def from_pretrained(cls, path):
42
- """Load a local converted bundle using MLX alone, with strict weights."""
43
- root = Path(path)
44
- config = ModelConfig.from_dict(json.loads((root / "config.json").read_text()))
45
- model = cls(config)
46
- index = root / "model.safetensors.index.json"
47
- if index.exists():
48
- files = sorted(set(json.loads(index.read_text())["weight_map"].values()))
49
- else:
50
- files = ["model.safetensors"]
51
- weights = {}
52
- for name in files:
53
- if Path(name).name != name:
54
- raise ValueError("Checkpoint shards must be in the model directory")
55
- shard = mx.load(str(root / name))
56
- if weights.keys() & shard.keys():
57
- raise ValueError("Duplicate tensor names across checkpoint shards")
58
- weights.update(shard)
59
- model.load_weights(list(weights.items()), strict=True)
60
- model.freeze()
61
- model.eval()
62
- mx.async_eval(model.parameters())
63
- return model
64
-
65
- @staticmethod
66
- def sanitize(weights):
67
- return weights
68
-
69
- def __call__(self, *args, **kwargs):
70
- from .pipeline import Pipeline
71
-
72
- return Pipeline(self).generate(*args, **kwargs)
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
sam3d_objects_mlx/sparse.py DELETED
@@ -1,172 +0,0 @@
1
- # Adapted from mlx_vlm.models.moge3.sparse and Meta SAM 3D Objects.
2
- """Sparse inference operators using MLX gathers, reductions, and attention."""
3
-
4
- from dataclasses import dataclass, field
5
- from itertools import product
6
-
7
- import mlx.core as mx
8
- import mlx.nn as nn
9
-
10
- from .layers import attend
11
-
12
-
13
- def nonzero(mask):
14
- mask = mask.reshape(-1)
15
- count = int(mx.sum(mask).item())
16
- return mx.sort(mx.where(mask, mx.arange(mask.size), mask.size))[:count]
17
-
18
-
19
- def coord_keys(coords, resolution):
20
- return (
21
- (coords[:, 0] * resolution + coords[:, 1]) * resolution + coords[:, 2]
22
- ) * resolution + coords[:, 3]
23
-
24
-
25
- @dataclass
26
- class Grid:
27
- coords: mx.array
28
- resolution: int
29
- cache: dict = field(default_factory=dict)
30
-
31
- def neighbors(self):
32
- if "neighbors" not in self.cache:
33
- offsets = mx.array(list(product((-1, 0, 1), repeat=3)), dtype=mx.int32)
34
- positions = self.coords[:, None, 1:] + offsets[None]
35
- valid = mx.all((positions >= 0) & (positions < self.resolution), axis=-1)
36
- r = self.resolution
37
- nkeys = (
38
- (self.coords[:, :1] * r + positions[:, :, 0]) * r + positions[:, :, 1]
39
- ) * r + positions[:, :, 2]
40
- keys = coord_keys(self.coords, r)
41
- order = mx.argsort(keys)
42
- sorted_keys = keys[order]
43
- index = mx.minimum(
44
- mx.searchsorted(sorted_keys, nkeys.reshape(-1)), keys.size - 1
45
- ).reshape(nkeys.shape)
46
- found = valid & (sorted_keys[index] == nkeys)
47
- self.cache["neighbors"] = mx.where(found, order[index], keys.size)
48
- return self.cache["neighbors"]
49
-
50
- def downsample(self):
51
- if "downsample" not in self.cache:
52
- coords = self.coords // mx.array([1, 2, 2, 2])
53
- r = (self.resolution + 1) // 2
54
- keys = coord_keys(coords, r)
55
- order = mx.argsort(keys)
56
- sorted_keys = keys[order]
57
- start = mx.concatenate(
58
- [mx.array([True]), sorted_keys[1:] != sorted_keys[:-1]]
59
- )
60
- first = nonzero(start)
61
- groups = mx.cumsum(start.astype(mx.int32)) - 1
62
- parent = mx.zeros(keys.size, mx.int32).at[order].add(groups)
63
- child = Grid(coords[order[first]], r)
64
- self.cache["downsample"] = (child, parent)
65
- return self.cache["downsample"]
66
-
67
- def subdivide(self):
68
- if "subdivide" not in self.cache:
69
- offsets = mx.array(
70
- [(0, *p) for p in product((0, 1), repeat=3)], dtype=mx.int32
71
- )
72
- coords = self.coords[:, None] * mx.array([1, 2, 2, 2]) + offsets[None]
73
- self.cache["subdivide"] = Grid(coords.reshape(-1, 4), self.resolution * 2)
74
- return self.cache["subdivide"]
75
-
76
- def windows(self, size, shift):
77
- key = ("windows", size, shift)
78
- if key not in self.cache:
79
- coords = mx.concatenate(
80
- [self.coords[:, :1], (self.coords[:, 1:] + shift) // size], axis=-1
81
- )
82
- keys = coord_keys(coords, (self.resolution + shift + size - 1) // size)
83
- order = mx.argsort(keys)
84
- sorted_keys = keys[order]
85
- starts = nonzero(
86
- mx.concatenate([mx.array([True]), sorted_keys[1:] != sorted_keys[:-1]])
87
- ).tolist()
88
- ends = starts[1:] + [keys.size]
89
- buckets = {}
90
- for start, end in zip(starts, ends):
91
- width = 1 << (end - start - 1).bit_length()
92
- buckets.setdefault(width, []).append((start, end - start))
93
- packed = []
94
- for width, ranges in sorted(buckets.items()):
95
- first = mx.array([s for s, _ in ranges])[:, None]
96
- lengths = mx.array([n for _, n in ranges])[:, None]
97
- offset = mx.arange(width)[None]
98
- valid = offset < lengths
99
- indices = mx.where(
100
- valid, order[mx.minimum(first + offset, keys.size - 1)], keys.size
101
- )
102
- packed.append((indices, valid))
103
- self.cache[key] = packed
104
- return self.cache[key]
105
-
106
-
107
- class SparseConv(nn.Module):
108
- def __init__(self, input_channels, output_channels, kernel=3):
109
- super().__init__()
110
- self.conv = nn.Conv3d(
111
- input_channels, output_channels, kernel, padding=kernel // 2
112
- )
113
-
114
- def __call__(self, x, grid):
115
- w, bias = self.conv.weight, self.conv.bias
116
- if w.shape[1] == 1:
117
- return mx.addmm(bias, x, w[:, 0, 0, 0].T)
118
- indices = grid.neighbors()
119
- padded = mx.concatenate([x, mx.zeros((1, x.shape[-1]), x.dtype)])
120
- # Limit temporary im2col storage to 16 MiB per matrix product.
121
- rows = max(1, (16 << 20) // (27 * x.shape[-1] * x.dtype.size))
122
- weight = w.reshape(w.shape[0], -1).T
123
- outputs = []
124
- for start in range(0, x.shape[0], rows):
125
- values = padded[indices[start : start + rows]].reshape(-1, weight.shape[0])
126
- outputs.append(mx.addmm(bias, values, weight))
127
- return mx.concatenate(outputs, axis=0)
128
-
129
-
130
- def pool(x, grid):
131
- child, parent = grid.downsample()
132
- sums = (
133
- mx.zeros((child.coords.shape[0], x.shape[-1]), mx.float32)
134
- .at[parent]
135
- .add(x.astype(mx.float32))
136
- )
137
- # Upstream torch.scatter_reduce(mean) includes the initial zero value.
138
- counts = (
139
- mx.ones((child.coords.shape[0], 1), mx.float32)
140
- .at[parent]
141
- .add(mx.ones((x.shape[0], 1)))
142
- )
143
- return (sums / counts).astype(x.dtype), child, parent
144
-
145
-
146
- class SparseGroupNorm(nn.Module):
147
- def __init__(self, channels, groups=32):
148
- super().__init__()
149
- self.weight = mx.ones(channels)
150
- self.bias = mx.zeros(channels)
151
- self.groups = min(groups, channels)
152
-
153
- def __call__(self, x):
154
- # Each streamed request has one object; normalize across all its voxels.
155
- h = x.astype(mx.float32).reshape(x.shape[0], self.groups, -1)
156
- mean = h.mean(axis=(0, 2), keepdims=True)
157
- var = ((h - mean) ** 2).mean(axis=(0, 2), keepdims=True)
158
- h = ((h - mean) * mx.rsqrt(var + 1e-5)).reshape(x.shape)
159
- return (h * self.weight + self.bias).astype(x.dtype)
160
-
161
-
162
- def window_attention(qkv, grid, size, shift):
163
- n, _, heads, dim = qkv.shape
164
- padded = mx.concatenate([qkv, mx.zeros((1, 3, heads, dim), qkv.dtype)])
165
- result = mx.zeros((n + 1, heads * dim), qkv.dtype)
166
- for indices, valid in grid.windows(size, shift):
167
- values = padded[indices]
168
- out = attend(*(values[:, :, i] for i in range(3)), mask=valid[:, None, None, :])
169
- result = result.at[indices.reshape(-1)].add(
170
- mx.where(valid[..., None], out, 0).reshape(-1, heads * dim)
171
- )
172
- return result[:n]
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
sam3d_objects_mlx/vision.py DELETED
@@ -1,215 +0,0 @@
1
- # Adapted from mlx_vlm.models.dinov2 and Meta SAM 3D Objects.
2
- # See LICENSE in this directory for the SAM components.
3
- """DINOv2 register-token image encoders and point-map conditioning."""
4
-
5
- import mlx.core as mx
6
- import mlx.nn as nn
7
-
8
- from .layers import LayerNorm, Sequential, attend, layer_norm
9
- from .resize import resize
10
-
11
-
12
- class PatchEmbed(nn.Module):
13
- def __init__(self, channels):
14
- super().__init__()
15
- self.proj = nn.Conv2d(3, channels, 14, stride=14)
16
-
17
- def __call__(self, x):
18
- x = self.proj(x)
19
- return x.reshape(x.shape[0], -1, x.shape[-1])
20
-
21
-
22
- class VisionAttention(nn.Module):
23
- def __init__(self, channels, heads):
24
- super().__init__()
25
- self.heads = heads
26
- self.qkv = nn.Linear(channels, 3 * channels)
27
- self.proj = nn.Linear(channels, channels)
28
-
29
- def __call__(self, x):
30
- qkv = self.qkv(x).reshape(*x.shape[:2], 3, self.heads, -1)
31
- return self.proj(attend(*(qkv[:, :, i] for i in range(3))))
32
-
33
-
34
- class VisionMLP(nn.Module):
35
- def __init__(self, channels, ratio=4):
36
- super().__init__()
37
- self.fc1 = nn.Linear(channels, channels * ratio)
38
- self.fc2 = nn.Linear(channels * ratio, channels)
39
-
40
- def __call__(self, x):
41
- return self.fc2(nn.gelu(self.fc1(x)))
42
-
43
-
44
- class LayerScale(nn.Module):
45
- def __init__(self, channels):
46
- super().__init__()
47
- self.gamma = mx.ones(channels)
48
-
49
- def __call__(self, x):
50
- return x * self.gamma
51
-
52
-
53
- class VisionBlock(nn.Module):
54
- def __init__(self, channels, heads, *, layer_scale=True, ratio=4):
55
- super().__init__()
56
- self.norm1 = LayerNorm(channels, eps=1e-6)
57
- self.attn = VisionAttention(channels, heads)
58
- self.norm2 = LayerNorm(channels, eps=1e-6)
59
- self.mlp = VisionMLP(channels, ratio)
60
- self.ls1 = LayerScale(channels) if layer_scale else nn.Identity()
61
- self.ls2 = LayerScale(channels) if layer_scale else nn.Identity()
62
-
63
- def __call__(self, x):
64
- x = x + self.ls1(self.attn(self.norm1(x)))
65
- return x + self.ls2(self.mlp(self.norm2(x)))
66
-
67
-
68
- class DINOv2(nn.Module):
69
- def __init__(self, config):
70
- super().__init__()
71
- c = config.dino_hidden_size
72
- n = (config.dino_image_size // 14) ** 2
73
- self.patch_embed = PatchEmbed(c)
74
- self.cls_token = mx.zeros((1, 1, c))
75
- self.mask_token = mx.zeros((1, c))
76
- self.pos_embed = mx.zeros((1, n + 1, c))
77
- self.register_tokens = mx.zeros((1, 4, c))
78
- self.blocks = [
79
- VisionBlock(c, config.dino_heads) for _ in range(config.dino_layers)
80
- ]
81
- self.norm = LayerNorm(c, eps=1e-6)
82
-
83
- def __call__(self, image, prenorm=False):
84
- x = self.patch_embed(image)
85
- if x.shape[1] + 1 != self.pos_embed.shape[1]:
86
- raise ValueError("DINO input must match the checkpoint image size")
87
- x = (
88
- mx.concatenate(
89
- [mx.broadcast_to(self.cls_token, (x.shape[0], 1, x.shape[-1])), x],
90
- axis=1,
91
- )
92
- + self.pos_embed
93
- )
94
- x = mx.concatenate(
95
- [
96
- x[:, :1],
97
- mx.broadcast_to(self.register_tokens, (x.shape[0], 4, x.shape[-1])),
98
- x[:, 1:],
99
- ],
100
- axis=1,
101
- )
102
- for block in self.blocks:
103
- x = block(x)
104
- if prenorm:
105
- return layer_norm(x)
106
- x = self.norm(x)
107
- return mx.concatenate([x[:, :1], x[:, 5:]], axis=1)
108
-
109
-
110
- class Dino(nn.Module):
111
- def __init__(self, config, prenorm=False):
112
- super().__init__()
113
- self.backbone = DINOv2(config)
114
- self._size = config.image_size
115
- self._prenorm = prenorm
116
-
117
- def __call__(self, image):
118
- x = resize(image, self._size, self._size)
119
- if x.shape[-1] == 1:
120
- x = mx.repeat(x, 3, axis=-1)
121
- x = (x - mx.array([0.485, 0.456, 0.406])) / mx.array([0.229, 0.224, 0.225])
122
- return self.backbone(x.astype(self.backbone.cls_token.dtype), self._prenorm)
123
-
124
-
125
- class PointPatchEmbed(nn.Module):
126
- def __init__(self, config):
127
- super().__init__()
128
- c, p, s = config.point_channels, config.point_patch_size, config.point_size
129
- self._patch, self._size = p, s
130
- self.point_proj = nn.Linear(3, c)
131
- self.invalid_xyz_token = mx.zeros(c)
132
- self.pos_embed = mx.zeros((1, c, s // p, s // p))
133
- self.pos_embed_window = mx.zeros((1, 1 + p * p, c))
134
- self.cls_token = mx.zeros((1, 1, c))
135
- self.blocks = [VisionBlock(c, config.point_heads, layer_scale=False, ratio=2)]
136
-
137
- def __call__(self, points):
138
- points = resize(points, self._size, self._size, nearest=True)
139
- valid = mx.all(mx.isfinite(points), axis=-1, keepdims=True)
140
- x = self.point_proj(
141
- mx.where(valid, points, 0).astype(self.point_proj.weight.dtype)
142
- )
143
- x = mx.where(valid, x, self.invalid_xyz_token)
144
- b, h, w, c = x.shape
145
- p = self._patch
146
- x = (
147
- x.reshape(b, h // p, p, w // p, p, c)
148
- .transpose(0, 1, 3, 2, 4, 5)
149
- .reshape(-1, p * p, c)
150
- )
151
- x = (
152
- mx.concatenate(
153
- [mx.broadcast_to(self.cls_token, (x.shape[0], 1, c)), x], axis=1
154
- )
155
- + self.pos_embed_window
156
- )
157
- for block in self.blocks:
158
- x = block(x)
159
- return x[:, 0].reshape(b, -1, c) + self.pos_embed.transpose(0, 2, 3, 1).reshape(
160
- 1, -1, c
161
- )
162
-
163
-
164
- class ProjectionFFN(nn.Module):
165
- def __init__(self, input_dim, output_dim):
166
- super().__init__()
167
- hidden = ((int(8 * output_dim / 3) + 255) // 256) * 256
168
- self.w1 = nn.Linear(input_dim, hidden, bias=False)
169
- self.w2 = nn.Linear(hidden, output_dim, bias=False)
170
- self.w3 = nn.Linear(input_dim, hidden, bias=False)
171
-
172
- def __call__(self, x):
173
- return self.w2(nn.silu(self.w1(x)) * self.w3(x))
174
-
175
-
176
- class ConditionEncoder(nn.Module):
177
- def __init__(self, config, *, pointmap=False):
178
- super().__init__()
179
- self.module_list = [
180
- Dino(config, prenorm=not pointmap),
181
- Dino(config, prenorm=not pointmap),
182
- ]
183
- dims = [config.dino_hidden_size] * 2
184
- if pointmap:
185
- self.module_list.append(PointPatchEmbed(config))
186
- dims.append(config.point_channels)
187
- self.idx_emb = mx.zeros((3, config.cond_channels))
188
- self.projection_nets = [
189
- Sequential(LayerNorm(d), ProjectionFFN(d, config.cond_channels))
190
- for d in dims
191
- ]
192
-
193
- def __call__(self, inputs):
194
- groups = [
195
- ("image", "rgb_image"),
196
- ("mask", "rgb_image_mask"),
197
- ("pointmap", "rgb_pointmap"),
198
- ]
199
- tokens = []
200
- for i, module in enumerate(self.module_list):
201
- for pos, name in enumerate(groups[i]):
202
- if i == 2 and inputs.get(name) is None:
203
- # Match the trained condition dropout: zero complete tokens,
204
- # including their learned positional embedding.
205
- count = (module._size // module._patch) ** 2
206
- tokens.append(
207
- mx.zeros(
208
- (inputs["image"].shape[0], count, self.idx_emb.shape[-1]),
209
- self.idx_emb.dtype,
210
- )
211
- )
212
- continue
213
- x = self.projection_nets[i](module(inputs[name]))
214
- tokens.append(x + self.idx_emb[pos : pos + 1, None])
215
- return mx.concatenate(tokens, axis=1)
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
validation.json CHANGED
@@ -13,7 +13,7 @@
13
  "external_runtime_imports": [],
14
  "regression_tests": {
15
  "class": "TestSAM3DObjects",
16
- "passed": 9
17
  },
18
  "reference_comparison": {
19
  "precision": "float32",
@@ -60,30 +60,30 @@
60
  ],
61
  "samples": [
62
  {
63
- "seconds": 20.503766250010813,
64
- "peak_memory_gb": 10.560477761
65
  },
66
  {
67
- "seconds": 20.648073707998265,
68
- "peak_memory_gb": 10.561346009
69
  },
70
  {
71
- "seconds": 20.7599100000225,
72
- "peak_memory_gb": 10.561346037
73
  }
74
  ],
75
- "median_seconds": 20.648073707998265,
76
- "peak_memory_gb": 10.561346037,
77
- "gaussians": 844864,
78
- "mesh_vertices": 611900,
79
- "mesh_faces": 1215398,
80
  "pointmap_conditioned": true,
81
  "async_validation": {
82
  "request_ids": [
83
  "0",
84
  "1"
85
  ],
86
- "event_loop_heartbeats": 3624
87
  }
88
  }
89
  }
 
13
  "external_runtime_imports": [],
14
  "regression_tests": {
15
  "class": "TestSAM3DObjects",
16
+ "passed": 13
17
  },
18
  "reference_comparison": {
19
  "precision": "float32",
 
60
  ],
61
  "samples": [
62
  {
63
+ "seconds": 14.864514124987181,
64
+ "peak_memory_gb": 10.87792566
65
  },
66
  {
67
+ "seconds": 15.67705433399533,
68
+ "peak_memory_gb": 10.878777628
69
  },
70
  {
71
+ "seconds": 15.963315250002779,
72
+ "peak_memory_gb": 10.976393304
73
  }
74
  ],
75
+ "median_seconds": 15.67705433399533,
76
+ "peak_memory_gb": 10.976393304,
77
+ "gaussians": 845184,
78
+ "mesh_vertices": 629536,
79
+ "mesh_faces": 1250780,
80
  "pointmap_conditioned": true,
81
  "async_validation": {
82
  "request_ids": [
83
  "0",
84
  "1"
85
  ],
86
+ "event_loop_heartbeats": 2810
87
  }
88
  }
89
  }