autoforge 1.7.1__tar.gz → 1.7.3__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 (41) hide show
  1. {autoforge-1.7.1 → autoforge-1.7.3}/PKG-INFO +5 -2
  2. {autoforge-1.7.1 → autoforge-1.7.3}/README.md +4 -1
  3. {autoforge-1.7.1 → autoforge-1.7.3}/pyproject.toml +1 -1
  4. {autoforge-1.7.1 → autoforge-1.7.3}/src/AutoForge.egg-info/PKG-INFO +5 -2
  5. {autoforge-1.7.1 → autoforge-1.7.3}/src/autoforge/Helper/Heightmaps/ChristofidesHeightMap.py +2 -1
  6. {autoforge-1.7.1 → autoforge-1.7.3}/src/autoforge/Helper/PruningHelper.py +6 -3
  7. {autoforge-1.7.1 → autoforge-1.7.3}/src/autoforge/Modules/Optimizer.py +51 -63
  8. {autoforge-1.7.1 → autoforge-1.7.3}/src/autoforge/auto_forge.py +1 -1
  9. {autoforge-1.7.1 → autoforge-1.7.3}/setup.cfg +0 -0
  10. {autoforge-1.7.1 → autoforge-1.7.3}/src/AutoForge.egg-info/SOURCES.txt +0 -0
  11. {autoforge-1.7.1 → autoforge-1.7.3}/src/AutoForge.egg-info/dependency_links.txt +0 -0
  12. {autoforge-1.7.1 → autoforge-1.7.3}/src/AutoForge.egg-info/entry_points.txt +0 -0
  13. {autoforge-1.7.1 → autoforge-1.7.3}/src/AutoForge.egg-info/requires.txt +0 -0
  14. {autoforge-1.7.1 → autoforge-1.7.3}/src/AutoForge.egg-info/top_level.txt +0 -0
  15. {autoforge-1.7.1 → autoforge-1.7.3}/src/autoforge/Gui/__init__.py +0 -0
  16. {autoforge-1.7.1 → autoforge-1.7.3}/src/autoforge/Gui/browser_gui/Views/__init__.py +0 -0
  17. {autoforge-1.7.1 → autoforge-1.7.3}/src/autoforge/Gui/browser_gui/Views/backend_database.py +0 -0
  18. {autoforge-1.7.1 → autoforge-1.7.3}/src/autoforge/Gui/browser_gui/Views/filament_editor.py +0 -0
  19. {autoforge-1.7.1 → autoforge-1.7.3}/src/autoforge/Gui/browser_gui/app.py +0 -0
  20. {autoforge-1.7.1 → autoforge-1.7.3}/src/autoforge/Gui/browser_gui/main.py +0 -0
  21. {autoforge-1.7.1 → autoforge-1.7.3}/src/autoforge/Helper/CAdamW.py +0 -0
  22. {autoforge-1.7.1 → autoforge-1.7.3}/src/autoforge/Helper/FilamentHelper.py +0 -0
  23. {autoforge-1.7.1 → autoforge-1.7.3}/src/autoforge/Helper/Heightmaps/DepthEstimateHeightMap.py +0 -0
  24. {autoforge-1.7.1 → autoforge-1.7.3}/src/autoforge/Helper/Heightmaps/OldHeightmap.py +0 -0
  25. {autoforge-1.7.1 → autoforge-1.7.3}/src/autoforge/Helper/Heightmaps/__init__.py +0 -0
  26. {autoforge-1.7.1 → autoforge-1.7.3}/src/autoforge/Helper/ImageHelper.py +0 -0
  27. {autoforge-1.7.1 → autoforge-1.7.3}/src/autoforge/Helper/OptimizerHelper.py +0 -0
  28. {autoforge-1.7.1 → autoforge-1.7.3}/src/autoforge/Helper/OutputHelper.py +0 -0
  29. {autoforge-1.7.1 → autoforge-1.7.3}/src/autoforge/Helper/__init__.py +0 -0
  30. {autoforge-1.7.1 → autoforge-1.7.3}/src/autoforge/Helper/filamentcolors_library.py +0 -0
  31. {autoforge-1.7.1 → autoforge-1.7.3}/src/autoforge/Init_comparer/__init__.py +0 -0
  32. {autoforge-1.7.1 → autoforge-1.7.3}/src/autoforge/Init_comparer/auto_forge_init_check.py +0 -0
  33. {autoforge-1.7.1 → autoforge-1.7.3}/src/autoforge/Init_comparer/plot_results.py +0 -0
  34. {autoforge-1.7.1 → autoforge-1.7.3}/src/autoforge/Loss/LossFunctions.py +0 -0
  35. {autoforge-1.7.1 → autoforge-1.7.3}/src/autoforge/Loss/PerceptionLoss.py +0 -0
  36. {autoforge-1.7.1 → autoforge-1.7.3}/src/autoforge/Loss/__init__.py +0 -0
  37. {autoforge-1.7.1 → autoforge-1.7.3}/src/autoforge/Misc/__init__.py +0 -0
  38. {autoforge-1.7.1 → autoforge-1.7.3}/src/autoforge/Misc/color_fit_all.py +0 -0
  39. {autoforge-1.7.1 → autoforge-1.7.3}/src/autoforge/Modules/__init__.py +0 -0
  40. {autoforge-1.7.1 → autoforge-1.7.3}/src/autoforge/__init__.py +0 -0
  41. {autoforge-1.7.1 → autoforge-1.7.3}/src/autoforge/__main__.py +0 -0
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.4
2
2
  Name: AutoForge
