def describe_image(pred_df,fname):
    """A convenience function; writes a string that describes the tiles in a full-sized image.
        It is designed to work with a prediction dataframe that has the following columns:
        filename, tile_row, tile_col, empty, category_1, category_2...category_n, where
        the filename is the original full-size image filename, stem only (no extension or path)
        and the values are 1/0 for each category.
    """
    ostring = '' #the description string we will build
    tiles_mask = pred_df[pred_df['filename']==fname].index
    tilerows = pred_df.loc[tiles_mask]
    ntiles = len(tilerows)
    nempty = sum(pred_df.loc[tiles_mask]['empty'])
    if(ntiles == nempty):
        ostring = 'empty'
    else:
        non_empty_mask = pred_df[(pred_df['filename']==fname) & (pred_df['empty']==0)].index
        nonemptyrows = pred_df.loc[non_empty_mask]
        for i in range(len(nonemptyrows)):
            cn = nonemptyrows.iloc[i].index #index of the row = the column names.
            cv = nonemptyrows.iloc[i].values #values in this row
            rowcol = '[' + str(cv[1]) + ']' + '[' + str(cv[2]) + ']' #row, col written as '[0][5]' etc.
            sumvals = sum(cv[3:])
            obj_str = rowcol + ':'
            #If none of the categories are 1 including the 'empty' row, then the model is uncertain
            if sumvals == 0:
                obj_str += 'uncertain'
            #else we compile a string of whatever is in each tile
            else:
                for j in range(4,len(cn)):
                    if cv[j] > 0:
                        obj_str += cn[j] + ' '
            if len(obj_str) > 0:
                ostring += ' ' + obj_str
    return ostring