autoforge 1.7.0__tar.gz → 1.7.2__tar.gz

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (41) hide show
  1. {autoforge-1.7.0 → autoforge-1.7.2}/PKG-INFO +21 -2
  2. {autoforge-1.7.0 → autoforge-1.7.2}/README.md +20 -1
  3. {autoforge-1.7.0 → autoforge-1.7.2}/pyproject.toml +1 -1
  4. {autoforge-1.7.0 → autoforge-1.7.2}/src/AutoForge.egg-info/PKG-INFO +21 -2
  5. {autoforge-1.7.0 → autoforge-1.7.2}/src/autoforge/Helper/ImageHelper.py +16 -0
  6. {autoforge-1.7.0 → autoforge-1.7.2}/src/autoforge/Helper/OutputHelper.py +5 -3
  7. {autoforge-1.7.0 → autoforge-1.7.2}/src/autoforge/Modules/Optimizer.py +61 -69
  8. {autoforge-1.7.0 → autoforge-1.7.2}/src/autoforge/auto_forge.py +14 -4
  9. {autoforge-1.7.0 → autoforge-1.7.2}/setup.cfg +0 -0
  10. {autoforge-1.7.0 → autoforge-1.7.2}/src/AutoForge.egg-info/SOURCES.txt +0 -0
  11. {autoforge-1.7.0 → autoforge-1.7.2}/src/AutoForge.egg-info/dependency_links.txt +0 -0
  12. {autoforge-1.7.0 → autoforge-1.7.2}/src/AutoForge.egg-info/entry_points.txt +0 -0
  13. {autoforge-1.7.0 → autoforge-1.7.2}/src/AutoForge.egg-info/requires.txt +0 -0
  14. {autoforge-1.7.0 → autoforge-1.7.2}/src/AutoForge.egg-info/top_level.txt +0 -0
  15. {autoforge-1.7.0 → autoforge-1.7.2}/src/autoforge/Gui/__init__.py +0 -0
  16. {autoforge-1.7.0 → autoforge-1.7.2}/src/autoforge/Gui/browser_gui/Views/__init__.py +0 -0
  17. {autoforge-1.7.0 → autoforge-1.7.2}/src/autoforge/Gui/browser_gui/Views/backend_database.py +0 -0
  18. {autoforge-1.7.0 → autoforge-1.7.2}/src/autoforge/Gui/browser_gui/Views/filament_editor.py +0 -0
  19. {autoforge-1.7.0 → autoforge-1.7.2}/src/autoforge/Gui/browser_gui/app.py +0 -0
  20. {autoforge-1.7.0 → autoforge-1.7.2}/src/autoforge/Gui/browser_gui/main.py +0 -0
  21. {autoforge-1.7.0 → autoforge-1.7.2}/src/autoforge/Helper/CAdamW.py +0 -0
  22. {autoforge-1.7.0 → autoforge-1.7.2}/src/autoforge/Helper/FilamentHelper.py +0 -0
  23. {autoforge-1.7.0 → autoforge-1.7.2}/src/autoforge/Helper/Heightmaps/ChristofidesHeightMap.py +0 -0
  24. {autoforge-1.7.0 → autoforge-1.7.2}/src/autoforge/Helper/Heightmaps/DepthEstimateHeightMap.py +0 -0
  25. {autoforge-1.7.0 → autoforge-1.7.2}/src/autoforge/Helper/Heightmaps/OldHeightmap.py +0 -0
  26. {autoforge-1.7.0 → autoforge-1.7.2}/src/autoforge/Helper/Heightmaps/__init__.py +0 -0
  27. {autoforge-1.7.0 → autoforge-1.7.2}/src/autoforge/Helper/OptimizerHelper.py +0 -0
  28. {autoforge-1.7.0 → autoforge-1.7.2}/src/autoforge/Helper/PruningHelper.py +0 -0
  29. {autoforge-1.7.0 → autoforge-1.7.2}/src/autoforge/Helper/__init__.py +0 -0
  30. {autoforge-1.7.0 → autoforge-1.7.2}/src/autoforge/Helper/filamentcolors_library.py +0 -0
  31. {autoforge-1.7.0 → autoforge-1.7.2}/src/autoforge/Init_comparer/__init__.py +0 -0
  32. {autoforge-1.7.0 → autoforge-1.7.2}/src/autoforge/Init_comparer/auto_forge_init_check.py +0 -0
  33. {autoforge-1.7.0 → autoforge-1.7.2}/src/autoforge/Init_comparer/plot_results.py +0 -0
  34. {autoforge-1.7.0 → autoforge-1.7.2}/src/autoforge/Loss/LossFunctions.py +0 -0
  35. {autoforge-1.7.0 → autoforge-1.7.2}/src/autoforge/Loss/PerceptionLoss.py +0 -0
  36. {autoforge-1.7.0 → autoforge-1.7.2}/src/autoforge/Loss/__init__.py +0 -0
  37. {autoforge-1.7.0 → autoforge-1.7.2}/src/autoforge/Misc/__init__.py +0 -0
  38. {autoforge-1.7.0 → autoforge-1.7.2}/src/autoforge/Misc/color_fit_all.py +0 -0
  39. {autoforge-1.7.0 → autoforge-1.7.2}/src/autoforge/Modules/__init__.py +0 -0
  40. {autoforge-1.7.0 → autoforge-1.7.2}/src/autoforge/__init__.py +0 -0
  41. {autoforge-1.7.0 → autoforge-1.7.2}/src/autoforge/__main__.py +0 -0
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.4
2
2
  Name: AutoForge
