MorphContour.sml

  Download

More scripts: Advanced

Syntax Highlighing:

comments, key words, predefined symbols, class members & methods, functions & classes
            
######################################################################################################
#
# MorphContour.sml  (Stand Alone)
#
######################################################################################################
#
# Author:
#    Dave Breitwisch, MicroImages, Inc.
#    with contributions from Randy Smith, MicroImages, Inc.
#
# Started:
#    17 November 2005
#
# Updated:
#    18 July 2006
#
######################################################################################################
#
# Documentation:  MorphContour.sml
#
# Purpose:
#
#  The purpose of this Stand Alone SML script is to demonstrate the results of a surface-fitting
#  process from contour lines using morphological functions as described in the paper "An Image Space 
#  Algorithm for Morphological Contour Interpolation" by Barrett, Mortensen and Taylor.
#
#  The basic concept of this algorithm is to grow two known contour lines until their medial line is 
#  reached.  This line is then given the average value of the two lines and is added to the resulting 
#  image.  This new line is then used in the next iteration to find additional medial lines.  The 
#  process continues until there are no new cell values being added to the final image.
#
#  Some limitations of this algorithm include: 1) no filling of areas that are in between contour lines of 
#  the same value, such as ridges, saddles, peaks, or depressions; 2) there are also limitations based 
#  on the element structure used in the morphological functions, which lead to some unfilled cells if
#  the output DEM is set to be an integer data type.
#
######################################################################################################
# Function Prototypes
func MyErosion(class RASTER Rin, class RASTER Rtemp, numeric maskValue);
proc MyDilation(class RASTER Rin, class RASTER Rout, class RASTER Rmask, numeric maskValue, numeric minValue);
proc MyRasterXOR(class RASTER Rin1, class RASTER Rin2, class RASTER Rout);
proc MyMedialLine(class RASTER Rin1, class RASTER Rin2, class RASTER Rmask);
func MyAddToAccumulator(class RASTER Rin, class RASTER Rmask, class RASTER Rout, numeric intContValue);
proc Main();
# Global Variables
class VECTOR Vect;		# the input vector containing the 3D contours
class RASTER A;			# raster with initial contour z values transfered from input vector
class RASTER T, U, V, W, Temp;	# intermediate working rasters
class RASTER DEM;		# the final output DEM raster
numeric numlins, numcols;
array numeric element[3,3];
string table$, field$;		# names of line table and field containing elevation values
# Used as global rather then passing by reference since they are used in recursive functions
class RASTER FILL; #intermediate raster for filling holes
numeric value;
numeric currsize = 64;
numeric currpos = 0;
array numeric rowlist[currsize];
array numeric collist[currsize];
# Start process here
######################################################################################################
clear();
Main();
# Additional functions called
######################################################################################################
######################################################################################################
# This is the main function for the script.
proc Main() {
	numeric done = 1, numRuns = 0;
	# Create the Morphological element
	element[1,1] = null;
	element[1,2] = 0;
	element[1,3] = null;
	element[2,1] = 0;
	element[2,2] = 0;
	element[2,3] = 0;
	element[3,1] = null;
	element[3,2] = 0;
	element[3,3] = null;
	local class GEOREF vGeoref, rGeoref;
	local numeric dbErr;
	local class DATABASE vlineDB;		# vector line database
	local class DBTABLEINFO tInfo;	# class instance for selected line table
	local class DBFIELDINFO fInfo;	# class instance for selected field with elevation values
	local string outFilename$, outRastname$, outRastdesc$, tempFilename$;
	local string prompt$;
	local numeric rType;			# numeric flag for output data type for DEM
	local string rastType$;		# raster data type string
	local numeric cellSize;
	local numeric k, elev;
	# select the input vector object
	PopupMessage("Select a vector object containing the vector contours with elevations:");
	GetInputVector(Vect);
	vGeoref = GetLastUsedGeorefObject(Vect);
	if (vGeoref == 0) {
		CloseVector(Vect);
		PopupMessage("The vector you selected does not contain a valid georeference.  Exiting the script....");
		return;
	}
	# select the table and field in the line database that contains the elevation values
	vlineDB = OpenVectorLineDatabase(Vect);
	PopupMessage("Select line database table and field containing the contour line elevations:");
	dbErr = PopupSelectTableField(vlineDB, table$, field$);
	if (dbErr < 1) {		# exit script if user cancels the table selection dialog
		printf("No database table selected; exiting script.");
		Exit();
		}
	tInfo = DatabaseGetTableInfo(vlineDB, table$);
	fInfo = FieldGetInfoByName(tInfo, field$);
	# prompt for the output project file and name and description for the output DEM
	outFilename$ = GetOutputFileName("","Select or create a project file for the output DEM:", "rvc");
	outRastname$ = PopupString("Enter a name for the output DEM raster:", "DEM");
	outRastdesc$ = PopupString("Enter a description for the output DEM raster:", "");
	# prompt for and set the data type for the output DEM raster
	prompt$ = "Choose bit-depth of output elevation raster:\n" +
					"Press Yes to accept the default: 16-bit signed integer.\n" +
					"Press No to change to 32-bit floating point values.";
	rType = PopupYesNo(prompt$, 1);
	if (rType == 1) then
		rastType$ = "16-bit signed";
	else
		rastType$ = "32-bit float";
	# prompt for the cell size for the output DEM
	cellSize = PopupNum("Enter desired cell size of output raster in meters", 30, 0);
	# create temporary file for the contour raster R 
	tempFilename$ = CreateTempFileName();
	# create a blank contour raster A using the extents of the input vector and the specified cell size
	CreateRasterFromObject(Vect, A, tempFilename$, "RastCon", "", cellSize, cellSize, rastType$, vGeoref);
	rGeoref = GetLastUsedGeorefObject(A);
	numlins = NumLins(A);
	numcols = NumCols(A);
	# tranfer contour line elevation values to the contour raster
	for k = 1 to Vect.$Info.NumLines {
		elev = TableReadFieldNum(tInfo, field$, k);
		VectorElementToRaster(Vect, A, "line", k, elev, vGeoref, rGeoref);
		}
	# Use Max and Min values outside the boundaries of the contour line values -or- use the Max and Min values of the raster
	numeric max = GlobalMax(A) + 1;
	#numeric Max = Vect.$Info.MaxZ + 1;
	numeric min = GlobalMin(A);
	#numeric Min = Vect.$Info.MinZ - 1;
	# Create the necessary working objects
	CreateRaster(T, tempFilename$, "T", "T", numlins, numcols, rastType$);
	CreateRaster(U, tempFilename$, "U", "U", numlins, numcols, rastType$);
	CreateRaster(V, tempFilename$, "V", "V", numlins, numcols, "binary");
	CopyGeorefToObject(V, rGeoref);
	CreateRaster(W, tempFilename$, "W", "W", numlins, numcols, rastType$);
	CreateTempRaster(Temp, numlins, numcols, rastType$, 0); 
	while (done) {
		done = 0;
		numRuns++;
		# Filling all inter-contour cells in mask rasters with the maximum value 
		RasterCopy(A, W);
		RasterCopy(A, T);
		numeric i, j;
		for i = 1 to numlins {
			for j = 1 to numcols {
				if (A[i,j] == min) {
					W[i,j] = max;
					T[i,j] = max;
				}
			}
		}
		# use morphological functions to compute a set of medial lines for all contour pairs
		while ( MyErosion(W, Temp, max) > 0) { }
		MyDilation(W, U, T, max, min);
		MyRasterXOR(W, U, V);
		MyMedialLine(W, U, V);
		done = MyAddToAccumulator(W, V, A, min);
		print("Accumulator Count:", done, "\nRun Completed:", numRuns, "\n");
	}
	# close intermediate work rasters used in the morphological interpolation
	CloseRaster(T);
	CloseRaster(W);
	CloseRaster(U);
	CloseRaster(V);
	# Fill blank areas left after morphological interpolation
	CreateRaster(FILL, tempFilename$, "FILL", "FILL", numlins, numcols, rastType$);
	RasterCopy(A, FILL);
	# Set min values to maximum before filling by erosion
	for i = 1 to numlins {
		for j = 1 to numcols {
			if (FILL[i,j] == min) FILL[i,j] = max;
		}
	}
	while ( MyErosion(FILL, Temp, max) > 0 ) { } 
	CloseRaster(Temp);
	CreateRaster(DEM, outFilename$, outRastname$, outRastdesc$, numlins, numcols, rastType$);
	RasterCopy(FILL, DEM);
	CreatePyramid(DEM);
	CreateHistogram(DEM);
	CloseRaster(DEM);
	CloseRaster(A);
	CloseRaster(FILL);
}
######################################################################################################
# This function copies the input raster to the output raster (Rtemp) and morphologically
# erodes the output raster cells that have a starting value equal to the maskValue (inter-contour
# cells).  The function returns the number of cells that were eroded.
func MyErosion(class RASTER Rin, class RASTER Rtemp, numeric maskValue) {
	RasterCopy(Rin, Rtemp);
	local numeric r, c, er, ec, tempValue, newValue, count=0;
	for r = 1 to NumLins(Rin) {
		for c = 1 to NumCols(Rin) {
			newValue = maskValue;
			if (Rin[r,c] == maskValue) {
				count++;
				# For each cell in the element
				for er = -1 to 1 {
					for ec = -1 to 1 {
						# If the element value isn't null
						if (!IsNull(element[er+2,ec+2])) {
							# If the element is within the bounds of Rin
							if ( ((r + er) >= 1) and ((r + er) <= NumLins(W)) and ((c + ec) >= 1) and ((c + ec) <= NumCols(W)) ) {   
								tempValue = Rin[r + er, c + ec];
								if (tempValue < newValue) {
									newValue = tempValue;
								} # if
							} # if
						} # if
					} # for ec
				} # for er
				if (newValue != maskValue) {
					count--;
					Rtemp[r,c] = newValue;
				} # if
			} # for if
		} # for c
	} # for r
	RasterCopy(Rtemp, Rin);
	print("Erosion Count:", count);
	return count;
} # end MyErosion
######################################################################################################
# This procedure copies the input raster to an output raster and then morphologically
# dilates the masked cells in output raster.  
proc MyDilation(class RASTER Rin, class RASTER Rout, class RASTER Rmask, numeric maskValue, numeric minValue) {
	RasterCopy(Rin, Rout);
	local numeric r, c, er, ec, tempValue, newValue, count=0;
	for r = 1 to NumLins(Rin) {
		for c = 1 to NumCols(Rin) {
			newValue = minValue;
			if (Rmask[r,c] == maskValue) {
				# For each cell in the element
				for er = -1 to 1 {
					for ec = -1 to 1 {
						# If the element value isn't null
						if (!IsNull(element[er+2,ec+2])) {
							# If the element is within the bounds of Rin
							if ( ((r + er) >= 1) and ((r + er) <= NumLins(W)) and ((c + ec) >= 1) and ((c + ec) <= NumCols(W)) ) {   
								tempValue = Rin[r + er, c + ec];
								if (tempValue > newValue) {
									newValue = tempValue;
								} # if
							} # if
						} # if
					} # for ec
				} # for er
				if (newValue != minValue) {
					Rout[r,c] = newValue;
				} # if
			} # if
		} # for c
	} # for r
} # end of MyDilation
######################################################################################################
# This function compares the two input rasters and creates a binary output 
# raster where a value of 1 means that the input raster cell values are different.
proc MyRasterXOR(class RASTER Rin1, class RASTER Rin2, class RASTER Rout) {
	numeric i, j;
	for i = 1 to NumLins(Rin1) {
		for j = 1 to NumCols(Rin1) {
			if (Rin1[i,j] != Rin2[i,j]) {
				Rout[i,j] = 1;
			} else {
				Rout[i,j] = 0;
			}
		}
	}
} # end MyRasterXOR()
######################################################################################################
# This function sets the cells in the raster that symbolizes the new medial lines
# (Rin1) to the average value of the two contour values that created it. 
proc MyMedialLine(class RASTER Rin1, class RASTER Rin2, class RASTER Rmask) {
	numeric i, j;
	for i = 1 to NumLins(Rin1) {
		for j = 1 to NumCols(Rin1) {
			if (Rmask[i,j]) {
				Rin1[i,j] = (Rin2[i,j] + Rin1[i,j]) / 2;
			}
		}
	}
}
######################################################################################################
# This function adds the medial lines that were created to the final output raster
# (also used as the starting raster for the next iteration through the process).  It returns the 
# number of new cell values added to the output raster.
func MyAddToAccumulator(class RASTER Rin, class RASTER Rmask, class RASTER Rout, numeric intContValue) {
	numeric i, j, count = 0;
	for i = 1 to NumLins(Rin) {
		for j = 1 to NumCols(Rin) {
			if ( (Rmask[i,j]) and (Rout[i,j] == intContValue) ) {
				Rout[i,j] = Rin[i,j];
				count++;
			}
		}
	}
	return count;
}
printf ("%s %.2f %.2f %.2f", "Global Mean, Median and Variance values are:", GlobalMean (DEM), GlobalMedian (DEM),GlobalVariance(DEM));