lidar_label_builder.llb_tools

Utility functions for building a labeling database for Convolutional Neural Networks (CNNs).

This module provides a collection of secondary functions to assist with various tasks related to the construction of a labeling database for CNNs. Use these functions to streamline common operations involved in dataset preparation, annotation, and organization.

  1"""
  2Utility functions for building a labeling database for Convolutional Neural Networks (CNNs).
  3
  4This module provides a collection of secondary functions to assist with various tasks related to the 
  5construction of a labeling database for CNNs. Use these functions to streamline common operations involved in 
  6dataset preparation, annotation, and organization.
  7
  8"""
  9
 10import os
 11import geopandas as gpd
 12import lidar_label_builder as llb
 13import pandas as pd
 14import json
 15from matplotlib import pyplot as plt
 16from pathlib import Path
 17
 18#Connect To Global Variables
 19# scriptDir = os.path.dirname(os.path.abspath(__file__))
 20# parentDir = os.path.dirname(scriptDir)
 21# global_vars = os.path.join(parentDir, 'configs', 'global_variables.json')
 22# with open(global_vars, 'r') as f:
 23#     params_dict = json.load(f)
 24with (Path(__file__).resolve().parent.parent / 'configs' / 'global_variables.json').open('r') as f:
 25    params_dict = json.load(f)
 26
 27LABEL_FILE_PATTERN = params_dict['LABEL_FILE_PATTERN']
 28LABEL_FILE_EXT = params_dict['LABEL_FILE_EXT']
 29
 30
 31def import_filter_concat_dfs(path, colsToFilter:list =None, filterType:str = None):
 32    """
 33    Import and filter GeoDataFrames from shapefiles or directories of shapefiles. If multiple shapefiles will concatenate into one single filtered df.
 34    Args:
 35        path (str or list): File path to a shapefile, directory of shapeiles, or a list of file paths to shapefiles.
 36        colsToFilter (list, optional): A list of columns to keep or remove from geodataframe.
 37        filterType (str, optional): Used to specify if colsToFilter should been kept or removed from dataframe. 'keep' or 'remove'
 38    Returns:
 39        pandas.DataFrame: Filtered GeoDataFrame containing Regions of Interest (ROIs) and associated clipped DEMs.
 40    Raises:
 41        ValueError: If the input 'path' is of an unsupported type or if the file type or directory structure is not valid.
 42    """
 43    if colsToFilter != None and filterType == None:
 44        raise ValueError ('Specify keep or remove for filterType ')
 45    
 46    if isinstance(path, str):
 47        if os.path.isfile(path) and LABEL_FILE_PATTERN in path and path.lower().endswith(LABEL_FILE_EXT):
 48            df = gpd.read_file(filePath, truncation=False)
 49            if colsToFilter != None:
 50                if filterType == 'remove':
 51                    filteredDf = df.drop(columns=colsToFilter)
 52                elif filterType == 'keep':
 53                    filteredDf = df[colsToFilter]
 54                else:
 55                    raise print('Invalid filtertype skipping filter')
 56            else:
 57                filteredDf = df
 58            
 59        elif os.path.isdir(path):
 60            dfs=[]
 61            labelDfs = []
 62            for root, dirs, files in os.walk(path):
 63                for file in files:
 64                    if LABEL_FILE_PATTERN in file and file.lower().endswith(LABEL_FILE_EXT):
 65                        filePath=os.path.join(root, file)
 66                        labelDfs.append(filePath)
 67                        df = gpd.read_file(filePath, truncation=False)
 68                        if colsToFilter != None:
 69                            if filterType == 'remove':
 70                                filteredDf = df.drop(columns=colsToFilter)
 71                            elif filterType == 'keep':
 72                                filteredDf = df[colsToFilter]
 73                            else:
 74                                raise print('Invalid filtertype skipping filter')
 75                        else:
 76                            filteredDf = df
 77                        dfs.append(filteredDf)
 78            print('Joining Dataframes: ' + str(labelDfs))
 79            filteredDf = gpd.GeoDataFrame(pd.concat(dfs, ignore_index=True), crs=dfs[0].crs)
 80        else:
 81            raise ValueError(f"Unsupported file type or directory structure for path: {path}")
 82    elif isinstance(path, list):
 83        dfs = []
 84        for p in path:
 85            if os.path.isfile(p) and p.lower().endswith('.shp'):
 86                df = gpd.read_file(filePath, truncation=False)
 87                if colsToFilter != None:
 88                    if filterType == 'remove':
 89                        filteredDf = df.drop(columns=colsToFilter)
 90                    elif filterType == 'keep':
 91                        filteredDf = df[colsToFilter]
 92                    else:
 93                        raise print('Invalid filtertype skipping filter')
 94                else:
 95                    filteredDf = df
 96                dfs.append(filteredDf)
 97            else:
 98                print("Unsupported input type for {p}. Extension must be {LABEL_FILE_EXT}.")
 99                
