Radiometric calibration of full waveform ALS data, Horn, Austria

Introduction to Radiometric Calibration

Airborne Laser Scanning is a widely used technique for obtaining the geometry of the Earth's surface. Apart from geometric information, ALS systems can provide additional information about the recorded signal strength of each echo. In order to utilize this information for the study of the backscatter characteristic of the sensed surface, radiometric calibration is essential. Whereas relative radiometric calibration tries to minimise radiometric differences within a strip and its neighbouring strips, absolute radiometric calibration aims at providing quantities which do not depend on mission parameters but are only related to target properties. This is achieved by using radiometric in-situ measurements acting as control elements. For more information on the principle of radiometric calibration, please refer to documentation of the opalsRadioCal module and the related literature.

Description of data

The ALS data that are used for the present use case where obtained by RIEGL Laser Measurement Systems GmbH with the laser scanner RIEGL LMS-Q680i operating at the wavelength of 1550 nm. This instrument gives the opportunity of full waveform analysis, something that is required for performing radiometric calibration. The flight was performed on the 22nd of September, 2011 and the median point density (last echo) was 11.8 points/m2. The study area is part of the town of Horn in Lower Austria, as shown in Figure 1.

Next to the ALS data acquisition, in-situ radiometric field measurements of reference surfaces were realised on the 5th December, 2011 in order to allow an absolute radiometric calibration. For that purpose, different reflecting surface types were chosen, although in the present subset of data only one of these areas is included. All areas were measured multiple times (on slightly different locations) at zero angle of incidence (observation of the surface in normal direction) and the resulting median was selected as representative reflectivity value. For the following processing it is assumed that the reflectance of these reference surfaces follows the rule of Lambert. These measurements were performed with two RIEGL reflectometer instruments.

All the necessary data as well as the .bat files and the Python script, that are going to be mentioned later on, can be downloaded as a zip folder from the OPALS download page. After the download, extract the data to the directory of your choice.

Fig. 1: Study area in Horn

Data Processing

The processing of the available ALS data is divided into four different phases. In the sections that follow, there is a short description of the modules that are used during each phase as well as some comments on the results (if applicable). The corresponding code is also provided, both in the form of commandline executables and in Python (at the end of the page).

Phase 1: Calculation of calibration constant Ccal

The first phase of this use case consists of the preparation of the data and the calculation of a mean calibration constant for each one of the two point clouds. This can be achieved by running the following sequence of commands for each strip (%1 should be replaced by the desired filename without extension) or simplify the run, call the .bat file Part_1_Calculation_CalCon.bat in the OPALS Shell.

rem //Import 3D point cloud from compressed LAS (including extra byte pulse/echo width) and calculate beam vector for each point (based on the flight trajectory)
opalsImport -inFile %1.laz -trjFile 110922_HORN_Q680i.txt -storeBeamInfo BeamVector BeamVectorSCS Range ScanAngle -iFormat LAS_1.4_echowidth.xml
rem //Create strip-wise digital elevation models (DEMs)
opalsGrid -inFile %1.odm -interpolation movingPlanes
rem //Create shadings from the DEMs - for visualisation only
opalsShade -inFile %1_z.tif
rem //Derive boundaries of the individual strips
opalsBounds -inFile %1.odm
rem //Calculate surface normals info per LiDAR echo (required later for deriving reflectance)
opalsNormals -inFile %1.odm -normalsAlg simplePlane -neighbours 8 -searchRadius 5 -filter Echo[Last]

In this first phase, the two .laz files as well as the trajectory file (110922_HORN_Q680i.txt) are initially imported into the ODM. The latter is used for the extraction and storage of the trajectory information BeamVector, BeamVectorSCS, Range and ScanAngle (see parameter -storeBeamInfo in the opalsImport module), which is going to be used later on for the determination of the incidence angle of each pulse. For the same purpose, the surface normals information for each LiDAR echo is also computed, by running the opalsNormals module. In order to have a robust estimation, the number of neighbours is set to 8 in a search radius of 5 meters.

What follows next is the first use of the opalsRadioCal module, which aims at the calculation of the calibration constant. For that, the .wnp file is used, which contains the coordinates of the natural reference targets where reflectance measurements took place. In addition, the reflectivity file (calRegionHorn_reflectivity.txt) is required, in which the reflectance values of the corresponding targets can be found (In is possible to store the calibration polygons and the corresponding reflectivity values within a single shape file, as it can be found in calRegionHorn.shp. See also Module RadioCal for further details). These reflectance values refer to a specific wavelength that was used for obtaining the data (1550 nm in this use case) and to different incidence angles from 0 to 90 degrees. The following command estimated a common calibration constant for both strips.

