autoforge 1.6.0__tar.gz → 1.6.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.6.0 → autoforge-1.6.2}/PKG-INFO +29 -13
  2. {autoforge-1.6.0 → autoforge-1.6.2}/README.md +28 -12
  3. {autoforge-1.6.0 → autoforge-1.6.2}/pyproject.toml +1 -1
  4. {autoforge-1.6.0 → autoforge-1.6.2}/src/AutoForge.egg-info/PKG-INFO +29 -13
  5. {autoforge-1.6.0 → autoforge-1.6.2}/src/autoforge/Init_comparer/auto_forge_init_check.py +50 -40
  6. {autoforge-1.6.0 → autoforge-1.6.2}/src/autoforge/Init_comparer/plot_results.py +53 -45
  7. {autoforge-1.6.0 → autoforge-1.6.2}/src/autoforge/Loss/LossFunctions.py +24 -28
  8. {autoforge-1.6.0 → autoforge-1.6.2}/src/autoforge/Modules/Optimizer.py +21 -2
  9. {autoforge-1.6.0 → autoforge-1.6.2}/src/autoforge/auto_forge.py +30 -15
  10. {autoforge-1.6.0 → autoforge-1.6.2}/setup.cfg +0 -0
  11. {autoforge-1.6.0 → autoforge-1.6.2}/src/AutoForge.egg-info/SOURCES.txt +0 -0
  12. {autoforge-1.6.0 → autoforge-1.6.2}/src/AutoForge.egg-info/dependency_links.txt +0 -0
  13. {autoforge-1.6.0 → autoforge-1.6.2}/src/AutoForge.egg-info/entry_points.txt +0 -0
  14. {autoforge-1.6.0 → autoforge-1.6.2}/src/AutoForge.egg-info/requires.txt +0 -0
  15. {autoforge-1.6.0 → autoforge-1.6.2}/src/AutoForge.egg-info/top_level.txt +0 -0
  16. {autoforge-1.6.0 → autoforge-1.6.2}/src/autoforge/Gui/__init__.py +0 -0
  17. {autoforge-1.6.0 → autoforge-1.6.2}/src/autoforge/Gui/browser_gui/Views/__init__.py +0 -0
  18. {autoforge-1.6.0 → autoforge-1.6.2}/src/autoforge/Gui/browser_gui/Views/backend_database.py +0 -0
  19. {autoforge-1.6.0 → autoforge-1.6.2}/src/autoforge/Gui/browser_gui/Views/filament_editor.py +0 -0
  20. {autoforge-1.6.0 → autoforge-1.6.2}/src/autoforge/Gui/browser_gui/app.py +0 -0
  21. {autoforge-1.6.0 → autoforge-1.6.2}/src/autoforge/Gui/browser_gui/main.py +0 -0
  22. {autoforge-1.6.0 → autoforge-1.6.2}/src/autoforge/Helper/CAdamW.py +0 -0
  23. {autoforge-1.6.0 → autoforge-1.6.2}/src/autoforge/Helper/FilamentHelper.py +0 -0
  24. {autoforge-1.6.0 → autoforge-1.6.2}/src/autoforge/Helper/Heightmaps/ChristofidesHeightMap.py +0 -0
  25. {autoforge-1.6.0 → autoforge-1.6.2}/src/autoforge/Helper/Heightmaps/DepthEstimateHeightMap.py +0 -0
  26. {autoforge-1.6.0 → autoforge-1.6.2}/src/autoforge/Helper/Heightmaps/OldHeightmap.py +0 -0
  27. {autoforge-1.6.0 → autoforge-1.6.2}/src/autoforge/Helper/Heightmaps/__init__.py +0 -0
  28. {autoforge-1.6.0 → autoforge-1.6.2}/src/autoforge/Helper/ImageHelper.py +0 -0
  29. {autoforge-1.6.0 → autoforge-1.6.2}/src/autoforge/Helper/OptimizerHelper.py +0 -0
  30. {autoforge-1.6.0 → autoforge-1.6.2}/src/autoforge/Helper/OutputHelper.py +0 -0
  31. {autoforge-1.6.0 → autoforge-1.6.2}/src/autoforge/Helper/PruningHelper.py +0 -0
  32. {autoforge-1.6.0 → autoforge-1.6.2}/src/autoforge/Helper/__init__.py +0 -0
  33. {autoforge-1.6.0 → autoforge-1.6.2}/src/autoforge/Helper/filamentcolors_library.py +0 -0
  34. {autoforge-1.6.0 → autoforge-1.6.2}/src/autoforge/Init_comparer/__init__.py +0 -0
  35. {autoforge-1.6.0 → autoforge-1.6.2}/src/autoforge/Loss/PerceptionLoss.py +0 -0
  36. {autoforge-1.6.0 → autoforge-1.6.2}/src/autoforge/Loss/__init__.py +0 -0
  37. {autoforge-1.6.0 → autoforge-1.6.2}/src/autoforge/Misc/__init__.py +0 -0
  38. {autoforge-1.6.0 → autoforge-1.6.2}/src/autoforge/Misc/color_fit_all.py +0 -0
  39. {autoforge-1.6.0 → autoforge-1.6.2}/src/autoforge/Modules/__init__.py +0 -0
  40. {autoforge-1.6.0 → autoforge-1.6.2}/src/autoforge/__init__.py +0 -0
  41. {autoforge-1.6.0 → autoforge-1.6.2}/src/autoforge/__main__.py +0 -0
