implemented sand classification

development
kvos 7 years ago
parent f3f13ec180
commit c31ae42fbd

@ -0,0 +1,181 @@
# -*- coding: utf-8 -*-
#==========================================================#
# Extract shorelines from Landsat images
#==========================================================#
# Initial settings
import ee
import matplotlib.pyplot as plt
import matplotlib.cm as cm
import numpy as np
import pandas as pd
from datetime import datetime
import pickle
import pdb
import pytz
# image processing modules
import skimage.filters as filters
import skimage.exposure as exposure
import skimage.transform as transform
import sklearn.decomposition as decomposition
import skimage.morphology as morphology
import skimage.measure as measure
# machine learning modules
from sklearn.cluster import KMeans
# my modules
import functions.utils as utils
import functions.sds as sds
# some settings
np.seterr(all='ignore') # raise/ignore divisions by 0 and nans
plt.rcParams['axes.grid'] = False
plt.rcParams['figure.max_open_warning'] = 100
ee.Initialize()
# parameters
cloud_thresh = 0.5 # threshold for cloud cover
plot_bool = True # if you want the plots
prob_high = 99.9 # upper probability to clip and rescale pixel intensity
min_contour_points = 100# minimum number of points contained in each water line
output_epsg = 28356 # GDA94 / MGA Zone 56
buffer_size = 10 # radius (in pixels) of disk for buffer (pixel classification)
min_beach_size = 50 # number of pixels in a beach (pixel classification)
# select collection
satname = 'L8'
input_col = ee.ImageCollection('LANDSAT/LC08/C01/T1_RT_TOA') # Landsat 8 Tier 1 TOA
# location (Narrabeen-Collaroy beach)
polygon = [[[151.3473129272461,-33.69035274454718],
[151.2820816040039,-33.68206818063878],
[151.27281188964844,-33.74775138989556],
[151.3425064086914,-33.75231878701767],
[151.3473129272461,-33.69035274454718]]];
# dates
start_date = '2013-01-01'
end_date = '2018-12-31'
# filter by location and date
flt_col = input_col.filterBounds(ee.Geometry.Polygon(polygon)).filterDate(start_date, end_date)
n_img = flt_col.size().getInfo()
print('Number of images covering the polygon:', n_img)
im_all = flt_col.getInfo().get('features')
i = 0 # first image
# find image in ee database
im = ee.Image(im_all[i].get('id'))
# load image as np.array
im_pan, im_ms, im_cloud, crs, meta = sds.read_eeimage(im, polygon, satname, plot_bool)
# rescale intensities
im_ms = sds.rescale_image_intensity(im_ms, im_cloud, prob_high, plot_bool)
im_pan = sds.rescale_image_intensity(im_pan, im_cloud, prob_high, plot_bool)
# pansharpen rgb image
im_ms_ps = sds.pansharpen(im_ms[:,:,[0,1,2]], im_pan, im_cloud, plot_bool)
# add down-sized bands for NIR and SWIR (since pansharpening is not possible)
im_ms_ps = np.append(im_ms_ps, im_ms[:,:,[3,4]], axis=2)
# calculate NDWI
im_ndwi = sds.nd_index(im_ms_ps[:,:,3], im_ms_ps[:,:,1], im_cloud, plot_bool)
# edge detection
wl_pix = sds.find_wl_contours(im_ndwi, im_cloud, min_contour_points, plot_bool)
# convert from pixels to world coordinates
wl_coords = sds.convert_pix2world(wl_pix, crs['crs_15m'])
# convert to output epsg spatial reference
wl = sds.convert_epsg(wl_coords, crs['epsg_code'], output_epsg)
# classify sand pixels
im_sand = sds.classify_sand_unsupervised(im_ms_ps, im_pan, im_cloud, wl_pix, buffer_size, min_beach_size, plot_bool)
#%%
plt.figure()
plt.imshow(im_ms_ps[:,:,[2,1,0]])
for i,contour in enumerate(wl_pix): plt.plot(contour[:, 1], contour[:, 0], linewidth=2)
plt.axis('image')
plt.title('Detected water lines')
plt.show()
vec = im_ms_ps.reshape(im_ms_ps.shape[0] * im_ms_ps.shape[1], im_ms_ps.shape[2])
vec_pan = im_pan.reshape(im_pan.shape[0]*im_pan.shape[1])
features = np.zeros((len(vec), 5))
features[:,[0,1,2,3]] = vec[:,[0,1,2,3]]
features[:,4] = vec_pan
vec_mask = im_cloud.reshape(im_ms_ps.shape[0] * im_ms_ps.shape[1])
# create buffer
im_buffer = np.zeros((im_ms_ps.shape[0], im_ms_ps.shape[1]))
for i, contour in enumerate(wl_pix):
indices = [(int(_[0]), int(_[1])) for _ in list(np.round(contour))]
for j, idx in enumerate(indices):
im_buffer[idx] = 1
plt.figure()
plt.imshow(im_buffer)
plt.draw()
se = morphology.disk(buffer_size)
im_buffer = morphology.binary_dilation(im_buffer, se)
plt.figure()
plt.imshow(im_buffer)
plt.draw()
vec_buffer = (im_buffer == 1).reshape(im_ms_ps.shape[0] * im_ms_ps.shape[1])
vec_buffer= np.logical_and(vec_buffer, ~vec_mask)
#vec_buffer = np.ravel_multi_index(z,(im_ms_ps.shape[0], im_ms_ps.shape[1]))
kmeans = KMeans(n_clusters=6, random_state=0).fit(vec[vec_buffer,:])
labels = kmeans.labels_
labels_full = np.ones((len(vec_mask))) * np.nan
labels_full[vec_buffer] = labels
im_labels = labels_full.reshape(im_ms_ps.shape[0], im_ms_ps.shape[1])
plt.figure()
plt.imshow(im_labels)
plt.axis('equal')
plt.draw()
utils.compare_images(im_labels, im_pan)
plt.figure()
for i in range(6): plt.plot(kmeans.cluster_centers_[i,:])
plt.draw()
im_sand = im_labels == np.argmax(np.mean(kmeans.cluster_centers_[:,[0,1,2,4]], axis=1))
im_sand2 = morphology.remove_small_objects(im_sand, min_size=min_beach_size, connectivity=2)
im_sand3 = morphology.binary_dilation(im_sand2, morphology.disk(1))
plt.figure()
plt.imshow(im_sand3)
plt.draw()
im_ms_ps[im_sand3,0] = 0
im_ms_ps[im_sand3,1] = 0
im_ms_ps[im_sand3,2] = 1
plt.figure()
plt.imshow(im_ms_ps[:,:,[2,1,0]])
plt.axis('image')
plt.title('Sand classification')
plt.show()
#%%

