napari-musa 1.0.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.
@@ -0,0 +1,864 @@
1
+ """Widget to manage and process the data"""
2
+
3
+ import sys
4
+ from os.path import dirname, splitext
5
+
6
+ sys.path.append(dirname(dirname(__file__)))
7
+
8
+ import h5py
9
+ import napari
10
+ import numpy as np
11
+ from magicgui.widgets import (
12
+ CheckBox,
13
+ ComboBox,
14
+ Container,
15
+ FloatSpinBox,
16
+ PushButton,
17
+ SpinBox,
18
+ )
19
+ from napari.utils.notifications import show_info, show_warning
20
+ from PIL import Image
21
+ from qtpy.QtCore import QTimer, Signal
22
+ from qtpy.QtWidgets import (
23
+ QFileDialog,
24
+ QGroupBox,
25
+ QHBoxLayout,
26
+ QInputDialog,
27
+ QScrollArea,
28
+ QVBoxLayout,
29
+ QWidget,
30
+ )
31
+ from scipy.io import savemat
32
+
33
+ from napari_musa.modules.functions import (
34
+ HSI2RGB,
35
+ SVD_denoise,
36
+ create_mask,
37
+ crop_xy,
38
+ derivative,
39
+ despike,
40
+ dimensionality_reduction,
41
+ open_file,
42
+ preprocessing,
43
+ reduce_spatial_dimension_dwt,
44
+ )
45
+
46
+
47
+ class DataManager(QWidget): # From QWidget
48
+ """ """
49
+
50
+ mode_added = Signal()
51
+
52
+ def __init__(self, viewer: napari.Viewer, data):
53
+ """ """
54
+ super().__init__() # Initialize the QWidget
55
+ self.viewer = viewer
56
+ self.data = data
57
+ # Configure the scroll area
58
+ scroll = QScrollArea()
59
+ scroll.setWidgetResizable(True)
60
+ content_widget = QWidget() # Container widget
61
+ content_layout = QVBoxLayout(
62
+ content_widget
63
+ ) # Vertical layout: organize widgets from top to bottom
64
+ # Configure UI
65
+ self.createUI(
66
+ content_layout
67
+ ) # The function to create the UI that fills the content_layout
68
+ # Configure principal layout
69
+ scroll.setWidget(
70
+ content_widget
71
+ ) # content_widget is a content of scroll area
72
+ main_layout = QVBoxLayout(self)
73
+ main_layout.addWidget(scroll)
74
+ self.setLayout(main_layout)
75
+
76
+ def createUI(self, layout):
77
+ """Create the components for the UI"""
78
+ layout.addWidget(self.create_open_box())
79
+ layout.addWidget(self.create_save_layer_box())
80
+ layout.addWidget(self.create_processing_box())
81
+ layout.addWidget(self.create_manipulation_box())
82
+ layout.addWidget(self.create_dimred_box())
83
+
84
+ # %% Creation of UI boxes
85
+ # OPEN BOX
86
+ def create_open_box(self):
87
+ """Create box and elements for file opening"""
88
+ open_box = QGroupBox("Open file")
89
+ open_layout = QVBoxLayout()
90
+ open_layout.addSpacing(10)
91
+ # Elements
92
+ self.modes_combobox = ComboBox(
93
+ choices=self.data.modes, label="Select the imaging mode"
94
+ )
95
+ open_btn = PushButton(text="Open File")
96
+ open_btn.clicked.connect(self.open_btn_f)
97
+
98
+ # Add widgets to the layout
99
+ open_layout.addWidget(
100
+ Container(
101
+ widgets=[self.modes_combobox], layout="horizontal"
102
+ ).native
103
+ )
104
+ open_layout.addWidget(
105
+ Container(
106
+ widgets=[
107
+ open_btn,
108
+ ],
109
+ layout="horizontal",
110
+ ).native
111
+ )
112
+ open_box.setLayout(open_layout)
113
+ return open_box
114
+
115
+ def create_save_layer_box(self):
116
+ """Create box and elements to save a selected layer"""
117
+ save_box = QGroupBox("Save")
118
+ save_layout = QVBoxLayout()
119
+ save_layout.addSpacing(10)
120
+ savedata_btn = PushButton(text="Save selected layer")
121
+ savedata_btn.clicked.connect(self.savedata_btn_f)
122
+
123
+ save_layout.addWidget(
124
+ Container(
125
+ widgets=[
126
+ savedata_btn,
127
+ ],
128
+ layout="horizontal",
129
+ ).native
130
+ )
131
+ save_box.setLayout(save_layout)
132
+ return save_box
133
+
134
+ # PREPROCESSING BOX
135
+ def create_processing_box(self):
136
+ """Preprocessing of the data"""
137
+ processing_box = QGroupBox("Processing")
138
+ processing_layout = QVBoxLayout()
139
+ processing_layout.addSpacing(20)
140
+ #
141
+ # Crop
142
+ crop_box = QGroupBox("Crop")
143
+ crop_layout = self.create_crop_section()
144
+ crop_box.setLayout(crop_layout)
145
+ processing_layout.addSpacing(20)
146
+ processing_layout.addWidget(crop_box)
147
+
148
+ # Mask
149
+ mask_box = QGroupBox("Mask")
150
+ mask_layout = self.create_mask_section()
151
+ mask_box.setLayout(mask_layout)
152
+ processing_layout.addSpacing(20)
153
+ processing_layout.addWidget(mask_box)
154
+
155
+ #
156
+ # Data cleaning
157
+ cleaning_box = QGroupBox("Data cleaning")
158
+ cleaning_layout = QVBoxLayout() # one layout for each box box
159
+ cleaning_layout.addSpacing(10)
160
+ # Despike
161
+ despike_layout = self.create_despike_section()
162
+ cleaning_layout.addLayout(despike_layout)
163
+ # SVD preprocessing
164
+ SVD_denoise_layout = self.create_SVD_denoise_section()
165
+ cleaning_layout.addLayout(SVD_denoise_layout)
166
+ # Median filter
167
+ medfilt_layout = self.create_medfilt_section()
168
+ cleaning_layout.addLayout(medfilt_layout)
169
+ # Gaussian filter
170
+ gaussian_layout = self.create_gaussian_section()
171
+ cleaning_layout.addLayout(gaussian_layout)
172
+ # Savitzky-Golay
173
+ savgol_layout = self.create_savgol_section()
174
+ cleaning_layout.addLayout(savgol_layout)
175
+ # Background
176
+ bkg_layout = self.create_bkg_section()
177
+ cleaning_layout.addLayout(bkg_layout)
178
+ # Preprocessing button
179
+ processing_btn_layout = QVBoxLayout()
180
+ preprocessing_btn = PushButton(text="Process data")
181
+ preprocessing_btn.clicked.connect(self.preprocessing_btn_f)
182
+ processing_btn_layout.addWidget(
183
+ Container(widgets=[preprocessing_btn]).native
184
+ )
185
+ cleaning_layout.addLayout(processing_btn_layout)
186
+ cleaning_box.setLayout(cleaning_layout)
187
+ # Add cleaning box to processing layout
188
+ processing_layout.addWidget(cleaning_box)
189
+ processing_box.setLayout(processing_layout)
190
+ return processing_box
191
+
192
+ # MANIPULATION BOX
193
+ def create_manipulation_box(self):
194
+ manipulation_box = QGroupBox("Data manipulation")
195
+ manipulation_layout = QVBoxLayout()
196
+ manipulation_layout.addSpacing(20)
197
+ derivative_btn = PushButton(text="Create first derivative")
198
+ derivative_btn.clicked.connect(self.derivative_btn_f)
199
+ manipulation_layout.addWidget(
200
+ Container(
201
+ widgets=[
202
+ derivative_btn,
203
+ ],
204
+ layout="horizontal",
205
+ ).native
206
+ )
207
+
208
+ manipulation_box.setLayout(manipulation_layout)
209
+ return manipulation_box
210
+
211
+ # DIMENSIONAL REDUCTION BOX
212
+ def create_dimred_box(self):
213
+ dimred_box = QGroupBox("Dimensionality reduction")
214
+ dimred_layout = QVBoxLayout()
215
+ dimred_layout.addSpacing(20)
216
+ self.spectral_dimred_checkbox = CheckBox(text="Spectral Reduction")
217
+ self.spatial_dimred_checkbox = CheckBox(text="Spatial Reduction")
218
+ dimred_btn = PushButton(text="Reduce data")
219
+ dimred_btn.clicked.connect(self.dimred_btn_f)
220
+ dimred_layout.addWidget(
221
+ Container(
222
+ widgets=[
223
+ self.spectral_dimred_checkbox,
224
+ self.spatial_dimred_checkbox,
225
+ dimred_btn,
226
+ ]
227
+ ).native
228
+ )
229
+ dimred_box.setLayout(dimred_layout)
230
+ return dimred_box
231
+
232
+ # UI SUB-BOXES
233
+ def create_crop_section(self):
234
+ """Crop and create mask"""
235
+ crop_xy_layout = QHBoxLayout()
236
+ crop_xy_btn = PushButton(text="Crop \n (rectangle shape)")
237
+ crop_xy_btn.clicked.connect(self.crop_xy_btn_f)
238
+ crop_xy_layout.addWidget(Container(widgets=[crop_xy_btn]).native)
239
+
240
+ crop_wl_layout = QHBoxLayout()
241
+ self.min_wl_spinbox = SpinBox(
242
+ min=0, max=10000, step=1, value=0, label="Min. WL channel"
243
+ )
244
+ self.max_wl_spinbox = SpinBox(
245
+ min=0, max=10000, step=1, value=100, label="Max. WL channel"
246
+ )
247
+ crop_wl_btn = PushButton(text="Crop wavelengths")
248
+ crop_wl_btn.clicked.connect(self.crop_wl_btn_f)
249
+ crop_wl_layout.addWidget(
250
+ Container(
251
+ widgets=[
252
+ self.min_wl_spinbox,
253
+ self.max_wl_spinbox,
254
+ crop_wl_btn,
255
+ ]
256
+ ).native
257
+ )
258
+ crop_xy_layout.addLayout(crop_wl_layout)
259
+ return crop_xy_layout
260
+
261
+ def create_mask_section(self):
262
+ mask_layout = QHBoxLayout()
263
+ self.mask_reduced_checkbox = CheckBox(text="From reduced dataset")
264
+ mask_btn = PushButton(text="Create Mask \n(from Label layer)")
265
+ mask_btn.clicked.connect(self.mask_btn_f)
266
+ mask_layout.addWidget(
267
+ Container(
268
+ widgets=[
269
+ self.mask_reduced_checkbox,
270
+ mask_btn,
271
+ ],
272
+ layout="horizontal",
273
+ ).native
274
+ )
275
+ return mask_layout
276
+
277
+ def create_despike_section(self):
278
+ """UI of despike section"""
279
+ despike_layout = QHBoxLayout()
280
+ despike_btn = PushButton(text="Despike")
281
+ despike_btn.clicked.connect(self.despike_btn_f)
282
+ despike_layout.addWidget(Container(widgets=[despike_btn]).native)
283
+ return despike_layout
284
+
285
+ def create_SVD_denoise_section(self):
286
+ """UI of SVD denoising section"""
287
+ SVD_layout = QHBoxLayout()
288
+ SVD_btn = PushButton(text="SVD Calculation")
289
+ SVD_btn.clicked.connect(self.SVD_btn_f)
290
+ self.SVD_spinbox = SpinBox(
291
+ min=1, max=1000, value=5, step=1, name="N. of components"
292
+ )
293
+ SVD_denoise_btn = PushButton(text="SVD Denoise")
294
+ SVD_denoise_btn.clicked.connect(self.SVD_denoise_btn_f)
295
+
296
+ SVD_layout.addWidget(Container(widgets=[SVD_btn]).native)
297
+ SVD_layout.addWidget(
298
+ Container(widgets=[self.SVD_spinbox, SVD_denoise_btn]).native
299
+ )
300
+ return SVD_layout
301
+
302
+ def create_medfilt_section(self):
303
+ """Median filter"""
304
+ medfilt_layout = QHBoxLayout()
305
+ # self.medfilt_checkbox = CheckBox(text="2D Gaussian filter")
306
+ # self.medfilt_spinbox = FloatSpinBox(
307
+ # min=0.3, max=5.0, value=1.0, step=0.1, name="Sigma"
308
+ self.medfilt_checkbox = CheckBox(text="2D medfilt")
309
+ self.medfilt_spinbox = SpinBox(
310
+ min=1, max=101, value=5, step=2, name="Window"
311
+ )
312
+ medfilt_layout.addWidget(
313
+ Container(widgets=[self.medfilt_checkbox]).native
314
+ )
315
+ medfilt_layout.addWidget(
316
+ Container(widgets=[self.medfilt_spinbox]).native
317
+ )
318
+ return medfilt_layout
319
+
320
+ def create_gaussian_section(self):
321
+ """gaussian filter"""
322
+ gaussian_layout = QHBoxLayout()
323
+ self.gaussian_checkbox = CheckBox(text="2D Gaussian filter")
324
+ self.gaussian_spinbox = FloatSpinBox(
325
+ min=0.3, max=5.0, value=1.0, step=0.1, name="Sigma"
326
+ )
327
+ gaussian_layout.addWidget(
328
+ Container(widgets=[self.gaussian_checkbox]).native
329
+ )
330
+ gaussian_layout.addWidget(
331
+ Container(widgets=[self.gaussian_spinbox]).native
332
+ )
333
+ return gaussian_layout
334
+
335
+ def create_savgol_section(self):
336
+ """Savitzky-Golay"""
337
+ savgol_layout = QHBoxLayout()
338
+ savgol_variables_layout = QHBoxLayout()
339
+ self.savgol_checkbox = CheckBox(text="Savitzky-Golay filter")
340
+ self.savgolw_spinbox = SpinBox(
341
+ min=1, max=100, value=11, step=2, name="Window"
342
+ )
343
+ self.savgolp_spinbox = SpinBox(
344
+ min=1, max=100, value=3, step=2, name="Polynom"
345
+ )
346
+ savgol_layout.addWidget(
347
+ Container(widgets=[self.savgol_checkbox]).native
348
+ )
349
+ savgol_variables_layout.addWidget(
350
+ Container(
351
+ widgets=[self.savgolw_spinbox, self.savgolp_spinbox]
352
+ ).native
353
+ )
354
+ savgol_layout.addLayout(savgol_variables_layout)
355
+ return savgol_layout
356
+
357
+ def create_bkg_section(self):
358
+ """Background correction"""
359
+ bkg_layout = QHBoxLayout()
360
+ bkg_variables_layout = QHBoxLayout()
361
+
362
+ self.bkg_checkbox = CheckBox(text="Background correction (SNIP)")
363
+ self.bkgw_spinbox = SpinBox(
364
+ min=1, max=1000, value=30, step=2, name="Window"
365
+ )
366
+ bkg_layout.addWidget(Container(widgets=[self.bkg_checkbox]).native)
367
+ bkg_variables_layout.addWidget(
368
+ Container(widgets=[self.bkgw_spinbox]).native
369
+ )
370
+ bkg_layout.addLayout(bkg_variables_layout)
371
+ return bkg_layout
372
+
373
+ # %% Button functions
374
+ def open_btn_f(self):
375
+ """ """
376
+ self.data.filepath, _ = QFileDialog.getOpenFileName()
377
+ print(f"The data with path {self.data.filepath} will now be opened")
378
+ data_mode = self.modes_combobox.value
379
+
380
+ self.data.hypercubes[data_mode], self.data.wls[data_mode] = open_file(
381
+ self.data.filepath
382
+ )
383
+ if isinstance(self.data.hypercubes[data_mode], int) and isinstance(
384
+ self.data.wls[data_mode], int
385
+ ):
386
+ hsi_cube_name, ok1 = QInputDialog.getText(
387
+ None,
388
+ "Name of the spectral hypercube",
389
+ "Insert the variable name of the spectral hypercube:",
390
+ )
391
+ wl_name, ok2 = QInputDialog.getText(
392
+ None,
393
+ "Name of the wavelengths vector",
394
+ "Insert the variable name of the wavelengths vector:",
395
+ )
396
+
397
+ if ok1 and ok2:
398
+ self.data.hypercubes[data_mode], self.data.wls[data_mode] = (
399
+ open_file(
400
+ self.data.filepath,
401
+ hsi_cube_var=hsi_cube_name,
402
+ wl_var=wl_name,
403
+ )
404
+ )
405
+
406
+ self.viewer.add_image(
407
+ self.data.hypercubes[data_mode].transpose(
408
+ 2, 0, 1
409
+ ), # napari wants (channels, height, width)
410
+ name=str(data_mode),
411
+ metadata={"type": "hyperspectral_cube"},
412
+ )
413
+ # Array for spatial cropping
414
+ # self.crop_array = [
415
+ # 0,
416
+ # 0,
417
+ # self.data.hypercubes[data_mode].shape[0],
418
+ # self.data.hypercubes[data_mode].shape[1],
419
+ # ]
420
+
421
+ def savedata_btn_f(self):
422
+ data_mode = self.modes_combobox.value
423
+ selected_layer = self.viewer.layers.selection.active
424
+ cube_types = [
425
+ "hyperspectral_cube",
426
+ "reduced_hsi_cube",
427
+ "masked_hsi_cube",
428
+ ]
429
+ rgb_types = ["rgb", "reduced_rgb", "masked_rgb", "false_rgb"]
430
+ if (
431
+ selected_layer
432
+ and selected_layer.metadata.get("type") in cube_types
433
+ ):
434
+ save_dict = {
435
+ "data": selected_layer.data.transpose(1, 2, 0),
436
+ "WL": self.data.wls[data_mode],
437
+ }
438
+ filename, _ = QFileDialog.getSaveFileName(
439
+ self,
440
+ "Save selected dataset",
441
+ "",
442
+ "MATLAB file (*.mat);;HDF5 file (*.h5)",
443
+ )
444
+ savemat(filename, save_dict)
445
+
446
+ if not filename:
447
+ return # the user has canceled the save dialog
448
+
449
+ ext = splitext(filename)[1].lower()
450
+ if ext == "":
451
+ if _.startswith("MATLAB"):
452
+ filename += ".mat"
453
+ ext = ".mat"
454
+ elif _.startswith("HDF5"):
455
+ filename += ".h5"
456
+ ext = ".h5"
457
+
458
+ if ext == ".mat":
459
+ savemat(filename, save_dict)
460
+ elif ext == ".h5":
461
+ with h5py.File(filename, "w") as f:
462
+ f.create_dataset(
463
+ "data", data=self.data.hypercubes[data_mode]
464
+ )
465
+ f.create_dataset("WL", data=self.data.wls[data_mode])
466
+
467
+ # RGB
468
+ elif (
469
+ selected_layer and selected_layer.metadata.get("type") in rgb_types
470
+ ):
471
+ filename, _ = QFileDialog.getSaveFileName(
472
+ self,
473
+ "Save selected rgb",
474
+ "",
475
+ "PNG image (*.png);;JPEG image (*.jpg *.jpeg)",
476
+ )
477
+ if not filename:
478
+ return
479
+ if not (
480
+ filename.lower().endswith(".png")
481
+ or filename.lower().endswith(".jpg")
482
+ or filename.lower().endswith(".jpeg")
483
+ ):
484
+ if _.startswith("PNG"):
485
+ filename += ".png"
486
+ else:
487
+ filename += ".jpg"
488
+ rgb_data = selected_layer.data
489
+ if rgb_data.dtype != "uint8":
490
+ rgb_data = (
491
+ 255
492
+ * (rgb_data - rgb_data.min())
493
+ / (rgb_data.max() - rgb_data.min())
494
+ ).astype("uint8")
495
+ rgb_image = Image.fromarray(rgb_data[..., :3])
496
+ rgb_image.save(filename)
497
+
498
+ # LABEL LAYER
499
+ elif isinstance(selected_layer, napari.layers.Labels):
500
+ filename, _ = QFileDialog.getSaveFileName(
501
+ self,
502
+ "Save selected label layer",
503
+ "",
504
+ "PNG image (*.png);;JPEG image (*.jpg *.jpeg)",
505
+ )
506
+ if not filename:
507
+ return
508
+ if not (
509
+ filename.lower().endswith(".png")
510
+ or filename.lower().endswith(".jpg")
511
+ or filename.lower().endswith(".jpeg")
512
+ ):
513
+ if _.startswith("PNG"):
514
+ filename += ".png"
515
+ else:
516
+ filename += ".jpg"
517
+ labels = selected_layer.data.astype(np.uint8)
518
+ colormap = selected_layer.colormap
519
+ lut = (
520
+ colormap.map(np.arange(labels.max() + 1))[:, :3] * 255
521
+ ).astype(np.uint8)
522
+ colored_labels = lut[labels]
523
+ alpha = np.where(labels == 0, 0, 255).astype(
524
+ np.uint8
525
+ ) # 255 = opaque
526
+ rgba = np.dstack((colored_labels, alpha)) # (H, W, 4)
527
+ img = Image.fromarray(rgba, mode="RGBA")
528
+ img.save(filename)
529
+
530
+ def crop_xy_btn_f(self):
531
+ """ """
532
+ data_mode = self.modes_combobox.value
533
+ selected_layers = list(self.viewer.layers.selection)
534
+ print(selected_layers)
535
+ shape_layer = [
536
+ layer
537
+ for layer in selected_layers
538
+ if isinstance(layer, napari.layers.Shapes)
539
+ ]
540
+ # If there are not selected shape layers
541
+ if not shape_layer or len(shape_layer[0].data) == 0:
542
+ show_warning("No shape layer or rect found")
543
+ print("No shape layer or rect found")
544
+ return None
545
+
546
+ shape = shape_layer[0].data[0]
547
+ if self.data.rgb.get(data_mode) is not None:
548
+ print(self.data.rgb[data_mode].shape)
549
+ self.data.hypercubes[data_mode], self.data.rgb[data_mode] = (
550
+ crop_xy(
551
+ self.data.hypercubes[data_mode],
552
+ shape,
553
+ rgb=self.data.rgb[data_mode],
554
+ )
555
+ )
556
+ print(self.data.rgb[data_mode].shape)
557
+ if any(
558
+ layer.name == str(data_mode) + " RGB"
559
+ for layer in self.viewer.layers
560
+ ):
561
+ self.viewer.layers.remove(str(data_mode) + " RGB")
562
+ self.viewer.add_image(
563
+ self.data.rgb[data_mode],
564
+ name=str(data_mode) + " RGB",
565
+ rgb=True,
566
+ metadata={"type": "rgb"},
567
+ )
568
+ else:
569
+ self.data.hypercubes[data_mode] = crop_xy(
570
+ self.data.hypercubes[data_mode], shape
571
+ )
572
+ if any(layer.name == str(data_mode) for layer in self.viewer.layers):
573
+ self.viewer.layers.remove(str(data_mode))
574
+ self.viewer.add_image(
575
+ self.data.hypercubes[data_mode].transpose(2, 0, 1),
576
+ name=str(data_mode),
577
+ metadata={"type": "hyperspectral_cube"},
578
+ )
579
+ return
580
+
581
+ def crop_wl_btn_f(self):
582
+ """ """
583
+ data_mode = self.modes_combobox.value
584
+ min_wl = self.min_wl_spinbox.value
585
+ max_wl = self.max_wl_spinbox.value
586
+ self.data.hypercubes[data_mode] = self.data.hypercubes[data_mode][
587
+ :, :, min_wl:max_wl
588
+ ]
589
+ self.data.wls[data_mode] = self.data.wls[data_mode][min_wl:max_wl]
590
+ print(f"Cropped shape: {self.data.hypercubes[data_mode].shape}")
591
+ if any(layer.name == str(data_mode) for layer in self.viewer.layers):
592
+ self.viewer.layers.remove(str(data_mode))
593
+ self.viewer.add_image(
594
+ self.data.hypercubes[data_mode].transpose(2, 0, 1),
595
+ name=str(data_mode),
596
+ metadata={"type": "hyperspectral_cube"},
597
+ )
598
+ return
599
+
600
+ def mask_btn_f(self):
601
+ """ """
602
+ data_mode = self.modes_combobox.value
603
+ # SELECT LABEL LAYER
604
+ # takes all the layers but the seleciton is only in the image (WL) in which i've done it
605
+ active_layer = self.viewer.layers.selection.active
606
+ print(isinstance(active_layer, napari.layers.Labels))
607
+ if isinstance(active_layer, napari.layers.Labels):
608
+ labels_layer = active_layer.data
609
+ else:
610
+ show_warning("The selected layer is not a label layer")
611
+ return
612
+
613
+ # If coming from UMAP, we don't need to do np.sum
614
+ print(labels_layer.shape)
615
+ binary_mask = np.where(labels_layer == 0, np.nan, 1).astype(float)
616
+
617
+ if self.mask_reduced_checkbox.value:
618
+ data = self.data.hypercubes_red[data_mode]
619
+ rgb = self.data.rgb_red[data_mode]
620
+ else:
621
+ data = self.data.hypercubes[data_mode]
622
+ if self.data.rgb.get(data_mode) is None:
623
+ show_warning("RGB image not found \nRGB created")
624
+ self.data.rgb[data_mode] = HSI2RGB(
625
+ self.data.wls[data_mode],
626
+ self.data.hypercubes[data_mode],
627
+ self.data.hypercubes[data_mode].shape[0],
628
+ self.data.hypercubes[data_mode].shape[1],
629
+ 65,
630
+ False,
631
+ )
632
+ print(self.data.rgb[data_mode].shape)
633
+ self.viewer.add_image(
634
+ self.data.rgb[data_mode],
635
+ name=str(data_mode) + " RGB",
636
+ rgb=True,
637
+ metadata={"type": "rgb"},
638
+ )
639
+ rgb = self.data.rgb[data_mode]
640
+
641
+ (
642
+ self.data.hypercubes_masked[data_mode],
643
+ self.data.rgb_masked[data_mode],
644
+ ) = create_mask(data, rgb, binary_mask)
645
+
646
+ self.viewer.add_image(
647
+ self.data.hypercubes_masked[data_mode].transpose(2, 0, 1),
648
+ name=str(data_mode) + " masked",
649
+ metadata={"type": "masked_hsi_cube"},
650
+ )
651
+
652
+ self.viewer.add_image(
653
+ self.data.rgb_masked[data_mode],
654
+ name=str(data_mode) + " masked - RGB",
655
+ metadata={"type": "masked_rgb"},
656
+ )
657
+ return
658
+
659
+ def despike_btn_f(self):
660
+ data_mode = self.modes_combobox.value
661
+ self.data.hypercubes[data_mode] = despike(
662
+ self.data.hypercubes[data_mode]
663
+ )
664
+ print(f"Despiked dataset of {data_mode} created")
665
+ QTimer.singleShot(
666
+ 0, lambda: show_info("Despiked dataset of {data_mode} created")
667
+ )
668
+ if any(layer.name == str(data_mode) for layer in self.viewer.layers):
669
+ self.viewer.layers.remove(str(data_mode))
670
+ self.viewer.add_image(
671
+ self.data.hypercubes[data_mode].transpose(2, 0, 1),
672
+ name=str(data_mode),
673
+ metadata={"type": "hyperspectral_cube"},
674
+ )
675
+ return
676
+
677
+ def SVD_btn_f(self):
678
+ data_mode = self.modes_combobox.value
679
+
680
+ self.data.hypercubes[data_mode], self.data.svd_maps[data_mode] = (
681
+ SVD_denoise(
682
+ self.data.hypercubes[data_mode],
683
+ self.data.hypercubes[data_mode].shape[2], # all components
684
+ )
685
+ )
686
+ U_3D = self.data.svd_maps[data_mode][0].reshape(
687
+ self.data.hypercubes[data_mode].shape
688
+ )
689
+ if any(layer.name == str(data_mode) for layer in self.viewer.layers):
690
+ self.viewer.layers.remove(str(data_mode))
691
+ self.viewer.add_image(
692
+ U_3D.transpose(2, 0, 1),
693
+ name=str(data_mode) + " - SVD",
694
+ metadata={"type": "SVD_hsi"},
695
+ )
696
+ return
697
+
698
+ def SVD_denoise_btn_f(self):
699
+ data_mode = self.modes_combobox.value
700
+ components = self.SVD_spinbox.value
701
+ self.data.hypercubes[data_mode], maps = SVD_denoise(
702
+ self.data.hypercubes[data_mode],
703
+ components,
704
+ matrices=self.data.svd_maps[data_mode],
705
+ )
706
+ print(f"SVD denoise of {data_mode} created")
707
+ QTimer.singleShot(
708
+ 0, lambda: show_info("SVD denoise of {data_mode} created")
709
+ )
710
+ self.viewer.add_image(
711
+ self.data.hypercubes[data_mode].transpose(2, 0, 1),
712
+ name=str(data_mode),
713
+ metadata={"type": "hyperspectral_cube"},
714
+ )
715
+ return
716
+
717
+ def preprocessing_btn_f(self):
718
+ data_mode = self.modes_combobox.value
719
+ medfilt_checkbox = self.medfilt_checkbox.value
720
+ gaussian_checkbox = self.gaussian_checkbox.value
721
+ savgol_checkbox = self.savgol_checkbox.value
722
+ bkg_checkbox = self.bkg_checkbox.value
723
+ medfilt_w = self.medfilt_spinbox.value
724
+ gaussian_s = self.gaussian_spinbox.value
725
+ savgol_w = self.savgolw_spinbox.value
726
+ savgol_p = self.savgolp_spinbox.value
727
+ bkg_w = self.bkgw_spinbox.value
728
+
729
+ self.data.hypercubes[data_mode] = preprocessing(
730
+ self.data.hypercubes[data_mode],
731
+ medfilt_w,
732
+ gaussian_s,
733
+ savgol_w,
734
+ savgol_p,
735
+ bkg_w,
736
+ medfilt_checkbox=medfilt_checkbox,
737
+ gaussian_checkbox=gaussian_checkbox,
738
+ savgol_checkbox=savgol_checkbox,
739
+ bkg_checkbox=bkg_checkbox,
740
+ )
741
+ QTimer.singleShot(0, lambda: show_info("Preprocessing completed!"))
742
+ if any(layer.name == str(data_mode) for layer in self.viewer.layers):
743
+ self.viewer.layers.remove(str(data_mode))
744
+ self.viewer.add_image(
745
+ self.data.hypercubes[data_mode].transpose(2, 0, 1),
746
+ name=str(data_mode),
747
+ metadata={"type": "hyperspectral_cube"},
748
+ )
749
+ return
750
+
751
+ def derivative_btn_f(self):
752
+ data_mode = self.modes_combobox.value
753
+ name = str(data_mode) + " - derivative"
754
+ # Add the name of the derivative in the list of modes and in the combobox
755
+ if name not in self.data.modes:
756
+ self.data.modes.append(name)
757
+ self.modes_combobox.choices = self.data.modes
758
+ self.mode_added.emit()
759
+
760
+ self.data.hypercubes[data_mode + " - derivative"] = derivative(
761
+ self.data.hypercubes[data_mode],
762
+ savgol_w=9,
763
+ savgol_pol=3,
764
+ deriv=1,
765
+ )
766
+ self.viewer.add_image(
767
+ self.data.hypercubes[data_mode + " - derivative"].transpose(
768
+ 2, 0, 1
769
+ ),
770
+ name=str(data_mode + " - derivative"),
771
+ metadata={"type": "hyperspectral_cube"},
772
+ )
773
+ self.data.wls[data_mode + " - derivative"] = self.data.wls[data_mode]
774
+
775
+ return
776
+
777
+ def dimred_btn_f(self):
778
+ data_mode = self.modes_combobox.value
779
+ dataset = self.data.hypercubes[data_mode]
780
+ spectral_dimred_checkbox = self.spectral_dimred_checkbox.value
781
+ spatial_dimred_checkbox = self.spatial_dimred_checkbox.value
782
+ (
783
+ self.data.hypercubes_red[data_mode],
784
+ self.data.wls_red[data_mode],
785
+ self.data.rgb_red[data_mode],
786
+ ) = dimensionality_reduction(
787
+ dataset,
788
+ spectral_dimred_checkbox,
789
+ spatial_dimred_checkbox,
790
+ self.data.wls[data_mode],
791
+ )
792
+ print(
793
+ f"Dimensionality of dataset (Mode: {data_mode}) has been reduced"
794
+ )
795
+ print(
796
+ f"New channel array of dimension {self.data.wls_red[data_mode].shape}"
797
+ )
798
+ print(
799
+ f"New rgb matrix of reduced dataset. Dimensions: {self.data.rgb_red[data_mode].shape}"
800
+ )
801
+ if spatial_dimred_checkbox:
802
+ (
803
+ self.data.hypercubes_spatial_red[data_mode],
804
+ self.data.hypercubes_spatial_red_params[data_mode],
805
+ ) = reduce_spatial_dimension_dwt(dataset)
806
+
807
+ # print(self.data.hypercubes_red[data_mode].shape)
808
+ self.viewer.add_image(
809
+ self.data.hypercubes_red[data_mode].transpose(2, 0, 1),
810
+ name=str(data_mode) + " - REDUCED",
811
+ metadata={"type": "reduced_hsi_cube"},
812
+ )
813
+ self.viewer.add_image(
814
+ self.data.rgb_red[data_mode],
815
+ name=str(data_mode) + " - REDUCED RGB",
816
+ metadata={"type": "reduced_rgb"},
817
+ )
818
+
819
+ # %% Other functions
820
+ def update_wl(self):
821
+ """ """
822
+ data_mode = self.modes_combobox.value
823
+ wl_index = self.viewer.dims.current_step[0]
824
+ max_index = len(self.data.wls[data_mode]) - 1
825
+ wl_index = min(wl_index, max_index)
826
+ wl_value = wl_index
827
+ wl = round(self.data.wls[data_mode][wl_index], 2)
828
+ self.viewer.text_overlay.text = (
829
+ f"Wavelength: {wl} nm \nChannel: {wl_value}"
830
+ )
831
+
832
+ def layer_auto_selection(self):
833
+ """ """
834
+ selected_layer = self.viewer.layers.selection.active
835
+ if selected_layer is None:
836
+ return
837
+ elif selected_layer.metadata.get("type") == "hyperspectral_cube":
838
+ print(selected_layer.name)
839
+ self.modes_combobox.value = selected_layer.name
840
+
841
+ """
842
+ # cambiare nomi in modo che tolgo la grandezza della stringa del nome
843
+ elif selected_layer.metadata.get("type") == "rgb":
844
+ print(selected_layer.name[:-4])
845
+ self.modes_combobox.value = selected_layer.name[:-4]
846
+ elif selected_layer.metadata.get("type") == "reduced_hsi_cube":
847
+ print(selected_layer.name[:-10])
848
+ self.modes_combobox.value = selected_layer.name[:-10]
849
+ elif selected_layer.metadata.get("type") == "reduced_rgb":
850
+ print(selected_layer.name[:-14])
851
+ self.modes_combobox.value = selected_layer.name[:-14]
852
+ elif selected_layer.metadata.get("type") == "masked_hsi_cube":
853
+ print(selected_layer.name[:-7])
854
+ self.modes_combobox.value = selected_layer.name[:-7]
855
+ elif selected_layer.metadata.get("type") == "masked_rgb":
856
+ print(selected_layer.name[:-13])
857
+ self.modes_combobox.value = selected_layer.name[:-13]
858
+ elif selected_layer.metadata.get("type") == "denoised_hsi":
859
+ print(selected_layer.name[:-11])
860
+ self.modes_combobox.value = selected_layer.name[:-11]
861
+ elif selected_layer.metadata.get("type") == "SVD_hsi":
862
+ print(selected_layer.name[:-6])
863
+ self.modes_combobox.value = selected_layer.name[:-6]
864
+ """