@@ -1,6 +1,6 @@
1
- Metadata-Version: 2.2
1
+ Metadata-Version: 2.4
2
2
  Name: AutoForge
3
- Version: 1.6.0
3
+ Version: 1.6.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
@@ -48,10 +48,6 @@ The pruning is set to a maximum of 8 color and 20 swaps, so each image uses at m
48
48
  </div>
49
49
  </div>
50
50
 
51
- If someone uses this program and 3d prints something with it, please let me know :) \
52
- I would love to see what you made!
53
-
54
-
55
51
  ## Features
56
52
 
57
53
  - **Image-to-Model Conversion**: Converts an input image into a layered model suitable for 3D printing.
@@ -63,8 +59,23 @@ I would love to see what you made!
63
59
  - **Live Visualization**: (Optional) Displays live composite images during the optimization process.
64
60
  - **Hueforge export**: Outputs a project file that can be opened with hueforge.
65
61
 
62
+ ## [Google Colab Notebook](https://colab.research.google.com/drive/13sWCss9-ew2xnoJl4c4SSpComSNi-1ih)
63
+
64
+ If you're new to Google Colab, follow these simple steps to run the notebook:
65
+
66
+ 1. **Open the Notebook:**
67
+ Click [here](https://colab.research.google.com/drive/13sWCss9-ew2xnoJl4c4SSpComSNi-1ih) to open the notebook in Google Colab.
68
+
69
+ 2. **Select GPU Runtime:**
70
+ In Colab, go to the menu and click **Runtime → Change runtime type**. Under **Hardware accelerator**, choose **T4 GPU** (or any other available GPU type).
71
+
72
+ 3. **Run All Cells:**
73
+ Once the notebook loads, click **Runtime → Run all** to execute the cells in sequence. Follow the on-screen prompts to configure the materials CSV, upload your image, and run Autoforge.
66
74
 
67
- ## Installation
75
+ 4. **Review and Download:**
76
+ After the process completes, you'll see a preview of the output image. Finally, the notebook will zip the output folder for you to download.
77
+
78
+ ## Manual Installation
68
79
 
69
80
  To install AutoForge, simply install the current version from PyPI:
70
81
  ```bash
@@ -81,13 +92,18 @@ The script is run from the command line and accepts several arguments. Below is
81
92
  > **Note:** You will need [Hueforge](https://shop.thehueforge.com/) installed to export your filament CSV.
82
93
  > To get your CSV file, simply go to the "Filaments" menu in Hueforge, click the export button, select your filaments, and export them as a CSV file.
83
94
 
84
- > If you want to limit the amount of colors the program can use, you can simply delete the colors you don't want from the CSV file.
85
95
 
86
96
  ```bash
87
- autoforge --input_image path/to/input_image.jpg --csv_file path/to/materials.csv --output_folder outputs
97
+ autoforge --input_image path/to/input_image.jpg --csv_file path/to/materials.csv
98
+ ```
99
+
100
+ > If you want to limit the amount of colors the program can use, you can set these as command line arguments. \
101
+ > For Example: 8 colors and a maximum of 20 swaps:
102
+
103
+ ```bash
104
+ autoforge --input_image path/to/input_image.jpg --csv_file path/to/materials.csv --pruning_max_colors 8 --pruning_max_swaps 20
88
105
  ```
89
106
 
90
- ### Command Line Arguments
91
107
 
92
108
  ### Command Line Arguments
93
109
 
@@ -99,10 +115,10 @@ autoforge --input_image path/to/input_image.jpg --csv_file path/to/materials.csv
99
115
 
100
116
  - `--output_folder` Folder where output files will be saved (default: `output`).
101
117
  - `--iterations` Number of optimization iterations (default: 5000).
102
- - `--learning_rate` Learning rate for optimization (default: 1e-2).
118
+ - `--learning_rate` Learning rate for optimization (default: 0.015).
103
119
  - `--layer_height` Layer thickness in millimeters (default: 0.04).
104
- - `--max_layers` Maximum number of layers (default: 50).
105
- **Note:** This is about 2mm + the background height
120
+ - `--max_layers` Maximum number of layers (default: 75).
121
+ **Note:** This is about 3mm + the background height
106
122
  - `--background_height` Height of the background in millimeters (default: 0.4).
107
123
  **Note:** The background height must be divisible by the layer height.
108
124
  - `--background_color` Background color in hexadecimal format (default: `#000000` aka Black).
@@ -24,10 +24,6 @@ The pruning is set to a maximum of 8 color and 20 swaps, so each image uses at m
24
24
  </div>
25
25
  </div>
26
26
 
27
- If someone uses this program and 3d prints something with it, please let me know :) \
28
- I would love to see what you made!
29
-
30
-
31
27
  ## Features
