Update hyperspectral image reader imports, add utils, and update README for Hugging Face
Browse files- README.md +105 -71
- hyperspectral_image_reader/__init__.py +4 -0
- hyperspectral_image_reader/hyperspectral_pipelines.py +10 -2
- hyperspectral_image_reader/read_dataset_image.py +7 -4
- hyperspectral_image_reader/utils/__init__.py +4 -0
- hyperspectral_image_reader/utils/ds_load.py +229 -0
- hyperspectral_image_reader/utils/hsi3d.py +112 -0
README.md
CHANGED
|
@@ -7,42 +7,56 @@ tags:
|
|
| 7 |
- foundation-model
|
| 8 |
- image-segmentation
|
| 9 |
---
|
|
|
|
| 10 |
|
| 11 |
-
|
| 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 |
-
🚀
|
| 21 |
-
|
|
|
|
|
|
|
| 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,
|
|
|
|
|
|
|
| 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).
|
| 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,
|
| 44 |
-
from hyperspectral_image_reader
|
| 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
|
| 56 |
-
merge_indexs=[8, 32], # Layers doing patch merging
|
| 57 |
class_number=-1 # Number of classes for mask decoder
|
| 58 |
)
|
| 59 |
model = model.to(device)
|
| 60 |
model.eval()
|
| 61 |
|
| 62 |
-
# 2.
|
| 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 |
-
#
|
|
|
|
|
|
|
|
|
|
| 70 |
loader = LoadHyperspectralImage(dataset_type="harvard")
|
| 71 |
wavelengths = loader.wavelengths
|
| 72 |
|
| 73 |
-
#
|
| 74 |
-
# GSD represents Ground Sampling Distance (defaults to 0.01)
|
| 75 |
with torch.no_grad():
|
| 76 |
-
|
| 77 |
-
|
| 78 |
-
|
| 79 |
-
|
|
|
|
|
|
|
|
|
|
| 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 [
|
| 87 |
|
| 88 |
### Python API Usage:
|
|
|
|
| 89 |
```python
|
| 90 |
-
from hyperspectral_image_reader
|
| 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
|
| 109 |
-
|
| 110 |
-
| Dataset Name
|
| 111 |
-
| :--- | :--- | :---: | :---: | :---: | :---: | :--- |
|
| 112 |
-
| **50 Outdoor**
|
| 113 |
-
| **Agricultural Plant**
|
| 114 |
-
| **ARAD1K16**
|
| 115 |
-
| **ARAD1K31**
|
| 116 |
-
| **CAVE**
|
| 117 |
-
| **DeepHS-NIR**
|
| 118 |
-
| **DeepHS-VIS**
|
| 119 |
-
| **DeepHS-VISCOR**
|
| 120 |
-
| **Harvard**
|
| 121 |
-
| **HOT-2024-NIR**
|
| 122 |
-
| **HOT-2024-RedNIR**
|
| 123 |
-
| **HOT-2024-VIS**
|
| 124 |
-
| **HSI Drive v2.0**
|
| 125 |
-
| **HSI Road**
|
| 126 |
-
| **HSODBIT v2**
|
| 127 |
-
| **HSSOD**
|
| 128 |
-
| **HyKo v2-NIR**
|
| 129 |
-
| **HyKo v2-VIS**
|
| 130 |
-
| **HyperBlood**
|
| 131 |
-
| **HyperDrive-VNIR**
|
| 132 |
-
| **HyperspectralCity v2** | `hyperspectralcityv2` |
|
| 133 |
-
| **ICVL**
|
| 134 |
-
| **LIB-HSI**
|
| 135 |
-
| **UM-EMM**
|
| 136 |
-
| **UM-LD 2015**
|
| 137 |
-
| **UM-NS 2002**
|
| 138 |
-
| **UM-NS 2004**
|
| 139 |
-
| **UM-OS**
|
| 140 |
-
| **UM-RI 2015**
|
| 141 |
-
| **Virginia Tech Tree**
|
| 142 |
-
| **Apple Fire Blight**
|
| 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{
|
| 152 |
-
|
| 153 |
-
|
| 154 |
-
|
| 155 |
-
|
| 156 |
-
|
| 157 |
-
|
| 158 |
-
|
| 159 |
}
|
| 160 |
```
|
| 161 |
-
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 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 |
+
[](https://bmvc2026.bmva.org/)
|
| 20 |
+
[](https://bmvc2026.bmva.org/)
|
| 21 |
+
[](https://arxiv.org/abs/2605.17286)
|
| 22 |
+
[](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 |
-
|
| 32 |
-
from
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 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
|
| 17 |
-
|
| 18 |
-
|
|
|
|
|
|
|
|
|
|
| 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 |
+
|