1 //----------------------------------------------------------------------------
2 //
3 // License:  See top level LICENSE.txt file.
4 //
5 // Author:  Oscar Kramer
6 //
7 // Description:
8 //
9 // Command line application "ossim-create-bitmask" to build overviews.
10 //
11 //----------------------------------------------------------------------------
12 // $Id: ossim-create-bitmask.cpp 3081 2011-12-22 16:34:12Z oscar.kramer $
13 
14 #include <ossim/ossimConfig.h>
15 #include <ossim/base/ossimCommon.h>
16 #include <ossim/base/ossimArgumentParser.h>
17 #include <ossim/base/ossimApplicationUsage.h>
18 #include <ossim/base/ossimFilename.h>
19 #include <ossim/init/ossimInit.h>
20 #include <ossim/base/ossimNotify.h>
21 #include <ossim/base/ossimKeywordNames.h>
22 #include <ossim/base/ossimStdOutProgress.h>
23 #include <ossim/imaging/ossimBitMaskWriter.h>
24 #include <ossim/imaging/ossimImageHandlerRegistry.h>
25 #include <ossim/imaging/ossimImageSourceSequencer.h>
26 #include <ossim/imaging/ossimPixelFlipper.h> // for its keywords
27 #include <cstdio>
28 #include <cstdlib> /* for exit */
29 
30 using namespace std;
31 
32 //*************************************************************************************************
33 // FINALIZE -- Convenient location for placing debug breakpoint for catching program exit.
34 //*************************************************************************************************
finalize(int code)35 void finalize(int code)
36 {
37    exit (code);
38 }
39 
40 //*************************************************************************************************
41 // MAIN
42 //*************************************************************************************************
main(int argc,char * argv[])43 int main(int argc, char* argv[])
44 {
45    ossimString tempString;
46    double tempDouble, tempDouble2;
47    ossimArgumentParser::ossimParameter stringParam(tempString);
48    ossimArgumentParser::ossimParameter doubleParam(tempDouble);
49    ossimArgumentParser::ossimParameter doubleParam2(tempDouble2);
50 
51    ossimArgumentParser argumentParser(&argc, argv);
52    ossimInit::instance()->addOptions(argumentParser);
53    ossimInit::instance()->initialize(argumentParser);
54 
55    argumentParser.getApplicationUsage()->setApplicationName(argumentParser.getApplicationName());
56    argumentParser.getApplicationUsage()->setDescription(argumentParser.getApplicationName()+
57       " Generates a bit-mask given source image and target pixel range to mask out. If the input"
58       " image has overviews, then masks will be generated for all R-levels.");
59    argumentParser.getApplicationUsage()->setCommandLineUsage(argumentParser.getApplicationName()+
60       " [options] <input file>");
61 
62    argumentParser.getApplicationUsage()->addCommandLineOption("-d",
63       "Write mask to directory specified.");
64 
65    argumentParser.getApplicationUsage()->addCommandLineOption("-e or --entry",
66       "Give the entry(zero based) for which to build a mask. Only one entry can be processed. "
67       "If the input is multi-entry and no entry was specified, entry 0 is assumed.");
68 
69    argumentParser.getApplicationUsage()->addCommandLineOption("-h or --help",
70       "Shows help");
71 
72    argumentParser.getApplicationUsage()->addCommandLineOption("--mask-mode <mode>",
73       "Specifies how to treat multi-band imagery when determining whether pixel will be masked "
74       "according to the defined target value or range. Possible modes are: "
75       "\"mask_full_and_partial_targets\" (default) | \"mask_only_full_targets\".");
76 
77    argumentParser.getApplicationUsage()->addCommandLineOption("--mask-range <min> <max>",
78       "Specifies the range of pixels to target for masking out.");
79 
80    argumentParser.getApplicationUsage()->addCommandLineOption("--mask-value <target>",
81       "Specifies the unique pixel value to target for masking out.");
82 
83    argumentParser.getApplicationUsage()->addCommandLineOption("-o",
84       "Write mask to file specified.  If used on a multi-entry file, given \"foo.mask\" you will "
85       "get: \"foo_e0.mask\". If none specified, the input filename is used with \".mask\" "
86       "extension.");
87 
88    argumentParser.getApplicationUsage()->addCommandLineOption("--ovr-from-image",
89       "Uses exclusively the image overview data when computing subsequent overviews. "
90       "Normally the mask overview from the prior level is referenced for establishing the masks at "
91       "the next level.");
92 
93    argumentParser.getApplicationUsage()->addCommandLineOption("--spec-kwl <filename>",
94       "In lieu of providing mask parameters on the command line, this option specifies a keyword "
95       "list filename that contains all settings. Typically used when spawning from other process.");
96 
97    argumentParser.getApplicationUsage()->addCommandLineOption("-x or --exclude-fullres",
98       "Excludes R0 mask computation. The mask file will start at R1.");
99 
100    if(argumentParser.read("-h") || argumentParser.read("--help"))
101    {
102       argumentParser.getApplicationUsage()->write(std::cout);
103       finalize(0);
104    }
105 
106    if ( argumentParser.read("--version") || argumentParser.read("-v"))
107    {
108       ossimNotify(ossimNotifyLevel_NOTICE)<< argumentParser.getApplicationName().c_str() << " "
109          << ossimInit::instance()->instance()->version().c_str()<< std::endl;
110       finalize(0);
111    }
112 
113    // Fetch command line options:
114    ossimFilename outputFile;
115    if (argumentParser.read("-o", stringParam))
116       outputFile = tempString.trim();
117 
118    ossimFilename outputDir;
119    if (argumentParser.read("-d", stringParam))
120       outputDir = tempString.trim();
121 
122    bool exclude_r0 = false;
123    if (argumentParser.read("-x") || argumentParser.read("--exclude-fullres"))
124       exclude_r0 = true;
125 
126    bool entry_specified = false;
127    ossim_int32 entry = 0;
128    if (argumentParser.read("-e", stringParam) || argumentParser.read("--entry", stringParam))
129    {
130       entry = ossimString(tempString).toUInt32();
131       entry_specified = true;
132    }
133 
134    double target_min = 0;
135    double target_max = 0;
136    if (argumentParser.read("--mask-range", doubleParam, doubleParam2))
137    {
138       target_min = tempDouble;
139       target_max = tempDouble2;
140    }
141 
142    if (argumentParser.read("--mask-value", doubleParam))
143    {
144       target_min = tempDouble;
145       target_max = target_min;
146    }
147 
148    ossimString mask_mode = "REPLACE_ALL_BANDS_IF_ANY_TARGET";
149    ossimString mask_mode_arg;
150    if (argumentParser.read("--mask-mode", stringParam))
151    {
152       mask_mode_arg = tempString;
153       if (mask_mode_arg == "mask_full_and_partial_targets")
154          mask_mode = "REPLACE_ALL_BANDS_IF_ANY_TARGET";
155       else if (mask_mode_arg == "mask_only_full_targets")
156          mask_mode = "REPLACE_ONLY_FULL_TARGETS";
157       else
158       {
159          ossimNotify(ossimNotifyLevel_NOTICE)<< argumentParser.getApplicationName().c_str() << " "
160             << " Unknown mask-mode <"<<mask_mode_arg<<"> specified. See usage below." << std::endl;
161          argumentParser.getApplicationUsage()->write(std::cout);
162          finalize(1);
163       }
164    }
165 
166    bool ovr_from_image = false;
167    if (argumentParser.read("--ovr-from-image"))
168       ovr_from_image = true;
169 
170    ossimFilename spec_kwl_file;
171    if (argumentParser.read("--spec_kwl", stringParam))
172       spec_kwl_file = tempString.trim();
173 
174    // Handle bad command line:
175    argumentParser.reportRemainingOptionsAsUnrecognized();
176    if (argumentParser.errors())
177    {
178       argumentParser.writeErrorMessages(std::cout);
179       finalize(1);
180    }
181    if ((argumentParser.argc()<2) && spec_kwl_file.empty())
182    {
183       argumentParser.getApplicationUsage()->write(std::cout);
184       finalize(1);
185    }
186 
187    // Establish input filename:
188    ossimFilename inputFile;
189    ossimKeywordlist kwl;
190    if (spec_kwl_file.isReadable())
191    {
192       kwl.addFile(spec_kwl_file);
193       inputFile = kwl.find(ossimKeywordNames::IMAGE_FILE_KW);
194       outputFile = kwl.find(ossimKeywordNames::OUTPUT_FILE_KW);
195    }
196    else
197    {
198       inputFile = argv[1];
199    }
200 
201    // Establish the input image handler:
202    ossimRefPtr<ossimImageHandler> handler = ossimImageHandlerRegistry::instance()->open(inputFile);
203    if (!handler.valid())
204    {
205       ossimNotify(ossimNotifyLevel_FATAL)<<argumentParser.getApplicationName().c_str()
206          <<" Error encountered opening input file <"<<inputFile<<">."<<endl;
207       finalize(1);
208    }
209 
210    // Establish output filename:
211    if (outputFile.empty())
212       outputFile = handler->getFilenameWithThisExtension("mask", entry_specified);
213    if (!outputDir.empty())
214       outputFile.setPath(outputDir);
215    else
216    {
217       ossimFilename path (outputFile.path());
218       if (path.empty())
219          outputFile.setPath(inputFile.path());
220    }
221 
222    // Consider input file with multiple entries:
223    std::vector<ossim_uint32> entryList;
224    handler->getEntryList(entryList);
225    if (entryList.size() <= entry)
226    {
227       ossimNotify(ossimNotifyLevel_FATAL)<<argumentParser.getApplicationName().c_str()
228          <<" Entry specified <"<<entry<<"> exceeds total number of entries available <"
229          <<entryList.size()<<">. Aborting..."<<endl;
230       finalize(1);
231    }
232    handler->setCurrentEntry(entry);
233 
234 
235    // Establish a keywordlist to pass to the mask builder. This KWL may have already been specified
236    // on the command line with --spec_kwl option:
237    kwl.add(ossimKeywordNames::OUTPUT_FILE_KW, outputFile.chars()); // may overwrite same value
238    if (kwl.getSize() == 0)
239    {
240       kwl.add(ossimKeywordNames::IMAGE_FILE_KW, inputFile.chars());
241       ossimString target_str (ossimString::toString(target_min)+" "+ossimString::toString(target_max));
242       kwl.add(ossimPixelFlipper::PF_TARGET_RANGE_KW, target_str.chars());
243       kwl.add(ossimPixelFlipper::PF_REPLACEMENT_MODE_KW, mask_mode.chars());
244       kwl.add(ossimPixelFlipper::PF_REPLACEMENT_VALUE_KW, (int) 0);
245       if (exclude_r0)
246          kwl.add(ossimBitMaskWriter::BM_STARTING_RLEVEL_KW, (int) 1);
247       else
248          kwl.add(ossimBitMaskWriter::BM_STARTING_RLEVEL_KW, (int) 0);
249    }
250 
251    // Instantiate the bit mask processor and write out the mask file:
252    ossimRefPtr<ossimBitMaskWriter> mask_writer = new ossimBitMaskWriter;
253    mask_writer->loadState(kwl);
254    mask_writer->connectMyInputTo(handler.get());
255 
256    // Need to loop over all R-levels. Use a sequencer:
257    ossimRefPtr<ossimImageSourceSequencer> sequencer =  new ossimImageSourceSequencer(handler.get());
258    sequencer->initialize();
259    int num_rlevels = handler->getNumberOfDecimationLevels();
260    int num_tiles = sequencer->getNumberOfTiles();
261    int tile_idx = 0;
262    int percent_complete = 0;
263    ossimRefPtr<ossimImageData> tile = 0;
264 
265    int start_res = 0;
266    if (exclude_r0)
267    {
268       start_res = 1;
269       num_tiles = (num_tiles+3)/4;
270    }
271 
272    for (int r=start_res; r<num_rlevels; r++)
273    {
274       ossimNotify(ossimNotifyLevel_NOTICE)<<"\nProcessing R-level "<<r<<"... "<<endl;
275 
276       // Set the area of interest to the full image rectangle at this R-level:
277       ossimIrect rect (handler->getBoundingRect(r));
278       sequencer->setAreaOfInterest(rect);
279       sequencer->setToStartOfSequence();
280       do
281       {
282          tile = sequencer->getNextTile(r);
283          mask_writer->generateMask(tile, r);
284 
285          percent_complete = 100 * tile_idx++/num_tiles;
286          ossimNotify(ossimNotifyLevel_NOTICE)<<percent_complete<< "%\r";
287       }
288       while(tile.valid());
289       tile_idx = 0;
290       num_tiles = (num_tiles+3)/4;
291       if (num_tiles == 0)
292          num_tiles = 1;
293 
294       // Check if additional overviews are to be generated directly from the mask at first R-level:
295       if ((r == start_res) && !ovr_from_image)
296       {
297          ossimNotify(ossimNotifyLevel_NOTICE)<<"\nBuilding remaining overviews from initial mask..."
298             <<endl;
299          mask_writer->buildOverviews(num_rlevels);
300          break;
301       }
302    }
303 
304    // Finished sequencing all levels, ready to write out the mask buffers:
305    mask_writer->close();
306    ossimNotify(ossimNotifyLevel_NOTICE)<<"\nSuccessfully wrote mask file to <"<<outputFile
307       <<">. Finished."<<endl;
308 
309    finalize(0);
310 }
311