100        filteredDf = gpd.GeoDataFrame(pd.concat(dfs, ignore_index=True), crs=dfs[0].crs)
101    else:
102        raise ValueError("Unsupported input type for {path}. Must be a string or a list of strings.")
103    
104    return filteredDf
105
106
107def load_json_params_llb(filePath:str,  print_info:bool=True):
108    """Loads parameters from a specified JSON file for configuring the lidar label builder.
109
110    Args:
111        filePath (str): Path to the JSON parameter file.
112        print_info (bool, optional): If True, prints the loaded parameters and their types. Defaults to True.
113
114    Returns:
115        tuple: A tuple containing the following elements:
116            labels (list or None): List of label names or None if not provided.
117            outDirectory (str or None): Path to the output directory or None if not provided.
118            pathToPolygon (str or None): Path to the polygon file or None if not provided.
119            roiWidth (int or None): Width of the region of interest (ROI) or None if not provided.
120            numRois (int or None): Number of ROIs to generate or None if not provided.
121            aoiLabel (str or None): Area of interest (AOI) label or None if not provided.
122            polygonSource (str or None): Source of the polygon data or None if not provided.
123            roiMethod (str or None): Method for ROI generation or None if not provided.
124            rasterMethod (str or None): Method for raster processing or None if not provided.
125            rasterPaths (list or None): List of raster file paths or None if not provided.
126            plotkwargs (dict or None): Dictionary of keyword arguments for plotting or None if not provided.
127    """
128
129    params = filePath
130    with open(params, 'r') as f:
131        params_dict = json.load(f)
132
133   
134    labels = params_dict.get('labels', None)
135    outDirectory = params_dict.get('outDirectory', None)
136    pathToPolygon = params_dict.get('pathToPolygon', None)
137    roiWidth = params_dict.get('roiWidth', None)
138    numRois = params_dict.get('numRois', None)
139    aoiLabel = params_dict.get('aoiLabel', None)
140    polygonSource = params_dict.get('polygonSource', None)
141    roiMethod = params_dict.get('roiMethod', None)
142    rasterMethod = params_dict.get('rasterMethod', None)
143
144    # Dynamically handle raster paths
145    # rasterPaths = [params_dict.get(f'rasterPath{i}', None) for i in range(10) if f'rasterPath{i}' in params_dict]
146    rasterPaths = params_dict.get('rasterPaths',None)
147  
148    plotkwargs = params_dict.get('plotkwargs', None)
149    
150
151    if print_info:
152        # Print the parameters and their types
153        print("Labels ({}): {}".format(type(labels), labels))
154        print("Out Directory ({}): {}".format(type(outDirectory), outDirectory))
155        print("Path to Polygon ({}): {}".format(type(pathToPolygon), pathToPolygon))
156        print("ROI Width ({}): {}".format(type(roiWidth), roiWidth))
157        print("Number of ROIs ({}): {}".format(type(numRois), numRois))
158        print("AOI Label ({}): {}".format(type(aoiLabel), aoiLabel))
159        print("Polygon Source ({}): {}".format(type(polygonSource), polygonSource))
160        print("ROI Method ({}): {}".format(type(roiMethod), roiMethod))
161        print("Raster Method ({}): {}".format(type(rasterMethod), rasterMethod))
162        print("Raster Paths ({}): {}".format(type(rasterPaths), rasterPaths))
163        print("Plot kwargs ({}): {}".format(type(plotkwargs), plotkwargs))
164
165
166    return labels, outDirectory, pathToPolygon, roiWidth, numRois, aoiLabel, polygonSource, \
167           roiMethod, rasterMethod, rasterPaths, plotkwargs
168
169
170def visualize_kwargs(rasterPaths:list, plotkwargs:dict):
171    """Plots a visualization of the keyword arguments (kwargs) for the rasters. Can be useful to make sure keyword arguments are achieving the desired result.
172
173    Args:
174        rasterPaths (list): A list of raster paths corresponding to the kwargs
175        plotkwargs (dict): A dictionary of keyword arguments to plot the rasters.
176
177    Returns:
178        None
179    """
180
181    derivs = [llb.get_raster_as_grid(path)[0] for path in rasterPaths]
182    fig,axs = plt.subplots()
183    plt.subplots_adjust(right = 0.7)
184
185    # Plot the raster images
186    for deriv, kwarg in zip(derivs, plotkwargs):
187        axs.imshow(deriv, **kwarg)
188
189    # Show the plot
190    plt.show()
191    return None
192
193def rm_filepath_cols(gdf:gpd.GeoDataFrame):
194    """Removes all columns with raster and image filepaths.
195
196    Args:
197        gdf (gpd.GeoDataFrame): Filepath to a roi geodataframe with filepaths to remove.
198
199    Returns:
200        gdf (gpd.GeoDataFrame): The modified geodataframe after filepath columns were removed.
201    """
202    rasterCols = [col for col in gdf.columns if col.startswith('rstr_clp')]
203    imageCols = [col for col in gdf.columns if col == 'imgPaths']
204   
205    for rasterCol in rasterCols:
206        gdf.drop(rasterCol, axis=1, inplace=True)
207
208    for imgCol in imageCols:
209        gdf.drop(imgCol, axis= 1, inplace=True)
210
211    return gdf
LABEL_FILE_PATTERN = '_labels'
LABEL_FILE_EXT = '.shp'
def import_filter_concat_dfs(path, colsToFilter: list = None, filterType: str = None):
 32def import_filter_concat_dfs(path, colsToFilter:list =None, filterType:str = None):
 33    """
 34    Import and filter GeoDataFrames from shapefiles or directories of shapefiles. If multiple shapefiles will concatenate into one single filtered df.
 35    Args:
 36        path (str or list): File path to a shapefile, directory of shapeiles, or a list of file paths to shapefiles.
 37        colsToFilter (list, optional): A list of columns to keep or remove from geodataframe.
 38        filterType (str, optional): Used to specify if colsToFilter should been kept or removed from dataframe. 'keep' or 'remove'
 39    Returns:
 40        pandas.DataFrame: Filtered GeoDataFrame containing Regions of Interest (ROIs) and associated clipped DEMs.
 41    Raises:
 42        ValueError: If the input 'path' is of an unsupported type or if the file type or directory structure is not valid.
 43    """
 44    if colsToFilter != None and filterType == None:
 45        raise ValueError ('Specify keep or remove for filterType ')
 46    
 47    if isinstance(path, str):
 48        if os.path.isfile(path) and LABEL_FILE_PATTERN in path and path.lower().endswith(LABEL_FILE_EXT):
 49            df = gpd.read_file(filePath, truncation=False)
 50            if colsToFilter != None:
 51                if filterType == 'remove':
 52                    filteredDf = df.drop(columns=colsToFilter)
 53                elif filterType == 'keep':
 54                    filteredDf = df[colsToFilter]
 55                else:
 56                    raise print('Invalid filtertype skipping filter')
 57            else:
 58                filteredDf = df
 59            
 60        elif os.path.isdir(path):
 61            dfs=[]
 62            labelDfs = []
 63            for root, dirs, files in os.walk(path):
 64                for file in files:
 65                    if LABEL_FILE_PATTERN in file and file.lower().endswith(LABEL_FILE_EXT):
 66                        filePath=os.path.join(root, file)
 67                        labelDfs.append(filePath)
 68                        df = gpd.read_file(filePath, truncation=False)
 69                        if colsToFilter != None:
 70                            if filterType == 'remove':
 71                                filteredDf = df.drop(columns=colsToFilter)
 72                            elif filterType == 'keep':
 73                                filteredDf = df[colsToFilter]
 74                            else:
 75                                raise print('Invalid filtertype skipping filter')
 76                        else:
 77                            filteredDf = df
 78                        dfs.append(filteredDf)
 79            print('Joining Dataframes: ' + str(labelDfs))
 80            filteredDf = gpd.GeoDataFrame(pd.concat(dfs, ignore_index=True), crs=dfs[0].crs)
 81        else:
 82            raise ValueError(f"Unsupported file type or directory structure for path: {path}")
 83    elif isinstance(path, list):
 84        dfs = []
 85        for p in path:
 86            if os.path.isfile(p) and p.lower().endswith('.shp'):
 87                df = gpd.read_file(filePath, truncation=False)
 88                if colsToFilter != None:
 89                    if filterType == 'remove':
 90                        filteredDf = df.drop(columns=colsToFilter)
 91                    elif filterType == 'keep':
 92                        filteredDf = df[colsToFilter]
 93                    else:
 94                        raise print('Invalid filtertype skipping filter')
 95                else:
 96                    filteredDf = df
 97                dfs.append(filteredDf)
 98            else:
 99                print("Unsupported input type for {p}. Extension must be {LABEL_FILE_EXT}.")
