v.photo.geometry
Recovers acquisition geometry from aerial photos.
Derives camera pose, ground sample distance, image footprints, flight path, and overlap from a directory of aerial photos and an elevation model.
v.photo.geometry [-f] input=name elevation=name [sensor=width,height] [time_gap=float] [overlap=name] [format=name] [footprints=name] [stations=name] [path=name] [overlap_density=name] [--overwrite] [--verbose] [--quiet] [--qq] [--ui]
Example:
v.photo.geometry input=name elevation=name footprints=name
grass.tools.Tools.v_photo_geometry(input, elevation, sensor=None, time_gap=30.0, overlap=None, format="csv", footprints=None, stations=None, path=None, overlap_density=None, flags=None, overwrite=None, verbose=None, quiet=None, superquiet=None)
Example:
tools = Tools()
tools.v_photo_geometry(input="name", elevation="name", format="json", footprints="name")
This grass.tools API is experimental in version 8.5 and expected to be stable in version 8.6.
grass.script.parse_command("v.photo.geometry", input, elevation, sensor=None, time_gap=30.0, overlap=None, format="csv", footprints=None, stations=None, path=None, overlap_density=None, flags=None, overwrite=None, verbose=None, quiet=None, superquiet=None)
Example:
gs.parse_command("v.photo.geometry", input="name", elevation="name", format="json", footprints="name")
Parameters
input=name [required]
Directory of aerial photos
elevation=name [required]
Name of input elevation raster map
sensor=width,height
Camera sensor dimensions in mm (width,height)
Overrides the sensor size estimated from image metadata
time_gap=float
Time gap in seconds that splits a flight into separate segments
Images separated by more than this gap do not inform each other's estimated heading
Default: 30.0
overlap=name
Output file for per-image overlap statistics
Use "-" to write to standard output
format=name
Format of the overlap statistics output (with overlap=)
Allowed values: plain, csv, json
Default: csv
plain: Human readable text
csv: CSV table
json: JSON object
footprints=name
Output vector map of image footprint areas
stations=name
Output 3D vector map of camera station points
path=name
Output 3D vector map of the estimated flight path
overlap_density=name
Output raster map of the number of overlapping images per cell
-f
Flat-ground footprints that ignore terrain relief (faster)
--overwrite
Allow output files to overwrite existing files
--help
Print usage summary
--verbose
Verbose module output
--quiet
Quiet module output
--qq
Very quiet module output
--ui
Force launching GUI dialog
input : str, required
Directory of aerial photos
Used as: input, dir, name
elevation : str | np.ndarray, required
Name of input elevation raster map
Used as: input, raster, name
sensor : tuple[float, float] | list[float] | str, optional
Camera sensor dimensions in mm (width,height)
Overrides the sensor size estimated from image metadata
Used as: width,height
time_gap : float, optional
Time gap in seconds that splits a flight into separate segments
Images separated by more than this gap do not inform each other's estimated heading
Default: 30.0
overlap : str, optional
Output file for per-image overlap statistics
Use "-" to write to standard output
Used as: output, file, name
format : str, optional
Format of the overlap statistics output (with overlap=)
Used as: name
Allowed values: plain, csv, json
plain: Human readable text
csv: CSV table
json: JSON object
Default: csv
footprints : str, optional
Output vector map of image footprint areas
Used as: output, vector, name
stations : str, optional
Output 3D vector map of camera station points
Used as: output, vector, name
path : str, optional
Output 3D vector map of the estimated flight path
Used as: output, vector, name
overlap_density : str | type(np.ndarray) | type(np.array) | type(gs.array.array), optional
Output raster map of the number of overlapping images per cell
Used as: output, raster, name
flags : str, optional
Allowed values: f
f
Flat-ground footprints that ignore terrain relief (faster)
overwrite : bool, optional
Allow output files to overwrite existing files
Default: None
verbose : bool, optional
Verbose module output
Default: None
quiet : bool, optional
Quiet module output
Default: None
superquiet : bool, optional
Very quiet module output
Default: None
Returns:
result : grass.tools.support.ToolResult | np.ndarray | tuple[np.ndarray] | None
If the tool produces text as standard output, a ToolResult object will be returned. Otherwise, None will be returned. If an array type (e.g., np.ndarray) is used for one of the raster outputs, the result will be an array and will have the shape corresponding to the computational region. If an array type is used for more than one raster output, the result will be a tuple of arrays.
Raises:
grass.tools.ToolError: When the tool ended with an error.
input : str, required
Directory of aerial photos
Used as: input, dir, name
elevation : str, required
Name of input elevation raster map
Used as: input, raster, name
sensor : tuple[float, float] | list[float] | str, optional
Camera sensor dimensions in mm (width,height)
Overrides the sensor size estimated from image metadata
Used as: width,height
time_gap : float, optional
Time gap in seconds that splits a flight into separate segments
Images separated by more than this gap do not inform each other's estimated heading
Default: 30.0
overlap : str, optional
Output file for per-image overlap statistics
Use "-" to write to standard output
Used as: output, file, name
format : str, optional
Format of the overlap statistics output (with overlap=)
Used as: name
Allowed values: plain, csv, json
plain: Human readable text
csv: CSV table
json: JSON object
Default: csv
footprints : str, optional
Output vector map of image footprint areas
Used as: output, vector, name
stations : str, optional
Output 3D vector map of camera station points
Used as: output, vector, name
path : str, optional
Output 3D vector map of the estimated flight path
Used as: output, vector, name
overlap_density : str, optional
Output raster map of the number of overlapping images per cell
Used as: output, raster, name
flags : str, optional
Allowed values: f
f
Flat-ground footprints that ignore terrain relief (faster)
overwrite : bool, optional
Allow output files to overwrite existing files
Default: None
verbose : bool, optional
Verbose module output
Default: None
quiet : bool, optional
Quiet module output
Default: None
superquiet : bool, optional
Very quiet module output
Default: None
DESCRIPTION
v.photo.geometry recovers acquisition geometry from a directory of aerial photos whose flight metadata is incomplete. From the image metadata and an elevation model it derives the camera positions and orientations, above-ground altitude, ground sample distance (GSD), and image footprints, and writes them as vector maps:
- footprints: one 3D area per image, ray-traced onto the elevation model (or projected onto flat ground with -f), with the full set of per-image attributes.
- stations: 3D points at the camera positions with the same attributes (yaw, pitch, roll, altitude, AGL, GSD, sensor size, camera make, model, lens, and body serial number).
- path: one 3D line per camera body and flight segment, reconstructing the flown track. Curved tracks (typical for manned sorties) are smoothed with a Catmull-Rom spline; grid patterns (typical for UAS mapping missions) are kept as straight legs.
- overlap: a per-image forward and side overlap report written as
plain text, CSV, or JSON (format), to a file or to standard
output (
overlap=-). - overlap_density: a raster counting the number of images that cover each cell, colored with the magma color table.
At least one of these outputs is required.
The tool was written for imagery that arrives without flight logs, such as post-disaster reconnaissance photos, where flight planning parameters have to be reconstructed from the images themselves.

