batoolset-gui 0.2.0__py3-none-any.whl

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 (74) hide show
  1. batoolset/GUI/__init__.py +0 -0
  2. batoolset/GUI/customdialog.py +136 -0
  3. batoolset/GUI/exception_handler.py +74 -0
  4. batoolset/GUI/list_gui.py +514 -0
  5. batoolset/GUI/multi_image_display.py +1038 -0
  6. batoolset/GUI/open.py +325 -0
  7. batoolset/GUI/overlay_hints.py +45 -0
  8. batoolset/GUI/paint2.py +1369 -0
  9. batoolset/GUI/scrollablepaint.py +1165 -0
  10. batoolset/dialogs/__init__.py +0 -0
  11. batoolset/dialogs/opensave.py +228 -0
  12. batoolset/drawings/__init__.py +0 -0
  13. batoolset/drawings/math_text.py +294 -0
  14. batoolset/drawings/pdf_svg_renderer.py +376 -0
  15. batoolset/drawings/shapes/Position.py +183 -0
  16. batoolset/drawings/shapes/__init__.py +0 -0
  17. batoolset/drawings/shapes/automated_alignment_drawing_refactored.py +346 -0
  18. batoolset/drawings/shapes/circle2d.py +99 -0
  19. batoolset/drawings/shapes/compute_optimal_size_all_merged.py +328 -0
  20. batoolset/drawings/shapes/ellipse2d.py +336 -0
  21. batoolset/drawings/shapes/freehand2d.py +235 -0
  22. batoolset/drawings/shapes/group.py +1985 -0
  23. batoolset/drawings/shapes/image2d.py +3157 -0
  24. batoolset/drawings/shapes/line2d.py +579 -0
  25. batoolset/drawings/shapes/point2d.py +257 -0
  26. batoolset/drawings/shapes/polygon2d.py +357 -0
  27. batoolset/drawings/shapes/polyline2d.py +235 -0
  28. batoolset/drawings/shapes/rect2d.py +388 -0
  29. batoolset/drawings/shapes/rectangle2d.py +601 -0
  30. batoolset/drawings/shapes/scalebar.py +310 -0
  31. batoolset/drawings/shapes/serializablefont.py +133 -0
  32. batoolset/drawings/shapes/serializableguiparams.py +14 -0
  33. batoolset/drawings/shapes/square2d.py +102 -0
  34. batoolset/drawings/shapes/test_publication_style.py +220 -0
  35. batoolset/drawings/shapes/textorientation.py +45 -0
  36. batoolset/drawings/shapes/tools.py +467 -0
  37. batoolset/drawings/shapes/transform2D.py +350 -0
  38. batoolset/drawings/shapes/txt2d.py +615 -0
  39. batoolset/drawings/svg_clip_renderer.py +247 -0
  40. batoolset/drawings/svg_embed.py +165 -0
  41. batoolset/drawings/svg_text_renderer.py +183 -0
  42. batoolset/drawings/widgets/__init__.py +0 -0
  43. batoolset/drawings/widgets/col.py +343 -0
  44. batoolset/drawings/widgets/image_group.py +248 -0
  45. batoolset/drawings/widgets/image_organizer.py +96 -0
  46. batoolset/drawings/widgets/paint.py +184 -0
  47. batoolset/drawings/widgets/vectorial.py +256 -0
  48. batoolset/figure/__init__.py +0 -0
  49. batoolset/figure/alignment.py +822 -0
  50. batoolset/figure/column.py +1117 -0
  51. batoolset/figure/fig_tools.py +285 -0
  52. batoolset/figure/minimal_debugger_scale_etc.py +139 -0
  53. batoolset/figure/panel.py +697 -0
  54. batoolset/figure/row.py +852 -0
  55. batoolset/figure/user_selection.py +287 -0
  56. batoolset/icons/__init__.py +0 -0
  57. batoolset/icons/tools.py +248 -0
  58. batoolset/luts/__init__.py +0 -0
  59. batoolset/luts/lut_minimal_test.py +1308 -0
  60. batoolset/luts/lutcreatorfromformula.py +209 -0
  61. batoolset/luts/luteditor.py +243 -0
  62. batoolset/pyqts/__init__.py +0 -0
  63. batoolset/pyqts/pleasewaitdialog.py +14 -0
  64. batoolset/pyqts/tools.py +760 -0
  65. batoolset/serializations/__init__.py +0 -0
  66. batoolset/serializations/tools.py +1338 -0
  67. batoolset/theme.py +647 -0
  68. batoolset/uis/__init__.py +0 -0
  69. batoolset/uis/blinker.py +56 -0
  70. batoolset_gui-0.2.0.dist-info/METADATA +54 -0
  71. batoolset_gui-0.2.0.dist-info/RECORD +74 -0
  72. batoolset_gui-0.2.0.dist-info/WHEEL +5 -0
  73. batoolset_gui-0.2.0.dist-info/licenses/LICENSE +24 -0
  74. batoolset_gui-0.2.0.dist-info/top_level.txt +1 -0
