hsi-preprocessing-toolkit 1.1.3__tar.gz → 1.1.5.dev4__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.
- {hsi_preprocessing_toolkit-1.1.3 → hsi_preprocessing_toolkit-1.1.5.dev4}/PKG-INFO +2 -1
- {hsi_preprocessing_toolkit-1.1.3 → hsi_preprocessing_toolkit-1.1.5.dev4}/README.md +1 -0
- {hsi_preprocessing_toolkit-1.1.3 → hsi_preprocessing_toolkit-1.1.5.dev4}/pyproject.toml +2 -2
- {hsi_preprocessing_toolkit-1.1.3 → hsi_preprocessing_toolkit-1.1.5.dev4}/src/hsi_preprocessing_toolkit/__main__.py +212 -168
- hsi_preprocessing_toolkit-1.1.5.dev4/src/hsi_preprocessing_toolkit/algorithm.py +42 -0
- hsi_preprocessing_toolkit-1.1.5.dev4/src/hsi_preprocessing_toolkit/i18n.py +78 -0
- {hsi_preprocessing_toolkit-1.1.3 → hsi_preprocessing_toolkit-1.1.5.dev4}/src/hsi_preprocessing_toolkit/__init__.py +0 -0
- {hsi_preprocessing_toolkit-1.1.3/src/hsi_preprocessing_toolkit → hsi_preprocessing_toolkit-1.1.5.dev4/src/hsi_preprocessing_toolkit/page}/scanner_calc.py +0 -0
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
Metadata-Version: 2.4
|
|
2
2
|
Name: hsi-preprocessing-toolkit
|
|
3
|
-
Version: 1.1.
|
|
3
|
+
Version: 1.1.5.dev4
|
|
4
4
|
Summary: HSI Preprocessing Toolkit
|
|
5
5
|
Author: songyz2023
|
|
6
6
|
Author-email: songyz2023 <songyz2023dlut@outlook.com>
|
|
@@ -39,6 +39,7 @@ A Hyperspectral Preprocessing Toolkit that
|
|
|
39
39
|
3. Preview HSI, and convert it to RGB `.png` file
|
|
40
40
|
4. Crop and rotate the HSI and preview in realtime
|
|
41
41
|
5. Select spectrals of interest visually and save them into a `.mat` file
|
|
42
|
+
6. Mix multiple HSI image with layers. (coming in v2.0.0, now availabe in git)
|
|
42
43
|
|
|
43
44
|
|
|
44
45
|
## Usage
|
|
@@ -16,6 +16,7 @@ A Hyperspectral Preprocessing Toolkit that
|
|
|
16
16
|
3. Preview HSI, and convert it to RGB `.png` file
|
|
17
17
|
4. Crop and rotate the HSI and preview in realtime
|
|
18
18
|
5. Select spectrals of interest visually and save them into a `.mat` file
|
|
19
|
+
6. Mix multiple HSI image with layers. (coming in v2.0.0, now availabe in git)
|
|
19
20
|
|
|
20
21
|
|
|
21
22
|
## Usage
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
[project]
|
|
2
2
|
name = "hsi-preprocessing-toolkit"
|
|
3
|
-
version = "1.1.
|
|
3
|
+
version = "1.1.5.dev4"
|
|
4
4
|
description = "HSI Preprocessing Toolkit"
|
|
5
5
|
readme = "README.md"
|
|
6
6
|
requires-python = ">=3.13"
|
|
@@ -33,7 +33,7 @@ url = "https://mirrors.tuna.tsinghua.edu.cn/pypi/web/simple"
|
|
|
33
33
|
default = true
|
|
34
34
|
|
|
35
35
|
[build-system]
|
|
36
|
-
requires = ["uv_build >= 0.8.0, <0.
|
|
36
|
+
requires = ["uv_build >= 0.8.0, <0.10.0"]
|
|
37
37
|
build-backend = "uv_build"
|
|
38
38
|
|
|
39
39
|
[tool.hatch.build.targets.wheel]
|
|
@@ -8,91 +8,19 @@ from rasterio import open as rasterio_open
|
|
|
8
8
|
from scipy.io import savemat, loadmat
|
|
9
9
|
import gradio as gr
|
|
10
10
|
import gradio.utils
|
|
11
|
-
from scipy.ndimage import rotate
|
|
12
11
|
from rs_fusion_datasets.util.hsi2rgb import _hsi2rgb, hsi2rgb
|
|
13
12
|
from jaxtyping import Float
|
|
14
13
|
from enum import Enum
|
|
15
|
-
from scanner_calc import scanner_calc_tab
|
|
16
|
-
|
|
17
|
-
|
|
18
|
-
|
|
19
|
-
|
|
20
|
-
|
|
21
|
-
|
|
22
|
-
|
|
23
|
-
|
|
24
|
-
|
|
25
|
-
"input_format": "Input Image Shape Format",
|
|
26
|
-
"data_files": "Data Files",
|
|
27
|
-
"manual_normalize": "Manual Normalize (affects preview only)",
|
|
28
|
-
"normalize_min": "Normalize Min",
|
|
29
|
-
"normalize_max": "Normalize Max",
|
|
30
|
-
"wavelength_start": "Wavelength Range Start",
|
|
31
|
-
"wavelength_end": "Wavelength Range End",
|
|
32
|
-
"processing": "Processing",
|
|
33
|
-
"crop": "Crop",
|
|
34
|
-
"top": "Top",
|
|
35
|
-
"bottom": "Bottom",
|
|
36
|
-
"left": "Left",
|
|
37
|
-
"right": "Right",
|
|
38
|
-
"rotate": "Rotate",
|
|
39
|
-
"rotate_degree": "Rotate Degree",
|
|
40
|
-
"preview": "Preview",
|
|
41
|
-
"apply_processing": "Apply Processing Effects",
|
|
42
|
-
"mat_data_type": "MAT Data Type",
|
|
43
|
-
"mat_format": "MAT Image Shape Format",
|
|
44
|
-
"mat_key": "Key of MAT file",
|
|
45
|
-
"compress_mat": "Produce Compressed MAT File",
|
|
46
|
-
"spectral_selection": "Spectral Selection",
|
|
47
|
-
"spectral_selection_help": "Click on the image to select pixels for spectral data extraction. The selected pixels will be plotted in the spectral plot below.",
|
|
48
|
-
"spectral_plot": "Spectral Plot",
|
|
49
|
-
"style": "Style",
|
|
50
|
-
"clear": "Clear",
|
|
51
|
-
"download": "Download",
|
|
52
|
-
"output_results": "Output Results",
|
|
53
|
-
"mat_file": "MAT File",
|
|
54
|
-
"info": "Info",
|
|
55
|
-
"same_as_input": "Same",
|
|
56
|
-
"auto_detect": "Auto Detect",
|
|
57
|
-
},
|
|
58
|
-
'zh-CN':{
|
|
59
|
-
"title": "高光谱图像预处理工具箱",
|
|
60
|
-
"load": "加载",
|
|
61
|
-
"upload_instructions": "**应上传以下两种格式中的一种**\n1. 同时上传一个.hdr文件 + 一个无后缀的数据文件\n2. 一个.mat文件",
|
|
62
|
-
"input_format": "输入数据形状",
|
|
63
|
-
"data_files": "数据文件",
|
|
64
|
-
"manual_normalize": "手动归一化(仅影响预览结果)",
|
|
65
|
-
"normalize_min": "归一化最小值",
|
|
66
|
-
"normalize_max": "归一化最大值",
|
|
67
|
-
"wavelength_start": "波长范围起始",
|
|
68
|
-
"wavelength_end": "波长范围结束",
|
|
69
|
-
"processing": "处理",
|
|
70
|
-
"crop": "裁切",
|
|
71
|
-
"top": "上",
|
|
72
|
-
"bottom": "下",
|
|
73
|
-
"left": "左",
|
|
74
|
-
"right": "右",
|
|
75
|
-
"rotate": "旋转",
|
|
76
|
-
"rotate_degree": "旋转角度",
|
|
77
|
-
"preview": "预览",
|
|
78
|
-
"apply_processing": "应用处理效果",
|
|
79
|
-
"mat_data_type": "mat文件数据类型",
|
|
80
|
-
"mat_format": "mat文件格式",
|
|
81
|
-
"mat_key": "mat文件的key",
|
|
82
|
-
"compress_mat": "启用mat文件压缩",
|
|
83
|
-
"spectral_selection": "光谱选择",
|
|
84
|
-
"spectral_selection_help": "点击预览图像图像中的像素进行光谱数据提取。选中的像素将在下方的光谱图中绘制。",
|
|
85
|
-
"spectral_plot": "光谱图",
|
|
86
|
-
"style": "样式",
|
|
87
|
-
"clear": "清空",
|
|
88
|
-
"download": "下载",
|
|
89
|
-
"output_results": "输出结果",
|
|
90
|
-
"mat_file": "MAT文件",
|
|
91
|
-
"info": "信息",
|
|
92
|
-
"same_as_input": "与输入相同",
|
|
93
|
-
"auto_detect": "自动检测",
|
|
94
|
-
}
|
|
95
|
-
})
|
|
14
|
+
from .page.scanner_calc import scanner_calc_tab
|
|
15
|
+
import logging
|
|
16
|
+
from .i18n import i18n
|
|
17
|
+
from .algorithm import composite_img
|
|
18
|
+
|
|
19
|
+
|
|
20
|
+
logging.basicConfig(level=logging.INFO)
|
|
21
|
+
logger = logging.getLogger(__name__)
|
|
22
|
+
logger.info("started")
|
|
23
|
+
|
|
96
24
|
|
|
97
25
|
class AppState(Enum):
|
|
98
26
|
NOT_LOADED = 0
|
|
@@ -151,19 +79,7 @@ def load_data(
|
|
|
151
79
|
return data, data_path
|
|
152
80
|
|
|
153
81
|
|
|
154
|
-
# HWC -> HWC
|
|
155
|
-
def process_img(img, crop_top, crop_left, crop_bottom, crop_right, rotate_deg):
|
|
156
|
-
# Rotate
|
|
157
|
-
if rotate_deg % 360 != 0:
|
|
158
|
-
img = rotate(img, angle=rotate_deg, axes=(0, 1), reshape=True)
|
|
159
82
|
|
|
160
|
-
# Crop
|
|
161
|
-
if crop_top > 0 or crop_left > 0 or crop_bottom > 0 or crop_right > 0:
|
|
162
|
-
crop_bottom = None if crop_bottom == 0 else -crop_bottom
|
|
163
|
-
crop_right = None if crop_right == 0 else -crop_right
|
|
164
|
-
img = img[crop_top:crop_bottom, crop_left:crop_right, :]
|
|
165
|
-
|
|
166
|
-
return img
|
|
167
83
|
|
|
168
84
|
# def update_ui(state_app_state: AppState):
|
|
169
85
|
# gr.Info(f"App state changed to {state_app_state.name}")
|
|
@@ -179,11 +95,15 @@ def process_img(img, crop_top, crop_left, crop_bottom, crop_right, rotate_deg):
|
|
|
179
95
|
# )
|
|
180
96
|
|
|
181
97
|
def gr_load(
|
|
98
|
+
state_current_layer_index,
|
|
99
|
+
state_transforms :list[dict], state_original_rgb, state_original_data, state_original_data_path,
|
|
182
100
|
dat_files :List[gradio.utils.NamedString] | None,
|
|
183
101
|
input_format, input_mat_key: str | None,
|
|
184
102
|
manual_normalize: bool, normalize_min: float, normalize_max: float,
|
|
185
103
|
wavelength_from: int, wavelength_to: int,
|
|
186
104
|
):
|
|
105
|
+
logger.info(f"gr_load {state_current_layer_index=} {len(state_original_rgb)=}")
|
|
106
|
+
|
|
187
107
|
data, data_path = load_data(dat_files, input_format, input_mat_key)
|
|
188
108
|
if data is None:
|
|
189
109
|
raise gr.Error("No data file provided or data loading failed.")
|
|
@@ -201,26 +121,40 @@ def gr_load(
|
|
|
201
121
|
"original_reflection_mean": float(data.mean()),
|
|
202
122
|
"original_reflection_std": float(data.std()),
|
|
203
123
|
})
|
|
204
|
-
return rgb, data, data_path, AppState.LOADED, logging_text
|
|
205
124
|
|
|
206
|
-
|
|
207
|
-
|
|
208
|
-
|
|
209
|
-
|
|
125
|
+
# FIXME: Do not use dirty fix
|
|
126
|
+
while state_current_layer_index >= len(state_original_rgb):
|
|
127
|
+
state_original_rgb.append(None)
|
|
128
|
+
state_original_data.append(None)
|
|
129
|
+
state_original_data_path.append(None)
|
|
130
|
+
state_transforms.append(DEFAULT_TRANSFORM)
|
|
131
|
+
|
|
132
|
+
state_original_rgb[state_current_layer_index] = rgb
|
|
133
|
+
state_original_data[state_current_layer_index] = data
|
|
134
|
+
state_original_data_path[state_current_layer_index] = data_path
|
|
135
|
+
state_transforms[state_current_layer_index] = DEFAULT_TRANSFORM
|
|
136
|
+
|
|
137
|
+
logger.info(f"gr_loaded {state_current_layer_index=} {len(state_original_rgb)=}")
|
|
138
|
+
|
|
139
|
+
return state_transforms, state_original_rgb, state_original_data, state_original_data_path, AppState.LOADED, logging_text
|
|
140
|
+
|
|
141
|
+
def gr_composite(
|
|
142
|
+
state_original_rgb :Float[np.ndarray, 'h w c'] | None,
|
|
143
|
+
state_transforms,
|
|
210
144
|
):
|
|
211
|
-
|
|
212
|
-
|
|
213
|
-
|
|
214
|
-
|
|
215
|
-
), AppState.PREVIEWED
|
|
145
|
+
logger.info("gr_composite")
|
|
146
|
+
img = composite_img(state_original_rgb, state_transforms)
|
|
147
|
+
logger.info(f"gr_composited {img.shape=} {type(img)=}")
|
|
148
|
+
return img, AppState.PREVIEWED
|
|
216
149
|
|
|
217
|
-
|
|
150
|
+
|
|
151
|
+
def gr_download_selected_spectral(state_current_layer_index, data, state_select_location, data_path):
|
|
218
152
|
if not state_select_location or len(state_select_location) == 0:
|
|
219
153
|
raise gr.Error("No spectral data selected. Please select at least one pixel to download")
|
|
220
154
|
|
|
221
155
|
gr.Info("Converting ...")
|
|
222
156
|
|
|
223
|
-
dat_path = Path(data_path.name)
|
|
157
|
+
dat_path = Path(data_path[state_current_layer_index].name)
|
|
224
158
|
result = []
|
|
225
159
|
result_location = []
|
|
226
160
|
for (row, col) in state_select_location:
|
|
@@ -238,17 +172,17 @@ def gr_download_selected_spectral(data, state_select_location, data_path):
|
|
|
238
172
|
return str(mat_path)
|
|
239
173
|
|
|
240
174
|
def gr_convert(
|
|
175
|
+
state_current_layer_index,
|
|
176
|
+
state_transforms,
|
|
241
177
|
data, data_path,
|
|
242
|
-
crop_top: int, crop_left: int, crop_bottom: int, crop_right: int,
|
|
243
|
-
rotate_deg: int,
|
|
244
178
|
mat_dtype, output_format, mat_key, compress_mat: bool,
|
|
245
179
|
logging_text: str
|
|
246
180
|
):
|
|
247
|
-
|
|
248
|
-
output_format = input_format
|
|
181
|
+
logger.info("gr_convert")
|
|
249
182
|
|
|
250
|
-
|
|
251
|
-
|
|
183
|
+
data_path = data_path[state_current_layer_index]
|
|
184
|
+
data = composite_img(
|
|
185
|
+
data, state_transforms
|
|
252
186
|
)
|
|
253
187
|
|
|
254
188
|
# Convert to mat
|
|
@@ -313,26 +247,80 @@ def gr_on_img_clicked(evt: gr.SelectData, state_figure :tuple, data, state_selec
|
|
|
313
247
|
state_figure = (fig, ax)
|
|
314
248
|
return fig, state_figure, state_select_location
|
|
315
249
|
|
|
250
|
+
def gr_update_transforms(state_transforms, state_current_layer_index, crop_top, crop_left, crop_bottom, crop_right, rotate_deg, offset_x, offset_y):
|
|
251
|
+
state_transforms[state_current_layer_index] = {
|
|
252
|
+
'rotation': rotate_deg,
|
|
253
|
+
'crop': [crop_top, crop_left, crop_bottom, crop_right],
|
|
254
|
+
'location': [offset_x, offset_y]
|
|
255
|
+
}
|
|
256
|
+
return state_transforms
|
|
316
257
|
|
|
317
|
-
|
|
258
|
+
def gr_on_state_current_layer_index_changed(state_current_layer_index, state_transforms, state_original_data):
|
|
259
|
+
if state_current_layer_index >= len(state_transforms):
|
|
260
|
+
trans = DEFAULT_TRANSFORM
|
|
261
|
+
shape = [0,0]
|
|
262
|
+
else:
|
|
263
|
+
trans = state_transforms[state_current_layer_index]
|
|
264
|
+
shape = state_original_data[state_current_layer_index].shape
|
|
265
|
+
|
|
266
|
+
crop_top, crop_left, crop_bottom, crop_right = trans['crop']
|
|
267
|
+
rotate_deg = trans['rotation']
|
|
268
|
+
offset_x, offset_y = trans['location']
|
|
269
|
+
|
|
270
|
+
max_h, max_w = shape[:2]
|
|
271
|
+
logger.info(f"gr_on_state_current_layer_index_changed {state_current_layer_index=} {max_h=}, {max_w=}")
|
|
272
|
+
update_crop_top = gr.update(value=crop_top, maximum=max_h, minimum=0)
|
|
273
|
+
update_crop_bottom = gr.update(value=crop_bottom, maximum=max_h, minimum=0)
|
|
274
|
+
update_crop_left = gr.update(value=crop_left, maximum=max_w, minimum=0)
|
|
275
|
+
update_crop_right = gr.update(value=crop_bottom, maximum=max_w, minimum=0)
|
|
276
|
+
|
|
277
|
+
return update_crop_top, update_crop_left, update_crop_bottom, update_crop_right, rotate_deg, offset_x, offset_y
|
|
278
|
+
|
|
279
|
+
|
|
280
|
+
DEFAULT_TRANSFORM = {
|
|
281
|
+
'rotation': 0,
|
|
282
|
+
'crop': [0,0,0,0],
|
|
283
|
+
'location': [0,0]
|
|
284
|
+
}
|
|
285
|
+
|
|
286
|
+
def main():
|
|
318
287
|
theme = gr.themes.Default(primary_hue='cyan').set(
|
|
319
288
|
button_primary_background_fill='#39c5bb',
|
|
320
289
|
button_primary_background_fill_hover="#30A8A0",
|
|
321
290
|
)
|
|
322
291
|
|
|
323
292
|
with gr.Blocks(title="hsi-preprocessing-toolkit", theme=theme) as demo:
|
|
324
|
-
|
|
325
|
-
|
|
326
|
-
|
|
327
|
-
|
|
328
|
-
|
|
329
|
-
|
|
330
|
-
|
|
293
|
+
# 应用整体状态
|
|
294
|
+
state_app_state = gr.State(value=AppState.NOT_LOADED) # 操作阶段
|
|
295
|
+
# 输入的状态,支持多输出
|
|
296
|
+
state_current_layer_index = gr.State(value=0) # 已选中的图层的数组index
|
|
297
|
+
state_max_layer_index = gr.State(value=0) # 已选中的图层的数组index
|
|
298
|
+
state_original_data = gr.State(value=[]) # 原数据
|
|
299
|
+
state_data_path = gr.State(value=[]) # 原数据文件路径
|
|
300
|
+
state_original_rgb = gr.State(value=[]) # 原数据的RGB代理
|
|
301
|
+
state_transforms = gr.State(value=[]) # 对数据的变换
|
|
302
|
+
# 缓存状态
|
|
303
|
+
# state_original_alpha = gr.State(value=[]) # 用于合成的Alpha通道
|
|
304
|
+
# state_original_offset = gr.State(value=[]) # 尽管用alpha更好,但先用offset吧
|
|
305
|
+
# 中间状态,多个
|
|
306
|
+
# state_local_rgb = gr.State(value=[])
|
|
307
|
+
# 输出内容状态,保持单个
|
|
308
|
+
state_processed_data = gr.State(value=None) # 处理后的数据
|
|
309
|
+
state_selected_location = gr.State(value=[]) # 已选择的光谱XY坐标点
|
|
310
|
+
state_spectral_figure = gr.State(value=None) # 已选择的光谱的绘图
|
|
331
311
|
|
|
332
312
|
|
|
333
313
|
with gr.Tab(i18n("title"), id="hsi_preprocessing_toolkit"):
|
|
334
314
|
with gr.Row():
|
|
335
315
|
with gr.Column():
|
|
316
|
+
with gr.Column():
|
|
317
|
+
current_layer_index_slider = gr.Slider(
|
|
318
|
+
label="当前图层",
|
|
319
|
+
minimum=0,
|
|
320
|
+
maximum=3,
|
|
321
|
+
step=1,
|
|
322
|
+
value=0
|
|
323
|
+
)
|
|
336
324
|
with gr.Accordion(i18n("load")) as load_panel:
|
|
337
325
|
gr.Markdown(i18n("upload_instructions"))
|
|
338
326
|
with gr.Row():
|
|
@@ -391,8 +379,10 @@ if __name__ == "__main__":
|
|
|
391
379
|
value=1000,
|
|
392
380
|
precision=1,
|
|
393
381
|
)
|
|
394
|
-
|
|
395
|
-
|
|
382
|
+
with gr.Row():
|
|
383
|
+
reload_btn = gr.Button(i18n("load"), variant="primary")
|
|
384
|
+
reset_loaded_btn = gr.Button(i18n("clear"), variant="secondary")
|
|
385
|
+
# TODO: set maxium to be shape[i]
|
|
396
386
|
with gr.Accordion(i18n("processing"), visible=False) as preview_panel:
|
|
397
387
|
with gr.Column():
|
|
398
388
|
gr.Markdown(f"### {i18n('crop')}")
|
|
@@ -400,26 +390,26 @@ if __name__ == "__main__":
|
|
|
400
390
|
crop_top = gr.Slider(
|
|
401
391
|
label=i18n("top"),
|
|
402
392
|
minimum=0,
|
|
403
|
-
maximum=
|
|
393
|
+
maximum=9999,
|
|
404
394
|
step=1,
|
|
405
395
|
)
|
|
406
396
|
crop_bottom = gr.Slider(
|
|
407
397
|
label=i18n("bottom"),
|
|
408
398
|
minimum=0,
|
|
409
|
-
maximum=
|
|
399
|
+
maximum=9999,
|
|
410
400
|
step=1,
|
|
411
401
|
)
|
|
412
402
|
with gr.Row():
|
|
413
403
|
crop_left = gr.Slider(
|
|
414
404
|
label=i18n("left"),
|
|
415
405
|
minimum=0,
|
|
416
|
-
maximum=
|
|
406
|
+
maximum=9999,
|
|
417
407
|
step=1,
|
|
418
408
|
)
|
|
419
409
|
crop_right = gr.Slider(
|
|
420
410
|
label=i18n("right"),
|
|
421
411
|
minimum=0,
|
|
422
|
-
maximum=
|
|
412
|
+
maximum=9999,
|
|
423
413
|
step=1,
|
|
424
414
|
)
|
|
425
415
|
|
|
@@ -432,24 +422,36 @@ if __name__ == "__main__":
|
|
|
432
422
|
step=1,
|
|
433
423
|
value=0
|
|
434
424
|
)
|
|
435
|
-
# 这个按钮因为实时预览实际上不需要了,但是暂且隐藏备用
|
|
436
|
-
preview_btn = gr.Button(i18n("preview"), variant="primary", visible=False)
|
|
437
425
|
|
|
426
|
+
with gr.Column():
|
|
427
|
+
gr.Markdown(f"### 平移")
|
|
428
|
+
with gr.Row():
|
|
429
|
+
offset_x = gr.Slider(
|
|
430
|
+
label="X轴",
|
|
431
|
+
minimum=0,
|
|
432
|
+
maximum=+9999,
|
|
433
|
+
step=1,
|
|
434
|
+
)
|
|
435
|
+
offset_y = gr.Slider(
|
|
436
|
+
label="Y轴",
|
|
437
|
+
minimum=0,
|
|
438
|
+
maximum=+9999,
|
|
439
|
+
step=1,
|
|
440
|
+
)
|
|
438
441
|
|
|
439
442
|
with gr.Accordion(i18n("apply_processing"), visible=False) as convert_panel:
|
|
440
443
|
mat_dtype = gr.Radio(
|
|
441
444
|
label=i18n("mat_data_type"),
|
|
442
|
-
choices=["
|
|
445
|
+
choices=["auto", "uint8", "uint16", "float32"],
|
|
443
446
|
value="float32"
|
|
444
447
|
)
|
|
445
448
|
output_format = gr.Radio(
|
|
446
449
|
label=i18n("mat_format"),
|
|
447
450
|
choices=[
|
|
448
|
-
'same',
|
|
449
451
|
'HWC',
|
|
450
452
|
'CHW'
|
|
451
453
|
],
|
|
452
|
-
value='
|
|
454
|
+
value='CHW'
|
|
453
455
|
)
|
|
454
456
|
mat_key = gr.Text(
|
|
455
457
|
label=i18n("mat_key"),
|
|
@@ -496,44 +498,74 @@ if __name__ == "__main__":
|
|
|
496
498
|
label=i18n("info"),
|
|
497
499
|
)
|
|
498
500
|
|
|
499
|
-
|
|
500
|
-
# dat_files.upload(
|
|
501
|
-
# fn=gr_on_file_upload,
|
|
502
|
-
# inputs=[dat_files, input_format, manual_normalize, normalize_min, normalize_max, wavelength_from, wavelength_to],
|
|
503
|
-
# outputs=[state_original_rgb, state_original_data, state_data_path, state_app_state]
|
|
504
|
-
# )
|
|
505
501
|
reload_btn.click(
|
|
506
502
|
fn=gr_load,
|
|
507
|
-
inputs=[dat_files, input_format, input_mat_key, manual_normalize, normalize_min, normalize_max, wavelength_from, wavelength_to],
|
|
503
|
+
inputs=[state_current_layer_index, state_transforms, state_original_rgb, state_original_data, state_data_path, dat_files, input_format, input_mat_key, manual_normalize, normalize_min, normalize_max, wavelength_from, wavelength_to],
|
|
504
|
+
outputs=[state_transforms, state_original_rgb, state_original_data, state_data_path, state_app_state, logging_text]
|
|
505
|
+
)
|
|
506
|
+
reset_loaded_btn.click(
|
|
507
|
+
fn=lambda logging_text: ([],[],[],AppState.NOT_LOADED, "已清空"),
|
|
508
|
+
inputs=[logging_text],
|
|
508
509
|
outputs=[state_original_rgb, state_original_data, state_data_path, state_app_state, logging_text]
|
|
509
510
|
)
|
|
510
511
|
convert_btn.click(
|
|
511
512
|
fn=gr_convert,
|
|
512
513
|
inputs=[
|
|
514
|
+
state_current_layer_index,
|
|
515
|
+
state_transforms,
|
|
513
516
|
state_original_data, state_data_path,
|
|
514
|
-
crop_top, crop_left, crop_bottom, crop_right,
|
|
515
|
-
rotate_deg,
|
|
516
517
|
mat_dtype, output_format, mat_key, compress_mat,
|
|
517
518
|
logging_text
|
|
518
519
|
],
|
|
519
520
|
outputs=[state_processed_data ,mat_file_output, logging_text, state_app_state]
|
|
520
521
|
)
|
|
521
|
-
for component in [crop_top, crop_left, crop_bottom, crop_right, rotate_deg]:
|
|
522
|
+
for component in [crop_top, crop_left, crop_bottom, crop_right, rotate_deg, offset_x, offset_y]:
|
|
522
523
|
component.change(
|
|
523
|
-
fn=
|
|
524
|
-
inputs=[
|
|
525
|
-
outputs=[
|
|
524
|
+
fn = gr_update_transforms,
|
|
525
|
+
inputs=[state_transforms, state_current_layer_index, crop_top, crop_left, crop_bottom, crop_right, rotate_deg, offset_x, offset_y],
|
|
526
|
+
outputs=[state_transforms]
|
|
526
527
|
)
|
|
527
|
-
|
|
528
|
-
|
|
529
|
-
fn=
|
|
530
|
-
inputs=[
|
|
531
|
-
state_original_rgb,
|
|
532
|
-
crop_top, crop_left, crop_bottom, crop_right,
|
|
533
|
-
rotate_deg,
|
|
534
|
-
],
|
|
528
|
+
|
|
529
|
+
state_transforms.change(
|
|
530
|
+
fn=gr_composite,
|
|
531
|
+
inputs=[state_original_rgb, state_transforms],
|
|
535
532
|
outputs=[preview_img, state_app_state]
|
|
536
533
|
)
|
|
534
|
+
# Legacy Code:
|
|
535
|
+
# preview_btn.click(
|
|
536
|
+
# fn=gr_composite,
|
|
537
|
+
# inputs=[
|
|
538
|
+
# state_original_rgb,
|
|
539
|
+
# crop_top, crop_left, crop_bottom, crop_right,
|
|
540
|
+
# rotate_deg,
|
|
541
|
+
# ],
|
|
542
|
+
# outputs=[preview_img, state_app_state]
|
|
543
|
+
# )
|
|
544
|
+
current_layer_index_slider.change(
|
|
545
|
+
fn = lambda x: x,
|
|
546
|
+
inputs=[current_layer_index_slider],
|
|
547
|
+
outputs=[state_current_layer_index]
|
|
548
|
+
|
|
549
|
+
)
|
|
550
|
+
|
|
551
|
+
# ------------
|
|
552
|
+
state_current_layer_index.change(
|
|
553
|
+
fn=gr_on_state_current_layer_index_changed,
|
|
554
|
+
inputs=[state_current_layer_index, state_transforms, state_original_data],
|
|
555
|
+
outputs=[
|
|
556
|
+
crop_top, crop_left, crop_bottom, crop_right, rotate_deg, offset_x, offset_y
|
|
557
|
+
]
|
|
558
|
+
)
|
|
559
|
+
state_original_data.change(
|
|
560
|
+
fn=gr_on_state_current_layer_index_changed,
|
|
561
|
+
inputs=[state_current_layer_index, state_transforms, state_original_data],
|
|
562
|
+
outputs=[
|
|
563
|
+
crop_top, crop_left, crop_bottom, crop_right, rotate_deg, offset_x, offset_y
|
|
564
|
+
]
|
|
565
|
+
)
|
|
566
|
+
# ------------
|
|
567
|
+
|
|
568
|
+
|
|
537
569
|
preview_img.select(
|
|
538
570
|
fn=gr_on_img_clicked,
|
|
539
571
|
inputs=[state_spectral_figure, state_processed_data, state_selected_location, wavelength_from, wavelength_to, plot_hint],
|
|
@@ -545,24 +577,26 @@ if __name__ == "__main__":
|
|
|
545
577
|
)
|
|
546
578
|
download_select_spectral.click(
|
|
547
579
|
fn=gr_download_selected_spectral,
|
|
548
|
-
inputs=[state_processed_data, state_selected_location, state_data_path],
|
|
580
|
+
inputs=[state_current_layer_index, state_processed_data, state_selected_location, state_data_path],
|
|
549
581
|
outputs=[download_select_spectral]
|
|
550
582
|
)
|
|
551
|
-
|
|
552
|
-
|
|
553
|
-
|
|
554
|
-
|
|
555
|
-
|
|
556
|
-
|
|
557
|
-
|
|
558
|
-
|
|
559
|
-
|
|
560
|
-
|
|
561
|
-
|
|
562
|
-
|
|
563
|
-
|
|
564
|
-
|
|
565
|
-
|
|
583
|
+
# TODO: Bring it back
|
|
584
|
+
# state_original_data.change(
|
|
585
|
+
# fn=lambda x,i: (
|
|
586
|
+
# gr.update(maximum=x[i].shape[1]),
|
|
587
|
+
# gr.update(maximum=x[i].shape[1]),
|
|
588
|
+
# gr.update(maximum=x[i].shape[0]),
|
|
589
|
+
# gr.update(maximum=x[i].shape[0]),
|
|
590
|
+
# ),
|
|
591
|
+
# inputs=[ state_original_data, state_current_layer_index ],
|
|
592
|
+
# outputs=[crop_left, crop_right, crop_top, crop_bottom]
|
|
593
|
+
# )
|
|
594
|
+
# TODO: shoud we bring it back?
|
|
595
|
+
# state_original_rgb.change(
|
|
596
|
+
# fn=lambda x:x,
|
|
597
|
+
# inputs=[state_original_rgb],
|
|
598
|
+
# outputs=[preview_img]
|
|
599
|
+
# )
|
|
566
600
|
state_app_state.change(
|
|
567
601
|
fn=lambda x: (
|
|
568
602
|
gr.update(visible=True, open=(x==AppState.NOT_LOADED)),
|
|
@@ -573,8 +607,18 @@ if __name__ == "__main__":
|
|
|
573
607
|
inputs=[state_app_state],
|
|
574
608
|
outputs=[load_panel, preview_panel, convert_panel, plot_panel]
|
|
575
609
|
)
|
|
610
|
+
# FIXME:
|
|
611
|
+
# state_max_layer_index.change(
|
|
612
|
+
# fn=lambda :gr.update(maximum=state_max_layer_index),
|
|
613
|
+
# inputs=[],
|
|
614
|
+
# outputs=[current_layer_index_slider]
|
|
615
|
+
# )
|
|
576
616
|
with gr.Tab("推扫参数计算"):
|
|
577
617
|
scanner_calc_tab()
|
|
578
618
|
|
|
579
619
|
demo.launch(share=False, inbrowser=True, i18n=i18n)
|
|
580
620
|
|
|
621
|
+
|
|
622
|
+
if __name__ == "__main__":
|
|
623
|
+
plt.rcParams['font.family'] = 'SimHei'
|
|
624
|
+
main()
|
|
@@ -0,0 +1,42 @@
|
|
|
1
|
+
from scipy.ndimage import rotate
|
|
2
|
+
import numpy as np
|
|
3
|
+
import logging
|
|
4
|
+
from copy import deepcopy
|
|
5
|
+
|
|
6
|
+
logging.basicConfig(level=logging.INFO)
|
|
7
|
+
logger = logging.getLogger(__name__)
|
|
8
|
+
logger.info("started")
|
|
9
|
+
|
|
10
|
+
# Return A HWC Image, the C should work with both RGB and HSI
|
|
11
|
+
def composite_img(imgs :list[np.ndarray], transforms:list[dict]):
|
|
12
|
+
imgs = deepcopy(imgs)
|
|
13
|
+
# Transform
|
|
14
|
+
logger.info(f"{transforms=}")
|
|
15
|
+
for i, (img,trans) in enumerate(zip(imgs, transforms)):
|
|
16
|
+
rotate_deg = trans['rotation']
|
|
17
|
+
if rotate_deg % 360 != 0:
|
|
18
|
+
imgs[i] = rotate(img, angle=rotate_deg, axes=(0, 1), reshape=True)
|
|
19
|
+
|
|
20
|
+
# Crop
|
|
21
|
+
crop_top, crop_left, crop_bottom, crop_right = trans['crop']
|
|
22
|
+
if crop_top > 0 or crop_left > 0 or crop_bottom > 0 or crop_right > 0:
|
|
23
|
+
crop_bottom = None if crop_bottom == 0 else -crop_bottom
|
|
24
|
+
crop_right = None if crop_right == 0 else -crop_right
|
|
25
|
+
imgs[i] = img[crop_top:crop_bottom, crop_left:crop_right, :]
|
|
26
|
+
|
|
27
|
+
# Caclate canvas size
|
|
28
|
+
shapes = [x.shape for x in imgs]
|
|
29
|
+
offsets = [ x['location'] for x in transforms ]
|
|
30
|
+
canvas_c = imgs[0].shape[-1]
|
|
31
|
+
sizes = [ ((h+abs(x)),w+abs(y)) for (h,w,_),(x,y) in zip(shapes, offsets)]
|
|
32
|
+
canvas_h, canvas_w = ( max([x[0] for x in sizes]), max([x[1] for x in sizes]) )
|
|
33
|
+
canvas_shape = (canvas_h, canvas_w, canvas_c)
|
|
34
|
+
logger.info(f"{sizes=} {canvas_shape=} {shapes=}")
|
|
35
|
+
canvas = np.zeros(shape=canvas_shape, dtype=imgs[0].dtype)
|
|
36
|
+
|
|
37
|
+
# 合成 Compositing
|
|
38
|
+
for img,trans in zip(imgs, transforms):
|
|
39
|
+
x,y = trans['location']
|
|
40
|
+
h,w,_ = img.shape
|
|
41
|
+
canvas[x:x+h, y:y+w] = img
|
|
42
|
+
return canvas
|
|
@@ -0,0 +1,78 @@
|
|
|
1
|
+
import gradio as gr
|
|
2
|
+
|
|
3
|
+
i18n = gr.I18n(**{
|
|
4
|
+
'en': {
|
|
5
|
+
"title": "hsi-preprocessing-toolkit",
|
|
6
|
+
"load": "Load",
|
|
7
|
+
"upload_instructions": "**Upload one of the following formats:**\n1. One .hdr file + one raw data file without extension\n2. One .mat file",
|
|
8
|
+
"input_format": "Input Image Shape Format",
|
|
9
|
+
"data_files": "Data Files",
|
|
10
|
+
"manual_normalize": "Manual Normalize (affects preview only)",
|
|
11
|
+
"normalize_min": "Normalize Min",
|
|
12
|
+
"normalize_max": "Normalize Max",
|
|
13
|
+
"wavelength_start": "Wavelength Range Start",
|
|
14
|
+
"wavelength_end": "Wavelength Range End",
|
|
15
|
+
"processing": "Processing",
|
|
16
|
+
"crop": "Crop",
|
|
17
|
+
"top": "Top",
|
|
18
|
+
"bottom": "Bottom",
|
|
19
|
+
"left": "Left",
|
|
20
|
+
"right": "Right",
|
|
21
|
+
"rotate": "Rotate",
|
|
22
|
+
"rotate_degree": "Rotate Degree",
|
|
23
|
+
"preview": "Preview",
|
|
24
|
+
"apply_processing": "Apply Processing Effects",
|
|
25
|
+
"mat_data_type": "MAT Data Type",
|
|
26
|
+
"mat_format": "MAT Image Shape Format",
|
|
27
|
+
"mat_key": "Key of MAT file",
|
|
28
|
+
"compress_mat": "Produce Compressed MAT File",
|
|
29
|
+
"spectral_selection": "Spectral Selection",
|
|
30
|
+
"spectral_selection_help": "Click on the image to select pixels for spectral data extraction. The selected pixels will be plotted in the spectral plot below.",
|
|
31
|
+
"spectral_plot": "Spectral Plot",
|
|
32
|
+
"style": "Style",
|
|
33
|
+
"clear": "Clear",
|
|
34
|
+
"download": "Download",
|
|
35
|
+
"output_results": "Output Results",
|
|
36
|
+
"mat_file": "MAT File",
|
|
37
|
+
"info": "Info",
|
|
38
|
+
"same_as_input": "Same",
|
|
39
|
+
"auto_detect": "Auto Detect",
|
|
40
|
+
},
|
|
41
|
+
'zh-CN':{
|
|
42
|
+
"title": "高光谱图像预处理工具箱",
|
|
43
|
+
"load": "加载",
|
|
44
|
+
"upload_instructions": "**应上传以下两种格式中的一种**\n1. 同时上传一个.hdr文件 + 一个无后缀的数据文件\n2. 一个.mat文件",
|
|
45
|
+
"input_format": "输入数据形状",
|
|
46
|
+
"data_files": "数据文件",
|
|
47
|
+
"manual_normalize": "手动归一化(仅影响预览结果)",
|
|
48
|
+
"normalize_min": "归一化最小值",
|
|
49
|
+
"normalize_max": "归一化最大值",
|
|
50
|
+
"wavelength_start": "波长范围起始",
|
|
51
|
+
"wavelength_end": "波长范围结束",
|
|
52
|
+
"processing": "处理",
|
|
53
|
+
"crop": "裁切",
|
|
54
|
+
"top": "上",
|
|
55
|
+
"bottom": "下",
|
|
56
|
+
"left": "左",
|
|
57
|
+
"right": "右",
|
|
58
|
+
"rotate": "旋转",
|
|
59
|
+
"rotate_degree": "旋转角度",
|
|
60
|
+
"preview": "预览",
|
|
61
|
+
"apply_processing": "应用处理效果",
|
|
62
|
+
"mat_data_type": "mat文件数据类型",
|
|
63
|
+
"mat_format": "mat文件格式",
|
|
64
|
+
"mat_key": "mat文件的key",
|
|
65
|
+
"compress_mat": "启用mat文件压缩",
|
|
66
|
+
"spectral_selection": "光谱选择",
|
|
67
|
+
"spectral_selection_help": "点击预览图像图像中的像素进行光谱数据提取。选中的像素将在下方的光谱图中绘制。",
|
|
68
|
+
"spectral_plot": "光谱图",
|
|
69
|
+
"style": "样式",
|
|
70
|
+
"clear": "清空",
|
|
71
|
+
"download": "下载",
|
|
72
|
+
"output_results": "输出结果",
|
|
73
|
+
"mat_file": "MAT文件",
|
|
74
|
+
"info": "信息",
|
|
75
|
+
"same_as_input": "与输入相同",
|
|
76
|
+
"auto_detect": "自动检测",
|
|
77
|
+
}
|
|
78
|
+
})
|
|
File without changes
|