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