(classifier, metadata, settings)
Apply the image classifier to all the images and save the classified images.

    KV WRL 2019

    Arguments:
    -----------
    classifier: joblib object
        classifier model to be used for image classification
    metadata: dict
        contains all the information about the satellite images that were downloaded
    settings: dict with the following keys
        'inputs': dict
            input parameters (sitename, filepath, polygon, dates, sat_list)
        'cloud_thresh': float
            value between 0 and 1 indicating the maximum cloud fraction in 
            the cropped image that is accepted
        'cloud_mask_issue': boolean
            True if there is an issue with the cloud mask and sand pixels
            are erroneously being masked on the images
        'output_epsg': int
            output spatial reference system as EPSG code
        'buffer_size': int
            size of the buffer (m) around the sandy pixels over which the pixels 
            are considered in the thresholding algorithm
        'min_beach_area': int
            minimum allowable object area (in metres^2) for the class 'sand',
            the area is converted to number of connected pixels
        'min_length_sl': int
            minimum length (in metres) of shoreline contour to be valid

    Returns:
    -----------
    Saves .jpg images with the output of the classification in the folder ./detection
Apply the image classifier to all the images and save the classified images.
Apply the image classifier to all the images and save the classified images .
def evaluate_classifier(classifier, metadata, settings):
    """
    Apply the image classifier to all the images and save the classified images.

    KV WRL 2019

    Arguments:
    -----------
    classifier: joblib object
        classifier model to be used for image classification
    metadata: dict
        contains all the information about the satellite images that were downloaded
    settings: dict with the following keys
        'inputs': dict
            input parameters (sitename, filepath, polygon, dates, sat_list)
        'cloud_thresh': float
            value between 0 and 1 indicating the maximum cloud fraction in 
            the cropped image that is accepted
        'cloud_mask_issue': boolean
            True if there is an issue with the cloud mask and sand pixels
            are erroneously being masked on the images
        'output_epsg': int
            output spatial reference system as EPSG code
        'buffer_size': int
            size of the buffer (m) around the sandy pixels over which the pixels 
            are considered in the thresholding algorithm
        'min_beach_area': int
            minimum allowable object area (in metres^2) for the class 'sand',
            the area is converted to number of connected pixels
        'min_length_sl': int
            minimum length (in metres) of shoreline contour to be valid

    Returns:
    -----------
    Saves .jpg images with the output of the classification in the folder ./detection
    
    """  
    
    # create folder called evaluation
    fp = os.path.join(os.getcwd(), 'evaluation')
    if not os.path.exists(fp):
        os.makedirs(fp)
        
    # initialize figure (not interactive)
    plt.ioff()
    fig,ax = plt.subplots(1,2,figsize=[17,10],sharex=True, sharey=True,
                          constrained_layout=True)

    # create colormap for labels
    cmap = cm.get_cmap('tab20c')
    colorpalette = cmap(np.arange(0,13,1))
    colours = np.zeros((3,4))
    colours[0,:] = colorpalette[5]
    colours[1,:] = np.array([204/255,1,1,1])
    colours[2,:] = np.array([0,91/255,1,1])
    # loop through satellites
    for satname in metadata.keys():
        filepath = SDS_tools.get_filepath(settings['inputs'],satname)
        filenames = metadata[satname]['filenames']
        
        # load classifiers and
        if satname in ['L5','L7','L8']:
            pixel_size = 15
        elif satname == 'S2':
            pixel_size = 10
        # convert settings['min_beach_area'] and settings['buffer_size'] from metres to pixels
        buffer_size_pixels = np.ceil(settings['buffer_size']/pixel_size)
        min_beach_area_pixels = np.ceil(settings['min_beach_area']/pixel_size**2)
        
        # loop through images
        for i in range(len(filenames)):   
            # image filename
            fn = SDS_tools.get_filenames(filenames[i],filepath, satname)
            # read and preprocess image
            im_ms, georef, cloud_mask, im_extra, im_QA, im_nodata = SDS_preprocess.preprocess_single(fn, satname, settings['cloud_mask_issue'])
            image_epsg = metadata[satname]['epsg'][i]

            # compute cloud_cover percentage (with no data pixels)
            cloud_cover_combined = np.divide(sum(sum(cloud_mask.astype(int))),
                                    (cloud_mask.shape[0]*cloud_mask.shape[1]))
            if cloud_cover_combined > 0.99: # if 99% of cloudy pixels in image skip
                continue

            # remove no data pixels from the cloud mask (for example L7 bands of no data should not be accounted for)
            cloud_mask_adv = np.logical_xor(cloud_mask, im_nodata)
            # compute updated cloud cover percentage (without no data pixels)
            cloud_cover = np.divide(sum(sum(cloud_mask_adv.astype(int))),
                                    (sum(sum((~im_nodata).astype(int)))))
            # skip image if cloud cover is above threshold
            if cloud_cover > settings['cloud_thresh']:
                continue
            # calculate a buffer around the reference shoreline (if any has been digitised)
            im_ref_buffer = SDS_shoreline.create_shoreline_buffer(cloud_mask.shape, georef, image_epsg,
                                                    pixel_size, settings)
            # classify image in 4 classes (sand, whitewater, water, other) with NN classifier
            im_classif, im_labels = SDS_shoreline.classify_image_NN(im_ms, im_extra, cloud_mask,
                                    min_beach_area_pixels, classifier)
            # there are two options to map the contours:
            # if there are pixels in the 'sand' class --> use find_wl_contours2 (enhanced)
            # otherwise use find_wl_contours2 (traditional)
            try: # use try/except structure for long runs
                if sum(sum(im_labels[:,:,0])) < 10 :
                    # compute MNDWI image (SWIR-G)
                    im_mndwi = SDS_tools.nd_index(im_ms[:,:,4], im_ms[:,:,1], cloud_mask)
                    # find water contours on MNDWI grayscale image
                    contours_mwi, t_mndwi = SDS_shoreline.find_wl_contours1(im_mndwi, cloud_mask, im_ref_buffer)
                else:
                    # use classification to refine threshold and extract the sand/water interface
                    contours_mwi, t_mndwi = SDS_shoreline.find_wl_contours2(im_ms, im_labels,
                                                cloud_mask, buffer_size_pixels, im_ref_buffer)
            except:
                print('Could not map shoreline for this image: ' + filenames[i])
                continue
            # process the water contours into a shoreline
            shoreline = SDS_shoreline.process_shoreline(contours_mwi, cloud_mask, georef, image_epsg, settings)
            try:
                sl_pix = SDS_tools.convert_world2pix(SDS_tools.convert_epsg(shoreline,
                                                                            settings['output_epsg'],
                                                                            image_epsg)[:,[0,1]], georef)
            except:
                # if try fails, just add nan into the shoreline vector so the next parts can still run
                sl_pix = np.array([[np.nan, np.nan],[np.nan, np.nan]])
            # make a plot
            im_RGB = SDS_preprocess.rescale_image_intensity(im_ms[:,:,[2,1,0]], cloud_mask, 99.9)
            # create classified image
            im_class = np.copy(im_RGB)
            for k in range(0,im_labels.shape[2]):
                im_class[im_labels[:,:,k],0] = colours[k,0]
                im_class[im_labels[:,:,k],1] = colours[k,1]
                im_class[im_labels[:,:,k],2] = colours[k,2]        
            # show images
            ax[0].imshow(im_RGB)
            ax[1].imshow(im_RGB)
            ax[1].imshow(im_class, alpha=0.5)
            ax[0].axis('off')
            ax[1].axis('off')
            filename = filenames[i][:filenames[i].find('.')][:-4] 
            ax[0].set_title(filename)  
            ax[0].plot(sl_pix[:,0], sl_pix[:,1], 'k.', markersize=3)
            ax[1].plot(sl_pix[:,0], sl_pix[:,1], 'k.', markersize=3)
            # save figure
            fig.savefig(os.path.join(fp,settings['inputs']['sitename'] + filename[:19] +'.jpg'), dpi=150)
            # clear axes
            for cax in fig.axes:
               cax.clear()
   
    # close the figure at the end
    plt.close()