32
28
 
33
29
  - **Image-to-Model Conversion**: Converts an input image into a layered model suitable for 3D printing.
@@ -39,8 +35,23 @@ I would love to see what you made!
39
35
  - **Live Visualization**: (Optional) Displays live composite images during the optimization process.
40
36
  - **Hueforge export**: Outputs a project file that can be opened with hueforge.
41
37
 
38
+ ## [Google Colab Notebook](https://colab.research.google.com/drive/13sWCss9-ew2xnoJl4c4SSpComSNi-1ih)
39
+
40
+ If you're new to Google Colab, follow these simple steps to run the notebook:
41
+
42
+ 1. **Open the Notebook:**
43
+ Click [here](https://colab.research.google.com/drive/13sWCss9-ew2xnoJl4c4SSpComSNi-1ih) to open the notebook in Google Colab.
44
+
45
+ 2. **Select GPU Runtime:**
46
+ In Colab, go to the menu and click **Runtime → Change runtime type**. Under **Hardware accelerator**, choose **T4 GPU** (or any other available GPU type).
47
+
48
+ 3. **Run All Cells:**
49
+ Once the notebook loads, click **Runtime → Run all** to execute the cells in sequence. Follow the on-screen prompts to configure the materials CSV, upload your image, and run Autoforge.
42
50
 
43
- ## Installation
51
+ 4. **Review and Download:**
52
+ After the process completes, you'll see a preview of the output image. Finally, the notebook will zip the output folder for you to download.
53
+
54
+ ## Manual Installation
44
55
 
45
56
  To install AutoForge, simply install the current version from PyPI:
46
57
  ```bash
@@ -57,13 +68,18 @@ The script is run from the command line and accepts several arguments. Below is
57
68
  > **Note:** You will need [Hueforge](https://shop.thehueforge.com/) installed to export your filament CSV.
58
69
  > To get your CSV file, simply go to the "Filaments" menu in Hueforge, click the export button, select your filaments, and export them as a CSV file.
59
70
 
60
- > If you want to limit the amount of colors the program can use, you can simply delete the colors you don't want from the CSV file.
61
71
 
62
72
  ```bash
63
- autoforge --input_image path/to/input_image.jpg --csv_file path/to/materials.csv --output_folder outputs
73
+ autoforge --input_image path/to/input_image.jpg --csv_file path/to/materials.csv
74
+ ```
75
+
76
+ > If you want to limit the amount of colors the program can use, you can set these as command line arguments. \
77
+ > For Example: 8 colors and a maximum of 20 swaps:
78
+
79
+ ```bash
80
+ autoforge --input_image path/to/input_image.jpg --csv_file path/to/materials.csv --pruning_max_colors 8 --pruning_max_swaps 20
64
81
  ```
65
82
 
66
- ### Command Line Arguments
67
83
 
68
84
  ### Command Line Arguments
69
85
 
@@ -75,10 +91,10 @@ autoforge --input_image path/to/input_image.jpg --csv_file path/to/materials.csv
75
91
 
76
92
  - `--output_folder` Folder where output files will be saved (default: `output`).
77
93
  - `--iterations` Number of optimization iterations (default: 5000).
78
- - `--learning_rate` Learning rate for optimization (default: 1e-2).
94
+ - `--learning_rate` Learning rate for optimization (default: 0.015).
79
95
  - `--layer_height` Layer thickness in millimeters (default: 0.04).
80
- - `--max_layers` Maximum number of layers (default: 50).
81
- **Note:** This is about 2mm + the background height
96
+ - `--max_layers` Maximum number of layers (default: 75).
97
+ **Note:** This is about 3mm + the background height
82
98
  - `--background_height` Height of the background in millimeters (default: 0.4).
83
99
  **Note:** The background height must be divisible by the layer height.
84
100
  - `--background_color` Background color in hexadecimal format (default: `#000000` aka Black).
@@ -178,4 +194,4 @@ Example Images: \
178
194
  <a href="https://www.vecteezy.com/free-photos/animal">Animal Stock photos by Vecteezy</a>
179
195
 
180
196
 
181
- Happy printing!
197
+ Happy printing!
@@ -4,7 +4,7 @@ build-backend = "setuptools.build_meta"
4
4
 
5
5
  [project]
6
6
  name = "AutoForge"
7
- version="1.6.0"
7
+ version="1.6.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
- Metadata-Version: 2.2
1
+ Metadata-Version: 2.4
2
2
  Name: AutoForge
3
- Version: 1.6.0
3
+ Version: 1.6.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
@@ -48,10 +48,6 @@ The pruning is set to a maximum of 8 color and 20 swaps, so each image uses at m
48
48
  </div>
49
49
  </div>
50
50
 
51
- If someone uses this program and 3d prints something with it, please let me know :) \
52
- I would love to see what you made!
53
-
54
-
55
51
  ## Features