@ -26,6 +26,7 @@ import skimage.transform as transform
import sklearn.decomposition as decomposition
import skimage.measure as measure
import skimage.morphology as morphology
from sklearn.cluster import KMeans
# import own modules
@ -605,3 +606,75 @@ def convert_epsg(points, epsg_in, epsg_out):
raise
return points_converted
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)
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
wl_pix: list of np.ndarray
list of arrays containig the pixel coordinates of the water line
buffer_size: int
radius of the disk used to create a buffer around the water line
min_beach_size: int
minimum number of connected pixels belonging to a single beach
plot_bool: boolean
True if plot is wanted
Returns: -----------
im_sand: np.ndarray
2D binary image containing True where sand pixels are located
"""
# reshape the 2D images into vectors
vec_ms_ps = im_ms_ps.reshape(im_ms_ps.shape[0] * im_ms_ps.shape[1], im_ms_ps.shape[2])
vec_pan = im_pan.reshape(im_pan.shape[0]*im_pan.shape[1])
vec_mask = cloud_mask.reshape(im_ms_ps.shape[0] * im_ms_ps.shape[1])
# add B,G,R,NIR and pan bands to the vector of features
vec_features = np.zeros((vec_ms_ps.shape[0], 5))
vec_features[:,[0,1,2,3]] = vec_ms_ps[:,[0,1,2,3]]
vec_features[:,4] = vec_pan
# 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]))
for i, contour in enumerate(wl_pix):
indices = [(int(_[0]), int(_[1])) for _ in list(np.round(contour))]
for j, idx in enumerate(indices):
im_buffer[idx] = 1
# perform a dilation on the binary image
se = morphology.disk(buffer_size)
im_buffer = morphology.binary_dilation(im_buffer, se)
vec_buffer = (im_buffer == 1).reshape(im_ms_ps.shape[0] * im_ms_ps.shape[1])
# add cloud mask to buffer
vec_buffer= np.logical_and(vec_buffer, ~vec_mask)
# perform kmeans (6 clusters)
kmeans = KMeans(n_clusters=6, random_state=0).fit(vec_features[vec_buffer,:])
labels = np.ones((len(vec_mask))) * np.nan
labels[vec_buffer] = kmeans.labels_
im_labels = labels.reshape(im_ms_ps.shape[0], im_ms_ps.shape[1])
# 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 = morphology.remove_small_objects(im_sand, min_size=min_beach_size, connectivity=2)
# im_sand = morphology.binary_dilation(im_sand, morphology.disk(1))
if plot_bool:
im = np.copy(im_ms_ps)
im[im_sand,0] = 0
im[im_sand,1] = 0
im[im_sand,2] = 1
plt.figure()
plt.imshow(im[:,:,[2,1,0]])
plt.axis('image')
plt.title('Sand classification')
plt.show()
return im_sand

Loading…
Cancel
Save