ThresholdNearWhite.sml

  Download

More scripts: Advanced

Syntax Highlighing:

comments, key words, predefined symbols, class members & methods, functions & classes
            
# ThresholdNearWhite.sml
# Script to process scanned topographic maps (24-bit color composite
# rasters in RVC format) to set near-white cells to white (255,255,255)
# Processes all Project Files in input directory and writes each output
# raster to its own Project File in the designated output directory. 
# Each input Project File is assumed to include one 24-bit color composite 
# raster; the script checks for 24-bit raster type and skips raster if other bit-depth.
# User sets three numerical thresholds (any order) to define lower limit
# of RGB values to define white. For example, for thresholds 245, 247, 249,
# any cell with set of RGB values with each component value equal to or greater than
# one of the three thresholds (e.g. R = 249, G = 245, B = 247) is set to 255,255,255.
# Processing results are reported in the dialog window (number and percentage of cells
# set to white) and written to a log file in the output directory.   
# Threshold values entered in dialog are saved in the tntproc.ini
# file and read automatically the next time the script is run.
# Version 21 September 2010
# Randy Smith, MicroImages, Inc.
# requires TNTmips version 2009:75.
class STRING inDir$, outDir$;
numeric err;
class STRINGLIST inMapList;
class STRING logfilename$;										# name of log file
class FILE logfile;
numeric changeCount, pct;			# number and percentage of cells adjusted
class DATETIME dt;
# classes for dialog and its components
class GUI_DLG dlgwin;
class GUI_CTRL_PUSHBUTTON outDirBtn;
class GUI_CTRL_PUSHBUTTON runBtn, cancelBtn, closeBtn;
class GUI_CTRL_EDIT_STRING indirtext, outdirtext;
class GUI_CTRL_LISTBOX MapListbox, Proclistbox, CellsSetListbox;
class GUI_CTRL_EDIT_NUMBER numMaps, numProc;
class GUI_CTRL_EDIT_NUMBER t1, t2, t3;
class GUI_CTRL_EDIT_STRING statustext;
class STATUSDIALOG statusD;
class STATUSCONTEXT statusC;
array numeric thresholds[3];
###############   User-defined Procedures   #############
##########################################
# error checking procedure
proc ReportError(numeric linenum, numeric err) {
	printf("FAILED -line: %d, error: %d\n", linenum - 1, err);
	PopupError(err); 
	}
##############################
# function to sort array increasing
func Sort(numeric A, numeric B)
	{
	return (A<=B);
	}
############################
### procedure to get the input directory
proc GetInDirectory ()
	{
	local numeric i;
	inDir$ = GetDirectory("", "Select directory with TNT Project files with map raster objects:");
	printf("Input directory = %s\n", inDir$);
	class FILEPATH inDirPath(inDir$);
	indirtext.SetValueStr(inDir$);				# write input directory name to dialog
	# clear dialog listboxes if they are still populated from a previous run
	if (MapListbox.GetCount() > 0) then
		MapListbox.DeleteAllItems();
	if (Proclistbox.GetCount() > 0)
			{ 
			Proclistbox.DeleteAllItems();
			numProc.SetValueNum(0);
			}
	outDir$ = "";
	outdirtext.SetValueStr("");		# clear output directory text field 
	statustext.SetValueStr("");		# clear status message text field
	inMapList = inDirPath.GetFileList("*.rvc");
	for i = 1 to 	inMapList.GetNumItems()
		{
		MapListbox.AddItem( inMapList[i-1] );		# add filename to listbox in dialog
		}
	# write number of files found to the dialog
	numMaps.SetValueNum(inMapList.GetNumItems() );
	# enable Output Directory and Cancel buttons
	outDirBtn.SetEnabled(1);
	cancelBtn.SetEnabled(1);
	}
