autoforge 1.5.0__tar.gz → 1.5.2__tar.gz
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- {autoforge-1.5.0 → autoforge-1.5.2}/PKG-INFO +8 -15
- {autoforge-1.5.0 → autoforge-1.5.2}/README.md +7 -14
- {autoforge-1.5.0 → autoforge-1.5.2}/pyproject.toml +1 -1
- {autoforge-1.5.0 → autoforge-1.5.2}/src/AutoForge.egg-info/PKG-INFO +8 -15
- {autoforge-1.5.0 → autoforge-1.5.2}/src/autoforge/Helper/OptimizerHelper.py +319 -148
- {autoforge-1.5.0 → autoforge-1.5.2}/src/autoforge/Modules/Optimizer.py +0 -1
- {autoforge-1.5.0 → autoforge-1.5.2}/src/autoforge/auto_forge.py +3 -3
- {autoforge-1.5.0 → autoforge-1.5.2}/setup.cfg +0 -0
- {autoforge-1.5.0 → autoforge-1.5.2}/src/AutoForge.egg-info/SOURCES.txt +0 -0
- {autoforge-1.5.0 → autoforge-1.5.2}/src/AutoForge.egg-info/dependency_links.txt +0 -0
- {autoforge-1.5.0 → autoforge-1.5.2}/src/AutoForge.egg-info/entry_points.txt +0 -0
- {autoforge-1.5.0 → autoforge-1.5.2}/src/AutoForge.egg-info/requires.txt +0 -0
- {autoforge-1.5.0 → autoforge-1.5.2}/src/AutoForge.egg-info/top_level.txt +0 -0
- {autoforge-1.5.0 → autoforge-1.5.2}/src/autoforge/Gui/Views/__init__.py +0 -0
- {autoforge-1.5.0 → autoforge-1.5.2}/src/autoforge/Gui/Views/backend_database.py +0 -0
- {autoforge-1.5.0 → autoforge-1.5.2}/src/autoforge/Gui/Views/filament_editor.py +0 -0
- {autoforge-1.5.0 → autoforge-1.5.2}/src/autoforge/Gui/__init__.py +0 -0
- {autoforge-1.5.0 → autoforge-1.5.2}/src/autoforge/Gui/app.py +0 -0
- {autoforge-1.5.0 → autoforge-1.5.2}/src/autoforge/Gui/main.py +0 -0
- {autoforge-1.5.0 → autoforge-1.5.2}/src/autoforge/Helper/CAdamW.py +0 -0
- {autoforge-1.5.0 → autoforge-1.5.2}/src/autoforge/Helper/FilamentHelper.py +0 -0
- {autoforge-1.5.0 → autoforge-1.5.2}/src/autoforge/Helper/ImageHelper.py +0 -0
- {autoforge-1.5.0 → autoforge-1.5.2}/src/autoforge/Helper/OutputHelper.py +0 -0
- {autoforge-1.5.0 → autoforge-1.5.2}/src/autoforge/Helper/PruningHelper.py +0 -0
- {autoforge-1.5.0 → autoforge-1.5.2}/src/autoforge/Helper/__init__.py +0 -0
- {autoforge-1.5.0 → autoforge-1.5.2}/src/autoforge/Helper/filamentcolors_library.py +0 -0
- {autoforge-1.5.0 → autoforge-1.5.2}/src/autoforge/Loss/LossFunctions.py +0 -0
- {autoforge-1.5.0 → autoforge-1.5.2}/src/autoforge/Loss/PerceptionLoss.py +0 -0
- {autoforge-1.5.0 → autoforge-1.5.2}/src/autoforge/Loss/__init__.py +0 -0
- {autoforge-1.5.0 → autoforge-1.5.2}/src/autoforge/Misc/__init__.py +0 -0
- {autoforge-1.5.0 → autoforge-1.5.2}/src/autoforge/Misc/color_fit_all.py +0 -0
- {autoforge-1.5.0 → autoforge-1.5.2}/src/autoforge/Modules/__init__.py +0 -0
- {autoforge-1.5.0 → autoforge-1.5.2}/src/autoforge/__init__.py +0 -0
- {autoforge-1.5.0 → autoforge-1.5.2}/src/autoforge/__main__.py +0 -0
- {autoforge-1.5.0 → autoforge-1.5.2}/src/autoforge/old_jax_files.py +0 -0
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
Metadata-Version: 2.2
|
|
2
2
|
Name: AutoForge
|
|
3
|
-
Version: 1.5.
|
|
3
|
+
Version: 1.5.2
|
|
4
4
|
Summary: AutoForge is a Python tool for generating 3D printed layered models from an input image.
|
|
5
5
|
Author-email: Hendric Voss <hvoss@techfak.uni-bielefeld.de>
|
|
6
6
|
License: CC BY-NC-SA 4.0
|
|
@@ -25,16 +25,6 @@ AutoForge is a Python tool for generating 3D printed layered models from an inpu
|
|
|
25
25
|
|
|
26
26
|
**TLDR:** It uses a picture to generate a 3D layer image that you can print with a 3d printer. Similar to [Hueforge](https://shop.thehueforge.com/), but without the manual work (and without the artistic control).
|
|
27
27
|
|
|
28
|
-
|
|
29
|
-
## Important Information
|
|
30
|
-
|
|
31
|
-
We recently switched from JAX to PyTorch, to allow for a more streamlined development process. \
|
|
32
|
-
This can have some unforeseen consequences, so please report any bugs you find.
|
|
33
|
-
|
|
34
|
-
This release should give better gpu support for more users, especially on windows. \
|
|
35
|
-
If you have problems running the code on your gpu, please refer to the [Pytorch Homepage](https://pytorch.org/) for help. \
|
|
36
|
-
Both CUDA and ROCm are supported, but you need to install the correct version of pytorch for your system.
|
|
37
|
-
|
|
38
28
|
## Example
|
|
39
29
|
All examples use only the 13 BambuLab Basic filaments, currently available in Hueforge.
|
|
40
30
|
<div style="display: flex; justify-content: center; gap: 20px;">
|
|
@@ -46,9 +36,9 @@ All examples use only the 13 BambuLab Basic filaments, currently available in Hu
|
|
|
46
36
|
</div>
|
|
47
37
|
<div style="text-align: center;">
|
|
48
38
|
<h3>Autoforge Output (75 color layers (3.0mm) + 10 gray background layers (0.4mm)) </h3>
|
|
49
|
-
<img src="https://github.com/hvoss-techfak/AutoForge/blob/main/images/lofi_discretized.
|
|
39
|
+
<img src="https://github.com/hvoss-techfak/AutoForge/blob/main/images/lofi_discretized.png" width="200" />
|
|
50
40
|
<img src="https://github.com/hvoss-techfak/AutoForge/blob/main/images/nature_discretized.jpg" width="200" />
|
|
51
|
-
<img src="https://github.com/hvoss-techfak/AutoForge/blob/main/images/cat_discretized.
|
|
41
|
+
<img src="https://github.com/hvoss-techfak/AutoForge/blob/main/images/cat_discretized.png" width="200" />
|
|
52
42
|
</div>
|
|
53
43
|
<div style="text-align: center;">
|
|
54
44
|
<h3>Imported Hueforge View</h3>
|
|
@@ -80,6 +70,9 @@ To install AutoForge, simply install the current version from PyPI:
|
|
|
80
70
|
pip install autoforge
|
|
81
71
|
```
|
|
82
72
|
|
|
73
|
+
If you have problems running the code on your gpu, please refer to the [Pytorch Homepage](https://pytorch.org/) for help. \
|
|
74
|
+
Both CUDA and ROCm are supported, but you need to install the correct version of pytorch for your system.
|
|
75
|
+
|
|
83
76
|
## Usage
|
|
84
77
|
|
|
85
78
|
The script is run from the command line and accepts several arguments. Below is an example command:
|
|
@@ -125,7 +118,7 @@ autoforge --input_image path/to/input_image.jpg --csv_file path/to/materials.csv
|
|
|
125
118
|
- `--random_seed`: Random seed for reproducibility (default: 0 (disabled) ).
|
|
126
119
|
|
|
127
120
|
### Experimental Deph Anything V2 parameters
|
|
128
|
-
I got a request to add
|
|
121
|
+
I got a request to add an initializing function that takes the original depth of the image into account when initializing. \
|
|
129
122
|
In theory this should give you a nice background/foreground separation, but in practice it can degrade the quality of the output. \
|
|
130
123
|
In addition you need the VRAM to run the depth model. \
|
|
131
124
|
I currently don't recommend it as the output is not as good as without it, but I will leave it in for now. \
|
|
@@ -172,7 +165,7 @@ The software is provided as-is and comes with no warranty or guarantee of suppor
|
|
|
172
165
|
## Acknowledgements
|
|
173
166
|
|
|
174
167
|
First and foremost:
|
|
175
|
-
- [Hueforge](https://shop.thehueforge.com/) for providing the
|
|
168
|
+
- [Hueforge](https://shop.thehueforge.com/) for providing the inspiration for this project.
|
|
176
169
|
Without it, this project would not have been possible.
|
|
177
170
|
|
|
178
171
|
AutoForge makes use of several open source libraries:
|
|
@@ -4,16 +4,6 @@ AutoForge is a Python tool for generating 3D printed layered models from an inpu
|
|
|
4
4
|
|
|
5
5
|
**TLDR:** It uses a picture to generate a 3D layer image that you can print with a 3d printer. Similar to [Hueforge](https://shop.thehueforge.com/), but without the manual work (and without the artistic control).
|
|
6
6
|
|
|
7
|
-
|
|
8
|
-
## Important Information
|
|
9
|
-
|
|
10
|
-
We recently switched from JAX to PyTorch, to allow for a more streamlined development process. \
|
|
11
|
-
This can have some unforeseen consequences, so please report any bugs you find.
|
|
12
|
-
|
|
13
|
-
This release should give better gpu support for more users, especially on windows. \
|
|
14
|
-
If you have problems running the code on your gpu, please refer to the [Pytorch Homepage](https://pytorch.org/) for help. \
|
|
15
|
-
Both CUDA and ROCm are supported, but you need to install the correct version of pytorch for your system.
|
|
16
|
-
|
|
17
7
|
## Example
|
|
18
8
|
All examples use only the 13 BambuLab Basic filaments, currently available in Hueforge.
|
|
19
9
|
<div style="display: flex; justify-content: center; gap: 20px;">
|
|
@@ -25,9 +15,9 @@ All examples use only the 13 BambuLab Basic filaments, currently available in Hu
|
|
|
25
15
|
</div>
|
|
26
16
|
<div style="text-align: center;">
|
|
27
17
|
<h3>Autoforge Output (75 color layers (3.0mm) + 10 gray background layers (0.4mm)) </h3>
|
|
28
|
-
<img src="https://github.com/hvoss-techfak/AutoForge/blob/main/images/lofi_discretized.
|
|
18
|
+
<img src="https://github.com/hvoss-techfak/AutoForge/blob/main/images/lofi_discretized.png" width="200" />
|
|
29
19
|
<img src="https://github.com/hvoss-techfak/AutoForge/blob/main/images/nature_discretized.jpg" width="200" />
|
|
30
|
-
<img src="https://github.com/hvoss-techfak/AutoForge/blob/main/images/cat_discretized.
|
|
20
|
+
<img src="https://github.com/hvoss-techfak/AutoForge/blob/main/images/cat_discretized.png" width="200" />
|
|
31
21
|
</div>
|
|
32
22
|
<div style="text-align: center;">
|
|
33
23
|
<h3>Imported Hueforge View</h3>
|
|
@@ -59,6 +49,9 @@ To install AutoForge, simply install the current version from PyPI:
|
|
|
59
49
|
pip install autoforge
|
|
60
50
|
```
|
|
61
51
|
|
|
52
|
+
If you have problems running the code on your gpu, please refer to the [Pytorch Homepage](https://pytorch.org/) for help. \
|
|
53
|
+
Both CUDA and ROCm are supported, but you need to install the correct version of pytorch for your system.
|
|
54
|
+
|
|
62
55
|
## Usage
|
|
63
56
|
|
|
64
57
|
The script is run from the command line and accepts several arguments. Below is an example command:
|
|
@@ -104,7 +97,7 @@ autoforge --input_image path/to/input_image.jpg --csv_file path/to/materials.csv
|
|
|
104
97
|
- `--random_seed`: Random seed for reproducibility (default: 0 (disabled) ).
|
|
105
98
|
|
|
106
99
|
### Experimental Deph Anything V2 parameters
|
|
107
|
-
I got a request to add
|
|
100
|
+
I got a request to add an initializing function that takes the original depth of the image into account when initializing. \
|
|
108
101
|
In theory this should give you a nice background/foreground separation, but in practice it can degrade the quality of the output. \
|
|
109
102
|
In addition you need the VRAM to run the depth model. \
|
|
110
103
|
I currently don't recommend it as the output is not as good as without it, but I will leave it in for now. \
|
|
@@ -151,7 +144,7 @@ The software is provided as-is and comes with no warranty or guarantee of suppor
|
|
|
151
144
|
## Acknowledgements
|
|
152
145
|
|
|
153
146
|
First and foremost:
|
|
154
|
-
- [Hueforge](https://shop.thehueforge.com/) for providing the
|
|
147
|
+
- [Hueforge](https://shop.thehueforge.com/) for providing the inspiration for this project.
|
|
155
148
|
Without it, this project would not have been possible.
|
|
156
149
|
|
|
157
150
|
AutoForge makes use of several open source libraries:
|
|
@@ -4,7 +4,7 @@ build-backend = "setuptools.build_meta"
|
|
|
4
4
|
|
|
5
5
|
[project]
|
|
6
6
|
name = "AutoForge"
|
|
7
|
-
version="1.5.
|
|
7
|
+
version="1.5.2"
|
|
8
8
|
description = "AutoForge is a Python tool for generating 3D printed layered models from an input image."
|
|
9
9
|
authors = [{ name = "Hendric Voss", email = "hvoss@techfak.uni-bielefeld.de" }]
|
|
10
10
|
readme = "README.md"
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
Metadata-Version: 2.2
|
|
2
2
|
Name: AutoForge
|
|
3
|
-
Version: 1.5.
|
|
3
|
+
Version: 1.5.2
|
|
4
4
|
Summary: AutoForge is a Python tool for generating 3D printed layered models from an input image.
|
|
5
5
|
Author-email: Hendric Voss <hvoss@techfak.uni-bielefeld.de>
|
|
6
6
|
License: CC BY-NC-SA 4.0
|
|
@@ -25,16 +25,6 @@ AutoForge is a Python tool for generating 3D printed layered models from an inpu
|
|
|
25
25
|
|
|
26
26
|
**TLDR:** It uses a picture to generate a 3D layer image that you can print with a 3d printer. Similar to [Hueforge](https://shop.thehueforge.com/), but without the manual work (and without the artistic control).
|
|
27
27
|
|
|
28
|
-
|
|
29
|
-
## Important Information
|
|
30
|
-
|
|
31
|
-
We recently switched from JAX to PyTorch, to allow for a more streamlined development process. \
|
|
32
|
-
This can have some unforeseen consequences, so please report any bugs you find.
|
|
33
|
-
|
|
34
|
-
This release should give better gpu support for more users, especially on windows. \
|
|
35
|
-
If you have problems running the code on your gpu, please refer to the [Pytorch Homepage](https://pytorch.org/) for help. \
|
|
36
|
-
Both CUDA and ROCm are supported, but you need to install the correct version of pytorch for your system.
|
|
37
|
-
|
|
38
28
|
## Example
|
|
39
29
|
All examples use only the 13 BambuLab Basic filaments, currently available in Hueforge.
|
|
40
30
|
<div style="display: flex; justify-content: center; gap: 20px;">
|
|
@@ -46,9 +36,9 @@ All examples use only the 13 BambuLab Basic filaments, currently available in Hu
|
|
|
46
36
|
</div>
|
|
47
37
|
<div style="text-align: center;">
|
|
48
38
|
<h3>Autoforge Output (75 color layers (3.0mm) + 10 gray background layers (0.4mm)) </h3>
|
|
49
|
-
<img src="https://github.com/hvoss-techfak/AutoForge/blob/main/images/lofi_discretized.
|
|
39
|
+
<img src="https://github.com/hvoss-techfak/AutoForge/blob/main/images/lofi_discretized.png" width="200" />
|
|
50
40
|
<img src="https://github.com/hvoss-techfak/AutoForge/blob/main/images/nature_discretized.jpg" width="200" />
|
|
51
|
-
<img src="https://github.com/hvoss-techfak/AutoForge/blob/main/images/cat_discretized.
|
|
41
|
+
<img src="https://github.com/hvoss-techfak/AutoForge/blob/main/images/cat_discretized.png" width="200" />
|
|
52
42
|
</div>
|
|
53
43
|
<div style="text-align: center;">
|
|
54
44
|
<h3>Imported Hueforge View</h3>
|
|
@@ -80,6 +70,9 @@ To install AutoForge, simply install the current version from PyPI:
|
|
|
80
70
|
pip install autoforge
|
|
81
71
|
```
|
|
82
72
|
|
|
73
|
+
If you have problems running the code on your gpu, please refer to the [Pytorch Homepage](https://pytorch.org/) for help. \
|
|
74
|
+
Both CUDA and ROCm are supported, but you need to install the correct version of pytorch for your system.
|
|
75
|
+
|
|
83
76
|
## Usage
|
|
84
77
|
|
|
85
78
|
The script is run from the command line and accepts several arguments. Below is an example command:
|
|
@@ -125,7 +118,7 @@ autoforge --input_image path/to/input_image.jpg --csv_file path/to/materials.csv
|
|
|
125
118
|
- `--random_seed`: Random seed for reproducibility (default: 0 (disabled) ).
|
|
126
119
|
|
|
127
120
|
### Experimental Deph Anything V2 parameters
|
|
128
|
-
I got a request to add
|
|
121
|
+
I got a request to add an initializing function that takes the original depth of the image into account when initializing. \
|
|
129
122
|
In theory this should give you a nice background/foreground separation, but in practice it can degrade the quality of the output. \
|
|
130
123
|
In addition you need the VRAM to run the depth model. \
|
|
131
124
|
I currently don't recommend it as the output is not as good as without it, but I will leave it in for now. \
|
|
@@ -172,7 +165,7 @@ The software is provided as-is and comes with no warranty or guarantee of suppor
|
|
|
172
165
|
## Acknowledgements
|
|
173
166
|
|
|
174
167
|
First and foremost:
|
|
175
|
-
- [Hueforge](https://shop.thehueforge.com/) for providing the
|
|
168
|
+
- [Hueforge](https://shop.thehueforge.com/) for providing the inspiration for this project.
|
|
176
169
|
Without it, this project would not have been possible.
|
|
177
170
|
|
|
178
171
|
AutoForge makes use of several open source libraries:
|
|
@@ -20,6 +20,16 @@ def adaptive_round(
|
|
|
20
20
|
) -> torch.Tensor:
|
|
21
21
|
"""
|
|
22
22
|
Smooth rounding based on temperature 'tau'.
|
|
23
|
+
|
|
24
|
+
Args:
|
|
25
|
+
x (torch.Tensor): The input tensor to be rounded.
|
|
26
|
+
tau (float): The current temperature parameter.
|
|
27
|
+
high_tau (float): The high threshold for the temperature.
|
|
28
|
+
low_tau (float): The low threshold for the temperature.
|
|
29
|
+
temp (float): The temperature parameter for the sigmoid function.
|
|
30
|
+
|
|
31
|
+
Returns:
|
|
32
|
+
torch.Tensor: The rounded tensor.
|
|
23
33
|
"""
|
|
24
34
|
if tau <= low_tau:
|
|
25
35
|
return torch.round(x)
|
|
@@ -40,6 +50,16 @@ def adaptive_round(
|
|
|
40
50
|
# A deterministic random generator that mimics torch.rand_like.
|
|
41
51
|
@torch.jit.script
|
|
42
52
|
def deterministic_rand_like(tensor: torch.Tensor, seed: int) -> torch.Tensor:
|
|
53
|
+
"""
|
|
54
|
+
Generate a deterministic random tensor that mimics torch.rand_like.
|
|
55
|
+
|
|
56
|
+
Args:
|
|
57
|
+
tensor (torch.Tensor): The input tensor whose shape and device will be used.
|
|
58
|
+
seed (int): The seed for the deterministic random generator.
|
|
59
|
+
|
|
60
|
+
Returns:
|
|
61
|
+
torch.Tensor: A tensor with the same shape as the input tensor, filled with deterministic random values.
|
|
62
|
+
"""
|
|
43
63
|
# Compute the total number of elements.
|
|
44
64
|
n: int = 1
|
|
45
65
|
for d in tensor.shape:
|
|
@@ -59,6 +79,18 @@ def deterministic_rand_like(tensor: torch.Tensor, seed: int) -> torch.Tensor:
|
|
|
59
79
|
def deterministic_gumbel_softmax(
|
|
60
80
|
logits: torch.Tensor, tau: float, hard: bool, rng_seed: int
|
|
61
81
|
) -> torch.Tensor:
|
|
82
|
+
"""
|
|
83
|
+
Apply the Gumbel-Softmax trick in a deterministic manner using a fixed random seed.
|
|
84
|
+
|
|
85
|
+
Args:
|
|
86
|
+
logits (torch.Tensor): The input logits tensor.
|
|
87
|
+
tau (float): The temperature parameter for the Gumbel-Softmax.
|
|
88
|
+
hard (bool): If True, the output will be one-hot encoded.
|
|
89
|
+
rng_seed (int): The seed for the deterministic random generator.
|
|
90
|
+
|
|
91
|
+
Returns:
|
|
92
|
+
torch.Tensor: The resulting tensor after applying the Gumbel-Softmax trick.
|
|
93
|
+
"""
|
|
62
94
|
eps: float = 1e-20
|
|
63
95
|
# Instead of torch.rand_like(..., generator=...), use our deterministic_rand_like.
|
|
64
96
|
U = deterministic_rand_like(logits, rng_seed)
|
|
@@ -83,218 +115,341 @@ def composite_image_cont(
|
|
|
83
115
|
tau_global: float,
|
|
84
116
|
h: float,
|
|
85
117
|
max_layers: int,
|
|
86
|
-
material_colors: torch.Tensor,
|
|
87
|
-
material_TDs: torch.Tensor,
|
|
88
|
-
background: torch.Tensor,
|
|
118
|
+
material_colors: torch.Tensor, # [n_materials, 3]
|
|
119
|
+
material_TDs: torch.Tensor, # [n_materials]
|
|
120
|
+
background: torch.Tensor, # [3]
|
|
89
121
|
) -> torch.Tensor:
|
|
90
122
|
"""
|
|
91
123
|
Continuous compositing over all pixels.
|
|
92
124
|
Uses Gumbel softmax with either hard or soft sampling depending on tau_global.
|
|
125
|
+
|
|
126
|
+
Args:
|
|
127
|
+
pixel_height_logits (torch.Tensor): Logits for pixel heights, shape [H, W].
|
|
128
|
+
global_logits (torch.Tensor): Logits for global material assignment, shape [max_layers, n_materials].
|
|
129
|
+
tau_height (float): Temperature parameter for height rounding.
|
|
130
|
+
tau_global (float): Temperature parameter for global material assignment.
|
|
131
|
+
h (float): Height of each layer.
|
|
132
|
+
max_layers (int): Maximum number of layers.
|
|
133
|
+
material_colors (torch.Tensor): Tensor of material colors, shape [n_materials, 3].
|
|
134
|
+
material_TDs (torch.Tensor): Tensor of material transmission/opacity parameters, shape [n_materials].
|
|
135
|
+
background (torch.Tensor): Background color tensor, shape [3].
|
|
136
|
+
|
|
137
|
+
Returns:
|
|
138
|
+
torch.Tensor: Composite image tensor, shape [H, W, 3].
|
|
93
139
|
"""
|
|
140
|
+
# Compute continuous and discrete layers for each pixel
|
|
94
141
|
pixel_height = (max_layers * h) * torch.sigmoid(pixel_height_logits)
|
|
95
142
|
continuous_layers = pixel_height / h
|
|
96
143
|
|
|
144
|
+
# adaptive rounding
|
|
97
145
|
adaptive_layers = adaptive_round(
|
|
98
146
|
continuous_layers, tau_height, high_tau=0.1, low_tau=0.01, temp=0.1
|
|
99
147
|
)
|
|
148
|
+
|
|
149
|
+
# "Straight-through" trick to keep gradient from adaptive rounding
|
|
100
150
|
discrete_layers_temp = torch.round(continuous_layers)
|
|
101
151
|
discrete_layers = (
|
|
102
152
|
discrete_layers_temp + (adaptive_layers - discrete_layers_temp).detach()
|
|
103
|
-
).to(torch.int32)
|
|
104
|
-
|
|
153
|
+
).to(torch.int32) # shape [H, W]
|
|
154
|
+
|
|
155
|
+
# Precompute global material assignments for each layer
|
|
156
|
+
# Use a single Gumbel Softmax call for all layers.
|
|
157
|
+
hard_flag = tau_global < 1e-3
|
|
158
|
+
# global_logits: [max_layers, n_materials]
|
|
159
|
+
p = F.gumbel_softmax(
|
|
160
|
+
global_logits, tau_global, hard=hard_flag, dim=1
|
|
161
|
+
) # [L, n_materials]
|
|
162
|
+
|
|
163
|
+
# Compute color_i and TD_i for each layer in one go.
|
|
164
|
+
# color_i -> [max_layers, 3], TD_i -> [max_layers]
|
|
165
|
+
layer_colors = p @ material_colors # [L, 3]
|
|
166
|
+
layer_TDs = p @ material_TDs # [L]
|
|
167
|
+
layer_TDs.clamp_(1e-8, 1e8)
|
|
168
|
+
|
|
169
|
+
# Prepare output and compositing variables
|
|
105
170
|
H, W = pixel_height.shape
|
|
106
171
|
comp = torch.zeros(H, W, 3, dtype=torch.float32, device=pixel_height.device)
|
|
107
172
|
remaining = torch.ones(H, W, dtype=torch.float32, device=pixel_height.device)
|
|
108
173
|
|
|
109
|
-
#
|
|
174
|
+
# Opacity function parameters
|
|
110
175
|
o = -1.2416557e-02
|
|
111
176
|
A = 9.6407950e-01
|
|
112
177
|
k = 3.4103447e01
|
|
113
178
|
b = -4.1554203e00
|
|
114
179
|
|
|
180
|
+
# Composite from top layer (max_layers-1) down to 0
|
|
115
181
|
for i in range(max_layers):
|
|
116
182
|
layer_idx = max_layers - 1 - i
|
|
117
|
-
|
|
118
|
-
|
|
119
|
-
|
|
120
|
-
# continuous mode: use hard gumbel if tau_global is very low
|
|
121
|
-
if tau_global < 1e-3:
|
|
122
|
-
p_i = F.gumbel_softmax(global_logits[layer_idx], tau_global, hard=True)
|
|
123
|
-
else:
|
|
124
|
-
p_i = F.gumbel_softmax(global_logits[layer_idx], tau_global, hard=False)
|
|
183
|
+
# Mask of which pixels actually print this layer:
|
|
184
|
+
p_print = (discrete_layers > layer_idx).to(dtype=comp.dtype)
|
|
125
185
|
|
|
126
|
-
|
|
127
|
-
|
|
128
|
-
TD_i = torch.clamp(TD_i, 1e-8, 1e8)
|
|
186
|
+
# Effective thickness for those pixels
|
|
187
|
+
eff_thick = p_print * h
|
|
129
188
|
|
|
189
|
+
# Opacity based on thickness and material TD
|
|
190
|
+
TD_i = layer_TDs[layer_idx]
|
|
130
191
|
opac = o + (A * torch.log1p(k * (eff_thick / TD_i)) + b * (eff_thick / TD_i))
|
|
131
192
|
opac = torch.clamp(opac, 0.0, 1.0)
|
|
132
193
|
|
|
133
|
-
|
|
134
|
-
|
|
194
|
+
# Add contribution to comp
|
|
195
|
+
color_i = layer_colors[layer_idx] # shape [3]
|
|
196
|
+
# We need to broadcast opac: [H, W] -> [H, W, 1]
|
|
197
|
+
comp = comp + (remaining * opac).unsqueeze(-1) * color_i
|
|
198
|
+
|
|
199
|
+
# Update the remaining factor
|
|
200
|
+
remaining = remaining * (1.0 - opac)
|
|
135
201
|
|
|
202
|
+
# Add background contribution
|
|
136
203
|
comp = comp + remaining.unsqueeze(-1) * background
|
|
204
|
+
|
|
205
|
+
# 6. Scale to [0, 255] range
|
|
137
206
|
return comp * 255.0
|
|
138
207
|
|
|
139
208
|
|
|
140
209
|
@torch.jit.script
|
|
141
210
|
def composite_image_disc(
|
|
142
|
-
pixel_height_logits: torch.Tensor,
|
|
143
|
-
global_logits: torch.Tensor,
|
|
211
|
+
pixel_height_logits: torch.Tensor, # [H,W]
|
|
212
|
+
global_logits: torch.Tensor, # [max_layers, n_materials]
|
|
144
213
|
tau_height: float,
|
|
145
214
|
tau_global: float,
|
|
146
215
|
h: float,
|
|
147
216
|
max_layers: int,
|
|
148
|
-
material_colors: torch.Tensor,
|
|
149
|
-
material_TDs: torch.Tensor,
|
|
150
|
-
background: torch.Tensor,
|
|
217
|
+
material_colors: torch.Tensor, # [n_materials, 3]
|
|
218
|
+
material_TDs: torch.Tensor, # [n_materials]
|
|
219
|
+
background: torch.Tensor, # [3]
|
|
151
220
|
rng_seed: int = -1,
|
|
152
221
|
) -> torch.Tensor:
|
|
153
222
|
"""
|
|
154
|
-
|
|
155
|
-
|
|
156
|
-
|
|
157
|
-
|
|
158
|
-
|
|
159
|
-
|
|
223
|
+
Perform discrete compositing over all pixels.
|
|
224
|
+
|
|
225
|
+
Args:
|
|
226
|
+
pixel_height_logits (torch.Tensor): Logits for pixel heights, shape [H, W].
|
|
227
|
+
global_logits (torch.Tensor): Logits for global material assignment, shape [max_layers, n_materials].
|
|
228
|
+
tau_height (float): Temperature parameter for height rounding.
|
|
229
|
+
tau_global (float): Temperature parameter for global material assignment.
|
|
230
|
+
h (float): Height of each layer.
|
|
231
|
+
max_layers (int): Maximum number of layers.
|
|
232
|
+
material_colors (torch.Tensor): Tensor of material colors, shape [n_materials, 3].
|
|
233
|
+
material_TDs (torch.Tensor): Tensor of material transmission/opacity parameters, shape [n_materials].
|
|
234
|
+
background (torch.Tensor): Background color tensor, shape [3].
|
|
235
|
+
rng_seed (int, optional): Random seed for deterministic sampling. Defaults to -1.
|
|
236
|
+
|
|
237
|
+
Returns:
|
|
238
|
+
torch.Tensor: Composite image tensor, shape [H, W, 3].
|
|
160
239
|
"""
|
|
161
|
-
|
|
240
|
+
|
|
241
|
+
# -------------------------------------------------------------------------
|
|
242
|
+
# 1) Compute discrete per-pixel layer counts (discrete_layers).
|
|
243
|
+
# -------------------------------------------------------------------------
|
|
244
|
+
# pixel_height ~ [0, max_layers*h]
|
|
162
245
|
pixel_height = (max_layers * h) * torch.sigmoid(pixel_height_logits)
|
|
246
|
+
|
|
247
|
+
# continuous_layers ~ [0, max_layers]
|
|
163
248
|
continuous_layers = pixel_height / h
|
|
164
249
|
|
|
250
|
+
# Use your "adaptive rounding" trick if desired:
|
|
165
251
|
adaptive_layers = adaptive_round(
|
|
166
252
|
continuous_layers, tau_height, high_tau=0.1, low_tau=0.01, temp=0.1
|
|
167
253
|
)
|
|
168
254
|
discrete_layers_temp = torch.round(continuous_layers)
|
|
169
255
|
discrete_layers = (
|
|
170
256
|
discrete_layers_temp + (adaptive_layers - discrete_layers_temp).detach()
|
|
171
|
-
).to(torch.int32)
|
|
172
|
-
|
|
173
|
-
|
|
174
|
-
|
|
175
|
-
|
|
176
|
-
|
|
177
|
-
|
|
178
|
-
|
|
179
|
-
|
|
180
|
-
|
|
181
|
-
|
|
182
|
-
# Opacity function parameters.
|
|
183
|
-
o = -1.2416557e-02
|
|
184
|
-
A = 9.6407950e-01
|
|
185
|
-
k = 3.4103447e01
|
|
186
|
-
b = -4.1554203e00
|
|
187
|
-
|
|
188
|
-
for i in range(max_layers):
|
|
189
|
-
layer_idx = max_layers - 1 - i
|
|
190
|
-
p_print = discrete_layers > layer_idx # Boolean mask [H,W]
|
|
191
|
-
eff_thick = (
|
|
192
|
-
p_print.to(torch.float32) * h
|
|
193
|
-
) # effective thickness for printed pixels
|
|
194
|
-
|
|
195
|
-
# Determine material for this layer using discrete (one-hot) gumbel softmax.
|
|
196
|
-
if rng_seed >= 0:
|
|
257
|
+
).to(torch.int32) # [H,W]
|
|
258
|
+
|
|
259
|
+
# -------------------------------------------------------------------------
|
|
260
|
+
# 2) Pick a single global material per layer, either deterministically
|
|
261
|
+
# or via gumbel softmax, as in original code.
|
|
262
|
+
# -------------------------------------------------------------------------
|
|
263
|
+
if rng_seed >= 0:
|
|
264
|
+
# Deterministic sampling for each layer
|
|
265
|
+
new_mats_list = []
|
|
266
|
+
for layer_idx in range(max_layers):
|
|
197
267
|
p_i = deterministic_gumbel_softmax(
|
|
198
|
-
global_logits[layer_idx],
|
|
199
|
-
|
|
200
|
-
|
|
201
|
-
|
|
202
|
-
# Cast new_mat to int32 so it matches cur_mat.
|
|
203
|
-
new_mat = torch.argmax(p_i, 0).to(torch.int32)
|
|
204
|
-
|
|
205
|
-
# 1. For pixels where no current material is set, start a new segment.
|
|
206
|
-
mask_new = p_print & (cur_mat == -1)
|
|
207
|
-
if mask_new.any():
|
|
208
|
-
cur_mat[mask_new] = new_mat
|
|
209
|
-
acc_thick[mask_new] = eff_thick[mask_new]
|
|
210
|
-
|
|
211
|
-
# 2. For pixels where the current segment has the same material, accumulate thickness.
|
|
212
|
-
mask_same = p_print & (cur_mat == new_mat)
|
|
213
|
-
if mask_same.any():
|
|
214
|
-
acc_thick[mask_same] = acc_thick[mask_same] + eff_thick[mask_same]
|
|
215
|
-
|
|
216
|
-
# 3. For pixels where the current segment has a different material,
|
|
217
|
-
# composite the accumulated segment and start a new one.
|
|
218
|
-
mask_diff = p_print & (cur_mat != -1) & (cur_mat != new_mat)
|
|
219
|
-
if mask_diff.any():
|
|
220
|
-
# Convert indices to int64 for index_select.
|
|
221
|
-
indices_diff = cur_mat[mask_diff].to(torch.int64)
|
|
222
|
-
td_vals = material_TDs.index_select(0, indices_diff)
|
|
223
|
-
col_vals = material_colors.index_select(0, indices_diff)
|
|
224
|
-
opac_vals = o + (
|
|
225
|
-
A * torch.log1p(k * (acc_thick[mask_diff] / td_vals))
|
|
226
|
-
+ b * (acc_thick[mask_diff] / td_vals)
|
|
227
|
-
)
|
|
228
|
-
opac_vals = torch.clamp(opac_vals, 0.0, 1.0)
|
|
229
|
-
comp[mask_diff] = comp[mask_diff] + (
|
|
230
|
-
(remaining[mask_diff] * opac_vals).unsqueeze(-1) * col_vals
|
|
231
|
-
)
|
|
232
|
-
remaining[mask_diff] = remaining[mask_diff] * (1 - opac_vals)
|
|
233
|
-
cur_mat[mask_diff] = new_mat
|
|
234
|
-
acc_thick[mask_diff] = eff_thick[mask_diff]
|
|
235
|
-
|
|
236
|
-
# 4. For pixels where this layer is not printed but we have a pending segment,
|
|
237
|
-
# composite that segment and reset.
|
|
238
|
-
mask_not_printed = (~p_print) & (cur_mat != -1)
|
|
239
|
-
if mask_not_printed.any():
|
|
240
|
-
indices_np = cur_mat[mask_not_printed].to(torch.int64)
|
|
241
|
-
td_vals = material_TDs.index_select(0, indices_np)
|
|
242
|
-
col_vals = material_colors.index_select(0, indices_np)
|
|
243
|
-
opac_vals = o + (
|
|
244
|
-
A * torch.log1p(k * (acc_thick[mask_not_printed] / td_vals))
|
|
245
|
-
+ b * (acc_thick[mask_not_printed] / td_vals)
|
|
268
|
+
global_logits[layer_idx],
|
|
269
|
+
tau_global,
|
|
270
|
+
hard=True,
|
|
271
|
+
rng_seed=(rng_seed + layer_idx),
|
|
246
272
|
)
|
|
247
|
-
|
|
248
|
-
|
|
249
|
-
|
|
250
|
-
|
|
251
|
-
|
|
252
|
-
|
|
253
|
-
|
|
254
|
-
|
|
255
|
-
# After the loop, composite any remaining accumulated segment.
|
|
256
|
-
mask_remain = cur_mat != -1
|
|
257
|
-
if mask_remain.any():
|
|
258
|
-
indices_rem = cur_mat[mask_remain].to(torch.int64)
|
|
259
|
-
td_vals = material_TDs.index_select(0, indices_rem)
|
|
260
|
-
col_vals = material_colors.index_select(0, indices_rem)
|
|
261
|
-
opac_vals = o + (
|
|
262
|
-
A * torch.log1p(k * (acc_thick[mask_remain] / td_vals))
|
|
263
|
-
+ b * (acc_thick[mask_remain] / td_vals)
|
|
264
|
-
)
|
|
265
|
-
opac_vals = torch.clamp(opac_vals, 0.0, 1.0)
|
|
266
|
-
comp[mask_remain] = comp[mask_remain] + (
|
|
267
|
-
(remaining[mask_remain] * opac_vals).unsqueeze(-1) * col_vals
|
|
268
|
-
)
|
|
269
|
-
remaining[mask_remain] = remaining[mask_remain] * (1 - opac_vals)
|
|
270
|
-
cur_mat[mask_remain] = -1
|
|
271
|
-
acc_thick[mask_remain] = 0.0
|
|
273
|
+
mat_i = torch.argmax(p_i, dim=0).to(torch.int32)
|
|
274
|
+
new_mats_list.append(mat_i)
|
|
275
|
+
new_mats = torch.stack(new_mats_list, dim=0) # [max_layers]
|
|
276
|
+
else:
|
|
277
|
+
# Standard (random) Gumbel softmax
|
|
278
|
+
p_all = F.gumbel_softmax(global_logits, tau_global, hard=True, dim=1) # [L, M]
|
|
279
|
+
new_mats = torch.argmax(p_all, dim=1).to(torch.int32) # [max_layers]
|
|
272
280
|
|
|
281
|
+
H, W = pixel_height.shape
|
|
282
|
+
device = pixel_height.device
|
|
283
|
+
|
|
284
|
+
comp = torch.zeros(H, W, 3, dtype=torch.float32, device=device)
|
|
285
|
+
remaining = torch.ones(H, W, dtype=torch.float32, device=device)
|
|
286
|
+
|
|
287
|
+
# Current material index for each pixel, or -1 for none
|
|
288
|
+
cur_mat = -torch.ones((H, W), dtype=torch.int32, device=device)
|
|
289
|
+
|
|
290
|
+
# Accumulated thickness for the current segment
|
|
291
|
+
acc_thick = torch.zeros((H, W), dtype=torch.float32, device=device)
|
|
292
|
+
|
|
293
|
+
# Opacity function parameters
|
|
294
|
+
o = 0.10868816
|
|
295
|
+
A = 0.3077416
|
|
296
|
+
k = 76.928215
|
|
297
|
+
b = 2.2291653
|
|
298
|
+
|
|
299
|
+
# Main compositing loop: top to bottom
|
|
300
|
+
for layer_idx in range(max_layers - 1, -1, -1):
|
|
301
|
+
# layer_mat is the global material chosen for this layer (int32 scalar).
|
|
302
|
+
layer_mat = new_mats[layer_idx] # shape []
|
|
303
|
+
|
|
304
|
+
# Which pixels actually print on this layer?
|
|
305
|
+
# p_print = (discrete_layers > layer_idx)
|
|
306
|
+
p_print = discrete_layers.gt(layer_idx) # bool
|
|
307
|
+
|
|
308
|
+
# ---------------------------------------------------------------------
|
|
309
|
+
# (A) "Finish" any existing segments that are now 'done'.
|
|
310
|
+
#
|
|
311
|
+
# A segment is done if:
|
|
312
|
+
# 1) cur_mat != -1, i.e. the pixel had an ongoing segment
|
|
313
|
+
# 2) EITHER
|
|
314
|
+
# - the pixel does not print now (~p_print),
|
|
315
|
+
# - OR the new layer material differs (cur_mat != layer_mat).
|
|
316
|
+
# ---------------------------------------------------------------------
|
|
317
|
+
mask_done = (cur_mat.ne(-1)) & ((~p_print) | (cur_mat.ne(layer_mat)))
|
|
318
|
+
|
|
319
|
+
# Convert to float for multiplications
|
|
320
|
+
mask_done_f = mask_done.to(torch.float32)
|
|
321
|
+
|
|
322
|
+
# Gather thickness densities & colors for the old segment
|
|
323
|
+
# We'll clamp cur_mat so -1 becomes 0 (doesn't matter since we multiply by 0).
|
|
324
|
+
old_inds_clamped = torch.clamp(cur_mat, min=0)
|
|
325
|
+
td_vals = material_TDs[old_inds_clamped] # [H, W]
|
|
326
|
+
col_vals = material_colors[old_inds_clamped] # [H, W, 3]
|
|
327
|
+
|
|
328
|
+
# Compute alpha from accumulated thickness
|
|
329
|
+
thick_ratio = acc_thick / td_vals
|
|
330
|
+
opac_vals = o + (A * torch.log1p(k * thick_ratio) + b * thick_ratio)
|
|
331
|
+
opac_vals = torch.clamp(opac_vals, 0.0, 1.0) # [H, W]
|
|
332
|
+
|
|
333
|
+
# Compositing the old segment:
|
|
334
|
+
# comp += mask_done_f * remaining * opac_vals * col_vals
|
|
335
|
+
# remaining *= (1 - mask_done_f * opac_vals) ...
|
|
336
|
+
# but we have to broadcast for color:
|
|
337
|
+
comp_add = (mask_done_f * remaining * opac_vals).unsqueeze(-1) * col_vals
|
|
338
|
+
comp = comp + comp_add
|
|
339
|
+
remaining = remaining - (mask_done_f * remaining * opac_vals)
|
|
340
|
+
|
|
341
|
+
# Reset old segment where mask_done is True
|
|
342
|
+
# cur_mat = -1, acc_thick = 0
|
|
343
|
+
# We'll do it by `torch.where(mask, val_if_true, val_if_false)`
|
|
344
|
+
cur_mat = torch.where(mask_done, torch.full_like(cur_mat, -1), cur_mat)
|
|
345
|
+
acc_thick = torch.where(mask_done, torch.zeros_like(acc_thick), acc_thick)
|
|
346
|
+
|
|
347
|
+
# ---------------------------------------------------------------------
|
|
348
|
+
# (B) For pixels that print this layer:
|
|
349
|
+
# - Start a new segment if cur_mat == -1
|
|
350
|
+
# - Accumulate thickness if cur_mat == layer_mat
|
|
351
|
+
# ---------------------------------------------------------------------
|
|
352
|
+
eff_thick = p_print.to(torch.float32) * h
|
|
353
|
+
|
|
354
|
+
# (B1) Start new segment where cur_mat == -1
|
|
355
|
+
mask_new = p_print & (cur_mat.eq(-1))
|
|
356
|
+
mask_new_f = mask_new.to(torch.float32)
|
|
357
|
+
|
|
358
|
+
# Set cur_mat to layer_mat if mask_new is True
|
|
359
|
+
# (layer_mat is shape [], so it will broadcast)
|
|
360
|
+
cur_mat = torch.where(mask_new, layer_mat, cur_mat)
|
|
361
|
+
|
|
362
|
+
# We add thickness:
|
|
363
|
+
acc_thick = acc_thick + mask_new_f * eff_thick
|
|
364
|
+
|
|
365
|
+
# (B2) Accumulate thickness where cur_mat == layer_mat
|
|
366
|
+
# We do this in a second mask to avoid confusion, but you can combine.
|
|
367
|
+
mask_same = p_print & (cur_mat.eq(layer_mat))
|
|
368
|
+
acc_thick = acc_thick + (mask_same.to(torch.float32) * eff_thick)
|
|
369
|
+
|
|
370
|
+
# -------------------------------------------------------------------------
|
|
371
|
+
# 5) After the loop, composite any remaining segments (cur_mat != -1).
|
|
372
|
+
# -------------------------------------------------------------------------
|
|
373
|
+
mask_remain = cur_mat.ne(-1)
|
|
374
|
+
mask_remain_f = mask_remain.to(torch.float32)
|
|
375
|
+
|
|
376
|
+
old_inds_clamped = torch.clamp(cur_mat, min=0)
|
|
377
|
+
td_vals = material_TDs[old_inds_clamped]
|
|
378
|
+
col_vals = material_colors[old_inds_clamped]
|
|
379
|
+
|
|
380
|
+
thick_ratio = acc_thick / td_vals
|
|
381
|
+
opac_vals = o + (A * torch.log1p(k * thick_ratio) + b * thick_ratio)
|
|
382
|
+
opac_vals = torch.clamp(opac_vals, 0.0, 1.0)
|
|
383
|
+
|
|
384
|
+
comp_add = (mask_remain_f * remaining * opac_vals).unsqueeze(-1) * col_vals
|
|
385
|
+
comp = comp + comp_add
|
|
386
|
+
remaining = remaining - (mask_remain_f * remaining * opac_vals)
|
|
387
|
+
|
|
388
|
+
# -------------------------------------------------------------------------
|
|
389
|
+
# 6) Composite background
|
|
390
|
+
# -------------------------------------------------------------------------
|
|
273
391
|
comp = comp + remaining.unsqueeze(-1) * background
|
|
274
392
|
return comp * 255.0
|
|
275
393
|
|
|
276
394
|
|
|
277
395
|
@torch.jit.script
|
|
278
|
-
def
|
|
279
|
-
|
|
396
|
+
def composite_image_combined(
|
|
397
|
+
pixel_height_logits: torch.Tensor, # [H,W]
|
|
398
|
+
global_logits: torch.Tensor, # [max_layers, n_materials]
|
|
399
|
+
tau_height: float,
|
|
400
|
+
tau_global: float,
|
|
401
|
+
h: float,
|
|
402
|
+
max_layers: int,
|
|
403
|
+
material_colors: torch.Tensor, # [n_materials, 3]
|
|
404
|
+
material_TDs: torch.Tensor, # [n_materials]
|
|
405
|
+
background: torch.Tensor, # [3]
|
|
406
|
+
rng_seed: int = -1,
|
|
280
407
|
) -> torch.Tensor:
|
|
281
408
|
"""
|
|
282
|
-
|
|
409
|
+
Combine continuous and discrete compositing over all pixels.
|
|
410
|
+
|
|
411
|
+
Args:
|
|
412
|
+
pixel_height_logits (torch.Tensor): Logits for pixel heights, shape [H, W].
|
|
413
|
+
global_logits (torch.Tensor): Logits for global material assignment, shape [max_layers, n_materials].
|
|
414
|
+
tau_height (float): Temperature parameter for height rounding.
|
|
415
|
+
tau_global (float): Temperature parameter for global material assignment.
|
|
416
|
+
h (float): Height of each layer.
|
|
417
|
+
max_layers (int): Maximum number of layers.
|
|
418
|
+
material_colors (torch.Tensor): Tensor of material colors, shape [n_materials, 3].
|
|
419
|
+
material_TDs (torch.Tensor): Tensor of material transmission/opacity parameters, shape [n_materials].
|
|
420
|
+
background (torch.Tensor): Background color tensor, shape [3].
|
|
421
|
+
rng_seed (int, optional): Random seed for deterministic sampling. Defaults to -1.
|
|
422
|
+
|
|
423
|
+
Returns:
|
|
424
|
+
torch.Tensor: Composite image tensor, shape [H, W, 3].
|
|
283
425
|
"""
|
|
284
|
-
|
|
285
|
-
|
|
286
|
-
|
|
287
|
-
|
|
288
|
-
|
|
289
|
-
|
|
290
|
-
|
|
426
|
+
cont = composite_image_cont(
|
|
427
|
+
pixel_height_logits,
|
|
428
|
+
global_logits,
|
|
429
|
+
tau_height,
|
|
430
|
+
tau_global,
|
|
431
|
+
h,
|
|
432
|
+
max_layers,
|
|
433
|
+
material_colors,
|
|
434
|
+
material_TDs,
|
|
435
|
+
background,
|
|
436
|
+
)
|
|
437
|
+
if tau_global < 1.0:
|
|
438
|
+
disc = composite_image_disc(
|
|
439
|
+
pixel_height_logits,
|
|
440
|
+
global_logits,
|
|
441
|
+
tau_height,
|
|
442
|
+
tau_global,
|
|
443
|
+
h,
|
|
444
|
+
max_layers,
|
|
445
|
+
material_colors,
|
|
446
|
+
material_TDs,
|
|
447
|
+
background,
|
|
448
|
+
rng_seed,
|
|
449
|
+
)
|
|
450
|
+
return cont * tau_global + disc * (1 - tau_global)
|
|
291
451
|
else:
|
|
292
|
-
|
|
293
|
-
hard_round = torch.round(x)
|
|
294
|
-
floor_val = torch.floor(x)
|
|
295
|
-
diff = x - floor_val
|
|
296
|
-
soft_round = floor_val + torch.sigmoid((diff - 0.5) / temp)
|
|
297
|
-
return ratio * soft_round + (1 - ratio) * hard_round
|
|
452
|
+
return cont
|
|
298
453
|
|
|
299
454
|
|
|
300
455
|
def discretize_solution(
|
|
@@ -302,6 +457,18 @@ def discretize_solution(
|
|
|
302
457
|
):
|
|
303
458
|
"""
|
|
304
459
|
Convert continuous logs to discrete layer counts and discrete color IDs.
|
|
460
|
+
|
|
461
|
+
Args:
|
|
462
|
+
params (dict): Dictionary containing the parameters 'pixel_height_logits' and 'global_logits'.
|
|
463
|
+
tau_global (float): Temperature parameter for global material assignment.
|
|
464
|
+
h (float): Height of each layer.
|
|
465
|
+
max_layers (int): Maximum number of layers.
|
|
466
|
+
rng_seed (int, optional): Random seed for deterministic sampling. Defaults to -1.
|
|
467
|
+
|
|
468
|
+
Returns:
|
|
469
|
+
tuple: A tuple containing:
|
|
470
|
+
- torch.Tensor: Discrete global material assignments, shape [max_layers].
|
|
471
|
+
- torch.Tensor: Discrete height image, shape [H, W].
|
|
305
472
|
"""
|
|
306
473
|
pixel_height_logits = params["pixel_height_logits"]
|
|
307
474
|
global_logits = params["global_logits"]
|
|
@@ -328,10 +495,10 @@ def initialize_pixel_height_logits(target):
|
|
|
328
495
|
Uses the formula: L = 0.299*R + 0.587*G + 0.114*B.
|
|
329
496
|
|
|
330
497
|
Args:
|
|
331
|
-
target (
|
|
498
|
+
target (np.ndarray): The target image array with shape (H, W, 3).
|
|
332
499
|
|
|
333
500
|
Returns:
|
|
334
|
-
|
|
501
|
+
np.ndarray: The initialized pixel height logits.
|
|
335
502
|
"""
|
|
336
503
|
# Compute normalized luminance in [0,1]
|
|
337
504
|
normalized_lum = (
|
|
@@ -711,6 +878,10 @@ def init_height_map(target, max_layers, h, eps=1e-6, random_seed=None):
|
|
|
711
878
|
|
|
712
879
|
Args:
|
|
713
880
|
target (jnp.ndarray): The target image array with shape (H, W, 3).
|
|
881
|
+
max_layers (int): Maximum number of layers.
|
|
882
|
+
h (float): Height of each layer.
|
|
883
|
+
eps (float): Small constant to avoid division by zero.
|
|
884
|
+
random_seed (int): Random seed for reproducibility.
|
|
714
885
|
|
|
715
886
|
Returns:
|
|
716
887
|
jnp.ndarray: The initialized pixel height logits.
|
|
@@ -20,7 +20,6 @@ from autoforge.Helper.OutputHelper import (
|
|
|
20
20
|
generate_project_file,
|
|
21
21
|
)
|
|
22
22
|
from autoforge.Helper.PruningHelper import prune_redundant_layers
|
|
23
|
-
from autoforge.Loss.PerceptionLoss import MultiLayerVGGPerceptualLoss
|
|
24
23
|
from autoforge.Modules.Optimizer import FilamentOptimizer
|
|
25
24
|
|
|
26
25
|
|
|
@@ -282,7 +281,8 @@ def main():
|
|
|
282
281
|
) # shape (H', W') cause of weird opencv quirk
|
|
283
282
|
|
|
284
283
|
# VGG Perceptual Loss
|
|
285
|
-
|
|
284
|
+
# We currently disable this as it is not used in the optimization.
|
|
285
|
+
perception_loss_module = None # MultiLayerVGGPerceptualLoss().to(device).eval()
|
|
286
286
|
|
|
287
287
|
# Create an optimizer instance
|
|
288
288
|
optimizer = FilamentOptimizer(
|
|
@@ -300,7 +300,7 @@ def main():
|
|
|
300
300
|
print("Starting optimization...")
|
|
301
301
|
tbar = tqdm(range(args.iterations))
|
|
302
302
|
for i in tbar:
|
|
303
|
-
loss_val = optimizer.step(record_best=i %
|
|
303
|
+
loss_val = optimizer.step(record_best=i % 10 == 0)
|
|
304
304
|
|
|
305
305
|
optimizer.visualize(interval=25)
|
|
306
306
|
optimizer.log_to_tensorboard(interval=100)
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|