3
- Version: 1.7.1
3
+ Version: 1.7.3
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
@@ -30,8 +30,11 @@ AutoForge is a Python tool for generating 3D printed layered models from an inpu
30
30
 
31
31
  **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).
32
32
 
33
+
34
+ ## You can now run Autoforge for free in your browser thanks to [Huggingface space support](https://huggingface.co/spaces/hvoss-techfak/Autoforge).
35
+
33
36
  ## Example
34
- All examples use only the 13 BambuLab Basic filaments, currently available in Hueforge, the background color is set to black.
37
+ All examples use only the 13 BambuLab Basic filaments, currently available in Hueforge 0.8.9, the background color is set to black.
35
38
  The pruning is set to a maximum of 8 color and 20 swaps, so each image uses at most 8 different colors and swaps the filament at most 20 times.
36
39
  <div style="display: flex; justify-content: center; gap: 20px;">
37
40
  <div style="text-align: center;">
@@ -4,8 +4,11 @@ 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
+ ## You can now run Autoforge for free in your browser thanks to [Huggingface space support](https://huggingface.co/spaces/hvoss-techfak/Autoforge).
9
+
7
10
  ## Example
8
- All examples use only the 13 BambuLab Basic filaments, currently available in Hueforge, the background color is set to black.
11
+ All examples use only the 13 BambuLab Basic filaments, currently available in Hueforge 0.8.9, the background color is set to black.
9
12
  The pruning is set to a maximum of 8 color and 20 swaps, so each image uses at most 8 different colors and swaps the filament at most 20 times.
10
13
  <div style="display: flex; justify-content: center; gap: 20px;">
11
14
  <div style="text-align: center;">
@@ -4,7 +4,7 @@ build-backend = "setuptools.build_meta"
4
4
 
5
5
  [project]
6
6
  name = "AutoForge"
7
- version="1.7.1"
7
+ version="1.7.3"
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.4
2
2
  Name: AutoForge
3
- Version: 1.7.1
3
+ Version: 1.7.3
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
@@ -30,8 +30,11 @@ AutoForge is a Python tool for generating 3D printed layered models from an inpu
30
30
 
31
31
  **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).
32
32
 
33
+
34
+ ## You can now run Autoforge for free in your browser thanks to [Huggingface space support](https://huggingface.co/spaces/hvoss-techfak/Autoforge).
35
+
33
36
  ## Example
34
- All examples use only the 13 BambuLab Basic filaments, currently available in Hueforge, the background color is set to black.
37
+ All examples use only the 13 BambuLab Basic filaments, currently available in Hueforge 0.8.9, the background color is set to black.
35
38
  The pruning is set to a maximum of 8 color and 20 swaps, so each image uses at most 8 different colors and swaps the filament at most 20 times.
36
39
  <div style="display: flex; justify-content: center; gap: 20px;">
37
40
  <div style="text-align: center;">
@@ -532,9 +532,10 @@ def run_init_threads(
532
532
  cluster_layers=None,
533
533
  material_colors=None,
534
534
  ):
535
+ background_tuple = (np.asarray(background_tuple) * 255).tolist()
535
536
  if random_seed is None:
536
537
  random_seed = np.random.randint(1e6)
