1 //*******************************************************************
2 //
3 // License: See LICENSE.txt file in the top level directory.
4 //
5 // Author: Garrett Potts
6 //
7 //*************************************************************************
8 // $Id: ossimImageHistogramSource.cpp 22737 2014-04-16 18:53:57Z gpotts $
9
10 #include <ossim/imaging/ossimImageHistogramSource.h>
11 #include <ossim/base/ossimMultiResLevelHistogram.h>
12 #include <ossim/base/ossimMultiBandHistogram.h>
13 #include <ossim/imaging/ossimImageData.h>
14 #include <ossim/imaging/ossimImageSourceSequencer.h>
15 #include <ossim/base/ossimCommon.h>
16 #include <ossim/base/ossimNotify.h>
17 #include <ossim/base/ossimTrace.h>
18
19 using namespace std;
20
21 static ossimTrace traceDebug("ossimImageHistogramSource:debug");
22
23 RTTI_DEF3(ossimImageHistogramSource, "ossimImageHistogramSource", ossimHistogramSource, ossimConnectableObjectListener, ossimProcessInterface);
24
ossimImageHistogramSource(ossimObject * owner)25 ossimImageHistogramSource::ossimImageHistogramSource(ossimObject* owner)
26 :ossimHistogramSource(owner,
27 1, // one input
28 0, // no outputs
29 true, // input list is fixed
30 false),// output can still grow though
31 theHistogramRecomputeFlag(true),
32 theMaxNumberOfResLevels(1),
33 theComputationMode(OSSIM_HISTO_MODE_NORMAL)
34 // theNumberOfTilesToUseInFastMode(100)
35 {
36 theAreaOfInterest.makeNan();
37 addListener((ossimConnectableObjectListener*)this);
38
39 theMinValueOverride = ossim::nan();
40 theMaxValueOverride = ossim::nan();
41 theNumberOfBinsOverride = -1;
42 }
43
~ossimImageHistogramSource()44 ossimImageHistogramSource::~ossimImageHistogramSource()
45 {
46 removeListener((ossimConnectableObjectListener*)this);
47 }
48
getObject()49 ossimObject* ossimImageHistogramSource::getObject()
50 {
51 return this;
52 }
53
getObject() const54 const ossimObject* ossimImageHistogramSource::getObject()const
55 {
56 return this;
57 }
58
setAreaOfInterest(const ossimIrect & rect)59 void ossimImageHistogramSource::setAreaOfInterest(const ossimIrect& rect)
60 {
61 if(rect != theAreaOfInterest)
62 {
63 theHistogramRecomputeFlag = true;
64 }
65 theAreaOfInterest = rect;
66 }
67
getAreaOfInterest() const68 ossimIrect ossimImageHistogramSource::getAreaOfInterest()const
69 {
70 return theAreaOfInterest;
71 }
72
getAreaOfInterest(ossimIrect & rect) const73 void ossimImageHistogramSource::getAreaOfInterest(ossimIrect& rect)const
74 {
75 rect = theAreaOfInterest;
76 }
77
getMaxNumberOfRLevels() const78 ossim_uint32 ossimImageHistogramSource::getMaxNumberOfRLevels()const
79 {
80 return theMaxNumberOfResLevels;
81 }
82
setMaxNumberOfRLevels(ossim_uint32 number)83 void ossimImageHistogramSource::setMaxNumberOfRLevels(ossim_uint32 number)
84 {
85 if(number != theMaxNumberOfResLevels)
86 {
87 theHistogramRecomputeFlag = true;
88 }
89 theMaxNumberOfResLevels = number;
90 }
91
getHistogram(const ossimIrect & rect)92 ossimRefPtr<ossimMultiResLevelHistogram> ossimImageHistogramSource::getHistogram(const ossimIrect& rect)
93 {
94 if((theAreaOfInterest != rect)||
95 (theAreaOfInterest.hasNans()))
96 {
97 theAreaOfInterest = rect;
98 theHistogramRecomputeFlag = true;
99 }
100
101 return getHistogram();
102 }
103
execute()104 bool ossimImageHistogramSource::execute()
105 {
106 if(!isSourceEnabled())
107 {
108 return theHistogram.valid();
109 }
110
111 setProcessStatus(ossimProcessInterface::PROCESS_STATUS_EXECUTING);
112 if(theHistogramRecomputeFlag)
113 {
114 if(theAreaOfInterest.hasNans())
115 {
116 ossimImageSource* interface = PTR_CAST(ossimImageSource, getInput(0));
117 if(interface)
118 {
119 theAreaOfInterest = interface->getBoundingRect();
120 }
121 }
122
123 switch(theComputationMode)
124 {
125 case OSSIM_HISTO_MODE_FAST:
126 {
127 computeFastModeHistogram();
128 break;
129 }
130 case OSSIM_HISTO_MODE_NORMAL:
131 default:
132 {
133 computeNormalModeHistogram();
134 break;
135 }
136 }
137 }
138
139 if (needsAborting())
140 {
141 setProcessStatus(ossimProcessInterface::PROCESS_STATUS_ABORTED);
142 theHistogramRecomputeFlag = false;
143 }
144 else
145 {
146 setProcessStatus(ossimProcessInterface::PROCESS_STATUS_NOT_EXECUTING);
147 }
148
149 return true;
150 }
151
canConnectMyInputTo(ossim_int32 myInputIndex,const ossimConnectableObject * object) const152 bool ossimImageHistogramSource::canConnectMyInputTo(ossim_int32 myInputIndex,
153 const ossimConnectableObject* object)const
154 {
155 return ((myInputIndex==0)&&PTR_CAST(ossimImageSource,
156 object));
157 }
158
setNumberOfBinsOverride(ossim_int32 numberOfBinsOverride)159 void ossimImageHistogramSource::setNumberOfBinsOverride(ossim_int32 numberOfBinsOverride)
160 {
161 theNumberOfBinsOverride = numberOfBinsOverride;
162 }
163
setMinValueOverride(ossim_float32 minValueOverride)164 void ossimImageHistogramSource::setMinValueOverride(ossim_float32 minValueOverride)
165 {
166 theMinValueOverride = minValueOverride;
167 }
setMaxValueOverride(ossim_float32 maxValueOverride)168 void ossimImageHistogramSource::setMaxValueOverride(ossim_float32 maxValueOverride)
169 {
170 theMaxValueOverride = maxValueOverride;
171 }
172
getComputationMode() const173 ossimHistogramMode ossimImageHistogramSource::getComputationMode()const
174 {
175 return theComputationMode;
176 }
177
setComputationMode(ossimHistogramMode mode)178 void ossimImageHistogramSource::setComputationMode(ossimHistogramMode mode)
179 {
180 theComputationMode = mode;
181 }
182
propertyEvent(ossimPropertyEvent &)183 void ossimImageHistogramSource::propertyEvent(ossimPropertyEvent& /* event */)
184 {
185 theHistogramRecomputeFlag = true;
186 }
187
connectInputEvent(ossimConnectionEvent &)188 void ossimImageHistogramSource::connectInputEvent(ossimConnectionEvent& /* event */)
189 {
190 theHistogramRecomputeFlag = true;
191 }
192
getHistogram()193 ossimRefPtr<ossimMultiResLevelHistogram> ossimImageHistogramSource::getHistogram()
194 {
195 execute();
196 return theHistogram;
197 }
198
getBinInformation(ossim_uint32 & numberOfBins,ossim_float32 & minValue,ossim_float32 & maxValue,ossim_float32 & nullValue,ossim_uint32 band) const199 bool ossimImageHistogramSource::getBinInformation(ossim_uint32& numberOfBins,
200 ossim_float32& minValue,
201 ossim_float32& maxValue,
202 ossim_float32& nullValue,
203 ossim_uint32 band)const
204 {
205 bool result = false;
206 ossimImageSource* input = PTR_CAST(ossimImageSource, getInput(0));
207 if(input)
208 {
209 result = ossim::getBinInformation( input, band, numberOfBins,
210 minValue, maxValue, nullValue );
211 if ( result )
212 {
213 if(ossim::isnan(theMinValueOverride) == false)
214 {
215 minValue = (float)theMinValueOverride;
216 }
217 if(ossim::isnan(theMaxValueOverride) == false)
218 {
219 maxValue = (float)theMaxValueOverride;
220 }
221 if(theNumberOfBinsOverride > 0)
222 {
223 numberOfBins = theNumberOfBinsOverride;
224 }
225 }
226 }
227 return result;
228 }
229
computeNormalModeHistogram()230 void ossimImageHistogramSource::computeNormalModeHistogram()
231 {
232 // ref ptr, not a leak.
233 theHistogram = new ossimMultiResLevelHistogram;
234 ossimImageSource *input = PTR_CAST(ossimImageSource, getInput(0));
235 if ( input )
236 {
237 // sum up all tiles needing processing. We will use the sequencer.
238 // ossim_uint32 numberOfResLevels = input->getNumberOfDecimationLevels();
239 ossim_uint32 index = 0;
240 double tileCount = 0.0;
241 double totalTiles = 0.0;
242 ossim_uint32 numberOfBands = input->getNumberOfOutputBands();
243 ossim_uint32 numberOfBins = 0;
244 ossim_float32 minValue = 0;
245 ossim_float32 maxValue = 0;
246 ossim_float32 nullValue = 0;
247 if ( getBinInformation(numberOfBins, minValue, maxValue, nullValue, 0) )
248 {
249 ossimRefPtr<ossimImageSourceSequencer> sequencer = new ossimImageSourceSequencer();
250
251 // If the input is tiled use that tile size:
252 ossimIpt inputTileSize;
253 inputTileSize.x = input->getTileWidth();
254 inputTileSize.y = input->getTileHeight();
255 if ( (inputTileSize.x != 0) && (inputTileSize.y != 0) )
256 {
257 sequencer->setTileSize( inputTileSize );
258 }
259
260 sequencer->connectMyInputTo(0, getInput(0));
261 sequencer->initialize();
262
263 vector<ossimDpt> decimationFactors;
264 input->getDecimationFactors(decimationFactors);
265 if ( !decimationFactors.size() )
266 {
267 ossimNotify(ossimNotifyLevel_WARN)
268 << "ossimImageHistogramSource::computeNormalModeHistogram WARNING:"
269 << "\nNo decimation factors from input. returning..." << std::endl;
270 return;
271 }
272
273 ossim_uint32 resLevelsToCompute = ossim::min((ossim_uint32)theMaxNumberOfResLevels,
274 (ossim_uint32)decimationFactors.size());
275
276 if( decimationFactors.size() < theMaxNumberOfResLevels)
277 {
278 ossimNotify(ossimNotifyLevel_WARN) << "Number Decimations is smaller than the request number of r-levels defaulting to the smallest of the 2 numbers" << endl;
279 }
280
281 theHistogram->create(resLevelsToCompute);
282 for(index = 0; index < resLevelsToCompute; ++index)
283 {
284 sequencer->setAreaOfInterest(theAreaOfInterest*decimationFactors[index]);
285
286 totalTiles += sequencer->getNumberOfTiles();
287 }
288
289 if(numberOfBins > 0)
290 {
291 setPercentComplete(0.0);
292 for(index = 0; (index < resLevelsToCompute); ++index)
293 {
294 // Check for abort request.
295 if (needsAborting())
296 {
297 setPercentComplete(100);
298 break;
299 }
300
301 //sequencer->setAreaOfInterest(input->getBoundingRect(index));
302 sequencer->setAreaOfInterest(theAreaOfInterest*decimationFactors[index]);
303
304 sequencer->setToStartOfSequence();
305
306 theHistogram->getMultiBandHistogram(index)->create(
307 numberOfBands, numberOfBins, minValue, maxValue, nullValue,
308 input->getOutputScalarType() );
309
310 ossimRefPtr<ossimImageData> data = sequencer->getNextTile(index);
311 ++tileCount;
312 setPercentComplete((100.0*(tileCount/totalTiles)));
313
314 ossim_uint32 resLevelTotalTiles = sequencer->getNumberOfTiles();
315 for (ossim_uint32 resLevelTileCount = 0;
316 resLevelTileCount < resLevelTotalTiles;
317 ++resLevelTileCount)
318 {
319 //---
320 // Counting nulls now so the check for OSSIM_EMPTY status
321 // removed. drb - 20190227
322 //---
323 if( data.valid() )
324 {
325 data->populateHistogram(
326 theHistogram->getMultiBandHistogram(index), theAreaOfInterest );
327 }
328
329 // Check for abort request.
330 if (needsAborting())
331 {
332 setPercentComplete(100);
333 break;
334 }
335
336 data = sequencer->getNextTile(index);
337 ++tileCount;
338 setPercentComplete((100.0*(tileCount/totalTiles)));
339 }
340 }
341 }
342 sequencer->disconnect();
343 sequencer = 0;
344 }
345 }
346 else
347 {
348 setPercentComplete(100.0);
349 }
350 }
351
computeFastModeHistogram()352 void ossimImageHistogramSource::computeFastModeHistogram()
353 {
354 // Compute at most 9 x 9 tiles of 16 x 16 tile size.
355
356 ossim_uint32 resLevelsToCompute = 1;
357
358 // ref ptr, not a leak.
359 theHistogram = new ossimMultiResLevelHistogram;
360 theHistogram->create(resLevelsToCompute);
361 ossimImageSource* input = PTR_CAST(ossimImageSource, getInput(0));
362 if(!input)
363 {
364 setPercentComplete(100.0);
365 return;
366 }
367 // sum up all tiles needing processing. We will use the sequencer.
368 // ossim_uint32 numberOfResLevels = input->getNumberOfDecimationLevels();
369 double tileCount = 0.0;
370 double totalTiles = 0.0;
371 ossim_uint32 numberOfBands = input->getNumberOfOutputBands();
372 ossim_uint32 numberOfBins = 0;
373 ossim_float32 minValue = 0;
374 ossim_float32 maxValue = 0;
375 ossim_float32 nullValue = 0;
376
377 // Assuming all bands have the same min, max, null as band 0:
378 if ( getBinInformation(numberOfBins, minValue, maxValue, nullValue, 0) )
379 {
380 // Fixed 32 x 32 tile size:
381 ossimIpt tileSize( 32, 32 );
382
383 ossimIrect tileBoundary = theAreaOfInterest;
384 tileBoundary.stretchToTileBoundary(tileSize);
385
386 // Max of 11 x 11 tiles accross the image.
387 const ossim_uint32 MAX_TILES_WIDE = 11;
388
389 ossim_uint32 tilesWide = ossim::min( (ossim_uint32)(tileBoundary.width()/tileSize.x),
390 MAX_TILES_WIDE);
391 ossim_uint32 tilesHigh = ossim::min( (ossim_uint32)(tileBoundary.height()/tileSize.y),
392 MAX_TILES_WIDE);
393 totalTiles = tilesWide*tilesHigh;
394
395 if(numberOfBins > 0)
396 {
397 ossimIpt origin = theAreaOfInterest.ul();
398
399 ossim_uint32 xTileOffset = tileBoundary.width() / tilesWide;
400 ossim_uint32 yTileOffset = tileBoundary.height() / tilesHigh;
401
402 theHistogram->getMultiBandHistogram(0)->create(
403 numberOfBands, numberOfBins, minValue, maxValue, nullValue,
404 input->getOutputScalarType() );
405 ossim_uint32 x = 0;
406 ossim_uint32 y = 0;
407 tileCount = 0;
408 totalTiles = tilesWide*tilesHigh;
409
410 for(y = 0; y < tilesHigh; ++y)
411 {
412 for(x = 0; x < tilesWide; ++x)
413 {
414 ossimIpt ul( origin.x + (x*xTileOffset), origin.y + (y*yTileOffset) );
415 ossimIrect tileRect(ul.x, ul.y, ul.x + tileSize.x-1, ul.y + tileSize.y-1);
416
417 ossimRefPtr<ossimImageData> data = input->getTile(tileRect);
418 if(data.valid()&&data->getBuf()&&(data->getDataObjectStatus() != OSSIM_EMPTY))
419 {
420 data->populateHistogram(
421 theHistogram->getMultiBandHistogram(0), theAreaOfInterest );
422 }
423
424 // Check for abort request.
425 if (needsAborting())
426 {
427 break;
428 }
429
430 ++tileCount;
431 }
432
433 // Check for abort request.
434 if (needsAborting())
435 {
436 setPercentComplete(100);
437 break;
438 }
439
440 setPercentComplete((100.0*(tileCount/totalTiles)));
441 }
442 }
443 }
444 }
445
loadState(const ossimKeywordlist & kwl,const char * prefix)446 bool ossimImageHistogramSource::loadState(const ossimKeywordlist& kwl,
447 const char* prefix)
448 {
449 bool result = ossimHistogramSource::loadState(kwl, prefix);
450
451 if ( result )
452 {
453 std::string myPrefix = (prefix?prefix:"");
454 std::string key = "area_of_interest";
455 ossimString value;
456 value.string() = kwl.findKey( myPrefix, key);
457 if ( value.size() )
458 {
459 theAreaOfInterest.toRect( value );
460 }
461
462 key = "mode";
463 value.string() = kwl.findKey( myPrefix, key);
464 if ( value.size() )
465 {
466 if(value == "normal")
467 {
468 theComputationMode = OSSIM_HISTO_MODE_NORMAL;
469 }
470 else if(value == "fast")
471 {
472 theComputationMode = OSSIM_HISTO_MODE_FAST;
473 }
474 }
475 }
476 #if 0 /* old loadState drb - 20181114 */
477 // setNumberOfInputs(2);
478 // ossimString rect = kwl.find(prefix, "rect");
479
480 if(!rect.empty())
481 {
482 loadState(kwl, prefix);
483 }
484 else
485 {
486 ossimString newPrefix = ossimString(prefix) + "area_of_interest.";
487 theAreaOfInterest.loadState(kwl, newPrefix);
488 }
489
490 ossimString mode = kwl.find(prefix, "mode");
491 mode = mode.downcase();
492 if(mode == "normal")
493 {
494 theComputationMode = OSSIM_HISTO_MODE_NORMAL;
495 }
496 else if(mode == "fast")
497 {
498 theComputationMode = OSSIM_HISTO_MODE_FAST;
499 }
500 if(getNumberOfInputs()!=1)
501 {
502 setNumberOfInputs(1);
503 }
504
505 // ossimString numberOfTiles = kwl.find(prefix, "number_of_tiles");
506 // if(!numberOfTiles.empty())
507 // {
508 // theNumberOfTilesToUseInFastMode = numberOfTiles.toUInt32();
509 // }
510
511 theInputListIsFixedFlag = true;
512 theOutputListIsFixedFlag = false;
513 #endif
514
515 return result;
516 }
517
saveState(ossimKeywordlist & kwl,const char * prefix) const518 bool ossimImageHistogramSource::saveState(ossimKeywordlist& kwl,
519 const char* prefix)const
520 {
521 bool result = ossimHistogramSource::saveState(kwl, prefix);
522 if(result)
523 {
524 std::string myPrefix = (prefix?prefix:"");
525 std::string key = "area_of_interest";
526 ossimString value = theAreaOfInterest.toString();
527 kwl.addPair( myPrefix, key, value.string() );
528
529 key = "mode";
530 if ( theComputationMode == OSSIM_HISTO_MODE_NORMAL )
531 {
532 value = "normal";
533 }
534 else if ( theComputationMode == OSSIM_HISTO_MODE_FAST )
535 {
536 value = "fast";
537 }
538 else
539 {
540 value = "unknown";
541 }
542 kwl.addPair( myPrefix, key, value.string() );
543 }
544 return result;
545 }
546