@@ -0,0 +1,1308 @@
1
+ # TODO could easily do a lut browser and editor --> would be really easy and may be useful too --> some day do it but not now !!!
2
+ # this is a minimal but already quite good version of the TA LUT class
3
+ import random
4
+ import traceback
5
+ from matplotlib.colors import ListedColormap
6
+ import math
7
+ from batoolset.img import int24_to_RGB
8
+ import matplotlib.pyplot as plt
9
+ import numpy as np
10
+ from matplotlib.backends.backend_agg import FigureCanvasAgg
11
+ from matplotlib.figure import Figure
12
+ from batoolset.img import fig_to_numpy
13
+ import base64
14
+ import numexpr as ne
15
+
16
+ # an inverted LUT is its name + INVERTED_SUFFIX_NAME, its formula + INVERTED_SUFFIX
17
+ INVERTED_SUFFIX_NAME = '_INVERTED'
18
+ INVERTED_SUFFIX = '|inverted'
19
+
20
+ def serialize_lut(lut_256_3):
21
+ # see also /home/aigouy/mon_prog/Python/epyseg_pkg/personal/FigGen_EzFig2/trash/tst_base64_encode_the_whole_np_array.py
22
+ # this generates a string that can be serialized
23
+ encoded_data = base64.b64encode(lut_256_3).decode()
24
+ return encoded_data
25
+
26
+ def deserialize_lut(string_lut, make_editable=True):
27
+ # see also /home/aigouy/mon_prog/Python/epyseg_pkg/personal/FigGen_EzFig2/trash/tst_base64_encode_the_whole_np_array.py
28
+ decoded_data = np.frombuffer(base64.b64decode(string_lut), dtype=np.uint8)
29
+ decoded_data = decoded_data.reshape((256, 3))
30
+ if make_editable:
31
+ decoded_data = decoded_data.copy() # required to make it editable
32
+ return decoded_data
33
+
34
+
35
+ def cmap_to_numpy(lut):
36
+ """
37
+ Converts a colormap to a NumPy array.
38
+
39
+ Args:
40
+ lut (Union[str, numpy.ndarray]): The colormap as a string name or a NumPy array.
41
+
42
+ Returns:
43
+ numpy.ndarray: The NumPy array representing the colormap.
44
+
45
+ Examples:
46
+ >>> colormap = 'hot'
47
+ >>> result = cmap_to_numpy(colormap)
48
+ >>> print(result[:1])
49
+ [[10 0 0]]
50
+
51
+ """
52
+ if isinstance(lut, str):
53
+ cm = plt.get_cmap(lut)
54
+ else:
55
+ cm = lut
56
+ out = cm(range(256)) * 255
57
+ out = out[..., 0:3].astype(np.uint8)
58
+ return out
59
+
60
+
61
+ def numpy_to_cmap(lut):
62
+ """
63
+ Converts a NumPy array to a colormap.
64
+
65
+ Args:
66
+ lut (numpy.ndarray): The NumPy array representing the colormap.
67
+
68
+ Returns:
69
+ matplotlib.colors.ListedColormap: The colormap created from the NumPy array.
70
+
71
+ # Examples:
72
+ # >>> colormap_array = np.array([[255, 0, 0], [0, 255, 0], [0, 0, 255]])
73
+ # >>> result = numpy_to_cmap(colormap_array)
74
+ # >>> print(result)
75
+ # <matplotlib.colors.ListedColormap object at 0x...>
76
+
77
+ """
78
+ lut_cp = np.copy(lut)
79
+ if lut_cp.max() > 1:
80
+ lut_cp = lut_cp / 255.
81
+
82
+ if lut_cp.shape[-1] == 3:
83
+ tmp = np.ones((256, 4), dtype=np.float64)
84
+ tmp[..., 0:3] = lut_cp[..., :]
85
+ lut_cp = tmp
86
+ newcmp = ListedColormap(lut_cp)
87
+ return newcmp
88
+
89
+
90
+ def create_ramp(min_val, max_val, lut, orientation='vertical'):
91
+ """
92
+ Creates a color ramp image using a colormap.
93
+
94
+ Args:
95
+ min_val (float): The minimum value of the color ramp.
96
+ max_val (float): The maximum value of the color ramp.
97
+ lut (Union[str, numpy.ndarray]): The colormap as a string name or a NumPy array.
98
+ orientation (str, optional): The orientation of the color ramp. Defaults to 'vertical'.
99
+
100
+ Returns:
101
+ numpy.ndarray: The color ramp image.
102
+
103
+ # Examples:
104
+ # >>> min_value = 0
105
+ # >>> max_value = 255
106
+ # >>> colormap = 'hot'
107
+ # >>> result = create_ramp(min_value, max_value, colormap)
108
+ # >>> print(result)
109
+
110
+
111
+ """
112
+ img = np.zeros((2, 1))
113
+ img[0] = min_val
114
+ img[1] = max_val
115
+ fig = Figure(tight_layout=True)
116
+ canvas = FigureCanvasAgg(fig)
117
+ ax = fig.subplots()
118
+
119
+ cmap = lut
120
+ if isinstance(lut, np.ndarray):
121
+ cmap = numpy_to_cmap(lut)
122
+
123
+ im = ax.imshow(img, cmap=cmap)
124
+ fig.colorbar(im, ax=ax, orientation=orientation)
125
+ ax.set_axis_off()
126
+ ax.remove()
127
+ canvas.draw()
128
+ img = fig_to_numpy(fig)
129
+ return img
130
+
131
+
132
+ def list_available_luts(return_matplotlib_lib_luts_in_separate_database=False):
133
+ """
134
+ Returns a dictionary of available colormaps.
135
+
136
+ Args:
137
+ return_matplotlib_lib_luts_in_separate_database (bool, optional): Whether to include matplotlib library colormaps separately. Defaults to False.
138
+
139
+ Returns:
140
+ dict: A dictionary containing the available colormaps.
141
+
142
+ Examples:
143
+ >>> result = list_available_luts()
144
+ >>> print(result)
145
+ {'DEFAULT': '22,13,-23', 'AFM_HOT': '34,35,36', 'GREEN': '0,3,0', 'RED': '3,0,0', 'BLUE': '0,0,3', 'GRAY': '3,3,3', 'GREY': '3,3,3', 'PACKING': 'PACKING', 'RED_HOT': '21,22,23', 'GREEN_HOT': '35,34,23', 'BLUE_HOT': '23,22,21', 'OCEAN': '23,28,3', 'GREEN_RED_VIOLET': '3,11,6', 'COLOR_PRINTABLE_ON_GRAY': '30,31,32', 'HI_LO': '3,3,3hl', 'DNA': '22,13,-23', 'RAINBOW_1': '22,13,-31', 'RAINBOW_2': '35,13,-35', 'RAINBOW_3': '33,13,10', 'RAINBOW_LIGHT': '33,13,-31', 'RAINBOW_ARTIFICIAL': '10,13,33', 'BLUE_WHITE_RED': '34,-13,-34', 'BROWN': '7,4,27', 'GREEN_FIRE_BLUE': '31,34,11', 'GREEN_FIRE_BLUE2': '31,34,29', 'ORANGE': '3,5,0', 'ORANGE_HOT': '21,-10,23', 'YELLOW_HOT': '34,-10,23', 'YELLOW': '3,3,0', 'YELLOW_PALE': '34,34,4', 'PURPLE_HOT': '34,36,34', 'PURPLE': '3,0,3', 'MAGENTA_HOT': '34,36,34', 'MAGENTA': '3,0,3', 'CYAN': '0,3,3', 'CYAN_HOT': '36,34,34', 'PM3D': '7,5,15', 'HSV': '3,2,2', 'HSV2': '7,5,15', 'YING_YANG': '13,13,13', 'YING_YANG2': '14,14,14', 'CYCLIC': '19,19,19', 'METEO1': '34,-35,-36', 'METEO2': '22,13,-31', 'DIABOLO_MENTHE': '-7,2,-7', 'DIABOLO_GRENADINE': '2,-7,-7', 'BLACK_PURPLE_PINK_WHITE': '30,31,32', 'RED_BLACK_BLUE': '-36,0,36', 'MATPLOTLIB_magma': 'magma', 'MATPLOTLIB_inferno': 'inferno', 'MATPLOTLIB_plasma': 'plasma', 'MATPLOTLIB_viridis': 'viridis', 'MATPLOTLIB_cividis': 'cividis', 'MATPLOTLIB_twilight': 'twilight', 'MATPLOTLIB_twilight_shifted': 'twilight_shifted', 'MATPLOTLIB_turbo': 'turbo', 'MATPLOTLIB_Blues': 'Blues', 'MATPLOTLIB_BrBG': 'BrBG', 'MATPLOTLIB_BuGn': 'BuGn', 'MATPLOTLIB_BuPu': 'BuPu', 'MATPLOTLIB_CMRmap': 'CMRmap', 'MATPLOTLIB_GnBu': 'GnBu', 'MATPLOTLIB_Greens': 'Greens', 'MATPLOTLIB_Greys': 'Greys', 'MATPLOTLIB_OrRd': 'OrRd', 'MATPLOTLIB_Oranges': 'Oranges', 'MATPLOTLIB_PRGn': 'PRGn', 'MATPLOTLIB_PiYG': 'PiYG', 'MATPLOTLIB_PuBu': 'PuBu', 'MATPLOTLIB_PuBuGn': 'PuBuGn', 'MATPLOTLIB_PuOr': 'PuOr', 'MATPLOTLIB_PuRd': 'PuRd', 'MATPLOTLIB_Purples': 'Purples', 'MATPLOTLIB_RdBu': 'RdBu', 'MATPLOTLIB_RdGy': 'RdGy', 'MATPLOTLIB_RdPu': 'RdPu', 'MATPLOTLIB_RdYlBu': 'RdYlBu', 'MATPLOTLIB_RdYlGn': 'RdYlGn', 'MATPLOTLIB_Reds': 'Reds', 'MATPLOTLIB_Spectral': 'Spectral', 'MATPLOTLIB_Wistia': 'Wistia', 'MATPLOTLIB_YlGn': 'YlGn', 'MATPLOTLIB_YlGnBu': 'YlGnBu', 'MATPLOTLIB_YlOrBr': 'YlOrBr', 'MATPLOTLIB_YlOrRd': 'YlOrRd', 'MATPLOTLIB_afmhot': 'afmhot', 'MATPLOTLIB_autumn': 'autumn', 'MATPLOTLIB_binary': 'binary', 'MATPLOTLIB_bone': 'bone', 'MATPLOTLIB_brg': 'brg', 'MATPLOTLIB_bwr': 'bwr', 'MATPLOTLIB_cool': 'cool', 'MATPLOTLIB_coolwarm': 'coolwarm', 'MATPLOTLIB_copper': 'copper', 'MATPLOTLIB_cubehelix': 'cubehelix', 'MATPLOTLIB_flag': 'flag', 'MATPLOTLIB_gist_earth': 'gist_earth', 'MATPLOTLIB_gist_gray': 'gist_gray', 'MATPLOTLIB_gist_heat': 'gist_heat', 'MATPLOTLIB_gist_ncar': 'gist_ncar', 'MATPLOTLIB_gist_rainbow': 'gist_rainbow', 'MATPLOTLIB_gist_stern': 'gist_stern', 'MATPLOTLIB_gist_yarg': 'gist_yarg', 'MATPLOTLIB_gnuplot': 'gnuplot', 'MATPLOTLIB_gnuplot2': 'gnuplot2', 'MATPLOTLIB_gray': 'gray', 'MATPLOTLIB_hot': 'hot', 'MATPLOTLIB_hsv': 'hsv', 'MATPLOTLIB_jet': 'jet', 'MATPLOTLIB_nipy_spectral': 'nipy_spectral', 'MATPLOTLIB_ocean': 'ocean', 'MATPLOTLIB_pink': 'pink', 'MATPLOTLIB_prism': 'prism', 'MATPLOTLIB_rainbow': 'rainbow', 'MATPLOTLIB_seismic': 'seismic', 'MATPLOTLIB_spring': 'spring', 'MATPLOTLIB_summer': 'summer', 'MATPLOTLIB_terrain': 'terrain', 'MATPLOTLIB_winter': 'winter', 'MATPLOTLIB_Accent': 'Accent', 'MATPLOTLIB_Dark2': 'Dark2', 'MATPLOTLIB_Paired': 'Paired', 'MATPLOTLIB_Pastel1': 'Pastel1', 'MATPLOTLIB_Pastel2': 'Pastel2', 'MATPLOTLIB_Set1': 'Set1', 'MATPLOTLIB_Set2': 'Set2', 'MATPLOTLIB_Set3': 'Set3', 'MATPLOTLIB_tab10': 'tab10', 'MATPLOTLIB_tab20': 'tab20', 'MATPLOTLIB_tab20b': 'tab20b', 'MATPLOTLIB_tab20c': 'tab20c', 'MATPLOTLIB_magma_r': 'magma_r', 'MATPLOTLIB_inferno_r': 'inferno_r', 'MATPLOTLIB_plasma_r': 'plasma_r', 'MATPLOTLIB_viridis_r': 'viridis_r', 'MATPLOTLIB_cividis_r': 'cividis_r', 'MATPLOTLIB_twilight_r': 'twilight_r', 'MATPLOTLIB_twilight_shifted_r': 'twilight_shifted_r', 'MATPLOTLIB_turbo_r': 'turbo_r', 'MATPLOTLIB_Blues_r': 'Blues_r', 'MATPLOTLIB_BrBG_r': 'BrBG_r', 'MATPLOTLIB_BuGn_r': 'BuGn_r', 'MATPLOTLIB_BuPu_r': 'BuPu_r', 'MATPLOTLIB_CMRmap_r': 'CMRmap_r', 'MATPLOTLIB_GnBu_r': 'GnBu_r', 'MATPLOTLIB_Greens_r': 'Greens_r', 'MATPLOTLIB_Greys_r': 'Greys_r', 'MATPLOTLIB_OrRd_r': 'OrRd_r', 'MATPLOTLIB_Oranges_r': 'Oranges_r', 'MATPLOTLIB_PRGn_r': 'PRGn_r', 'MATPLOTLIB_PiYG_r': 'PiYG_r', 'MATPLOTLIB_PuBu_r': 'PuBu_r', 'MATPLOTLIB_PuBuGn_r': 'PuBuGn_r', 'MATPLOTLIB_PuOr_r': 'PuOr_r', 'MATPLOTLIB_PuRd_r': 'PuRd_r', 'MATPLOTLIB_Purples_r': 'Purples_r', 'MATPLOTLIB_RdBu_r': 'RdBu_r', 'MATPLOTLIB_RdGy_r': 'RdGy_r', 'MATPLOTLIB_RdPu_r': 'RdPu_r', 'MATPLOTLIB_RdYlBu_r': 'RdYlBu_r', 'MATPLOTLIB_RdYlGn_r': 'RdYlGn_r', 'MATPLOTLIB_Reds_r': 'Reds_r', 'MATPLOTLIB_Spectral_r': 'Spectral_r', 'MATPLOTLIB_Wistia_r': 'Wistia_r', 'MATPLOTLIB_YlGn_r': 'YlGn_r', 'MATPLOTLIB_YlGnBu_r': 'YlGnBu_r', 'MATPLOTLIB_YlOrBr_r': 'YlOrBr_r', 'MATPLOTLIB_YlOrRd_r': 'YlOrRd_r', 'MATPLOTLIB_afmhot_r': 'afmhot_r', 'MATPLOTLIB_autumn_r': 'autumn_r', 'MATPLOTLIB_binary_r': 'binary_r', 'MATPLOTLIB_bone_r': 'bone_r', 'MATPLOTLIB_brg_r': 'brg_r', 'MATPLOTLIB_bwr_r': 'bwr_r', 'MATPLOTLIB_cool_r': 'cool_r', 'MATPLOTLIB_coolwarm_r': 'coolwarm_r', 'MATPLOTLIB_copper_r': 'copper_r', 'MATPLOTLIB_cubehelix_r': 'cubehelix_r', 'MATPLOTLIB_flag_r': 'flag_r', 'MATPLOTLIB_gist_earth_r': 'gist_earth_r', 'MATPLOTLIB_gist_gray_r': 'gist_gray_r', 'MATPLOTLIB_gist_heat_r': 'gist_heat_r', 'MATPLOTLIB_gist_ncar_r': 'gist_ncar_r', 'MATPLOTLIB_gist_rainbow_r': 'gist_rainbow_r', 'MATPLOTLIB_gist_stern_r': 'gist_stern_r', 'MATPLOTLIB_gist_yarg_r': 'gist_yarg_r', 'MATPLOTLIB_gnuplot_r': 'gnuplot_r', 'MATPLOTLIB_gnuplot2_r': 'gnuplot2_r', 'MATPLOTLIB_gray_r': 'gray_r', 'MATPLOTLIB_hot_r': 'hot_r', 'MATPLOTLIB_hsv_r': 'hsv_r', 'MATPLOTLIB_jet_r': 'jet_r', 'MATPLOTLIB_nipy_spectral_r': 'nipy_spectral_r', 'MATPLOTLIB_ocean_r': 'ocean_r', 'MATPLOTLIB_pink_r': 'pink_r', 'MATPLOTLIB_prism_r': 'prism_r', 'MATPLOTLIB_rainbow_r': 'rainbow_r', 'MATPLOTLIB_seismic_r': 'seismic_r', 'MATPLOTLIB_spring_r': 'spring_r', 'MATPLOTLIB_summer_r': 'summer_r', 'MATPLOTLIB_terrain_r': 'terrain_r', 'MATPLOTLIB_winter_r': 'winter_r', 'MATPLOTLIB_Accent_r': 'Accent_r', 'MATPLOTLIB_Dark2_r': 'Dark2_r', 'MATPLOTLIB_Paired_r': 'Paired_r', 'MATPLOTLIB_Pastel1_r': 'Pastel1_r', 'MATPLOTLIB_Pastel2_r': 'Pastel2_r', 'MATPLOTLIB_Set1_r': 'Set1_r', 'MATPLOTLIB_Set2_r': 'Set2_r', 'MATPLOTLIB_Set3_r': 'Set3_r', 'MATPLOTLIB_tab10_r': 'tab10_r', 'MATPLOTLIB_tab20_r': 'tab20_r', 'MATPLOTLIB_tab20b_r': 'tab20b_r', 'MATPLOTLIB_tab20c_r': 'tab20c_r'}
146
+
147
+ """
148
+ hash_pal = {
149
+ "DEFAULT": "22,13,-23",
150
+ "RED": "3,0,0",
151
+ "GREEN": "0,3,0",
152
+ "BLUE": "0,0,3",
153
+ "GRAY": "3,3,3",
154
+ "AFM_HOT": "34,35,36",
155
+ "PACKING": "PACKING",
156
+ "RED_HOT": "21,22,23",
157
+ "GREEN_HOT": "35,34,23",
158
+ "BLUE_HOT": "23,22,21",
159
+ "OCEAN": "23,28,3",
160
+ "GREEN_RED_VIOLET": "3,11,6",
161
+ "COLOR_PRINTABLE_ON_GRAY": "30,31,32",
162
+ "HI_LO": "3,3,3hl",
163
+ "DNA": "22,13,-23",
164
+ "RAINBOW_1": "22,13,-31",
165
+ "RAINBOW_2": "35,13,-35",
166
+ "RAINBOW_3": "33,13,10",
167
+ "RAINBOW_LIGHT": "33,13,-31",
168
+ "RAINBOW_ARTIFICIAL": "10,13,33",
169
+ "BLUE_WHITE_RED": "34,-13,-34",
170
+ "BROWN": "7,4,27",
171
+ "GREEN_FIRE_BLUE": "31,34,11",
172
+ "GREEN_FIRE_BLUE2": "31,34,29",
173
+ "ORANGE": "3,5,0",
174
+ "ORANGE_HOT": "21,-10,23",
175
+ "YELLOW_HOT": "34,-10,23",
176
+ "YELLOW": "3,3,0",
177
+ "YELLOW_PALE": "34,34,4",
178
+ "PURPLE_HOT": "34,36,34",
179
+ "PURPLE": "3,0,3",
180
+ "MAGENTA_HOT": "34,36,34",
181
+ "MAGENTA": "3,0,3",
182
+ "CYAN": "0,3,3",
183
+ "CYAN_HOT": "36,34,34",
184
+ "PM3D": "7,5,15",
185
+ "HSV": "3,2,2",
186
+ "HSV2": "7,5,15",
187
+ "YING_YANG": "13,13,13",
188
+ "YING_YANG2": "14,14,14",
189
+ "CYCLIC": "19,19,19",
190
+ "METEO1": "34,-35,-36",
191
+ "METEO2": "22,13,-31",
192
+ "DIABOLO_MENTHE": "-7,2,-7",
193
+ "DIABOLO_GRENADINE": "2,-7,-7",
194
+ "BLACK_PURPLE_PINK_WHITE": "30,31,32",
195
+ "RED_BLACK_BLUE": "-36,0,36"
196
+ }
197
+
198
+ # every LUT also inverted, as Fiji's Invert LUT (GRAY_INVERTED: dark signal on
199
+ # white); matplotlib maps have their own _r. DEFAULT (the acquisition colour)
200
+ # and PACKING are not colour ramps.
201
+ for name, formula in list(hash_pal.items()):
202
+ if name not in ('DEFAULT', 'PACKING'):
203
+ hash_pal[name + INVERTED_SUFFIX_NAME] = formula + INVERTED_SUFFIX
204
+
205
+ matplot_lib_luts = []
206
+ try:
207
+ # Get the available colormaps from matplotlib
208
+ matplot_lib_luts = get_matplotlib_available_luts()
209
+
210
+ # Add matplotlib colormaps to the dictionary
211
+ for matplotlib in matplot_lib_luts:
212
+ hash_pal['MATPLOTLIB_' + str(matplotlib)] = str(matplotlib)
213
+ except:
214
+ # no big deal if fails because I have enough luts already
215
+ # traceback.print_exc()
216
+ pass
217
+
218
+ # Check if separate matplotlib colormaps are requested
219
+ if return_matplotlib_lib_luts_in_separate_database:
220
+ return matplot_lib_luts, hash_pal
221
+
222
+ # Return the dictionary of colormaps
223
+ return hash_pal
224
+
225
+
226
+ def invert_lut_name(name, available=None):
227
+ """
228
+ The inverted twin of a LUT name, and back again: GRAY <-> GRAY_INVERTED,
229
+ MATPLOTLIB_viridis <-> MATPLOTLIB_viridis_r. The inversion is stored in the
230
+ name, so a saved figure keeps it.
231
+
232
+ Args:
233
+ name (str): an exact LUT key.
234
+ available (dict, optional): list_available_luts(), when already known.
235
+
236
+ Returns:
237
+ str or None: the twin's key; None when the LUT has none (DEFAULT, PACKING,
238
+ an unknown name).
239
+
240
+ Examples:
241
+ >>> invert_lut_name('GRAY')
242
+ 'GRAY_INVERTED'
243
+ >>> invert_lut_name('MATPLOTLIB_viridis_r')
244
+ 'MATPLOTLIB_viridis'
245
+ """
246
+ if not isinstance(name, str):
247
+ return None
248
+ available = list_available_luts() if available is None else available
249
+ if name.startswith('MATPLOTLIB_'):
250
+ twin = name[:-2] if name.endswith('_r') else name + '_r'
251
+ elif name.endswith(INVERTED_SUFFIX_NAME):
252
+ twin = name[:-len(INVERTED_SUFFIX_NAME)]
253
+ else:
254
+ twin = name + INVERTED_SUFFIX_NAME
255
+ return twin if twin in available else None
256
+
257
+
258
+ def is_inverted_lut(name):
259
+ """True for an inverted LUT key (GRAY_INVERTED, MATPLOTLIB_viridis_r)."""
260
+ return isinstance(name, str) and (name.endswith(INVERTED_SUFFIX_NAME) or
261
+ (name.startswith('MATPLOTLIB_') and name.endswith('_r')))
262
+
263
+
264
+ class PaletteCreator:
265
+ PALETTE_IRFAN = 0
266
+ PALETTE_IRFANVIEW = 0
267
+ PALETTE_IMAGEJ_EXPORTED = 1
268
+ PALETTE_IMAGEJ_RAW = 2
269
+ PALETTE_UNKNOWN = 3
270
+ currentPalette = "DEFAULT"
271
+
272
+ def __init__(self):
273
+ self.__list_palettes()
274
+
275
+ def __list_palettes(self):
276
+ """
277
+ Retrieves the list of available palettes, including matplotlib library colormaps.
278
+ """
279
+ self.matplot_lib_luts, self.list = list_available_luts(return_matplotlib_lib_luts_in_separate_database=True)
280
+
281
+ def get_packing_lut(self):
282
+ """
283
+ Returns the packing lookup table.
284
+
285
+ Returns:
286
+ numpy.ndarray: The packing lookup table.
287
+
288
+ # Examples:
289
+ # >>> palette_creator = PaletteCreator()
290
+ # >>> result = palette_creator.get_packing_lut()
291
+ # >>> print(result)
292
+ # [[ 64 64 64]
293
+ # [100 190 40]
294
+ # [190 190 30]
295
+ # [128 128 128]
296
+ # [ 0 0 175]
297
+ # [175 0 0]
298
+ # [100 0 170]
299
+ # [250 36 0]
300
+ # [150 75 0]
301
+ # ...
302
+ # ]
303
+ """
304
+ Sided3_or_less = (64, 64, 64)
305
+ Sided4 = (100, 190, 40)
306
+ Sided5 = (190, 190, 30)
307
+ Sided6 = (128, 128, 128)
308
+ Sided7 = (0, 0, 175)
309
+ Sided8 = (175, 0, 0)
310
+ Sided9 = (100, 0, 170)
311
+ Sided10 = (250, 36, 0)
312
+ Sided11_or_more = (150, 75, 0)
313
+
314
+ packing_lut = np.zeros((256, 3), dtype=np.uint8)
315
+
316
+ packing_lut[0] = Sided3_or_less
317
+ packing_lut[1] = Sided3_or_less
318
+ packing_lut[2] = Sided3_or_less
319
+ packing_lut[3] = Sided3_or_less
320
+ packing_lut[4] = Sided4
321
+ packing_lut[5] = Sided5
322
+ packing_lut[6] = Sided6
323
+ packing_lut[7] = Sided7
324
+ packing_lut[8] = Sided8
325
+ packing_lut[9] = Sided9
326
+ packing_lut[10] = Sided10
327
+ packing_lut[11] = Sided11_or_more
328
+ for iii in range(10, 256):
329
+ packing_lut[iii] = Sided11_or_more
330
+
331
+ return packing_lut
332
+
333
+ def count(self, strg, char2count):
334
+ """
335
+ Counts the number of occurrences of a character in a string.
336
+
337
+ Args:
338
+ strg (str): The input string.
339
+ char2count (str): The character to count.
340
+
341
+ Returns:
342
+ int: The count of the character in the string.
343
+
344
+ Examples:
345
+ >>> palette_creator = PaletteCreator()
346
+ >>> result = palette_creator.count("Hello world!", "o")
347
+ >>> print(result)
348
+ 2
349
+ """
350
+ if char2count is None:
351
+ return 0
352
+ if strg is None or strg == "":
353
+ return 0
354
+ nb = 0
355
+ for c in strg:
356
+ if c == char2count:
357
+ nb += 1
358
+ return nb
359
+
360
+ def convertAbs(self, txt):
361
+ """
362
+ Converts absolute value notation in a string.
363
+
364
+ Args:
365
+ txt (str): The input string.
366
+
367
+ Returns:
368
+ str: The string with absolute value notation converted.
369
+
370
+ # Examples:
371
+ # >>> palette_creator = PaletteCreator()
372
+ # >>> result = palette_creator.convertAbs("|-5| + 10")
373
+ # >>> print(result)
374
+ # "abs(-5) + 10"
375
+ """
376
+ try:
377
+ while "|" in txt:
378
+ size = len(txt)
379
+ pos = txt.index("|")
380
+ begin_pos = pos + 1
381
+ end_pos = pos
382
+
383
+ for i in range(pos + 1, size + 1):
384
+ c = txt[i]
385
+ if c == '|':
386
+ end_pos = i
387
+ break
388
+ end_pos = i + 1
389
+ power_equa = txt[begin_pos:end_pos]
390
+ beginning_of_word = txt[0:begin_pos - 1]
391
+ end_of_word = txt[end_pos + 1]
392
+ txt = beginning_of_word + "abs(" + power_equa + ")" + end_of_word
393
+ except:
394
+ traceback.print_exc()
395
+ return txt
396
+
397
+ def convertPowers(self, txt):
398
+ """
399
+ Converts power notation in a string.
400
+
401
+ Args:
402
+ txt (str): The input string.
403
+
404
+ Returns:
405
+ str: The string with power notation converted.
406
+
407
+ # Examples:
408
+ # >>> palette_creator = PaletteCreator()
409
+ # >>> result = palette_creator.convertPowers("2^3 + 4^2")
410
+ # >>> print(result)
411
+ # "2**3 + 4**2"
412
+ """
413
+ try:
414
+ while "^" in txt:
415
+ size = len(txt)
416
+ pos = txt.index("^")
417
+ begin_pos = pos
418
+ end_pos = pos
419
+ for i in range(pos, size):
420
+ c = txt[i]
421
+ if c == '+' or c == '-' or c == '/' or c == '*':
422
+ end_pos = i
423
+ break
424
+ end_pos = i + 1
425
+ for i in range(pos, 0, -1):
426
+ c = txt[i]
427
+ if c == '+' or c == '-' or c == '/' or c == '*':
428
+ begin_pos = i + 1
429
+ break
430
+ begin_pos = i
431
+ power_equa = txt[begin_pos:end_pos]
432
+ beginning_of_word = txt[0:begin_pos]
433
+ end_of_word = txt[end_pos:]
434
+ txt = beginning_of_word + self.power_parser(power_equa) + end_of_word
435
+ except:
436
+ traceback.print_exc()
437
+ return txt
438
+
439
+ # do cut left or right of pattern
440
+ # TODO --> do all of there
441
+ def power_parser(self, equation):
442
+ nb, power = equation.split("^", 1) # CommonClasses.strCutLeftFirst(equation, "^")
443
+ # power = CommonClasses.strCutRightFisrt(equation, "^")
444
+ return "math.pow(" + nb + "," + power + ")"
445
+
446
+ def javaifyEquation(self, equation):
447
+ """
448
+ Converts an equation into a Java-friendly format.
449
+
450
+ Args:
451
+ equation (str): The input equation.
452
+
453
+ Returns:
454
+ str: The equation in a Java-friendly format.
455
+
456
+ Raises:
457
+ Exception: If the equation has an incorrect number of absolute value bars or brackets.
458
+
459
+ # Examples:
460
+ # >>> palette_creator = PaletteCreator()
461
+ # >>> result = palette_creator.javaifyEquation("|x+y|")
462
+ # >>> print(result)
463
+ """
464
+ # print(equation)
465
+ equation = equation.replace(" ", "")
466
+
467
+ if "|" in equation:
468
+ nb_of_norm_bars = self.count(equation, "|")
469
+ if nb_of_norm_bars % 2 != 0:
470
+ raise Exception("equation is incorrect, nb of norm bars is not even")
471
+ return
472
+
473
+ equation = self.convertAbs(equation)
474
+
475
+ if "^" in equation:
476
+ equation = self.convertPowers(equation)
477
+
478
+ equation = equation.replace("[", "(")
479
+ equation = equation.replace("]", ")")
480
+ equation = equation.replace("{", "(")
481
+ equation = equation.replace("}", ")")
482
+
483
+ equation = equation.replace("math.sin(", "sin(")
484
+ equation = equation.replace("math.sin(", "sin(")
485
+ equation = equation.replace("math.cos(", "cos(")
486
+ equation = equation.replace("math.cos(", "cos(")
487
+ equation = equation.replace("math.tan(", "tan(")
488
+ equation = equation.replace("math.tan(", "tan(")
489
+ equation = equation.replace("math.sqrt(", "sqrt(")
490
+ equation = equation.replace("math.sqrt(", "sqrt(")
491
+ equation = equation.replace("sin(", "math.sin(")
492
+ equation = equation.replace("cos(", "math.cos(")
493
+ equation = equation.replace("tan(", "math.tan(")
494
+ equation = equation.replace("sqrt(", "math.sqrt(")
495
+ nb_of_open_brackets = self.count(equation, "(")
496
+ nb_of_closing_brackets = self.count(equation, ")")
497
+
498
+ if nb_of_open_brackets != nb_of_closing_brackets:
499
+ raise Exception("equation is incorrect, nb of opened and closed brackets don't match")
500
+
501
+ return equation
502
+
503
+ # def computeEquation(self, equation, variable, val):
504
+ # """
505
+ # Computes the result of an equation using the numexpr library.
506
+ #
507
+ # Args:
508
+ # equation (str): The equation to compute.
509
+ # variable (str): The variable name in the equation.
510
+ # val: The value for the variable.
511
+ #
512
+ # Returns:
513
+ # float: The computed result of the equation.
514
+ #
515
+ # # Examples:
516
+ # # >>> palette_creator = PaletteCreator()
517
+ # # >>> result = palette_creator.computeEquation("2*a*(b/c)**2", "a", [1, 2, 2])
518
+ # # >>> print(result)
519
+ # # array([0.88888889, 0.44444444, 4.])
520
+ # """
521
+ # import numexpr as ne
522
+ # var = {'a': np.array([1, 2, 2]), 'b': np.array([2, 1, 3]), 'c': np.array([3])}
523
+ # print('TOO', ne.evaluate('2*a*(b/c)**2', local_dict=var))
524
+ # try:
525
+ # for i in range(10):
526
+ # equation = equation.replace(str(i) + variable, str(i) + "*" + variable)
527
+ # for i in range(10):
528
+ # equation = equation.replace(variable + str(i), variable + "*" + str(i))
529
+ #
530
+ # equation = equation.replace(variable, "(double)" + variable)
531
+ # print(equation)
532
+ # except:
533
+ # traceback.print_exc()
534
+ # return 0
535
+ def computeEquation(self, equation, variable, val):
536
+ """
537
+ Computes the result of an equation using the numexpr library.
538
+
539
+ Args:
540
+ equation (str): The equation to compute.
541
+ variable (str): The variable name in the equation.
542
+ val: The value for the variable.
543
+
544
+ Returns:
545
+ numpy.ndarray: The computed result of the equation.
546
+ """
547
+ try:
548
+ # Handle the special case where the equation is just a minus sign
549
+ if equation.strip() == '-':
550
+ return -np.array(val)
551
+ if equation.strip() == '+':
552
+ return np.array(val)
553
+ # Prepare the variable dictionary
554
+ var = {variable: np.array(val)}
555
+
556
+ # Print the equation and variables for debugging
557
+ print(f"Evaluating equation: {equation}")
558
+ print(f"Variables: {var}")
559
+ # Evaluate the equation using numexpr
560
+ result = ne.evaluate(equation, local_dict=var)
561
+
562
+ return result
563
+ except Exception as e:
564
+ # Print the traceback and the error message
565
+ traceback.print_exc()
566
+ print(f"Error evaluating the equation '{equation}': {e}")
567
+ return np.zeros_like(val) # Return an array of zeros with the same shape as val
568
+
569
+ def create5(self, red, green, blue):
570
+ """
571
+ Creates a palette using the RGB components.
572
+
573
+ Args:
574
+ red (float): The red component value.
575
+ green (float): The green component value.
576
+ blue (float): The blue component value.
577
+
578
+ Returns:
579
+ numpy.ndarray: The created palette.
580
+
581
+ # Examples:
582
+ # >>> palette_creator = PaletteCreator()
583
+ # >>> result = palette_creator.create5(0.5, -0.2, 0.8)
584
+ # >>> print(result)
585
+ # array([[127, 0, 0],
586
+ # [136, 0, 0],
587
+ # [146, 0, 0],
588
+ # ...
589
+ # [ 0, 76, 153],
590
+ # [ 0, 76, 153],
591
+ # [ 0, 76, 153]], dtype=uint8)
592
+ """
593
+ l = 0
594
+ palette = np.zeros((256, 3), dtype=np.uint8)
595
+ R = np.zeros((256,), dtype=np.uint8)
596
+ G = np.zeros((256,), dtype=np.uint8)
597
+ B = np.zeros((256,), dtype=np.uint8)
598
+
599
+ for i in range(256):
600
+ R[i] = int(self.RGB_formula(i, red) * 255.0)
601
+ G[i] = int(self.RGB_formula(i, green) * 255.0)
602
+ B[i] = int(self.RGB_formula(i, blue) * 255.0)
603
+
604
+ if red < 0:
605
+ R = self.reverse_ramp(R)
606
+
607
+ if green < 0:
608
+ G = self.reverse_ramp(G)
609
+
610
+ if blue < 0:
611
+ B = self.reverse_ramp(B)
612
+
613
+ for i in range(len(B)):
614
+ palette[l, 0] = R[i]
615
+ palette[l, 1] = G[i]
616
+ palette[l, 2] = B[i]
617
+ l += 1
618
+
619
+ return palette
620
+
621
+ # # /*
622
+ # # * computes a color for each gray value between 0 and 255
623
+ # # */
624
+ def RGB_formula(self, i, equation):
625
+ """
626
+ Computes a color for each gray value between 0 and 255 based on the given equation.
627
+
628
+ Args:
629
+ i (int): The gray value.
630
+ equation (int): The equation index.
631
+
632
+ Returns:
633
+ float: The computed color value.
634
+
635
+ """
636
+ val = 0
637
+ cur_eq = abs(equation)
638
+
639
+ if cur_eq == 0 or cur_eq == 37 or cur_eq == 38 or cur_eq == 39:
640
+ # case 0:
641
+ # case 37:
642
+ # case 38:
643
+ # case 39:
644
+ val = 0
645
+ # break
646
+ elif cur_eq == 1:
647
+ val = 0.5
648
+ elif cur_eq == 2:
649
+ val = 1.0
650
+ elif cur_eq == 3:
651
+ val = i / 255.
652
+ elif cur_eq == 4:
653
+ val = (i / 255.) * (i / 255.)
654
+ elif cur_eq == 5:
655
+ val = (i / 255.) * (i / 255.) * (i / 255.)
656
+ elif cur_eq == 6:
657
+ val = (i / 255.) * (i / 255.) * (i / 255.) * (i / 255.)
658
+ elif cur_eq == 7:
659
+ val = math.sqrt(i / 255.)
660
+ elif cur_eq == 8:
661
+ val = math.sqrt(math.sqrt(i / 255.))
662
+ elif cur_eq == 9:
663
+ val = math.sin(i / 255. * 90. * math.pi / 180.)
664
+ elif cur_eq == 10:
665
+ val = math.cos(i / 255. * 90. * math.pi / 180.)
666
+ elif cur_eq == 11:
667
+ val = abs((i / 255.) - 0.5)
668
+ elif cur_eq == 12:
669
+ val = (2. * ((i / 255.) - 1.)) * (2. * ((i / 255.) - 1.))
670
+ elif cur_eq == 13:
671
+ val = math.sin(i / 255. * 180. * math.pi / 180.)
672
+ elif cur_eq == 14:
673
+ val = abs(math.cos(i / 255. * 90. * math.pi / 180.))
674
+ elif cur_eq == 15:
675
+ val = math.sin(i / 255. * 360. * math.pi / 180.)
676
+ elif cur_eq == 16:
677
+ val = math.cos(i / 255. * 360. * math.pi / 180.)
678
+ elif cur_eq == 17:
679
+ val = abs(math.sin(i / 255. * 360. * math.pi / 180.))
680
+ elif cur_eq == 18:
681
+ val = abs(math.cos(i / 255. * 360. * math.pi / 180.))
682
+ elif cur_eq == 19:
683
+ val = abs(math.sin(i / 255. * 720. * math.pi / 180.))
684
+ elif cur_eq == 20:
685
+ val = abs(math.cos(i / 255. * 720. * math.pi / 180.))
686
+ elif cur_eq == 21:
687
+ val = 3. * (i / 255.)
688
+ elif cur_eq == 22:
689
+ val = 3. * (i / 255.0) - 1.
690
+ elif cur_eq == 23:
691
+ val = 3. * (i / 255.0) - 2.
692
+ elif cur_eq == 24:
693
+ val = abs(3. * (i / 255.0) - 1.)
694
+ elif cur_eq == 25:
695
+ val = abs(3. * (i / 255.0) - 2.)
696
+ elif cur_eq == 26:
697
+ val = (3. * (i / 255.) - 1.) / 2.
698
+ elif cur_eq == 27:
699
+ val = (3. * (i / 255.) - 2.) / 2.
700
+ elif cur_eq == 28:
701
+ val = abs((3. * (i / 255.) - 1.) / 2.)
702
+ elif cur_eq == 29:
703
+ val = abs((3. * (i / 255.) - 2.) / 2.)
704
+ elif cur_eq == 30:
705
+ val = (i / 255.) / 0.32 - 0.78125
706
+ elif cur_eq == 31:
707
+ val = 2. * (i / 255.) - 0.84
708
+ elif cur_eq == 32:
709
+ val = (i / 255.)
710
+ # val = 4x1-2x+1.84x/0.08-11.5--> ? #show palette rgbformulae pr comprendre comment ils ont fait pr celui la ds gnuplot
711
+ elif cur_eq == 33:
712
+ val = abs(2. * (i / 255.) - 0.5)
713
+ elif cur_eq == 34:
714
+ val = 2. * (i / 255.)
715
+ elif cur_eq == 35:
716
+ val = 2. * (i / 255.) - 0.5
717
+ elif cur_eq == 36:
718
+ val = 2. * (i / 255.) - 1.
719
+
720
+ if val < 0.0:
721
+ val = 0.0
722
+
723
+ if val > 1.0:
724
+ val = 1.0
725
+
726
+ return val
727
+
728
+ def create_from_formula(self, red, green, blue):
729
+ """
730
+ Creates a color palette using formulas for the red, green, and blue channels.
731
+
732
+ Args:
733
+ red (str): The equation for the red channel.
734
+ green (str): The equation for the green channel.
735
+ blue (str): The equation for the blue channel.
736
+
737
+ Returns:
738
+ numpy.ndarray: The color palette as a NumPy array.
739
+
740
+ """
741
+ l = 0
742
+ palette = np.zeros((256, 3), dtype=np.uint8)
743
+ R = np.zeros((256,), dtype=np.uint8)
744
+ G = np.zeros((256,), dtype=np.uint8)
745
+ B = np.zeros((256,), dtype=np.uint8)
746
+
747
+ if red != "":
748
+ javaified_equation = self.javaifyEquation(red[0:1])
749
+ for i in range(256):
750
+ R[i] = (self.computeEquation(javaified_equation, "x", i / 255.) * 255.)
751
+
752
+ if green != "":
753
+ javaified_equation = self.javaifyEquation(green[0:1])
754
+ for i in range(256):
755
+ G[i] = (self.computeEquation(javaified_equation, "x", i / 255.) * 255.)
756
+
757
+ if blue != "":
758
+ javaified_equation = self.javaifyEquation(blue[0:1])
759
+ for i in range(256):
760
+ B[i] = (self.computeEquation(javaified_equation, "x", i / 255.) * 255.)
761
+
762
+ for i in range(256):
763
+ val = R[i]
764
+ if val < 0:
765
+ val = 0
766
+ if val > 255:
767
+ val = 255
768
+ R[i] = val
769
+
770
+ val = G[i]
771
+ if val < 0:
772
+ val = 0
773
+ if val > 255:
774
+ val = 255
775
+ G[i] = val
776
+
777
+ val = B[i]
778
+ if val < 0:
779
+ val = 0
780
+ if val > 255:
781
+ val = 255
782
+ B[i] = val
783
+
784
+ if red != "":
785
+ if red[0] == '-':
786
+ R = self.reverse_ramp(R)
787
+
788
+ if green != "":
789
+ if green[0] == '-':
790
+ G = self.reverse_ramp(G)
791
+
792
+ if blue != "":
793
+ if blue[0] == '-':
794
+ B = self.reverse_ramp(B)
795
+
796
+ for i in range(len(B)):
797
+ palette[l, 0] = R[i]
798
+ palette[l, 1] = G[i]
799
+ palette[l, 2] = B[i]
800
+ l += 1
801
+
802
+ return palette
803
+
804
+ def reverse_ramp(self, pal):
805
+ """
806
+ Reverses the color ramp by taking the negative of the intensity (255 - intensity).
807
+
808
+ Args:
809
+ pal (numpy.ndarray): The color ramp to be reversed.
810
+
811
+ Returns:
812
+ numpy.ndarray: The reversed color ramp.
813
+
814
+ """
815
+ tmp = np.zeros_like(pal)
816
+ for i in range(256):
817
+ tmp[255 - i] = pal[i]
818
+ return tmp
819
+
820
+ def createEmptyPalette(self):
821
+ """
822
+ Creates an empty palette.
823
+
824
+ Returns:
825
+ numpy.ndarray: The empty palette array.
826
+
827
+ """
828
+ return np.zeros((256, 3), dtype=np.uint8)
829
+
830
+ def create2(self, palette_name, equa1, equa2, equa3):
831
+ """
832
+ Creates a palette from a series of equations.
833
+
834
+ Args:
835
+ palette_name (str): The name of the palette.
836
+ equa1 (str): The equation for the first color channel.
837
+ equa2 (str): The equation for the second color channel.
838
+ equa3 (str): The equation for the third color channel.
839
+
840
+ Returns:
841
+ numpy.ndarray: The created palette.
842
+
843
+ # Examples:
844
+ # >>> palette_name = '37,38,-39'
845
+ # >>> equa1 = 'x + y'
846
+ # >>> equa2 = '2 * x - y'
847
+ # >>> equa3 = 'x - 2 * y'
848
+ # >>> result = PaletteCreator.create2(palette_name, equa1, equa2, equa3)
849
+ # >>> print(result)
850
+ # [[ 0 0 0]
851
+ # [255 0 0]
852
+ # [ 0 255 0]]
853
+
854
+ """
855
+ final_palette = None
856
+ try:
857
+ r = g = b = 3
858
+ if palette_name and "," in palette_name:
859
+ formula_size = len(palette_name.split(","))
860
+ if formula_size == 3:
861
+ r = int(palette_name.split(",")[0])
862
+ new_equa1 = self.create_compatible_equation(equa1, equa2, equa3, r)
863
+ g = int(palette_name.split(",")[1])
864
+ new_equa2 = self.create_compatible_equation(equa1, equa2, equa3, g)
865
+ b = int(palette_name.split(",")[2])
866
+ new_equa3 = self.create_compatible_equation(equa1, equa2, equa3, b)
867
+ equa1 = new_equa1
868
+ equa2 = new_equa2
869
+ equa3 = new_equa3
870
+
871
+ # print('passing there')
872
+
873
+ if abs(r) < 37 and abs(g) < 37 and abs(b) < 37:
874
+ return self.create3(palette_name)
875
+
876
+ if abs(r) >= 37 and abs(g) >= 37 and abs(b) >= 37:
877
+ palette1 = self.createEmptyPalette()
878
+ else:
879
+ palette1 = self.create3(palette_name)
880
+
881
+ # print('inside equa', equa1, equa2, equa3)
882
+ palette2 = self.create_from_formula(equa1, equa2, equa3)
883
+
884
+
885
+ # print(palette2.shape)
886
+
887
+ final_palette = np.zeros((palette1.shape[0], palette1.shape[1]), dtype=np.uint8)
888
+ l = 0
889
+ l1 = 0
890
+ l2 = 0
891
+ for i in range(256):
892
+ red1 = palette1[l1]
893
+ green1 = palette1[l1]
894
+ blue1 = palette1[l1]
895
+ l1 += 1
896
+ red2 = palette2[l2]
897
+ green2 = palette2[l2]
898
+ blue2 = palette2[l2]
899
+ l2 += 1
900
+ red = red2 if abs(r) >= 37 and abs(r) <= 39 else red1
901
+ green = green2 if abs(g) >= 37 and abs(g) <= 39 else green1
902
+ blue = blue2 if abs(b) >= 37 and abs(b) <= 39 else blue1
903
+
904
+ # print('final_palette', final_palette.shape)
905
+ # print('red', red)
906
+ # print('green', green)
907
+ # print('blue', blue)
908
+
909
+ final_palette[l, 0] = red[0]
910
+ final_palette[l, 1] = green[1]
911
+ final_palette[l, 2] = blue[2]
912
+ l += 1
913
+ except:
914
+ traceback.print_exc()
915
+
916
+ return final_palette
917
+
918
+ def create_compatible_equation(self, equa1, equa2, equa3, col):
919
+ """
920
+ Creates a compatible equation.
921
+
922
+ Args:
923
+ equa1 (str): The equation for the first color channel.
924
+ equa2 (str): The equation for the second color channel.
925
+ equa3 (str): The equation for the third color channel.
926
+ col (int): The color value.
927
+
928
+ Returns:
929
+ str: The compatible equation.
930
+
931
+ """
932
+ equa = ""
933
+ if col == 37:
934
+ equa = "+" + equa1
935
+ elif col == -37:
936
+ equa = "-(" + equa1 + ")"
937
+ elif col == 38:
938
+ equa = "+" + equa2
939
+ elif col == -38:
940
+ equa = "-(" + equa2 + ")"
941
+ elif col == 39:
942
+ equa = "+" + equa3
943
+ elif col == -39:
944
+ equa = "-(" + equa3 + ")"
945
+
946
+ return equa
947
+
948
+ def create3(self, palette_formula):
949
+ """
950
+ Creates a palette from its formula.
951
+
952
+ Args:
953
+ palette_formula (str): The formula for creating the palette.
954
+
955
+ Returns:
956
+ numpy.ndarray: The created palette.
957
+
958
+ Examples:
959
+ >>> p = PaletteCreator()
960
+ >>> result = p.create3('PACKING')
961
+ >>> print(result[:4])
962
+ [[64 64 64]
963
+ [64 64 64]
964
+ [64 64 64]
965
+ [64 64 64]]
966
+
967
+ """
968
+
969
+ if isinstance(palette_formula, str) and palette_formula.endswith(INVERTED_SUFFIX):
970
+ return self.reverse_ramp(self.create3(palette_formula[:-len(INVERTED_SUFFIX)]))
971
+
972
+ if palette_formula in self.matplot_lib_luts:
973
+ return cmap_to_numpy(palette_formula)
974
+
975
+ r = g = b = 3
976
+ if palette_formula is None:
977
+ palette = self.create5(r, g, b)
978
+ return palette
979
+
980
+ if "," in palette_formula.replace("hl", ""):
981
+ formula_size = len(palette_formula.split(","))
982
+ if formula_size == 3:
983
+ r = int(palette_formula.replace("hl", "").split(",")[0])
984
+ g = int(palette_formula.replace("hl", "").split(",")[1])
985
+ b = int(palette_formula.replace("hl", "").split(",")[2])
986
+ palette = self.create5(r, g, b)
987
+ else:
988
+ if palette_formula == 'PACKING':
989
+ return self.get_packing_lut()
990
+
991
+ palette = self.create5(r, g, b)
992
+
993
+ if palette_formula == self.list['HI_LO']:
994
+ palette[0, 0] = 0
995
+ palette[0, 1] = 0
996
+ palette[0, 2] = 255
997
+ palette[255, 0] = 255
998
+ palette[255, 1] = 0
999
+ palette[255, 2] = 0
1000
+
1001
+ return palette
1002
+
1003
+ def __show_lut(lut, title=None):
1004
+ """
1005
+ Displays the lookup table (LUT) as an image.
1006
+
1007
+ Args:
1008
+ lut (numpy.ndarray): The lookup table.
1009
+ title (str, optional): The title of the displayed image.
1010
+
1011
+ Returns:
1012
+ None
1013
+
1014
+ # Examples:
1015
+ # >>> lut = np.random.randint(0, 256, size=(256, 3), dtype=np.uint8)
1016
+ # >>> __show_lut(lut, title='LUT Image')
1017
+
1018
+ """
1019
+
1020
+ image = np.linspace(0, 255, 256, dtype=np.uint8)
1021
+ image = np.tile(image, (32, 1))
1022
+ image = apply_lut(image, lut, convert_to_RGB=True)
1023
+
1024
+ plt.imshow(image)
1025
+ if title is not None:
1026
+ plt.title(title)
1027
+ plt.show()
1028
+
1029
+ def lsm_LUT_to_numpy(lut):
1030
+ """
1031
+ Converts an LSM lookup table (LUT) to a NumPy array.
1032
+
1033
+ Args:
1034
+ lut (list): The LSM lookup table.
1035
+
1036
+ Returns:
1037
+ numpy.ndarray: The converted NumPy array representing the LUT.
1038
+
1039
+ Examples:
1040
+ >>> lut = [255, 0, 0, 0]
1041
+ >>> result = lsm_LUT_to_numpy(lut)
1042
+ >>> print(result[:3])
1043
+ [[0 0 0]
1044
+ [1 0 0]
1045
+ [2 0 0]]
1046
+
1047
+ """
1048
+
1049
+ R = np.linspace(0, lut[0], 256, dtype=np.uint8)
1050
+ G = np.linspace(0, lut[1], 256, dtype=np.uint8)
1051
+ B = np.linspace(0, lut[2], 256, dtype=np.uint8)
1052
+ lut = np.stack([R, G, B], axis=-1)
1053
+ return lut
1054
+
1055
+ def R_G_B_lut_to_int24_lut(lut):
1056
+ """
1057
+ Converts an RGB lookup table (LUT) to an int24 LUT.
1058
+
1059
+ Args:
1060
+ lut (numpy.ndarray): The RGB lookup table.
1061
+
1062
+ Returns:
1063
+ numpy.ndarray: The converted int24 LUT.
1064
+
1065
+ Examples:
1066
+ >>> lut = np.array([[255, 0, 0], [0, 255, 0], [0, 0, 255]], dtype=np.uint8)
1067
+ >>> result = R_G_B_lut_to_int24_lut(lut)
1068
+ >>> print(result)
1069
+ [16711680 65280 255]
1070
+
1071
+ """
1072
+
1073
+ if lut is None:
1074
+ return None
1075
+
1076
+ if isinstance(lut, list):
1077
+ lut = lsm_LUT_to_numpy(lut)
1078
+
1079
+ # print('dabu lut', lut, type(lut))
1080
+
1081
+
1082
+ if len(lut.shape) == 1:
1083
+ return lut
1084
+
1085
+ if lut.shape[-1] == 3:
1086
+ return lut[..., 0].astype(np.uint32) << 16 | lut[..., 1].astype(np.uint32) << 8 | lut[..., 2].astype(
1087
+ np.uint32)
1088
+ else:
1089
+ return lut[0, ...].astype(np.uint32) << 16 | lut[1, ...].astype(np.uint32) << 8 | lut[2, ...].astype(
1090
+ np.uint32)
1091
+
1092
+ def apply_lut(img, lut, convert_to_RGB=False, min=None, max=None, do_not_interpolate=False):
1093
+ """
1094
+ Applies a lookup table (LUT) to an image.
1095
+
1096
+ Args:
1097
+ img (numpy.ndarray): The input image.
1098
+ lut (numpy.ndarray or list): The lookup table.
1099
+ convert_to_RGB (bool, optional): Flag indicating whether to convert the image to RGB.
1100
+ min (float, optional): The minimum value for color coding.
1101
+ max (float, optional): The maximum value for color coding.
1102
+
1103
+ Returns:
1104
+ numpy.ndarray: The image with the LUT applied.
1105
+
1106
+ Examples:
1107
+ >>> img = np.random.randint(0, 256, size=(100, 100), dtype=np.uint8)
1108
+ >>> lut = np.random.randint(0, 256, size=(256, 3), dtype=np.uint8)
1109
+ >>> result = apply_lut(img, lut, convert_to_RGB=True)
1110
+ >>> print(result.shape)
1111
+ (100, 100, 3)
1112
+
1113
+ """
1114
+
1115
+ real_max = img.max()
1116
+ real_min = img.min()
1117
+
1118
+ if do_not_interpolate:
1119
+ pass
1120
+ else:
1121
+ if min is not None and max is not None:
1122
+ if img.max() > max:
1123
+ img[img > max] = max
1124
+ if img.min() < min:
1125
+ img[img < min] = min
1126
+
1127
+ img = np.interp(img, (real_min if real_min > min else min, real_max if real_max < max else max), (
1128
+ real_min if real_min > min else min,
1129
+ real_max if real_max < max else max))
1130
+
1131
+ img = np.interp(img, (min, max), (0, 255)).astype(np.uint8)
1132
+
1133
+ else:
1134
+ # there was a case where this interp did not work I guess it is for black images where max == min but need to check that and then I need a way to handle these images -> maybe set them to 0??? maybe --> need to try and think
1135
+ img = np.interp(img, (img.min(), img.max()), (0, 255)).astype(np.uint8)
1136
+
1137
+ if lut is None:
1138
+ return img
1139
+
1140
+ int24_lut = R_G_B_lut_to_int24_lut(lut)
1141
+
1142
+ image = int24_lut[img]
1143
+ if convert_to_RGB:
1144
+ image = int24_to_RGB(image)
1145
+
1146
+ return image
1147
+
1148
+
1149
+ def convert_ome_lut(original_lut):
1150
+ """
1151
+ Convert a 3-element 16-bit LUT to a (256, 3) 8-bit LUT.
1152
+
1153
+ Parameters:
1154
+ original_lut (list or np.array): A 3-element list or array representing R, G, B values in 16-bit range.
1155
+
1156
+ Returns:
1157
+ np.array: A (256, 3) numpy array representing the 8-bit LUT.
1158
+ """
1159
+ # Ensure input is a numpy array and of correct type
1160
+ original_lut = np.array(original_lut, dtype=np.uint16)
1161
+
1162
+ # Check if input has correct shape
1163
+ if original_lut.shape != (3,):
1164
+ raise ValueError("Input LUT must have exactly 3 elements")
1165
+
1166
+ # Scale down to 8-bit range (0-255)
1167
+ scaled_lut = (original_lut / 65535 * 255).astype(np.uint8)
1168
+
1169
+ # Create a (256, 3) array
1170
+ full_lut = np.zeros((256, 3), dtype=np.uint8)
1171
+
1172
+ # Fill each channel with a gradient
1173
+ for i in range(3):
1174
+ full_lut[:, i] = np.linspace(0, scaled_lut[i], 256, dtype=np.uint8)
1175
+
1176
+ return full_lut
1177
+
1178
+
1179
+ def matplotlib_to_TA(lut_name='viridis'):
1180
+ """
1181
+ Converts a Matplotlib colormap to a lookup table (LUT) compatible with TA software.
1182
+
1183
+ Args:
1184
+ lut_name (str, optional): The name of the Matplotlib colormap.
1185
+
1186
+ Returns:
1187
+ numpy.ndarray: The converted lookup table (LUT).
1188
+
1189
+ Examples:
1190
+ >>> lut = matplotlib_to_TA('viridis')
1191
+ >>> print(lut.shape)
1192
+ (256, 3)
1193
+
1194
+ """
1195
+
1196
+ cm = plt.get_cmap(lut_name)
1197
+ cm = cm(range(256)) * 255
1198
+ cm = cm[..., 0:3].astype(np.uint8)
1199
+ return cm
1200
+
1201
+
1202
+ def get_matplotlib_available_luts():
1203
+ """
1204
+ Returns a list of available Matplotlib colormaps.
1205
+
1206
+ Returns:
1207
+ list: The list of available colormaps.
1208
+
1209
+ Examples:
1210
+ >>> colormaps = get_matplotlib_available_luts()
1211
+ >>> print(colormaps[:2])
1212
+ ['magma', 'inferno']
1213
+
1214
+ """
1215
+
1216
+ return plt.colormaps()
1217
+
1218
+
1219
+ if __name__ == '__main__':
1220
+ # keep to avoid bugs
1221
+ import matplotlib
1222
+ matplotlib.use('TkAgg')
1223
+
1224
+ if True:
1225
+ # lsm type of Lut --> may need be changed
1226
+ lut = [255,128,255]
1227
+ lut = lsm_LUT_to_numpy(lut)
1228
+ print(lut)
1229
+ print(type(lut))
1230
+ img = create_ramp(300, 1800, lut)
1231
+ plt.imshow(img)
1232
+ plt.show()
1233
+ import sys
1234
+ sys.exit(0)
1235
+
1236
+ if True:
1237
+ lutcreator = PaletteCreator()
1238
+ lut = lutcreator.create3(lutcreator.list['MAGENTA_HOT']) #MAGENTA --> create it --> PURPLE instead
1239
+
1240
+ img = create_ramp(300, 1800,lut) # maybe ask at some point for the bg color --> TODO but almost all ok in fact
1241
+ plt.imshow(img)
1242
+ plt.show()
1243
+ import sys
1244
+ sys.exit(0)
1245
+
1246
+ if True:
1247
+ lutcreator = PaletteCreator()
1248
+ lut = lutcreator.create3(lutcreator.list['PACKING'])
1249
+
1250
+ img = create_ramp(300, 1800,lut) # maybe ask at some point for the bg color --> TODO but almost all ok in fact
1251
+ plt.imshow(img)
1252
+ plt.show()
1253
+ import sys
1254
+ sys.exit(0)
1255
+
1256
+
1257
+ if False:
1258
+ # create a ramp
1259
+ lutcreator = PaletteCreator()
1260
+ lut = lutcreator.create3(lutcreator.list['DNA'])
1261
+
1262
+ img = create_ramp(300, 1800,lut) # maybe ask at some point for the bg color --> TODO but almost all ok in fact
1263
+ plt.imshow(img)
1264
+ plt.show()
1265
+
1266
+ if False:
1267
+
1268
+ print(cmap_to_numpy('gray')) # 'viridis' 'rainbow' 'gray'
1269
+
1270
+
1271
+ if True:
1272
+ lutcreator = PaletteCreator()
1273
+
1274
+ luts = lutcreator.list
1275
+
1276
+ # lut.create_from_formula('22','13','-23')
1277
+ lut = lutcreator.create2('34,35,36', 'x', 'x', 'x') # bingo ça marche enfin!!!
1278
+ # print(lut)
1279
+ # print(lut.shape, lut.max(), lut.min())
1280
+
1281
+ lut = lutcreator.create3(lutcreator.list['DNA'])
1282
+ # print(lut)
1283
+ # print(lut.shape, lut.max(), lut.min())
1284
+
1285
+ # random lut each time
1286
+ # print(list(luts.keys()))
1287
+
1288
+ random_lut = random.choice(list(luts.keys()))
1289
+ # print('random.lut', random_lut)
1290
+
1291
+ # random_lut = 'HI_LO'
1292
+ lut = lutcreator.create3(luts[random_lut])
1293
+
1294
+ # TODO maybe do a loop over all luts maybe also on an image --> easy way to select best luts
1295
+
1296
+ __show_lut(lut, title=random_lut)
1297
+ # all seems ok now
1298
+ # plt.imshow(create_ramp(0,255, lut))
1299
+ # plt.show()
1300
+
1301
+
1302
+ # print(get_matplotlib_available_luts()) # --> y en a vraiment bcp --> je pourrais aussi les ajouter à ma db de luts
1303
+
1304
+ # can i do a visual lut selector or something that applies luts to the image à la tinder so that one can select the most appropriate LUT --> in fact that is not so hard to do now that everything is done through indexing
1305
+ # img = create_ramp(0, 255, None)
1306
+ # img = create_ramp(-1, 1, None) # --> super easy instead
1307
+
1308
+