rem //Calculate the calibration constant independently for each strip (i.e. phase 1 of radiometric calibration)
opalsRadioCal -inFile 680_110922_095526.odm 680_110922_095947.odm -outFile calRegions.odm -calRegionFile calRegionHorn.wnp

The optional parameter outFile creates an additional ODM file (calRegions.odm in this case), that can be further used to analyze the distribution of the estimated calibration constants within the calibration regions.

opalsHisto -inFile calRegions.odm -attribute _RadioCalConst

Therefore, opalsHisto module is used, having as attribute the previously calculated calibration constant (_RadioCalConst). The most robust estimator here is the median and therefore we consider from now on the median value 5.12 as the calibration constant for both strips, as also returned by the previous opalsRadioCal run. The histogram as well as the statistics of the calibration constants for both strips can be seen in Figure 2:

Fig. 2: Histogram of the calibration constants for both point clouds

Phase 2: Application of calibration constant Ccal

During the second phase, the formerly computed calibration constant is applied to both strips in order to calculate the calibrated radiometric values for each echo, by running the following command (or calling the .bat file Part_2_Application_CalCon.bat in the OPALS Shell as before):

opalsRadioCal -inFile 680_110922_095526.odm 680_110922_095947.odm -radioCal 5.12

After the second run of the opalsRadioCal module, the new radiometric attributes CrossSection, _IncidenceAngle, Reflectance, _Gamma, _Sigma, which fully describe the characteristics of the observed surface, are added to the ODM.

Phase 3: Amplitude, reflectance and gamma raster and colour maps

In the third phase of this use case, some raster colour maps are created. To produce all of the maps at once, just call the .bat file Part_3_Maps.bat for each of the point clouds (filename without extension) or alternatively run the command sequence below:

rem //Create echo amplitude raster derived from the Gaussian decomposition of the full-waveforms
opalsGrid -inFile %1.odm -outFile %1_A_025.tif -gridSize 0.25 -attribute Amplitude -interpolation movingPlanes -neighbours 8 -searchRadius 4 -filter Echo[Last]
rem //Generate colour coded amplitude map - for visualisation only
opalsZColor -inFile %1_A_025.tif -palFile greyPal.xml -scalePal 0,300
rem //Create reflectance raster for each strip independently
opalsGrid -inFile %1.odm -outFile %1_R_025.tif -gridSize 0.25 -attribute Reflectance -interpolation movingPlanes -neighbours 8 -searchRadius 4 -filter Echo[Last]
rem //Generate colour coded reflectance maps - for visualisation only
opalsZColor -inFile %1_R_025.tif -palFile greyPal.xml -scalePal 0,0.75
rem //Create gamma raster for each strip independently
opalsGrid -inFile %1.odm -outFile %1_G_025.tif -gridSize 0.25 -attribute _Gamma -interpolation movingPlanes -neighbours 8 -searchRadius 4 -filter Echo[Last]
rem //Generate colour coded gamma maps - for visualisation only
opalsZColor -inFile %1_G_025.tif -palFile greyPal.xml -scalePal 0,3

Using the opalsGrid module and the corresponding attribute every time, the rasters of the amplitude (before the calibration, as it was derived from the Gaussian decomposition of the full-waveforms), the reflectance (after the calibration) and the gamma for each LiDAR echo are created. The gridsize is set to 0.25, so that all the possible details are maintained. In addition, to create a colour map, the opalsZColor module is then used. The scale was set each time so that the visualisation is clear. The left picture corresponds to the first strip (680_110922_095526.laz) and the right one to the second strip (680_110922_095947.laz) in both figures.

Fig. 3: Amplitude maps of the two strips before the radiometric calibration. Left: 680_110922_095526.laz and right: 680_110922_095947.laz
Fig. 4: Reflectance maps of the two strips after the radiometric calibration. Left: 680_110922_095526.laz and right: 680_110922_095947.laz

In the amplitude maps, somebody can easily distinguish difference in the amplitude values, since it depends on a number of factors like the range, angle of incidence, surface characteristics, atmosphere etc. However, the effects of these factors have been eliminated by performing the radiometry calibration and therefore these previous difference do not appear anymore in the reflectance maps. In this way, it is possible to compare strips of different flights and/or different time periods.

Phase 4: Radiometry check: Strip differences

The last step is to perform a radiometry check between the two strips. In order to get some insight into the quality of the performed radiometric calibration, the strip differences in the amplitude, reflectance and gamma are going to be derived. It is expected to have considerable differences before the calibration (amplitude differences) and much smaller reflectance and gamma differences between the two strips after the calibration. The .bat file Part_4_Radiometry_Check.bat can be called and followed by both filenames this time or just run the commands below:

rem //strip difference of the amplitude maps
opalsAlgebra -inFile %1_A_025.tif %2_A_025.tif -formula "r[0] && r[1] ? r[0]-r[1] : invalid" -outFile A_diff.tif
opalsHisto -inFile A_diff.tif -sampleRange -30 30
opalsZColor -inFile A_diff.tif -palFile differencePal.xml -scale 45
rem //strip difference of the reflectance maps
opalsAlgebra -inFile %1_R_025.tif %2_R_025.tif -formula "r[0] && r[1] ? r[0]-r[1] : invalid" -outFile R_diff.tif
opalsHisto -inFile R_diff.tif
opalsZColor -inFile R_diff.tif -palFile differencePal.xml -scale 0.08
rem //strip difference of gamma maps
opalsAlgebra -inFile %1_G_025.tif %2_G_025.tif -formula "r[0] && r[1] ? r[0]-r[1] : invalid" -outFile G_diff.tif
opalsHisto -inFile G_diff.tif
opalsZColor -inFile G_diff.tif -palFile differencePal.xml -scale 0.32
Fig. 5: Difference maps in amplitude, reflectance and gamma between the two strips

From the results shown in Figure 5, it is observed that the highest differences appear indeed in the amplitude map. That is reasonable since the effects of the influencing factors are different in each strip depending on the conditions of the flight, the atmosphere etc. On the other hand, observing the reflectance and gamma maps, it is concluded that the point clouds have become quite homogeneous after the radiometric calibration and thus they can now be used in combination with each other.

Implementation in Python_script

The following Python script includes four different functions that correspond to the four different phases, as described above. It is created such that the output of the one function (phase) becomes the input for the next one. It can be run both through PyScripter, which is included in the OPALS package, and in the OPALS Shell, when converted to a .py file.

