IronKitty commited on
Commit
21aecfa
·
verified ·
1 Parent(s): bb11c1e

Update hyperspectral image reader imports, add utils, and update README for Hugging Face

Browse files
README.md CHANGED
@@ -7,42 +7,56 @@ tags:
7
  - foundation-model
8
  - image-segmentation
9
  ---
 
10
 
11
- # HyperVision: Channel-Adaptive Ground-Based Hyperspectral Vision Foundation Models
12
-
13
- *This is the official repository for the paper "HyperVision: Channel-Adaptive Ground-Based Hyperspectral Vision Foundation Models".*
14
  https://arxiv.org/abs/2605.17286
15
 
 
 
16
  **Code:** [https://github.com/lronkitty/HyperVision](https://github.com/lronkitty/HyperVision)
17
 
 
 
 
 
 
 
 
 
 
 
18
  ---
19
 
20
- 🚀 **News:**
21
- - **[2026-07-02]** Pre-trained checkpoints are now available! You can download them from [Hugging Face Checkpoints](https://huggingface.co/IronKitty/HyperVision/tree/main/checkpoints).
 
 
22
 
23
  ---
24
 
25
  ## 📖 Abstract
26
 
27
- While hyperspectral imaging provides rich spatial-spectral information across hundreds of narrow wavelength bands for precise material identification, ground-based hyperspectral pre-trained backbones remain absent, constrained by varying spectral configurations across sensors, the scarcity and inconsistency of labels, and the limited scale and scene diversity of existing datasets. To address these challenges and enable universal perception, we propose HyperVision, the first ground-based hyperspectral pre-trained backbone. First, to handle varying spectral configurations, HyperVision adopts a channel-adaptive dynamic embedding mechanism to map heterogeneous inputs into a unified token space. Second, to address the scarcity and inconsistency of labels, we introduce a multi-source pseudo-labeling method that fuses semantic representations from both spatial structures generated by SAM2 and fine-grained spectral material information extracted by HyperFree. Third, to compensate for limited dataset scale and enrich scene diversity, a cross-modal knowledge distillation mechanism is utilized to transfer rich semantic representations from a pre-trained RGB vision model to our hyperspectral backbone. Pre-trained on a collection of 15k images from 26 diverse ground-based datasets, HyperVision demonstrates exceptional generalization. Requiring only efficient head-only adaptation without adjusting backbone parameters, it achieves state-of-the-art performance compared to task-specific methods across three downstream tasks under varying sensor configurations, yielding up to a 16.3% relative improvement in hyperspectral semantic segmentation $\text{Acc}_{\text{M}}$, a 2.1% relative gain in object tracking AUC, and a 35.5% reduction in salient object detection MAE. The source code and pre-trained model will be publicly available online.
 
 
28
 
29
  ## 🛠️ Main Code (HyperVision)
30
 
31
- The core architecture and model initialization utilities are located under the [HyperVision](./HyperVision) directory.
32
 
33
- To initialize the model, you can use the model builder functions from [HyperVision/build_HyperVision.py](./HyperVision/build_HyperVision.py). The pre-trained checkpoints can be downloaded from [Hugging Face Checkpoints](https://huggingface.co/IronKitty/HyperVision/tree/main/checkpoints).
34
 
35
- We support different model configurations:
36
  - **HyperVision-B**: `build_HyperVision_b`
37
  - **HyperVision-L**: `build_HyperVision_l`
38
  - **HyperVision-H** (Default): `build_HyperVision_h`
39
 
40
  ### Python Example:
 
41
  ```python
42
  import torch
43
- from HyperVision import build_HyperVision_h, HyperVision_Predictor
44
- from hyperspectral_image_reader.read_dataset_image import load_hypervision_matrix
45
- from hyperspectral_image_reader.hyperspectral_pipelines import LoadHyperspectralImage
46
 
47
  device = "cuda" if torch.cuda.is_available() else "cpu"
48
  image_size = 512
@@ -52,42 +66,46 @@ model = build_HyperVision_h(
52
  checkpoint="path/to/hypervision_distilled_h.pth", # Optional checkpoint path (.pth weights)
53
  image_size=512, # Input resolution
54
  vit_patch_size=16, # Patch size of the backbone
55
- encoder_global_attn_indexes=[15, 23, 31], # Layers using global attention (can be adjusted for debugging/tuning)
56
- merge_indexs=[8, 32], # Layers doing patch merging (can be adjusted for debugging/tuning)
57
  class_number=-1 # Number of classes for mask decoder
58
  )
59
  model = model.to(device)
60
  model.eval()
61
 
62
- # 2. Use HyperVision_Predictor to manage preprocessing and feature extraction
63
- predictor = HyperVision_Predictor(model)
64
-
65
- # 3. Load the preprocessed HSI image as a numpy array using load_hypervision_matrix
66
  # Preprocessed shape: (H_ori, W_ori, C_hsi) with values in range [0, 255]
67
  img_matrix = load_hypervision_matrix("path/to/image.mat", dataset_name="harvard")
 
68
 
69
- # 4. Automatically retrieve the wavelengths list from the dataset pipeline config
 
 
 
70
  loader = LoadHyperspectralImage(dataset_type="harvard")
71
  wavelengths = loader.wavelengths
72
 
73
- # 5. Call predictor.set_image to preprocess the image and extract features (calculating embeddings)
74
- # GSD represents Ground Sampling Distance (defaults to 0.01)
75
  with torch.no_grad():
76
- predictor.set_image(img_matrix, test_mode=False, spectral_lengths=wavelengths, GSD=0.01)
77
-
78
- # 6. Extract multi-scale features from the predictor
79
- multi_stage_features = predictor.multi_scale_features
 
 
 
80
  ```
81
 
82
  ---
83
 
84
  ## 📊 Dataset Reading & Processing
85
 
86
- We provide [read_dataset_image.py](./hyperspectral_image_reader/read_dataset_image.py) to read and preprocess hyperspectral images from various datasets. The script utilizes the custom data pipeline class `LoadHyperspectralImage` from [configs/hypervision/hyperspectral_pipelines.py](./configs/hypervision/hyperspectral_pipelines.py) to normalize the images (scaled to `[0, 255]`) and reshape them into the format expected by HyperVision: `(H_ori, W_ori, C_hsi)`.
87
 
88
  ### Python API Usage:
 
89
  ```python
90
- from hyperspectral_image_reader.read_dataset_image import load_hypervision_matrix
91
 
92
  # Load and preprocess HSI matrix
93
  # dataset_name must be one of the supported dataset identifiers (e.g., 'harvard', 'arad_1k_31')
@@ -96,7 +114,9 @@ print("Processed shape (H, W, C):", img_matrix.shape)
96
  ```
97
 
98
  ### Command Line Interface:
 
99
  You can also run the script from the command line to load an HSI image and optionally save the processed matrix as a `.npy` file:
 
100
  ```bash
101
  python hyperspectral_image_reader/read_dataset_image.py --path /path/to/image.mat --dataset harvard --output output.npy
102
  ```
@@ -105,41 +125,41 @@ python hyperspectral_image_reader/read_dataset_image.py --path /path/to/image.ma
105
 
106
  ## 🗃️ Supported Datasets
107
 
108
- Below is a summary of the ground-based hyperspectral datasets supported by our pipeline, along with their respective keys to be used for the `--dataset` parameter:
109
-
110
- | Dataset Name | `--dataset` Key | # Bands | Wavelengths | # Images | Expected Extension | File Loader Detail / Required Accompanying Files |
111
- | :--- | :--- | :---: | :---: | :---: | :---: | :--- |
112
- | **50 Outdoor** | `fiftyoutdoor` | 33 | 400–720 nm | 50 | `.mat` | Custom Mat loader |
113
- | **Agricultural Plant** | `aphid` | 237 | 436–965 nm | 361 | `.npy` | NumPy array loader |
114
- | **ARAD1K16** | `arad_1k_16` | 16 | 400–1000 nm | 950 | `.mat` | HDF5 H5PY loader |
115
- | **ARAD1K31** | `arad_1k_31` | 31 | 400–700 nm | 949 | `.mat` | HDF5 H5PY loader |
116
- | **CAVE** | `cave` | 31 | 400–700 nm | 32 | `.mat` | Mat loader |
117
- | **DeepHS-NIR** | `deephsnir` | 252 | 950–1700 nm | 718 | `.bin` | ENVI binary (requires corresponding `.hdr` file in the same directory) |
118
- | **DeepHS-VIS** | `deephsvis` | 224 | 400–1000 nm | 3405 | `.bin` | ENVI binary (requires corresponding `.hdr` file in the same directory) |
119
- | **DeepHS-VISCOR** | `deephsviscor` | 249 | 400–1000 nm | 1566 | `.bin` | ENVI binary (requires corresponding `.hdr` file in the same directory) |
120
- | **Harvard** | `harvard` | 31 | 420–720 nm | 77 | `.mat` | Mat loader |
121
- | **HOT-2024-NIR** | `hotnir` | 25 | 665–960 nm | 477 | `.png` | PNG frame loader (requires corresponding false-color `.jpg` file) |
122
- | **HOT-2024-RedNIR** | `hotrednir` | 15 | 600–850 nm | 348 | `.png` | PNG frame loader (requires corresponding false-color `.jpg` file) |
123
- | **HOT-2024-VIS** | `hotvis` | 16 | 470–600 nm | 1070 | `.png` | PNG frame loader (requires corresponding false-color `.jpg` file) |
124
- | **HSI Drive v2.0** | `hsidrive20` | 25 | 600–975 nm | 752 | `.npy` | NumPy loader (requires corresponding pseudocolor `.png` file) |
125
- | **HSI Road** | `hsiroad` | 25 | 600–960 nm | 380 | `.tif` | TIF image loader |
126
- | **HSODBIT v2** | `hsodbitv2` | 200 | 400–1000 nm | 500 | `.mat` | HDF5 H5PY loader (requires corresponding color `.jpg` file) |
127
- | **HSSOD** | `hs_sod` | 81 | 380–720 nm | 60 | `.h5` | HDF5 H5PY loader (requires corresponding color `.jpg` file) |
128
- | **HyKo v2-NIR** | `hykov2nir` | 25 | 600–975 nm | 78 | `.mat` | Mat loader |
129
- | **HyKo v2-VIS** | `hykov2vis` | 16 | 470–630 nm | 163 | `.mat` | Mat loader |
130
- | **HyperBlood** | `hyperblood` | 128 | 377–1046 nm | 14 | `.mat` | Custom Mat loader |
131
- | **HyperDrive-VNIR** | `hyperdrivevnir` | 24 | 660–900 nm | 504 | `.npz` | NumPy archive (must contain `cube.npy` file inside) |
132
- | **HyperspectralCity v2** | `hyperspectralcityv2` | 128 | 450–950 nm | 1330 | `.hsd` | HSD raw data loader |
133
- | **ICVL** | `icvl` | 31 | 400–700 nm | 187 | `.h5` | HDF5 H5PY loader |
134
- | **LIB-HSI** | `libhsi` | 204 | 400–1000 nm | 393 | `.hdr` | ENVI header (requires corresponding raw binary data `.raw`/`.dat` file) |
135
- | **UM-EMM** | `umemm` | 33 | 400–720 nm | 3 | `.mat` | Mat loader |
136
- | **UM-LD 2015** | `umld2015` | 33 | 400–720 nm | 20 | `.mat` | Mat loader |
137
- | **UM-NS 2002** | `umns2002` | 31 | 410–710 nm | 8 | `.mat` | Mat loader |
138
- | **UM-NS 2004** | `umns2004` | 33 | 400–720 nm | 10 | `.mat` | Mat loader |
139
- | **UM-OS** | `umos` | 33 | 400–720 nm | 50 | `.mat` | Mat loader |
140
- | **UM-RI 2015** | `umri2015` | 33 | 400–720 nm | 33 | `.mat` | Mat loader |
141
- | **Virginia Tech Tree** | `virginia_tech_tree` | 420 | 400–1000 nm | 51 | `.hdr` | ENVI header (requires corresponding raw binary data file) |
142
- | **Apple Fire Blight** | `vnihdhiatlimafb` | 204 | 400–1000 nm | 420 | `.hdr` | ENVI header (requires corresponding raw binary data file) |
143
 
144
  ---
145
 
@@ -147,15 +167,29 @@ Below is a summary of the ground-based hyperspectral datasets supported by our p
147
 
148
  If you find our work or this project helpful, please consider citing our paper:
149
 
 
 
 
 
 
 
 
 
 
150
  ```bibtex
151
- @misc{fu2026hypervision,
152
- title={HyperVision: A Channel-Adaptive Ground-Based Hyperspectral Vision Pre-trained Backbone},
153
- author={Guanyiman Fu and Jingtao Li and Zihang Cheng and Zhuanfeng Li and Diqi Chen and Yan Xu and Xiangyu Liu and Fengchao Xiong and Jianfeng Lu and Chengrong Chen and Jun Zhou},
154
- year={2026},
155
- eprint={2605.17286},
156
- archivePrefix={arXiv},
157
- primaryClass={cs.CV},
158
- url={https://arxiv.org/abs/2605.17286},
159
  }
160
  ```
161
- *(Note: Citation details will be updated upon official publication)*
 
 
 
 
 
 
7
  - foundation-model
8
  - image-segmentation
9
  ---
10
+ # HyperVision: A Channel-Adaptive Ground-Based Hyperspectral Vision Pre-trained Backbone
11
 
12
+ *This is the official repository for the paper "HyperVision: A Channel-Adaptive Ground-Based Hyperspectral Vision Pre-trained Backbone".*
 
 
13
  https://arxiv.org/abs/2605.17286
14
 
15
+ and **Hyperspectral Images Reader with multiple dataset support.**
16
+
17
  **Code:** [https://github.com/lronkitty/HyperVision](https://github.com/lronkitty/HyperVision)
18
 
19
+ [![BMVC 2026](https://img.shields.io/badge/BMVC-2026-blue.svg)](https://bmvc2026.bmva.org/)
20
+ [![Paper Status](<https://img.shields.io/badge/Status-Accepted%20to%20BMVC%202026-success.svg>)](https://bmvc2026.bmva.org/)
21
+ [![arXiv](https://img.shields.io/badge/arXiv-2605.17286-b31b1b.svg)](https://arxiv.org/abs/2605.17286)
22
+ [![GitHub](https://img.shields.io/badge/GitHub-HyperVision-green.svg)](https://github.com/lronkitty/HyperVision)
23
+
24
+ > 📢 **Announcement**: Our paper **"HyperVision: A Channel-Adaptive Ground-Based Hyperspectral Vision Pre-trained Backbone"** has been officially accepted by **BMVC 2026**! 🎉
25
+
26
+ *Official PyTorch implementation for the BMVC 2026 paper:*
27
+ **"HyperVision: A Channel-Adaptive Ground-Based Hyperspectral Vision Pre-trained Backbone"**
28
+
29
  ---
30
 
31
+ ## 🚀 News
32
+
33
+ - **[31-Jul-2026]** 🎉 **HyperVision** has been officially accepted to **BMVC 2026**!
34
+ - **[02-Jul-2026]** 📦 Official codebase, model checkpoints, and inference pipelines released.
35
 
36
  ---
37
 
38
  ## 📖 Abstract
39
 
40
+ While hyperspectral imaging provides rich spatial-spectral information across hundreds of narrow wavelength bands for precise material identification, ground-based hyperspectral pre-trained backbones remain absent, constrained by varying spectral configurations across sensors, limited annotations and heterogeneous labeling schemes, and the limited scale and scene diversity of existing datasets. To address these challenges and enable universal perception, we propose HyperVision, the first ground-based hyperspectral pre-trained backbone. First, to handle varying spectral configurations, HyperVision adopts a channel-adaptive dynamic embedding mechanism to map heterogeneous inputs into a unified token space. Second, we develop an unsupervised representation learning framework. Specifically, to address limited annotations and heterogeneous labeling schemes, a multi-source pseudo-labeling method is introduced to fuse spatial structures from SAM2 and fine-grained spectral material information from HyperFree. Furthermore, to enrich scene diversity and compensate for limited dataset scale, a cross-modal knowledge distillation mechanism is utilized to transfer rich semantic representations from a pre-trained RGB vision model to our backbone. Pre-trained on a collection of 15k images from 26 diverse ground-based datasets, HyperVision demonstrates exceptional generalization. Requiring only efficient head-only adaptation without adjusting backbone parameters, it outperforms state-of-the-art task-specific methods across three downstream tasks under varying sensor configurations, yielding up to a 16.3% relative improvement in hyperspectral semantic segmentation $\mathrm{Acc}_{\mathrm{M}}$, a 2.1% relative gain in object tracking AUC, and a 35.5% reduction in salient object detection MAE.
41
+
42
+ ---
43
 
44
  ## 🛠️ Main Code (HyperVision)
45
 
46
+ The core architecture and model initialization utilities are located under the [HyperVision](./HyperVision) directory.
47
 
48
+ To initialize the model, you can use the model builder functions from [HyperVision/build_HyperVision.py](./HyperVision/build_HyperVision.py). We support different model configurations:
49
 
 
50
  - **HyperVision-B**: `build_HyperVision_b`
51
  - **HyperVision-L**: `build_HyperVision_l`
52
  - **HyperVision-H** (Default): `build_HyperVision_h`
53
 
54
  ### Python Example:
55
+
56
  ```python
57
  import torch
58
+ from HyperVision import build_HyperVision_h, build_HyperVision_l, build_HyperVision_b
59
+ from hyperspectral_image_reader import load_hypervision_matrix, LoadHyperspectralImage
 
60
 
61
  device = "cuda" if torch.cuda.is_available() else "cpu"
62
  image_size = 512
 
66
  checkpoint="path/to/hypervision_distilled_h.pth", # Optional checkpoint path (.pth weights)
67
  image_size=512, # Input resolution
68
  vit_patch_size=16, # Patch size of the backbone
69
+ encoder_global_attn_indexes=[15, 23, 31], # Layers using global attention
70
+ merge_indexs=[8, 32], # Layers doing patch merging
71
  class_number=-1 # Number of classes for mask decoder
72
  )
73
  model = model.to(device)
74
  model.eval()
75
 
76
+ # 2. Load and preprocess a hyperspectral image
 
 
 
77
  # Preprocessed shape: (H_ori, W_ori, C_hsi) with values in range [0, 255]
78
  img_matrix = load_hypervision_matrix("path/to/image.mat", dataset_name="harvard")
79
+ h_ori, w_ori = img_matrix.shape[:2]
80
 
81
+ # Convert to torch tensor with shape (1, C_hsi, H_ori, W_ori)
82
+ input_tensor = torch.as_tensor(img_matrix, device=device).permute(2, 0, 1).unsqueeze(0).float()
83
+
84
+ # 3. Automatically retrieve the wavelengths list from the dataset pipeline config
85
  loader = LoadHyperspectralImage(dataset_type="harvard")
86
  wavelengths = loader.wavelengths
87
 
88
+ # 4. Extract features via the HyperVision Image Encoder
 
89
  with torch.no_grad():
90
+ # GSD represents Ground Sampling Distance (defaults to [0.01])
91
+ multi_stage_features = model.image_encoder(
92
+ input_tensor,
93
+ test_mode=True,
94
+ input_wavelength=wavelengths,
95
+ GSD=[0.01]
96
+ )
97
  ```
98
 
99
  ---
100
 
101
  ## 📊 Dataset Reading & Processing
102
 
103
+ We provide [read_dataset_image.py](./hyperspectral_image_reader/read_dataset_image.py) to read and preprocess hyperspectral images from various datasets. The script utilizes the custom data pipeline class `LoadHyperspectralImage` from [hyperspectral_image_reader/hyperspectral_pipelines.py](./hyperspectral_image_reader/hyperspectral_pipelines.py) to normalize the images (scaled to `[0, 255]`) and reshape them into the format expected by HyperVision: `(H_ori, W_ori, C_hsi)`.
104
 
105
  ### Python API Usage:
106
+
107
  ```python
108
+ from hyperspectral_image_reader import load_hypervision_matrix
109
 
110
  # Load and preprocess HSI matrix
111
  # dataset_name must be one of the supported dataset identifiers (e.g., 'harvard', 'arad_1k_31')
 
114
  ```
115
 
116
  ### Command Line Interface:
117
+
118
  You can also run the script from the command line to load an HSI image and optionally save the processed matrix as a `.npy` file:
119
+
120
  ```bash
121
  python hyperspectral_image_reader/read_dataset_image.py --path /path/to/image.mat --dataset harvard --output output.npy
122
  ```
 
125
 
126
  ## 🗃️ Supported Datasets
127
 
128
+ Below is a summary of the 26 ground-based hyperspectral datasets (31 subset entries) supported by our pipeline, along with their respective keys for the `--dataset` parameter:
129
+
130
+ | Dataset Name | `--dataset` Key | # Bands | Wavelengths | # Images | Expected Extension | File Loader Detail / Required Accompanying Files |
131
+ | :----------------------------- | :---------------------- | :-----: | :----------: | :------: | :----------------: | :------------------------------------------------------------------------- |
132
+ | **50 Outdoor** | `fiftyoutdoor` | 33 | 400–720 nm | 50 | `.mat` | Custom Mat loader |
133
+ | **Agricultural Plant** | `aphid` | 237 | 436–965 nm | 361 | `.npy` | NumPy array loader |
134
+ | **ARAD1K16** | `arad_1k_16` | 16 | 400–1000 nm | 950 | `.mat` | HDF5 H5PY loader |
135
+ | **ARAD1K31** | `arad_1k_31` | 31 | 400–700 nm | 949 | `.mat` | HDF5 H5PY loader |
136
+ | **CAVE** | `cave` | 31 | 400–700 nm | 32 | `.mat` | Mat loader |
137
+ | **DeepHS-NIR** | `deephsnir` | 252 | 950–1700 nm | 718 | `.bin` | ENVI binary (requires corresponding`.hdr` file in the same directory) |
138
+ | **DeepHS-VIS** | `deephsvis` | 224 | 400–1000 nm | 3405 | `.bin` | ENVI binary (requires corresponding`.hdr` file in the same directory) |
139
+ | **DeepHS-VISCOR** | `deephsviscor` | 249 | 400–1000 nm | 1566 | `.bin` | ENVI binary (requires corresponding`.hdr` file in the same directory) |
140
+ | **Harvard** | `harvard` | 31 | 420–720 nm | 77 | `.mat` | Mat loader |
141
+ | **HOT-2024-NIR** | `hotnir` | 25 | 665–960 nm | 477 | `.png` | PNG frame loader (requires corresponding false-color`.jpg` file) |
142
+ | **HOT-2024-RedNIR** | `hotrednir` | 15 | 600–850 nm | 348 | `.png` | PNG frame loader (requires corresponding false-color`.jpg` file) |
143
+ | **HOT-2024-VIS** | `hotvis` | 16 | 470–600 nm | 1070 | `.png` | PNG frame loader (requires corresponding false-color`.jpg` file) |
144
+ | **HSI Drive v2.0** | `hsidrive20` | 25 | 600–975 nm | 752 | `.npy` | NumPy loader (requires corresponding pseudocolor`.png` file) |
145
+ | **HSI Road** | `hsiroad` | 25 | 600–960 nm | 380 | `.tif` | TIF image loader |
146
+ | **HSODBIT v2** | `hsodbitv2` | 200 | 400–1000 nm | 500 | `.mat` | HDF5 H5PY loader (requires corresponding color`.jpg` file) |
147
+ | **HSSOD** | `hs_sod` | 81 | 380–720 nm | 60 | `.h5` | HDF5 H5PY loader (requires corresponding color`.jpg` file) |
148
+ | **HyKo v2-NIR** | `hykov2nir` | 25 | 600–975 nm | 78 | `.mat` | Mat loader |
149
+ | **HyKo v2-VIS** | `hykov2vis` | 16 | 470–630 nm | 163 | `.mat` | Mat loader |
150
+ | **HyperBlood** | `hyperblood` | 128 | 377–1046 nm | 14 | `.mat` | Custom Mat loader |
151
+ | **HyperDrive-VNIR** | `hyperdrivevnir` | 24 | 660–900 nm | 504 | `.npz` | NumPy archive (must contain`cube.npy` file inside) |
152
+ | **HyperspectralCity v2** | `hyperspectralcityv2` | 128 | 450–950 nm | 1330 | `.hsd` | HSD raw data loader |
153
+ | **ICVL** | `icvl` | 31 | 400–700 nm | 187 | `.h5` | HDF5 H5PY loader |
154
+ | **LIB-HSI** | `libhsi` | 204 | 400–1000 nm | 393 | `.hdr` | ENVI header (requires corresponding raw binary data`.raw`/`.dat` file) |
155
+ | **UM-EMM** | `umemm` | 33 | 400–720 nm | 3 | `.mat` | Mat loader |
156
+ | **UM-LD 2015** | `umld2015` | 33 | 400–720 nm | 20 | `.mat` | Mat loader |
157
+ | **UM-NS 2002** | `umns2002` | 31 | 410–710 nm | 8 | `.mat` | Mat loader |
158
+ | **UM-NS 2004** | `umns2004` | 33 | 400–720 nm | 10 | `.mat` | Mat loader |
159
+ | **UM-OS** | `umos` | 33 | 400–720 nm | 50 | `.mat` | Mat loader |
160
+ | **UM-RI 2015** | `umri2015` | 33 | 400–720 nm | 33 | `.mat` | Mat loader |
161
+ | **Virginia Tech Tree** | `virginia_tech_tree` | 420 | 400–1000 nm | 51 | `.hdr` | ENVI header (requires corresponding raw binary data file) |
162
+ | **Apple Fire Blight** | `vnihdhiatlimafb` | 204 | 400–1000 nm | 420 | `.hdr` | ENVI header (requires corresponding raw binary data file) |
163
 
164
  ---
165
 
 
167
 
168
  If you find our work or this project helpful, please consider citing our paper:
169
 
170
+ <!-- ```bibtex
171
+ @inproceedings{fu2026hypervision,
172
+ title = {{HyperVision: A Channel-Adaptive Ground-Based Hyperspectral Vision Pre-trained Backbone}},
173
+ author = {Fu, Guanyiman and Li, Jingtao and Cheng, Zihang and Li, Zhuanfeng and Chen, Diqi and Xu, Yan and Liu, Xiangyu and Xiong, Fengchao and Lu, Jianfeng and Chen, Chengrong and Zhou, Jun},
174
+ booktitle = {Proc. British Mach. Vis. Conf. (BMVC)},
175
+ year = {2026}
176
+ }
177
+ ``` -->
178
+
179
  ```bibtex
180
+ @misc{fu2026hypervisionarxiv,
181
+ title = {{HyperVision: A Channel-Adaptive Ground-Based Hyperspectral Vision Pre-trained Backbone}},
182
+ author = {Guanyiman Fu and Jingtao Li and Zihang Cheng and Zhuanfeng Li and Diqi Chen and Yan Xu and Xiangyu Liu and Fengchao Xiong and Jianfeng Lu and Chengrong Chen and Jun Zhou},
183
+ year = {2026},
184
+ eprint = {2605.17286},
185
+ archivePrefix = {arXiv},
186
+ primaryClass = {cs.CV},
187
+ url = {https://arxiv.org/abs/2605.17286}
188
  }
189
  ```
190
+
191
+ <!-- ---
192
+
193
+ ## 📄 License
194
+
195
+ This project is licensed under the Apache 2.0 License. -->
hyperspectral_image_reader/__init__.py ADDED
@@ -0,0 +1,4 @@
 
 
 
 
 
1
+ from .hyperspectral_pipelines import LoadHyperspectralImage
2
+ from .read_dataset_image import load_hypervision_matrix
3
+
4
+ __all__ = ['LoadHyperspectralImage', 'load_hypervision_matrix']
hyperspectral_image_reader/hyperspectral_pipelines.py CHANGED
@@ -28,8 +28,16 @@ import rasterio
28
  import spectral.io.envi as envi
29
  import tifffile
30
  import random
31
- from configs.hypervision import ds_load
32
- from configs.hypervision.hsi3d import get_image_loader
 
 
 
 
 
 
 
 
33
  import cupy as cp
34
  from typing import Dict, Iterable, List, Optional, Sequence, Tuple, Union
35
  import logging
 
28
  import spectral.io.envi as envi
29
  import tifffile
30
  import random
31
+ try:
32
+ from hyperspectral_image_reader.utils import ds_load
33
+ from hyperspectral_image_reader.utils.hsi3d import get_image_loader
34
+ except ImportError:
35
+ try:
36
+ from .utils import ds_load
37
+ from .utils.hsi3d import get_image_loader
38
+ except (ImportError, ValueError):
39
+ from utils import ds_load
40
+ from utils.hsi3d import get_image_loader
41
  import cupy as cp
42
  from typing import Dict, Iterable, List, Optional, Sequence, Tuple, Union
43
  import logging
hyperspectral_image_reader/read_dataset_image.py CHANGED
@@ -12,10 +12,13 @@ if parent_dir not in sys.path:
12
  sys.path.append(parent_dir)
13
 
14
  try:
15
- from hyperspectral_pipelines import LoadHyperspectralImage
16
- except ImportError as e:
17
- print(f"Error: Could not import LoadHyperspectralImage. Detail: {e}")
18
- sys.exit(1)
 
 
 
19
 
20
  DATASET_EXTENSIONS = {
21
  # .mat / .h5 formats
 
12
  sys.path.append(parent_dir)
13
 
14
  try:
15
+ from hyperspectral_image_reader.hyperspectral_pipelines import LoadHyperspectralImage
16
+ except ImportError:
17
+ try:
18
+ from hyperspectral_pipelines import LoadHyperspectralImage
19
+ except ImportError as e:
20
+ print(f"Error: Could not import LoadHyperspectralImage. Detail: {e}")
21
+ sys.exit(1)
22
 
23
  DATASET_EXTENSIONS = {
24
  # .mat / .h5 formats
hyperspectral_image_reader/utils/__init__.py ADDED
@@ -0,0 +1,4 @@
 
 
 
 
 
1
+ from . import ds_load
2
+ from .hsi3d import get_image_loader
3
+
4
+ __all__ = ['ds_load', 'get_image_loader']
hyperspectral_image_reader/utils/ds_load.py ADDED
@@ -0,0 +1,229 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ '''
2
+ ************************************************************************
3
+ Copyright 2020 Institute of Theoretical and Applied Informatics,
4
+ Polish Academy of Sciences https://www.iitis.pl
5
+
6
+ Licensed under the Apache License, Version 2.0 (the "License");
7
+ you may not use this file except in compliance with the License.
8
+ You may obtain a copy of the License at
9
+
10
+ http://www.apache.org/licenses/LICENSE-2.0
11
+
12
+ Unless required by applicable law or agreed to in writing, software
13
+ distributed under the License is distributed on an "AS IS" BASIS,
14
+ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
15
+ See the License for the specific language governing permissions and
16
+ limitations under the License.
17
+ ************************************************************************
18
+
19
+ HSI blood classification dataset by M. Romaszewski, P.Glomb, M. Cholewa, A. Sochan
20
+ Institute of Theoretical and Applied Informatics, Polish Academy of Sciences (ITAI PAS) https://www.iitis.pl
21
+ Dataset DOI: 10.5281/zenodo.3984905
22
+
23
+ HyperBlood API
24
+ Basic loader for dataset files
25
+
26
+ Warning:
27
+ * By default, data is cleared by removing noisy bands and broken line in the image.
28
+ * Note that the 'F(2k)' image was captured with different camera. Its bands were interpolated
29
+ to match remaining images. However, due to spectral range differences between cameras, it has
30
+ less bands. After cleaning (default) all images have the same matching 113 bands.
31
+
32
+ NOISY_BANDS_INDICES = np.array([0,1,2,3,4,48,49,50,121,122,123,124,125,126,127])
33
+
34
+ @author: mromaszewski@iitis.pl
35
+ '''
36
+ import warnings
37
+ warnings.filterwarnings("ignore")
38
+ import unittest
39
+ import spectral.io.envi as envi
40
+ import numpy as np
41
+ import matplotlib.pyplot as plt
42
+ # from geotiff import GeoTiff
43
+ from osgeo import gdal
44
+ gdal.PushErrorHandler('CPLQuietErrorHandler')
45
+ # from osgeo import gdal_array
46
+
47
+ IMAGES = ['A(1)','B(1)','C(1)','D(1)','E(1)','E(7)','E(21)','F(1)','F(1a)','F(1s)','F(2)','F(2k)','F(7)','F(21)']
48
+
49
+ #change this to your DS location
50
+ # PATH_DATA = '../'
51
+
52
+ #------------------------ DATA LOADING ------------------------------------
53
+
54
+ def get_data(name,remove_bands=True,clean=True):
55
+ """
56
+ Returns HSI data from a datacube
57
+
58
+ Parameters:
59
+ ---------------------
60
+ name: name
61
+ remove_bands: if True, noisy bands are removed (leaving 113 bands)
62
+ clean: if True, remove damaged line
63
+
64
+ Returns:
65
+ -----------------------
66
+ data, wavelenghts as numpy arrays (float32)
67
+ """
68
+ # filename = "{}data/{}".format(PATH_DATA,name)
69
+ hsimage = gdal.Open(name)
70
+ hsimage = hsimage.ReadAsArray()
71
+ wavs = np.asarray([376.8200 , 381.7583 , 386.7018 , 391.6505 , 396.6044 , 401.5636 , 406.5280 , 411.4977 , 416.4725 , 421.4525 , 426.4379 , 431.4284 , 436.4241 , 441.4251 , 446.4313 , 451.4427 , 456.4594 , 461.4813 , 466.5084 , 471.5408 , 476.5783 , 481.6211 , 486.6691 , 491.7224 , 496.7808 , 501.8445 , 506.9134 , 511.9876 , 517.0670 , 522.1515 , 527.2413 , 532.3364 , 537.4367 , 542.5422 , 547.6529 , 552.7689 , 557.8901 , 563.0164 , 568.1481 , 573.2849 , 578.4270 , 583.5743 , 588.7269 , 593.8846 , 599.0476 , 604.2158 , 609.3892 , 614.5679 , 619.7518 , 624.9409 , 630.1353 , 635.3348 , 640.5396 , 645.7496 , 650.9649 , 656.1853 , 661.4111 , 666.6420 , 671.8782 , 677.1195 , 682.3661 , 687.6179 , 692.8750 , 698.1372 , 703.4047 , 708.6775 , 713.9554 , 719.2386 , 724.5271 , 729.8207 , 735.1196 , 740.4236 , 745.7329 , 751.0475 , 756.3672 , 761.6923 , 767.0225 , 772.3580 , 777.6987 , 783.0445 , 788.3956 , 793.7520 , 799.1135 , 804.4803 , 809.8524 , 815.2296 , 820.6121 , 825.9998 , 831.3927 , 836.7909 , 842.1942 , 847.6028 , 853.0167 , 858.4358 , 863.8600 , 869.2896 , 874.7243 , 880.1642 , 885.6095 , 891.0598 , 896.5155 , 901.9764 , 907.4425 , 912.9138 , 918.3903 , 923.8721 , 929.3591 , 934.8514 , 940.3488 , 945.8514 , 951.3594 , 956.8725 , 962.3909 , 967.9144 , 973.4432 , 978.9773 , 984.5165 , 990.0610 , 995.6107 , 1001.1656 , 1006.7258 , 1012.2913 , 1017.8618 , 1023.4377 , 1029.0188 , 1034.6050 , 1040.1965 , 1045.7932])
72
+ data = np.asarray(hsimage[:,:,:],dtype=np.float32).transpose(1,2,0) # (520, 696, 128)
73
+
74
+ #removal of damaged sensor line
75
+ fname = name.split('/')[-1].replace('.tif','')
76
+ if clean and fname!='F_2k':
77
+ data = np.delete(data,445,0)
78
+
79
+
80
+ if not remove_bands:
81
+ return data,wavs
82
+ return data[:,:,get_good_indices(fname)],wavs[get_good_indices(fname)]
83
+
84
+ def get_anno(name,remove_uncertain_blood=True,clean=True):
85
+ """
86
+ Returns annotation (GT) for data files as 2D int numpy array
87
+ Classes:
88
+ 0 - background
89
+ 1 - blood
90
+ 2 - ketchup
91
+ 3 - artificial blood
92
+ 4 - beetroot juice
93
+ 5 - poster paint
94
+ 6 - tomato concentrate
95
+ 7 - acrtylic paint
96
+ 8 - uncertain blood
97
+
98
+ Parameters:
99
+ ---------------------
100
+ name: name
101
+ clean: if True, remove damaged line
102
+ remove_uncertain_blood: if True, removes class 8
103
+
104
+ Returns:
105
+ -----------------------
106
+ annotation as numpy 2D array
107
+ """
108
+ name = convert_name(name)
109
+ filename = "{}anno/{}".format(PATH_DATA,name)
110
+ anno = np.load(filename+'.npz')['gt']
111
+ #removal of damaged sensor line
112
+ if clean and name!='F_2k':
113
+ anno = np.delete(anno,445,0)
114
+ #remove uncertain blood + technical classes
115
+ if remove_uncertain_blood:
116
+ anno[anno>7]=0
117
+ else:
118
+ anno[anno>8]=0
119
+
120
+ return anno
121
+
122
+
123
+ #------------------------ UTILITY ------------------------------------
124
+
125
+
126
+ def get_good_indices(name=None):
127
+ """
128
+ Returns indices of bands which are not noisy
129
+
130
+ Parameters:
131
+ ---------------------
132
+ name: name
133
+ Returns:
134
+ -----------------------
135
+ numpy array of good indices
136
+ """
137
+ name = convert_name(name)
138
+ if name!='F_2k':
139
+ indices = np.arange(128)
140
+ indices = indices[5:-7]
141
+ else:
142
+ indices = np.arange(116)
143
+ indices=np.delete(indices,[43,44,45])
144
+ return indices
145
+
146
+ def convert_name(name):
147
+ """
148
+ Ensures that the name is in the filename format
149
+ Parameters:
150
+ ---------------------
151
+ name: name
152
+
153
+ Returns:
154
+ -----------------------
155
+ cleaned name
156
+ """
157
+ name = name.replace('(','_')
158
+ name = name.replace(')','')
159
+ return name
160
+
161
+
162
+
163
+ def get_rgb(data,wavelengths,gamma=0.7,vnir_bands=[600, 550, 450]):
164
+ """
165
+ Treturns an (over)simplified RGB visualization of HSI data
166
+
167
+ Parameters:
168
+ ---------------------
169
+ data: data cube as nparray
170
+ annotation: wavelengths - band wavelenghts
171
+ gamma: gamma correction value
172
+ vnir_bands: bands used for RGB
173
+
174
+ Returns:
175
+ -----------------------
176
+ rgb image as numpy array
177
+ """
178
+ assert data.shape[2]==len(wavelengths)
179
+ max_data = np.max(data)
180
+ rgb_i = [np.argmin(np.abs(wavelengths - b)) for b in vnir_bands]
181
+ ret = data[:,:,rgb_i].copy()/max_data
182
+
183
+ if gamma!=1.0:
184
+ for i in range(3):
185
+ ret[:,:,i]=np.power(ret[:,:,i],gamma)
186
+
187
+ return ret
188
+
189
+ class LoadTest(unittest.TestCase):
190
+ def test_load(self):
191
+ """
192
+ test image loading
193
+ """
194
+ for name in IMAGES:
195
+ data,wavelengths = get_data(name,remove_bands=True)
196
+ anno = get_anno(name)
197
+ self.assertEqual(data.shape[2],113)
198
+ self.assertEqual(data.shape[2],wavelengths.shape[0])
199
+ rgb = get_rgb(data,wavelengths)
200
+ plt.subplot(1,2,1)
201
+ plt.imshow(rgb,interpolation='nearest')
202
+ plt.subplot(1,2,2)
203
+ plt.imshow(anno,interpolation='nearest')
204
+ plt.show()
205
+ plt.close()
206
+
207
+
208
+ def dis_test_indices(self):
209
+ '''
210
+ Ensure F_2k is loaded correctly
211
+ '''
212
+ _,wavs = get_data('F_2k',remove_bands=False)
213
+ assert 619.7518 in wavs
214
+ _,wavs = get_data('F_2k',remove_bands=True)
215
+ assert 619.7518 not in wavs
216
+ _,wavs2 = get_data('F_1',remove_bands=True)
217
+ assert np.sum(wavs-wavs2)==0
218
+
219
+ data,wavelengths = get_data('F_1',remove_bands=False)
220
+ self.assertEqual(data.shape[2],128)
221
+ self.assertEqual(data.shape[2],wavelengths.shape[0])
222
+ data,wavelengths = get_data('F_2k',remove_bands=False)
223
+ self.assertEqual(data.shape[2],116)
224
+ self.assertEqual(data.shape[2],wavelengths.shape[0])
225
+ anno = get_anno('F_1')
226
+
227
+
228
+ if __name__ == '__main__':
229
+ unittest.main()
hyperspectral_image_reader/utils/hsi3d.py ADDED
@@ -0,0 +1,112 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ import os
2
+ import os.path
3
+ import torch
4
+ import numpy as np
5
+ import pandas
6
+ from glob import glob
7
+ import random
8
+ from collections import OrderedDict
9
+ import cv2
10
+ import numpy as np
11
+
12
+ def X2Cube(img, cellSize=4):
13
+
14
+ B = [cellSize, cellSize]
15
+ skip = [cellSize, cellSize]
16
+ # Parameters
17
+ M, N = img.shape
18
+ col_extent = N - B[1] + 1
19
+ row_extent = M - B[0] + 1
20
+
21
+ # Get Starting block indices
22
+ start_idx = np.arange(B[0])[:, None] * N + np.arange(B[1])
23
+
24
+ # Generate Depth indeces
25
+ didx = M * N * np.arange(1)
26
+ start_idx = (didx[:, None] + start_idx.ravel()).reshape((-1, B[0], B[1]))
27
+
28
+ # Get offsetted indices across the height and width of input array
29
+ offset_idx = np.arange(row_extent)[:, None] * N + np.arange(col_extent)
30
+
31
+ # Get all actual indices & index into input array for final output
32
+ out = np.take(img, start_idx.ravel()[:, None] + offset_idx[::skip[0], ::skip[1]].ravel())
33
+ out = np.transpose(out)
34
+ DataCube = out.reshape(M//cellSize, N//cellSize, cellSize*cellSize)
35
+
36
+ hsi_min = np.min(DataCube, axis=(0,1), keepdims=True)
37
+ hsi_max = np.max(DataCube, axis=(0,1), keepdims=True)
38
+ hsi_normed = (DataCube - hsi_min) / ((hsi_max - hsi_min) + 1e-6) * 255
39
+ hsi_normed = hsi_normed.astype(np.uint8)
40
+ return hsi_normed
41
+
42
+
43
+ def X2Cube_fast(img, cellSize=4):
44
+ M, N = img.shape
45
+ if (M % cellSize) != 0 or (N % cellSize) != 0:
46
+ raise ValueError(f"Image shape {(M, N)} must be divisible by cellSize {cellSize}")
47
+
48
+ cubes = (
49
+ img.reshape(M // cellSize, cellSize, N // cellSize, cellSize)
50
+ .swapaxes(1, 2)
51
+ .reshape(M // cellSize, N // cellSize, cellSize * cellSize)
52
+ )
53
+
54
+ hsi_min = np.min(cubes, axis=(0, 1), keepdims=True)
55
+ hsi_max = np.max(cubes, axis=(0, 1), keepdims=True)
56
+ hsi_normed = (cubes - hsi_min) / ((hsi_max - hsi_min) + 1e-6) * 255
57
+ return hsi_normed.astype(np.uint8)
58
+
59
+
60
+ def get_image_loader(img_file: str, cellSize=-1) -> np.array:
61
+ if cellSize > 0:
62
+ img = cv2.imread(img_file, cv2.IMREAD_ANYCOLOR | cv2.IMREAD_ANYDEPTH)
63
+ img = X2Cube(img, cellSize)
64
+ elif cellSize == -1:
65
+ img = cv2.imread(img_file, cv2.IMREAD_COLOR)
66
+ else:
67
+ raise Exception
68
+ return img
69
+
70
+
71
+ def _get_frames(self, seq_id, frame_ids):
72
+ frame_list = []
73
+ for frame_id in frame_ids:
74
+ frame_name = '%04d.png' % (frame_id+1)
75
+ image_path = os.path.join(self.RGBHDataRootDir, self.sequence_list[seq_id], 'img', frame_name)
76
+ if self.cur_modalName == 'NIR':
77
+ hsi_image = self.get_image_loader(image_path, cellSize=5)
78
+ elif self.cur_modalName == 'VIS':
79
+ hsi_image = self.get_image_loader(image_path, cellSize=4)
80
+ elif self.cur_modalName == 'RedNIR':
81
+ hsi_image = self.get_image_loader(image_path, cellSize=4)
82
+ hsi_image = hsi_image[:,:,:-1]
83
+
84
+ frame_name = '%04d.jpg' % (frame_id+1)
85
+
86
+ image_path = os.path.join(self.RGBHDataRootDir.replace('HSI-'+self.cur_modalName, 'HSI-'+self.cur_modalName+'-FalseColor'), self.sequence_list[seq_id], 'img', frame_name)
87
+ hsi_fc_image = self.get_image_loader(image_path, cellSize=-1)
88
+ frame_list.append(np.concatenate((hsi_fc_image, hsi_image), axis=2))
89
+
90
+ return frame_list
91
+
92
+
93
+ def get_frames(self, seq_id, frame_ids, anno=None):
94
+ frame_list = self._get_frames(seq_id, frame_ids)
95
+
96
+ if anno is None:
97
+ anno = self.get_sequence_info(seq_id)
98
+
99
+ anno_frames = {}
100
+ for key, value in anno.items():
101
+ if key == 'seq_belong_mask':
102
+ continue
103
+ anno_frames[key] = [value[f_id, ...].clone() for f_id in frame_ids]
104
+
105
+ object_meta = OrderedDict({'object_class_name': None,
106
+ 'motion_class': None,
107
+ 'major_class': None,
108
+ 'root_class': None,
109
+ 'motion_adverb': None})
110
+
111
+ return frame_list, anno_frames, object_meta
112
+