3
- Version: 1.7.0
3
+ Version: 1.7.2
4
4
  Summary: AutoForge is a Python tool for generating 3D printed layered models from an input image.
5
5
  Author-email: Hendric Voss <hvoss@techfak.uni-bielefeld.de>
6
6
  License: CC BY-NC-SA 4.0
@@ -177,10 +177,29 @@ After running, the following files will be created in your specified output fold
177
177
  - **Hueforge Project File**: `project_file.hfp`
178
178
  - **Swap Instructions**: `swap_instructions.txt`
179
179
 
180
+ ## Development
181
+
182
+ To have a "nightly" version of the repository or have live updating changes during development please do the following:
183
+
184
+ ```bash
185
+ git clone https://github.com/hvoss-techfak/AutoForge.git
186
+ cd AutoForge
187
+ conda create -n forge python=3.11
188
+ conda activate forge
189
+ pip install -e .
190
+ ```
191
+
192
+ If the installed pytorch version has no cuda support execute the following:
193
+
194
+ ```bash
195
+ conda activate forge
196
+ pip install torch torchvision torchaudio --index-url https://download.pytorch.org/whl/cu118
197
+ ```
198
+
199
+
180
200
  ## Known Bugs
181
201
 
182
202
  - The optimizer can sometimes get stuck in a local minimum. If this happens, try running the optimization again with different settings.
183
- - Hueforge can't open STL files under linux. This is a known bug in hueforge.
184
203
 
185
204
  ## License
186
205
 
@@ -151,10 +151,29 @@ After running, the following files will be created in your specified output fold
151
151
  - **Hueforge Project File**: `project_file.hfp`
152
152
  - **Swap Instructions**: `swap_instructions.txt`
153
153
 
154
+ ## Development
155
+
156
+ To have a "nightly" version of the repository or have live updating changes during development please do the following:
157
+
158
+ ```bash
159
+ git clone https://github.com/hvoss-techfak/AutoForge.git
160
+ cd AutoForge
161
+ conda create -n forge python=3.11
162
+ conda activate forge
163
+ pip install -e .
164
+ ```
165
+
166
+ If the installed pytorch version has no cuda support execute the following:
167
+
168
+ ```bash
169
+ conda activate forge
170
+ pip install torch torchvision torchaudio --index-url https://download.pytorch.org/whl/cu118
171
+ ```
172
+
173
+
154
174
  ## Known Bugs