100                
101        filteredDf = gpd.GeoDataFrame(pd.concat(dfs, ignore_index=True), crs=dfs[0].crs)
102    else:
103        raise ValueError("Unsupported input type for {path}. Must be a string or a list of strings.")
104    
105    return filteredDf

Import and filter GeoDataFrames from shapefiles or directories of shapefiles. If multiple shapefiles will concatenate into one single filtered df.

Arguments:
  • path (str or list): File path to a shapefile, directory of shapeiles, or a list of file paths to shapefiles.
  • colsToFilter (list, optional): A list of columns to keep or remove from geodataframe.
  • filterType (str, optional): Used to specify if colsToFilter should been kept or removed from dataframe. 'keep' or 'remove'
Returns:

pandas.DataFrame: Filtered GeoDataFrame containing Regions of Interest (ROIs) and associated clipped DEMs.

Raises:
  • ValueError: If the input 'path' is of an unsupported type or if the file type or directory structure is not valid.
def load_json_params_llb(filePath: str, print_info: bool = True):
108def load_json_params_llb(filePath:str,  print_info:bool=True):
109    """Loads parameters from a specified JSON file for configuring the lidar label builder.
110
111    Args:
112        filePath (str): Path to the JSON parameter file.
113        print_info (bool, optional): If True, prints the loaded parameters and their types. Defaults to True.
114
115    Returns:
116        tuple: A tuple containing the following elements:
117            labels (list or None): List of label names or None if not provided.
118            outDirectory (str or None): Path to the output directory or None if not provided.
119            pathToPolygon (str or None): Path to the polygon file or None if not provided.
120            roiWidth (int or None): Width of the region of interest (ROI) or None if not provided.
121            numRois (int or None): Number of ROIs to generate or None if not provided.
122            aoiLabel (str or None): Area of interest (AOI) label or None if not provided.
123            polygonSource (str or None): Source of the polygon data or None if not provided.
124            roiMethod (str or None): Method for ROI generation or None if not provided.
125            rasterMethod (str or None): Method for raster processing or None if not provided.
126            rasterPaths (list or None): List of raster file paths or None if not provided.
127            plotkwargs (dict or None): Dictionary of keyword arguments for plotting or None if not provided.
128    """
129
130    params = filePath
131    with open(params, 'r') as f:
132        params_dict = json.load(f)
133
134   
135    labels = params_dict.get('labels', None)
136    outDirectory = params_dict.get('outDirectory', None)
137    pathToPolygon = params_dict.get('pathToPolygon', None)
138    roiWidth = params_dict.get('roiWidth', None)
139    numRois = params_dict.get('numRois', None)
140    aoiLabel = params_dict.get('aoiLabel', None)
141    polygonSource = params_dict.get('polygonSource', None)
142    roiMethod = params_dict.get('roiMethod', None)
143    rasterMethod = params_dict.get('rasterMethod', None)
144
145    # Dynamically handle raster paths
146    # rasterPaths = [params_dict.get(f'rasterPath{i}', None) for i in range(10) if f'rasterPath{i}' in params_dict]
147    rasterPaths = params_dict.get('rasterPaths',None)
148  
149    plotkwargs = params_dict.get('plotkwargs', None)
150    
151
152    if print_info:
153        # Print the parameters and their types
154        print("Labels ({}): {}".format(type(labels), labels))
155        print("Out Directory ({}): {}".format(type(outDirectory), outDirectory))
156        print("Path to Polygon ({}): {}".format(type(pathToPolygon), pathToPolygon))
157        print("ROI Width ({}): {}".format(type(roiWidth), roiWidth))
158        print("Number of ROIs ({}): {}".format(type(numRois), numRois))
159        print("AOI Label ({}): {}".format(type(aoiLabel), aoiLabel))
160        print("Polygon Source ({}): {}".format(type(polygonSource), polygonSource))
161        print("ROI Method ({}): {}".format(type(roiMethod), roiMethod))
162        print("Raster Method ({}): {}".format(type(rasterMethod), rasterMethod))
163        print("Raster Paths ({}): {}".format(type(rasterPaths), rasterPaths))
164        print("Plot kwargs ({}): {}".format(type(plotkwargs), plotkwargs))
165
166
167    return labels, outDirectory, pathToPolygon, roiWidth, numRois, aoiLabel, polygonSource, \
168           roiMethod, rasterMethod, rasterPaths, plotkwargs

