|
|
@ -26,7 +26,11 @@ import skimage.transform as transform
|
|
|
|
import sklearn.decomposition as decomposition
|
|
|
|
import sklearn.decomposition as decomposition
|
|
|
|
import skimage.measure as measure
|
|
|
|
import skimage.measure as measure
|
|
|
|
import skimage.morphology as morphology
|
|
|
|
import skimage.morphology as morphology
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
# machine learning modules
|
|
|
|
from sklearn.cluster import KMeans
|
|
|
|
from sklearn.cluster import KMeans
|
|
|
|
|
|
|
|
from sklearn.neural_network import MLPClassifier
|
|
|
|
|
|
|
|
from sklearn.externals import joblib
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
# import own modules
|
|
|
|
# import own modules
|
|
|
@ -397,10 +401,6 @@ def pansharpen(im_ms, im_pan, cloud_mask, plot_bool):
|
|
|
|
vec_pcs[:,0] = hist_match(vec_pan, vec_pcs[:,0])
|
|
|
|
vec_pcs[:,0] = hist_match(vec_pan, vec_pcs[:,0])
|
|
|
|
vec_ms_ps = pca.inverse_transform(vec_pcs)
|
|
|
|
vec_ms_ps = pca.inverse_transform(vec_pcs)
|
|
|
|
|
|
|
|
|
|
|
|
# normalise between 0 and 1
|
|
|
|
|
|
|
|
for i in range(vec_pcs.shape[1]):
|
|
|
|
|
|
|
|
vec_ms_ps[:,i] = np.divide(vec_ms_ps[:,i] - np.min(vec_ms_ps[:,i]),
|
|
|
|
|
|
|
|
np.max(vec_ms_ps[:,i]) - np.min(vec_ms_ps[:,i]))
|
|
|
|
|
|
|
|
# reshape vector into image
|
|
|
|
# reshape vector into image
|
|
|
|
vec_ms_ps_full = np.ones((len(vec_mask), im_ms.shape[2])) * np.nan
|
|
|
|
vec_ms_ps_full = np.ones((len(vec_mask), im_ms.shape[2])) * np.nan
|
|
|
|
vec_ms_ps_full[~vec_mask,:] = vec_ms_ps
|
|
|
|
vec_ms_ps_full[~vec_mask,:] = vec_ms_ps
|
|
|
@ -409,11 +409,11 @@ def pansharpen(im_ms, im_pan, cloud_mask, plot_bool):
|
|
|
|
if plot_bool:
|
|
|
|
if plot_bool:
|
|
|
|
plt.figure()
|
|
|
|
plt.figure()
|
|
|
|
ax1 = plt.subplot(121)
|
|
|
|
ax1 = plt.subplot(121)
|
|
|
|
plt.imshow(im_ms[:,:,[2,1,0]])
|
|
|
|
plt.imshow(rescale_image_intensity(im_ms[:,:,[2,1,0]], cloud_mask, 100, False))
|
|
|
|
plt.axis('off')
|
|
|
|
plt.axis('off')
|
|
|
|
plt.title('Original')
|
|
|
|
plt.title('Original')
|
|
|
|
ax2 = plt.subplot(122, sharex=ax1, sharey=ax1)
|
|
|
|
ax2 = plt.subplot(122, sharex=ax1, sharey=ax1)
|
|
|
|
plt.imshow(im_ms_ps[:,:,[2,1,0]])
|
|
|
|
plt.imshow(rescale_image_intensity(im_ms_ps[:,:,[2,1,0]], cloud_mask, 100, False))
|
|
|
|
plt.axis('off')
|
|
|
|
plt.axis('off')
|
|
|
|
plt.title('Pansharpened')
|
|
|
|
plt.title('Pansharpened')
|
|
|
|
plt.show()
|
|
|
|
plt.show()
|
|
|
@ -610,6 +610,10 @@ def convert_epsg(points, epsg_in, epsg_out):
|
|
|
|
def classify_sand_unsupervised(im_ms_ps, im_pan, cloud_mask, wl_pix, buffer_size, min_beach_size, plot_bool):
|
|
|
|
def classify_sand_unsupervised(im_ms_ps, im_pan, cloud_mask, wl_pix, buffer_size, min_beach_size, plot_bool):
|
|
|
|
"""
|
|
|
|
"""
|
|
|
|
Classifies sand pixels using an unsupervised algorithm (Kmeans)
|
|
|
|
Classifies sand pixels using an unsupervised algorithm (Kmeans)
|
|
|
|
|
|
|
|
Set buffer size to False if you want to classify the entire image,
|
|
|
|
|
|
|
|
otherwise buffer size defines the buffer around the shoreline in which
|
|
|
|
|
|
|
|
pixels are considered for classification.
|
|
|
|
|
|
|
|
This classification is not robust and is only used to train a supervised algorithm
|
|
|
|
|
|
|
|
|
|
|
|
KV WRL 2018
|
|
|
|
KV WRL 2018
|
|
|
|
|
|
|
|
|
|
|
@ -623,8 +627,9 @@ def classify_sand_unsupervised(im_ms_ps, im_pan, cloud_mask, wl_pix, buffer_size
|
|
|
|
2D cloud mask with True where cloud pixels are
|
|
|
|
2D cloud mask with True where cloud pixels are
|
|
|
|
wl_pix: list of np.ndarray
|
|
|
|
wl_pix: list of np.ndarray
|
|
|
|
list of arrays containig the pixel coordinates of the water line
|
|
|
|
list of arrays containig the pixel coordinates of the water line
|
|
|
|
buffer_size: int
|
|
|
|
buffer_size: int or False
|
|
|
|
radius of the disk used to create a buffer around the water line
|
|
|
|
radius of the disk used to create a buffer around the water line
|
|
|
|
|
|
|
|
when False, the entire image is considered for kmeans
|
|
|
|
min_beach_size: int
|
|
|
|
min_beach_size: int
|
|
|
|
minimum number of connected pixels belonging to a single beach
|
|
|
|
minimum number of connected pixels belonging to a single beach
|
|
|
|
plot_bool: boolean
|
|
|
|
plot_bool: boolean
|
|
|
@ -644,6 +649,7 @@ def classify_sand_unsupervised(im_ms_ps, im_pan, cloud_mask, wl_pix, buffer_size
|
|
|
|
vec_features[:,[0,1,2,3]] = vec_ms_ps[:,[0,1,2,3]]
|
|
|
|
vec_features[:,[0,1,2,3]] = vec_ms_ps[:,[0,1,2,3]]
|
|
|
|
vec_features[:,4] = vec_pan
|
|
|
|
vec_features[:,4] = vec_pan
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
if buffer_size:
|
|
|
|
# create binary image with ones where the detected water lines is
|
|
|
|
# create binary image with ones where the detected water lines is
|
|
|
|
im_buffer = np.zeros((im_ms_ps.shape[0], im_ms_ps.shape[1]))
|
|
|
|
im_buffer = np.zeros((im_ms_ps.shape[0], im_ms_ps.shape[1]))
|
|
|
|
for i, contour in enumerate(wl_pix):
|
|
|
|
for i, contour in enumerate(wl_pix):
|
|
|
@ -654,6 +660,8 @@ def classify_sand_unsupervised(im_ms_ps, im_pan, cloud_mask, wl_pix, buffer_size
|
|
|
|
se = morphology.disk(buffer_size)
|
|
|
|
se = morphology.disk(buffer_size)
|
|
|
|
im_buffer = morphology.binary_dilation(im_buffer, se)
|
|
|
|
im_buffer = morphology.binary_dilation(im_buffer, se)
|
|
|
|
vec_buffer = (im_buffer == 1).reshape(im_ms_ps.shape[0] * im_ms_ps.shape[1])
|
|
|
|
vec_buffer = (im_buffer == 1).reshape(im_ms_ps.shape[0] * im_ms_ps.shape[1])
|
|
|
|
|
|
|
|
else:
|
|
|
|
|
|
|
|
vec_buffer = np.ones((vec_pan.shape[0]))
|
|
|
|
# add cloud mask to buffer
|
|
|
|
# add cloud mask to buffer
|
|
|
|
vec_buffer= np.logical_and(vec_buffer, ~vec_mask)
|
|
|
|
vec_buffer= np.logical_and(vec_buffer, ~vec_mask)
|
|
|
|
# perform kmeans (6 clusters)
|
|
|
|
# perform kmeans (6 clusters)
|
|
|
@ -664,17 +672,98 @@ def classify_sand_unsupervised(im_ms_ps, im_pan, cloud_mask, wl_pix, buffer_size
|
|
|
|
# find the class with maximum reflection in the B,G,R,Pan
|
|
|
|
# find the class with maximum reflection in the B,G,R,Pan
|
|
|
|
im_sand = im_labels == np.argmax(np.mean(kmeans.cluster_centers_[:,[0,1,2,4]], axis=1))
|
|
|
|
im_sand = im_labels == np.argmax(np.mean(kmeans.cluster_centers_[:,[0,1,2,4]], axis=1))
|
|
|
|
im_sand = morphology.remove_small_objects(im_sand, min_size=min_beach_size, connectivity=2)
|
|
|
|
im_sand = morphology.remove_small_objects(im_sand, min_size=min_beach_size, connectivity=2)
|
|
|
|
|
|
|
|
im_sand = morphology.binary_erosion(im_sand, morphology.disk(1))
|
|
|
|
# im_sand = morphology.binary_dilation(im_sand, morphology.disk(1))
|
|
|
|
# im_sand = morphology.binary_dilation(im_sand, morphology.disk(1))
|
|
|
|
|
|
|
|
|
|
|
|
if plot_bool:
|
|
|
|
if plot_bool:
|
|
|
|
im = np.copy(im_ms_ps)
|
|
|
|
im = np.copy(rescale_image_intensity(im_ms_ps[:,:,[2,1,0]], cloud_mask, 100, False))
|
|
|
|
im[im_sand,0] = 0
|
|
|
|
im[im_sand,0] = 0
|
|
|
|
im[im_sand,1] = 0
|
|
|
|
im[im_sand,1] = 0
|
|
|
|
im[im_sand,2] = 1
|
|
|
|
im[im_sand,2] = 1
|
|
|
|
plt.figure()
|
|
|
|
plt.figure()
|
|
|
|
plt.imshow(im[:,:,[2,1,0]])
|
|
|
|
plt.imshow(im)
|
|
|
|
plt.axis('image')
|
|
|
|
plt.axis('image')
|
|
|
|
plt.title('Sand classification')
|
|
|
|
plt.title('Sand classification')
|
|
|
|
plt.show()
|
|
|
|
plt.show()
|
|
|
|
|
|
|
|
|
|
|
|
return im_sand
|
|
|
|
return im_sand
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
def classify_image_NN(im_ms_ps, im_pan, cloud_mask, plot_bool):
|
|
|
|
|
|
|
|
"""
|
|
|
|
|
|
|
|
Classifies every pixel in the image in one of 4 classes:
|
|
|
|
|
|
|
|
- sand --> label = 1
|
|
|
|
|
|
|
|
- whitewater (breaking waves and swash) --> label = 2
|
|
|
|
|
|
|
|
- water --> label = 3
|
|
|
|
|
|
|
|
- other (vegetation, buildings, rocks...) --> label = 0
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
The classifier is a Neural Network, trained with 7000 pixels for the class SAND and 1500 pixels for
|
|
|
|
|
|
|
|
each of the other classes. This is because the class of interest for my application is SAND and I
|
|
|
|
|
|
|
|
wanted to minimize the classification error for that class
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
KV WRL 2018
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
Arguments:
|
|
|
|
|
|
|
|
-----------
|
|
|
|
|
|
|
|
im_ms_ps: np.ndarray
|
|
|
|
|
|
|
|
Pansharpened RGB + downsampled NIR and SWIR
|
|
|
|
|
|
|
|
im_pan:
|
|
|
|
|
|
|
|
Panchromatic band
|
|
|
|
|
|
|
|
cloud_mask: np.ndarray
|
|
|
|
|
|
|
|
2D cloud mask with True where cloud pixels are
|
|
|
|
|
|
|
|
plot_bool: boolean
|
|
|
|
|
|
|
|
True if plot is wanted
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
Returns: -----------
|
|
|
|
|
|
|
|
im_labels: np.ndarray
|
|
|
|
|
|
|
|
2D binary image containing True where sand pixels are located
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
"""
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
# load classifier
|
|
|
|
|
|
|
|
clf = joblib.load('functions/NeuralNet_classif.pkl')
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
# calculate features
|
|
|
|
|
|
|
|
n_features = 10
|
|
|
|
|
|
|
|
im_features = np.zeros((im_ms_ps.shape[0], im_ms_ps.shape[1], n_features))
|
|
|
|
|
|
|
|
im_features[:,:,[0,1,2,3,4]] = im_ms_ps
|
|
|
|
|
|
|
|
im_features[:,:,5] = im_pan
|
|
|
|
|
|
|
|
im_features[:,:,6] = nd_index(im_ms_ps[:,:,3], im_ms_ps[:,:,1], cloud_mask, False) # (NIR-G)
|
|
|
|
|
|
|
|
im_features[:,:,7] = nd_index(im_ms_ps[:,:,3], im_ms_ps[:,:,2], cloud_mask, False) # ND(NIR-R)
|
|
|
|
|
|
|
|
im_features[:,:,8] = nd_index(im_ms_ps[:,:,0], im_ms_ps[:,:,2], cloud_mask, False) # ND(B-R)
|
|
|
|
|
|
|
|
im_features[:,:,9] = nd_index(im_ms_ps[:,:,4], im_ms_ps[:,:,1], cloud_mask, False) # ND(SWIR-G)
|
|
|
|
|
|
|
|
# remove NaNs and clouds
|
|
|
|
|
|
|
|
vec_features = im_features.reshape((im_ms_ps.shape[0] * im_ms_ps.shape[1], n_features))
|
|
|
|
|
|
|
|
vec_cloud = cloud_mask.reshape(cloud_mask.shape[0]*cloud_mask.shape[1])
|
|
|
|
|
|
|
|
vec_nan = np.any(np.isnan(vec_features), axis=1)
|
|
|
|
|
|
|
|
vec_mask = np.logical_or(vec_cloud, vec_nan)
|
|
|
|
|
|
|
|
vec_features = vec_features[~vec_mask, :]
|
|
|
|
|
|
|
|
# predict with NN classifier
|
|
|
|
|
|
|
|
labels = clf.predict(vec_features)
|
|
|
|
|
|
|
|
# recompose image
|
|
|
|
|
|
|
|
vec_classif = np.zeros((cloud_mask.shape[0]*cloud_mask.shape[1]))
|
|
|
|
|
|
|
|
vec_classif[~vec_mask] = labels
|
|
|
|
|
|
|
|
im_classif = vec_classif.reshape((im_ms_ps.shape[0], im_ms_ps.shape[1]))
|
|
|
|
|
|
|
|
# im_classif = morphology.remove_small_objects(im_classif, min_size=min_beach_size, connectivity=2)
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
if plot_bool:
|
|
|
|
|
|
|
|
im_display = rescale_image_intensity(im_ms_ps[:,:,[2,1,0]], cloud_mask, 100, False)
|
|
|
|
|
|
|
|
im = np.copy(im_display)
|
|
|
|
|
|
|
|
colours = np.array([[1,0,0],[1,1,0],[0,1,1],[0,0,1]])
|
|
|
|
|
|
|
|
for k in range(4):
|
|
|
|
|
|
|
|
im[im_classif == k,0] = colours[k,0]
|
|
|
|
|
|
|
|
im[im_classif == k,1] = colours[k,1]
|
|
|
|
|
|
|
|
im[im_classif == k,2] = colours[k,2]
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
plt.figure()
|
|
|
|
|
|
|
|
ax1 = plt.subplot(121)
|
|
|
|
|
|
|
|
plt.imshow(im_display)
|
|
|
|
|
|
|
|
plt.axis('off')
|
|
|
|
|
|
|
|
plt.title('Image')
|
|
|
|
|
|
|
|
ax2 = plt.subplot(122, sharex=ax1, sharey=ax1)
|
|
|
|
|
|
|
|
plt.imshow(im)
|
|
|
|
|
|
|
|
plt.axis('off')
|
|
|
|
|
|
|
|
plt.title('NN')
|
|
|
|
|
|
|
|
plt.draw()
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
return im_classif
|
|
|
|
|
|
|
|
|
|
|
|