155
175
 
156
176
  - The optimizer can sometimes get stuck in a local minimum. If this happens, try running the optimization again with different settings.
157
- - Hueforge can't open STL files under linux. This is a known bug in hueforge.
158
177
 
159
178
  ## License
160
179
 
@@ -4,7 +4,7 @@ build-backend = "setuptools.build_meta"
4
4
 
5
5
  [project]
6
6
  name = "AutoForge"
7
- version="1.7.0"
7
+ version="1.7.2"
8
8
  description = "AutoForge is a Python tool for generating 3D printed layered models from an input image."
9
9
  authors = [{ name = "Hendric Voss", email = "hvoss@techfak.uni-bielefeld.de" }]
10
10
  readme = "README.md"
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.4
2
2
  Name: AutoForge
3
- Version: 1.7.0
3
+ Version: 1.7.2
4
4
  Summary: AutoForge is a Python tool for generating 3D printed layered models from an input image.
5
5
  Author-email: Hendric Voss <hvoss@techfak.uni-bielefeld.de>
6
6
  License: CC BY-NC-SA 4.0
@@ -177,10 +177,29 @@ After running, the following files will be created in your specified output fold
177
177
  - **Hueforge Project File**: `project_file.hfp`
178
178
  - **Swap Instructions**: `swap_instructions.txt`
179
179
 
180
+ ## Development
181
+
182
+ To have a "nightly" version of the repository or have live updating changes during development please do the following:
183
+
184
+ ```bash
185
+ git clone https://github.com/hvoss-techfak/AutoForge.git
186
+ cd AutoForge
187
+ conda create -n forge python=3.11
188
+ conda activate forge
189
+ pip install -e .
190
+ ```
191
+
192
+ If the installed pytorch version has no cuda support execute the following:
193
+
194
+ ```bash
195
+ conda activate forge
196
+ pip install torch torchvision torchaudio --index-url https://download.pytorch.org/whl/cu118
197
+ ```
198
+
199
+
180
200
  ## Known Bugs
181
201
 
182
202
  - The optimizer can sometimes get stuck in a local minimum. If this happens, try running the optimization again with different settings.
183
- - Hueforge can't open STL files under linux. This is a known bug in hueforge.
184
203
 
185
204
  ## License
186
205
 
@@ -1,5 +1,21 @@
1
+ import os
2
+ from typing import Sequence
3
+
1
4
  import cv2
2
5
  import torch
6
+ from cv2.typing import MatLike
7
+ import numpy as np
8
+
9
+
10
+ def imread(filename: str, flags: int = cv2.IMREAD_COLOR) -> MatLike:
11
+ return cv2.imdecode(np.fromfile(filename, dtype=np.uint8), flags)
12
+
13
+
14
+ def imwrite(filename: str, img: MatLike, params: Sequence[int] = ()) -> None:
15
+ success, encoded_img = cv2.imencode(os.path.splitext(filename)[1], img, params)
16
+ if not success:
17
+ raise OSError(f"cv2 could not write to path {filename}")
18
+ encoded_img.tofile(filename)
3
19
 
4
20
 
5
21
  def resize_image(img, max_size):
@@ -98,12 +98,14 @@ def generate_project_file(
98
98
  "Color": mat[" Color"],
99
99
  "Name": mat[" Name"],
100
100
  # Convert Owned to a boolean (in case it is read as a string)
101
- "Owned": str(mat[" Owned"]).strip().lower() == "true",
101
+ "Owned": str(mat[" Owned"]).strip().lower() == "true"
102
+ if " Owned" in mat
103
+ else False,
102
104
  "Transmissivity": float(mat[" TD"])
103
105
  if not float(mat[" TD"]).is_integer()
104
106
  else int(mat[" TD"]),
105
- "Type": mat[" Type"],
106
- "uuid": mat[" Uuid"],
107
+ "Type": mat[" Type"] if " Type" in mat else "PLA",
108
+ "uuid": mat[" Uuid"] if " Uuid" in mat else str(uuid.uuid4()),
107
109
  }