def evaluate_classifier ( classifier , metadata , settings ) : # create folder called evaluation fp = os . path . join ( os . getcwd ( ) , 'evaluation' ) if not os . path . exists ( fp ) : os . makedirs ( fp ) # initialize figure (not interactive) plt . ioff ( ) fig , ax = plt . subplots ( 1 , 2 , figsize = [ 17 , 10 ] , sharex = True , sharey = True , constrained_layout = True ) # create colormap for labels cmap = cm . get_cmap ( 'tab20c' ) colorpalette = cmap ( np . arange ( 0 , 13 , 1 ) ) colours = np . zeros ( ( 3 , 4 ) ) colours [ 0 , : ] = colorpalette [ 5 ] colours [ 1 , : ] = np . array ( [ 204 / 255 , 1 , 1 , 1 ] ) colours [ 2 , : ] = np . array ( [ 0 , 91 / 255 , 1 , 1 ] ) # loop through satellites for satname in metadata . keys ( ) : filepath = SDS_tools . get_filepath ( settings [ 'inputs' ] , satname ) filenames = metadata [ satname ] [ 'filenames' ] # load classifiers and if satname in [ 'L5' , 'L7' , 'L8' ] : pixel_size = 15 elif satname == 'S2' : pixel_size = 10 # convert settings['min_beach_area'] and settings['buffer_size'] from metres to pixels buffer_size_pixels = np . ceil ( settings [ 'buffer_size' ] / pixel_size ) min_beach_area_pixels = np . ceil ( settings [ 'min_beach_area' ] / pixel_size ** 2 ) # loop through images for i in range ( len ( filenames ) ) : # image filename fn = SDS_tools . get_filenames ( filenames [ i ] , filepath , satname ) # read and preprocess image im_ms , georef , cloud_mask , im_extra , im_QA , im_nodata = SDS_preprocess . preprocess_single ( fn , satname , settings [ 'cloud_mask_issue' ] ) image_epsg = metadata [ satname ] [ 'epsg' ] [ i ] # compute cloud_cover percentage (with no data pixels) cloud_cover_combined = np . divide ( sum ( sum ( cloud_mask . astype ( int ) ) ) , ( cloud_mask . shape [ 0 ] * cloud_mask . shape [ 1 ] ) ) if cloud_cover_combined > 0.99 : # if 99% of cloudy pixels in image skip continue # remove no data pixels from the cloud mask (for example L7 bands of no data should not be accounted for) cloud_mask_adv = np . logical_xor ( cloud_mask , im_nodata ) # compute updated cloud cover percentage (without no data pixels) cloud_cover = np . divide ( sum ( sum ( cloud_mask_adv . astype ( int ) ) ) , ( sum ( sum ( ( ~ im_nodata ) . astype ( int ) ) ) ) ) # skip image if cloud cover is above threshold if cloud_cover > settings [ 'cloud_thresh' ] : continue # calculate a buffer around the reference shoreline (if any has been digitised) im_ref_buffer = SDS_shoreline . create_shoreline_buffer ( cloud_mask . shape , georef , image_epsg , pixel_size , settings ) # classify image in 4 classes (sand, whitewater, water, other) with NN classifier im_classif , im_labels = SDS_shoreline . classify_image_NN ( im_ms , im_extra , cloud_mask , min_beach_area_pixels , classifier ) # there are two options to map the contours: # if there are pixels in the 'sand' class --> use find_wl_contours2 (enhanced) # otherwise use find_wl_contours2 (traditional) try : # use try/except structure for long runs if sum ( sum ( im_labels [ : , : , 0 ] ) ) < 10 : # compute MNDWI image (SWIR-G) im_mndwi = SDS_tools . nd_index ( im_ms [ : , : , 4 ] , im_ms [ : , : , 1 ] , cloud_mask ) # find water contours on MNDWI grayscale image contours_mwi , t_mndwi = SDS_shoreline . find_wl_contours1 ( im_mndwi , cloud_mask , im_ref_buffer ) else : # use classification to refine threshold and extract the sand/water interface contours_mwi , t_mndwi = SDS_shoreline . find_wl_contours2 ( im_ms , im_labels , cloud_mask , buffer_size_pixels , im_ref_buffer ) except : print ( 'Could not map shoreline for this image: ' + filenames [ i ] ) continue # process the water contours into a shoreline shoreline = SDS_shoreline . process_shoreline ( contours_mwi , cloud_mask , georef , image_epsg , settings ) try : sl_pix = SDS_tools . convert_world2pix ( SDS_tools . convert_epsg ( shoreline , settings [ 'output_epsg' ] , image_epsg ) [ : , [ 0 , 1 ] ] , georef ) except : # if try fails, just add nan into the shoreline vector so the next parts can still run sl_pix = np . array ( [ [ np . nan , np . nan ] , [ np . nan , np . nan ] ] ) # make a plot im_RGB = SDS_preprocess . rescale_image_intensity ( im_ms [ : , : , [ 2 , 1 , 0 ] ] , cloud_mask , 99.9 ) # create classified image im_class = np . copy ( im_RGB ) for k in range ( 0 , im_labels . shape [ 2 ] ) : im_class [ im_labels [ : , : , k ] , 0 ] = colours [ k , 0 ] im_class [ im_labels [ : , : , k ] , 1 ] = colours [ k , 1 ] im_class [ im_labels [ : , : , k ] , 2 ] = colours [ k , 2 ] # show images ax [ 0 ] . imshow ( im_RGB ) ax [ 1 ] . imshow ( im_RGB ) ax [ 1 ] . imshow ( im_class , alpha = 0.5 ) ax [ 0 ] . axis ( 'off' ) ax [ 1 ] . axis ( 'off' ) filename = filenames [ i ] [ : filenames [ i ] . find ( '.' ) ] [ : - 4 ] ax [ 0 ] . set_title ( filename ) ax [ 0 ] . plot ( sl_pix [ : , 0 ] , sl_pix [ : , 1 ] , 'k.' , markersize = 3 ) ax [ 1 ] . plot ( sl_pix [ : , 0 ] , sl_pix [ : , 1 ] , 'k.' , markersize = 3 ) # save figure fig . savefig ( os . path . join ( fp , settings [ 'inputs' ] [ 'sitename' ] + filename [ : 19 ] + '.jpg' ) , dpi = 150 ) # clear axes for cax in fig . axes : cax . clear ( ) # close the figure at the end plt . close ( )