import os, opals
from opals import Import, Grid, Shade, Bounds, Normals, ZColor, RadioCal, Histo, Algebra
def Phase_1_Calc(files):
odm_files=[]
for filename in files:
# Create the output filename with extension .odm
out = os.path.splitext(filename)
odm_filename = os.path.join(out[0] + "." + 'odm')
odm_files.append(odm_filename)
# Import 3D point cloud from compressed LAS (including extra byte pulse/echo width) and calculate beam vector for each point (based on the flight trajectory)
imp=Import.Import(inFile=filename, trjFile='110922_HORN_Q680i.txt', storeBeamInfo=[opals.Types.BeamInfo.BeamVector,\
opals.Types.BeamInfo.BeamVectorSCS,opals.Types.BeamInfo.Range,opals.Types.BeamInfo.ScanAngle], iFormat='LAS_1.4_echowidth.xml',\
outFile=odm_filename)
imp.run()
imp.reset()
del imp
# Create the output filename with extension .tif
tif_filename = os.path.join(out[0] + '_z' + '.' + 'tif')
# Create strip-wise digital elevation models (DEMs)
grid=Grid.Grid(inFile=odm_filename, interpolation=opals.Types.GridInterpolator.movingPlanes, outFile=tif_filename)
grid.run()
grid.reset()
del grid
# Create shadings from the DEMs - for visualisation only
shade=Shade.Shade(inFile=tif_filename)
shade.run()
shade.reset()
del shade
# Derive boundaries of the individual strips
bounds=Bounds.Bounds(inFile=odm_filename)
bounds.run()
bounds.reset()
del bounds
# Calculate surface normals info per LiDAR echo (required later for deriving reflectance)
normals=Normals.Normals(inFile=odm_filename, normalsAlg=opals.Types.NormalsAlgorithm.simplePlane, neighbours=8, searchRadius=5, filter='Echo[Last]')
normals.run()
normals.reset()
del normals
radcal_filename = 'calRegions.odm'
# Calculate the calibration constant for both strip
radcal = RadioCal.RadioCal(inFile=odm_files, calRegionFile='calRegionHorn.wnp', outFile=radcal_filename)
radcal.run()
radcal.reset()
del radcal
return odm_files, radcal_filename
def Phase_2_App(odm_filename,median):
# Calculate the reflectance value of each LiDAR echo
radcal=RadioCal.RadioCal(inFile=odm_filename, radioCal=opals.Types.CalibrationStats(median))
radcal.run()
radcal.reset()
del radcal
def Phase_3_Maps(odm_filename):
# Create the output filename with extension .tif
out = os.path.splitext(odm_filename)
tifA025_filename = os.path.join(out[0] + '_A_025' + '.' + 'tif')
# Create echo amplitude raster derived from the Gaussian decomposition of the full-waveforms
grid=Grid.Grid(inFile=odm_filename, gridSize=0.25, interpolation=opals.Types.GridInterpolator.movingPlanes, filter='Echo[Last]', attribute='Amplitude', neighbours=8, \
searchRadius=4, outFile=tifA025_filename)
grid.run()
grid.reset()
del grid
# Generate colour coded amplitude map - for visualisation only
zcolor=ZColor.ZColor(inFile=tifA025_filename, palFile='greyPal.xml', scalePal='0,300')
zcolor.run()
zcolor.reset()
del zcolor
# Create the output filename with extension .tif
tifR025_filename = os.path.join(out[0] + '_R_025' +'.' + 'tif')
# Create reflectance raster for each strip independently
grid=Grid.Grid(inFile=odm_filename, outFile=tifR025_filename, gridSize=0.25, attribute='Reflectance', interpolation=opals.Types.GridInterpolator.movingPlanes, neighbours=8,\
searchRadius=4, filter='Echo[Last]')
grid.run()
grid.reset()
del grid
# Generate colour coded reflectance maps - for visualisation only
zcolor=ZColor.ZColor(inFile=tifR025_filename, palFile='greyPal.xml', scalePal='0,0.75')
zcolor.run()
zcolor.reset()
del zcolor
# Create the output filename with extension .tif
tifG025_filename = os.path.join(out[0] + '_G_025' +'.' + 'tif')
# Create Gamma raster for each strip independently
grid=Grid.Grid(inFile=odm_filename, outFile=tifG025_filename, gridSize=0.25, attribute='_Gamma', interpolation=opals.Types.GridInterpolator.movingPlanes, neighbours=8,\
searchRadius=4, filter='Echo[Last]')
grid.run()
grid.reset()
del grid
# Generate colour coded gamma maps - for visualisation only
zcolor=ZColor.ZColor(inFile=tifG025_filename, palFile='greyPal.xml', scalePal='0,3')
zcolor.run()
zcolor.reset()
del zcolor
return [tifA025_filename, tifR025_filename, tifG025_filename]
def Phase_4_Radiocheck(tifA025_filename1, tifA025_filename2, tifR025_filename1, tifR025_filename2, tifG025_filename1, tifG025_filename2):
# Strip difference of the amplitude maps
Algebra.Algebra(inFile=[tifA025_filename1,tifA025_filename2], formula="r[0] && r[1] ? r[0]-r[1] : invalid", outFile='A_diff.tif').run()
Histo.Histo(inFile='A_diff.tif',sampleRange=(-30,30)).run()
ZColor.ZColor(inFile='A_diff.tif', palFile='differencePal.xml', scalePal='45').run()
# Strip difference of the reflectance maps
Algebra.Algebra(inFile=[tifR025_filename1,tifR025_filename2], formula="r[0] && r[1] ? r[0]-r[1] : invalid", outFile='R_diff.tif').run()
Histo.Histo(inFile='R_diff.tif').run()
ZColor.ZColor(inFile='R_diff.tif', palFile='differencePal.xml', scalePal='0.08').run()
# Strip difference of the gamma maps
Algebra.Algebra(inFile=[tifG025_filename1,tifG025_filename2], formula="r[0] && r[1] ? r[0]-r[1] : invalid", outFile='G_diff.tif').run()
Histo.Histo(inFile='G_diff.tif').run()
ZColor.ZColor(inFile='G_diff.tif', palFile='differencePal.xml', scalePal='0.32').run()
def main():
odms, radcal_filename = Phase_1_Calc(['680_110922_095526.laz','680_110922_095947.laz'])
hist = Histo.Histo(inFile = radcal_filename, attribute='_RadioCalConst')
hist.run()
stat=hist.histogram[0]
median=stat.getMedian()
Phase_2_App(odms[0],median)
Phase_2_App(odms[1],median)
tiffs1=Phase_3_Maps(odms[0])
tiffs2=Phase_3_Maps(odms[1])
Phase_4_Radiocheck(tiffs1[0],tiffs2[0],tiffs1[1],tiffs2[1],tiffs1[2],tiffs2[2])
if __name__ == '__main__':
main()

References

Wagner, W. (2010). Radiometric calibration of small-footprint full-waveform airborne laser scanner measurements: Basic physical concepts. ISPRS Journal of Photogrammetry and Remote Sensing, 65(6), 505-513. doi:10.1016/j.isprsjprs.2010.06.007

Briese, C., Pfennigbauer, M., Lehner, H., Ullrich, A., Wagner, W., & Pfeifer, N. (2012). Radiometric Calibration Of Multi-Wavelength Airborne Laser Scanning Data. ISPRS Ann. Photogramm. Remote Sens. Spatial Inf. Sci. ISPRS Annals of Photogrammetry, Remote Sensing and Spatial Information Sciences, I-7, 335-340. doi:10.5194/isprsannals-i-7-335-2012

opalsRadioCal is the executable file of Module RadioCal
Definition: ModuleExecutables.hpp:173
opalsHisto is the executable file of Module Histo
Definition: ModuleExecutables.hpp:103