Loads parameters from a specified JSON file for configuring the lidar label builder.

Arguments:
  • filePath (str): Path to the JSON parameter file.
  • print_info (bool, optional): If True, prints the loaded parameters and their types. Defaults to True.
Returns:

tuple: A tuple containing the following elements: labels (list or None): List of label names or None if not provided. outDirectory (str or None): Path to the output directory or None if not provided. pathToPolygon (str or None): Path to the polygon file or None if not provided. roiWidth (int or None): Width of the region of interest (ROI) or None if not provided. numRois (int or None): Number of ROIs to generate or None if not provided. aoiLabel (str or None): Area of interest (AOI) label or None if not provided. polygonSource (str or None): Source of the polygon data or None if not provided. roiMethod (str or None): Method for ROI generation or None if not provided. rasterMethod (str or None): Method for raster processing or None if not provided. rasterPaths (list or None): List of raster file paths or None if not provided. plotkwargs (dict or None): Dictionary of keyword arguments for plotting or None if not provided.

def visualize_kwargs(rasterPaths: list, plotkwargs: dict):
171def visualize_kwargs(rasterPaths:list, plotkwargs:dict):
172    """Plots a visualization of the keyword arguments (kwargs) for the rasters. Can be useful to make sure keyword arguments are achieving the desired result.
173
174    Args:
175        rasterPaths (list): A list of raster paths corresponding to the kwargs
176        plotkwargs (dict): A dictionary of keyword arguments to plot the rasters.
177
178    Returns:
179        None
180    """
181
182    derivs = [llb.get_raster_as_grid(path)[0] for path in rasterPaths]
183    fig,axs = plt.subplots()
184    plt.subplots_adjust(right = 0.7)
185
186    # Plot the raster images
187    for deriv, kwarg in zip(derivs, plotkwargs):
188        axs.imshow(deriv, **kwarg)
189
190    # Show the plot
191    plt.show()
192    return None