############################
### procedure to get the output directory
proc GetOutDirectory ()
	{
	outDir$ = GetDirectory("", "Select output directory:");
	printf("Output directory = %s\n", outDir$);
	class FILEPATH outDirPath(outDir$);
	outdirtext.SetValueStr(outDir$);		# write output directory name to dialog
	# open log file for this run
	dt.SetCurrent();
	dt.ConvertToLocal();
	local class STRING date$ = sprintf("%d", dt.GetDateYYYYMMDD() );
	local class STRING time$;
	time$ = sprintf("%d%d%d", dt.GetHour(), dt.GetMin(), dt.GetSec() );
	logfilename$ = sprintf("%s/%s_%s.log", outDir$, date$, time$);
	print(logfilename$);
	logfile = fopen(logfilename$, "a");
	fprint(logfile, "Log for script setting near-white values to white.");
	fprintf(logfile, "Input directory = %s\n", inDir$);
	fprintf(logfile, "Output directory = %s\n", outDir$);
	fprintf(logfile, "Number of map files found = %d\n", inMapList.GetNumItems() );
	# enable Run button
	runBtn.SetEnabled(1);
	}
####################################################
### user-defined delegate function called by GENERAL_INPLACE filter
func setwhite(class IMAGE_PIPELINE_SAMPLEITERATOR Iterator, numeric valid)
	{
	if (valid)	# ignore null cells
		{
		local numeric r, g, b;
		r = Iterator[0].GetValue();
		g = Iterator[1].GetValue();
		b = Iterator[2].GetValue();
		if (r < 255 or g < 255 or b <255)	# only check non-white cells
			{
			if ( r >= thresholds[1] && 
				( (g >= thresholds[2] && b >= thresholds[3] ) or (b >= thresholds[2] && g >= thresholds[3] )  ) ) 
				{
				Iterator[0].PutValueRound(255);
				Iterator[1].PutValueRound(255);
				Iterator[2].PutValueRound(255);
				++changeCount;
				}
			else 
			if ( g >= thresholds[1] &&
				( (r >= thresholds[2] && b >= thresholds[3] ) or (b >= thresholds[2] && r >= thresholds[3] ) ) )
				{
				Iterator[0].PutValueRound(255);
				Iterator[1].PutValueRound(255);
				Iterator[2].PutValueRound(255);
				++changeCount;
				}
			else
			if ( b >= thresholds[1] &&
				( (r >= thresholds[2] && g >= thresholds[3] ) or (g >= thresholds[2] && r >= thresholds[3] ) ) )
				{
				Iterator[0].PutValueRound(255);
				Iterator[1].PutValueRound(255);
				Iterator[2].PutValueRound(255);
				++changeCount;
				}
			}
		return true;
		}
	else
		return false;
	}