Figure: Recovered image footprints (yellow), camera stations (dots),
and flight path (red) of a 215-image DJI Phantom 4 Pro mission over
the Lake Wheeler field laboratory, NCSU, on the same-day
orthomosaic.
NOTES
ExifTool requirement
Image metadata is read with ExifTool
(version 12.0 or newer), which must be installed separately;
g.extension cannot install it. On Debian/Ubuntu install
libimage-exiftool-perl, on Fedora perl-Image-ExifTool, on macOS
brew install exiftool. JPEG, TIFF, and DNG images are read,
recursively below the input directory.
The Python package pyproj is also required and is not installed by
g.extension: pip install pyproj.
Sensor size
The physical sensor size drives the GSD and footprint scale. It is resolved in this order:
- the sensor option (width,height in mm), when given;
- the EXIF focal plane resolution tags;
- the 35 mm equivalence scale factor, which ExifTool derives from its internal camera database when the focal plane tags are absent (the common case for drones).
The source used is reported per camera model. Estimated values inherit the precision of the source tags and are commonly a few percent larger than the true sensor dimensions, which propagates linearly into GSD and footprint size. When the sensor dimensions are known, pass them with sensor for exact results. The reported GSD assumes a nadir view; oblique imagery has a varying scale across the frame.
Orientation and heading
Yaw, pitch, and roll are taken from the maker orientation tags
(for example DJI FlightYawDegree, GimbalPitchDegree,
GimbalRollDegree, or GPSImgDirection) when present. Without them
the camera is assumed nadir and the heading of each image is estimated
from the GPS track: images are grouped per camera body (serial
number), split into flight segments wherever consecutive exposures are
more than time_gap seconds apart, and the heading is interpolated
along each segment. Multi-camera rigs that fire simultaneously against
a single GPS record are handled by this per-body grouping.
Footprints and the computational region
Footprint corners are ray-traced from the camera through the sensor corners onto the elevation raster, read once at the current computational region resolution. The region must cover the photo block. The GPS altitude and the elevation model must share a vertical datum; a datum offset shifts the AGL and scales every footprint. There is no offset option, so adjust the elevation model or the image altitudes beforehand when the datums differ. DEM cells that are NULL are treated as holes: corner rays pass through them and footprints whose corners cannot reach ground are skipped with a warning. With -f the terrain is ignored and each footprint is a flat rectangle at the ground elevation below the camera, rotated by yaw only; pitch and roll are not applied. This is faster but degrades with relief and off-nadir angles. Overlap statistics and the density raster treat each footprint as a convex quadrilateral; on very steep relief the terrain-draped corners can violate that assumption slightly.
Overlap statistics
Forward overlap is the fraction of an image footprint covered by the next image of the same camera within the same flight segment. Side overlap is the maximum overlap with any non-consecutive image of the same camera, which captures adjacent flight lines without reconstructing the line layout. Images without a footprint report empty values.
The overlap_density raster counts the images covering each cell of the current computational region, computed from the footprint polygons in memory. Cells covered by no image are NULL. The magma color table is applied to the output.

Figure: Overlap density of the same mission; the double-grid
crosshatch peaks at 12 images per cell.
Footprints of adjacent images overlap by design, so the footprints map contains overlapping areas; GRASS reports duplicate centroids when building its topology, which is expected here.
EXAMPLES
Recover the full geometry of a photo block:
g.region raster=dtm -p
v.photo.geometry input=/data/mission01 elevation=dtm \
footprints=m01_footprints stations=m01_stations \
path=m01_path overlap=m01_overlap.csv \
overlap_density=m01_density
Known camera, flat terrain, overlap only, printed to the terminal:
v.photo.geometry -f input=/data/mission01 elevation=dtm \
sensor=36.0,24.0 overlap=- format=plain
SEE ALSO
g.region, i.ortho.photo, v.info
REFERENCES
- ExifTool, Phil Harvey
-
White, C.T., Regmi, P., Reckling, W., Mitasova, H. (2026). Volumetric Change Detection with SfM Photogrammetry from Rapid-Response Aerial Imagery after Hurricane Helene. Remote Sensing (in review). The tool's camera-geometry recovery was developed for the flight diagnostics in this study.
AUTHORS
Corey T. White, NCSU GeoForAll Lab
SOURCE CODE
Available at: v.photo.geometry source code
(history)
Latest change: Wednesday Sep 16 04:51:13 2026 in commit 2116693