forked from JavierLopatin/Python-Remote-Sensing-Scripts
-
Notifications
You must be signed in to change notification settings - Fork 1
Expand file tree
/
Copy pathMNF.py
More file actions
executable file
·177 lines (160 loc) · 6.13 KB
/
MNF.py
File metadata and controls
executable file
·177 lines (160 loc) · 6.13 KB
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
#! /usr/bin/env python
########################################################################################################################
#
# MNF.py
# A python script to perform MNF transformation to remote sesning data.
#
# Info: The script perform MNF transformation to all raster images stored in a folder.
#
# Author: Javier Lopatin
# Email: javierlopatin@gmail.com
# Date: 09/08/2016
# Version: 1.0
#
# Usage:
#
# python MNF.py -i <Imput raster>
# -c <Number of components [default = inputRaster bands]>
# -p <Preprocessing: Brightness Normalization of Hyperspectral data [Optional]>
# -s <Apply Savitzky Golay filtering [Optional]>
#
# # --preprop [-p]: Brightness Normalization presented in Feilhauer et al., 2010
#
# # examples:
# # Get the regular MNF transformation
# python MNF.py -i raster
#
# # with Brightness Normalization
# python MNF_cmd.py -i raster -p
#
#
#
# Bibliography:
#
# Feilhauer, H., Asner, G. P., Martin, R. E., Schmidtlein, S. (2010): Brightness-normalized Partial Least Squares
# Regression for hyperspectral data. Journal of Quantitative Spectroscopy and Radiative Transfer 111(12-13),
# pp. 1947–1957. 10.1016/j.jqsrt.2010.03.007
#
# C-I Change and Q Du. 1999. Interference and Noise-Adjusted Principal Components Analysis.
# IEEE TGRS, Vol 36, No 5.
#
########################################################################################################################
from __future__ import division
import argparse
import numpy as np
import pandas as pd
from sklearn.decomposition import PCA
from sklearn.base import BaseEstimator, TransformerMixin
try:
import rasterio
except ImportError:
print("ERROR: Could not import Rasterio Python library.")
print("Check if Rasterio is installed.")
try:
import pysptools.noise as ns
except ImportError:
print("ERROR: Could not import Pysptools Python library.")
print("Check if Pysptools is installed.")
################
### Functions
################
class MNF(BaseEstimator, TransformerMixin):
"""
Apply a MNF transform to the image
'img' must have (raw, column, band) shape
"""
def __init__(self, n_components=1, BrightnessNormalization=False):
self.n_components = n_components
self.BrightnessNormalization = BrightnessNormalization
def fit(self, X, y=None):
return self # nothing else to do
def transform(self, X, y=None):
X = X.astype('float32')
# apply brightness normalization
# if raster
if self.BrightnessNormalization==True:
def norm(r):
norm = r / np.sqrt( np.sum((r**2), 0) )
return norm
if len(X.shape) == 3:
X = np.apply_along_axis(norm, 2, X)
# if 2D array
if len(X.shape) == 2:
X = np.apply_along_axis(norm, 0, X)
w = ns.Whiten()
wdata = w.apply(X)
numBands = X.shape[2]
h, w, numBands = wdata.shape
X = np.reshape(wdata, (w*h, numBands))
pca = PCA()
mnf = pca.fit_transform(X)
mnf = np.reshape(mnf, (h, w, numBands))
mnf = mnf[:,:,:self.n_components]
var = np.cumsum(np.round(pca.explained_variance_ratio_, decimals=4)*100)
return mnf, var
def saveMNF(img, inputRaster):
# Save TIF image to a nre directory of name MNF
img2 = np.transpose(img, [2,0,1]) # get to (band, raw, column) shape
output = outMNF
if args["preprop"]==True:
output = output[:-4] + "_BN.tif"
new_dataset = rasterio.open(output , 'w', driver='GTiff',
height=inputRaster.shape[0], width=inputRaster.shape[1],
count=img.shape[2], dtype=str(img.dtype),
crs=inputRaster.crs, transform=inputRaster.transform)
new_dataset.write(img2)
new_dataset.close()
### Run process
if __name__ == "__main__":
# create the arguments for the algorithm
parser = argparse.ArgumentParser()
parser.add_argument('-i','--inputRaster',
help='Input raster', type=str, required=True)
parser.add_argument('-c','--components',
help='Number of components.', type=int, default=False)
parser.add_argument('-p','--preprop',
help='Preprocessing: Brightness Normalization of Hyperspectral data [Optional].',
action="store_true", default=False)
parser.add_argument('-s','--SavitzkyGolay',
help='Apply Savitzky Golay filtering [Optional].', action="store_true", default=False)
parser.add_argument('--version', action='version', version='%(prog)s 1.0')
args = vars(parser.parse_args())
# data imputps/outputs
inRaster = args["inputRaster"]
outMNF = inRaster[:-4] + "_MNF.tif"
# load raster
r = rasterio.open(inRaster)
r2 = r.read() # transform to array
# set number of components to retrive
if args["components"] is not None:
n_components = args['components']
else:
n_components = r2.shape[0]
img = np.transpose(r2, [1,2,0]) # get to (raw, column, band) shape
#############
### Apply MNF
# Apply Brightness Normalization if the option -p is added
if args["preprop"]==True:
print("Creating MNF components of " + inRaster)
model = MNF(n_components=n_components, BrightnessNormalization=True)
mnf, var = model.fit_transform(img)
print("The accumulative explained variance per component is:")
print(var)
# otherwie
else:
print("Creating MNF components of " + inRaster)
model = MNF(n_components=n_components)
mnf, var = model.fit_transform(img)
print("The accumulative explained variance per component is:")
print(var)
# save the MNF image and explained variance
saveMNF(mnf, r)
bandNames = []
for i in range(mnf.shape[2]):
a = "MNF" + str(i+1)
bandNames.append(a)
bandNames = pd.DataFrame(bandNames)
variance = pd.DataFrame(var)
txtOut = pd.concat([bandNames, variance], axis=1)
txtOut.columns=["Bands", "AccVariance"]
txtOut.to_csv(outMNF[:-4] + ".csv", index=False, header=True, na_rep='NA')