OpenWalnut  1.4.0
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 < 4; ++j )
179  {
180  array[ 4*i + j ] = color[ j ]*255.;
181  }
182  }
183 }
184 
185 
186 void WTransferFunction::addColor( double iso, const WColor& color )
187 {
188  if( m_colors.size() == 0 )
189  {
190  m_colors.push_back( ColorEntry( iso, color ) );
191  }
192  else
193  {
194  std::vector<ColorEntry>::iterator e = find_if( m_colors.begin(), m_colors.end(), LessPred<ColorEntry>( iso ) );
195  m_colors.insert( e, ColorEntry( iso, color ) );
196  }
197 
198  if( m_alphas.size() >= 1 )
199  {
200  m_isomin = std::min( m_colors.front().iso, m_alphas.front().iso );
201  m_isomax = std::max( m_colors.back().iso, m_alphas.back().iso );
202  }
203  else
204  {
205  m_isomin = m_colors.front().iso;
206  m_isomax = m_colors.back().iso;
207  }
208 }
209 
210 void WTransferFunction::addAlpha( double iso, double alpha )
211 {
212  if( m_alphas.size() == 0 )
213  {
214  m_alphas.push_back( AlphaEntry( iso, alpha ) );
215  }
216  else
217  {
218  std::vector<AlphaEntry>::iterator e = find_if( m_alphas.begin(), m_alphas.end(), LessPred<AlphaEntry>( iso ) );
219  m_alphas.insert( e, AlphaEntry( iso, alpha ) );
220  }
221 
222  if( m_colors.size() >= 1 )
223  {
224  m_isomin = std::min( m_colors.front().iso, m_alphas.front().iso );
225  m_isomax = std::max( m_colors.back().iso, m_alphas.back().iso );
226  }
227  else
228  {
229  m_isomin = m_alphas.front().iso;
230  m_isomax = m_alphas.back().iso;
231  }
232 }
233 
234 WTransferFunction WTransferFunction::createFromRGBA( unsigned char const * const rgba, size_t size )
235 {
236  // we create a linear match to the transfer funciton given by rgba by scanning the
237  // alpha and color values in two passes.
238  // each pass starts at the left of the picture and moves right looking for a good match of
239  // the line between start point and end point to the given function in between. The error is
240  // normalized to a per-sample basis. If the maximum error is below MIN_ERROR_THRESHOLD, the
241  // line is accepted and the next segment is analyzed.
242  const double MIN_ERROR_THRESHOLD = 5.0;
243  WTransferFunction rgbatf;
244  std::vector < float > values( size );
245 
246  // copy channel
247  for( size_t i = 0; i < size; ++i )
248  {
249  values[ i ] = static_cast<double>( rgba[ i*4+3 ] );
250  }
251 
252  // add first and last alpha
253  rgbatf.addAlpha( 0.0, values[ 0 ]/255. );
254  rgbatf.addAlpha( 1.0, values[ size-1 ]/255. );
255 
256  std::vector < float > errors( size );
257 
258  size_t seed = 0;
259  while( seed < size-1 )
260  {
261  // start at first pixel and fit a line to the data
262  size_t to = seed+1;
263  while( to < size )
264  {
265  double error = 0.0;
266  double incline = ( values[ to ] - values[ seed ] )/( to-seed );
267  for( size_t j = seed+1; j < to; ++j )
268  {
269  error += std::sqrt( std::pow( values[ j ] - values[ seed ] - incline * ( j-seed ), 2 ) );
270  }
271  errors[ to ] = error/( to-seed ); // compute square error per pixel length of line
272  ++to;
273  }
274  size_t minElement = size-1;
275  double minerror = errors[ minElement ];
276  for( to = size-1; to > seed; --to )
277  {
278  if( errors[ to ] < minerror )
279  {
280  minElement = to;
281  minerror = errors[ to ];
282  if( minerror < MIN_ERROR_THRESHOLD )
283  {
284  break;
285  }
286  }
287  }
288  if( minElement < size-1 )
289  {
290  rgbatf.addAlpha( ( double )minElement/( double )( size-1 ), values[ minElement ]/255. );
291  }
292  seed = minElement;
293  }
294 
295 
296  // same for color
297  // add first and last color
298  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 ) );
299  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 ) );
300 
301  // first try of code: use combined RGB errors
302 
303  seed = 0;
304  while( seed < size-1 )
305  {
306  // start at first pixel and fit a line to the data
307  size_t to = seed+1;
308  while( to < size )
309  {
310  double error = 0.0;
311  double inclineR = ( rgba[ to*4+0 ] - rgba[ seed*4+0 ] )/( to-seed );
312  double inclineG = ( rgba[ to*4+1 ] - rgba[ seed*4+1 ] )/( to-seed );
313  double inclineB = ( rgba[ to*4+2 ] - rgba[ seed*4+2 ] )/( to-seed );
314 
315  for( size_t j = seed; j < to; ++j )
316  {
317  error += std::sqrt(
318  std::pow( rgba[ 4*j+0 ] - rgba[ 4*seed+0 ] - inclineR * ( j-seed ), 2 ) +
319  std::pow( rgba[ 4*j+1 ] - rgba[ 4*seed+1 ] - inclineG * ( j-seed ), 2 ) +
320  std::pow( rgba[ 4*j+2 ] - rgba[ 4*seed+2 ] - inclineB * ( j-seed ), 2 )
321  );
322  }
323  errors[ to ] = error/( to-seed ); // compute square error per pixel length of line
324  ++to;
325  }
326 
327  size_t minElement = size-1;
328  double minerror = errors[ size-1 ];
329  // traverse from back
330  for( to = size-2; to > seed; --to )
331  {
332  if( errors[ to ] < minerror )
333  {
334  minElement = to;
335  minerror = errors[ to ];
336  }
337  if( minerror < MIN_ERROR_THRESHOLD*2.0 ) //! the threshold here is larger than for alpha, becuase we compare all colors at once
338  {
339  break;
340  }
341  }
342  if( minElement < size-1 )
343  {
344  rgbatf.addColor( ( double )minElement/( double )( size-1 ),
345  WColor( rgba[ minElement*4+0 ]/255.f, rgba[ minElement*4+1 ]/255.f, rgba[ minElement*4+2 ]/255.f, 0.f ) );
346  }
347  seed = minElement;
348  }
349 
350  std::cout << "New Transfer Function: " << rgbatf << "." << std::endl;
351  return rgbatf;
352 }
353 
354 std::ostream& operator << ( std::ostream& out, const WTransferFunction& tf )
355 {
356  size_t numColors = tf.numColors();
357  for( size_t i = 0; i < numColors; ++i )
358  {
359  double iso = tf.getColorIsovalue( i );
360  WColor c = tf.getColor( i );
361  out << "c:" << iso << ":" << c[ 0 ] << ":" << c[ 1 ] << ":" << c[ 2 ] << ";";
362  }
363  size_t numAlphas = tf.numAlphas();
364  for( size_t i = 0; i < numAlphas; ++i )
365  {
366  double iso = tf.getAlphaIsovalue( i );
367  double alpha = tf.getAlpha( i );
368  out << "a:" << iso << ":" << alpha;
369  if( i != numAlphas-1 )
370  {
371  out << ";";
372  }
373  }
374  return out;
375 }