###################################################################
### procedure to process the rasters, called by pressing Run button
proc onRun()
	{
	local numeric success;
	statusD.Create();
	statusC = statusD.CreateContext();
	###########################################
	### loop through the list of input map rasters to adjust near-white colors to white
	local numeric i;   # counter
	thresholds[1] = dlgwin.GetCtrlByID("t1").GetValueNum();
	thresholds[2] = dlgwin.GetCtrlByID("t2").GetValueNum();
	thresholds[3] = dlgwin.GetCtrlByID("t3").GetValueNum();
	# write current thresholds to tntproc.ini
	IniWriteString("SMLmakeWhite", "Threshold1", NumToStr(thresholds[1]));
	IniWriteString("SMLmakeWhite", "Threshold2", NumToStr(thresholds[2]));
	IniWriteString("SMLmakeWhite", "Threshold3", NumToStr(thresholds[3]));
	# sort the array of threshold values in increasing order
	SortArray(thresholds, Sort);
	printf("White thresholds in increasing order: %d, %d, %d\n", thresholds[1], thresholds[2], thresholds[3] );
	for i = 1 to inMapList.GetNumItems()
		{
		fprintf(logfile, "\nMap file %d: %s\n", i, inMapList[i-1] );
		printf("\nMap file %d: %s\n", i, inMapList[i-1]);
		# set file and object paths for the current input map raster
		local class STRING mapFileName$ = inMapList[i-1];		# name of file (with extension) without path
		local class STRING mapFilePath$ = inDir$ + "/" + mapFileName$;		# add directory path
		local class FILEPATH mapFilePath(mapFilePath$);			# full filepath to map file
		printf("mapFilePath$ = %s\n", mapFilePath$);
		local class FILEPATH mapNameFilepath = mapFileName$;	# filepath for input file without path
		local class STRING mapName$ = mapFilePath.GetNameOnly();		# filename without extension
		statusC.Message = sprintf("Processing map %d, %s...", i, mapFileName$);
		# open the map raster from its filepath and object path
		local class STRINGLIST namelist;
		namelist = GetAllObjectNames(mapFilePath$, "RASTER");  # get list of raster object paths in input file  
		local class STRING mapObjPath$ = namelist[0]; 	# get path from first raster (should be only 1)
		printf("mapObjPath$ = %s\n", mapObjPath$);
		class RVC_RASTER MapRast;
		err = MapRast.OpenByName(mapFilePath, mapObjPath$, "Read");
		if (err < 0) ReportError(_context.CurrentLineNum, err);
		else printf("Map raster %d opened\n", i);
		if (MapRast.GetNumBits() == 24)	# check that raster is 24-bit composite
			{
			# construct filepath and make output Project File
			class FILEPATH outFilepath(outDir$);
			outFilepath.Append(mapFileName$);
			printf("Output filepath = %s\n", outFilepath);
			class RVC_OBJECT outFile;
			outFile.MakeFile(outFilepath, "");
			# construct objItem for output raster
			class RVC_OBJITEM outObjItem;
			class RVC_DESCRIPTOR outrastDescript;
			outrastDescript.SetName(mapName$);
			outrastDescript.SetDescription("");
			outObjItem.CreateNew(outFile.GetObjItem(), "RASTER", outrastDescript); 
			# re-initialize count of cells set to white by pipeline filter function
			changeCount = 0;
			# PIPELINE SOURCE
			class IMAGE_PIPELINE_SOURCE_RVC MapSource(MapRast.GetObjItem() );
			err = MapSource.Initialize();
			if (err < 0) ReportError(_context.CurrentLineNum, err);
			# PIPELINE FILTER
			class IMAGE_PIPELINE_FILTER_GENERAL_INPLACE filtSetWhite(MapSource, setwhite);
			err = filtSetWhite.Initialize();
			if (err < 0) ReportError(_context.CurrentLineNum, err);
			# PIPELINE TARGET
			class IMAGE_PIPELINE_TARGET_RVC targetRVC(filtSetWhite, outObjItem);
			err = targetRVC.Initialize();
			if (err < 0) ReportError(_context.CurrentLineNum, err);
			targetRVC.Process();
			MapRast.Close();
			if (changeCount >0) then
				++success;
			pct = 100 * changeCount / (MapRast.$Info.NumLins * MapRast.$Info.NumCols);
			numProc.SetValueNum(success);
			Proclistbox.AddItem( inMapList[i-1] );
			CellsSetListbox.AddItem( sprintf("%d (%.1f\%)", changeCount, pct) );
			fprintf(logfile, "Map %s processed.\n", inMapList[i-1]);
			fprintf(logfile, "%d cells changed to white.\n", changeCount);
			}
		else
			{
			fprintf(logfile, "Map %s is not a 24-bit composite raster.\n", inMapList[i-1]);
			printf("Map %s is not a 24-bit composite raster.\n", inMapList[i-1]);
			}
		}
	statusD.Destroy();
	statustext.SetValueStr("Processing complete.");
	runBtn.SetEnabled(0);
	cancelBtn.SetEnabled(0);
	} # end of onRun()
############################
### procedure called by the Cancel button
proc onCancel()
	{
	fclose(logfile);
	dlgwin.Close(0);
	Exit();
	}
############################
### procedure called by the Close button
proc onClose()
	{
	fclose(logfile);
	dlgwin.Close(0);
	Exit();
	}
