autoforge 1.4.2__tar.gz → 1.5.1__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.4.2 → autoforge-1.5.1}/PKG-INFO +8 -15
- {autoforge-1.4.2 → autoforge-1.5.1}/README.md +7 -14
- {autoforge-1.4.2 → autoforge-1.5.1}/pyproject.toml +1 -1
- {autoforge-1.4.2 → autoforge-1.5.1}/src/AutoForge.egg-info/PKG-INFO +8 -15
- {autoforge-1.4.2 → autoforge-1.5.1}/src/AutoForge.egg-info/SOURCES.txt +1 -0
- autoforge-1.5.1/src/autoforge/Helper/CAdamW.py +113 -0
- {autoforge-1.4.2 → autoforge-1.5.1}/src/autoforge/Helper/OptimizerHelper.py +168 -36
- {autoforge-1.4.2 → autoforge-1.5.1}/src/autoforge/Helper/OutputHelper.py +19 -11
- {autoforge-1.4.2 → autoforge-1.5.1}/src/autoforge/Helper/PruningHelper.py +5 -9
- {autoforge-1.4.2 → autoforge-1.5.1}/src/autoforge/Loss/LossFunctions.py +2 -3
- {autoforge-1.4.2 → autoforge-1.5.1}/src/autoforge/Modules/Optimizer.py +32 -22
- {autoforge-1.4.2 → autoforge-1.5.1}/src/autoforge/auto_forge.py +5 -3
- {autoforge-1.4.2 → autoforge-1.5.1}/setup.cfg +0 -0
- {autoforge-1.4.2 → autoforge-1.5.1}/src/AutoForge.egg-info/dependency_links.txt +0 -0
- {autoforge-1.4.2 → autoforge-1.5.1}/src/AutoForge.egg-info/entry_points.txt +0 -0
- {autoforge-1.4.2 → autoforge-1.5.1}/src/AutoForge.egg-info/requires.txt +0 -0
- {autoforge-1.4.2 → autoforge-1.5.1}/src/AutoForge.egg-info/top_level.txt +0 -0
- {autoforge-1.4.2 → autoforge-1.5.1}/src/autoforge/Gui/Views/__init__.py +0 -0
- {autoforge-1.4.2 → autoforge-1.5.1}/src/autoforge/Gui/Views/backend_database.py +0 -0
- {autoforge-1.4.2 → autoforge-1.5.1}/src/autoforge/Gui/Views/filament_editor.py +0 -0
- {autoforge-1.4.2 → autoforge-1.5.1}/src/autoforge/Gui/__init__.py +0 -0
- {autoforge-1.4.2 → autoforge-1.5.1}/src/autoforge/Gui/app.py +0 -0
- {autoforge-1.4.2 → autoforge-1.5.1}/src/autoforge/Gui/main.py +0 -0
- {autoforge-1.4.2 → autoforge-1.5.1}/src/autoforge/Helper/FilamentHelper.py +0 -0
- {autoforge-1.4.2 → autoforge-1.5.1}/src/autoforge/Helper/ImageHelper.py +0 -0
- {autoforge-1.4.2 → autoforge-1.5.1}/src/autoforge/Helper/__init__.py +0 -0
- {autoforge-1.4.2 → autoforge-1.5.1}/src/autoforge/Helper/filamentcolors_library.py +0 -0
- {autoforge-1.4.2 → autoforge-1.5.1}/src/autoforge/Loss/PerceptionLoss.py +0 -0
- {autoforge-1.4.2 → autoforge-1.5.1}/src/autoforge/Loss/__init__.py +0 -0
- {autoforge-1.4.2 → autoforge-1.5.1}/src/autoforge/Misc/__init__.py +0 -0
- {autoforge-1.4.2 → autoforge-1.5.1}/src/autoforge/Misc/color_fit_all.py +0 -0
- {autoforge-1.4.2 → autoforge-1.5.1}/src/autoforge/Modules/__init__.py +0 -0
- {autoforge-1.4.2 → autoforge-1.5.1}/src/autoforge/__init__.py +0 -0
- {autoforge-1.4.2 → autoforge-1.5.1}/src/autoforge/__main__.py +0 -0
- {autoforge-1.4.2 → autoforge-1.5.1}/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.
|
|
3
|
+
Version: 1.5.1
|
|
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.
|
|
7
|
+
version="1.5.1"
|
|
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.
|
|
3
|
+
Version: 1.5.1
|
|
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:
|
|
@@ -16,6 +16,7 @@ src/autoforge/Gui/main.py
|
|
|
16
16
|
src/autoforge/Gui/Views/__init__.py
|
|
17
17
|
src/autoforge/Gui/Views/backend_database.py
|
|
18
18
|
src/autoforge/Gui/Views/filament_editor.py
|
|
19
|
+
src/autoforge/Helper/CAdamW.py
|
|
19
20
|
src/autoforge/Helper/FilamentHelper.py
|
|
20
21
|
src/autoforge/Helper/ImageHelper.py
|
|
21
22
|
src/autoforge/Helper/OptimizerHelper.py
|
|
@@ -0,0 +1,113 @@
|
|
|
1
|
+
# Taken from https://github.com/kyleliang919/C-Optim/blob/main/c_adamw.py
|
|
2
|
+
# copy dependencies from transformers/optimization.py
|
|
3
|
+
import math
|
|
4
|
+
import warnings
|
|
5
|
+
from typing import Callable, Iterable, Tuple
|
|
6
|
+
|
|
7
|
+
import torch
|
|
8
|
+
from torch import nn
|
|
9
|
+
from torch.optim import Optimizer
|
|
10
|
+
|
|
11
|
+
|
|
12
|
+
class CAdamW(Optimizer):
|
|
13
|
+
"""
|
|
14
|
+
Implements Adam algorithm with weight decay fix as introduced in [Decoupled Weight Decay
|
|
15
|
+
Regularization](https://arxiv.org/abs/1711.05101).
|
|
16
|
+
|
|
17
|
+
Parameters:
|
|
18
|
+
params (`Iterable[nn.parameter.Parameter]`):
|
|
19
|
+
Iterable of parameters to optimize or dictionaries defining parameter groups.
|
|
20
|
+
lr (`float`, *optional*, defaults to 0.001):
|
|
21
|
+
The learning rate to use.
|
|
22
|
+
betas (`Tuple[float,float]`, *optional*, defaults to `(0.9, 0.999)`):
|
|
23
|
+
Adam's betas parameters (b1, b2).
|
|
24
|
+
eps (`float`, *optional*, defaults to 1e-06):
|
|
25
|
+
Adam's epsilon for numerical stability.
|
|
26
|
+
weight_decay (`float`, *optional*, defaults to 0.0):
|
|
27
|
+
Decoupled weight decay to apply.
|
|
28
|
+
correct_bias (`bool`, *optional*, defaults to `True`):
|
|
29
|
+
Whether or not to correct bias in Adam (for instance, in Bert TF repository they use `False`).
|
|
30
|
+
no_deprecation_warning (`bool`, *optional*, defaults to `False`):
|
|
31
|
+
A flag used to disable the deprecation warning (set to `True` to disable the warning).
|
|
32
|
+
"""
|
|
33
|
+
|
|
34
|
+
def __init__(
|
|
35
|
+
self,
|
|
36
|
+
params: Iterable[nn.parameter.Parameter],
|
|
37
|
+
lr: float = 1e-3,
|
|
38
|
+
betas: Tuple[float, float] = (0.9, 0.999),
|
|
39
|
+
eps: float = 1e-6,
|
|
40
|
+
weight_decay: float = 0.0,
|
|
41
|
+
correct_bias: bool = True,
|
|
42
|
+
):
|
|
43
|
+
|
|
44
|
+
if lr < 0.0:
|
|
45
|
+
raise ValueError(f"Invalid learning rate: {lr} - should be >= 0.0")
|
|
46
|
+
if not 0.0 <= betas[0] < 1.0:
|
|
47
|
+
raise ValueError(f"Invalid beta parameter: {betas[0]} - should be in [0.0, 1.0)")
|
|
48
|
+
if not 0.0 <= betas[1] < 1.0:
|
|
49
|
+
raise ValueError(f"Invalid beta parameter: {betas[1]} - should be in [0.0, 1.0)")
|
|
50
|
+
if not 0.0 <= eps:
|
|
51
|
+
raise ValueError(f"Invalid epsilon value: {eps} - should be >= 0.0")
|
|
52
|
+
defaults = {"lr": lr, "betas": betas, "eps": eps, "weight_decay": weight_decay, "correct_bias": correct_bias}
|
|
53
|
+
super().__init__(params, defaults)
|
|
54
|
+
self.init_lr = lr
|
|
55
|
+
|
|
56
|
+
@torch.no_grad()
|
|
57
|
+
def step(self, closure: Callable = None):
|
|
58
|
+
"""
|
|
59
|
+
Performs a single optimization step.
|
|
60
|
+
|
|
61
|
+
Arguments:
|
|
62
|
+
closure (`Callable`, *optional*): A closure that reevaluates the model and returns the loss.
|
|
63
|
+
"""
|
|
64
|
+
loss = None
|
|
65
|
+
if closure is not None:
|
|
66
|
+
loss = closure()
|
|
67
|
+
|
|
68
|
+
for group in self.param_groups:
|
|
69
|
+
for i, p in enumerate(group["params"]):
|
|
70
|
+
if p.grad is None:
|
|
71
|
+
continue
|
|
72
|
+
|
|
73
|
+
grad = p.grad
|
|
74
|
+
state = self.state[p]
|
|
75
|
+
|
|
76
|
+
if "step" not in state:
|
|
77
|
+
state["step"] = 0
|
|
78
|
+
|
|
79
|
+
# State initialization
|
|
80
|
+
if "exp_avg" not in state:
|
|
81
|
+
# Exponential moving average of gradient values
|
|
82
|
+
state["exp_avg"] = torch.zeros_like(grad)
|
|
83
|
+
# Exponential moving average of squared gradient values
|
|
84
|
+
state["exp_avg_sq"] = torch.zeros_like(grad)
|
|
85
|
+
|
|
86
|
+
exp_avg, exp_avg_sq = state["exp_avg"], state["exp_avg_sq"]
|
|
87
|
+
beta1, beta2 = group["betas"]
|
|
88
|
+
|
|
89
|
+
state["step"] += 1
|
|
90
|
+
|
|
91
|
+
# apply weight decay
|
|
92
|
+
if group["weight_decay"] > 0.0:
|
|
93
|
+
p.add_(p, alpha=(-group["lr"] * group["weight_decay"]))
|
|
94
|
+
|
|
95
|
+
# Decay the first and second moment running average coefficient
|
|
96
|
+
# In-place operations to update the averages at the same time
|
|
97
|
+
exp_avg.mul_(beta1).add_(grad, alpha=(1.0 - beta1))
|
|
98
|
+
exp_avg_sq.mul_(beta2).addcmul_(grad, grad, value=1.0 - beta2)
|
|
99
|
+
denom = exp_avg_sq.sqrt().add_(group["eps"])
|
|
100
|
+
|
|
101
|
+
step_size = group["lr"]
|
|
102
|
+
if group["correct_bias"]: # No bias correction for Bert
|
|
103
|
+
bias_correction1 = 1.0 - beta1 ** state["step"]
|
|
104
|
+
bias_correction2 = 1.0 - beta2 ** state["step"]
|
|
105
|
+
step_size = step_size * math.sqrt(bias_correction2) / bias_correction1
|
|
106
|
+
|
|
107
|
+
# compute norm gradient
|
|
108
|
+
mask = (exp_avg * grad > 0).to(grad.dtype)
|
|
109
|
+
# mask = mask * (mask.numel() / (mask.sum() + 1)) ## original implementation, leaving it here for record
|
|
110
|
+
mask.div_(mask.mean().clamp_(min=1e-3)) # https://huggingface.co/rwightman/timm-optim-caution found this implementation is more favoarable in many cases
|
|
111
|
+
norm_grad = (exp_avg * mask) / denom
|
|
112
|
+
p.add_(norm_grad, alpha=-step_size)
|
|
113
|
+
return loss
|
|
@@ -76,7 +76,7 @@ def deterministic_gumbel_softmax(
|
|
|
76
76
|
|
|
77
77
|
|
|
78
78
|
@torch.jit.script
|
|
79
|
-
def
|
|
79
|
+
def composite_image_cont(
|
|
80
80
|
pixel_height_logits: torch.Tensor,
|
|
81
81
|
global_logits: torch.Tensor,
|
|
82
82
|
tau_height: float,
|
|
@@ -86,32 +86,27 @@ def composite_image(
|
|
|
86
86
|
material_colors: torch.Tensor,
|
|
87
87
|
material_TDs: torch.Tensor,
|
|
88
88
|
background: torch.Tensor,
|
|
89
|
-
mode: str = "continuous",
|
|
90
|
-
rng_seed: int = -1, # <--- new optional argument
|
|
91
89
|
) -> torch.Tensor:
|
|
92
90
|
"""
|
|
93
|
-
|
|
94
|
-
|
|
95
|
-
If rng_seed >= 0 and mode=="discrete", we will fix the random seed
|
|
96
|
-
for each layer so that we get the same random Gumbel noise for reproducibility.
|
|
91
|
+
Continuous compositing over all pixels.
|
|
92
|
+
Uses Gumbel softmax with either hard or soft sampling depending on tau_global.
|
|
97
93
|
"""
|
|
98
94
|
pixel_height = (max_layers * h) * torch.sigmoid(pixel_height_logits)
|
|
99
95
|
continuous_layers = pixel_height / h
|
|
100
96
|
|
|
101
|
-
# same as before
|
|
102
97
|
adaptive_layers = adaptive_round(
|
|
103
98
|
continuous_layers, tau_height, high_tau=0.1, low_tau=0.01, temp=0.1
|
|
104
99
|
)
|
|
105
100
|
discrete_layers_temp = torch.round(continuous_layers)
|
|
106
101
|
discrete_layers = (
|
|
107
102
|
discrete_layers_temp + (adaptive_layers - discrete_layers_temp).detach()
|
|
108
|
-
)
|
|
109
|
-
discrete_layers = discrete_layers.to(torch.int32)
|
|
103
|
+
).to(torch.int32)
|
|
110
104
|
|
|
111
105
|
H, W = pixel_height.shape
|
|
112
106
|
comp = torch.zeros(H, W, 3, dtype=torch.float32, device=pixel_height.device)
|
|
113
107
|
remaining = torch.ones(H, W, dtype=torch.float32, device=pixel_height.device)
|
|
114
108
|
|
|
109
|
+
# opacity function parameters
|
|
115
110
|
o = -1.2416557e-02
|
|
116
111
|
A = 9.6407950e-01
|
|
117
112
|
k = 3.4103447e01
|
|
@@ -119,39 +114,20 @@ def composite_image(
|
|
|
119
114
|
|
|
120
115
|
for i in range(max_layers):
|
|
121
116
|
layer_idx = max_layers - 1 - i
|
|
122
|
-
p_print = (discrete_layers > layer_idx).float()
|
|
123
|
-
eff_thick = p_print * h
|
|
124
|
-
|
|
125
|
-
#
|
|
126
|
-
|
|
127
|
-
if mode == "discrete":
|
|
128
|
-
if rng_seed >= 0:
|
|
129
|
-
# Use a context manager so we don’t pollute the global seed
|
|
130
|
-
p_i = deterministic_gumbel_softmax(
|
|
131
|
-
global_logits[layer_idx],
|
|
132
|
-
tau_global,
|
|
133
|
-
hard=True,
|
|
134
|
-
rng_seed=rng_seed + layer_idx,
|
|
135
|
-
)
|
|
136
|
-
else:
|
|
137
|
-
# same as before, but will be random every call
|
|
138
|
-
p_i = F.gumbel_softmax(global_logits[layer_idx], tau_global, hard=True)
|
|
139
|
-
|
|
140
|
-
elif mode == "continuous":
|
|
141
|
-
if tau_global < 1e-3:
|
|
142
|
-
p_i = F.gumbel_softmax(global_logits[layer_idx], tau_global, hard=True)
|
|
143
|
-
else:
|
|
144
|
-
p_i = F.gumbel_softmax(global_logits[layer_idx], tau_global, hard=False)
|
|
145
|
-
else:
|
|
146
|
-
# e.g. "pruning" or any other fallback
|
|
117
|
+
p_print = (discrete_layers > layer_idx).float() # [H,W]
|
|
118
|
+
eff_thick = p_print * h # effective thickness per pixel
|
|
119
|
+
|
|
120
|
+
# continuous mode: use hard gumbel if tau_global is very low
|
|
121
|
+
if tau_global < 1e-3:
|
|
147
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)
|
|
148
125
|
|
|
149
126
|
color_i = torch.matmul(p_i, material_colors)
|
|
150
127
|
TD_i = torch.matmul(p_i, material_TDs)
|
|
151
128
|
TD_i = torch.clamp(TD_i, 1e-8, 1e8)
|
|
152
129
|
|
|
153
130
|
opac = o + (A * torch.log1p(k * (eff_thick / TD_i)) + b * (eff_thick / TD_i))
|
|
154
|
-
# opac = torch.where(TD_i / 10 <= h, torch.tensor(1.0), opac)
|
|
155
131
|
opac = torch.clamp(opac, 0.0, 1.0)
|
|
156
132
|
|
|
157
133
|
comp = comp + ((remaining * opac).unsqueeze(-1) * color_i)
|
|
@@ -161,6 +137,162 @@ def composite_image(
|
|
|
161
137
|
return comp * 255.0
|
|
162
138
|
|
|
163
139
|
|
|
140
|
+
@torch.jit.script
|
|
141
|
+
def composite_image_disc(
|
|
142
|
+
pixel_height_logits: torch.Tensor, # [H,W]
|
|
143
|
+
global_logits: torch.Tensor, # [max_layers, n_materials]
|
|
144
|
+
tau_height: float,
|
|
145
|
+
tau_global: float,
|
|
146
|
+
h: float,
|
|
147
|
+
max_layers: int,
|
|
148
|
+
material_colors: torch.Tensor, # [n_materials, 3]
|
|
149
|
+
material_TDs: torch.Tensor, # [n_materials]
|
|
150
|
+
background: torch.Tensor, # [3]
|
|
151
|
+
rng_seed: int = -1,
|
|
152
|
+
) -> torch.Tensor:
|
|
153
|
+
"""
|
|
154
|
+
An optimized rewrite of the original compositing code.
|
|
155
|
+
|
|
156
|
+
The main changes:
|
|
157
|
+
- We combine the old (mask_diff) and (mask_not_printed) logic
|
|
158
|
+
into a single step that composites any pixel which is 'done'
|
|
159
|
+
with its current material (either because it's not printing
|
|
160
|
+
this layer or because the layer material is different).
|
|
161
|
+
- Then we update (or start) new segments for any pixel that
|
|
162
|
+
actually prints this layer and whose material matches or
|
|
163
|
+
doesn't match.
|
|
164
|
+
- Fewer conditionals, fewer index_select calls, and no repeated
|
|
165
|
+
if mask.any(): checks.
|
|
166
|
+
"""
|
|
167
|
+
|
|
168
|
+
# Determine how many discrete layers each pixel prints
|
|
169
|
+
pixel_height = (max_layers * h) * torch.sigmoid(pixel_height_logits)
|
|
170
|
+
continuous_layers = pixel_height / h
|
|
171
|
+
|
|
172
|
+
# Example "adaptive rounding" approach
|
|
173
|
+
adaptive_layers = adaptive_round(
|
|
174
|
+
continuous_layers, tau_height, high_tau=0.1, low_tau=0.01, temp=0.1
|
|
175
|
+
)
|
|
176
|
+
discrete_layers_temp = torch.round(continuous_layers)
|
|
177
|
+
discrete_layers = (
|
|
178
|
+
discrete_layers_temp + (adaptive_layers - discrete_layers_temp).detach()
|
|
179
|
+
).to(torch.int32) # [H,W]
|
|
180
|
+
|
|
181
|
+
# Precompute the chosen material for each layer
|
|
182
|
+
# (One material per layer for the entire image.)
|
|
183
|
+
if rng_seed >= 0:
|
|
184
|
+
# Deterministic sampling for each layer
|
|
185
|
+
new_mats_list = []
|
|
186
|
+
for layer_idx in range(max_layers):
|
|
187
|
+
p_i = deterministic_gumbel_softmax(
|
|
188
|
+
global_logits[layer_idx],
|
|
189
|
+
tau_global,
|
|
190
|
+
hard=True,
|
|
191
|
+
rng_seed=(rng_seed + layer_idx),
|
|
192
|
+
)
|
|
193
|
+
mat_i = torch.argmax(p_i, dim=0).to(torch.int32)
|
|
194
|
+
new_mats_list.append(mat_i)
|
|
195
|
+
new_mats = torch.stack(new_mats_list, dim=0) # [max_layers]
|
|
196
|
+
else:
|
|
197
|
+
# Standard (random) Gumbel softmax
|
|
198
|
+
p_all = F.gumbel_softmax(global_logits, tau_global, hard=True, dim=1) # [L, M]
|
|
199
|
+
new_mats = torch.argmax(p_all, dim=1).to(torch.int32) # [max_layers]
|
|
200
|
+
|
|
201
|
+
|
|
202
|
+
H, W = pixel_height.shape
|
|
203
|
+
device = pixel_height.device
|
|
204
|
+
|
|
205
|
+
comp = torch.zeros(H, W, 3, dtype=torch.float32, device=device)
|
|
206
|
+
remaining = torch.ones(H, W, dtype=torch.float32, device=device)
|
|
207
|
+
|
|
208
|
+
# Tracks the material currently being accumulated (or -1 for none)
|
|
209
|
+
cur_mat = -torch.ones((H, W), dtype=torch.int32, device=device)
|
|
210
|
+
# Tracks the accumulated thickness for the current segment
|
|
211
|
+
acc_thick = torch.zeros((H, W), dtype=torch.float32, device=device)
|
|
212
|
+
|
|
213
|
+
# Opacity function parameters
|
|
214
|
+
o = 0.10868816
|
|
215
|
+
A = 0.3077416
|
|
216
|
+
k = 76.928215
|
|
217
|
+
b = 2.2291653
|
|
218
|
+
|
|
219
|
+
# Main compositing loop: top to bottom
|
|
220
|
+
for layer_idx in range(max_layers - 1, -1, -1):
|
|
221
|
+
# Material chosen for this layer
|
|
222
|
+
layer_mat = new_mats[layer_idx] # a single int
|
|
223
|
+
# Which pixels actually print this layer?
|
|
224
|
+
p_print = discrete_layers > layer_idx # [H,W] bool
|
|
225
|
+
|
|
226
|
+
# --------------------------------------------------
|
|
227
|
+
# (A) Composite any "finished" segment, i.e. the pixel
|
|
228
|
+
# is *done* with its old segment if:
|
|
229
|
+
# (1) we already had a segment (cur_mat != -1), AND
|
|
230
|
+
# (2) either the pixel is not printing this layer,
|
|
231
|
+
# or the pixel's new layer material != cur_mat
|
|
232
|
+
# --------------------------------------------------
|
|
233
|
+
mask_done = (cur_mat != -1) & (~p_print | (cur_mat != layer_mat))
|
|
234
|
+
if mask_done.any():
|
|
235
|
+
old_inds = cur_mat[mask_done].to(torch.int64)
|
|
236
|
+
td_vals = material_TDs[old_inds] # gather thickness densities
|
|
237
|
+
col_vals = material_colors[old_inds] # gather old material colors
|
|
238
|
+
|
|
239
|
+
thick_ratio = acc_thick[mask_done] / td_vals
|
|
240
|
+
opac_vals = o + (A * torch.log1p(k * thick_ratio) + b * thick_ratio)
|
|
241
|
+
opac_vals = torch.clamp(opac_vals, 0.0, 1.0)
|
|
242
|
+
|
|
243
|
+
# composite old segment
|
|
244
|
+
comp[mask_done] += (remaining[mask_done] * opac_vals).unsqueeze(
|
|
245
|
+
-1
|
|
246
|
+
) * col_vals
|
|
247
|
+
remaining[mask_done] *= 1.0 - opac_vals
|
|
248
|
+
|
|
249
|
+
# Reset old segment
|
|
250
|
+
cur_mat[mask_done] = -1
|
|
251
|
+
acc_thick[mask_done] = 0.0
|
|
252
|
+
|
|
253
|
+
# --------------------------------------------------
|
|
254
|
+
# (B) For pixels that do print this layer:
|
|
255
|
+
# - start a new segment if cur_mat == -1
|
|
256
|
+
# - accumulate thickness if cur_mat == layer_mat
|
|
257
|
+
# --------------------------------------------------
|
|
258
|
+
eff_thick = p_print.to(torch.float32) * h
|
|
259
|
+
|
|
260
|
+
# Start new segment
|
|
261
|
+
mask_new = p_print & (cur_mat == -1)
|
|
262
|
+
if mask_new.any():
|
|
263
|
+
cur_mat[mask_new] = layer_mat
|
|
264
|
+
acc_thick[mask_new] = eff_thick[mask_new]
|
|
265
|
+
|
|
266
|
+
# Accumulate thickness
|
|
267
|
+
mask_same = p_print & (cur_mat == layer_mat)
|
|
268
|
+
if mask_same.any():
|
|
269
|
+
acc_thick[mask_same] += eff_thick[mask_same]
|
|
270
|
+
|
|
271
|
+
|
|
272
|
+
|
|
273
|
+
# After the loop, composite any still-pending segments
|
|
274
|
+
mask_remain = cur_mat != -1
|
|
275
|
+
if mask_remain.any():
|
|
276
|
+
old_inds = cur_mat[mask_remain].to(torch.int64)
|
|
277
|
+
td_vals = material_TDs[old_inds]
|
|
278
|
+
col_vals = material_colors[old_inds]
|
|
279
|
+
|
|
280
|
+
thick_ratio = acc_thick[mask_remain] / td_vals
|
|
281
|
+
opac_vals = o + (A * torch.log1p(k * thick_ratio) + b * thick_ratio)
|
|
282
|
+
opac_vals = torch.clamp(opac_vals, 0.0, 1.0)
|
|
283
|
+
|
|
284
|
+
comp[mask_remain] += (remaining[mask_remain] * opac_vals).unsqueeze(
|
|
285
|
+
-1
|
|
286
|
+
) * col_vals
|
|
287
|
+
remaining[mask_remain] *= 1.0 - opac_vals
|
|
288
|
+
|
|
289
|
+
# -------------------------------------------------------------------------
|
|
290
|
+
# Composite background
|
|
291
|
+
# -------------------------------------------------------------------------
|
|
292
|
+
comp += remaining.unsqueeze(-1) * background
|
|
293
|
+
return comp * 255.0
|
|
294
|
+
|
|
295
|
+
|
|
164
296
|
@torch.jit.script
|
|
165
297
|
def adaptive_round(
|
|
166
298
|
x: torch.Tensor, tau: float, high_tau: float, low_tau: float, temp: float
|
|
@@ -33,7 +33,7 @@ def extract_filament_swaps(disc_global, disc_height_image, background_layers):
|
|
|
33
33
|
current = int(disc_global[i])
|
|
34
34
|
# If this is the first layer or the material changes from the previous layer…
|
|
35
35
|
if current != prev:
|
|
36
|
-
slider =
|
|
36
|
+
slider = i + background_layers
|
|
37
37
|
slider_values.append(slider)
|
|
38
38
|
filament_indices.append(prev)
|
|
39
39
|
prev = current
|
|
@@ -118,7 +118,7 @@ def generate_project_file(
|
|
|
118
118
|
},
|
|
119
119
|
)
|
|
120
120
|
# add black to slider value
|
|
121
|
-
slider_values.insert(0, (args.background_height // args.layer_height)
|
|
121
|
+
slider_values.insert(0, (args.background_height // args.layer_height))
|
|
122
122
|
|
|
123
123
|
# reverse order of filament set
|
|
124
124
|
filament_set = filament_set[::-1]
|
|
@@ -186,11 +186,9 @@ def generate_project_file(
|
|
|
186
186
|
json.dump(project_data, f, indent=4)
|
|
187
187
|
|
|
188
188
|
|
|
189
|
-
|
|
190
|
-
|
|
191
|
-
|
|
192
|
-
|
|
193
|
-
def generate_stl(height_map, filename, background_height, maximum_x_y_size, alpha_mask=None):
|
|
189
|
+
def generate_stl(
|
|
190
|
+
height_map, filename, background_height, maximum_x_y_size, alpha_mask=None
|
|
191
|
+
):
|
|
194
192
|
"""
|
|
195
193
|
Generate a binary STL file from a height map with an optional alpha mask.
|
|
196
194
|
If alpha_mask is provided, vertices where alpha < 128 are omitted.
|
|
@@ -213,7 +211,7 @@ def generate_stl(height_map, filename, background_height, maximum_x_y_size, alph
|
|
|
213
211
|
if alpha_mask is None:
|
|
214
212
|
valid_mask = np.ones((H, W), dtype=bool)
|
|
215
213
|
else:
|
|
216
|
-
valid_mask =
|
|
214
|
+
valid_mask = alpha_mask >= 128
|
|
217
215
|
|
|
218
216
|
# --- Create vertices for the top surface ---
|
|
219
217
|
# We compute a grid of vertices (even for masked-out regions) but later only use those that are valid.
|
|
@@ -250,7 +248,12 @@ def generate_stl(height_map, filename, background_height, maximum_x_y_size, alph
|
|
|
250
248
|
top_triangles_indices = [] # Each element is a tuple of 3 (i,j) indices.
|
|
251
249
|
for i in range(H - 1):
|
|
252
250
|
for j in range(W - 1):
|
|
253
|
-
if
|
|
251
|
+
if (
|
|
252
|
+
valid_mask[i, j]
|
|
253
|
+
and valid_mask[i, j + 1]
|
|
254
|
+
and valid_mask[i + 1, j + 1]
|
|
255
|
+
and valid_mask[i + 1, j]
|
|
256
|
+
):
|
|
254
257
|
# Define the four corners of the quad.
|
|
255
258
|
v0 = top_vertices[i, j]
|
|
256
259
|
v1 = top_vertices[i, j + 1]
|
|
@@ -306,7 +309,12 @@ def generate_stl(height_map, filename, background_height, maximum_x_y_size, alph
|
|
|
306
309
|
# The ordering is reversed (relative to the top face) so that the normals face downward.
|
|
307
310
|
for i in range(H - 1):
|
|
308
311
|
for j in range(W - 1):
|
|
309
|
-
if
|
|
312
|
+
if (
|
|
313
|
+
valid_mask[i, j]
|
|
314
|
+
and valid_mask[i, j + 1]
|
|
315
|
+
and valid_mask[i + 1, j + 1]
|
|
316
|
+
and valid_mask[i + 1, j]
|
|
317
|
+
):
|
|
310
318
|
v0 = bottom_vertices[i, j]
|
|
311
319
|
v1 = bottom_vertices[i, j + 1]
|
|
312
320
|
v2 = bottom_vertices[i + 1, j + 1]
|
|
@@ -383,7 +391,7 @@ def generate_swap_instructions(
|
|
|
383
391
|
instructions.append("Start with your background color")
|
|
384
392
|
for i in range(0, L):
|
|
385
393
|
if i == 0 or int(discrete_global[i]) != int(discrete_global[i - 1]):
|
|
386
|
-
ie = i
|
|
394
|
+
ie = i + 1
|
|
387
395
|
instructions.append(
|
|
388
396
|
f"At layer #{ie + background_layers} ({(ie * h) + background_height:.2f}mm) swap to {material_names[int(discrete_global[i])]}"
|
|
389
397
|
)
|
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
import torch
|
|
2
2
|
from tqdm import tqdm
|
|
3
3
|
|
|
4
|
-
from autoforge.Helper.OptimizerHelper import
|
|
4
|
+
from autoforge.Helper.OptimizerHelper import discretize_solution, composite_image_disc
|
|
5
5
|
from autoforge.Loss.LossFunctions import compute_loss
|
|
6
6
|
|
|
7
7
|
|
|
@@ -55,7 +55,7 @@ def prune_num_colors(
|
|
|
55
55
|
)
|
|
56
56
|
|
|
57
57
|
with torch.no_grad():
|
|
58
|
-
out_im =
|
|
58
|
+
out_im = composite_image_disc(
|
|
59
59
|
pixel_height_logits,
|
|
60
60
|
logits_for_disc,
|
|
61
61
|
tau_for_comp,
|
|
@@ -65,7 +65,6 @@ def prune_num_colors(
|
|
|
65
65
|
material_colors,
|
|
66
66
|
material_TDs,
|
|
67
67
|
background,
|
|
68
|
-
mode="discrete",
|
|
69
68
|
rng_seed=rng_seed,
|
|
70
69
|
)
|
|
71
70
|
|
|
@@ -148,7 +147,7 @@ def prune_num_swaps(
|
|
|
148
147
|
)
|
|
149
148
|
|
|
150
149
|
with torch.no_grad():
|
|
151
|
-
out_im =
|
|
150
|
+
out_im = composite_image_disc(
|
|
152
151
|
pixel_height_logits,
|
|
153
152
|
logits_for_disc,
|
|
154
153
|
tau_for_comp,
|
|
@@ -158,7 +157,6 @@ def prune_num_swaps(
|
|
|
158
157
|
material_colors,
|
|
159
158
|
material_TDs,
|
|
160
159
|
background,
|
|
161
|
-
mode="discrete",
|
|
162
160
|
rng_seed=rng_seed,
|
|
163
161
|
)
|
|
164
162
|
return compute_loss(
|
|
@@ -413,7 +411,7 @@ def prune_redundant_layers(
|
|
|
413
411
|
current_max_layers = current_params["global_logits"].shape[0]
|
|
414
412
|
|
|
415
413
|
# Compute baseline composite and loss.
|
|
416
|
-
comp =
|
|
414
|
+
comp = composite_image_disc(
|
|
417
415
|
current_params["pixel_height_logits"],
|
|
418
416
|
current_params["global_logits"],
|
|
419
417
|
final_tau,
|
|
@@ -423,7 +421,6 @@ def prune_redundant_layers(
|
|
|
423
421
|
material_colors,
|
|
424
422
|
material_TDs,
|
|
425
423
|
background,
|
|
426
|
-
mode="discrete",
|
|
427
424
|
rng_seed=rng_seed,
|
|
428
425
|
)
|
|
429
426
|
disc_global, _ = discretize_solution(
|
|
@@ -458,7 +455,7 @@ def prune_redundant_layers(
|
|
|
458
455
|
candidate_params, candidate_max_layers = remove_layer_from_solution(
|
|
459
456
|
current_params, layer, final_tau, h, current_max_layers, rng_seed
|
|
460
457
|
)
|
|
461
|
-
candidate_comp =
|
|
458
|
+
candidate_comp = composite_image_disc(
|
|
462
459
|
candidate_params["pixel_height_logits"],
|
|
463
460
|
candidate_params["global_logits"],
|
|
464
461
|
final_tau,
|
|
@@ -468,7 +465,6 @@ def prune_redundant_layers(
|
|
|
468
465
|
material_colors,
|
|
469
466
|
material_TDs,
|
|
470
467
|
background,
|
|
471
|
-
mode="discrete",
|
|
472
468
|
rng_seed=rng_seed,
|
|
473
469
|
)
|
|
474
470
|
candidate_disc_global, _ = discretize_solution(
|
|
@@ -2,7 +2,7 @@ import torch
|
|
|
2
2
|
import torch.nn.functional as F
|
|
3
3
|
|
|
4
4
|
from autoforge.Helper.ImageHelper import srgb_to_lab, increase_saturation
|
|
5
|
-
from autoforge.Helper.OptimizerHelper import
|
|
5
|
+
from autoforge.Helper.OptimizerHelper import composite_image_cont
|
|
6
6
|
|
|
7
7
|
|
|
8
8
|
def loss_fn(
|
|
@@ -22,7 +22,7 @@ def loss_fn(
|
|
|
22
22
|
Full forward pass for continuous assignment:
|
|
23
23
|
composite, then compute unified loss on (global_logits).
|
|
24
24
|
"""
|
|
25
|
-
comp =
|
|
25
|
+
comp = composite_image_cont(
|
|
26
26
|
params["pixel_height_logits"],
|
|
27
27
|
params["global_logits"],
|
|
28
28
|
tau_height,
|
|
@@ -32,7 +32,6 @@ def loss_fn(
|
|
|
32
32
|
material_colors,
|
|
33
33
|
material_TDs,
|
|
34
34
|
background,
|
|
35
|
-
mode="continuous",
|
|
36
35
|
)
|
|
37
36
|
return compute_loss(
|
|
38
37
|
material_assignment=params["global_logits"],
|
|
@@ -5,11 +5,15 @@ import random
|
|
|
5
5
|
import matplotlib.pyplot as plt
|
|
6
6
|
import numpy as np
|
|
7
7
|
import torch
|
|
8
|
-
import torch.optim as optim
|
|
9
8
|
from tqdm import tqdm
|
|
10
9
|
from torch.utils.tensorboard import SummaryWriter
|
|
11
10
|
|
|
12
|
-
from autoforge.Helper.
|
|
11
|
+
from autoforge.Helper.CAdamW import CAdamW
|
|
12
|
+
from autoforge.Helper.OptimizerHelper import (
|
|
13
|
+
discretize_solution,
|
|
14
|
+
composite_image_cont,
|
|
15
|
+
composite_image_disc,
|
|
16
|
+
)
|
|
13
17
|
from autoforge.Helper.PruningHelper import prune_colors_and_swaps, find_color_bands
|
|
14
18
|
from autoforge.Loss.LossFunctions import loss_fn, compute_loss
|
|
15
19
|
|
|
@@ -64,7 +68,7 @@ class FilamentOptimizer:
|
|
|
64
68
|
# Initialize TensorBoard writer
|
|
65
69
|
if args.tensorboard:
|
|
66
70
|
if args.run_name:
|
|
67
|
-
self.writer = SummaryWriter(log_dir=f
|
|
71
|
+
self.writer = SummaryWriter(log_dir=f"runs/{args.run_name}")
|
|
68
72
|
else:
|
|
69
73
|
self.writer = SummaryWriter()
|
|
70
74
|
else:
|
|
@@ -99,7 +103,7 @@ class FilamentOptimizer:
|
|
|
99
103
|
)
|
|
100
104
|
|
|
101
105
|
# Initialize optimizer
|
|
102
|
-
self.optimizer =
|
|
106
|
+
self.optimizer = CAdamW(
|
|
103
107
|
[self.params["pixel_height_logits"], self.params["global_logits"]],
|
|
104
108
|
lr=self.learning_rate,
|
|
105
109
|
)
|
|
@@ -193,7 +197,9 @@ class FilamentOptimizer:
|
|
|
193
197
|
|
|
194
198
|
return loss
|
|
195
199
|
|
|
196
|
-
def log_to_tensorboard(
|
|
200
|
+
def log_to_tensorboard(
|
|
201
|
+
self, interval: int = 100, namespace: str = "", step: int = None
|
|
202
|
+
):
|
|
197
203
|
"""
|
|
198
204
|
Log metrics and images to TensorBoard.
|
|
199
205
|
|
|
@@ -211,22 +217,26 @@ class FilamentOptimizer:
|
|
|
211
217
|
steps = step if step is not None else self.num_steps_done
|
|
212
218
|
|
|
213
219
|
# Log metrics
|
|
214
|
-
self.writer.add_scalar(
|
|
215
|
-
|
|
220
|
+
self.writer.add_scalar(
|
|
221
|
+
f"Loss/{prefix}best_discrete", self.best_discrete_loss, steps
|
|
222
|
+
)
|
|
223
|
+
self.writer.add_scalar(f"Loss/{prefix}best_swaps", self.best_swaps, steps)
|
|
216
224
|
|
|
217
225
|
tau_height, tau_global = self._get_tau()
|
|
218
226
|
|
|
219
227
|
# Metrics that are only relevant for the main optimization loop
|
|
220
228
|
if not prefix:
|
|
221
|
-
self.writer.add_scalar(
|
|
222
|
-
self.writer.add_scalar(
|
|
223
|
-
self.writer.add_scalar(
|
|
224
|
-
|
|
229
|
+
self.writer.add_scalar("Params/tau_height", tau_height, steps)
|
|
230
|
+
self.writer.add_scalar("Params/tau_global", tau_global, steps)
|
|
231
|
+
self.writer.add_scalar(
|
|
232
|
+
"Params/lr", self.optimizer.param_groups[0]["lr"], steps
|
|
233
|
+
)
|
|
234
|
+
self.writer.add_scalar("Loss/train", self.loss, steps)
|
|
225
235
|
|
|
226
236
|
# Log images periodically
|
|
227
237
|
if (steps + 1) % interval == 0:
|
|
228
238
|
with torch.no_grad():
|
|
229
|
-
comp_img =
|
|
239
|
+
comp_img = composite_image_cont(
|
|
230
240
|
self.params["pixel_height_logits"],
|
|
231
241
|
self.params["global_logits"],
|
|
232
242
|
tau_height,
|
|
@@ -235,9 +245,13 @@ class FilamentOptimizer:
|
|
|
235
245
|
self.max_layers,
|
|
236
246
|
self.material_colors,
|
|
237
247
|
self.material_TDs,
|
|
238
|
-
self.background
|
|
248
|
+
self.background,
|
|
249
|
+
)
|
|
250
|
+
self.writer.add_images(
|
|
251
|
+
f"Current Output/{prefix}composite",
|
|
252
|
+
comp_img.permute(2, 0, 1).unsqueeze(0) / 255.0,
|
|
253
|
+
steps,
|
|
239
254
|
)
|
|
240
|
-
self.writer.add_images(f'Current Output/{prefix}composite', comp_img.permute(2, 0, 1).unsqueeze(0) / 255.0, steps)
|
|
241
255
|
|
|
242
256
|
def visualize(self, interval: int = 25):
|
|
243
257
|
"""
|
|
@@ -255,7 +269,7 @@ class FilamentOptimizer:
|
|
|
255
269
|
|
|
256
270
|
with torch.no_grad():
|
|
257
271
|
tau_h, tau_g = self._get_tau()
|
|
258
|
-
comp =
|
|
272
|
+
comp = composite_image_cont(
|
|
259
273
|
self.params["pixel_height_logits"],
|
|
260
274
|
self.params["global_logits"],
|
|
261
275
|
tau_h,
|
|
@@ -265,14 +279,13 @@ class FilamentOptimizer:
|
|
|
265
279
|
self.material_colors,
|
|
266
280
|
self.material_TDs,
|
|
267
281
|
self.background,
|
|
268
|
-
mode="continuous",
|
|
269
282
|
)
|
|
270
283
|
comp_np = np.clip(comp.cpu().detach().numpy(), 0, 255).astype(np.uint8)
|
|
271
284
|
self.current_comp_ax.set_data(comp_np)
|
|
272
285
|
|
|
273
286
|
if self.best_params is not None:
|
|
274
287
|
with torch.no_grad():
|
|
275
|
-
best_comp =
|
|
288
|
+
best_comp = composite_image_disc(
|
|
276
289
|
self.best_params["pixel_height_logits"],
|
|
277
290
|
self.best_params["global_logits"],
|
|
278
291
|
self.final_tau,
|
|
@@ -282,7 +295,6 @@ class FilamentOptimizer:
|
|
|
282
295
|
self.material_colors,
|
|
283
296
|
self.material_TDs,
|
|
284
297
|
self.background,
|
|
285
|
-
mode="discrete",
|
|
286
298
|
rng_seed=self.best_seed,
|
|
287
299
|
)
|
|
288
300
|
best_comp_np = np.clip(best_comp.cpu().detach().numpy(), 0, 255).astype(
|
|
@@ -434,7 +446,7 @@ class FilamentOptimizer:
|
|
|
434
446
|
|
|
435
447
|
# 2) Compute discrete-mode composite
|
|
436
448
|
with torch.no_grad():
|
|
437
|
-
comp_disc =
|
|
449
|
+
comp_disc = composite_image_disc(
|
|
438
450
|
self.params["pixel_height_logits"],
|
|
439
451
|
self.params["global_logits"],
|
|
440
452
|
self.final_tau,
|
|
@@ -444,7 +456,6 @@ class FilamentOptimizer:
|
|
|
444
456
|
self.material_colors,
|
|
445
457
|
self.material_TDs,
|
|
446
458
|
self.background,
|
|
447
|
-
mode="discrete", # or "discrete" if you prefer fully discrete
|
|
448
459
|
rng_seed=seed,
|
|
449
460
|
)
|
|
450
461
|
|
|
@@ -487,7 +498,7 @@ class FilamentOptimizer:
|
|
|
487
498
|
disc_global, disc_height_image = discretize_solution(
|
|
488
499
|
self.best_params, self.final_tau, self.h, self.max_layers, rng_seed=seed
|
|
489
500
|
)
|
|
490
|
-
comp_disc =
|
|
501
|
+
comp_disc = composite_image_disc(
|
|
491
502
|
self.best_params["pixel_height_logits"],
|
|
492
503
|
self.best_params["global_logits"],
|
|
493
504
|
self.final_tau,
|
|
@@ -497,7 +508,6 @@ class FilamentOptimizer:
|
|
|
497
508
|
self.material_colors,
|
|
498
509
|
self.material_TDs,
|
|
499
510
|
self.background,
|
|
500
|
-
mode="discrete",
|
|
501
511
|
rng_seed=seed,
|
|
502
512
|
)
|
|
503
513
|
current_disc_loss = compute_loss(
|
|
@@ -10,9 +10,9 @@ from autoforge.Helper.FilamentHelper import hex_to_rgb, load_materials
|
|
|
10
10
|
from autoforge.Helper.ImageHelper import resize_image, resize_image_exact
|
|
11
11
|
from autoforge.Helper.OptimizerHelper import (
|
|
12
12
|
init_height_map,
|
|
13
|
-
composite_image,
|
|
14
13
|
discretize_solution,
|
|
15
14
|
init_height_map_depth_color_adjusted,
|
|
15
|
+
composite_image_disc,
|
|
16
16
|
)
|
|
17
17
|
from autoforge.Helper.OutputHelper import (
|
|
18
18
|
generate_stl,
|
|
@@ -207,6 +207,9 @@ def main():
|
|
|
207
207
|
"Background must be multiple of layer_height"
|
|
208
208
|
)
|
|
209
209
|
|
|
210
|
+
assert os.path.exists(args.input_image), "Input image not found"
|
|
211
|
+
assert os.path.exists(args.csv_file), "CSV file not found"
|
|
212
|
+
|
|
210
213
|
random_seed = args.random_seed
|
|
211
214
|
if random_seed == 0:
|
|
212
215
|
random_seed = int(time.time())
|
|
@@ -387,7 +390,7 @@ def main():
|
|
|
387
390
|
|
|
388
391
|
print("Done. Saving outputs...")
|
|
389
392
|
# Save Image
|
|
390
|
-
comp_disc =
|
|
393
|
+
comp_disc = composite_image_disc(
|
|
391
394
|
params["pixel_height_logits"],
|
|
392
395
|
params["global_logits"],
|
|
393
396
|
args.final_tau,
|
|
@@ -397,7 +400,6 @@ def main():
|
|
|
397
400
|
material_colors,
|
|
398
401
|
material_TDs,
|
|
399
402
|
background,
|
|
400
|
-
mode="discrete", # or "discrete" if you prefer fully discrete
|
|
401
403
|
rng_seed=optimizer.best_seed,
|
|
402
404
|
)
|
|
403
405
|
|
|
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
|