Plots a visualization of the keyword arguments (kwargs) for the rasters. Can be useful to make sure keyword arguments are achieving the desired result.

Arguments:
  • rasterPaths (list): A list of raster paths corresponding to the kwargs
  • plotkwargs (dict): A dictionary of keyword arguments to plot the rasters.
Returns:

None

def rm_filepath_cols(gdf: geopandas.geodataframe.GeoDataFrame):
194def rm_filepath_cols(gdf:gpd.GeoDataFrame):
195    """Removes all columns with raster and image filepaths.
196
197    Args:
198        gdf (gpd.GeoDataFrame): Filepath to a roi geodataframe with filepaths to remove.
199
200    Returns:
201        gdf (gpd.GeoDataFrame): The modified geodataframe after filepath columns were removed.
202    """
203    rasterCols = [col for col in gdf.columns if col.startswith('rstr_clp')]
204    imageCols = [col for col in gdf.columns if col == 'imgPaths']
205   
206    for rasterCol in rasterCols:
207        gdf.drop(rasterCol, axis=1, inplace=True)
208
209    for imgCol in imageCols:
210        gdf.drop(imgCol, axis= 1, inplace=True)
211
212    return gdf

Removes all columns with raster and image filepaths.

Arguments:
  • gdf (gpd.GeoDataFrame): Filepath to a roi geodataframe with filepaths to remove.
Returns:

gdf (gpd.GeoDataFrame): The modified geodataframe after filepath columns were removed.