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.
Files changed (35) hide show
  1. {autoforge-1.4.2 → autoforge-1.5.1}/PKG-INFO +8 -15
  2. {autoforge-1.4.2 → autoforge-1.5.1}/README.md +7 -14
  3. {autoforge-1.4.2 → autoforge-1.5.1}/pyproject.toml +1 -1
  4. {autoforge-1.4.2 → autoforge-1.5.1}/src/AutoForge.egg-info/PKG-INFO +8 -15
  5. {autoforge-1.4.2 → autoforge-1.5.1}/src/AutoForge.egg-info/SOURCES.txt +1 -0
  6. autoforge-1.5.1/src/autoforge/Helper/CAdamW.py +113 -0
  7. {autoforge-1.4.2 → autoforge-1.5.1}/src/autoforge/Helper/OptimizerHelper.py +168 -36
  8. {autoforge-1.4.2 → autoforge-1.5.1}/src/autoforge/Helper/OutputHelper.py +19 -11
  9. {autoforge-1.4.2 → autoforge-1.5.1}/src/autoforge/Helper/PruningHelper.py +5 -9
  10. {autoforge-1.4.2 → autoforge-1.5.1}/src/autoforge/Loss/LossFunctions.py +2 -3
  11. {autoforge-1.4.2 → autoforge-1.5.1}/src/autoforge/Modules/Optimizer.py +32 -22
  12. {autoforge-1.4.2 → autoforge-1.5.1}/src/autoforge/auto_forge.py +5 -3
  13. {autoforge-1.4.2 → autoforge-1.5.1}/setup.cfg +0 -0
  14. {autoforge-1.4.2 → autoforge-1.5.1}/src/AutoForge.egg-info/dependency_links.txt +0 -0
  15. {autoforge-1.4.2 → autoforge-1.5.1}/src/AutoForge.egg-info/entry_points.txt +0 -0
  16. {autoforge-1.4.2 → autoforge-1.5.1}/src/AutoForge.egg-info/requires.txt +0 -0
  17. {autoforge-1.4.2 → autoforge-1.5.1}/src/AutoForge.egg-info/top_level.txt +0 -0
  18. {autoforge-1.4.2 → autoforge-1.5.1}/src/autoforge/Gui/Views/__init__.py +0 -0
  19. {autoforge-1.4.2 → autoforge-1.5.1}/src/autoforge/Gui/Views/backend_database.py +0 -0
  20. {autoforge-1.4.2 → autoforge-1.5.1}/src/autoforge/Gui/Views/filament_editor.py +0 -0
  21. {autoforge-1.4.2 → autoforge-1.5.1}/src/autoforge/Gui/__init__.py +0 -0
  22. {autoforge-1.4.2 → autoforge-1.5.1}/src/autoforge/Gui/app.py +0 -0
  23. {autoforge-1.4.2 → autoforge-1.5.1}/src/autoforge/Gui/main.py +0 -0
  24. {autoforge-1.4.2 → autoforge-1.5.1}/src/autoforge/Helper/FilamentHelper.py +0 -0
  25. {autoforge-1.4.2 → autoforge-1.5.1}/src/autoforge/Helper/ImageHelper.py +0 -0
  26. {autoforge-1.4.2 → autoforge-1.5.1}/src/autoforge/Helper/__init__.py +0 -0
  27. {autoforge-1.4.2 → autoforge-1.5.1}/src/autoforge/Helper/filamentcolors_library.py +0 -0
  28. {autoforge-1.4.2 → autoforge-1.5.1}/src/autoforge/Loss/PerceptionLoss.py +0 -0
  29. {autoforge-1.4.2 → autoforge-1.5.1}/src/autoforge/Loss/__init__.py +0 -0
  30. {autoforge-1.4.2 → autoforge-1.5.1}/src/autoforge/Misc/__init__.py +0 -0
  31. {autoforge-1.4.2 → autoforge-1.5.1}/src/autoforge/Misc/color_fit_all.py +0 -0
  32. {autoforge-1.4.2 → autoforge-1.5.1}/src/autoforge/Modules/__init__.py +0 -0
  33. {autoforge-1.4.2 → autoforge-1.5.1}/src/autoforge/__init__.py +0 -0
  34. {autoforge-1.4.2 → autoforge-1.5.1}/src/autoforge/__main__.py +0 -0
  35. {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.4.2
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.jpg" width="200" />
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.jpg" width="200" />
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 a initializing function that takes the original deph of the image into account when initializing. \
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 filament data and inspiration for this project.
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.jpg" width="200" />
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.jpg" width="200" />
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 a initializing function that takes the original deph of the image into account when initializing. \
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 filament data and inspiration for this project.
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.4.2"
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.4.2
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.jpg" width="200" />
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.jpg" width="200" />
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 a initializing function that takes the original deph of the image into account when initializing. \
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 filament data and inspiration for this project.
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 composite_image(
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
- Vectorized compositing over all pixels (H x W).
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
- # This is the key difference for "discrete":
126
- # we now optionally fix the random seed if rng_seed >= 0
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 = (i + background_layers) - 1
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) - 1)
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
- import numpy as np
190
- import struct
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 = (alpha_mask >= 128)
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 valid_mask[i, j] and valid_mask[i, j + 1] and valid_mask[i + 1, j + 1] and valid_mask[i + 1, j]:
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 valid_mask[i, j] and valid_mask[i, j + 1] and valid_mask[i + 1, j + 1] and valid_mask[i + 1, j]:
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 composite_image, discretize_solution
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 = composite_image(
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 = composite_image(
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 = composite_image(
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 = composite_image(
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 composite_image
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 = composite_image(
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.OptimizerHelper import composite_image, discretize_solution
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'runs/{args.run_name}')
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 = optim.Adam(
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(self, interval: int = 100, namespace: str = "", step: int = None):
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(f'Loss/{prefix}best_discrete', self.best_discrete_loss, steps)
215
- self.writer.add_scalar(f'Loss/{prefix}best_swaps', self.best_swaps, steps)
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('Params/tau_height', tau_height, steps)
222
- self.writer.add_scalar('Params/tau_global', tau_global, steps)
223
- self.writer.add_scalar('Params/lr', self.optimizer.param_groups[0]['lr'], steps)
224
- self.writer.add_scalar('Loss/train', self.loss, steps)
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 = composite_image(
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 = composite_image(
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 = composite_image(
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 = composite_image(
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 = composite_image(
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 = composite_image(
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