##################################################
#################   Main Program   #####################
clear();
class STRING xml$='<?xml version="1.0"?>
<!DOCTYPE root SYSTEM "smlforms.dtd">
<root>
	<dialog id="dlgMakeWhite" Title="Cleanup Near-White" Buttons="">
		<pane Orientation="Horizontal" HorizResize="Fixed">
			<pushbutton Name=" Input Directory " OnPressed="GetInDirectory()"/>
			<edittext id="indirtext" width="40" ReadOnly="true"/>
		</pane>
		<pane Orientation="Horizontal" HorizResize="Fixed">
			<pushbutton id="outDirBtn" Name=" Output Directory " Enabled="false" OnPressed="GetOutDirectory()"/>
			<edittext id="outdirtext" Width="40" ReadOnly="true"/>
		</pane>
		<groupbox>
			<label>Set three threshold values for RGB (in any order) to define "white":</label>
			<pane Orientation="Horizontal" HorizResize="Expand">
				<editnumber id="t1" HorizResize="Fixed"  Width="3" Precision="0" Default="254" MinVal="0" MaxVal="255"/><label HorizResize="Expand"> Threshold 1</label>
				<editnumber id="t2" HorizResize="Fixed"  Width="3" Precision="0" Default="254" MinVal="0" MaxVal="255"/><label HorizResize="Expand"> Threshold 2</label>
				<editnumber id="t3" HorizResize="Fixed"  Width="3" Precision="0" Default="254" MinVal="0" MaxVal="255"/><label HorizResize="Expand"> Threshold 3</label>
			</pane>
		</groupbox>
		<groupbox ExtraBorder="3">
			<pane Orientation="Horizontal" HorizResize="Fixed">
				<pane Orientation="Vertical" HorizResize="Fixed" VertResize="Fixed">
					<listbox id="MapListbox" Width="20"/>
					<pane Orientation="Horizontal" HorizResize="Fixed">
						<editnumber id="numMaps" Width="2" Precision="0" BlankZero="true" ReadOnly="true"/>
						<label> Map raster objects   </label>
					</pane>
				</pane>
				<pane Orientation="Vertical" HorizResize="Fixed" VertResize="Fixed">
					<listbox id="Proclistbox" Width="25"/>
					<pane Orientation="Horizontal" HorizResize="Fixed">
						<editnumber id="numProc" Width="2" Precision="0" BlankZero="true" ReadOnly="true"/>
						<label>maps processed</label>
					</pane>
				</pane>
				<pane Orientation="Vertical" HorizResize="Fixed" VertResize="Fixed">
					<listbox id="CellsSetListbox" Width="20"/>
					<label>Cells set white</label>
				</pane>
			</pane>
		</groupbox>
		<pane Orientation="Horizontal" HorizAlign="left">
			<edittext id="statustext" Width="20" ReadOnly="true"/>
		</pane>
		<pane Orientation="Horizontal" HorizAlign="right">
			<pushbutton id="runBtn" Name=" Run " Enabled="false" OnPressed="onRun()"/>
			<pushbutton id="cancelBtn" Name=" Cancel " Enabled="false" OnPressed="onCancel()"/>
			<pushbutton id="closeBtn" Name=" Close " OnPressed="onClose()"/>
		</pane>
	</dialog>
</root>';
### parse XML text for the dialog into memory
class XMLDOC dlgdoc;
err = dlgdoc.Parse(xml$); 
### get the dialog element from the parsed XML document
class XMLNODE dlgnode;
dlgnode = dlgdoc.GetElementByID("dlgMakeWhite");
### set the dialog XML element as the source for the GUI_DLG class instance
dlgwin.SetXMLNode(dlgnode);
dlgwin.CreateModeless();
### get handles for dialog controls
outDirBtn = dlgwin.GetCtrlByID("outDirBtn");
indirtext = dlgwin.GetCtrlByID("indirtext");
outdirtext = dlgwin.GetCtrlByID("outdirtext");
numMaps = dlgwin.GetCtrlByID("numMaps");
numProc = dlgwin.GetCtrlByID("numProc");
MapListbox = dlgwin.GetCtrlByID("MapListbox");
Proclistbox = dlgwin.GetCtrlByID("Proclistbox");
CellsSetListbox = dlgwin.GetCtrlByID("CellsSetListbox");
statustext = dlgwin.GetCtrlByID("statustext");
runBtn = dlgwin.GetCtrlByID("runBtn");
cancelBtn = dlgwin.GetCtrlByID("cancelBtn");
closeBtn = dlgwin.GetCtrlByID("closeBtn");
t1 = dlgwin.GetCtrlByID("t1");
t2 = dlgwin.GetCtrlByID("t2");
t3 = dlgwin.GetCtrlByID("t3");
# read saved threshold values from tntproc.ini and set in dialog before opening
class STRING t1$, t2$, t3$;
t1$ = IniReadString("SMLmakeWhite", "Threshold1", "254");
t2$ = IniReadString("SMLmakeWhite", "Threshold2", "254");
t3$ = IniReadString("SMLmakeWhite", "Threshold3", "254");
t1.SetValue(StrToNum(t1$), 0);
t2.SetValue(StrToNum(t2$), 0);
t3.SetValue(StrToNum(t3$), 0);
dlgwin.Open();
WaitForExit();