56
52
 
57
53
  - **Image-to-Model Conversion**: Converts an input image into a layered model suitable for 3D printing.
@@ -63,8 +59,23 @@ I would love to see what you made!
63
59
  - **Live Visualization**: (Optional) Displays live composite images during the optimization process.
64
60
  - **Hueforge export**: Outputs a project file that can be opened with hueforge.
65
61
 
62
+ ## [Google Colab Notebook](https://colab.research.google.com/drive/13sWCss9-ew2xnoJl4c4SSpComSNi-1ih)
63
+
64
+ If you're new to Google Colab, follow these simple steps to run the notebook:
65
+
66
+ 1. **Open the Notebook:**
67
+ Click [here](https://colab.research.google.com/drive/13sWCss9-ew2xnoJl4c4SSpComSNi-1ih) to open the notebook in Google Colab.
68
+
69
+ 2. **Select GPU Runtime:**
70
+ In Colab, go to the menu and click **Runtime → Change runtime type**. Under **Hardware accelerator**, choose **T4 GPU** (or any other available GPU type).
71
+
72
+ 3. **Run All Cells:**
73
+ Once the notebook loads, click **Runtime → Run all** to execute the cells in sequence. Follow the on-screen prompts to configure the materials CSV, upload your image, and run Autoforge.
66
74
 
67
- ## Installation
75
+ 4. **Review and Download:**
76
+ After the process completes, you'll see a preview of the output image. Finally, the notebook will zip the output folder for you to download.
77
+
78
+ ## Manual Installation
68
79
 
69
80
  To install AutoForge, simply install the current version from PyPI:
70
81
  ```bash
@@ -81,13 +92,18 @@ The script is run from the command line and accepts several arguments. Below is
81
92
  > **Note:** You will need [Hueforge](https://shop.thehueforge.com/) installed to export your filament CSV.
82
93
  > To get your CSV file, simply go to the "Filaments" menu in Hueforge, click the export button, select your filaments, and export them as a CSV file.
83
94
 
84
- > If you want to limit the amount of colors the program can use, you can simply delete the colors you don't want from the CSV file.
85
95
 
86
96
  ```bash
87
- autoforge --input_image path/to/input_image.jpg --csv_file path/to/materials.csv --output_folder outputs
97
+ autoforge --input_image path/to/input_image.jpg --csv_file path/to/materials.csv
98
+ ```
99
+
100
+ > If you want to limit the amount of colors the program can use, you can set these as command line arguments. \
101
+ > For Example: 8 colors and a maximum of 20 swaps:
102
+
103
+ ```bash
104
+ autoforge --input_image path/to/input_image.jpg --csv_file path/to/materials.csv --pruning_max_colors 8 --pruning_max_swaps 20
88
105
  ```
89
106
 
90
- ### Command Line Arguments
91
107
 
92
108
  ### Command Line Arguments
93
109
 
@@ -99,10 +115,10 @@ autoforge --input_image path/to/input_image.jpg --csv_file path/to/materials.csv
99
115
 
100
116
  - `--output_folder` Folder where output files will be saved (default: `output`).
101
117
  - `--iterations` Number of optimization iterations (default: 5000).
102
- - `--learning_rate` Learning rate for optimization (default: 1e-2).
118
+ - `--learning_rate` Learning rate for optimization (default: 0.015).
103
119
  - `--layer_height` Layer thickness in millimeters (default: 0.04).
104
- - `--max_layers` Maximum number of layers (default: 50).
105
- **Note:** This is about 2mm + the background height
120
+ - `--max_layers` Maximum number of layers (default: 75).
121
+ **Note:** This is about 3mm + the background height
106
122
  - `--background_height` Height of the background in millimeters (default: 0.4).
107
123
  **Note:** The background height must be divisible by the layer height.
108
124
  - `--background_color` Background color in hexadecimal format (default: `#000000` aka Black).
@@ -24,15 +24,20 @@ class Config:
24
24
  csv_file = "default_materials.csv" # Path to CSV file with material data
25
25
  output_folder = "output"
26
26
  iterations = 2000
27
- learning_rate = 1e-2
27
+ learning_rate = 0.015
28
+
29
+ warmup_fraction = 0.1 #[0.0, 1.0]
30
+ height_logits_learning_start_fraction = 0.1 #[0.0, 1.0] but should be less than height_logits_learning_full_fraction
31
+ height_logits_learning_full_fraction = 0.5 # [0.0, 1.0] but should be more than height_logits_learning_start_fraction
32
+ init_tau = 1.0 # [0.0, 1.0] needs to be more than final_tau
33
+ final_tau = 0.01 # [0.0, 1.0] needs to be less than init_tau
34
+
28
35
  layer_height = 0.04
29
36
  max_layers = 75
30
37
  min_layers = 0
31
38
  background_height = 0.4
32
39
  background_color = "#000000"
33
40
  output_size = 128
34
- init_tau = 1.0
35
- final_tau = 0.01
36
41
  visualize = False
37
42
  stl_output_size = 200
38
43
  perform_pruning = True
@@ -52,12 +57,19 @@ class Config:
52
57
  tensorboard = False
53
58
 
54
59
 
55
- def main(input_image, csv_file, init_method, cluster_layers, lab_space):
56
- # Create config object using default values
60
+ def main(input_image, csv_file, warmup, h_start, h_full, tau_init, tau_final):
61
+ # Create config object using default values and override with given hyperparameters.
57
62
  args = Config()
58
63
  args.input_image = input_image
59
64
  args.csv_file = csv_file
60
65
 
66
+ # Set the hyperparameters for the grid search.
67
+ args.warmup_fraction = warmup
68
+ args.height_logits_learning_start_fraction = h_start
69
+ args.height_logits_learning_full_fraction = h_full
70
+ args.init_tau = tau_init
71
+ args.final_tau = tau_final
72
+
61
73
  if torch.cuda.is_available():
62
74
  device = torch.device("cuda")
63
75
  elif args.mps and torch.backends.mps.is_available():
@@ -128,9 +140,9 @@ def main(input_image, csv_file, init_method, cluster_layers, lab_space):
128
140
  args.layer_height,
129
141
  bgr_tuple,
130
142
  random_seed=random_seed,
131
- init_method=init_method,
132
- cluster_layers=cluster_layers,
133
- lab_space=lab_space,
143
+ init_method="kmeans",
144
+ cluster_layers=20,
145
+ lab_space=False,
134
146
  num_threads=8,
135
147
  )