537
- lab_space = False
538
+ lab_space = True
538
539
  tasks = [
539
540
  delayed(init_height_map)(
540
541
  target,
@@ -51,6 +51,7 @@ def prune_num_colors(
51
51
  *,
52
52
  fast: bool = True, # enable incremental search
53
53
  chunking_percent=0.05, # percentage of layers to process at once
54
+ allowed_loss_increase_percent=0.005, # percentage of loss increase allowed over best loss
54
55
  ) -> torch.Tensor:
55
56
  """Iteratively merge materials until the number of distinct colors is
56
57
  <= max_colors_allowed or until merging would worsen the loss.
@@ -116,7 +117,7 @@ def prune_num_colors(
116
117
  n_jobs=n_jobs, backend="threading", prefer="threads"
117
118
  )(delayed(score_color)(best_dg, *pair) for pair in chunk)
118
119
  merge_loss, merge_dg = min(cand_results, key=lambda x: x[0])
119
- if merge_loss < best_loss:
120
+ if merge_loss < best_loss * (1 + allowed_loss_increase_percent):
120
121
  best_dg, best_loss = merge_dg, merge_loss
121
122
  improved = True
122
123
  break # move on to next outer iteration
@@ -164,6 +165,7 @@ def prune_num_swaps(
164
165
  *,
165
166
  fast: bool = True, # enable incremental search
166
167
  chunking_percent=0.05, # percentage of layers to process at once
168
+ allowed_loss_increase_percent=0.005, # percentage of loss increase allowed over best loss
167
169
  ) -> torch.Tensor:
168
170
  """Reduce the number of color boundaries until it is below or equal to
169
171
  *max_swaps_allowed*, stopping earlier if any further merge would worsen the
@@ -231,7 +233,7 @@ def prune_num_swaps(
231
233
  for band_a, band_b, direction in chunk
232
234
  )
233
235
  merge_loss, merge_dg = min(cand_results, key=lambda x: x[0])
234
- if merge_loss < best_loss:
236
+ if merge_loss < best_loss * (1 + allowed_loss_increase_percent):
235
237
  best_dg, best_loss = merge_dg, merge_loss
236
238
  improved = True
237
239
  break
@@ -389,6 +391,7 @@ def prune_redundant_layers(
389
391
  *,
390
392
  fast: bool = True, # NEW: enable 10 percent incremental search
391
393
  chunking_percent=0.05, # percentage of layers to process at once
394
+ allowed_loss_increase_percent=0.005, # percentage of loss increase allowed over best loss
392
395
  ):
393
396
  """Iteratively drop layers until the loss cannot be improved.
394
397
 
@@ -466,7 +469,7 @@ def prune_redundant_layers(
466
469
  cand_loss, cand_params, cand_max_layers = min(
467
470
  cand_results, key=lambda x: x[0]
468
471
  )
469
- if cand_loss <= best_loss:
472
+ if cand_loss <= best_loss * (1 + allowed_loss_increase_percent):
470
473
  removed_layers += 1
471
474
  best_loss = cand_loss
472
475
  current_max_layers = cand_max_layers
@@ -5,12 +5,6 @@ import matplotlib.pyplot as plt
5
5
  import numpy as np
6
6
  import torch
7
7
  from torch.utils.tensorboard import SummaryWriter
8
- from torchcam.methods import SmoothGradCAMpp
9
- from torchcam.utils import overlay_mask
10
- from torchvision import models
11
- from torchvision.models import ResNet18_Weights
12
- from torchvision.transforms._functional_tensor import resize, normalize
13
- from torchvision.transforms.functional import to_pil_image
14
8
  from tqdm import tqdm
15
9
 
16
10
  from autoforge.Helper.CAdamW import CAdamW
@@ -119,48 +113,48 @@ class FilamentOptimizer:
119
113
  lr=self.learning_rate,
120
114
  )
121
115
 
122
- # Setup the focus map component
123
- # Load a state-of-the-art ResNet50 model with pretrained weights.
124
- cam_model = models.resnet18(weights=ResNet18_Weights.DEFAULT).to(
125
- self.target.device
126
- )
127
- cam_model.eval()
128
- # Use ScoreCAM from torchcam on the 'layer4' block.
129
- cam_extractor = SmoothGradCAMpp(cam_model, target_layer="layer4")
130
- # Preprocess the target image for ResNet50.
131
- target_preprocessed = normalize(
132
- resize(self.target.permute(2, 0, 1), (224, 224)) / 255.0,
133
- [0.485, 0.456, 0.406],
134
- [0.229, 0.224, 0.225],
135
- )
136
- focus_maps = []
137
- for _ in tqdm(range(1), desc="Creating loss function attention map..."):
138
- out = cam_model(target_preprocessed.unsqueeze(0))
139
- # ScoreCAM returns a dict with keys corresponding to layers.
140
- focus_map = cam_extractor(out.squeeze(0).argmax().item(), out)[0]
141
- # Normalize the focus map to [0, 1]
142
- focus_map = (focus_map - focus_map.min()) / (
143
- focus_map.max() - focus_map.min() + 1e-8
144
- )
145
- focus_maps.append(focus_map)
146
- # get maximum over all maps
147
- self.focus_map = torch.stack(focus_maps)
148
- self.focus_map = self.focus_map.max(dim=0)[0]
149
- # normalize again
150
- self.focus_map = (self.focus_map - self.focus_map.min()) / (
151
- self.focus_map.max() - self.focus_map.min() + 1e-8
152
- )
153
-
154
- # resize back to target size, current dim is [1,h,w]
155
- self.focus_map = (
156
- torch.nn.functional.interpolate(
157
- self.focus_map.unsqueeze(0),
158
- (self.H, self.W),
159
- mode="bilinear",
160
- )
161
- .squeeze(0)
162
- .squeeze(0)
163
- )
116
+ # # Setup the focus map component
117
+ # # Load a state-of-the-art ResNet50 model with pretrained weights.
118
+ # cam_model = models.resnet18(weights=ResNet18_Weights.DEFAULT).to(
119
+ # self.target.device
120
+ # )
121
+ # cam_model.eval()
122
+ # # Use ScoreCAM from torchcam on the 'layer4' block.
123
+ # cam_extractor = SmoothGradCAMpp(cam_model, target_layer="layer4")
124
+ # # Preprocess the target image for ResNet50.
125
+ # target_preprocessed = normalize(
126
+ # resize(self.target.permute(2, 0, 1), (224, 224)) / 255.0,
127
+ # [0.485, 0.456, 0.406],
128
+ # [0.229, 0.224, 0.225],
129
+ # )
130
+ # focus_maps = []
131
+ # for _ in tqdm(range(1), desc="Creating loss function attention map..."):
132
+ # out = cam_model(target_preprocessed.unsqueeze(0))
133
+ # # ScoreCAM returns a dict with keys corresponding to layers.
134
+ # focus_map = cam_extractor(out.squeeze(0).argmax().item(), out)[0]
135
+ # # Normalize the focus map to [0, 1]
136
+ # focus_map = (focus_map - focus_map.min()) / (
137
+ # focus_map.max() - focus_map.min() + 1e-8
138
+ # )
139
+ # focus_maps.append(focus_map)
140
+ # # get maximum over all maps
141
+ # self.focus_map = torch.stack(focus_maps)
142
+ # self.focus_map = self.focus_map.max(dim=0)[0]
143
+ # # normalize again
144
+ # self.focus_map = (self.focus_map - self.focus_map.min()) / (
145
+ # self.focus_map.max() - self.focus_map.min() + 1e-8
146
+ # )
147
+ #
148
+ # # resize back to target size, current dim is [1,h,w]
149
+ # self.focus_map = (
150
+ # torch.nn.functional.interpolate(
151
+ # self.focus_map.unsqueeze(0),
152
+ # (self.H, self.W),
153
+ # mode="bilinear",
154
+ # )
155
+ # .squeeze(0)
156
+ # .squeeze(0)
157
+ # )
164
158
 
165
159
  # Setup best discrete solution tracking
166
160
  self.best_discrete_loss = float("inf")
@@ -202,17 +196,14 @@ class FilamentOptimizer:
202
196
  )
203
197
  self.ax[1, 1].set_title("Height Map Changes")
204
198
 
205
- target_vis = self.target.cpu().detach().numpy()
206
- focus_vis = self.focus_map.cpu().detach().numpy()
207
-
208
- overlay = overlay_mask(
209
- to_pil_image(target_vis),
210
- to_pil_image(focus_vis, mode="F"),
211
- alpha=0.5,
212
- )
213
-
214
- self.focus_map_ax = self.ax[1, 2].imshow(overlay)
215
- self.ax[1, 2].set_title("Image Focus Map (currently unused)")
199
+ # target_vis = self.target.cpu().detach().numpy()
200
+ # focus_vis = self.focus_map.cpu().detach().numpy()
201
+ #
202
+ # overlay = overlay_mask(
203
+ # to_pil_image(target_vis),
204
+ # to_pil_image(focus_vis, mode="F"),
205
+ # alpha=0.5,
206
+ # )
216
207
 
217
208
  # Compute and store the initial height map for later difference computation.
218
209
  with torch.no_grad():
@@ -286,7 +277,7 @@ class FilamentOptimizer:
286
277
  material_TDs=self.material_TDs,
287
278
  background=self.background,
288
279
  add_penalty_loss=10.0,
289
- focus_map=self.focus_map,
280
+ focus_map=None,
290
281
  focus_strength=0.0,
291
282
  )
292
283
 
@@ -609,9 +600,6 @@ class FilamentOptimizer:
609
600
  rng_seed=seed,
610
601
  )
611
602
 
612
- # 3) Compute the "discrete" loss function
613
- # If you have a separate function for that, just call it.
614
- # Otherwise, reuse the standard compute_loss with add_penalty_loss=False, etc.
615
603
  current_disc_loss = compute_loss(
616
604
  comp=comp_disc,
617
605
  target=self.target,
@@ -390,7 +390,7 @@ def main():
390
390
  for i in tbar:
391
391
  loss_val = optimizer.step(record_best=i % 8 == 0)
392
392
 
393
- optimizer.visualize(interval=25)
393
+ optimizer.visualize(interval=100)
394
394
  optimizer.log_to_tensorboard(interval=100)
395
395
 
396
396
  if (i + 1) % 100 == 0:
File without changes