Skip to content

Commit 5acfc6c

Browse files
Add srcnodata param to fly_thumb_georef()
Masks black camera frame border pixels via -srcnodata in addition to warp fill. Default "0" is acceptable for thumbnails; set NULL for full-res scans where shadow detail matters. Relates to #22 Co-Authored-By: Claude Opus 4.6 <[email protected]>
1 parent 96a6e89 commit 5acfc6c

File tree

2 files changed

+61
-22
lines changed

2 files changed

+61
-22
lines changed

R/fly_thumb_georef.R

Lines changed: 36 additions & 13 deletions
Original file line numberDiff line numberDiff line change
@@ -11,6 +11,12 @@
1111
#' @param dest_dir Directory for output GeoTIFFs. Created if it does not
1212
#' exist.
1313
#' @param overwrite If `FALSE` (default), skip files that already exist.
14+
#' @param srcnodata Source nodata value passed to GDAL warp. Black pixels
15+
#' matching this value are treated as transparent (alpha=0 for RGB,
16+
#' nodata for grayscale). Default `"0"` masks camera frame borders and
17+
#' film holder edges at the cost of losing real black pixels — acceptable
18+
#' for thumbnails but may need adjustment for full-resolution scans.
19+
#' Set to `NULL` to disable source nodata detection entirely.
1420
#' @return A tibble with columns `airp_id`, `source`, `dest`, and `success`.
1521
#'
1622
#' @details
@@ -19,14 +25,21 @@
1925
#' translates the image with GCPs then warps to the target CRS using
2026
#' bilinear resampling.
2127
#'
22-
#' **Nodata handling:** Warping a rectangular thumbnail into a rotated
23-
#' footprint creates fill pixels outside the source frame. Band count is
24-
#' read from each file header to choose the masking strategy: RGB
25-
#' thumbnails (3+ bands) get an alpha band (`-dstalpha`) so fill areas
26-
#' are transparent; grayscale thumbnails (1 band) use nodata=0. This
27-
#' only masks GDAL warp fill — black camera frame borders within the
28-
#' original image (from film holder edges, fiducial marks, or scanning
29-
#' artifacts) are preserved as valid pixels.
28+
#' **Nodata handling:** Two sources of unwanted black pixels are masked:
29+
#'
30+
#' 1. **Warp fill** — GDAL creates black pixels outside the rotated source
31+
#' frame. RGB thumbnails get an alpha band (`-dstalpha`); grayscale use
32+
#' `dstnodata=0`.
33+
#' 2. **Camera frame borders** — film holder edges, fiducial marks, and
34+
#' scanning artifacts produce black (value 0) pixels within the source
35+
#' image. The `srcnodata` parameter (default `"0"`) tells GDAL to treat
36+
#' these as transparent before warping.
37+
#'
38+
#' **Tradeoff:** `srcnodata = "0"` also masks real black pixels (deep
39+
#' shadows). At thumbnail resolution (~1250x1250) this is acceptable —
40+
#' shadow detail is minimal. For full-resolution scans where shadow
41+
#' detail matters, set `srcnodata = NULL` and handle frame masking
42+
#' downstream (e.g., circle detection).
3043
#'
3144
#' **Accuracy:** footprints assume flat terrain and nadir camera angle.
3245
#' The georeferenced thumbnails are approximate — useful for visual context,
@@ -45,7 +58,8 @@
4558
#'
4659
#' @export
4760
fly_thumb_georef <- function(fetch_result, photos_sf,
48-
dest_dir = "georef", overwrite = FALSE) {
61+
dest_dir = "georef", overwrite = FALSE,
62+
srcnodata = "0") {
4963
if (!all(c("airp_id", "dest", "success") %in% names(fetch_result))) {
5064
stop("`fetch_result` must be output from `fly_fetch()`.", call. = FALSE)
5165
}
@@ -85,7 +99,7 @@ fly_thumb_georef <- function(fetch_result, photos_sf,
8599
fp <- footprints[fp_idx[1], ]
86100

87101
results$success[i] <- tryCatch(
88-
georef_one(src, fp, out_file),
102+
georef_one(src, fp, out_file, srcnodata = srcnodata),
89103
error = function(e) {
90104
message("Failed to georef ", basename(src), ": ", e$message)
91105
FALSE
@@ -100,7 +114,7 @@ fly_thumb_georef <- function(fetch_result, photos_sf,
100114

101115
#' Georeference a single thumbnail to a footprint polygon
102116
#' @noRd
103-
georef_one <- function(src, fp, out_file) {
117+
georef_one <- function(src, fp, out_file, srcnodata = "0") {
104118
# Get footprint corner coordinates
105119
# fly_footprint builds: BL, BR, TR, TL, BL (closing)
106120
coords <- sf::st_coordinates(fp)[1:4, , drop = FALSE]
@@ -138,9 +152,18 @@ georef_one <- function(src, fp, out_file) {
138152
)
139153

140154
# Step 2: warp to target CRS with nodata handling
141-
# RGB: add alpha band (-dstalpha) for clean masking in mosaics
142-
# Grayscale: set nodata=0 (losing some shadow detail is acceptable)
155+
# srcnodata: masks black source pixels (camera frame borders)
156+
# RGB: alpha band (-dstalpha) for transparent fill in mosaics
157+
# Grayscale: dstnodata=0 for nodata metadata
143158
warp_opts <- c("-t_srs", "EPSG:3005", "-r", "bilinear")
159+
if (!is.null(srcnodata)) {
160+
src_val <- if (is_rgb) {
161+
paste(rep(srcnodata, n_bands), collapse = " ")
162+
} else {
163+
srcnodata
164+
}
165+
warp_opts <- c(warp_opts, "-srcnodata", src_val)
166+
}
144167
if (is_rgb) {
145168
warp_opts <- c(warp_opts, "-dstalpha")
146169
} else {

man/fly_thumb_georef.Rd

Lines changed: 25 additions & 9 deletions
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

0 commit comments

Comments
 (0)