136
148
 
@@ -186,47 +198,41 @@ def main(input_image, csv_file, init_method, cluster_layers, lab_space):
186
198
  return mse_loss.item()
187
199
 
188
200
 
189
- def main_suppressed(input_image, csv_file, init_method, cluster_layers, lab_space):
201
+ def main_suppressed(input_image, csv_file, warmup, h_start, h_full, tau_init, tau_final):
190
202
  with open(os.devnull, "w") as fnull:
191
203
  with redirect_stdout(fnull), redirect_stderr(fnull):
192
- result = main(input_image, csv_file, init_method, cluster_layers, lab_space)
204
+ result = main(input_image, csv_file, warmup, h_start, h_full, tau_init, tau_final)
193
205
  return result
194
206
 
195
207
 
196
208
  if __name__ == "__main__":
197
209
  folder = "../../../images/test_images/"
198
210
  csv_file = "../../../bambulab.csv"
199
- images = [folder + "/" + img for img in os.listdir(folder) if img.endswith(".jpg")]
200
- parallel_limit = os.cpu_count()
201
- methods = [
202
- "kmeans",
203
- "quantize_median",
204
- "quantize_maxcoverage",
205
- "quantize_fastoctree",
206
- ]
207
- max_layers = 75
208
- cluster_layers = [
209
- max_layers // 4,
210
- max_layers // 2,
211
- max_layers,
212
- max_layers * 2,
213
- max_layers * 4,
214
- ]
215
- use_lab_space = [True, False]
216
-
217
- # test every permutation using itertools
211
+ images = [os.path.join(folder, img) for img in os.listdir(folder) if img.endswith(".jpg")]
212
+ fixed_lr = 1e-1
213
+ parallel_limit = 10
214
+
215
+ # Define grid search parameters
216
+ warmup_values = np.linspace(0, 1, 5) # 5 values for warmup_fraction
217
+ height_values = np.linspace(0, 1, 5) # 5 values for height fractions
218
+ tau_values = np.linspace(0, 1, 5) # 5 values for tau
219
+
220
+ # Build valid pairs for height fractions (start < full)
221
+ height_pairs = [(hs, hf) for hs in height_values for hf in height_values if hs < hf]
222
+ # Build valid pairs for tau (init_tau > final_tau)
223
+ tau_pairs = [(ti, tf) for ti in tau_values for tf in tau_values if ti > tf]
224
+
225
+ # Create grid: warmup x height_pairs x tau_pairs = 5 * 10 * 10 = 500 combinations
218
226
  from itertools import product
227
+ hyperparam_grid = list(product(warmup_values, height_pairs, tau_pairs))
228
+ print(f"Total hyperparameter combinations: {len(hyperparam_grid)}")
219
229
 
220
230
  out_dict = {}
221
- do_list = list(product(methods, cluster_layers, use_lab_space))
222
- shuffle(do_list)
223
-
224
- for i, (method, cluster, lab) in enumerate(do_list):
231
+ # Loop over each hyperparameter combination
232
+ for idx, (warmup, (h_start, h_full), (tau_init, tau_final)) in enumerate(hyperparam_grid):
225
233
  try:
226
- out_dict_str = f"{method}_{cluster}_{lab}"
227
- print(
228
- f"Running {method} with {cluster} clusters and lab={lab}, {i + 1}/{len(do_list)}"
229
- )
234
+ out_dict_str = f"w={warmup}_height_start={h_start}_height_full={h_full}_tau_init={tau_init}_tau_final={tau_final}"
235
+ print(f"Running {idx+1}/{len(hyperparam_grid)}: {out_dict_str}")
230
236
  exec = ProcessPoolExecutor(max_workers=parallel_limit)
231
237
  tlist = []
232
238
  for img in images:
@@ -236,15 +242,19 @@ if __name__ == "__main__":
236
242
  main_suppressed,
237
243
  img,
238
244
  csv_file,
239
- method,
240
- cluster,
241
- lab,
245
+ warmup,
246
+ h_start,
247
+ h_full,
248
+ tau_init,
249
+ tau_final,
242
250
  )
243
251
  )
244
252
  for t in tqdm(concurrent.futures.as_completed(tlist), total=len(tlist)):
245
253
  result_list = out_dict.get(out_dict_str, [])
246
254
  result_list.append(t.result())
247
255
  out_dict[out_dict_str] = result_list
256
+
257
+ exec.shutdown()
248
258
  # save out_dict as json
249
259
  import json
250
260
 
@@ -14,10 +14,12 @@ for key, values in data.items():
14
14
  arr = np.array(values)
15
15
  mean_val = np.mean(arr)
16
16
  std_val = np.std(arr)
17
+ #key is formatted as lr=float, we need to extract the float value and round it to 4 decimal values
18
+ key = round(float(key.split("=")[1]), 5)
17
19
  stats.append((key, mean_val, std_val, arr))
18
20
 
19
21
  # Sort the list of tuples based on the mean (ascending order)
20
- stats.sort(key=lambda x: x[1])
22
+ #stats.sort(key=lambda x: x[1])
21
23
 
22
24
  # Extract sorted lists of keys, means, stds, and store the sorted arrays in a dictionary
23
25
  sorted_keys = [item[0] for item in stats]
@@ -53,7 +55,6 @@ fig.update_layout(
53
55
  barmode="group",
54
56
  )
55
57
 
56
-
57
58
  # Function to generate significance stars based on the p-value.
58
59
  def significance_label(p):
59
60
  if p < 0.001:
@@ -65,50 +66,57 @@ def significance_label(p):
65
66
  else:
66
67
  return ""
67
68
 
69
+ # First, compute all significant pairwise comparisons
70
+ significant_pairs = [] # will hold tuples of (p_value, i, j)
71
+ for i in range(n_keys):
72
+ for j in range(i + 1, n_keys):
73
+ group1 = data_sorted[sorted_keys[i]]
74
+ group2 = data_sorted[sorted_keys[j]]
75
+ stat, p_value = ttest_ind(group1, group2)
76
+ if p_value < 0.05:
77
+ significant_pairs.append((p_value, i, j))
78
+
79
+ # Sort pairs by p-value (most significant first)
80
+ significant_pairs.sort(key=lambda x: x[0])
81
+
82
+ # To ensure each bar is annotated only once, track used indices.
83
+ used_bars = set()
84
+ base_margin = max(means) * 0.05 # margin above the error bars
85
+
86
+ significant_pairs = []
87
+ for p_value, i, j in significant_pairs:
88
+ # If either bar is already annotated, skip this pair.
89
+ if i in used_bars or j in used_bars:
90
+ continue
91
+
92
+ # Compute the base y-level from the higher error bar of the two.
93
+ base_y = max(means[i] + stds[i], means[j] + stds[j])
94
+ line_y = base_y + base_margin
95
+
96
+ # Mark these bars as used.
97
+ used_bars.update([i, j])
98
+
99
+ # Draw horizontal significance line between the two bars.
100
+ fig.add_shape(
101
+ type="line",
102
+ x0=x_positions[i],
103
+ x1=x_positions[j],
104
+ y0=line_y,
105
+ y1=line_y,
106
+ line=dict(color="black", width=1),
107
+ xref="x",
108
+ yref="y",
109
+ )
110
+ # Place the significance annotation (stars) above the line.
111
+ fig.add_annotation(
112
+ x=(x_positions[i] + x_positions[j]) / 2,
113
+ y=line_y + base_margin * 0.2,
114
+ text=significance_label(p_value),
115
+ showarrow=False,
116
+ font=dict(size=12),
117
+ )
118
+
68
119
 