108
110
  filament_set.append(filament_entry)
109
111
 
@@ -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")
@@ -171,7 +165,8 @@ class FilamentOptimizer:
171
165
 
172
166
  # If you want a figure for real-time visualization:
173
167
  if self.visualize_flag:
174
- plt.ion()
168
+ if self.args.disable_visualization_for_gradio != 1:
169
+ plt.ion()
175
170
  self.fig, self.ax = plt.subplots(2, 3, figsize=(14, 6))
176
171
 
177
172
  self.target_im_ax = self.ax[0, 0].imshow(
@@ -188,7 +183,8 @@ class FilamentOptimizer:
188
183
  np.zeros((self.H, self.W, 3), dtype=np.uint8)
189
184
  )
190
185
  self.ax[0, 2].set_title("Best Discrete Composite")
191
- plt.pause(0.1)
186
+ if self.args.disable_visualization_for_gradio != 1:
187
+ plt.pause(0.1)
192
188
 
193
189
  self.depth_map_ax = self.ax[1, 0].imshow(
194
190
  np.zeros((self.H, self.W), dtype=np.uint8), cmap="viridis"
@@ -200,17 +196,14 @@ class FilamentOptimizer:
200
196
  )
201
197
  self.ax[1, 1].set_title("Height Map Changes")
202
198
 
203
- target_vis = self.target.cpu().detach().numpy()
204
- focus_vis = self.focus_map.cpu().detach().numpy()
205
-
206
- overlay = overlay_mask(
207
- to_pil_image(target_vis),
208
- to_pil_image(focus_vis, mode="F"),
209
- alpha=0.5,
210
- )
211
-
212
- self.focus_map_ax = self.ax[1, 2].imshow(overlay)
213
- 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
+ # )
214
207
 
215
208
  # Compute and store the initial height map for later difference computation.
216
209
  with torch.no_grad():
@@ -284,7 +277,7 @@ class FilamentOptimizer:
284
277
  material_TDs=self.material_TDs,
285
278
  background=self.background,
286
279
  add_penalty_loss=10.0,
287
- focus_map=self.focus_map,
280
+ focus_map=None,
288
281
  focus_strength=0.0,
289
282
  )
290
283
 
@@ -445,10 +438,11 @@ class FilamentOptimizer:
445
438
  self.diff_depth_map_ax.set_clim(-2.5, 2.5)
446
439
 
447
440
  self.fig.suptitle(
448
- f"Step {self.num_steps_done}, Tau: {tau_g:.4f}, Loss: {self.loss:.4f}, Best Discrete Loss: {self.best_discrete_loss:.4f}"
441
+ f"Step {self.num_steps_done}/{self.args.iterations}, Tau: {tau_g:.4f}, Loss: {self.loss:.4f}, Best Discrete Loss: {self.best_discrete_loss:.4f}"
449
442
  )
450
-
451
- plt.pause(0.01)
443
+ if self.args.disable_visualization_for_gradio != 1:
444
+ plt.pause(0.01)
445
+ plt.savefig(self.args.output_folder + "/vis_temp.png")
452
446
 
453
447
  def get_current_parameters(self):
