peakviz 0.1.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.
- peakviz-0.1.0/LICENSE +0 -0
- peakviz-0.1.0/PKG-INFO +33 -0
- peakviz-0.1.0/README.md +17 -0
- peakviz-0.1.0/pyproject.toml +24 -0
- peakviz-0.1.0/setup.cfg +4 -0
- peakviz-0.1.0/src/__init__.py +1 -0
- peakviz-0.1.0/src/dataloader.py +137 -0
- peakviz-0.1.0/src/peakviz.egg-info/PKG-INFO +33 -0
- peakviz-0.1.0/src/peakviz.egg-info/SOURCES.txt +16 -0
- peakviz-0.1.0/src/peakviz.egg-info/dependency_links.txt +1 -0
- peakviz-0.1.0/src/peakviz.egg-info/requires.txt +5 -0
- peakviz-0.1.0/src/peakviz.egg-info/top_level.txt +5 -0
- peakviz-0.1.0/src/point_sensor.py +348 -0
- peakviz-0.1.0/src/pre_processing.py +53 -0
- peakviz-0.1.0/src/vizualization.py +163 -0
- peakviz-0.1.0/tests/test_dataloader.py +40 -0
- peakviz-0.1.0/tests/test_imaging_sensor.py +23 -0
- peakviz-0.1.0/tests/test_vizualization.py +50 -0
peakviz-0.1.0/LICENSE
ADDED
|
File without changes
|
peakviz-0.1.0/PKG-INFO
ADDED
|
@@ -0,0 +1,33 @@
|
|
|
1
|
+
Metadata-Version: 2.4
|
|
2
|
+
Name: peakviz
|
|
3
|
+
Version: 0.1.0
|
|
4
|
+
Summary: A tool to visualize Hyperspectral data from point and imaging sensors.
|
|
5
|
+
Author-email: Tasnim Tabassum Nova <tabassumnova1@gmail.com>
|
|
6
|
+
Project-URL: Homepage, https://github.com/TabassumNova/PeakViz
|
|
7
|
+
Requires-Python: >=3.8
|
|
8
|
+
Description-Content-Type: text/markdown
|
|
9
|
+
License-File: LICENSE
|
|
10
|
+
Requires-Dist: PyQt5
|
|
11
|
+
Requires-Dist: numpy
|
|
12
|
+
Requires-Dist: matplotlib
|
|
13
|
+
Requires-Dist: pandas
|
|
14
|
+
Requires-Dist: plotly
|
|
15
|
+
Dynamic: license-file
|
|
16
|
+
|
|
17
|
+
# PeakViz
|
|
18
|
+
This repository represents a simple workflow to visualize Hyperspectral data from point sensors: PSR and Fourier Transform Infrared Spectroscopy (FTIR) sensor. The wavelength ranges for these sensors are :
|
|
19
|
+
- PSR : 350.0 - 2500.0 nm
|
|
20
|
+
- FTIR : 2500.0629 - 15385.6915 nm
|
|
21
|
+
|
|
22
|
+
<p align="center">
|
|
23
|
+
<img src="image1.png" width="500"/>
|
|
24
|
+
</p>
|
|
25
|
+
|
|
26
|
+
# Tutorial
|
|
27
|
+
Coming soon ...
|
|
28
|
+
|
|
29
|
+
# Acknowledgement
|
|
30
|
+
This work is performed at [Helmholtz Institute Freiberg for Resource Technology](https://www.hzdr.de/db/Cms?pOid=32948&pNid=2423&pLang=en) in the [Exploration](https://www.iexplo.space/) department
|
|
31
|
+
|
|
32
|
+
|
|
33
|
+
|
peakviz-0.1.0/README.md
ADDED
|
@@ -0,0 +1,17 @@
|
|
|
1
|
+
# PeakViz
|
|
2
|
+
This repository represents a simple workflow to visualize Hyperspectral data from point sensors: PSR and Fourier Transform Infrared Spectroscopy (FTIR) sensor. The wavelength ranges for these sensors are :
|
|
3
|
+
- PSR : 350.0 - 2500.0 nm
|
|
4
|
+
- FTIR : 2500.0629 - 15385.6915 nm
|
|
5
|
+
|
|
6
|
+
<p align="center">
|
|
7
|
+
<img src="image1.png" width="500"/>
|
|
8
|
+
</p>
|
|
9
|
+
|
|
10
|
+
# Tutorial
|
|
11
|
+
Coming soon ...
|
|
12
|
+
|
|
13
|
+
# Acknowledgement
|
|
14
|
+
This work is performed at [Helmholtz Institute Freiberg for Resource Technology](https://www.hzdr.de/db/Cms?pOid=32948&pNid=2423&pLang=en) in the [Exploration](https://www.iexplo.space/) department
|
|
15
|
+
|
|
16
|
+
|
|
17
|
+
|
|
@@ -0,0 +1,24 @@
|
|
|
1
|
+
[build-system]
|
|
2
|
+
requires = ["setuptools>=61.0", "wheel"]
|
|
3
|
+
build-backend = "setuptools.build_meta"
|
|
4
|
+
|
|
5
|
+
[project]
|
|
6
|
+
name = "peakviz"
|
|
7
|
+
version = "0.1.0"
|
|
8
|
+
description = "A tool to visualize Hyperspectral data from point and imaging sensors."
|
|
9
|
+
authors = [
|
|
10
|
+
{ name = "Tasnim Tabassum Nova", email = "tabassumnova1@gmail.com" }
|
|
11
|
+
]
|
|
12
|
+
readme = "README.md"
|
|
13
|
+
license = { file = "LICENSE" }
|
|
14
|
+
requires-python = ">=3.8"
|
|
15
|
+
dependencies = [
|
|
16
|
+
"PyQt5",
|
|
17
|
+
"numpy",
|
|
18
|
+
"matplotlib",
|
|
19
|
+
"pandas",
|
|
20
|
+
"plotly",
|
|
21
|
+
]
|
|
22
|
+
|
|
23
|
+
[project.urls]
|
|
24
|
+
Homepage = "https://github.com/TabassumNova/PeakViz"
|
peakviz-0.1.0/setup.cfg
ADDED
|
@@ -0,0 +1 @@
|
|
|
1
|
+
from . import dataloader, point_sensor, pre_processing, vizualization
|
|
@@ -0,0 +1,137 @@
|
|
|
1
|
+
import pandas as pd
|
|
2
|
+
import os
|
|
3
|
+
import re
|
|
4
|
+
|
|
5
|
+
def is_float(s):
|
|
6
|
+
try:
|
|
7
|
+
float(s)
|
|
8
|
+
return True
|
|
9
|
+
except ValueError:
|
|
10
|
+
return False
|
|
11
|
+
|
|
12
|
+
def check_and_extract_line(file_path, search_text):
|
|
13
|
+
with open(file_path, 'r') as file:
|
|
14
|
+
content = file.read()
|
|
15
|
+
if search_text in content:
|
|
16
|
+
# Use regex to find the line starting with the specified 'line' variable
|
|
17
|
+
pattern = f"^{re.escape(search_text)}.*$"
|
|
18
|
+
match = re.search(pattern, content, re.MULTILINE)
|
|
19
|
+
if match:
|
|
20
|
+
return True, match.group(0)
|
|
21
|
+
return False, None
|
|
22
|
+
|
|
23
|
+
def find_xy(extracted_line):
|
|
24
|
+
if re.search(r'reflectance', extracted_line, re.IGNORECASE):
|
|
25
|
+
return 'wavelength', 'reflectance'
|
|
26
|
+
elif re.search(r'absorbance', extracted_line, re.IGNORECASE):
|
|
27
|
+
wavenumber_present = bool(re.search(r'wavenumber', extracted_line, re.IGNORECASE))
|
|
28
|
+
wavelength_present = bool(re.search(r'nanometer', extracted_line, re.IGNORECASE))
|
|
29
|
+
if wavenumber_present:
|
|
30
|
+
return 'wavenumber', 'absorbance'
|
|
31
|
+
elif wavelength_present:
|
|
32
|
+
return 'wavelength', 'absorbance'
|
|
33
|
+
|
|
34
|
+
|
|
35
|
+
# For point sensors
|
|
36
|
+
# Load all type of files
|
|
37
|
+
# Reflectance/ Absorbance/ Nanometer/ Wavenumber
|
|
38
|
+
def load_data(paths, signal_type, search_text):
|
|
39
|
+
all_energy = []
|
|
40
|
+
## for reference spectrum
|
|
41
|
+
spectrum_dict = {}
|
|
42
|
+
spectrum_dict['reflectance'] = []
|
|
43
|
+
spectrum_dict['absorbance'] = []
|
|
44
|
+
# if signal_type == 'reference':
|
|
45
|
+
# refSpectrum_dict = {}
|
|
46
|
+
# refSpectrum_dict['reflectance'] = []
|
|
47
|
+
# refSpectrum_dict['absorbance'] = []
|
|
48
|
+
|
|
49
|
+
###
|
|
50
|
+
for file_path in paths:
|
|
51
|
+
seach_text_present, extracted_line = check_and_extract_line(file_path, search_text)
|
|
52
|
+
x_axis, y_axis = find_xy(extracted_line)
|
|
53
|
+
if y_axis != signal_type and signal_type != 'reference':
|
|
54
|
+
return None, None
|
|
55
|
+
|
|
56
|
+
filename = os.path.basename(file_path)
|
|
57
|
+
with open(file_path, 'r') as f:
|
|
58
|
+
lines = f.readlines()
|
|
59
|
+
|
|
60
|
+
if x_axis == 'wavelength':
|
|
61
|
+
sample_energy = [filename]
|
|
62
|
+
wavelength_list = ['sample']
|
|
63
|
+
for line0 in lines:
|
|
64
|
+
line = line0.strip().split(" ")
|
|
65
|
+
if is_float(line[0]):
|
|
66
|
+
wavelength = float(line[0])
|
|
67
|
+
energy = float(line[-1])
|
|
68
|
+
sample_energy.append(energy)
|
|
69
|
+
wavelength_list.append(wavelength)
|
|
70
|
+
# all_energy.append(sample_energy)
|
|
71
|
+
spectrum_dict[y_axis].append(sample_energy)
|
|
72
|
+
|
|
73
|
+
elif x_axis == 'wavenumber':
|
|
74
|
+
sample_energy = []
|
|
75
|
+
wavelength_list = []
|
|
76
|
+
for line0 in lines:
|
|
77
|
+
line = line0.strip().split(" ")
|
|
78
|
+
if is_float(line[0]):
|
|
79
|
+
energy = float(line[-1])
|
|
80
|
+
sample_energy = [energy] + sample_energy
|
|
81
|
+
# TODO
|
|
82
|
+
# Fill wavelength list only for one sample file to avoid
|
|
83
|
+
# iterative process
|
|
84
|
+
wavenumber = float(line[0])
|
|
85
|
+
wavelength = (1/wavenumber)* (10 ** 7)
|
|
86
|
+
wavelength_list = [wavelength] + wavelength_list
|
|
87
|
+
wavelength_list = ['sample'] + wavelength_list
|
|
88
|
+
sample_energy = [filename] + sample_energy
|
|
89
|
+
# all_energy.append(sample_energy)
|
|
90
|
+
spectrum_dict[y_axis].append(sample_energy)
|
|
91
|
+
|
|
92
|
+
if len(spectrum_dict['reflectance']) == 0:
|
|
93
|
+
reflectance_df = None
|
|
94
|
+
else:
|
|
95
|
+
reflectance_df = pd.DataFrame(spectrum_dict['reflectance'], columns=wavelength_list)
|
|
96
|
+
if len(spectrum_dict['absorbance']) == 0:
|
|
97
|
+
absorbance_df = None
|
|
98
|
+
else:
|
|
99
|
+
absorbance_df = pd.DataFrame(spectrum_dict['absorbance'], columns=wavelength_list)
|
|
100
|
+
|
|
101
|
+
return reflectance_df, absorbance_df
|
|
102
|
+
# df = pd.DataFrame(spectrum_dict, columns=wavelength_list)
|
|
103
|
+
# return df
|
|
104
|
+
|
|
105
|
+
|
|
106
|
+
def load_refSpectrum(spectrum_paths):
|
|
107
|
+
all_energy = []
|
|
108
|
+
for file_path in spectrum_paths:
|
|
109
|
+
filename = os.path.basename(file_path)
|
|
110
|
+
sample_energy = []
|
|
111
|
+
wavelength_list = []
|
|
112
|
+
with open(file_path, 'r') as f:
|
|
113
|
+
lines = f.readlines()
|
|
114
|
+
for line0 in lines:
|
|
115
|
+
line = line0.strip().split(" ")
|
|
116
|
+
if is_float(line[1]):
|
|
117
|
+
wavelength = float(line[1])
|
|
118
|
+
energy = float(line[-1])
|
|
119
|
+
sample_energy.append(energy)
|
|
120
|
+
wavelength_list.append(wavelength)
|
|
121
|
+
pass
|
|
122
|
+
wavelength_list = ['polymer'] + wavelength_list
|
|
123
|
+
sample_energy = [filename] + sample_energy
|
|
124
|
+
all_energy.append(sample_energy)
|
|
125
|
+
refSpectrum_df = pd.DataFrame(all_energy, columns=wavelength_list)
|
|
126
|
+
return refSpectrum_df
|
|
127
|
+
|
|
128
|
+
|
|
129
|
+
|
|
130
|
+
|
|
131
|
+
|
|
132
|
+
|
|
133
|
+
|
|
134
|
+
|
|
135
|
+
|
|
136
|
+
|
|
137
|
+
|
|
@@ -0,0 +1,33 @@
|
|
|
1
|
+
Metadata-Version: 2.4
|
|
2
|
+
Name: peakviz
|
|
3
|
+
Version: 0.1.0
|
|
4
|
+
Summary: A tool to visualize Hyperspectral data from point and imaging sensors.
|
|
5
|
+
Author-email: Tasnim Tabassum Nova <tabassumnova1@gmail.com>
|
|
6
|
+
Project-URL: Homepage, https://github.com/TabassumNova/PeakViz
|
|
7
|
+
Requires-Python: >=3.8
|
|
8
|
+
Description-Content-Type: text/markdown
|
|
9
|
+
License-File: LICENSE
|
|
10
|
+
Requires-Dist: PyQt5
|
|
11
|
+
Requires-Dist: numpy
|
|
12
|
+
Requires-Dist: matplotlib
|
|
13
|
+
Requires-Dist: pandas
|
|
14
|
+
Requires-Dist: plotly
|
|
15
|
+
Dynamic: license-file
|
|
16
|
+
|
|
17
|
+
# PeakViz
|
|
18
|
+
This repository represents a simple workflow to visualize Hyperspectral data from point sensors: PSR and Fourier Transform Infrared Spectroscopy (FTIR) sensor. The wavelength ranges for these sensors are :
|
|
19
|
+
- PSR : 350.0 - 2500.0 nm
|
|
20
|
+
- FTIR : 2500.0629 - 15385.6915 nm
|
|
21
|
+
|
|
22
|
+
<p align="center">
|
|
23
|
+
<img src="image1.png" width="500"/>
|
|
24
|
+
</p>
|
|
25
|
+
|
|
26
|
+
# Tutorial
|
|
27
|
+
Coming soon ...
|
|
28
|
+
|
|
29
|
+
# Acknowledgement
|
|
30
|
+
This work is performed at [Helmholtz Institute Freiberg for Resource Technology](https://www.hzdr.de/db/Cms?pOid=32948&pNid=2423&pLang=en) in the [Exploration](https://www.iexplo.space/) department
|
|
31
|
+
|
|
32
|
+
|
|
33
|
+
|
|
@@ -0,0 +1,16 @@
|
|
|
1
|
+
LICENSE
|
|
2
|
+
README.md
|
|
3
|
+
pyproject.toml
|
|
4
|
+
src/__init__.py
|
|
5
|
+
src/dataloader.py
|
|
6
|
+
src/point_sensor.py
|
|
7
|
+
src/pre_processing.py
|
|
8
|
+
src/vizualization.py
|
|
9
|
+
src/peakviz.egg-info/PKG-INFO
|
|
10
|
+
src/peakviz.egg-info/SOURCES.txt
|
|
11
|
+
src/peakviz.egg-info/dependency_links.txt
|
|
12
|
+
src/peakviz.egg-info/requires.txt
|
|
13
|
+
src/peakviz.egg-info/top_level.txt
|
|
14
|
+
tests/test_dataloader.py
|
|
15
|
+
tests/test_imaging_sensor.py
|
|
16
|
+
tests/test_vizualization.py
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
|
|
@@ -0,0 +1,348 @@
|
|
|
1
|
+
import sys
|
|
2
|
+
from PyQt5.QtCore import *
|
|
3
|
+
from PyQt5.QtWidgets import *
|
|
4
|
+
from PyQt5.QtGui import *
|
|
5
|
+
|
|
6
|
+
from .dataloader import *
|
|
7
|
+
from .vizualization import *
|
|
8
|
+
from .pre_processing import *
|
|
9
|
+
|
|
10
|
+
class PointSensor(QScrollArea):
|
|
11
|
+
|
|
12
|
+
def __init__(self):
|
|
13
|
+
super(PointSensor, self).__init__()
|
|
14
|
+
self.widget1 = QWidget()
|
|
15
|
+
layout1 = QGridLayout(self.widget1)
|
|
16
|
+
layout1.setAlignment(Qt.AlignTop)
|
|
17
|
+
|
|
18
|
+
self.sensor = None
|
|
19
|
+
self.search_text = None #TODO: make this text from user input
|
|
20
|
+
self.reflectance_files = None
|
|
21
|
+
self.absorbance_files = None
|
|
22
|
+
self.reflectance_df = None
|
|
23
|
+
self.absorbance_df = None
|
|
24
|
+
self.batchname = ''
|
|
25
|
+
self.lib_path = None
|
|
26
|
+
self.library_df = None
|
|
27
|
+
self.spectrum_paths = None
|
|
28
|
+
self.refSpectrum_df = {}
|
|
29
|
+
self.rescaling_flag = False
|
|
30
|
+
self.average_flag = False
|
|
31
|
+
self.reflectance_rescaled_df = None
|
|
32
|
+
self.absorbance_rescaled_df = None
|
|
33
|
+
self.reflectance_averaged_df = None
|
|
34
|
+
self.absorbance_averaged_df = None
|
|
35
|
+
self.download_flag = False
|
|
36
|
+
self.setFixedWidth(500)
|
|
37
|
+
self.setFixedHeight(700)
|
|
38
|
+
|
|
39
|
+
# Choose sensor
|
|
40
|
+
self.label1 = QLabel(self.widget1)
|
|
41
|
+
self.label1.setObjectName('Choose point sensor: ')
|
|
42
|
+
self.label1.setText('Choose point sensor: ')
|
|
43
|
+
font = QFont()
|
|
44
|
+
font.setBold(True)
|
|
45
|
+
font.setPointSize(14)
|
|
46
|
+
self.label1.setFont(font)
|
|
47
|
+
self.label1.setGeometry(QRect(10, 20, 150, 20))
|
|
48
|
+
|
|
49
|
+
|
|
50
|
+
# creating check box for choosing sensor
|
|
51
|
+
self.checkBoxSWIR = QCheckBox("VNIR/SWIR", self.widget1)
|
|
52
|
+
self.checkBoxSWIR.setGeometry(10, 50, 100, 20)
|
|
53
|
+
self.checkBoxMWIR = QCheckBox("MWIR/LWIR", self.widget1)
|
|
54
|
+
self.checkBoxMWIR.setGeometry(120, 50, 100, 20)
|
|
55
|
+
|
|
56
|
+
# calling the uncheck method if any check box state is changed
|
|
57
|
+
self.checkBoxSWIR.stateChanged.connect(self.select_sensor)
|
|
58
|
+
self.checkBoxMWIR.stateChanged.connect(self.select_sensor)
|
|
59
|
+
|
|
60
|
+
# Select Reflectance files
|
|
61
|
+
self.label2 = QLabel(self.widget1)
|
|
62
|
+
self.label2.setObjectName('Select single or multiple "Reflectance" files')
|
|
63
|
+
self.label2.setText('Select single or multiple "Reflectance" files')
|
|
64
|
+
self.label2.setFont(font)
|
|
65
|
+
self.label2.setGeometry(QRect(10, 90, 300, 20))
|
|
66
|
+
|
|
67
|
+
# Button for loading Reflectance
|
|
68
|
+
self.btn1 = QPushButton(self.widget1)
|
|
69
|
+
self.btn1.setObjectName('Load Reflectance')
|
|
70
|
+
self.btn1.setText('Load Reflectance')
|
|
71
|
+
self.btn1.setGeometry(QRect(10, 120, 130, 40))
|
|
72
|
+
self.btn1.clicked.connect(self.reflectance_file_dialog)
|
|
73
|
+
|
|
74
|
+
# Message for loading Reflectance files
|
|
75
|
+
self.label3 = QLabel(self.widget1)
|
|
76
|
+
self.label3.setObjectName('Reflectance loaded')
|
|
77
|
+
self.label3.setText('')
|
|
78
|
+
self.label3.setStyleSheet("border: 0.5px solid gray;")
|
|
79
|
+
self.label3.setGeometry(QRect(150, 125, 130, 30))
|
|
80
|
+
|
|
81
|
+
|
|
82
|
+
# Label for renaming files from comment (VNIR/SWIR only)
|
|
83
|
+
self.rename_label = QLabel(self.widget1)
|
|
84
|
+
self.rename_label.setObjectName('Rename files from comment')
|
|
85
|
+
self.rename_label.setText('Rename the files (Only applicable to VNIR/SWIR)')
|
|
86
|
+
self.rename_label.setFont(font)
|
|
87
|
+
self.rename_label.setGeometry(QRect(10, 170, 350, 20))
|
|
88
|
+
|
|
89
|
+
# Checkbox for renaming
|
|
90
|
+
self.rename_checkbox = QCheckBox('Rename', self.widget1)
|
|
91
|
+
self.rename_checkbox.setGeometry(QRect(10, 195, 80, 25))
|
|
92
|
+
# self.rename_checkbox.stateChanged.connect(self.select_rename)
|
|
93
|
+
|
|
94
|
+
# Save button beside checkbox
|
|
95
|
+
self.save_button = QPushButton('Save', self.widget1)
|
|
96
|
+
self.save_button.setGeometry(QRect(100, 195, 80, 25))
|
|
97
|
+
# self.save_button.clicked.connect(self.rename_files)
|
|
98
|
+
|
|
99
|
+
# Select Absorbance files
|
|
100
|
+
self.label4 = QLabel(self.widget1)
|
|
101
|
+
self.label4.setObjectName('Select single or multiple "Absorbance" files (Optional)')
|
|
102
|
+
self.label4.setText('Select single or multiple "Absorbance" files (Optional)')
|
|
103
|
+
self.label4.setFont(font)
|
|
104
|
+
self.label4.setGeometry(QRect(10, 235, 365, 20))
|
|
105
|
+
|
|
106
|
+
# Button for loading Absorbance
|
|
107
|
+
self.btn2 = QPushButton(self.widget1)
|
|
108
|
+
self.btn2.setObjectName('Load Absorbance')
|
|
109
|
+
self.btn2.setText('Load Absorbance')
|
|
110
|
+
self.btn2.setGeometry(QRect(10, 265, 130, 40))
|
|
111
|
+
self.btn2.clicked.connect(self.absorbance_file_dialog)
|
|
112
|
+
|
|
113
|
+
# Message for loading Absorbance files
|
|
114
|
+
self.label5 = QLabel(self.widget1)
|
|
115
|
+
self.label5.setObjectName('Absorbance loaded')
|
|
116
|
+
self.label5.setText('')
|
|
117
|
+
self.label5.setStyleSheet("border: 0.5px solid gray;")
|
|
118
|
+
self.label5.setGeometry(QRect(150, 270, 130, 30))
|
|
119
|
+
|
|
120
|
+
# Select Library file
|
|
121
|
+
self.label6 = QLabel(self.widget1)
|
|
122
|
+
self.label6.setObjectName('Select fingerprint library for the specified sensor (Optional)')
|
|
123
|
+
self.label6.setText('Select fingerprint library for the specified sensor (Optional)')
|
|
124
|
+
self.label6.setFont(font)
|
|
125
|
+
self.label6.setGeometry(QRect(10, 310, 400, 20))
|
|
126
|
+
|
|
127
|
+
# Button for loading Library
|
|
128
|
+
self.btn3 = QPushButton(self.widget1)
|
|
129
|
+
self.btn3.setObjectName('Load Fingerprints')
|
|
130
|
+
self.btn3.setText('Load Fingerprints')
|
|
131
|
+
self.btn3.setGeometry(QRect(10, 340, 130, 40))
|
|
132
|
+
self.btn3.clicked.connect(self.open_library)
|
|
133
|
+
|
|
134
|
+
# Message for library files
|
|
135
|
+
self.label7 = QLabel(self.widget1)
|
|
136
|
+
self.label7.setObjectName('Fingerprints loaded')
|
|
137
|
+
self.label7.setText('')
|
|
138
|
+
self.label7.setStyleSheet("border: 0.5px solid gray;")
|
|
139
|
+
self.label7.setGeometry(QRect(150, 345, 130, 30))
|
|
140
|
+
|
|
141
|
+
#### test
|
|
142
|
+
# Select Reference spectrum
|
|
143
|
+
self.label8 = QLabel(self.widget1)
|
|
144
|
+
self.label8.setObjectName('Select Reference spectrum for the specified sensor (Optional)')
|
|
145
|
+
self.label8.setText('Select Reference spectrum for the specified sensor (Optional)')
|
|
146
|
+
self.label8.setFont(font)
|
|
147
|
+
self.label8.setGeometry(QRect(10, 385, 420, 20))
|
|
148
|
+
|
|
149
|
+
# Button for loading Reference spectrum
|
|
150
|
+
self.btn4 = QPushButton(self.widget1)
|
|
151
|
+
self.btn4.setObjectName('Load Spectrums')
|
|
152
|
+
self.btn4.setText('Load Spectrums')
|
|
153
|
+
self.btn4.setGeometry(QRect(10, 415, 130, 40))
|
|
154
|
+
self.btn4.clicked.connect(self.open_refSpectrums)
|
|
155
|
+
|
|
156
|
+
# Message for Reference spectrum
|
|
157
|
+
self.label9 = QLabel(self.widget1)
|
|
158
|
+
self.label9.setObjectName('Spectrums loaded')
|
|
159
|
+
self.label9.setText('')
|
|
160
|
+
self.label9.setStyleSheet("border: 0.5px solid gray;")
|
|
161
|
+
self.label9.setGeometry(QRect(150, 420, 130, 30))
|
|
162
|
+
#########
|
|
163
|
+
|
|
164
|
+
# Select pre-processing method
|
|
165
|
+
self.label10 = QLabel(self.widget1)
|
|
166
|
+
self.label10.setObjectName('Select Pre-processing method (Optional)')
|
|
167
|
+
self.label10.setText('Select Pre-processing method (Optional)')
|
|
168
|
+
self.label10.setFont(font)
|
|
169
|
+
self.label10.setGeometry(QRect(10, 460, 365, 20))
|
|
170
|
+
|
|
171
|
+
# creating check box for re-scaling
|
|
172
|
+
self.checkBoxRescaling = QCheckBox("Y-axis rescaling", self.widget1)
|
|
173
|
+
self.checkBoxRescaling.setGeometry(10, 490, 160, 30)
|
|
174
|
+
self.checkBoxRescaling.stateChanged.connect(self.select_rescaling)
|
|
175
|
+
|
|
176
|
+
# creating check box for choosing sensor
|
|
177
|
+
self.checkBoxAverage = QCheckBox("Average", self.widget1)
|
|
178
|
+
self.checkBoxAverage.setGeometry(160, 490, 160, 30)
|
|
179
|
+
self.checkBoxAverage.stateChanged.connect(self.select_average)
|
|
180
|
+
|
|
181
|
+
# Start visualization
|
|
182
|
+
self.label11 = QLabel(self.widget1)
|
|
183
|
+
self.label11.setObjectName('Data visualisation')
|
|
184
|
+
self.label11.setText('Data visualisation')
|
|
185
|
+
self.label11.setFont(font)
|
|
186
|
+
self.label11.setGeometry(QRect(10, 530, 365, 20))
|
|
187
|
+
|
|
188
|
+
# creating check box for choosing sensor
|
|
189
|
+
self.checkBoxDownload = QCheckBox("Download as .html", self.widget1)
|
|
190
|
+
self.checkBoxDownload.setGeometry(10, 560, 150, 30)
|
|
191
|
+
self.checkBoxDownload.stateChanged.connect(self.select_download)
|
|
192
|
+
|
|
193
|
+
# For opening data
|
|
194
|
+
self.btn5 = QPushButton(self.widget1)
|
|
195
|
+
self.btn5.setObjectName('Open Data')
|
|
196
|
+
self.btn5.setText('Open Data')
|
|
197
|
+
self.btn5.setGeometry(QRect(10, 590, 111, 40))
|
|
198
|
+
self.btn5.clicked.connect(self.open_data)
|
|
199
|
+
|
|
200
|
+
self.setWidget(self.widget1)
|
|
201
|
+
self.setWidgetResizable(True)
|
|
202
|
+
self.widget1.setLayout(layout1)
|
|
203
|
+
|
|
204
|
+
def select_sensor(self, state):
|
|
205
|
+
if state == Qt.Checked:
|
|
206
|
+
if self.sender() == self.checkBoxSWIR:
|
|
207
|
+
self.checkBoxMWIR.setChecked(False)
|
|
208
|
+
self.sensor = 'VNIR_SWIR'
|
|
209
|
+
self.search_text = 'Measurement:'
|
|
210
|
+
elif self.sender() == self.checkBoxMWIR:
|
|
211
|
+
self.checkBoxSWIR.setChecked(False)
|
|
212
|
+
self.sensor = 'MWIR_LWIR'
|
|
213
|
+
self.search_text = 'XYUNITS'
|
|
214
|
+
|
|
215
|
+
def create_batchname(self, file_path):
|
|
216
|
+
# Get the batchname
|
|
217
|
+
if self.sensor == 'VNIR_SWIR':
|
|
218
|
+
clock = 3
|
|
219
|
+
elif self.sensor == 'MWIR_LWIR':
|
|
220
|
+
clock = 4
|
|
221
|
+
while clock:
|
|
222
|
+
file_path, folder = os.path.split(file_path)
|
|
223
|
+
clock -= 1
|
|
224
|
+
self.batchname = folder
|
|
225
|
+
|
|
226
|
+
|
|
227
|
+
def reflectance_file_dialog(self):
|
|
228
|
+
# Open the file dialog and get the selected file name
|
|
229
|
+
self.reflectance_files, _ = QFileDialog.getOpenFileNames(self)
|
|
230
|
+
if self.reflectance_files:
|
|
231
|
+
self.reflectance_df, _ = load_data(self.reflectance_files, signal_type='reflectance',
|
|
232
|
+
search_text= self.search_text)
|
|
233
|
+
message = "Reflectance loaded" if self.reflectance_df is not None else "Error!"
|
|
234
|
+
self.label3.setText(message)
|
|
235
|
+
###
|
|
236
|
+
if not self.batchname:
|
|
237
|
+
self.create_batchname(self.reflectance_files[0])
|
|
238
|
+
|
|
239
|
+
|
|
240
|
+
def absorbance_file_dialog(self):
|
|
241
|
+
# Open the file dialog and get the selected file name
|
|
242
|
+
self.absorbance_files, _ = QFileDialog.getOpenFileNames(self)
|
|
243
|
+
if self.absorbance_files:
|
|
244
|
+
_, self.absorbance_df = load_data(self.absorbance_files, signal_type='absorbance',
|
|
245
|
+
search_text= self.search_text)
|
|
246
|
+
message = "Absorbance loaded" if self.absorbance_df is not None else "Error!"
|
|
247
|
+
self.label5.setText(message)
|
|
248
|
+
if not self.batchname:
|
|
249
|
+
self.create_batchname(self.absorbance_files[0])
|
|
250
|
+
|
|
251
|
+
|
|
252
|
+
def open_library(self):
|
|
253
|
+
# Open the file dialog and get the selected excel file
|
|
254
|
+
# Load the file as pandas dataframe
|
|
255
|
+
self.lib_path, _ = QFileDialog.getOpenFileName(self)
|
|
256
|
+
if self.lib_path:
|
|
257
|
+
self.label7.setText("Library loaded")
|
|
258
|
+
self.library_df = pd.read_excel(self.lib_path)
|
|
259
|
+
|
|
260
|
+
def open_refSpectrums(self):
|
|
261
|
+
# Select one or multiple .txt files for reference spectrum
|
|
262
|
+
# Open the file dialog and get the selected .txt files
|
|
263
|
+
# Load the file as pandas dataframe
|
|
264
|
+
self.spectrum_paths, _ = QFileDialog.getOpenFileNames(self)
|
|
265
|
+
if self.spectrum_paths:
|
|
266
|
+
# self.refSpectrum_df = load_refSpectrum(self.spectrum_paths)
|
|
267
|
+
###
|
|
268
|
+
reflectRef_df, absorbRef_df = load_data(self.spectrum_paths, signal_type='reference',
|
|
269
|
+
search_text= 'XYUNITS')
|
|
270
|
+
if reflectRef_df is not None:
|
|
271
|
+
# self.reflectance_df = pd.concat([self.reflectance_df, reflectRef_df], axis=0)
|
|
272
|
+
self.refSpectrum_df['Reflectance'] = reflectRef_df
|
|
273
|
+
if absorbRef_df is not None:
|
|
274
|
+
# self.absorbance_df = pd.concat([self.absorbance_df, absorbRef_df], axis=0)
|
|
275
|
+
self.refSpectrum_df['Absorbance'] = absorbRef_df
|
|
276
|
+
###
|
|
277
|
+
self.label9.setText('Spectrums loaded')
|
|
278
|
+
|
|
279
|
+
def select_rescaling(self, state):
|
|
280
|
+
if state == Qt.Checked:
|
|
281
|
+
if self.sender() == self.checkBoxRescaling:
|
|
282
|
+
self.rescaling_flag = True
|
|
283
|
+
if self.reflectance_files:
|
|
284
|
+
self.reflectance_rescaled_df = rescale_data(self.reflectance_df)
|
|
285
|
+
if self.absorbance_files:
|
|
286
|
+
self.absorbance_rescaled_df = rescale_data(self.absorbance_df)
|
|
287
|
+
|
|
288
|
+
def select_average(self, state):
|
|
289
|
+
if state == Qt.Checked:
|
|
290
|
+
if self.sender() == self.checkBoxAverage:
|
|
291
|
+
self.average_flag = True
|
|
292
|
+
if self.reflectance_files:
|
|
293
|
+
self.reflectance_averaged_df = average_data(self.reflectance_df,
|
|
294
|
+
self.reflectance_files[0],
|
|
295
|
+
signal_type= 'Reflectance')
|
|
296
|
+
if self.absorbance_files:
|
|
297
|
+
self.absorbance_averaged_df = average_data(self.absorbance_df,
|
|
298
|
+
self.absorbance_files[0],
|
|
299
|
+
signal_type= 'Absorbance')
|
|
300
|
+
|
|
301
|
+
def select_download(self, state):
|
|
302
|
+
if state == Qt.Checked:
|
|
303
|
+
if self.sender() == self.checkBoxDownload:
|
|
304
|
+
self.download_flag = True
|
|
305
|
+
|
|
306
|
+
|
|
307
|
+
def open_data(self):
|
|
308
|
+
df_plot = {}
|
|
309
|
+
if self.reflectance_files:
|
|
310
|
+
df_plot['Reflectance'] = (self.reflectance_df, 'Reflectance')
|
|
311
|
+
if self.absorbance_files:
|
|
312
|
+
df_plot['Absorbance'] = (self.absorbance_df, 'Absorbance')
|
|
313
|
+
if self.rescaling_flag:
|
|
314
|
+
df_plot['Reflectance (Re-scaled)'] = (self.reflectance_rescaled_df, 'Reflectance')
|
|
315
|
+
df_plot['Absorbance (Re-scaled)'] = (self.absorbance_rescaled_df, 'Absorbance')
|
|
316
|
+
pass
|
|
317
|
+
|
|
318
|
+
# This creates vizualization to plot multiple data
|
|
319
|
+
viz(self.batchname, df_plot, fingerprint_library=self.library_df,
|
|
320
|
+
reference_Spectrums=self.refSpectrum_df,
|
|
321
|
+
sensor=self.sensor, download=self.download_flag)
|
|
322
|
+
# Check if del obj is possible
|
|
323
|
+
self.reflectance_files = None
|
|
324
|
+
self.absorbance_files = None
|
|
325
|
+
self.reflectance_df = None
|
|
326
|
+
self.absorbance_df = None
|
|
327
|
+
self.batchname = None
|
|
328
|
+
# self.library_df = None
|
|
329
|
+
# self.rescaling_flag = False
|
|
330
|
+
self.reflectance_rescaled_df = None
|
|
331
|
+
self.absorbance_rescaled_df = None
|
|
332
|
+
# self.download_flag = False
|
|
333
|
+
self.label3.setText("")
|
|
334
|
+
self.label5.setText("")
|
|
335
|
+
self.label9.setText("")
|
|
336
|
+
# self.label7.setText("")
|
|
337
|
+
# if hasattr(self, 'downloadCheckBox'):
|
|
338
|
+
self.checkBoxDownload.setChecked(False)
|
|
339
|
+
# if hasattr(self, 'averageCheckBox'):
|
|
340
|
+
self.checkBoxAverage.setChecked(False)
|
|
341
|
+
|
|
342
|
+
|
|
343
|
+
|
|
344
|
+
# if __name__ == "__main__":
|
|
345
|
+
# app = QApplication(sys.argv)
|
|
346
|
+
# window = Window()
|
|
347
|
+
# window.show()
|
|
348
|
+
# sys.exit(app.exec())
|
|
@@ -0,0 +1,53 @@
|
|
|
1
|
+
import numpy as np
|
|
2
|
+
import pandas as pd
|
|
3
|
+
import os
|
|
4
|
+
|
|
5
|
+
def rescale_data(df):
|
|
6
|
+
new = []
|
|
7
|
+
for index, row in df.iterrows():
|
|
8
|
+
text = [row.iloc[0]]
|
|
9
|
+
energy = np.array(row)[1:]
|
|
10
|
+
min_val = energy.min()
|
|
11
|
+
max_val = energy.max()
|
|
12
|
+
rescaled_energy = list((energy - min_val) / (max_val - min_val))
|
|
13
|
+
new.append(text+rescaled_energy)
|
|
14
|
+
|
|
15
|
+
rescaled_df = pd.DataFrame(new, columns=list(df.columns))
|
|
16
|
+
return rescaled_df
|
|
17
|
+
|
|
18
|
+
def common_prefix(strings):
|
|
19
|
+
if not strings:
|
|
20
|
+
return ""
|
|
21
|
+
|
|
22
|
+
# Find the length of the shortest string
|
|
23
|
+
min_length = min(len(s) for s in strings)
|
|
24
|
+
|
|
25
|
+
# Compare characters
|
|
26
|
+
for i in range(min_length):
|
|
27
|
+
if len(set(s[i] for s in strings)) > 1:
|
|
28
|
+
return strings[0][:i]
|
|
29
|
+
|
|
30
|
+
# If we've made it here, return the whole shortest string
|
|
31
|
+
return strings[0][:min_length]
|
|
32
|
+
|
|
33
|
+
def average_data(df, input_path, signal_type):
|
|
34
|
+
samples = list(df['sample'])
|
|
35
|
+
averaged_name = common_prefix(samples)
|
|
36
|
+
averaged_list = []
|
|
37
|
+
for (columnName, columnData) in df.items():
|
|
38
|
+
is_float = np.issubdtype(columnData.dtype, np.floating)
|
|
39
|
+
if is_float:
|
|
40
|
+
mean = columnData.mean().item()
|
|
41
|
+
averaged_list.append(mean)
|
|
42
|
+
averaged_df1 = pd.DataFrame([averaged_list], columns=list(df.columns[1:]))
|
|
43
|
+
|
|
44
|
+
### For generating text file
|
|
45
|
+
reshaped_df = averaged_df1.melt(var_name="Wavelength (nm)", value_name="FTIR signal").reset_index(drop=True)
|
|
46
|
+
file_path, folder = os.path.split(input_path)
|
|
47
|
+
output_path = os.path.join(file_path, averaged_name+ signal_type + '.txt')
|
|
48
|
+
with open(output_path, 'w') as f:
|
|
49
|
+
f.write("XYUNITS Nanometer; "+ signal_type + "\n")
|
|
50
|
+
reshaped_df.to_csv(f, sep=' ', index=False, header=True)
|
|
51
|
+
|
|
52
|
+
averaged_df2 = pd.DataFrame([[averaged_name]+averaged_list], columns=list(df.columns))
|
|
53
|
+
return averaged_df2
|
|
@@ -0,0 +1,163 @@
|
|
|
1
|
+
import ast
|
|
2
|
+
import plotly.graph_objects as go
|
|
3
|
+
import plotly.express as px
|
|
4
|
+
import numpy as np
|
|
5
|
+
|
|
6
|
+
|
|
7
|
+
def viz(batch_name, df_plot, fingerprint_library=None, reference_Spectrums=None,
|
|
8
|
+
sensor='', download=False):
|
|
9
|
+
fig = go.Figure()
|
|
10
|
+
buttons = []
|
|
11
|
+
start = 0
|
|
12
|
+
end = start
|
|
13
|
+
visible_dict = {}
|
|
14
|
+
y_axis_dict = {}
|
|
15
|
+
for key_index, (key, plots) in enumerate(df_plot.items()):
|
|
16
|
+
df = plots[0]
|
|
17
|
+
y_axis_dict[key] = plots[1]
|
|
18
|
+
wavelengths = list(df.columns)[1:]
|
|
19
|
+
data = []
|
|
20
|
+
max_energy = df.iloc[:, 1:].max().max() # For plotting vertical lines
|
|
21
|
+
min_energy = df.iloc[:, 1:].min().min() # For plotting vertical lines
|
|
22
|
+
for index, row in df.iterrows():
|
|
23
|
+
sample_name = list(row)[0]
|
|
24
|
+
energy = list(row)[1:]
|
|
25
|
+
graph = go.Scatter(x=wavelengths, y=energy, name = sample_name, mode='lines',
|
|
26
|
+
visible=(key_index == 0),
|
|
27
|
+
# hoverinfo='x'
|
|
28
|
+
)
|
|
29
|
+
data.append(graph)
|
|
30
|
+
fig.add_trace(graph)
|
|
31
|
+
end += 1
|
|
32
|
+
|
|
33
|
+
# Visualisation for Reference Spectrum libraries
|
|
34
|
+
if reference_Spectrums:
|
|
35
|
+
ref_df = reference_Spectrums[key]
|
|
36
|
+
wavelengths = list(ref_df.columns)[1:]
|
|
37
|
+
for index, row in ref_df.iterrows():
|
|
38
|
+
polymer_name = list(row)[0]
|
|
39
|
+
energy = list(row)[1:]
|
|
40
|
+
graph = go.Scatter(x=wavelengths, y=energy, name = polymer_name, mode='lines',
|
|
41
|
+
visible=(key_index == 0),
|
|
42
|
+
# hoverinfo='x'
|
|
43
|
+
)
|
|
44
|
+
data.append(graph)
|
|
45
|
+
fig.add_trace(graph)
|
|
46
|
+
end += 1
|
|
47
|
+
# TODO: remove redundancy
|
|
48
|
+
max_energy = max(max_energy, ref_df.iloc[:, 1:].max().max()) # For plotting vertical lines
|
|
49
|
+
min_energy = min(min_energy, ref_df.iloc[:, 1:].min().min()) # For plotting vertical lines
|
|
50
|
+
|
|
51
|
+
# Visualisation for fingerprint libraries
|
|
52
|
+
if fingerprint_library is not None:
|
|
53
|
+
lib = fingerprint_library if sensor=='imaging' else fingerprint_library[fingerprint_library['sensor'] == sensor]
|
|
54
|
+
unique_groups = lib['polymer'].unique()
|
|
55
|
+
color_map = {group: px.colors.qualitative.Light24[i % len(px.colors.qualitative.Light24)] for i, group in enumerate(unique_groups)}
|
|
56
|
+
for index, row in lib.iterrows():
|
|
57
|
+
group_name = row['polymer']
|
|
58
|
+
colour = row['colour']
|
|
59
|
+
# Check if the row['wavelengths'] is a string of ranges
|
|
60
|
+
# If yes, then plot shaded area
|
|
61
|
+
if '-' in row['wavelengths']:
|
|
62
|
+
ranges = row['wavelengths'].strip('[]').split(',')
|
|
63
|
+
for i, r in enumerate(ranges):
|
|
64
|
+
r = r.strip()
|
|
65
|
+
if '-' in r:
|
|
66
|
+
start_range, end_range = map(float, r.split('-'))
|
|
67
|
+
shade = go.Scatter(
|
|
68
|
+
x=[start_range, start_range, end_range, end_range, start_range],
|
|
69
|
+
y=[min_energy, max_energy, max_energy, min_energy, min_energy],
|
|
70
|
+
fill='toself',
|
|
71
|
+
fillcolor='LightSkyBlue',
|
|
72
|
+
opacity=0.3,
|
|
73
|
+
mode='lines',
|
|
74
|
+
line={'color': 'LightSkyBlue'},
|
|
75
|
+
name=group_name,
|
|
76
|
+
legendgroup=group_name,
|
|
77
|
+
showlegend=True if i == 0 else False,
|
|
78
|
+
visible=(key_index == 0),
|
|
79
|
+
)
|
|
80
|
+
data.append(shade)
|
|
81
|
+
fig.add_trace(shade)
|
|
82
|
+
end += 1
|
|
83
|
+
|
|
84
|
+
else:
|
|
85
|
+
wavelengths = [float(x) for x in ast.literal_eval(row['wavelengths'])]
|
|
86
|
+
for i, x_value in enumerate(wavelengths):
|
|
87
|
+
line = go.Scatter(
|
|
88
|
+
x=[x_value]*2, # Duplicate x values for vertical lines
|
|
89
|
+
y=np.linspace(min_energy, max_energy, num=2).tolist(), # Alternate y values for vertical lines
|
|
90
|
+
mode='lines+text',
|
|
91
|
+
name=group_name,
|
|
92
|
+
line={'color': colour},
|
|
93
|
+
legendgroup=group_name,
|
|
94
|
+
# showlegend=False,
|
|
95
|
+
showlegend=True if i == 0 else False,
|
|
96
|
+
visible=(key_index == 0),
|
|
97
|
+
textposition="top left",
|
|
98
|
+
)
|
|
99
|
+
data.append(line)
|
|
100
|
+
fig.add_trace(line)
|
|
101
|
+
# Add a vertical line at x=2 using data coordinates
|
|
102
|
+
|
|
103
|
+
end += 1
|
|
104
|
+
|
|
105
|
+
visible_dict[key] = (start, end)
|
|
106
|
+
start = end
|
|
107
|
+
|
|
108
|
+
|
|
109
|
+
|
|
110
|
+
|
|
111
|
+
# Buttons for dropdown menu
|
|
112
|
+
for dd_key, limit in visible_dict.items():
|
|
113
|
+
visible = [False] * len(fig.data)
|
|
114
|
+
visible[limit[0] : limit[1]] = [True] * (limit[1]-limit[0])
|
|
115
|
+
buttons.append(dict(
|
|
116
|
+
label=dd_key,
|
|
117
|
+
method="update",
|
|
118
|
+
args=[{"visible": visible},
|
|
119
|
+
{'yaxis': {'title': y_axis_dict[dd_key]}},
|
|
120
|
+
{"title": f"{batch_name} : {sensor} - {dd_key}"}]
|
|
121
|
+
))
|
|
122
|
+
|
|
123
|
+
# fig.add_annotation(textangle=-90)
|
|
124
|
+
# Add dropdown menu
|
|
125
|
+
fig.update_layout(
|
|
126
|
+
updatemenus=[go.layout.Updatemenu(
|
|
127
|
+
buttons=buttons,
|
|
128
|
+
direction="down",
|
|
129
|
+
pad={"r": 10, "t": 10},
|
|
130
|
+
showactive=False,
|
|
131
|
+
x=0.1,
|
|
132
|
+
xanchor="left",
|
|
133
|
+
y=1.1,
|
|
134
|
+
yanchor="top"
|
|
135
|
+
)]
|
|
136
|
+
)
|
|
137
|
+
|
|
138
|
+
text = batch_name + "_" + sensor
|
|
139
|
+
fig.update_layout(
|
|
140
|
+
# yaxis_range=[0, 1],
|
|
141
|
+
xaxis_title='Wavelength',
|
|
142
|
+
# yaxis_title='Reflectance',
|
|
143
|
+
title={
|
|
144
|
+
'text': batch_name + " : " + sensor,
|
|
145
|
+
'font': {
|
|
146
|
+
'size': 24,
|
|
147
|
+
'color': 'black',
|
|
148
|
+
'family': 'Arial',
|
|
149
|
+
'weight': 'bold'
|
|
150
|
+
},
|
|
151
|
+
'x': 0.5,
|
|
152
|
+
'xanchor': 'center'
|
|
153
|
+
},
|
|
154
|
+
)
|
|
155
|
+
fig.update_layout(hovermode="x unified")
|
|
156
|
+
fig.update_traces(textposition='top center')
|
|
157
|
+
|
|
158
|
+
if download:
|
|
159
|
+
fig.write_html(text + ".html")
|
|
160
|
+
|
|
161
|
+
fig.show()
|
|
162
|
+
|
|
163
|
+
|
|
@@ -0,0 +1,40 @@
|
|
|
1
|
+
import pytest
|
|
2
|
+
import numpy as np
|
|
3
|
+
import pandas as pd
|
|
4
|
+
import os
|
|
5
|
+
from src import dataloader
|
|
6
|
+
|
|
7
|
+
def test_is_float():
|
|
8
|
+
assert dataloader.is_float('1.23')
|
|
9
|
+
assert not dataloader.is_float('abc')
|
|
10
|
+
|
|
11
|
+
def test_find_xy_reflectance():
|
|
12
|
+
line = 'wavelength reflectance'
|
|
13
|
+
x, y = dataloader.find_xy(line)
|
|
14
|
+
assert x == 'wavelength' and y == 'reflectance'
|
|
15
|
+
|
|
16
|
+
def test_find_xy_absorbance():
|
|
17
|
+
line = 'wavenumber absorbance'
|
|
18
|
+
x, y = dataloader.find_xy(line)
|
|
19
|
+
assert x == 'wavenumber' and y == 'absorbance'
|
|
20
|
+
|
|
21
|
+
def test_load_data(tmp_path):
|
|
22
|
+
# Create a dummy reflectance file
|
|
23
|
+
file_path = tmp_path / 'test.txt'
|
|
24
|
+
with open(file_path, 'w') as f:
|
|
25
|
+
f.write('wavelength reflectance\n')
|
|
26
|
+
f.write('1000 0.1\n')
|
|
27
|
+
f.write('1100 0.2\n')
|
|
28
|
+
reflectance_df, absorbance_df = dataloader.load_data([str(file_path)], 'reflectance', 'wavelength reflectance')
|
|
29
|
+
assert reflectance_df is not None
|
|
30
|
+
assert absorbance_df is None
|
|
31
|
+
assert 'sample' in reflectance_df.columns
|
|
32
|
+
|
|
33
|
+
def test_load_refSpectrum(tmp_path):
|
|
34
|
+
file_path = tmp_path / 'ref.txt'
|
|
35
|
+
with open(file_path, 'w') as f:
|
|
36
|
+
f.write('sample 1000 0.1\n')
|
|
37
|
+
f.write('sample 1100 0.2\n')
|
|
38
|
+
df = dataloader.load_refSpectrum([str(file_path)])
|
|
39
|
+
assert df is not None
|
|
40
|
+
assert 'polymer' in df.columns or 'sample' in df.columns
|
|
@@ -0,0 +1,23 @@
|
|
|
1
|
+
import pytest
|
|
2
|
+
import numpy as np
|
|
3
|
+
import pandas as pd
|
|
4
|
+
from src import imaging_sensor
|
|
5
|
+
|
|
6
|
+
def test_imaging_sensor_init():
|
|
7
|
+
sensor = imaging_sensor.ImagingSensor()
|
|
8
|
+
assert hasattr(sensor, 'widget1')
|
|
9
|
+
assert hasattr(sensor, 'hylib_dict')
|
|
10
|
+
assert sensor.hylib_dict is None
|
|
11
|
+
|
|
12
|
+
def test_select_absorbance(monkeypatch):
|
|
13
|
+
sensor = imaging_sensor.ImagingSensor()
|
|
14
|
+
sensor.hylib_dict = {'dummy': pd.DataFrame(np.random.rand(2, 2))}
|
|
15
|
+
sensor.checkBoxAbsorbance = type('obj', (), {'isChecked': lambda self: True})()
|
|
16
|
+
sensor.sender = lambda: sensor.checkBoxAbsorbance
|
|
17
|
+
# Patch convert_absorbance to check if called
|
|
18
|
+
called = {}
|
|
19
|
+
def fake_convert():
|
|
20
|
+
called['yes'] = True
|
|
21
|
+
sensor.convert_absorbance = fake_convert
|
|
22
|
+
sensor.select_absorbance(Qt.Checked)
|
|
23
|
+
assert called.get('yes')
|
|
@@ -0,0 +1,50 @@
|
|
|
1
|
+
import pytest
|
|
2
|
+
import numpy as np
|
|
3
|
+
import pandas as pd
|
|
4
|
+
import os
|
|
5
|
+
from src import vizualization
|
|
6
|
+
|
|
7
|
+
def test_hex_rgba():
|
|
8
|
+
# Test the hex_rgba function for correct rgba output
|
|
9
|
+
hex_color = '#FF0000'
|
|
10
|
+
transparency = 0.5
|
|
11
|
+
expected = (255, 0, 0, 0.5)
|
|
12
|
+
result = vizualization.hex_rgba(hex_color, transparency)
|
|
13
|
+
assert result == expected
|
|
14
|
+
|
|
15
|
+
def test_create_masked_image(tmp_path, monkeypatch):
|
|
16
|
+
# Create dummy RGB and mask files
|
|
17
|
+
import cv2
|
|
18
|
+
rgb = np.ones((60, 60, 3), dtype=np.uint8) * 255
|
|
19
|
+
mask = np.zeros((10, 10, 1), dtype=np.uint8)
|
|
20
|
+
mask[2:8, 2:8, 0] = 1
|
|
21
|
+
rgb_path = tmp_path / 'RGB.png'
|
|
22
|
+
mask_path = tmp_path / 'mask.hdr'
|
|
23
|
+
cv2.imwrite(str(rgb_path), rgb)
|
|
24
|
+
# Patch io.load to return a dummy object with .data
|
|
25
|
+
class DummyMask:
|
|
26
|
+
def __init__(self, data):
|
|
27
|
+
self.data = data
|
|
28
|
+
monkeypatch.setattr(vizualization.io, 'load', lambda path: DummyMask(mask))
|
|
29
|
+
vizualization.create_masked_image(str(tmp_path))
|
|
30
|
+
assert os.path.exists(tmp_path / 'RGB_masked.png')
|
|
31
|
+
|
|
32
|
+
def test_viz(monkeypatch):
|
|
33
|
+
# Test viz function with minimal DataFrame
|
|
34
|
+
df = pd.DataFrame({
|
|
35
|
+
'sample': ['A', 'B'],
|
|
36
|
+
1000: [0.1, 0.2],
|
|
37
|
+
1100: [0.2, 0.3]
|
|
38
|
+
})
|
|
39
|
+
df_plot = {'test': [df, 'Reflectance']}
|
|
40
|
+
# Patch fig.show to avoid opening browser
|
|
41
|
+
monkeypatch.setattr(vizualization.go.Figure, 'show', lambda self: None)
|
|
42
|
+
vizualization.viz('batch', df_plot, sensor='imaging', download=False)
|
|
43
|
+
|
|
44
|
+
def test_viz_image_data(monkeypatch, tmp_path):
|
|
45
|
+
# Test viz_image_data with dummy hylib_dict
|
|
46
|
+
df = pd.DataFrame(np.random.rand(5, 5), columns=[str(i) for i in range(5)])
|
|
47
|
+
hylib_dict = {'test': df}
|
|
48
|
+
# Patch fig.show to avoid opening browser
|
|
49
|
+
monkeypatch.setattr(vizualization.go.Figure, 'show', lambda self: None)
|
|
50
|
+
vizualization.viz_image_data('batch', hylib_dict, str(tmp_path), sensor='imaging', download=False)
|