OSSIM - Open Source Software Image Map  Version 1.9.0 (20180803)
ossimKMeansFilter.cpp
Go to the documentation of this file.
1 //**************************************************************************************************
2 //
3 // OSSIM Open Source Geospatial Data Processing Library
4 // See top level LICENSE.txt file for license information
5 //
6 //**************************************************************************************************
7 
20 
22 
25  m_numClusters(0),
26  m_tile(0),
27  m_outputScalarType(OSSIM_SCALAR_UNKNOWN),
28  m_initialized(false),
29  m_thresholdMode(NONE)
30 {
31  setDescription("K-Means pixel classification filter.");
32 }
33 
35  ossimMultiBandHistogram* histogram)
36 : ossimImageSourceFilter(input_source),
37  m_histogram (histogram),
38  m_numClusters(0),
39  m_tile(0),
40  m_outputScalarType(OSSIM_SCALAR_UNKNOWN),
41  m_initialized(false),
42  m_thresholdMode(NONE)
43 {
44  setDescription("K-Means pixel classification filter.");
45 }
46 
48 {
49 }
50 
52 {
53  m_histogram = histo;
54  m_initialized = 0;
55  m_thresholds.clear();
56 }
57 
59  ossim_uint32 resLevel)
60 {
61  if(!theInputConnection || (m_numClusters == 0))
62  return 0;
63 
64  if (!m_initialized)
65  {
66  initialize();
67  if (!m_initialized)
68  return 0;
69  }
70  if (m_classifiers.empty() && !computeKMeans())
71  return 0;
72 
73  ossimRefPtr<ossimImageData> inTile = theInputConnection->getTile(tileRect, resLevel);
74  if (!inTile || !inTile->getBuf())
75  return 0;
76 
77  if(!m_tile)
78  {
79  allocate();
80  if (!m_tile)
81  return 0;
82  }
83 
84  m_tile->setImageRectangle(tileRect);
85  m_tile->makeBlank();
86 
87  // Quick handling special case of empty input tile:
88  if (inTile->getDataObjectStatus() == OSSIM_EMPTY)
89  return m_tile;
90 
91  // Since a histogram is being used, the bin value reflects a range:
92  ossimKMeansClustering* bandClusters = 0;
93  const ossimKMeansClustering::Cluster* cluster = 0;
94  ossim_uint8* outBuf; // TODO: Only K < 256 is currently supported
95  double null_pixel = theInputConnection->getNullPixelValue();
96  double pixel;
97  ossimIpt ipt;
98  ossim_uint32 offset = 0;
100  for (ossim_uint32 band=0; band<numBands; ++band)
101  {
102  // Need bin size of histogram since only center values were used in clustering:
103  double delta = m_histogram->getHistogram(band)->GetBucketSize() / 2.0;
104  bandClusters = m_classifiers[band].get();
105  outBuf = (ossim_uint8*)(m_tile->getBuf(band));
106  for (ipt.y=tileRect.ul().y; ipt.y<=tileRect.lr().y; ++ipt.y)
107  {
108  for (ipt.x=tileRect.ul().x; ipt.x<=tileRect.lr().x; ++ipt.x)
109  {
110  pixel = inTile->getPix(ipt, band);
111  if (pixel != null_pixel)
112  {
113  // if thresholding, only interested in the threshold point, not the cluster:
114  if (m_thresholds.size() > band)
115  {
116  if (pixel <= m_thresholds[band])
117  outBuf[offset] = (ossim_uint8) m_pixelValues[0];
118  else
119  outBuf[offset] = (ossim_uint8) m_pixelValues[1];
120  }
121  else
122  {
123  // Determine its group and remap it using the group's DN:
124  for (ossim_uint32 gid=0; gid<m_numClusters; ++gid)
125  {
126  cluster = bandClusters->getCluster(gid);
127  if (!cluster)
128  continue;
129  if ((pixel >= (cluster->min-delta)) && (pixel <= (cluster->max+delta)))
130  {
131  outBuf[offset] = (ossim_uint8) m_pixelValues[gid];
132  break;
133  }
134  }
135  }
136  }
137  ++offset;
138  }
139  }
140  } // end loop over bands
141 
142  m_tile->validate();
143  return m_tile;
144 }
145 
147 {
148  if (!m_initialized)
149  {
150  initialize();
151  if (!m_initialized)
152  return;
153  }
154 
156  if(!m_tile.valid())
157  return;
158 
159  ossim_uint32 numBands = getNumberOfInputBands();
160  if (m_numClusters && (m_classifiers.size() == numBands))
161  {
162  for (ossim_uint32 band=0; band<numBands; band++)
163  {
164  double min = m_classifiers[band]->getCluster(0)->min;
165  double max = m_classifiers[band]->getCluster(0)->max;
166 
167  for (ossim_uint32 gid=1; gid<m_numClusters; gid++)
168  {
169  if (m_classifiers[band]->getCluster(gid)->min < min)
170  min = m_classifiers[band]->getCluster(gid)->min;
171  if (m_classifiers[band]->getCluster(gid)->max > max)
172  max = m_classifiers[band]->getCluster(gid)->max;
173  }
174  m_tile->setMinPix(min, band);
175  m_tile->setMaxPix(max, band);
176  }
177  }
178  m_tile->initialize();
179 }
180 
182 {
183  // This assigns theInputConnection if one is there.
184  m_initialized = false;
186  m_tile = 0;
187 
188  if ( !theInputConnection )
189  return;
190 
191  // If an input histogram was provided, use it. Otherwise compute one:
192  if (!m_histogram.valid())
193  {
195  histoSource->connectMyInputTo(theInputConnection);
197  histoSource->setMaxNumberOfRLevels(1);
198  histoSource->execute();
199  m_histogram = histoSource->getHistogram()->getMultiBandHistogram(0);
200  }
201 
202  if (!m_histogram.valid())
203  {
204  ostringstream xmsg;
205  xmsg<<"ossimKMeansFilter:"<<__LINE__<<" Could not establish a histogram. Cannot "
206  "initialize filter";
207  throw ossimException(xmsg.str());
208  }
209 
210  ossim_uint32 numBands = getNumberOfInputBands();
211  for (ossim_uint32 band=0; band<numBands; band++)
212  {
214  m_minPixelValue.push_back(h->GetRangeMin());
215  m_maxPixelValue.push_back(h->GetRangeMax());
216  }
217  m_initialized = true;
218 }
219 
221 {
222  m_classifiers.clear();
223  m_thresholds.clear();
224 
225  ostringstream xmsg;
226  if (m_numClusters == 0)
227  {
228  xmsg<<"ossimKMeansFilter:"<<__LINE__<<" Number of groups has not been initialized!";
229  throw ossimException(xmsg.str());
230  }
231 
232  if (!m_initialized)
233  initialize();
234 
235  ossim_uint32 numBands = getNumberOfInputBands();
236  for (ossim_uint32 band=0; band<numBands; band++)
237  {
239  if (!band_histo.valid())
240  {
241  xmsg<<"ossimKMeansFilter:"<<__LINE__<<" Null band histogram returned!";
242  throw ossimException(xmsg.str());
243  }
244 
246  classifier->setVerbose();
247  classifier->setNumClusters(m_numClusters);
248  classifier->setSamples(band_histo->GetVals(), band_histo->GetRes());
249  classifier->setPopulations(band_histo->GetCounts(), band_histo->GetRes());
250  if (!classifier->computeKmeans())
251  {
252  cout<<"ossimKMeansFilter:"<<__LINE__<<" No K-means clustering data available."<<endl;
253  break;
254  }
255  m_classifiers.push_back(classifier);
256 
257  if ((m_thresholdMode != NONE) && (classifier->getNumClusters() == 2))
258  {
259  double mean0 = classifier->getMean(0);
260  double mean1 = classifier->getMean(1);
261  double sigma0 = classifier->getSigma(0);
262  double sigma1 = classifier->getSigma(1);
263  double threshold = 0;
264  switch (m_thresholdMode)
265  {
266  case MEAN:
267  threshold = (mean0 + mean1)/2.0;
268  break;
269  case SIGMA_WEIGHTED:
270  threshold = (sigma1*mean0 + sigma0*mean1)/(sigma0 + sigma1);
271  break;
272  case VARIANCE_WEIGHTED:
273  threshold = (sigma1*sigma1*mean0 + sigma0*sigma0*mean1)/(sigma0*sigma0 + sigma1*sigma1);
274  break;
275  default:
276  break;
277  }
278  m_thresholds.push_back(threshold);
279  cout<<"ossimKMeansFilter:"<<__LINE__<<" Using threshold = "<<threshold<<endl;
280  }
281  }
282 
283  return (m_classifiers.size() == numBands);
284 }
285 
287 {
288  m_classifiers.clear();
289  m_numClusters = 0;
290  m_initialized = false;
291 }
292 
294 {
295  m_thresholdMode = mode;
296 }
297 
299 {
300  if (K > 255)
301  {
302  ostringstream xmsg;
303  xmsg << "ossimKMeansFilter:"<<__LINE__<<" Requested K="<<K<<" but only max 255 supported!";
304  throw ossimException(xmsg.str());
305  }
306  clear();
307 
308  // Define default replacement pixel values (unless already done):
309  m_numClusters = K;
310  if (m_pixelValues.size() != m_numClusters)
311  {
312  m_pixelValues.clear();
313  for (ossim_uint32 i=1; i<=m_numClusters; ++i)
314  m_pixelValues.push_back(i);
315  }
316 }
317 
319 {
320  if (dns == 0)
321  return;
322 
323  if (K != m_numClusters)
324  setNumClusters(K);
325 
326  m_pixelValues.clear();
327  for (ossim_uint32 i=0; i<m_numClusters; ++i)
328  m_pixelValues.push_back(dns[i]);
329 }
330 
332 {
333  if (band < m_classifiers.size())
334  return m_classifiers[band].get();
335  return 0;
336 }
337 
338 bool ossimKMeansFilter::saveState(ossimKeywordlist& kwl, const char* prefix)const
339 {
340  if (m_numClusters == 0)
341  return true;
342 
343  ossim_uint32 numBands = getNumberOfInputBands();
344  kwl.add(prefix, "num_bands", numBands);
345  kwl.add(prefix, "num_clusters", m_numClusters);
346 
347  ossimString key;
348  ossimString keybase1;
349  ossimString keybase2;
350  const ossimKMeansClustering* bandClusters = 0;
351  const ossimKMeansClustering::Cluster* cluster = 0;
352  for (ossim_uint32 band=0; band<numBands; band++)
353  {
354  if (numBands > 1)
355  {
356  keybase1 = "band";
357  keybase1 += ossimString::toString(band) + ".";
358  }
359 
360  // Need bin size of histogram since only center values were used in clustering:
361  bandClusters = m_classifiers[band].get();
362  for (ossim_uint32 gid=0; gid < m_numClusters; ++gid)
363  {
364  keybase2 = keybase1;
365  keybase2 += "cluster";
366  keybase2 += ossimString::toString(gid);
367  cluster = bandClusters->getCluster(gid);
368  key = keybase2 + ".mean";
369  kwl.add(prefix, key.chars(), cluster->mean);
370  key = keybase2 + ".sigma";
371  kwl.add(prefix, key.chars(), cluster->sigma);
372  key = keybase2 + ".min";
373  kwl.add(prefix, key.chars(), cluster->min);
374  key = keybase2 + ".max";
375  kwl.add(prefix, key.chars(), cluster->max);
376  }
377  }
378 
379  bool rtn_stat = ossimImageSourceFilter::saveState(kwl, prefix);
380  return rtn_stat;
381 }
382 
383 bool ossimKMeansFilter::loadState(const ossimKeywordlist& orig_kwl, const char* prefix)
384 {
385  bool return_state = true;
386  //ossimKeywordlist kwl (orig_kwl); // need non-const copy
387 
388  // TODO: Need to implement
389 
390  return_state &= ossimImageSourceFilter::loadState(orig_kwl, prefix);
391 
392  return return_state;
393 }
394 
396 {
398 
399  if (m_numClusters > 255)
400  myType = OSSIM_UINT16; // Can't have more than 65535 groups! NOT YET SUPPORTED
401  else if (m_numClusters > 0)
402  myType = OSSIM_UINT8;
403 
404  return myType;
405 }
406 
408 {
409  if (band < m_minPixelValue.size())
410  return m_minPixelValue[band];
411  return 1;
412 }
413 
415 {
416  if (band < m_maxPixelValue.size())
417  return m_maxPixelValue[band];
418  return 255.0;
419 }
420 
421 
float GetRangeMin() const
virtual ossimRefPtr< ossimImageData > getTile(const ossimIrect &origin, ossim_uint32 resLevel=0)
virtual void setDescription(const ossimString &description)
virtual ossim_uint32 getNumberOfInputBands() const
void setVerbose(bool v=true) const
RTTI_DEF1(ossimKMeansFilter, "ossimKMeansFilter", ossimImageSourceFilter)
void setThresholdMode(ThresholdMode mode)
Special use case is to use K-means for thresholding an image into two values based on the histogram&#39;s...
std::basic_ostringstream< char > ostringstream
Class for char output memory streams.
Definition: ossimIosFwd.h:35
void setComputationMode(ossimHistogramMode mode)
virtual void setImageRectangle(const ossimIrect &rect)
16 bit unsigned integer
virtual double getMinPixelValue(ossim_uint32 band=0) const
Returns the min pixel of the band.
Represents serializable keyword/value map.
bool valid() const
Definition: ossimRefPtr.h:75
float GetRangeMax() const
const ossimKMeansClustering * getBandClassifier(ossim_uint32 band=0) const
Callers may be interested in reporting the cluster statistics computed by this class.
ossim_uint32 m_numClusters
static ossimString toString(bool aValue)
Numeric to string methods.
const ossimIpt & ul() const
Definition: ossimIrect.h:274
virtual ossimDataObjectStatus getDataObjectStatus() const
void setInputHistogram(ossimMultiBandHistogram *histo)
Sets the input source&#39;s histogram for quicker K-means analysis.
virtual ossim_float64 getPix(const ossimIpt &position, ossim_uint32 band=0) const
Will return the pixel at location position.
std::vector< double > m_thresholds
virtual void initialize()
Initialize the data buffer.
void setSamples(T *samples, ossim_uint32 num_entries)
const ossimKMeansClustering::Cluster * getCluster(ossim_uint32 i) const
std::vector< ossimRefPtr< ossimKMeansClustering > > m_classifiers
void add(const char *prefix, const ossimKeywordlist &kwl, bool overwrite=true)
int GetRes() const
static ossimImageDataFactory * instance()
virtual ossimScalarType getOutputScalarType() const
This will be used to query the output pixel type of the tile source.
virtual ossimDataObjectStatus validate() const
void setClusterPixelValues(const ossim_uint32 *dns, ossim_uint32 K)
Optionally define the output digital numbers for each cluster to be used for remapping the input pixe...
virtual ossimRefPtr< ossimMultiResLevelHistogram > getHistogram(const ossimIrect &rect)
virtual bool loadState(const ossimKeywordlist &kwl, const char *prefix=NULL)
Method to the load (recreate) the state of an object from a keyword list.
void setNumClusters(ossim_uint32 K)
ossimImageSource * theInputConnection
unsigned int ossim_uint32
ossimRefPtr< ossimMultiBandHistogram > m_histogram
Have num_bands entries.
const char * chars() const
For backward compatibility.
Definition: ossimString.h:77
ossimRefPtr< ossimHistogram > getHistogram(ossim_int32 band)
std::vector< double > m_minPixelValue
virtual bool saveState(ossimKeywordlist &kwl, const char *prefix=NULL) const
Method to save the state of an object to a keyword list.
virtual bool loadState(const ossimKeywordlist &kwl, const char *prefix=0)
Method to the load (recreate) the state of an object from a keyword list.
const ossimIpt & lr() const
Definition: ossimIrect.h:276
std::vector< double > m_maxPixelValue
virtual void initialize()
ThresholdMode m_thresholdMode
virtual ossim_int32 connectMyInputTo(ossimConnectableObject *inputObject, bool makeOutputConnection=true, bool createEventFlag=true)
Will try to connect this objects input to the passed in object.
virtual ossimRefPtr< ossimImageData > create(ossimSource *owner, ossimScalarType scalar, ossim_uint32 bands=1) const
ossim_uint32 getNumClusters() const
ossimScalarType
virtual void makeBlank()
Initializes data to null pixel values.
void allocate()
Called on first getTile, will initialize all data needed.
virtual void setMaxPix(ossim_float64 max_pix)
virtual bool saveState(ossimKeywordlist &kwl, const char *prefix=0) const
Method to save the state of an object to a keyword list.
ossimRefPtr< ossimImageData > m_tile
#define max(a, b)
Definition: auxiliary.h:76
ossim_int32 y
Definition: ossimIpt.h:142
virtual const void * getBuf() const
virtual double getMaxPixelValue(ossim_uint32 band=0) const
Returns the max pixel of the band.
float * GetCounts()
std::vector< ossim_uint32 > m_pixelValues
virtual void setMaxNumberOfRLevels(ossim_uint32 number)
float * GetVals()
ossim_int32 x
Definition: ossimIpt.h:141
8 bit unsigned integer
void setPopulations(T *populations, ossim_uint32 num_entries)
double getSigma(ossim_uint32 groupId) const
ossimRefPtr< ossimMultiBandHistogram > getMultiBandHistogram(ossim_uint32 resLevel) const
double getMean(ossim_uint32 groupId) const
void setNumClusters(ossim_uint32 K)
Defines how many classification clusters will be resolved.
virtual void setMinPix(ossim_float64 min_pix)
unsigned char ossim_uint8
virtual double getNullPixelValue(ossim_uint32 band=0) const
Each band has a null pixel associated with it.
#define min(a, b)
Definition: auxiliary.h:75
virtual ossimRefPtr< ossimImageData > getTile(const ossimIpt &origin, ossim_uint32 resLevel=0)