autoforge 1.1.3__tar.gz → 1.2.0__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 (30) hide show
  1. {autoforge-1.1.3 → autoforge-1.2.0}/PKG-INFO +22 -10
  2. {autoforge-1.1.3 → autoforge-1.2.0}/README.md +19 -5
  3. {autoforge-1.1.3 → autoforge-1.2.0}/pyproject.toml +3 -8
  4. {autoforge-1.1.3 → autoforge-1.2.0}/src/AutoForge.egg-info/PKG-INFO +22 -10
  5. {autoforge-1.1.3 → autoforge-1.2.0}/src/AutoForge.egg-info/SOURCES.txt +12 -1
  6. {autoforge-1.1.3 → autoforge-1.2.0}/src/AutoForge.egg-info/requires.txt +2 -5
  7. autoforge-1.2.0/src/autoforge/Helper/FilamentHelper.py +127 -0
  8. autoforge-1.2.0/src/autoforge/Helper/ImageHelper.py +13 -0
  9. autoforge-1.2.0/src/autoforge/Helper/OptimizerHelper.py +333 -0
  10. autoforge-1.2.0/src/autoforge/Helper/OutputHelper.py +341 -0
  11. autoforge-1.2.0/src/autoforge/Helper/PruningHelper.py +320 -0
  12. autoforge-1.2.0/src/autoforge/Loss/LossFunctions.py +98 -0
  13. autoforge-1.2.0/src/autoforge/Loss/PerceptionLoss.py +65 -0
  14. autoforge-1.2.0/src/autoforge/Loss/__init__.py +0 -0
  15. autoforge-1.2.0/src/autoforge/Modules/Optimizer.py +436 -0
  16. autoforge-1.2.0/src/autoforge/Modules/__init__.py +0 -0
  17. autoforge-1.2.0/src/autoforge/__init__.py +0 -0
  18. autoforge-1.2.0/src/autoforge/auto_forge.py +301 -0
  19. autoforge-1.2.0/src/autoforge/old_jax_files.py +142 -0
  20. autoforge-1.1.3/src/autoforge/auto_forge.py +0 -753
  21. autoforge-1.1.3/src/autoforge/helper_functions.py +0 -699
  22. {autoforge-1.1.3 → autoforge-1.2.0}/setup.cfg +0 -0
  23. {autoforge-1.1.3 → autoforge-1.2.0}/src/AutoForge.egg-info/dependency_links.txt +0 -0
  24. {autoforge-1.1.3 → autoforge-1.2.0}/src/AutoForge.egg-info/entry_points.txt +0 -0
  25. {autoforge-1.1.3 → autoforge-1.2.0}/src/AutoForge.egg-info/top_level.txt +0 -0
  26. {autoforge-1.1.3/src/autoforge → autoforge-1.2.0/src/autoforge/Helper}/__init__.py +0 -0
  27. {autoforge-1.1.3 → autoforge-1.2.0}/src/autoforge/__main__.py +0 -0
  28. {autoforge-1.1.3 → autoforge-1.2.0}/src/autoforge/auto_forge_gradio.py +0 -0
  29. {autoforge-1.1.3 → autoforge-1.2.0}/src/autoforge/color_fit_bayesian.py +0 -0
  30. {autoforge-1.1.3 → autoforge-1.2.0}/src/autoforge/filamentcolors_library.py +0 -0
@@ -1,14 +1,14 @@
1
1
  Metadata-Version: 2.2
2
2
  Name: AutoForge
3
- Version: 1.1.3
3
+ Version: 1.2.0
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
7
7
  Requires-Python: >=3.9
8
8
  Description-Content-Type: text/markdown
9
9
  Requires-Dist: numpy>=1.19.0
10
- Requires-Dist: jax>=0.3.10
11
- Requires-Dist: jaxlib>=0.3.10
10
+ Requires-Dist: torch>=2.6.0
11
+ Requires-Dist: torchvision>=0.21.0
12
12
  Requires-Dist: opencv-python>=4.2.0.32
13
13
  Requires-Dist: matplotlib>=3.3.0
14
14
  Requires-Dist: configargparse>=1.2.3
@@ -17,8 +17,6 @@ Requires-Dist: pandas>=1.1.0
17
17
  Requires-Dist: scikit-image
