Weak Supervision Pipeline¶
Deterministic weak labeling algorithm for generating silver training data from raw LiDAR. Implements Phases A (download & weak supervise) and B (preprocess) of the pipeline.
See also
- Architecture: Phase A for the RANSAC plane fitting algorithm design
- Data Pipeline: Step 1 for the step-by-step data flow
src.weak_supervision ¶
Weak supervision algorithm for bridge LiDAR classification.
Core algorithm for determining linear vs. complex bridges using RANSAC plane fitting, linearity checking, convex hull masking, and Z-distance heuristic labeling.
FailureReason ¶
Bases: Enum
Why a bridge failed weak supervision.
Most values are set by process_bridge() itself. TIMEOUT is set by the caller's subprocess wrapper - included here so the consumer can dispatch on all failure reasons with a single enum.
NO_POINTS
class-attribute
instance-attribute
¶
No LiDAR points found in the EPT source for this bridge geometry.
RANSAC_INSUFFICIENT
class-attribute
instance-attribute
¶
Too few points (or unique points) to run RANSAC.
RANSAC_FAILED
class-attribute
instance-attribute
¶
RANSAC fitting raised an exception.
RANSAC_LOW_INLIERS
class-attribute
instance-attribute
¶
RANSAC converged but found fewer inliers than min_ransac_inliers.
HULL_FAILED
class-attribute
instance-attribute
¶
Convex hull of RANSAC inliers could not be computed.
HIGH_RMSE
class-attribute
instance-attribute
¶
Inlier RMSE exceeded max_rmse threshold (poor plane fit).
CURVED
class-attribute
instance-attribute
¶
Bridge skeleton exceeded the linearity deviation threshold (arch or sag).
EXCEPTION
class-attribute
instance-attribute
¶
Unhandled exception during processing.
TIMEOUT
class-attribute
instance-attribute
¶
Bridge exceeded the wall-clock timeout (set by caller, not process_bridge).
BridgeProcessingConfig
dataclass
¶
Centralized configuration for bridge processing pipeline. Contains all magic numbers, thresholds, and parameters.
from_dict
classmethod
¶
from_dict(d: Dict[str, Any]) -> BridgeProcessingConfig
Create config from dictionary (for multiprocessing serialization).
to_dict ¶
Convert config to dictionary (for multiprocessing serialization).
check_bridge_linearity ¶
check_bridge_linearity(xy_points: ndarray, z_points: ndarray, config: BridgeProcessingConfig, deviation_threshold: Optional[float] = None) -> Tuple[bool, float]
Slices the bridge into bins, finds the median Z for each bin (the 'skeleton'), and checks if that skeleton deviates from a straight line.
Parameters:
| Name | Type | Description | Default |
|---|---|---|---|
xy_points
|
ndarray
|
XY coordinates of points |
required |
z_points
|
ndarray
|
Z coordinates of points |
required |
config
|
BridgeProcessingConfig
|
BridgeProcessingConfig instance |
required |
deviation_threshold
|
Optional[float]
|
Optional threshold override (defaults to config.linearity_deviation_threshold) |
None
|
Returns:
| Type | Description |
|---|---|
Tuple[bool, float]
|
Tuple of (is_curved: bool, max_deviation: float) |
fit_ransac_from_arrays ¶
fit_ransac_from_arrays(arrays: ndarray, config: BridgeProcessingConfig) -> Dict[str, Any]
Run RANSAC plane fitting on in-memory point arrays (e.g. from LAZ). Used for visualization; does not run linearity or RMSE rejection.
Parameters:
| Name | Type | Description | Default |
|---|---|---|---|
arrays
|
ndarray
|
Structured array with fields X, Y, Z, Classification (PDAL-style). |
required |
config
|
BridgeProcessingConfig
|
BridgeProcessingConfig instance. |
required |
Returns:
| Type | Description |
|---|---|
Dict[str, Any]
|
On success: dict with success=True, x_center, y_center, X_local, Y_local, Z, coef_x, coef_y, intercept, inlier_mask_full, lateral_mask, dist_from_plane_all. |
Dict[str, Any]
|
On failure: dict with success=False, error=str. |
process_bridge ¶
process_bridge(ept_url: str, bridge_geometry: Any, config: BridgeProcessingConfig, buffer_meters: Optional[float] = None) -> Optional[Dict[str, Any]]
Process a single bridge with weak supervision rules.
Parameters:
| Name | Type | Description | Default |
|---|---|---|---|
ept_url
|
str
|
EPT URL for the lidar source |
required |
bridge_geometry
|
Any
|
Shapely geometry of the bridge |
required |
config
|
BridgeProcessingConfig
|
BridgeProcessingConfig instance with all parameters |
required |
buffer_meters
|
Optional[float]
|
Buffer size in meters (defaults to config.default_buffer_meters) |
None
|
Returns:
| Type | Description |
|---|---|
Optional[Dict[str, Any]]
|
Dictionary with keys: 'success' (bool), 'arrays' (numpy structured array), |
Optional[Dict[str, Any]]
|
'original_arrays' (numpy structured array), 'rmse' (float), 'deviation' (float), |
Optional[Dict[str, Any]]
|
'error' (str). Returns None if processing failed. |
src.preprocess_bridges ¶
Bridge Normalization Preprocessing Script.
Processes bridge lidar data from HUC-organized silver_training directory, normalizes coordinates and classifications per bridge, and saves normalized outputs while preserving HUC folder structure.
Example
process_laz_file ¶
process_laz_file(filepath: Path, output_dir: Path, skip_existing: bool = False) -> Tuple[bool, Optional[str]]
Process a single LAZ file with normalization and class remapping.
Parameters:
| Name | Type | Description | Default |
|---|---|---|---|
filepath
|
Path
|
Path to input LAZ file |
required |
output_dir
|
Path
|
Output directory for .npy and .json files |
required |
skip_existing
|
bool
|
If True, skip files that already have output files |
False
|
Returns:
| Type | Description |
|---|---|
Tuple[bool, Optional[str]]
|
Tuple of (success: bool, error_message: Optional[str]) |
process_laz_file_worker ¶
Worker function for multiprocessing that processes a single LAZ file.
Parameters:
| Name | Type | Description | Default |
|---|---|---|---|
args
|
Tuple[Path, Path, bool]
|
Tuple of (filepath, output_dir, skip_existing) |
required |
Returns:
| Type | Description |
|---|---|
Tuple[bool, Optional[str], Path, bool]
|
Tuple of (success: bool, error_message: Optional[str], filepath: Path, was_skipped: bool) |
process_huc_folder ¶
process_huc_folder(huc_dir: Path, output_base_dir: Path, skip_existing: bool = False, show_progress: bool = True, num_workers: Optional[int] = None) -> Dict[str, Any]
Process all LAZ files in a HUC folder.
Parameters:
| Name | Type | Description | Default |
|---|---|---|---|
huc_dir
|
Path
|
Input HUC directory containing LAZ files |
required |
output_base_dir
|
Path
|
Base output directory (will create huc_id subfolder) |
required |
skip_existing
|
bool
|
If True, skip files that already have output files |
False
|
show_progress
|
bool
|
If True, show progress bar (if tqdm available) |
True
|
num_workers
|
Optional[int]
|
Number of parallel workers (None = use CPU count, 1 = sequential) |
None
|
Returns:
| Type | Description |
|---|---|
Dict[str, Any]
|
Dictionary with processing statistics |
CLI Arguments¶
| Argument | Default | Description |
|---|---|---|
--input-dir |
./data/ml-data/silver_training |
Input directory with HUC-organized LAZ/LAS files |
--output-dir |
./data/ml-data/silver_training_normalized |
Output directory for .npy and .json |
--skip-existing |
False | Skip if .npy + .json already exist |
--hucs |
all | Specific HUC IDs to process |
--workers |
CPU count | Parallel workers per HUC |
--no-progress |
False | Disable progress bars |
Usage¶
# Process all HUCs
python src/preprocess_bridges.py
# Process specific HUCs with resume
python src/preprocess_bridges.py --hucs 02050206 03070101 --skip-existing
src.download_and_weak_supervise_hucs ¶
HUC-based Bridge Processing Pipeline.
Pipeline for processing bridge lidar data organized by Hydrologic Unit Code (HUC) regions. This script finds intersecting lidar sources, applies ground filtering and weak supervision rules to generate labeled training data for machine learning models.
Input directory structure:
hucs_dir/
{huc_id}/
osm_bridges_lidar_subset__{huc_id}.gpkg
Example
# Process all bridges in all HUCs with default settings
python src/download_and_weak_supervise_hucs.py
# Process bridges in specific HUC regions
python src/download_and_weak_supervise_hucs.py --hucs 01010001 01010002
# Resume processing (skip already processed)
python src/download_and_weak_supervise_hucs.py --skip-existing
# Custom directories and settings
python src/download_and_weak_supervise_hucs.py \
--hucs-dir ./data/osm/hucs \
--source-dir ./data/ml-data/source \
--silver-dir ./data/ml-data/silver_training \
--buffer 15.0 --workers 8
LidarSourceFinder ¶
Class for finding intersecting lidar sources. Loads lidar_resources.geojson and builds spatial index for fast queries.
DataManager ¶
Manages file I/O and directory organization.
get_paths ¶
Get file paths for source and silver training outputs.
Returns:
| Type | Description |
|---|---|
Tuple[str, str]
|
Tuple of (source_path, silver_path) as strings for pdal compatibility |
ensure_directories ¶
Create output directories for a HUC if they don't exist.
file_exists ¶
Check if both source and silver files already exist.
no_points_sentinel_path ¶
Path to sentinel file indicating no lidar points were found for this bridge/source.
no_points_sentinel_exists ¶
Check if a no-points sentinel exists (skip on restart with --skip-existing).
write_no_points_sentinel ¶
Write sentinel so this (huc_id, osmid, source_name) is skipped on restart with --skip-existing.
timeout_sentinel_path ¶
Path to sentinel file indicating this bridge timed out during processing.
timeout_sentinel_exists ¶
Check if a timeout sentinel exists (skip on restart with --skip-existing).
write_timeout_sentinel ¶
Write sentinel so this timed-out bridge is skipped on restart with --skip-existing.
save_files ¶
save_files(original_arrays: Any, modified_arrays: Any, huc_id: str, osmid: str, source_name: str, config: BridgeProcessingConfig) -> bool
Save both source and silver training files.
Parameters:
| Name | Type | Description | Default |
|---|---|---|---|
original_arrays
|
Any
|
Arrays after SMRF, before weak supervision classification |
required |
modified_arrays
|
Any
|
Arrays after weak supervision classification |
required |
config
|
BridgeProcessingConfig
|
BridgeProcessingConfig instance |
required |
Returns:
| Type | Description |
|---|---|
bool
|
True if successful, False otherwise |
save_source_only ¶
save_source_only(original_arrays: Any, huc_id: str, osmid: str, source_name: str, config: BridgeProcessingConfig) -> bool
Save only the source training file (e.g. when silver is rejected).
Parameters:
| Name | Type | Description | Default |
|---|---|---|---|
original_arrays
|
Any
|
Point arrays to write (e.g. raw or SMRF output) |
required |
huc_id
|
str
|
HUC identifier |
required |
osmid
|
str
|
OSM bridge ID |
required |
source_name
|
str
|
Source name for filename |
required |
config
|
BridgeProcessingConfig
|
BridgeProcessingConfig instance |
required |
Returns:
| Type | Description |
|---|---|
bool
|
True if successful, False otherwise |
BridgeProcessor ¶
Main orchestrator class for processing bridges across HUCs.
find_huc_files ¶
Find all HUC GPKG files.
Returns:
| Type | Description |
|---|---|
List[Tuple[str, str]]
|
List of (huc_id, file_path) tuples where file_path is a string |
load_bridges ¶
Load bridges from GPKG file, optionally filtered by OSM IDs.
generate_tasks ¶
generate_tasks(huc_ids: Optional[List[str]] = None, osm_ids: Optional[List[str]] = None) -> List[TaskTuple]
Generate all (bridge, source) tasks for processing in parallel per HUC.
Returns:
| Type | Description |
|---|---|
List[TaskTuple]
|
List of task tuples for process_bridge_source function |
process ¶
process(huc_ids: Optional[List[str]] = None, osm_ids: Optional[List[str]] = None, show_progress: bool = True, shuffle_seed: Optional[int] = None) -> None
Main processing method with parallel execution.
process_bridge_source ¶
Process a single (bridge, source) pair. This function is called by worker processes.
Parameters:
| Name | Type | Description | Default |
|---|---|---|---|
args
|
TaskTuple
|
Tuple of (huc_id, osmid, bridge_geometry_wkt, source_url, source_name, buffer_meters, source_dir, silver_dir, config_dict) |
required |
Returns:
| Type | Description |
|---|---|
Dict[str, Any]
|
Dictionary with keys: 'success' (bool), 'huc_id' (str), 'osmid' (str), |
Dict[str, Any]
|
'source_name' (str), 'error' (Optional[str]), 'rmse' (Optional[float]), |
Dict[str, Any]
|
'deviation' (Optional[float]), 'skipped' (Optional[bool]) |
CLI Arguments¶
| Argument | Default | Description |
|---|---|---|
--hucs-dir |
./data/osm/hucs |
Directory containing per-HUC GPKG files |
--source-dir |
./data/ml-data/source |
Output directory for raw LAZ downloads |
--silver-dir |
./data/ml-data/silver_training |
Output directory for weak-supervised LAZ |
--lidar-resources |
./data/usgs_entwine/lidar_resources.geojson |
USGS EPT source index |
--hucs |
all | Space-separated HUC IDs to process |
--osm-ids |
all | Space-separated OSM IDs to process |
--buffer |
10.0 | Bridge geometry buffer in meters |
--workers |
CPU count | Parallel worker processes |
--skip-existing |
False | Skip bridges already processed |
--bridge-timeout |
300 | Seconds before a hung bridge is skipped |
--shuffle-seed |
None | Seed for task shuffle (reproducible order for debugging) |
--results-csv |
None | Write per-bridge results to this CSV path |
--log-dir |
./logs |
Directory for processing logs |
--no-progress |
False | Disable tqdm progress bars |
Usage¶
# Process all HUCs
python src/download_and_weak_supervise_hucs.py
# Process specific HUCs with resume
python src/download_and_weak_supervise_hucs.py \
--hucs 02050206 03070101 \
--skip-existing
# Custom output paths and timeout
python src/download_and_weak_supervise_hucs.py \
--source-dir /data/source \
--silver-dir /data/silver \
--bridge-timeout 600 \
--results-csv results.csv
src.download_and_weak_supervise_demo ¶
Bridge Weak Supervision Demo Script.
Standalone demo script that processes a small set of target bridges from a single lidar dataset. Uses the shared weak_supervision module for the core algorithm (RANSAC plane fitting, linearity checks, Z-distance classification).
This script is a simplified, single-dataset version of the full HUC-based pipeline (download_and_weak_supervise_hucs.py). It is useful for quickly testing the weak supervision algorithm on a handful of known bridges.