OpenWalnut  1.5.0dev
WMHistogramEqualization.cpp
1 //---------------------------------------------------------------------------
2 //
3 // Project: OpenWalnut ( http://www.openwalnut.org )
4 //
5 // Copyright 2009 OpenWalnut Community, BSV@Uni-Leipzig and CNCF@MPI-CBS
6 // For more information see http://www.openwalnut.org/copying
7 //
8 // This file is part of OpenWalnut.
9 //
10 // OpenWalnut is free software: you can redistribute it and/or modify
11 // it under the terms of the GNU Lesser General Public License as published by
12 // the Free Software Foundation, either version 3 of the License, or
13 // (at your option) any later version.
14 //
15 // OpenWalnut is distributed in the hope that it will be useful,
16 // but WITHOUT ANY WARRANTY; without even the implied warranty of
17 // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
18 // GNU Lesser General Public License for more details.
19 //
20 // You should have received a copy of the GNU Lesser General Public License
21 // along with OpenWalnut. If not, see <http://www.gnu.org/licenses/>.
22 //
23 //---------------------------------------------------------------------------
24 
25 #include <fstream>
26 #include <memory>
27 #include <string>
28 #include <vector>
29 
30 #include "WMHistogramEqualization.h"
31 #include "WMHistogramEqualization.xpm"
32 #include "core/common/WPropertyHelper.h"
33 #include "core/dataHandler/WDataHandler.h"
34 #include "core/kernel/WKernel.h"
35 
36 // This line is needed by the module loader to actually find your module. You need to add this to your module too. Do NOT add a ";" here.
37 W_LOADABLE_MODULE( WMHistogramEqualization )
38 
40  WModule()
41 {
42 }
43 
45 {
46  // Cleanup!
47 }
48 
49 std::shared_ptr< WModule > WMHistogramEqualization::factory() const
50 {
51  return std::shared_ptr< WModule >( new WMHistogramEqualization() );
52 }
53 
55 {
56  return WMHistogramEqualization_xpm;
57 }
58 
59 const std::string WMHistogramEqualization::getName() const
60 {
61  return "Histogram Equalization";
62 }
63 
64 const std::string WMHistogramEqualization::getDescription() const
65 {
66  return "This module takes an arbitrary data set and equalizes its histogram. This increases contrast in several regions of the data.";
67 }
68 
70 {
71  // the dataset to process. Only accept scalar data.
72  m_input = std::shared_ptr< WModuleInputData < WDataSetScalar > >(
73  new WModuleInputData< WDataSetScalar >( shared_from_this(),
74  "in", "The dataset whose histogram gets equalized." )
75  );
77 
78  // the output containing the equalized data.
79  m_output = std::shared_ptr< WModuleOutputData < WDataSetScalar > >(
80  new WModuleOutputData< WDataSetScalar >( shared_from_this(),
81  "out", "The dataset which has a linear cumulative histogram." )
82  );
84 
85  // call WModule's initialization
87 }
88 
90 {
91  m_propCondition = std::shared_ptr< WCondition >( new WCondition() );
92 
93  // clamping related stuff
94  m_clamping = m_properties->addPropertyGroup( "Clamping", "Clamping values in the dataset." );
95 
96  m_clamp = m_clamping->addProperty( "Clamp values?", "Values below the specified threshold are clamped to min and max respectively.",
97  true, m_propCondition );
98 
99  m_histogramResolution = m_clamping->addProperty( "Histogram resolution", "How many buckets should be used for the initial data histogram?",
100  10000, m_propCondition );
101  m_histogramResolution->setMin( 10 );
102  m_histogramResolution->setMax( 1000000 );
103 
104  m_clampPerc = m_clamping->addProperty( "Min-Max clamping in %", "Percent that are clamped from the beginning and the end of the histogram.",
105  1.0, m_propCondition );
106  m_clampPerc->setMin( 0.0 );
107  m_clampPerc->setMax( 100.0 );
108 
109  // equalizing related props
110  m_equalizing = m_properties->addPropertyGroup( "Equalizing", "Equalizing values in the dataset." );
111 
112  m_equalize = m_equalizing->addProperty( "Equalize histogram", "If true, the dataset's cumulative histogram gets linearized.",
113  true, m_propCondition );
114 
115  m_cdfResolution = m_equalizing->addProperty( "CDF histogram resolution",
116  "How many buckets should be used for the data histogram used for equalizing?",
117  10000, m_propCondition );
118  m_cdfResolution->setMin( 10 );
119  m_cdfResolution->setMax( 1000000 );
120 
121  // call WModule's initialization
123 }
124 
126 {
127  // get notified about data changes
128  m_moduleState.setResetable( true, true );
129  m_moduleState.add( m_input->getDataChangedCondition() );
130  // Remember the condition provided to some properties in properties()? The condition can now be used with this condition set.
132 
133  ready();
134 
135  // main loop
136  while( !m_shutdownFlag() )
137  {
138  debugLog() << "Waiting ...";
140 
141  // woke up since the module is requested to finish?
142  if( m_shutdownFlag() )
143  {
144  break;
145  }
146 
147  // To query whether an input was updated, simply ask the input:
148  bool dataUpdated = m_input->updated();
149 
150  // Remember the above criteria. We now need to check if the data is valid. After a connect-update, it might be NULL.
151  std::shared_ptr< WDataSetScalar > dataSet = m_input->getData();
152  if( !dataSet )
153  {
154  debugLog() << "Resetting output.";
155  m_output->reset();
156  continue;
157  }
158  std::shared_ptr< WValueSetBase > valueSet = dataSet->getValueSet();
159  dataUpdated = dataUpdated && dataSet;
160 
161  // prepare progress indicators
162  std::shared_ptr< WProgress > progress( new WProgress( "Processing", 4 ) );
163  m_progress->addSubProgress( progress );
164 
165  // The data is valid and we received an update. The data is not NULL but may be the same as in previous loops.
166  size_t histRes = m_histogramResolution->get( true );
167  size_t cdfHistRes = m_cdfResolution->get( true );
168  debugLog() << "Calculating histogram with resolution " << histRes;
169 
170  // Grab the histogram whose modus (interval with most of the action) is used as interval for histogram equalization
171  std::shared_ptr< const WValueSetHistogram > hist = dataSet->getHistogram( histRes );
172  ++*progress;
173 
174  // find interval borders and remove first and last p%
175  double lower = hist->getMinimum();
176  double upper = hist->getMaximum();
177 
178  double perc = m_clampPerc->get( true );
179 
180  // should the histogram be clamped before processing?
181  ++*progress;
182  if( m_clamp->get( true ) )
183  {
184  debugLog() << "Clamping histogram";
185 
186  size_t accumL = 0; // accumulation of values from below
187  size_t accumU = 0; // accumulation of values from upper side
188  size_t accumMax = static_cast< size_t >( static_cast< double >( hist->getTotalElementCount() ) * perc / 100.0 );
189  bool foundL = false;
190  bool foundU = false;
191  size_t curI = 0;
192  size_t maxI = hist->size() - 1; // the largest index in hist
193 
194  // search the histogram until the bucket with needed accumulative value is found
195  while( curI <= maxI )
196  {
197  accumL += ( *hist )[ curI ];
198  accumU += ( *hist )[ maxI - curI ];
199 
200  // lower border found?
201  if( !foundL && ( accumL >= accumMax ) )
202  {
203  foundL = true;
204  lower = hist->getIntervalForIndex( curI ).first;
205  }
206  if( !foundU && ( accumU >= accumMax ) )
207  {
208  foundU = true;
209  upper = hist->getIntervalForIndex( maxI - curI ).second;
210  }
211 
212  curI++;
213  }
214 
215  debugLog() << "Clamped " << perc << "% of [" << hist->getMinimum() << ", " << hist->getMaximum() << "]" <<
216  " resulting in new interval [" << lower << ", " << upper <<").";
217 
218  // with this new interval, extract a new histogram and use it for equalization
219  hist = std::shared_ptr< const WValueSetHistogram >( new WValueSetHistogram( valueSet, lower, upper, cdfHistRes ) );
220  }
221 
222  // the new data
223  std::vector< double > newData;
224  newData.resize( hist->getTotalElementCount(), 0 );
225 
226  // these values are needed to rescale the data to the original interval
227  double valueMin = hist->getMinimum();
228  double valueScaler = hist->getMaximum() - valueMin;
229 
230  // equalize?
231  ++*progress;
232  if( m_equalize->get( true ) )
233  {
234  // calculate the cumulative distribution function
235  debugLog() << "Calculating cumulative distribution function";
236  std::vector< double > cdf;
237  cdf.resize( hist->size() );
238 
239  // go through each CDF item and fill it, which is the sum of all previous items in hist
240  size_t accum = 0;
241  double cdfMin = ( *hist )[ 0 ];
242  double cdfScaler = static_cast< double >( valueSet->rawSize() ) - cdfMin;
243  for( size_t i = 0; i < hist->size(); ++i )
244  {
245  // the CDF at i is the summed up histogram from 0 to i
246  // we additionally require the histogram to be normalized so divide by the total count
247  accum += ( *hist )[ i ];
248  cdf[ i ] = accum - cdfMin;
249  }
250 
251  // finally, build the new dataset
252  debugLog() << "Calculating equalized value-set";
253  for( size_t vi = 0; vi < valueSet->rawSize(); ++vi )
254  {
255  size_t idx = hist->getIndexForValue( valueSet->getScalarDouble( vi ) );
256  double cdfVI = cdf[ idx ];
257  newData[ vi ] = valueMin + ( valueScaler * cdfVI / cdfScaler );
258  }
259  }
260  else
261  {
262  // finally, build the new dataset
263  debugLog() << "Calculating value-set";
264  size_t maxI = hist->size() - 1;
265  for( size_t vi = 0; vi < valueSet->rawSize(); ++vi )
266  {
267  size_t idx = hist->getIndexForValue( valueSet->getScalarDouble( vi ) );
268  double idxScaled = ( static_cast< double >( idx )/ static_cast< double >( maxI ) );
269  newData[ vi ] = valueMin + ( valueScaler * idxScaled );
270  }
271  }
272  ++*progress;
273 
274  // update output with a new dataset, reuse grid
275  debugLog() << "Updating output";
276 
277  // construct
278  std::shared_ptr< WDataSetScalar > d = std::shared_ptr< WDataSetScalar >(
279  new WDataSetScalar( std::shared_ptr< WValueSetBase >(
280  new WValueSet< double >( 0,
281  1,
282  std::shared_ptr< std::vector< double > >(
283  new std::vector< double >( newData ) ),
284  W_DT_DOUBLE ) ), dataSet->getGrid() )
285  );
286  m_output->updateData( d );
287 
288  debugLog() << "Done";
289 
290  progress->finish();
291  m_progress->removeSubProgress( progress );
292  }
293 }
294 
virtual void wait() const
Wait for the condition.
void setResetable(bool resetable=true, bool autoReset=true)
Sets the resetable flag.
virtual void add(std::shared_ptr< WCondition > condition)
Adds another condition to the set of conditions to wait for.
Class to encapsulate boost::condition_variable_any.
Definition: WCondition.h:42
This data set type contains scalars as values.
This modules takes a dataset and equalizes its histogram.
virtual std::shared_ptr< WModule > factory() const
Due to the prototype design pattern used to build modules, this method returns a new instance of this...
WMHistogramEqualization()
Default constructor.
virtual void connectors()
Initialize the connectors this module is using.
WPropInt m_histogramResolution
Resolution of the initial histogram.
WPropBool m_equalize
If true, histogram equalization is turned on.
virtual void moduleMain()
Entry point after loading the module.
WPropGroup m_equalizing
Group for keeping all the equalizing-related props.
std::shared_ptr< WModuleInputData< WDataSetScalar > > m_input
An input connector used to get datasets from other modules.
virtual const std::string getName() const
Gives back the name of this module.
WPropDouble m_clampPerc
How many percent should be clamped from the histogram.
virtual ~WMHistogramEqualization()
Destructor.
virtual const std::string getDescription() const
Gives back a description of this module.
virtual const char ** getXPMIcon() const
Get the icon for this module in XPM format.
std::shared_ptr< WModuleOutputData< WDataSetScalar > > m_output
The output connector used to provide the calculated data to other modules.
WPropGroup m_clamping
Group for keeping all the clamping related props.
virtual void properties()
Initialize the properties for this module.
WPropBool m_clamp
True if the values should be clamped before further processing.
WPropInt m_cdfResolution
Resolution with which the CDF gets calculated.
std::shared_ptr< WCondition > m_propCondition
A condition used to notify about changes in several properties.
Class offering an instantiate-able data connection between modules.
Class offering an instantiate-able data connection between modules.
Class representing a single module of OpenWalnut.
Definition: WModule.h:72
virtual void properties()
Initialize properties in this function.
Definition: WModule.cpp:212
wlog::WStreamedLogger debugLog() const
Logger instance for comfortable debug logging.
Definition: WModule.cpp:575
void addConnector(std::shared_ptr< WModuleInputConnector > con)
Adds the specified connector to the list of inputs.
Definition: WModule.cpp:108
std::shared_ptr< WProperties > m_properties
The property object for the module.
Definition: WModule.h:640
void ready()
Call this whenever your module is ready and can react on property changes.
Definition: WModule.cpp:505
WConditionSet m_moduleState
The internal state of the module.
Definition: WModule.h:703
std::shared_ptr< WProgressCombiner > m_progress
Progress indicator used as parent for all progress' of this module.
Definition: WModule.h:652
virtual void connectors()
Initialize connectors in this function.
Definition: WModule.cpp:208
Class managing progress inside of modules.
Definition: WProgress.h:42
WBoolFlag m_shutdownFlag
Condition getting fired whenever the thread should quit.
Used to find the occurrence frequencies of values in a value set.
Base Class for all value set types.
Definition: WValueSet.h:47