69
- # To avoid overlapping significance annotations when bars share a common index,
70
- # keep track of the highest annotation drawn for each bar.
71
- annotation_levels = {i: 0 for i in range(n_keys)}
72
- # Base margin added above the highest error bar to start the significance line.
73
- base_margin = max(means) * 0.05 # Adjust as needed
74
-
75
- # Loop over adjacent pairs in the sorted order and perform t-tests.
76
- for i in range(n_keys - 1):
77
- group1 = data_sorted[sorted_keys[i]]
78
- group2 = data_sorted[sorted_keys[i + 1]]
79
- stat, p_value = ttest_ind(group1, group2)
80
-
81
- # Only annotate if the difference is significant (p < 0.05)
82
- if p_value < 0.05:
83
- # Determine the base y-level from the taller of the two error bars.
84
- base_y = max(means[i] + stds[i], means[i + 1] + stds[i + 1])
85
- # Ensure new annotations are drawn above any previous ones.
86
- current_level = max(annotation_levels[i], annotation_levels[i + 1])
87
- line_y = base_y + base_margin + current_level
88
-
89
- # Update annotation levels for these two bars.
90
- annotation_levels[i] = line_y
91
- annotation_levels[i + 1] = line_y
92
-
93
- # Add a horizontal line between the two bars.
94
- fig.add_shape(
95
- type="line",
96
- x0=x_positions[i],
97
- x1=x_positions[i + 1],
98
- y0=line_y,
99
- y1=line_y,
100
- line=dict(color="black", width=1),
101
- xref="x",
102
- yref="y",
103
- )
104
- # Add the significance stars annotation above the line.
105
- fig.add_annotation(
106
- x=(x_positions[i] + x_positions[i + 1]) / 2,
107
- y=line_y + base_margin * 0.2,
108
- text=significance_label(p_value),
109
- showarrow=False,
110
- font=dict(size=12),
111
- )
112
120
  fig.update_layout(
113
121
  autosize=False,
114
122
  width=1920,
@@ -50,46 +50,42 @@ def compute_loss(
50
50
  add_penalty_loss: float = 0.0,
51
51
  ) -> torch.Tensor:
52
52
  """
53
- Combined MSE + Perceptual + penalty losses.
53
+ Combined MSE + Perceptual + penalty losses with patch-based smoothness.
54
54
  """
55
- # MSE
55
+ # MSE Loss
56
56
  comp_mse = srgb_to_lab(comp)
57
- # we slightly increase saturation of our target image to make the color matching more robust
58
- # target = increase_saturation(target, 0.1)
59
57
  target_mse = srgb_to_lab(target)
60
58
  mse_loss = F.huber_loss(comp_mse, target_mse)
61
59
 
62
60
  if pixel_height_logits is not None:
61
+ # Existing neighbor-based smoothness loss:
63
62
  target_gray = target.mean(dim=2) # shape becomes [H, W]
64
-
65
- # Compute weights using the grayscale image
66
- weight_x = torch.exp(
67
- -torch.abs(target_gray[:, 1:] - target_gray[:, :-1])
68
- ) # shape [H, W-1]
69
- weight_y = torch.exp(
70
- -torch.abs(target_gray[1:, :] - target_gray[:-1, :])
71
- ) # shape [H-1, W]
72
-
63
+ weight_x = torch.exp(-torch.abs(target_gray[:, 1:] - target_gray[:, :-1]))
64
+ weight_y = torch.exp(-torch.abs(target_gray[1:, :] - target_gray[:-1, :]))
73
65
  weight_x = torch.clamp(weight_x, 0.5, 1.0)
74
66
  weight_y = torch.clamp(weight_y, 0.5, 1.0)
75
-
76
- # Compute differences in the depth map
77
- dx = torch.abs(
78
- pixel_height_logits[:, 1:] - pixel_height_logits[:, :-1]
79
- ) # shape [H, W-1]
80
- dy = torch.abs(
81
- pixel_height_logits[1:, :] - pixel_height_logits[:-1, :]
82
- ) # shape [H-1, W]
83
-
84
- # provide a target tensor (zeros) for the huber loss.
67
+ dx = torch.abs(pixel_height_logits[:, 1:] - pixel_height_logits[:, :-1])
68
+ dy = torch.abs(pixel_height_logits[1:, :] - pixel_height_logits[:-1, :])
85
69
  loss_dx = torch.mean(F.huber_loss(dx * weight_x, torch.zeros_like(dx)))
86
70
  loss_dy = torch.mean(F.huber_loss(dy * weight_y, torch.zeros_like(dy)))
87
- # loss_dx = torch.mean(F.huber_loss(dx, torch.zeros_like(dx)))
88
- # loss_dy = torch.mean(F.huber_loss(dy, torch.zeros_like(dy)))
71
+ smoothness_loss = (loss_dx + loss_dy) * (10 * add_penalty_loss)
89
72
 
90
- smoothness_loss = (loss_dx + loss_dy) * (20 * add_penalty_loss)
91
- # print(f"mse_loss: {mse_loss}, smoothness_loss: {smoothness_loss}")
92
- total_loss = mse_loss + smoothness_loss
73
+ # Additional patch-based smoothness loss (using a 3x3 Laplacian):
74
+ laplacian_kernel = (
75
+ torch.tensor(
76
+ [[0, 1, 0], [1, -4, 1], [0, 1, 0]],
77
+ dtype=pixel_height_logits.dtype,
78
+ device=pixel_height_logits.device,
79
+ )
80
+ .unsqueeze(0)
81
+ .unsqueeze(0)
82
+ )
83
+ height_map = pixel_height_logits.unsqueeze(0).unsqueeze(0)
84
+ laplacian_output = F.conv2d(height_map, laplacian_kernel, padding=1)
85
+ patch_smooth_loss = (
86
+ F.huber_loss(laplacian_output, torch.zeros_like(laplacian_output)) * 10
87
+ )
88
+ total_loss = mse_loss + smoothness_loss + add_penalty_loss * patch_smooth_loss
93
89
  else:
94
90
  total_loss = mse_loss
95
91
  return total_loss
@@ -95,7 +95,7 @@ class FilamentOptimizer:
95
95
 
96
96
  # Tau schedule
97
97
  self.num_steps_done = 0
98
- self.warmup_steps = args.iterations // 10
98
+ self.warmup_steps = min(args.iterations-1,args.warmup_fraction * args.iterations)
99
99
  self.decay_rate = (self.init_tau - self.final_tau) / (
100
100
  args.iterations - self.warmup_steps
101
101
  )
@@ -197,6 +197,18 @@ class FilamentOptimizer:
197
197
  / (self.args.iterations // 5),
198
198
  )
199
199
 
200
+ #start_fraction = self.args.height_logits_learning_start_fraction
201
+ #full_fraction = self.args.height_logits_learning_full_fraction
202
+
203
+ # total_iters = self.args.iterations
204
+ # current_step = self.num_steps_done
205
+ # if current_step < start_fraction * total_iters:
206
+ # scaling = 0.0
207
+ # elif current_step < full_fraction * total_iters:
208
+ # scaling = (current_step - start_fraction * total_iters) / ((full_fraction - start_fraction) * total_iters)
209
+ # else:
210
+ # scaling = 1.0
211
+
200
212
  loss = loss_fn(
201
213
  self.params,
202
214
  target=self.target,
@@ -208,10 +220,17 @@ class FilamentOptimizer:
208
220
  material_TDs=self.material_TDs,
209
221
  background=self.background,
210
222
  perception_loss_module=self.perception_loss_module,
211
- add_penalty_loss=penalty_loss,
223
+ add_penalty_loss=1.0,
212
224
  )
213
225
 
214
226
  loss.backward()
227
+
228
+ # We scale the gradients for the height logits by a factor of 'scaling' to force no updates in the beginning
229
+ # and allow stronger and stronger updates as we go along.
230
+ if self.params["pixel_height_logits"].grad is not None:
231
+ self.params["pixel_height_logits"].grad.mul_(1e-8)
232
+
233
+
215
234
  self.optimizer.step()
216
235
 
217
236
  self.num_steps_done += 1
@@ -45,10 +45,38 @@ def main():
45
45
  "--iterations", type=int, default=5000, help="Number of optimization iterations"
46
46
  )