18
18
  Requires-Dist: scikit-learn
19
19
  Requires-Dist: optax>=0.1.3
20
- Provides-Extra: gpu
21
- Requires-Dist: jax[cuda12]; extra == "gpu"
22
20
 
23
21
  # AutoForge
24
22
 
@@ -27,7 +25,10 @@ AutoForge is a Python tool for generating 3D printed layered models from an inpu
27
25
  **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).
28
26
 
29
27
 
28
+ ## Important Information
30
29
 
30
+ We recently switched from JAX to PyTorch, to allow for a more streamlined development process. \
31
+ This can have some unforeseen consequences, so please report any bugs you find.
31
32
 
32
33
  ## Example
33
34
  All examples use only the 13 BambuLab Basic filaments, currently available in Hueforge.
@@ -104,18 +105,26 @@ autoforge --input_image path/to/input_image.jpg --csv_file path/to/materials.csv
104
105
  - `--input_image`: **(Required)** Path to the input image.
105
106
  - `--csv_file`: **(Required)** Path to the CSV file containing material data. The CSV should include columns for the brand, name, color (hex code), and TD values.
106
107
  - `--output_folder`: **(Required)** Folder where output files will be saved.
107
- - `--iterations`: Number of optimization iterations (default: 20000).
108
- - `--learning_rate`: Learning rate for the optimizer (default: 5e-3).
108
+ - `--iterations`: Number of optimization iterations (default: 5000).
109
+ - `--best_loss_iterations`: Percentage of optimization iterations after which we start to record the best loss (default: 4000).
110
+ **Note:** We currently do this as we increase a penalty loss for color and color swaps as the time goes on. Without this the penalties would have no impact.
111
+ - `--learning_rate`: Learning rate for the optimizer (default: 1e-2).
109
112
  - `--layer_height`: Layer thickness in millimeters (default: 0.04).
110
113
  - `--max_layers`: Maximum number of layers (default: 75).
111
114
  **Note:** This is about 3mm + the background height
112
115
  - `--background_height`: Height of the background in millimeters (default: 0.4).
113
116
  **Note:** The background height must be divisible by the layer height.
114
- - `--background_color`: Background color in hexadecimal format (default: `#8e9089` aka Bambulab Grey).
115
- - `--max_size`: Maximum dimension (width or height) for the resized target image (default: 512).
117
+ - `--background_color`: Background color in hexadecimal format (default: `#000000` aka Black).
118
+ **Note:** The solver currently assumes that you have a solid color in the background, which means a color with a TD value of 4 or less (if you have a background height of 0.4)
119
+ - `--output_size`: Maximum dimension for target image (default: 1024).
120
+ - `--solver_size`: Maximum dimension for solver (fast) image (default: 128).
121
+ **Note:** We solve on a smaller size as this is many times faster, but also a bit less accurate. Increase if you need more accuracy.
116
122
  - `--decay`: Final tau value for the Gumbel-Softmax formulation (default: 0.01).
117
- - `--loss`: Loss function to use. Choices are `mse`, `perceptual`, or `perceptual_l1` (default: `mse`).
118
123
  - `--visualize`: Flag to enable live visualization of the composite image during optimization.
124
+ - `--perform_pruning`: Perform pruning after optimization (default: True).
125
+ - `--pruning_max_colors`: Max number of colors allowed after pruning (default: 10).
126
+ - `--pruning_max_swaps`: Max number of swaps allowed after pruning (default: 20).
127
+ - `--random_seed`: Random seed for reproducibility (default: 0 (disabled) ).
119
128
 
120
129
  ## Outputs
121
130
 
@@ -141,6 +150,9 @@ Although I would love to be it fully color compatible with hueforge, I don't thi
141
150
  ## License
142
151
 
