More scripts: Raster
Syntax Highlighing:
comments, key words, predefined symbols, class members & methods, functions & classes
# PipelineNDVIfromTIFF_CropAndMaskFromRegion.sml
# Sample standalone script to demonstrate an image pipeline application.
# Computes a Normalized Difference Vegetation Index (NDVI) raster from Red and
# Near-infrared bands directly from an input GeoTIFF file containing a 4-band multispectral
# QuickBird or Ikonos satellite image. The GeoTIFF file does not need to have been previously linked
# or imported to TNTmips. NDVI computations are restricted to areas of the image delineated by
# polygons in an input vector object. The polygons are converted to a region which is used to
# mask the areas outside the polygons and to crop the image to the extent of the polygons.
# NDVI is calculated for each image cell using the formula:
# 1000 * (Near Infrared - Red) / (Near Infrared + Red)
# Values in the resulting NDVI raster can range from -1000 to + 1000 (using Signed 16-bit raster type).
# If the input image is projected, there may be 0-value cells around the edges. A ValidityNear filter
# is used to identify cells that have zero value in both bands; the filter creates a null mask
# for each band with such cells marked as null.
# Path radiance (atmospheric scattering / haze) is an additive effect that varies between
# bands, and so should be removed before calculating NDVI. This script uses the minimum (non-null)
# raster value in each band as an approximation to the Path Radiance value for that band. This value
# is subtracted using the SCALEOFFSET filter (scale = 1.00, offset value negative).
# The LINEAR filter (linear combination of band values) is used to perform the addition and
# subtraction of the band values.
# The NDVI image is produced as a single 16-bit signed integer RVC raster in a Project File.
# 9 January 2008
# Randy Smith, MicroImages, Inc.
# updated 17 January 2014
# Requires Version 2008:74 or later of the TNT products.
numeric err;
# error checking procedure
proc ReportError(numeric linenum, numeric err) {
printf("FAILED -line: %d, error: %d\n", linenum - 1, err);
PopupError(err);
}
################################################################################
################################# Main program ###############################
clear();
# set context so script does not exit on error, allowing manual
# handling of errors.
_context.AbortOnError = 0;
# CHOOSE GEOTIFF FILE containing QuickBird or IKONOS 4-band multispectral image
class FILEPATH inFilePath(GetInputFileName("","Choose GeoTIFF file with source image:", ".tif"));
# PIPELINE SOURCE
# set GeoTIFF file containing a four-band QuickBird or Ikonos image as source
class IMAGE_PIPELINE_SOURCE_TIFF source_tif( inFilePath );
err = source_tif.Initialize();
if (err < 0 )
ReportError(_context.CurrentLineNum, err);
else {
print("Source TIFF file initialized");
}
printf("Number of bands in TIFF file = %d\n", source_tif.GetNumSamples() );
if (source_tif.GetNumSamples() <> 4) {
print("Selected TIFF file does not contain 4 bands; exiting now.");
Exit();
}
# Get georeference and coordinate reference system from the GeoTIFF image.
class IMAGE_PIPELINE_GEOREFERENCE georefImg;
georefImg = source_tif.GetGeoreference();
class SR_COORDREFSYS imgCRS = georefImg.GetCRS();
if (!imgCRS.IsDefined() ) {
print("Image is not georeferenced; exiting now.");
Exit();
}
else if (imgCRS.IsLocal() ) {
print("Image has local engineering georeference; exiting now.");
Exit();
}
else
printf("Image coordinate reference system = %s\n", imgCRS.Name );
# Get coordinate transformation from image to its defined CRS
class TRANS2D_MAPGEN imgToCRS = georefImg.GetTransGen();
# CHOOSE VECTOR OBJECT WITH POLYGONS TO INDICATE AREAS TO PROCESS
class RVC_VECTOR polyVect;
class RVC_OBJITEM vectObjItem;
class RVC_GEOREFERENCE vectGeoref;
class SR_COORDREFSYS vectCRS;
DlgGetObject("Choose vector object with polygons outlining areas to process:", "Vector", vectObjItem, "ExistingOnly");
polyVect.Open(vectObjItem, "Read");
if (polyVect.$Info.NumPolys == 0) {
print("Vector object contains no polygons; exiting now.");
Exit();
}
err = polyVect.GetDefaultGeoref(vectGeoref);
if (err < 0 ) {
ReportError(_context.CurrentLineNum, err);
print("Polygon vector must be georeferenced and is not; exiting now.");
Exit();
}
vectCRS = vectGeoref.GetCoordRefSys();
printf("Vector coordinate reference system = %s\n", vectCRS.Name);
# CHOOSE OUTPUT RASTER
# DlgGetObject creates only an ObjItem for the output; the new raster will then be created
# by the TARGET_RVC with the desired compression. The ObjectPath of the ObjItem is not
# valid until the output raster is actually created by the pipeline.
class RVC_OBJITEM ndviOutObjItem;
DlgGetObject("Choose new raster for NDVI result:", "Raster", ndviOutObjItem, "NewOnly");
# PIPELINE FILTER_SELECT to select red and near-infrared bands for processing (don't need to carry the rest
# forward through the pipeline).
# The source TIFF file consists of 4 bands (samples) per pixel, which have numeric indices 0 = blue,
# 1 = green, 2 = red, 3 = near-infrared. These samples are selected by index number in this filter.
numeric numSamples = 2; # number of bands from 4-band TIFF file to be used
array numeric srcSamples[2]; # array to contain the numeric indices of the bands to select
srcSamples[1] = 2; # index for red band
srcSamples[2] = 3; # index for near-infrared band
class IMAGE_PIPELINE_FILTER_SELECT filter_select(source_tif, srcSamples, numSamples);
err = filter_select.Initialize();
if (err < 0 )
ReportError(_context.CurrentLineNum, err);
else {
print("Initialized band selection filter");
}
# the result of this FILTER_SELECT is an "image" with two samples per pixel, with the red value as index 0
# and the near-infrared value as index 1.
class STRING pixelType = filter_select.GetPixelType();
printf("Pixel type in image = %s\n", pixelType);
class STRING datatypeRed$, datatypeNIR$;
class IMAGE_PIPELINE_PIXEL pixel;
pixel = filter_select.GetPixelValueMax();
datatypeRed$ = pixel.GetSample(0).GetDataType();
datatypeNIR$ = pixel.GetSample(1).GetDataType();
printf("Datatype of red band = %s\n", datatypeRed$ );
printf("Datatype of near-infrared band = %s\n", datatypeNIR$ );
# CONVERT INPUT VECTOR TO A REGION FOR USE AS A PIPELINE SOURCE
class REGION2D RegFromVect = ConvertVectToRegion(polyVect, GetLastUsedGeorefObject(polyVect) );
# if vector has different coordinate reference system from image, reproject region
if (vectCRS.Name <> imgCRS.Name) {
printf("Converting region to image CRS: %s\n", imgCRS.Name);
RegFromVect.ConvertTo(imgCRS);
}
class RECT vecRegionExtents = RegFromVect.Extents;
printf("Minimum map extents of polyVect region: %.2f, %.2f\n", vecRegionExtents.x1, vecRegionExtents.y1);
printf("Maximum map extents of polyVect region: %.2f, %.2f\n\n", vecRegionExtents.x2, vecRegionExtents.y2);
# GET EXTENTS OF THE IMAGE SOURCE AS A REGION
class REGION2D imgReg;
err = filter_select.ComputeGeoreferenceRegion(imgReg);
if (err < 0 )
ReportError(_context.CurrentLineNum, err);
# CHECK THAT REGION FROM VECTOR IS CONTAINED WITHIN IMAGE REGION
if (imgReg.TestRegion(RegFromVect, "FullInside") ) {
print("Image region contains vector extents.");
}
else {
print("Input vector is not contained within the source image. Exiting now.");
Exit();
}
# PIPELINE SOURCE FOR REGION CREATED FROM THE VECTOR POLYGONS
# Construct using source image as a reference so the "image dimensions" of the region source match,
# enabling the region source to be used as a mask in the MASKVALIDITY filter
class IMAGE_PIPELINE_SOURCE_REGION sourceReg(RegFromVect, filter_select);
err = sourceReg.Initialize();
if (err < 0 )
ReportError(_context.CurrentLineNum, err);
else {
print("Initialized region source.");
printf("Size of region = %d lines, %d columns\n", sourceReg.GetTotalRows(), sourceReg.GetTotalColumns() );
}
# Get the extents of the region as a RECT in image coordinates
# to use to crop the source image. These are the extents of the region used to
# create the region source, not those of the image used as its reference.
class RECT regExtents;
regExtents = sourceReg.GetRegionExtents();
printf("Minimum values of cropping rectangle from polygons: x = %.2f, y = %.2f\n", regExtents.x1, regExtents.y1);
printf("Maximum values of cropping rectangle from polygons: x = %.2f, y = %.2f\n", regExtents.x2, regExtents.y2);
# SET UP PIPELINE FILTER TO MASK PORTIONS OF SOURCE IMAGE
# mask area outside the region created from the vector polygons
class IMAGE_PIPELINE_FILTER_MASKVALIDITY filterMask(filter_select, sourceReg);
err = filterMask.Initialize();
if (err < 0 )
ReportError(_context.CurrentLineNum, err);
else {
print("Initialized image mask filter.");
}
# SET UP PIPELINE FILTER TO CROP THE SOURCE IMAGE
class IMAGE_PIPELINE_FILTER_CROP filter_Crop(filterMask, regExtents);
err = filter_Crop.Initialize();
if (err < 0 )
ReportError(_context.CurrentLineNum, err);
else {
print("Initialized image crop filter.");
}
# PIPELINE FILTER_VALIDITYNEAR
# Filter to set null for cells based on nearness (distance) to a specified value
# In this case test for zero value in both bands (assumed to be non-image cells) and set
# such cells to null (0 value in null mask created by the filter)
# validity flag
numeric resultValid = 0; # set 1 if pixels near test value should be valid, 0 if invalid;
# we want 0 values to be invalid, so set this parameter to false (0)
numeric distance = 0.5; # distance to test must be greater than 0; use a decimal value less
# than 1 to test for exact match to an integer value
class IMAGE_PIPELINE_FILTER_VALIDITYNEAR validity(filter_Crop, "Orthogonal", resultValid);
# define two 0-value samples to provide the set of values
class IMAGE_PIPELINE_SAMPLE redNullSamp("Gray", "UINT16", 0);
class IMAGE_PIPELINE_SAMPLE nirNullSamp("Gray", "UINT16", 0);
# define a text pixel with the two 0-value samples to pass to the validity filter
class IMAGE_PIPELINE_PIXEL testPixel(redNullSamp, nirNullSamp);
# add the pixel value to test and the test distance to the validity filter;
validity.AddValue(testPixel, distance);
array numeric samplesToTest[2]; # array to set which image samples to test (in this case, both)
samplesToTest[1] = 0; # sample index for red
samplesToTest[2] = 1; # sample index for near-infrared
validity.SetSamplesTest(samplesToTest, 2);
err = validity.Initialize();
if (err < 0 )
ReportError(_context.CurrentLineNum, err);
else {
print("Initialized ValidityNear filter.");
}
# GET MINIMUM AND MAXIMUM VALUES of the red and near-infrared bands.
# First call method on stage to compute the range of values in the bands; using the "Exact"
# value for the RangeType parameter in this method to compute the actual range of values using the
# rasters without sampling (i.e. computing using the pyramids, if any). The "Default" value returns
# the range of the raster datatype.
validity.ComputePixelRange("Exact");
class IMAGE_PIPELINE_PIXEL minVals, maxVals; # container to hold the minimum and maximum value for each band
minVals = validity.GetPixelValueMin();
maxVals = validity.GetPixelValueMax();
printf("Minimum value of red band = %d\n", minVals.GetSample(0).GetValue() );
printf("Maximum value of red band = %d\n", maxVals.GetSample(0).GetValue() );
printf("Minimum value of near-infrared band = %d\n", minVals.GetSample(1).GetValue() );
printf("Maximum value of near-infrared band = %d\n", maxVals.GetSample(1).GetValue() );
# PIPELINE FILTER_SCALEOFFSET applies scale (multiply by constant) and offset (add constant value) to each cell
# Use with scale = 1 and negative offset to subtract the minimum valid value from each of the bands
# as an approximate correction for path-radiance.
class IMAGE_PIPELINE_FILTER_SCALEOFFSET offsetMin(validity);
# set scale = 1 and negative offset equal to minimum value for red band (0) and near-infrared band (1)
offsetMin.SetScaleOffset(0, 1, minVals.GetSample(0).GetValue() * -1);
offsetMin.SetScaleOffset(1, 1, minVals.GetSample(1).GetValue() * -1);
err = offsetMin.Initialize();
if (err < 0 )
ReportError(_context.CurrentLineNum, err);
else {
print("Initialized Scale-offset filter for path-radiance correction.");
}
# PIPELINE FILTER_LINEAR produces a linear combination of the band values using a coefficient for each band
# Cell Value = a * Band1value + b * Band2value + ...
# Use here to subtract band values to provide dividend for the NDVI expression: NIR - Red;
# Set number of samples in target to 1; coefficient for NIR = 1 and for Red = -1
numeric numTargetSamples = 1;
numeric targetSampleNum = 0; # 0th target sample
numeric sourceSampleNum = 0; # 0th source sample (red)
numeric coefValue; # value of the coefficient
class IMAGE_PIPELINE_FILTER_LINEAR linearSubtract(offsetMin, numTargetSamples);
coefValue = -1; # set coefficient value = -1 for red
linearSubtract.SetCoefficient(targetSampleNum, sourceSampleNum, coefValue);
sourceSampleNum = 1; # 1st (near-infrared) source sample
coefValue = 1; # set coefficient value = 1 for near-infrared
linearSubtract.SetCoefficient(targetSampleNum, sourceSampleNum, coefValue);
err = linearSubtract.Initialize();
if (err < 0 )
ReportError(_context.CurrentLineNum, err);
else {
print("Initialized linear filter for subtracting red from near-infrared.");
}
# PIPELINE FILTER_LINEAR
# Use here to add band values to provide divisor for the NDVI expression: NIR + Red;
# Set number of samples in target to 1; coefficient for both NIR and Red = 1;
class IMAGE_PIPELINE_FILTER_LINEAR linearAdd(offsetMin, numTargetSamples);
sourceSampleNum = 0; # 0th source sample (red)
linearAdd.SetCoefficient(targetSampleNum, sourceSampleNum, coefValue);
sourceSampleNum = 1; # 1st source sample (near-infrared)
linearAdd.SetCoefficient(targetSampleNum, sourceSampleNum, coefValue);
err = linearAdd.Initialize();
if (err < 0 )
ReportError(_context.CurrentLineNum, err);
else {
print("Initialized linear filter for adding red and near-infrared.");
}
# PIPELINE FILTER_DIVIDE: filter to divide two images
# Use to perform the final NDVI division using the results from the two Linear filters
# The stage takes two source stages, the dividend stage followed by the divisor stage,
# Output of this filter is a single 64-bit floating-point image with possible range -1.00 to + 1.00
class IMAGE_PIPELINE_FILTER_DIVIDE divide(linearSubtract, linearAdd);
err = divide.Initialize();
if (err < 0 )
ReportError(_context.CurrentLineNum, err);
else {
print("Initialized divide filter to compute NDVI.");
}
# PIPELINE FILTER_SCALEOFFSET
# Scale floating-point values to range -1000 to + 1000 using scale factor = 1000 and offset = 0
class IMAGE_PIPELINE_FILTER_SCALEOFFSET scale1000(divide);
scale1000.SetScaleOffset(0, 1000, 0);
err = scale1000.Initialize();
if (err < 0 )
ReportError(_context.CurrentLineNum, err);
else {
print("Initialized scale-offset filter to rescale NDVI.");
}
# PIPELINE FILTER_DATATYPE: filter to change datatype
# Change rescaled NDVI from 64-bit floating point to 16-bit signed integer
class IMAGE_PIPELINE_FILTER_DATATYPE changeToSINT16(scale1000, "SINT16", "Invalidate");
err = changeToSINT16.Initialize();
if (err < 0 )
ReportError(_context.CurrentLineNum, err);
else {
print("Initialized datatype filter to convert to signed 16-bit.");
}
# PIPELINE TARGET
class IMAGE_PIPELINE_TARGET_RVC target_rvc(changeToSINT16, ndviOutObjItem);
target_rvc.Initialize();
if (err < 0 )
ReportError(_context.CurrentLineNum, err);
else {
print("Initialized NDVI target.");
}
# EXECUTE the pipeline process
print("Processing...");
target_rvc.Process();
print("Done.");