47
47
 
48
+ parser.add_argument(
49
+ "--warmup_fraction",
50
+ type=float,
51
+ default=1.0,
52
+ help="Fraction of iterations for keeping the tau at the initial value",
53
+ )
54
+ #
55
+ # parser.add_argument(
56
+ # "--height_logits_learning_start_fraction", type=float, default=0.75, help="Fraction of iterations at which we start to learn the height map"
57
+ # )
58
+ #
59
+ # parser.add_argument(
60
+ # "--height_logits_learning_full_fraction", type=float, default=1.0, help="Fraction of iterations at which the height map learning is fully enabled"
61
+ # )
62
+
63
+ parser.add_argument(
64
+ "--init_tau",
65
+ type=float,
66
+ default=1.0,
67
+ help="Initial tau value for Gumbel-Softmax",
68
+ )
69
+ parser.add_argument(
70
+ "--final_tau",
71
+ type=float,
72
+ default=0.01,
73
+ help="Final tau value for Gumbel-Softmax",
74
+ )
75
+
48
76
  parser.add_argument(
49
77
  "--learning_rate",
50
78
  type=float,
51
- default=1e-2,
79
+ default=0.015,
52
80
  help="Learning rate for optimization",
53
81
  )
54
82
  parser.add_argument(
@@ -73,19 +101,6 @@ def main():
73
101
  "--background_color", type=str, default="#000000", help="Background color"
74
102
  )
75
103
 
76
- parser.add_argument(
77
- "--init_tau",
78
- type=float,
79
- default=1.0,
80
- help="Initial tau value for Gumbel-Softmax",
81
- )
82
- parser.add_argument(
83
- "--final_tau",
84
- type=float,
85
- default=0.01,
86
- help="Final tau value for Gumbel-Softmax",
87
- )
88
-
89
104
  parser.add_argument(
90
105
  "--visualize",
91
106
  action="store_true",
@@ -289,7 +304,7 @@ def main():
289
304
  random_seed=random_seed,
290
305
  num_threads=16,
291
306
  init_method="kmeans",
292
- cluster_layers=300,
307
+ cluster_layers=18,
293
308
  lab_space=True,
294
309
  )
295
310
 
File without changes