143
152
  AutoForge © 2025 by Hendric Voss is licensed under [CC BY-NC-SA 4.0](https://creativecommons.org/licenses/by-nc-sa/4.0/).
153
+ The software is provided as-is and comes with no warranty or guarantee of support.
154
+
155
+ The
144
156
 
145
157
  ## Acknowledgements
146
158
 
@@ -5,7 +5,10 @@ AutoForge is a Python tool for generating 3D printed layered models from an inpu
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
7
 
8
+ ## Important Information
8
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.
9
12
 
10
13
  ## Example
11
14
  All examples use only the 13 BambuLab Basic filaments, currently available in Hueforge.
@@ -82,18 +85,26 @@ autoforge --input_image path/to/input_image.jpg --csv_file path/to/materials.csv
82
85
  - `--input_image`: **(Required)** Path to the input image.
83
86
  - `--csv_file`: **(Required)** Path to the CSV file containing material data. The CSV should include columns for the brand, name, color (hex code), and TD values.
84
87
  - `--output_folder`: **(Required)** Folder where output files will be saved.
85
- - `--iterations`: Number of optimization iterations (default: 20000).
86
- - `--learning_rate`: Learning rate for the optimizer (default: 5e-3).
88
+ - `--iterations`: Number of optimization iterations (default: 5000).
89
+ - `--best_loss_iterations`: Percentage of optimization iterations after which we start to record the best loss (default: 4000).
90
+ **Note:** We currently do this as we increase a penalty loss for color and color swaps as the time goes on. Without this the penalties would have no impact.
91
+ - `--learning_rate`: Learning rate for the optimizer (default: 1e-2).
87
92
  - `--layer_height`: Layer thickness in millimeters (default: 0.04).
88
93
  - `--max_layers`: Maximum number of layers (default: 75).
89
94
  **Note:** This is about 3mm + the background height
90
95
  - `--background_height`: Height of the background in millimeters (default: 0.4).
91
96
  **Note:** The background height must be divisible by the layer height.
92
- - `--background_color`: Background color in hexadecimal format (default: `#8e9089` aka Bambulab Grey).
93
- - `--max_size`: Maximum dimension (width or height) for the resized target image (default: 512).
97
+ - `--background_color`: Background color in hexadecimal format (default: `#000000` aka Black).
98
+ **Note:** The solver currently assumes that you have a solid color in the background, which means a color with a TD value of 4 or less (if you have a background height of 0.4)
99
+ - `--output_size`: Maximum dimension for target image (default: 1024).
100
+ - `--solver_size`: Maximum dimension for solver (fast) image (default: 128).
101
+ **Note:** We solve on a smaller size as this is many times faster, but also a bit less accurate. Increase if you need more accuracy.
94
102
  - `--decay`: Final tau value for the Gumbel-Softmax formulation (default: 0.01).
95
- - `--loss`: Loss function to use. Choices are `mse`, `perceptual`, or `perceptual_l1` (default: `mse`).
96
103
  - `--visualize`: Flag to enable live visualization of the composite image during optimization.
104
+ - `--perform_pruning`: Perform pruning after optimization (default: True).
105
+ - `--pruning_max_colors`: Max number of colors allowed after pruning (default: 10).
106
+ - `--pruning_max_swaps`: Max number of swaps allowed after pruning (default: 20).
107
+ - `--random_seed`: Random seed for reproducibility (default: 0 (disabled) ).
97
108
 
98
109
  ## Outputs
99
110
 
@@ -119,6 +130,9 @@ Although I would love to be it fully color compatible with hueforge, I don't thi
119
130
  ## License
120
131
 
121
132
  AutoForge © 2025 by Hendric Voss is licensed under [CC BY-NC-SA 4.0](https://creativecommons.org/licenses/by-nc-sa/4.0/).
133
+ The software is provided as-is and comes with no warranty or guarantee of support.
134
+
135
+ The
122
136
 
123
137
  ## Acknowledgements
124
138
 
@@ -4,7 +4,7 @@ build-backend = "setuptools.build_meta"
4
4
 
5
5
  [project]
6
6
  name = "AutoForge"
7
- version="1.1.3"
7
+ version="1.2.0"
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"
@@ -12,8 +12,8 @@ requires-python = ">=3.9"
12
12
  license = { text = "CC BY-NC-SA 4.0" }
13
13
  dependencies = [
14
14
  "numpy>=1.19.0",
15
- "jax>=0.3.10",
16
- "jaxlib>=0.3.10",
15
+ "torch>=2.6.0",
16
+ "torchvision>=0.21.0",
17
17
  "opencv-python>=4.2.0.32",
18
18
  "matplotlib>=3.3.0",
19
19
  "configargparse>=1.2.3",
@@ -27,10 +27,5 @@ dependencies = [
27
27
  [tool.setuptools]
28
28
  package-dir = {"" = "src"}
29
29
 
30
- [project.optional-dependencies]
31
- gpu = [
32
- "jax[cuda12]",
33
- ]
34
-
35
30
  [project.scripts]
36
31
  autoforge = "autoforge.auto_forge:main"
@@ -1,14 +1,14 @@
1
1
  Metadata-Version: 2.2
2
2
  Name: AutoForge
3
- Version: 1.1.3
3
+ Version: 1.2.0
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
7
7
  Requires-Python: >=3.9
8
8
  Description-Content-Type: text/markdown
9
9
  Requires-Dist: numpy>=1.19.0
10
- Requires-Dist: jax>=0.3.10
11
- Requires-Dist: jaxlib>=0.3.10
10
+ Requires-Dist: torch>=2.6.0
11
+ Requires-Dist: torchvision>=0.21.0
12
12
  Requires-Dist: opencv-python>=4.2.0.32
13
13
  Requires-Dist: matplotlib>=3.3.0
14
14
  Requires-Dist: configargparse>=1.2.3
@@ -17,8 +17,6 @@ Requires-Dist: pandas>=1.1.0
17
17
  Requires-Dist: scikit-image
18
18
  Requires-Dist: scikit-learn
19
19
  Requires-Dist: optax>=0.1.3
20
- Provides-Extra: gpu
21
- Requires-Dist: jax[cuda12]; extra == "gpu"
22
20
 
23
21
  # AutoForge
24
22
 
@@ -27,7 +25,10 @@ AutoForge is a Python tool for generating 3D printed layered models from an inpu
27
25
  **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).
28
26
 
29
27
 
28
+ ## Important Information
30
29
 
30
+ We recently switched from JAX to PyTorch, to allow for a more streamlined development process. \
31
+ This can have some unforeseen consequences, so please report any bugs you find.
31
32
 
32
33
  ## Example
33
34
  All examples use only the 13 BambuLab Basic filaments, currently available in Hueforge.
@@ -104,18 +105,26 @@ autoforge --input_image path/to/input_image.jpg --csv_file path/to/materials.csv
104
105
  - `--input_image`: **(Required)** Path to the input image.
105
106
  - `--csv_file`: **(Required)** Path to the CSV file containing material data. The CSV should include columns for the brand, name, color (hex code), and TD values.
106
107
  - `--output_folder`: **(Required)** Folder where output files will be saved.
107
- - `--iterations`: Number of optimization iterations (default: 20000).
108
- - `--learning_rate`: Learning rate for the optimizer (default: 5e-3).
108
+ - `--iterations`: Number of optimization iterations (default: 5000).
109
+ - `--best_loss_iterations`: Percentage of optimization iterations after which we start to record the best loss (default: 4000).
110
+ **Note:** We currently do this as we increase a penalty loss for color and color swaps as the time goes on. Without this the penalties would have no impact.
111
+ - `--learning_rate`: Learning rate for the optimizer (default: 1e-2).
109
112
  - `--layer_height`: Layer thickness in millimeters (default: 0.04).
110
113
  - `--max_layers`: Maximum number of layers (default: 75).
111
114
  **Note:** This is about 3mm + the background height
112
115
  - `--background_height`: Height of the background in millimeters (default: 0.4).
113
116
  **Note:** The background height must be divisible by the layer height.
114
- - `--background_color`: Background color in hexadecimal format (default: `#8e9089` aka Bambulab Grey).
115
- - `--max_size`: Maximum dimension (width or height) for the resized target image (default: 512).
117
+ - `--background_color`: Background color in hexadecimal format (default: `#000000` aka Black).
118
+ **Note:** The solver currently assumes that you have a solid color in the background, which means a color with a TD value of 4 or less (if you have a background height of 0.4)
119
+ - `--output_size`: Maximum dimension for target image (default: 1024).
120
+ - `--solver_size`: Maximum dimension for solver (fast) image (default: 128).
121
+ **Note:** We solve on a smaller size as this is many times faster, but also a bit less accurate. Increase if you need more accuracy.
116
122
  - `--decay`: Final tau value for the Gumbel-Softmax formulation (default: 0.01).
117
- - `--loss`: Loss function to use. Choices are `mse`, `perceptual`, or `perceptual_l1` (default: `mse`).
118
123
  - `--visualize`: Flag to enable live visualization of the composite image during optimization.
124
+ - `--perform_pruning`: Perform pruning after optimization (default: True).
125
+ - `--pruning_max_colors`: Max number of colors allowed after pruning (default: 10).
126
+ - `--pruning_max_swaps`: Max number of swaps allowed after pruning (default: 20).
127
+ - `--random_seed`: Random seed for reproducibility (default: 0 (disabled) ).
119
128
 
120
129
  ## Outputs
121
130
 
@@ -141,6 +150,9 @@ Although I would love to be it fully color compatible with hueforge, I don't thi
141
150
  ## License
142
151
 
143
152
  AutoForge © 2025 by Hendric Voss is licensed under [CC BY-NC-SA 4.0](https://creativecommons.org/licenses/by-nc-sa/4.0/).
153
+ The software is provided as-is and comes with no warranty or guarantee of support.
154
+
155
+ The
144
156
 
145
157
  ## Acknowledgements
146
158
 
@@ -12,4 +12,15 @@ src/autoforge/auto_forge.py
12
12
  src/autoforge/auto_forge_gradio.py
13
13
  src/autoforge/color_fit_bayesian.py
14
14
  src/autoforge/filamentcolors_library.py
15
- src/autoforge/helper_functions.py
15
+ src/autoforge/old_jax_files.py
16
+ src/autoforge/Helper/FilamentHelper.py
17
+ src/autoforge/Helper/ImageHelper.py
18
+ src/autoforge/Helper/OptimizerHelper.py
19
+ src/autoforge/Helper/OutputHelper.py
20
+ src/autoforge/Helper/PruningHelper.py
21
+ src/autoforge/Helper/__init__.py
22
+ src/autoforge/Loss/LossFunctions.py
23
+ src/autoforge/Loss/PerceptionLoss.py
24
+ src/autoforge/Loss/__init__.py
25
+ src/autoforge/Modules/Optimizer.py
26
+ src/autoforge/Modules/__init__.py
@@ -1,6 +1,6 @@
1
1
  numpy>=1.19.0
2
- jax>=0.3.10
3
- jaxlib>=0.3.10
2
+ torch>=2.6.0
3
+ torchvision>=0.21.0
4
4
  opencv-python>=4.2.0.32
5
5
  matplotlib>=3.3.0
6
6
  configargparse>=1.2.3
@@ -9,6 +9,3 @@ pandas>=1.1.0
9
9
  scikit-image
10
10
  scikit-learn
11
11
  optax>=0.1.3
12
-
13
- [gpu]
14
- jax[cuda12]
@@ -0,0 +1,127 @@
1
+ import numpy as np
2
+ import pandas as pd
3
+ import torch
4
+
5
+
6
+ def load_materials(csv_filename):
7
+ """
8
+ Load material data from a CSV file.
9
+
10
+ Args:
11
+ csv_filename (str): Path to the hueforge CSV file containing material data.
12
+
13
+ Returns:
14
+ tuple: A tuple containing:
15
+ - material_colors (jnp.ndarray): Array of material colors in float64.
16
+ - material_TDs (jnp.ndarray): Array of material transmission/opacity parameters in float64.
17
+ - material_names (list): List of material names.
18
+ - colors_list (list): List of color hex strings.
19
+ """
20
+ df = pd.read_csv(csv_filename)
21
+ material_names = [
22
+ brand + " - " + name
23
+ for brand, name in zip(df["Brand"].tolist(), df[" Name"].tolist())
24
+ ]
25
+ material_TDs = (df[" TD"].astype(float)).to_numpy()
26
+ colors_list = df[" Color"].tolist()
27
+ # Use float64 for material colors.
28
+ material_colors = np.array(
29
+ [hex_to_rgb(color) for color in colors_list], dtype=np.float64
30
+ )
31
+ material_TDs = np.array(material_TDs, dtype=np.float64)
32
+ return material_colors, material_TDs, material_names, colors_list
33
+
34
+
35
+ def load_materials_data(csv_filename):
36
+ """
37
+ Load the full material data from the CSV file.
38
+
39
+ Args:
40
+ csv_filename (str): Path to the CSV file containing material data.
41
+
42
+ Returns:
43
+ list: A list of dictionaries (one per material) with keys such as
44
+ "Brand", "Type", "Color", "Name", "TD", "Owned", and "Uuid".
45
+ """
46
+ df = pd.read_csv(csv_filename)
47
+ # Use a consistent key naming. For example, convert 'TD' to 'Transmissivity' and 'Uuid' to 'uuid'
48
+ records = df.to_dict(orient="records")
49
+ return records
50
+
51
+
52
+ def hex_to_rgb(hex_str):
53
+ """
54
+ Convert a hex color string to a normalized RGB list.
55
+
56
+ Args:
57
+ hex_str (str): The hex color string (e.g., '#RRGGBB').
58
+
59
+ Returns:
60
+ list: A list of three floats representing the RGB values normalized to [0, 1].
61
+ """
62
+ hex_str = hex_str.lstrip("#")
63
+ return [int(hex_str[i : i + 2], 16) / 255.0 for i in (0, 2, 4)]
64
+
65
+
66
+ def extract_colors_from_swatches(swatch_data):
67
+ # we keep only data with transmission distance
68
+ swatch_data = [swatch for swatch in swatch_data if swatch["td"]]
69
+
70
+ # For now we load it and convert it in the same way as the hueforge csv files
71
+ out = {}
72
+ for swatch in swatch_data:
73
+ brand = swatch["manufacturer"]["name"]
74
+ name = swatch["color_name"]
75
+ color = swatch["hex_color"]
76
+ td = swatch["td"]
77
+ out[(brand, name)] = (color, td)
78
+
79
+ # convert to the same format as the hueforge csv files
80
+ material_names = [brand + " - " + name for (brand, name) in out.keys()]
81
+ material_colors = np.array(
82
+ [hex_to_rgb("#" + color) for color, _ in out.values()], dtype=np.float64
83
+ )
84
+ material_TDs = np.array([td for _, td in out.values()], dtype=np.float64)
85
+ colors_list = [color for color, _ in out.values()]
86
+
87
+ return material_colors, material_TDs, material_names, colors_list
88
+
89
+
90
+ def swatch_data_to_table(swatch_data):
91
+ """
92
+ Converts swatch JSON data into a table (list of dicts) with columns:
93
+ "Brand", "Name", "Transmission Distance", "Hex Color".
94
+ """
95
+ table = []
96
+ for swatch in swatch_data:
97
+ if not swatch["td"]:
98
+ continue
99
+ brand = swatch["manufacturer"]["name"]
100
+ name = swatch["color_name"]
101
+ hex_color = swatch["hex_color"]
102
+ td = swatch["td"]
103
+ table.append(
104
+ {
105
+ "Brand": brand,
106
+ "Name": name,
107
+ "Transmission Distance": td,
108
+ "Hex Color": f"#{hex_color}",
109
+ }
110
+ )
111
+ return table
112
+
113
+
114
+ def count_distinct_colors(dg: torch.Tensor) -> int:
115
+ """
116
+ Count how many distinct color/material IDs appear in dg.
117
+ """
118
+ unique_mats = torch.unique(dg)
119
+ return len(unique_mats)
120
+
121
+
122
+ def count_swaps(dg: torch.Tensor) -> int:
123
+ """
124
+ Count how many color changes (swaps) occur between adjacent layers.
125
+ """
126
+ # A 'swap' is whenever dg[i] != dg[i+1].
127
+ return int((dg[:-1] != dg[1:]).sum().item())
@@ -0,0 +1,13 @@
1
+ import cv2
2
+
3
+
4
+ def resize_image(img, max_size):
5
+ h_img, w_img, _ = img.shape
6
+ if w_img >= h_img:
7
+ new_w = max_size
8
+ new_h = int(max_size * h_img / w_img)
9
+ else:
10
+ new_h = max_size
11
+ new_w = int(max_size * w_img / h_img)
12
+ img_out = cv2.resize(img, (new_w, new_h))
13
+ return img_out