454
448
  """
@@ -544,8 +538,9 @@ class FilamentOptimizer:
544
538
  prune_num_swaps,
545
539
  prune_redundant_layers,
546
540
  )
541
+
547
542
  if search_seed:
548
- self.rng_seed_search(self.best_discrete_loss, 1000, autoset_seed=True)
543
+ self.rng_seed_search(self.best_discrete_loss, 100, autoset_seed=True)
549
544
 
550
545
  prune_num_colors(
551
546
  self,
@@ -605,9 +600,6 @@ class FilamentOptimizer:
605
600
  rng_seed=seed,
606
601
  )
607
602
 
608
- # 3) Compute the "discrete" loss function
609
- # If you have a separate function for that, just call it.
610
- # Otherwise, reuse the standard compute_loss with add_penalty_loss=False, etc.
611
603
  current_disc_loss = compute_loss(
612
604
  comp=comp_disc,
613
605
  target=self.target,
@@ -1,3 +1,4 @@
1
+ import argparse
1
2
  import sys
2
3
  import os
3
4
  import time
@@ -15,7 +16,7 @@ from autoforge.Helper.Heightmaps.DepthEstimateHeightMap import (
15
16
  init_height_map_depth_color_adjusted,
16
17
  )
17
18
 
18
- from autoforge.Helper.ImageHelper import resize_image
19
+ from autoforge.Helper.ImageHelper import resize_image, imread
19
20
  from autoforge.Helper.OutputHelper import (
20
21
  generate_stl,
21
22
  generate_swap_instructions,
@@ -120,6 +121,7 @@ def main():
120
121
  type=bool,
121
122
  default=True,
122
123
  help="Enable visualization during optimization",
124
+ action=argparse.BooleanOptionalAction,
123
125
  )
124
126
 
125
127
  # Instead of an output_size parameter, we use stl_output_size and nozzle_diameter.
@@ -147,12 +149,14 @@ def main():
147
149
  type=bool,
148
150
  default=True,
149
151
  help="Perform pruning after optimization",
152
+ action=argparse.BooleanOptionalAction,
150
153
  )
151
154
  parser.add_argument(
152
155
  "--fast_pruning",
153
156
  type=bool,
154
157
  default=True,
155
158
  help="Use fast pruning method",
159
+ action=argparse.BooleanOptionalAction,
156
160
  )
157
161
  parser.add_argument(
158
162
  "--fast_pruning_percent",
@@ -242,7 +246,7 @@ def main():
242
246
  parser.add_argument(
243
247
  "--num_init_rounds",
244
248
  type=int,
245
- default=64,
249
+ default=128,
246
250
  help="Number of rounds to choose the starting height map from.",
247
251
  )
248
252
  parser.add_argument(
@@ -251,6 +255,12 @@ def main():
251
255
  default=-1,
252
256
  help="Number of layers to cluster the image into.",
253
257
  )
258
+ parser.add_argument(
259
+ "--disable_visualization_for_gradio",
260
+ type=int,
261
+ default=0,
262
+ help="Simple switch to disable the matplotlib render window for gradio rendering.",
263
+ )
254
264
 
255
265
  args = parser.parse_args()
256
266
  if args.num_init_cluster_layers == -1:
@@ -302,7 +312,7 @@ def main():
302
312
  material_TDs = torch.tensor(material_TDs_np, dtype=torch.float32, device=device)
303
313
 
304
314
  # Read input image
305
- img = cv2.imread(args.input_image, cv2.IMREAD_UNCHANGED)
315
+ img = imread(args.input_image, cv2.IMREAD_UNCHANGED)
306
316
  computed_output_size = int(round(args.stl_output_size * 2 / args.nozzle_diameter))
307
317
  print(f"Computed solving pixel size: {computed_output_size}")
308
318
  alpha = None
@@ -380,7 +390,7 @@ def main():
380
390
  for i in tbar:
381
391
  loss_val = optimizer.step(record_best=i % 8 == 0)
382
392
 
383
- optimizer.visualize(interval=25)
393
+ optimizer.visualize(interval=100)
384
394
  optimizer.log_to_tensorboard(interval=100)
385
395
 
386
396
  if (i + 1) % 100 == 0:
File without changes