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();