高光谱图像集样本的图像信息与光谱信息于一身。图像信息可以反映样本的大小、形状、缺陷等外部品质特征,由于不同成分对光谱吸收也不同,在某个特定波长下图像对某个缺陷会有较显著的反映,而光谱信息能充分反映样品内部的物理结构、化学成分的差异。
高光谱图像数据
Hyperspectral Remote Sensing Scenes - Grupo de Inteligencia Computacional (GIC) (ehu.eus)(点击下方阅读原文可跳转)
包含Indian Pines、Salinas、Pavia Centre and University、Cuprite、 Kennedy Space Center、Botswana、anomaly detection(7个地区),在每个地区都会有介绍类型数量、波段数,可直接下载。
高光谱图像数据的读取
1.Matlab读取.Mat格式的高光谱遥感数据:
load('Botswana.mat')
InputMatImg=Botswana;
b = size(InputMatImg);
fprintf('输入图像宽度为 %d\n',b(1));
fprintf('输入图像高度为 %d\n',b(2));
fprintf('输入图像波段数为 %d\n',b(3));
i=120;j=180;k=220;%自选三个波段
InputImg_r= InputMatImg(:,:,i);%获取第i个波段
InputImg_g= InputMatImg(:,:,j);%获取第j个波段
InputImg_b= InputMatImg(:,:,k);%获取第k个波段
InputImg_r= uint8(InputImg_r);%将i波段的灰度值转为0~255
InputImg_g= uint8(InputImg_g);%将j波段的灰度值转为0~255
InputImg_b= uint8(InputImg_b);%将k波段的灰度值转为0~255
RGBImg=cat(3,InputImg_r,InputImg_g,InputImg_b);%将i、j、k三个波段进行合成
figure;
subplot(221);imshow(InputImg_r);title('红色波段');
subplot(222);imshow(InputImg_g);title('绿色波段');
subplot(223);imshow(InputImg_b);title('蓝色波段');
subplot(224);imshow(RGBImg);title('合成波段');
imwrite(InputImg_r,['MATBand',num2str(i),'.jpg']);
imwrite(InputImg_g,['MATBand',num2str(j),'.jpg']);
imwrite(InputImg_b,['MATBand',num2str(k),'.jpg']);
imwrite(RGBImg,'compositeMATRGBimg.jpg');
2.python读取.Mat格式的高光谱遥感数据:
def testLoadmat(is_plot):
"""
作用:
loadmat()函数用于加载.mat数据文件
参数:
file_name: str
.mat文件的文件名(当appendmat = True时,不用加.mat后缀),也可以打开类似文件的对象(file-like object)
mdict: dict, 可选,插入文件变量的字典
appendmat: bool , 可选,若为真则在文件名后添加后缀
byte_order: str / None, 可选
默认为none。表示从mat文件中猜测的字节顺序,可以是“native”,“=”,“little”,“<”,“>”,“BIG”之一。
mat_dtype: bool,可选
若为真,则返回与加载到MATLAB中相同的dtype的数组,而不是保存数组的dtype。
squeeze_me: bool,可选,判断是否压缩单位矩阵的维数
chars_as_string: bool,可选,将char数组转string数组
matlab_compatible: bool,可选
返回被MATLAB读取的数组(当squeeze_me = false, chars_as_string = false, mat_dtype = true, struct_as_record = true)
struct_as_record: bool,可选
设置flag来判断加载MATLAB以numpy记录数组还是以原形式numpy数组(dtype为对象)。
verify_compressed_data_integrity: bool,可选,MATLAB文件的长度是否已确认。
variable_names: None / sequence
若为None(默认),则读取文件中的所有变量。否则variable_names将是一个string序列,表示需要从mat文件中读取的变量名。此时读取器将跳过不是这个变量名的序列,一定程度上减少读取时间。
返回:
mat_dict: dict
以变量名为key,数组为values的字典
# 加载数据
X = sio.loadmat('data/Botswana/Botswana.mat')
print(X)
此时输出的X是一个字典:
{'__header__': b'MATLAB 5.0 MAT-file,
Platform: GLNXA64,
Created on: Thu Feb 20 15:12:42 2014',
'__version__': '1.0',
'__globals__': [],
'Botswana': array([[[3996, 3952, 3698, ..., 60, 53, 54],
[8471, 8255, 8288, ..., 0, 0, 0]]],
dtype=uint16)}
若要获取其中的数据部分Botswana,则可直接输出X[‘Botswana’],
Python代码如下:
import scipy.io as sio
# 加载高光谱数据和其对应的标签
X = sio.loadmat('data/Botswana/Botswana.mat')['Botswana'] # 取字典中该项的值
y = sio.loadmat('data/Botswana/Botswana_gt.mat')['Botswana_gt']
# 打印高光谱数据及其标签的形状
print('Hyperspectral data shape: ', X.shape) # (1476, 256, 145)
print('Label shape: ', y.shape) # (1476, 256)
高光谱图像数据的预处理及使用(python)
在统计学中,主成分分析PCA是一种简化数据集的技术。它是一个线性变换。这个变换把数据变换到一个新的坐标系统中,使得任何数据投影的第一大方差在第一个坐标(称为第一主成分)上,第二大方差在第二个坐标(第二主成分)上,依次类推。主成分分析经常用于减少数据集的维数,同时保持数据集的对方差贡献最大的特征。这是通过保留低阶主成分,忽略高阶主成分做到的。这样低阶成分往往能够保留住数据的最重要方面。
关于 PCA降维的数学原理可参考这篇文章,非常详细。【机器学习】降维——PCA(非常详细) - 知乎 (zhihu.com)(点击下方阅读原文可跳转)
代码如下:
def applyPCA(X, numComponents):
newX = np.reshape(X, (-1, X.shape[2])) # 行数未知,列数为145 将高光谱数据展开成一维数据序列(377856, 145)
# print(newX.shape)
pca = PCA(n_components=numComponents, whiten=True)
newX = pca.fit_transform(newX)
newX = np.reshape(newX, (X.shape[0], X.shape[1], numComponents)) # 将降维后的数据按照指定的形状排列
return newX
# 对高光谱数据 X 应用 PCA 变换
X_pca = applyPCA(X, numComponents=pca_components)
print('Data shape after PCA: ', X_pca.shape)
PCA方法:fit_transform(X)
对部分数据先拟合fit,找到该part的整体指标,如均值、方差、最大值最小值等等,然后对该X进行转换transform,从而实现数据的标准化、归一化等等。
用X来训练PCA模型,同时返回降维后的数据。
newX=pca.fit_transform(X),newX就是降维后的数据。
# 对单个像素周围提取 patch 时,边缘像素就无法取了,因此,给这部分像素进行 padding 操作
def padWithZeros(X, margin=2):
newX = np.zeros(
(X.shape[0] + 2 * margin, X.shape[1] + 2 * margin, X.shape[2]))
x_offset = margin
y_offset = margin
newX[x_offset:X.shape[0] + x_offset, y_offset:X.shape[1] + y_offset, :] = X
return newX
# 在每个像素周围提取 patch ,然后创建成符合 keras 处理的格式
def createImageCubes(X, y, windowSize=5, removeZeroLabels=True):
# 给 X 做 padding
margin = int((windowSize - 1) / 2)
zeroPaddedX = padWithZeros(X, margin=margin)
# split patches
patchesData = np.zeros(
(X.shape[0] * X.shape[1], windowSize, windowSize, X.shape[2]))
patchesLabels = np.zeros((X.shape[0] * X.shape[1]))
patchIndex = 0
for r in range(margin, zeroPaddedX.shape[0] - margin):
for c in range(margin, zeroPaddedX.shape[1] - margin):
patch = zeroPaddedX[r - margin:r + margin + 1,
c - margin:c + margin + 1]
patchesData[patchIndex, :, :, :] = patch
patchesLabels[patchIndex] = y[r - margin, c - margin]
patchIndex = patchIndex + 1
if removeZeroLabels:
patchesData = patchesData[patchesLabels > 0, :, :, :]
patchesLabels = patchesLabels[patchesLabels > 0]
patchesLabels -= 1
return patchesData, patchesLabels
# 在每个像素周围提取 patch ,然后创建成符合 keras 处理的格式
X_pca, y = createImageCubes(X_pca, y, windowSize=patch_size)
print('Data cube X shape: ', X_pca.shape)
print('Data cube y shape: ', y.shape
创建训练数据和测试数据集:
def splitTrainTestSet(X, y, testRatio, randomState=345):
# 能够将数据集按照用户的需要指定划分为训练集和测试集
# test_size 若在0~1之间,为测试集样本数目与原始样本数目之比;若为整数,则是测试集样本的数目
X_train, X_test, y_train, y_test = train_test_split(X, y, test_size=testRatio, random_state=randomState, stratify=y)
return X_train, X_test, y_train, y_test
Xtrain, Xtest, ytrain, ytest = splitTrainTestSet(X_pca, y, test_ratio)
print('Xtrain shape: ', Xtrain.shape)
print('Xtest shape: ', Xtest.shape)
training: 75个数据,其中60个属于A类,15个属于B类。
testing: 25个数据,其中20个属于A类,5个属于B类。
改变 Xtrain, Ytrain 的形状,以符合 keras 的要求:
Xtrain = Xtrain.reshape(-1, patch_size, patch_size, pca_components, 1)# (974, 15, 15, 30, 1)
Xtest = Xtest.reshape(-1, patch_size, patch_size, pca_components, 1)
print('before transpose: Xtrain shape: ', Xtrain.shape)
print('before transpose: Xtest shape: ', Xtest.shape)
添加了一个维数,即通道数。
为了适应 pytorch 结构,数据要做转置:
# 一个数的下标为[z, y, x]
Xtrain = Xtrain.transpose(0, 4, 3, 1, 2) # 对数据做转置
Xtest = Xtest.transpose(0, 4, 3, 1, 2)
print('after transpose: Xtrain shape: ', Xtrain.shape) # (162, 1, 30, 15, 15)
print('after transpose: Xtest shape: ', Xtest.shape)
因为输入数据后,将首先通过三维卷积层,所以要将数据调整为三维卷积层可以接受的形式,
即输入层现在的维度是(Channel×Depth1×Height1×Width1)。
对Xtrain、Xtest数据做调整:Channel-1、Depth1-30、Height1-15、Width1-15
制作数据集:
""" Training dataset"""
class TrainDS(torch.utils.data.Dataset):
def __init__(self):
self.len = Xtrain.shape[0]
self.x_data = torch.FloatTensor(Xtrain)
self.y_data = torch.LongTensor(ytrain)
def __getitem__(self, index):
# 根据索引返回数据和对应的标签
return self.x_data[index], self.y_data[index]
def __len__(self):
# 返回文件数据的数目
return self.len
""" Testing dataset"""
class TestDS(torch.utils.data.Dataset):
def __init__(self):
self.len = Xtest.shape[0]
self.x_data = torch.FloatTensor(Xtest)
self.y_data = torch.LongTensor(ytest)
def __getitem__(self, index):
# 根据索引返回数据和对应的标签
return self.x_data[index], self.y_data[index]
def __len__(self):
# 返回文件数据的数目
return self.len
# 创建 trainloader 和 testloader
trainset = TrainDS()
testset = TestDS()
train_loader = torch.utils.data.DataLoader(dataset=trainset,
batch_size=batch_size, # 256
shuffle=True,
num_workers=0)
test_loader = torch.utils.data.DataLoader(dataset=testset,
batch_size=batch_size,
shuffle=False,
num_workers=0)
# 该接口主要用来将自定义的数据读取接口的输出或者PyTorch已有的数据读取接口的输入按照batch size封装成Tensor,后续只需要再包装成Variable即可作为模型的输入,因此该接口有点承上启下的作用,
# dataset (Dataset) – 加载数据的数据集。
# batch_size (int, optional) – 每个batch加载多少个样本(默认: 1)。
# shuffle (bool, optional) – 设置为True时会在每个epoch重新打乱数据(默认: False).
# sampler (Sampler, optional) – 定义从数据集中提取样本的策略,即生成index的方式,可以顺序也可以乱序
# num_workers (int, optional) – 用多少个子进程加载数据。0表示数据将在主进程中加载(默认: 0)
# collate_fn (callable, optional) –将一个batch的数据和标签进行合并操作。
# pin_memory (bool, optional) –设置pin_memory=True,则意味着生成的Tensor数据最开始是属于内存中的锁页内存,这样将内存的Tensor转义到GPU的显存就会更快一些。
# drop_last (bool, optional) – 如果数据集大小不能被batch size整除,则设置为True后可删除最后一个不完整的batch。如果设为False并且数据集的大小不能被batch size整除,则最后一个batch将更小。(默认: False)
# timeout,是用来设置数据读取的超时时间的,但超过这个时间还没读取到数据的话就会报错。