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