OpenWalnut  1.5.0dev
WTransferFunction.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 <algorithm>
26 #include <cmath>
27 #include <iostream>
28 #include <vector>
29 
30 #include "WAssert.h"
31 #include "WTransferFunction.h"
32 
34 {
35  if( m_histogram.size() != rhs.m_histogram.size() )
36  {
37  return false;
38  }
39  {
40  std::vector< double >::const_iterator ait1 = m_histogram.begin();
41  std::vector< double >::const_iterator ait2 = rhs.m_histogram.begin();
42  for( ;
43  ait1 != m_histogram.end();
44  ++ait1, ++ait2 )
45  {
46  if( *ait1 != *ait2 )
47  {
48  return false;
49  }
50  }
51  }
52 
53 
54  if( m_colors.size() != rhs.m_colors.size() || m_alphas.size() != rhs.m_alphas.size() )
55  {
56  return false;
57  }
58 
59  if( m_isomin != rhs.m_isomin && m_isomax != rhs.m_isomax )
60  {
61  return false;
62  }
63 
64  std::vector< ColorEntry >::const_iterator it1 = m_colors.begin();
65  std::vector< ColorEntry >::const_iterator it2 = rhs.m_colors.begin();
66  for( ;
67  it1 != m_colors.end();
68  ++it1, ++it2 )
69  {
70  if( !( *it1 == *it2 ) )
71  {
72  return false;
73  }
74  }
75 
76  std::vector< AlphaEntry >::const_iterator ait1 = m_alphas.begin();
77  std::vector< AlphaEntry >::const_iterator ait2 = rhs.m_alphas.begin();
78  for( ;
79  ait1 != m_alphas.end();
80  ++ait1, ++ait2 )
81  {
82  if( !( *ait1 == *ait2 ) )
83  {
84  return false;
85  }
86  }
87 
88  return true;
89 }
90 
92 {
93  return !( ( *this ) == rhs );
94 }
95 
96 namespace
97 {
98  //! linear blend between two colors in rgb space if ta = 1.-tb
99  WColor blend( const WColor&a, double ta, const WColor &b, double tb )
100  {
101  return WColor(
102  ta*a[ 0 ]+tb*b[ 0 ],
103  ta*a[ 1 ]+tb*b[ 1 ],
104  ta*a[ 2 ]+tb*b[ 2 ], 1. );
105  }
106 
107  //! linear blend between two variables
108  double ablend( const double a, const double ta, const double b, const double tb )
109  {
110  return a*ta + b*tb;
111  }
112 } // namespace
113 
114 
115 void WTransferFunction::sample1DTransferFunction( unsigned char*array, int width, double min, double max ) const
116 {
117  if( m_colors.size() < 1 ) return;
118  if( m_alphas.size() < 1 ) return;
119 
120  std::vector< ColorEntry >::const_iterator c1 = m_colors.begin();
121  std::vector< ColorEntry >::const_iterator c2 = c1+1;
122 
123  std::vector< AlphaEntry >::const_iterator a1 = m_alphas.begin();
124  std::vector< AlphaEntry >::const_iterator a2 = a1+1;
125 
126  for( int i = 0; i < width; ++i )
127  {
128  WColor color;
129  double iso = ( double )i/( double )width * ( max-min ) + min;
130 
131  if( iso <= m_isomin )
132  {
133  color = m_colors.begin()->color;
134  color[ 3 ] = m_alphas.begin()->alpha;
135  }
136  else if( iso >= m_isomax )
137  {
138  color = m_colors.back().color;
139  color[ 3 ] = m_alphas.back().alpha;
140  }
141  else
142  {
143  while( c2 != m_colors.end() && iso > c2->iso )
144  {
145  WAssert( c2 != m_colors.end(), "Corruption of internal data structure." );
146  c1++;
147  c2++;
148  WAssert( c1 != m_colors.end(), "Corruption of internal data structure." );
149  }
150 
151  while( a2 != m_alphas.end() && iso > a2->iso )
152  {
153  WAssert( a2 != m_alphas.end(), "Corruption of internal data structure." );
154  a1++;
155  a2++;
156  WAssert( a1 != m_alphas.end(), "Corruption of internal data structure." );
157  }
158 
159  if( c2 == m_colors.end() )
160  {
161  color = c1->color;
162  }
163  else
164  {
165  double colorParameter = ( iso - c1->iso )/( c2->iso - c1->iso );
166  color = blend( c1->color, 1.-colorParameter, c2->color, colorParameter );
167  }
168  if( a2 == m_alphas.end() )
169  {
170  color[ 3 ] = a1->alpha;
171  }
172  else
173  {
174  double alphaParameter = ( iso - a1->iso )/( a2->iso - a1->iso );
175  color[ 3 ] = ablend( a1->alpha, 1.-alphaParameter, a2->alpha, alphaParameter );
176  }
177  }
178  for( int j = 0; j < 3; ++j )
179  {
180  array[ 4*i + j ] = color[ j ]*255.;
181  }
182  array[ 4*i + 3 ] = color[ 3 ] * 255.0 * m_opacityScale;
183  }
184 }
185 
186 
187 void WTransferFunction::addColor( double iso, const WColor& color )
188 {
189  if( m_colors.size() == 0 )
190  {
191  m_colors.push_back( ColorEntry( iso, color ) );
192  }
193  else
194  {
195  std::vector<ColorEntry>::iterator e = find_if( m_colors.begin(), m_colors.end(), LessPred<ColorEntry>( iso ) );
196  m_colors.insert( e, ColorEntry( iso, color ) );
197  }
198 
199  if( m_alphas.size() >= 1 )
200  {
201  m_isomin = std::min( m_colors.front().iso, m_alphas.front().iso );
202  m_isomax = std::max( m_colors.back().iso, m_alphas.back().iso );
203  }
204  else
205  {
206  m_isomin = m_colors.front().iso;
207  m_isomax = m_colors.back().iso;
208  }
209 }
210 
211 void WTransferFunction::addAlpha( double iso, double alpha )
212 {
213  if( m_alphas.size() == 0 )
214  {
215  m_alphas.push_back( AlphaEntry( iso, alpha ) );
216  }
217  else
218  {
219  std::vector<AlphaEntry>::iterator e = find_if( m_alphas.begin(), m_alphas.end(), LessPred<AlphaEntry>( iso ) );
220  m_alphas.insert( e, AlphaEntry( iso, alpha ) );
221  }
222 
223  if( m_colors.size() >= 1 )
224  {
225  m_isomin = std::min( m_colors.front().iso, m_alphas.front().iso );
226  m_isomax = std::max( m_colors.back().iso, m_alphas.back().iso );
227  }
228  else
229  {
230  m_isomin = m_alphas.front().iso;
231  m_isomax = m_alphas.back().iso;
232  }
233 }
234 
235 WTransferFunction WTransferFunction::createFromRGBA( unsigned char const * const rgba, size_t size )
236 {
237  // we create a linear match to the transfer funciton given by rgba by scanning the
238  // alpha and color values in two passes.
239  // each pass starts at the left of the picture and moves right looking for a good match of
240  // the line between start point and end point to the given function in between. The error is
241  // normalized to a per-sample basis. If the maximum error is below MIN_ERROR_THRESHOLD, the
242  // line is accepted and the next segment is analyzed.
243  const double MIN_ERROR_THRESHOLD = 5.0;
244  WTransferFunction rgbatf;
245  std::vector < float > values( size );
246 
247  // copy channel
248  for( size_t i = 0; i < size; ++i )
249  {
250  values[ i ] = static_cast<double>( rgba[ i*4+3 ] );
251  }
252 
253  // add first and last alpha
254  rgbatf.addAlpha( 0.0, values[ 0 ]/255. );
255  rgbatf.addAlpha( 1.0, values[ size-1 ]/255. );
256 
257  std::vector < float > errors( size );
258 
259  size_t seed = 0;
260  while( seed < size-1 )
261  {
262  // start at first pixel and fit a line to the data
263  size_t to = seed+1;
264  while( to < size )
265  {
266  double error = 0.0;
267  double incline = ( values[ to ] - values[ seed ] )/( to-seed );
268  for( size_t j = seed+1; j < to; ++j )
269  {
270  error += std::sqrt( std::pow( values[ j ] - values[ seed ] - incline * ( j-seed ), 2 ) );
271  }
272  errors[ to ] = error/( to-seed ); // compute square error per pixel length of line
273  ++to;
274  }
275  size_t minElement = size-1;
276  double minerror = errors[ minElement ];
277  for( to = size-1; to > seed; --to )
278  {
279  if( errors[ to ] < minerror )
280  {
281  minElement = to;
282  minerror = errors[ to ];
283  if( minerror < MIN_ERROR_THRESHOLD )
284  {
285  break;
286  }
287  }
288  }
289  if( minElement < size-1 )
290  {
291  rgbatf.addAlpha( ( double )minElement/( double )( size-1 ), values[ minElement ]/255. );
292  }
293  seed = minElement;
294  }
295 
296 
297  // same for color
298  // add first and last color
299  rgbatf.addColor( 0.0, WColor( rgba[ 0*4+0 ]/255.f, rgba[ 0*4+1 ]/255.f, rgba[ 0*4+2 ]/255.f, 0.f ) );
300  rgbatf.addColor( 1.0, WColor( rgba[ ( size-1 )*4+0 ]/255.f, rgba[ ( size-1 )*4+1 ]/255.f, rgba[ ( size-1 )*4+2 ]/255.f, 0.f ) );
301 
302  // first try of code: use combined RGB errors
303 
304  seed = 0;
305  while( seed < size-1 )
306  {
307  // start at first pixel and fit a line to the data
308  size_t to = seed+1;
309  while( to < size )
310  {
311  double error = 0.0;
312  double inclineR = ( rgba[ to*4+0 ] - rgba[ seed*4+0 ] )/( to-seed );
313  double inclineG = ( rgba[ to*4+1 ] - rgba[ seed*4+1 ] )/( to-seed );
314  double inclineB = ( rgba[ to*4+2 ] - rgba[ seed*4+2 ] )/( to-seed );
315 
316  for( size_t j = seed; j < to; ++j )
317  {
318  error += std::sqrt(
319  std::pow( rgba[ 4*j+0 ] - rgba[ 4*seed+0 ] - inclineR * ( j-seed ), 2 ) +
320  std::pow( rgba[ 4*j+1 ] - rgba[ 4*seed+1 ] - inclineG * ( j-seed ), 2 ) +
321  std::pow( rgba[ 4*j+2 ] - rgba[ 4*seed+2 ] - inclineB * ( j-seed ), 2 )
322  );
323  }
324  errors[ to ] = error/( to-seed ); // compute square error per pixel length of line
325  ++to;
326  }
327 
328  size_t minElement = size-1;
329  double minerror = errors[ size-1 ];
330  // traverse from back
331  for( to = size-2; to > seed; --to )
332  {
333  if( errors[ to ] < minerror )
334  {
335  minElement = to;
336  minerror = errors[ to ];
337  }
338  if( minerror < MIN_ERROR_THRESHOLD*2.0 ) //! the threshold here is larger than for alpha, becuase we compare all colors at once
339  {
340  break;
341  }
342  }
343  if( minElement < size-1 )
344  {
345  rgbatf.addColor( ( double )minElement/( double )( size-1 ),
346  WColor( rgba[ minElement*4+0 ]/255.f, rgba[ minElement*4+1 ]/255.f, rgba[ minElement*4+2 ]/255.f, 0.f ) );
347  }
348  seed = minElement;
349  }
350 
351  std::cout << "New Transfer Function: " << rgbatf << "." << std::endl;
352  return rgbatf;
353 }
354 
355 std::ostream& operator << ( std::ostream& out, const WTransferFunction& tf )
356 {
357  size_t numColors = tf.numColors();
358  for( size_t i = 0; i < numColors; ++i )
359  {
360  double iso = tf.getColorIsovalue( i );
361  WColor c = tf.getColor( i );
362  out << "c:" << iso << ":" << c[ 0 ] << ":" << c[ 1 ] << ":" << c[ 2 ] << ";";
363  }
364  size_t numAlphas = tf.numAlphas();
365  for( size_t i = 0; i < numAlphas; ++i )
366  {
367  double iso = tf.getAlphaIsovalue( i );
368  double alpha = tf.getAlpha( i );
369  out << "a:" << iso << ":" << alpha;
370  if( i != numAlphas-1 )
371  {
372  out << ";";
373  }
374  }
375  return out;
376 }
A class that stores a 1D transfer function which consists of a linear interpolation of alpha and colo...
bool operator==(const WTransferFunction &rhs) const
Check equivalence of two transfer functions.
double m_isomax
The largest used iso value.
double getColorIsovalue(size_t i) const
The isovalue of the color at a given index.
bool operator!=(const WTransferFunction &rhs) const
Check equivalence of two transfer functions.
std::vector< ColorEntry > m_colors
Sorted list of colors.
std::vector< AlphaEntry > m_alphas
Sorted list of alpha values.
std::vector< double > m_histogram
Sores a histogram.
double m_isomin
The smallest used iso value.
size_t numAlphas() const
Get the number of alphas.
double getAlphaIsovalue(size_t i) const
Get the isovalue at a given index in the alpha values.
size_t numColors() const
Get the number of colors.
void sample1DTransferFunction(unsigned char *array, int width, double min, double max) const
sample/render the transfer function linearly between min and max in an RGBA texture.
double getAlpha(size_t i) const
Get alpha at given index.
void addColor(double iso, const WColor &color)
Insert a new color point.
const WColor & getColor(size_t i) const
Get color at given index.
void addAlpha(double iso, double alpha)
Insert a new alpha point.
static WTransferFunction createFromRGBA(unsigned char const *const rgba, size_t size)
Try to estimate a transfer function from an RGBA image.
double m_opacityScale
Factor by which the output opacity is scaled to allow for easier specification of very small opacity ...
Alpha entries represent linearly interpolated transparency values along the isovalue scale.
Color entries are linearly interpolated colors along isovalues.
Templatized comparison predicate for internal searching.