Skip to contents

Convert raw hyperspectral imaging data (digital numbers) to calibrated reflectance values using white and dark reference measurements. This is the essential first step in hyperspectral data processing.

Usage

hsi_calc_reflectance(
  x,
  whiteref,
  darkref,
  darkspec = NULL,
  tint = c(1, 1),
  in_memory = FALSE,
  filename = "",
  overwrite = FALSE,
  ...
)

Arguments

x

A SpatRaster with raw hyperspectral sample data. Band names must be numeric wavelengths in nm.

whiteref

A SpatRaster with white reference data. Must have the same bands and wavelengths as x.

darkref

A SpatRaster with dark reference data from the white reference session. Must have the same bands and wavelengths as x.

darkspec

A SpatRaster with dark reference data from the specimen session. Default NULL. Required for dual-exposure workflows where tint values differ. When NULL and tint = c(1, 1), a single dark reference is sufficient.

tint

Numeric vector of length 2. Integration times for white reference and specimen capture, in that order. Default c(1, 1) assumes equal integration times.

in_memory

Logical. Process entirely in RAM. Default FALSE. Set TRUE only when data fits comfortably in available memory.

filename

Character. Output filename. Default "" keeps result in memory.

overwrite

Logical. Overwrite existing file. Default FALSE.

...

Additional arguments passed to terra::writeRaster().

Value

A SpatRaster with reflectance values.

Details

All inputs must share the same spatial resolution, number of bands, wavelength labels, and compatible spatial extents. When reading .raw ESRI data, load with terra::rast(x, noflip = TRUE).

Three calibration paths are supported:

Single session (darkspec = NULL, tint = c(1, 1)): specimen, white reference, and dark reference all share the same integration time. No scaling is needed. This is the simplest workflow and produces correct reflectance, though signal-to-noise is lower than with a dual-exposure strategy.

Matched darks (darkspec provided): a dual-exposure workflow where the specimen is overexposed relative to the white reference to maximise signal. Each subtraction uses the dark reference captured at the matching integration time. This is the recommended approach for dual-exposure scanning. Many scanners capture a dark reference per session, so matched darks are typically available for standard workflows.

$$R(\lambda) = \frac{specimen - dark_{specimen}}{white - dark_{white}} \times \frac{t_{white}}{t_{specimen}}$$

Scaled dark (darkspec = NULL, tint values differ): fallback for dual-exposure workflows when only the white-session dark reference is available. The dark reference is scaled by the integration time ratio before numerator subtraction. This assumes dark current scales linearly with integration time. In practice, some detectors have a large fixed-pattern noise component that does not scale with exposure time. Scaling overestimates the specimen dark current, producing severely degraded reflectance — often negative across entire spectra. Use only as a last resort.

$$R(\lambda) = \frac{specimen - dark_{white} \times \frac{t_{specimen}}{t_{white}}}{white - dark_{white}} \times \frac{t_{white}}{t_{specimen}}$$

Examples

if (FALSE) { # \dontrun{
x <- terra::rast("capture/testdata.tif")
whiteref <- terra::rast("capture/WHITEREF_testdata.tif")
darkref <- terra::rast("capture/DARKREF_testdata.tif")

# Path 1: single session, equal integration times
x_reflectance <- hsi_calc_reflectance(
  x = x,
  whiteref = whiteref,
  darkref = darkref
)

# Path 2a: matched darks (recommended)
darkspec <- terra::rast("specimen/DARKREF_testdata.tif")

x_reflectance <- hsi_calc_reflectance(
  x = x,
  whiteref = whiteref,
  darkref = darkref,
  darkspec = darkspec,
  tint = c(3, 9)
)

# Path 2b: scaled dark (single dark, different integration times)
x_reflectance <- hsi_calc_reflectance(
  x = x,
  whiteref = whiteref,
  darkref = darkref,
  tint = c(3, 9),
  filename = "output_reflectance.tif",
  overwrite = TRUE
)
} # }