asp_plot.bundle_adjust#
Attributes#
Classes#
Visualize before/after camera position and orientation changes. |
|
Plot bundle adjustment results from GeoDataFrames. |
|
Read before/after camera geometry from an ASP bundle_adjust folder. |
|
Read and process bundle adjustment output files from ASP. |
Module Contents#
- class asp_plot.bundle_adjust.PlotBundleAdjustCameras(gdf, **kwargs)#
Bases:
asp_plot.utils.PlotterVisualize before/after camera position and orientation changes.
Consumes the GeoDataFrame from
ReadBundleAdjustCameras.get_camera_optimization_gdf()and renders per-camera bar rows (issues #95 and #43):Horizontal and vertical camera-center change (meters).
Roll / pitch / yaw orientation change (degrees), with the value printed on every bar. A single satellite cartoon beside the row is a legend for the body axes and the sense of each rotation (X = along-track, Y = across-track, Z = nadir); it is deliberately not scaled – the numbers carry the magnitude.
Only when ASP wrote
triangulation_offsets.txt(>= 3.6): the per-image median and mean change of the triangulated points (meters), i.e. what the camera change did on the ground. A solver can trade tens of meters of camera translation against millidegrees of rotation with almost no effect on the ground, so this row is the context for the first.
When a run changed nothing (e.g. an identity
--initial-transformused to recover the unadjusted cameras), the panels are still drawn with a “no camera change” note overlaid, rather than left as empty axes.- Parameters:
gdf (geopandas.GeoDataFrame) – Output of
ReadBundleAdjustCameras.get_camera_optimization_gdf().**kwargs – Forwarded to
asp_plot.utils.Plotter.
- plot_center_offset_bars(ax=None, save_dir=None, fig_fn=None, index_labels=False, legend_outside=False)#
Per-camera bars of horizontal and vertical camera-center change.
Uses ASP’s
camera_offsets.txtvalues when available (seeoffsets_from_asp); otherwise falls back to the horizontal/vertical components of the.adjusttranslation.- Parameters:
ax (matplotlib.axes.Axes or None, optional) – Axes to draw on. A new figure is created if None.
save_dir (str or None, optional) – If both are given (and a new figure was created), save the figure.
fig_fn (str or None, optional) – If both are given (and a new figure was created), save the figure.
index_labels (bool, optional) – Label cameras by number only (for stacked rows that share the camera order); default False prints
#n camera_id.legend_outside (bool, optional) – Place the legend to the right of the axes instead of inside.
- plot_orientation_bars(ax=None, save_dir=None, fig_fn=None, index_labels=False)#
Per-camera bars of the roll / pitch / yaw orientation change (degrees).
The
.adjustrotation is shown as signed intrinsic XYZ Euler angles (roll about the along-track X axis, pitch about across-track Y, yaw about nadir Z), colored to match_draw_satellite(), with the value printed on every bar so sub-millidegree changes stay readable.- Parameters:
ax (matplotlib.axes.Axes or None, optional) – Axes to draw on. A new figure is created if None.
save_dir (str or None, optional) – If both are given (and a new figure was created), save the figure.
fig_fn (str or None, optional) – If both are given (and a new figure was created), save the figure.
index_labels (bool, optional) – Label cameras by number only; default False prints
#n camera_id.
- plot_triangulation_offset_bars(ax=None, save_dir=None, fig_fn=None, index_labels=False, legend_outside=False)#
Per-camera bars of the triangulated-point change (meters).
From ASP’s
triangulation_offsets.txt(>= 3.6): for each input image, the median and mean distance between its initial and final triangulated points, with the point count printed above each pair.- Parameters:
ax (matplotlib.axes.Axes or None, optional) – Axes to draw on. A new figure is created if None.
save_dir (str or None, optional) – If both are given (and a new figure was created), save the figure.
fig_fn (str or None, optional) – If both are given (and a new figure was created), save the figure.
index_labels (bool, optional) – Label cameras by number only; default False prints
#n camera_id.legend_outside (bool, optional) – Place the legend to the right of the axes instead of inside.
- Raises:
ValueError – If the GeoDataFrame carries no
triangulation_offsets.txtvalues.
- summary_plot(save_dir=None, fig_fn=None)#
Combined summary: center-displacement bars, orientation bars, and – when
triangulation_offsets.txtwas written – triangulated-point change bars.The rows share the camera order; only the bottom row prints the camera ids. The orientation legend cartoon sits beside its row.
- gdf#
- property has_triangulation_offsets#
True when
triangulation_offsets.txtvalues are present.
- property is_identity#
True when every camera-center offset and rotation angle is zero.
- class asp_plot.bundle_adjust.PlotBundleAdjustFiles(geodataframes, **kwargs)#
Bases:
asp_plot.utils.PlotterPlot bundle adjustment results from GeoDataFrames.
This class extends the base Plotter class to provide specialized plotting functionality for bundle adjustment results. It can create visualizations of residuals, geodiff results, and other bundle adjustment outputs.
- geodataframes#
List of GeoDataFrames containing bundle adjustment data to plot
- Type:
list of geopandas.GeoDataFrame
Examples
>>> ba_reader = ReadBundleAdjustFiles('/path/to/asp', 'ba') >>> initial_gdf, final_gdf = ba_reader.get_initial_final_residuals_gdfs() >>> ba_plotter = PlotBundleAdjustFiles([initial_gdf, final_gdf], title="Bundle Adjustment Residuals") >>> ba_plotter.plot_n_gdfs(column_name="mean_residual_meters", cbar_label="Mean residual (m)")
- gdf_percentile_stats(gdf, column_name='mean_residual')#
Calculate percentile statistics for a GeoDataFrame column.
Computes the 25th, 50th, 84th, and 95th percentiles for a specified column in a GeoDataFrame.
- Parameters:
gdf (geopandas.GeoDataFrame) – GeoDataFrame to analyze
column_name (str, optional) – Column to calculate statistics for, default is “mean_residual”
- Returns:
List of statistics at the 25th, 50th, 84th, and 95th percentiles
- Return type:
Notes
These percentiles are commonly used in remote sensing and photogrammetry to assess error distributions. The 50th percentile is the median, while the 84th percentile is approximately equivalent to one standard deviation in a normal distribution.
- plot_n_gdfs(column_name='mean_residual', cbar_label='Mean residual (px)', clip_final=True, clim=None, common_clim=True, symm_clim=False, cmap='inferno', map_crs='EPSG:4326', save_dir=None, fig_fn=None, **ctx_kwargs)#
Plot multiple GeoDataFrames in a grid layout.
Creates a figure with multiple subplots, one for each GeoDataFrame in the geodataframes list. Each subplot shows the spatial distribution of the specified column values.
- Parameters:
column_name (str, optional) – Column to visualize, default is “mean_residual”
cbar_label (str, optional) – Label for the colorbar, default is “Mean residual (px)”
clip_final (bool, optional) – Whether to clip the final plot to prevent autoscaling, default is True
clim (tuple or None, optional) – Color limits as (min, max), default is None (auto)
common_clim (bool, optional) – Whether to use the same color limits for all plots, default is True
symm_clim (bool, optional) – Whether to use symmetric color limits, default is False
cmap (str, optional) – Matplotlib colormap name, default is “inferno”
map_crs (str, optional) – Coordinate reference system for plotting, default is “EPSG:4326”
save_dir (str or None, optional) – Directory to save the figure, default is None (don’t save)
fig_fn (str or None, optional) – Filename for the saved figure, default is None
**ctx_kwargs (dict, optional) – Additional keyword arguments for contextily basemap
- Returns:
Displays the plot and optionally saves it
- Return type:
None
Notes
Each subplot includes a text box showing statistics for the displayed data, including the number of points and percentile values. The layout automatically adjusts based on the number of GeoDataFrames to plot.
- geodataframes#
- class asp_plot.bundle_adjust.ReadBundleAdjustCameras(directory, bundle_adjust_directory, stem=None)#
Read before/after camera geometry from an ASP bundle_adjust folder.
Unlike
asp_plot.csm_camera.csm_camera_summary_plot, which requires the user to supply the original (pre-adjustment) camera files, this reader works directly on abundle_adjustoutput directory. It combines three self-contained products that ASP always writes there:*.adjust– the rigid adjustment (ECEF translation + rotation quaternion) applied to each camera. Per ASP’s convention a world point projects the same in the original camera asR * (P - C) + C + Tin the adjusted camera, so the translationTis the bulk camera-center shift (exact at the camera center for pixel (0, 0)).*.adjusted_state.json– the optimized CSM camera state, used to locate each camera center in space.*camera_offsets.txt(optional) – ASP’s authoritative per-camera horizontal and vertical camera-center change, in the local North-East-Down frame. When present it is used for the reported magnitudes (it also folds in the rotation lever-arm that the bulk translationTdoes not).
- Parameters:
Examples
>>> reader = ReadBundleAdjustCameras('/path/to/asp', 'ba') >>> gdf = reader.get_camera_optimization_gdf(map_crs=32616)
- get_camera_offsets_df()#
Read
*camera_offsets.txtinto a DataFrame if it exists.- Returns:
DataFrame with columns
image,horizontal_offset_m, andvertical_offset_m(one row per input image, in ASP’s order). Returns None if the file is not present (it is only written by recent ASP versions).- Return type:
pandas.DataFrame or None
- get_camera_optimization_gdf(map_crs=None, original_cameras_directory=None)#
Build a per-camera GeoDataFrame of before/after camera changes.
Discovery is driven by the
.adjustfiles, which ASP writes for every camera. Each camera’s absolute center comes from its.adjusted_state.json(WorldView/CSM and jitter runs) or, when that is absent (DigitalGlobe runs), from the original camera.xmlephemeris.- Parameters:
map_crs (int or None, optional) – EPSG code (e.g. a UTM zone) for the output geometry. If None, the geometry is returned in geographic coordinates (EPSG:4326). The east/north/up offsets do not depend on it.
original_cameras_directory (str or None, optional) – Directory holding the original
.xmlcameras, used only for DigitalGlobe runs that lack.adjusted_state.json. If None, the BA directory and its parent are searched automatically.
- Returns:
One row per camera, with geometry at the (projected) camera center and columns:
camera_id: camera filename stem (display label).t_east,t_north,t_up: ECEF translationTdecomposed into the local ENU frame (meters).t_horizontal: horizontal magnitude ofT(meters).adj_roll,adj_pitch,adj_yaw: the.adjustrotation as intrinsic XYZ Euler angles (degrees).horizontal_offset_m,vertical_offset_m: ASP’s reported camera-center change fromcamera_offsets.txtwhen available, otherwise filled fromt_horizontal/t_up.offsets_from_asp: True if the offsets came fromcamera_offsets.txt, False if derived fromT.tri_mean_m,tri_median_m,tri_count: per-image mean and median change of the triangulated points (meters) and the point count, fromtriangulation_offsets.txt(ASP >= 3.6); NaN when that report is absent.
- Return type:
geopandas.GeoDataFrame
- Raises:
ValueError – If no
.adjustfiles are found in the directory.
- get_triangulation_offsets_df()#
Read
*triangulation_offsets.txtinto a DataFrame if it exists.ASP >= 3.6 writes this report (“Changes in triangulated points” in the bundle_adjust docs): for each input image, the mean, median, and count of the distances (ECEF, meters) between the initial triangulated points (after any initial adjustment or alignment transform, before any DEM constraint) and the final ones after optimization. It is the ground effect of the camera change, which the camera-center offsets alone do not show.
- Returns:
Columns
image,tri_mean_m,tri_median_m,tri_count(one row per input image, in ASP’s order), or None when the file is absent (older ASP versions).- Return type:
pandas.DataFrame or None
- static read_adjust_file(adjust_path)#
Parse an ASP
.adjustfile.- Parameters:
adjust_path (str) – Path to a
.adjustfile.- Returns:
(translation, rotation)wheretranslationis a length-3 numpy array of the ECEF camera-center shift in meters androtationis ascipy.spatial.transform.Rotation.- Return type:
Notes
The first line holds the translation
x y z(meters); the second holds the rotation quaternion in ASP’sw x y zorder, which is reordered to thex y z worder that SciPy expects.
- bundle_adjust_directory#
- directory#
- full_directory#
- stem = None#
- class asp_plot.bundle_adjust.ReadBundleAdjustFiles(directory, bundle_adjust_directory, stem=None)#
Read and process bundle adjustment output files from ASP.
This class provides functionality to read and process the outputs from ASP’s bundle_adjust tool, including residual point maps, map-projected residuals, and triangulation uncertainty. It can also generate geodiff files to compare bundle adjustment results with reference DEMs.
Examples
>>> ba_reader = ReadBundleAdjustFiles('/path/to/asp', 'ba') >>> initial_gdf, final_gdf = ba_reader.get_initial_final_residuals_gdfs() >>> geodiff_initial, geodiff_final = ba_reader.get_initial_final_geodiff_gdfs() >>> mapproj_gdf = ba_reader.get_mapproj_residuals_gdf()
- generate_geodiff(path)#
Generate geodiff file comparing residuals to a reference DEM.
Uses ASP’s geodiff tool to calculate height differences between bundle adjustment residual points and a reference DEM.
- Parameters:
path (str) – Path to the bundle adjustment residual CSV file
Notes
The geodiff output file will be saved with the same name as the input file, but with “-diff.csv” appended. This method requires the geodiff tool from ASP and a reference DEM that was used in the bundle adjustment process.
- get_csv_paths(geodiff_files=False)#
Get paths to bundle adjustment residual CSV files.
Finds the paths to the initial and final residual point map CSV files from bundle adjustment. Can optionally find or generate geodiff files comparing these residuals to a reference DEM.
- Parameters:
geodiff_files (bool, optional) – Whether to return paths to geodiff files instead of regular residual CSV files, default is False
- Returns:
Paths to initial and final residual CSV files or geodiff files
- Return type:
- Raises:
ValueError – If required bundle adjustment CSV files cannot be found
Notes
If geodiff_files is True and the files don’t exist, this method will attempt to generate them using the geodiff ASP tool.
- get_geodiff_gdf(csv_path)#
Read geodiff output into a GeoDataFrame.
Reads a geodiff output CSV file and converts it to a GeoDataFrame with appropriate geometry.
- Parameters:
csv_path (str) – Path to the geodiff CSV file
- Returns:
GeoDataFrame containing geodiff data with point geometry
- Return type:
geopandas.GeoDataFrame
Notes
The returned GeoDataFrame has a ‘filename’ attribute set to the base name of the input file. The main data column is ‘height_diff_meters’, which contains the height differences between residual points and the reference DEM.
- get_initial_final_geodiff_gdfs()#
Get both initial and final geodiff results as GeoDataFrames.
A convenience method that combines get_csv_paths() and get_geodiff_gdf() to obtain both initial and final geodiff results.
- Returns:
GeoDataFrames containing initial and final geodiff data
- Return type:
tuple of geopandas.GeoDataFrame
Notes
This is a convenience method that calls get_csv_paths(geodiff_files=True) and get_geodiff_gdf() for both initial and final geodiff files. If the geodiff files don’t exist, they will be generated using generate_geodiff().
- get_initial_final_residuals_gdfs(residuals_in_meters=True)#
Get both initial and final residuals as GeoDataFrames.
A convenience method that combines get_csv_paths() and get_residuals_gdf() to obtain both initial and final bundle adjustment residuals.
- Parameters:
residuals_in_meters (bool, optional) – Whether to convert residuals from pixels to meters using the mean GSD from the stereopair metadata, default is True
- Returns:
GeoDataFrames containing initial and final residual data
- Return type:
tuple of geopandas.GeoDataFrame
Notes
This is a convenience method that calls get_csv_paths() and get_residuals_gdf() for both initial and final residual files.
- get_mapproj_residuals_gdf()#
Get map-projected residuals as a GeoDataFrame.
Reads the map-projected match offsets file produced by bundle_adjust and converts it to a GeoDataFrame.
- Returns:
GeoDataFrame containing map-projected residual data
- Return type:
geopandas.GeoDataFrame
- Raises:
ValueError – If the map-projected match offsets file cannot be found
Notes
The main data column is ‘mapproj_ip_dist_meters’, which contains the distance between matched interest points in the map-projected images after bundle adjustment.
- get_propagated_triangulation_uncert_df()#
Get propagated triangulation uncertainty as a DataFrame.
Reads the triangulation uncertainty file produced by bundle_adjust, which contains statistics on the expected horizontal and vertical error in the DEM.
- Returns:
DataFrame containing triangulation uncertainty data
- Return type:
pandas.DataFrame
- Raises:
ValueError – If the triangulation uncertainty file cannot be found
Notes
The triangulation uncertainty file contains statistics on how the bundle adjustment residuals propagate to uncertainty in triangulation. This is an estimate of the final DEM error.
- get_residuals_gdf(csv_path, residuals_in_meters=True)#
Read bundle adjustment residuals into a GeoDataFrame.
Reads a bundle adjustment residuals CSV file and converts it to a GeoDataFrame with appropriate geometry. Can optionally convert residuals from pixels to meters.
- Parameters:
- Returns:
GeoDataFrame containing residual data with point geometry
- Return type:
geopandas.GeoDataFrame
Notes
The returned GeoDataFrame has a ‘filename’ attribute set to the base name of the input file. If residuals_in_meters is True, a new column ‘mean_residual_meters’ will be added with pixel residuals converted to meters.
- bundle_adjust_directory#
- directory#
- full_directory#
- stem_glob#
- asp_plot.bundle_adjust.logger#