VCG Library
color.h
1 /****************************************************************************
2 * VCGLib o o *
3 * Visual and Computer Graphics Library o o *
4 * _ O _ *
5 * Copyright(C) 2004-2016 \/)\/ *
6 * Visual Computing Lab /\/| *
7 * ISTI - Italian National Research Council | *
8 * \ *
9 * All rights reserved. *
10 * *
11 * This program is free software; you can redistribute it and/or modify *
12 * it under the terms of the GNU General Public License as published by *
13 * the Free Software Foundation; either version 2 of the License, or *
14 * (at your option) any later version. *
15 * *
16 * This program is distributed in the hope that it will be useful, *
17 * but WITHOUT ANY WARRANTY; without even the implied warranty of *
18 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the *
19 * GNU General Public License (http://www.gnu.org/licenses/gpl.txt) *
20 * for more details. *
21 * *
22 ****************************************************************************/
23 
24 #ifndef __VCG_TRI_UPDATE_COLOR
25 #define __VCG_TRI_UPDATE_COLOR
26 
27 #include <math.h>
28 #include <time.h>
29 #include <vcg/space/color4.h>
30 #include <vcg/math/histogram.h>
31 #include <vcg/math/perlin_noise.h>
32 #include <vcg/math/random_generator.h>
33 #include <vcg/complex/algorithms/clean.h>
34 #include <vcg/complex/algorithms/stat.h>
35 
36 namespace vcg {
37 namespace tri {
38 
51 template <class MeshType>
53 {
54 public:
55  typedef typename MeshType::VertexType VertexType;
56  typedef typename MeshType::VertexPointer VertexPointer;
57  typedef typename MeshType::VertexIterator VertexIterator;
58  typedef typename MeshType::FaceType FaceType;
59  typedef typename MeshType::FacePointer FacePointer;
60  typedef typename MeshType::FaceIterator FaceIterator;
61  typedef typename MeshType::EdgeIterator EdgeIterator;
62  typedef typename MeshType::TetraType TetraType;
63  typedef typename MeshType::TetraPointer TetraPointer;
64  typedef typename MeshType::TetraIterator TetraIterator;
65 
66 
67  typedef typename MeshType::ScalarType ScalarType;
68  typedef typename MeshType::CoordType CoordType;
69 
70  class ColorAvgInfo
71  {
72  public:
73  unsigned int r;
74  unsigned int g;
75  unsigned int b;
76  unsigned int a;
77  int cnt;
78  };
79 
82  static int PerVertexConstant(MeshType &m, Color4b vs=Color4b::White,bool selected=false)
83  {
84  RequirePerVertexColor(m);
85 
86  int cnt=0;
87  for(VertexIterator vi=m.vert.begin();vi!=m.vert.end();++vi)
88  if(!(*vi).IsD()){
89  if(!selected || (*vi).IsS())
90  {
91  (*vi).C() = vs;
92  ++cnt;
93  }
94  }
95  return cnt;
96  }
97 
100  static int PerFaceConstant(MeshType &m, Color4b vs=Color4b::White,bool selected=false)
101  {
102  RequirePerFaceColor(m);
103  int cnt=0;
104  for(FaceIterator fi=m.face.begin();fi!=m.face.end();++fi)
105  if(!(*fi).IsD()){
106  if(!selected || (*fi).IsS())
107  {
108  (*fi).C() = vs;
109  ++cnt;
110  }
111  }
112  return cnt;
113  }
114 
117  static int PerTetraConstant(MeshType & m, Color4b vs = Color4b::White, bool selected = false)
118  {
119  RequirePerTetraColor(m);
120  int cnt = 0;
121  ForEachTetra(m, [&] (TetraType & t) {
122  if (!selected || t.IsS())
123  {
124  t.C() = vs;
125  ++cnt;
126  }
127  });
128  return cnt;
129  }
130 
136  static void PerVertexFromTetra(MeshType & m)
137  {
138  RequirePerTetraColor(m);
139  RequirePerVertexColor(m);
140 
141  ColorAvgInfo csi;
142  csi.r = csi.g = csi.b = csi.cnt = 0;
143  SimpleTempData<typename MeshType::VertContainer, ColorAvgInfo> TD(m.vert, csi);
144 
145  ForEachTetra(m, [&] (TetraType & t) {
146  for (int i = 0; i < 4; ++i)
147  {
148  TD[t.V(i)].r += t.C()[0];
149  TD[t.V(i)].g += t.C()[1];
150  TD[t.V(i)].b += t.C()[2];
151  TD[t.V(i)].a += t.C()[3];
152  TD[t.V(i)].cnt += 1;
153  }
154  });
155 
156  ForEachVertex(m, [&] (VertexType & v) {
157  v.C()[0] = TD[v].r / TD[v].cnt;
158  v.C()[1] = TD[v].g / TD[v].cnt;
159  v.C()[2] = TD[v].b / TD[v].cnt;
160  v.C()[3] = TD[v].a / TD[v].cnt;
161  });
162  }
163 
169  static void PerVertexFromFace( MeshType &m)
170  {
171  RequirePerFaceColor(m);
172  RequirePerVertexColor(m);
173 
174  ColorAvgInfo csi;
175  csi.r=0; csi.g=0; csi.b=0; csi.cnt=0;
176  SimpleTempData<typename MeshType::VertContainer, ColorAvgInfo> TD(m.vert,csi);
177 
178  FaceIterator fi;
179  for(fi=m.face.begin();fi!=m.face.end();++fi)
180  if(!(*fi).IsD())
181  for(int j=0;j<3;++j)
182  {
183  TD[(*fi).V(j)].r+=(*fi).C()[0];
184  TD[(*fi).V(j)].g+=(*fi).C()[1];
185  TD[(*fi).V(j)].b+=(*fi).C()[2];
186  TD[(*fi).V(j)].a+=(*fi).C()[3];
187  ++TD[(*fi).V(j)].cnt;
188  }
189 
190  VertexIterator vi;
191  for(vi=m.vert.begin();vi!=m.vert.end();++vi)
192  if(!(*vi).IsD() && TD[*vi].cnt>0 )
193  {
194  (*vi).C()[0] = TD[*vi].r / TD[*vi].cnt;
195  (*vi).C()[1] = TD[*vi].g / TD[*vi].cnt;
196  (*vi).C()[2] = TD[*vi].b / TD[*vi].cnt;
197  (*vi).C()[3] = TD[*vi].a / TD[*vi].cnt;
198  }
199  }
200 
204  static void PerFaceFromVertex( MeshType &m)
205  {
206  RequirePerFaceColor(m);
207  RequirePerVertexColor(m);
208 
209  FaceIterator fi;
210  for(fi=m.face.begin();fi!=m.face.end();++fi) if(!(*fi).IsD())
211  {
212  Color4f avg = (Color4f::Construct((*fi).V(0)->C()) +
213  Color4f::Construct((*fi).V(1)->C()) +
214  Color4f::Construct((*fi).V(2)->C()) )/ 3.0;
215  (*fi).C().Import(avg);
216  }
217  }
218 
223  static void PerVertexQualityRamp(MeshType &m, ScalarType minq = 0., ScalarType maxq = 0.)
224  {
225  RequirePerVertexQuality(m);
226  RequirePerVertexColor(m);
227 
228  if(minq == maxq)
229  {
230  std::pair<ScalarType, ScalarType> minmax = Stat<MeshType>::ComputePerVertexQualityMinMax(m);
231  minq=minmax.first;
232  maxq=minmax.second;
233  }
234  for(VertexIterator vi=m.vert.begin();vi!=m.vert.end();++vi)
235  if(!(*vi).IsD())
236  (*vi).C().SetColorRamp(minq,maxq,(*vi).Q());
237  }
238 
239 
244  static void PerVertexQualityRampParula(MeshType &m, ScalarType minq = 0., ScalarType maxq = 0.)
245  {
246  RequirePerVertexQuality(m);
247  RequirePerVertexColor(m);
248 
249  if(minq == maxq)
250  {
251  std::pair<ScalarType, ScalarType> minmax = Stat<MeshType>::ComputePerVertexQualityMinMax(m);
252  minq=minmax.first;
253  maxq=minmax.second;
254  }
255  for(VertexIterator vi = m.vert.begin(); vi != m.vert.end(); ++vi)
256  if(!(*vi).IsD())
257  (*vi).C().SetColorRampParula(minq, maxq, (*vi).Q());
258  }
259 
264  static void PerTetraQualityRamp(MeshType &m, ScalarType minq = 0., ScalarType maxq = 0., bool selected = false)
265  {
266  RequirePerTetraColor(m);
267  RequirePerTetraQuality(m);
268 
269  if(minq == maxq)
270  {
271  std::pair<ScalarType, ScalarType> minmax = Stat<MeshType>::ComputePerTetraQualityMinMax(m);
272  minq=minmax.first;
273  maxq=minmax.second;
274  }
275 
276  ForEachTetra(m, [&] (TetraType & t){
277  if (!selected || t.IsS())
278  t.C().SetColorRamp(minq, maxq, t.Q());
279  });
280  }
285  static void PerFaceQualityRamp(MeshType &m, ScalarType minq = 0, ScalarType maxq = 0, bool selected = false)
286  {
287  RequirePerFaceColor(m);
288  RequirePerFaceQuality(m);
289 
290  if(minq == maxq)
291  {
292  std::pair<ScalarType, ScalarType> minmax = Stat<MeshType>::ComputePerFaceQualityMinMax(m);
293  minq=minmax.first;
294  maxq=minmax.second;
295  }
296  for(FaceIterator fi = m.face.begin();fi != m.face.end(); ++fi)
297  if(!(*fi).IsD())
298  if(!selected || (*fi).IsS())
299  (*fi).C().SetColorRamp(minq, maxq, (*fi).Q());
300  }
301 
306  static void PerEdgeQualityRamp(MeshType &m, ScalarType minq = 0, ScalarType maxq = 0, bool selected = false)
307  {
308  RequirePerEdgeColor(m);
309  RequirePerEdgeQuality(m);
310 
311  if(minq == maxq)
312  {
313  std::pair<ScalarType, ScalarType> minmax = Stat<MeshType>::ComputePerEdgeQualityMinMax(m);
314  minq=minmax.first;
315  maxq=minmax.second;
316  }
317  for(EdgeIterator ei=m.edge.begin();ei!=m.edge.end();++ei) if(!(*ei).IsD())
318  if(!selected || (*ei).IsS())
319  (*ei).C().SetColorRamp(minq,maxq,(*ei).Q());
320  }
321 
326  static void PerVertexQualityGray(MeshType &m, ScalarType minq = 0, ScalarType maxq = 0)
327  {
328  RequirePerVertexColor(m);
329  RequirePerVertexQuality(m);
330  if(minq==maxq)
331  {
332  std::pair<ScalarType, ScalarType> minmax = Stat<MeshType>::ComputePerVertexQualityMinMax(m);
333  minq=minmax.first;
334  maxq=minmax.second;
335  }
336  for(VertexIterator vi=m.vert.begin();vi!=m.vert.end();++vi)
337  if(!(*vi).IsD())
338  (*vi).C().SetGrayShade( ((*vi).Q()-minq)/(maxq-minq));
339  }
340 
345  static void PerFaceQualityGray(MeshType &m, ScalarType minq = 0, ScalarType maxq = 0)
346  {
347  RequirePerFaceColor(m);
348  RequirePerFaceQuality(m);
349 
350  if(minq==maxq)
351  {
352  std::pair<ScalarType, ScalarType> minmax = Stat<MeshType>::ComputePerFaceQualityMinMax(m);
353  minq=minmax.first;
354  maxq=minmax.second;
355  }
356  for(FaceIterator fi=m.face.begin();fi!=m.face.end();++fi) if(!(*fi).IsD())
357  (*fi).C().SetGrayShade( ((*fi).Q()-minq)/(maxq-minq));
358  }
359 
371  static void PerVertexBorderFlag( MeshType &m, Color4b BorderColor=Color4b::Blue, Color4b InternalColor=Color4b::White, Color4b MixColor=Color4b::Cyan)
372  {
373  RequirePerVertexColor(m);
374 
375  Color4b BaseColor = Color4b::Green;
376 
377  PerVertexConstant(m,BaseColor);
378  for(FaceIterator fi=m.face.begin();fi!=m.face.end();++fi) if(!(*fi).IsD())
379  for(int j=0;j<3;++j)
380  {
381  if((*fi).IsB(j)){
382  if( (*fi).V(j)->C() == BaseColor) (*fi).V(j)->C() = BorderColor;
383  if( (*fi).V(j)->C() == InternalColor) (*fi).V(j)->C() = MixColor;
384  if( (*fi).V1(j)->C() == BaseColor) (*fi).V1(j)->C() = BorderColor;
385  if( (*fi).V1(j)->C() == InternalColor) (*fi).V1(j)->C() = MixColor;
386  } else
387  {
388  if( (*fi).V(j)->C() == BaseColor) (*fi).V(j)->C() = InternalColor;
389  if( (*fi).V(j)->C() == BorderColor) (*fi).V(j)->C() = MixColor;
390  if( (*fi).V1(j)->C() == BaseColor) (*fi).V1(j)->C() = InternalColor;
391  if( (*fi).V1(j)->C() == BorderColor) (*fi).V1(j)->C() = MixColor;
392  }
393  }
394 
395  }
396 
401  static void PerFaceRandomConnectedComponent( MeshType &m)
402  {
403  RequirePerFaceColor(m);
404  RequireFFAdjacency(m);
405 
406  std::vector< std::pair<int, typename MeshType::FacePointer> > CCV;
407  int ScatterSize= std::min (100,tri::Clean<MeshType>::ConnectedComponents(m, CCV)); // number of random color to be used. Never use too many.
408 
409  ConnectedComponentIterator<MeshType> ci;
410  for(unsigned int i=0;i<CCV.size();++i)
411  {
412  Color4b BaseColor = Color4b::Scatter(ScatterSize, i%ScatterSize,.4f,.7f);
413  std::vector<typename MeshType::FacePointer> FPV;
414  for(ci.start(m,CCV[i].second);!ci.completed();++ci)
415  (*ci)->C()=BaseColor;
416  }
417  }
418 
423  static void PerFaceRandom(MeshType &m)
424  {
425  RequirePerFaceColor(m);
426  FaceIterator fi;
427  Color4b BaseColor = Color4b::Black;
428  PerFaceConstant(m,BaseColor);
429  int id_num=0;
430  for(fi=m.face.begin();fi!=m.face.end();++fi)
431  if(!(*fi).IsD())
432  {
433  id_num++;
434  if((*fi).C() == BaseColor) (*fi).C() = Color4b::Scatter(50, id_num%50,.4f,.7f);
435  for(int j=0;j<3;++j)
436  if((*fi).IsF(j))
437  {
438  assert(!IsBorder((*fi),j));
439  (*fi).FFp(j)->C()= (*fi).C();
440  }
441  }
442  }
443 
450  static void PerVertexPerlinNoise(MeshType& m, CoordType period, CoordType offset = CoordType(0, 0, 0), bool onSelected = false)
451  {
452  RequirePerVertexColor(m);
453 
454  CoordType p[3];
455 
456  for(VertexIterator vi = m.vert.begin(); vi!=m.vert.end(); ++vi)
457  if(!(*vi).IsD())
458  if ((!onSelected) || ((*vi).IsS()))
459  {
460  // perlin noise is defined in 022
461  p[0] = (vi->P()/period[0])+offset;
462  p[1] = (vi->P()/period[1])+offset;
463  p[2] = (vi->P()/period[2])+offset;
464  (*vi).C() = Color4b( int(127+128.0*math::Perlin::Noise(p[0][0],p[0][1],p[0][2])),
465  int(127+128.0*math::Perlin::Noise(p[1][0],p[1][1],p[1][2])),
466  int(127+128.0*math::Perlin::Noise(p[2][0],p[2][1],p[2][2])),
467  255 );
468  }
469 
470  }
471 
472 
477  static void PerVertexPerlinColoring(MeshType& m, ScalarType period, CoordType offset = CoordType(0, 0, 0), Color4b color1 = Color4b::Black, Color4b color2 = Color4b::White, bool onSelected = false)
478  {
479  RequirePerVertexColor(m);
480 
481  CoordType p;
482 
483  for (VertexIterator vi = m.vert.begin(); vi != m.vert.end(); ++vi)
484  if (!(*vi).IsD())
485  if ((!onSelected) || ((*vi).IsS()))
486  {
487  // perlin noise is defined in 022
488  p = (vi->P() / period) + offset;
489  double factor = (math::Perlin::Noise(p[0], p[1], p[2]) + 1.0) / 2.0;
490 
491  int rr = (color1[0] * factor) + (color2[0] * (1.0 - factor));
492  int gg = (color1[1] * factor) + (color2[1] * (1.0 - factor));
493  int bb = (color1[2] * factor) + (color2[2] * (1.0 - factor));
494  int aa = (color1[3] * factor) + (color2[3] * (1.0 - factor));
495 
496  (*vi).C() = Color4b(rr, gg, bb, aa);
497  }
498  }
499 
503  static void PerVertexAddNoise(MeshType& m, int noiseBits, bool onSelected=false)
504  {
505  RequirePerVertexColor(m);
506 
507  if(noiseBits>8) noiseBits = 8;
508  if(noiseBits<1) return;
509 
510  math::SubtractiveRingRNG randomGen = math::SubtractiveRingRNG(time(NULL));
511  for(VertexIterator vi = m.vert.begin(); vi!=m.vert.end(); ++vi)
512  if(!(*vi).IsD())
513  if ((!onSelected) || ((*vi).IsS()))
514  {
515  (*vi).C()[0] = math::Clamp<int>((*vi).C()[0] + randomGen.generate(int(2*pow(2.0f,noiseBits))) - int(pow(2.0f,noiseBits)),0,255);
516  (*vi).C()[1] = math::Clamp<int>((*vi).C()[1] + randomGen.generate(int(2*pow(2.0f,noiseBits))) - int(pow(2.0f,noiseBits)),0,255);
517  (*vi).C()[2] = math::Clamp<int>((*vi).C()[2] + randomGen.generate(int(2*pow(2.0f,noiseBits))) - int(pow(2.0f,noiseBits)),0,255);
518  }
519 
520  }
521 
522 
525  static int PerVertexThresholding(MeshType &m, float threshold, const Color4b c1 = Color4<unsigned char>::Black, const Color4b c2 = Color4<unsigned char>::White, const bool ProcessSelected=false)
526  {
527  RequirePerVertexColor(m);
528 
529  int counter=0;
530  VertexIterator vi;
531  for(vi=m.vert.begin();vi!=m.vert.end();++vi) //scan all the vertex...
532  {
533  if(!(*vi).IsD()) //if it has not been deleted...
534  {
535  if(!ProcessSelected || (*vi).IsS()) //if this vertex has been selected, do transormation
536  {
537  float value = ComputeLightness((*vi).C());
538 
539  if(value<=threshold) (*vi).C() = c1;
540  else (*vi).C() = c2;
541  ++counter;
542  }
543  }
544  }
545  return counter;
546  }
547 
548  // Computes the lightness value for a specified color. lightness = 0.5*(Max(R,G,B)+Min(R,G,B))
549  static float ComputeLightness(Color4b c)
550  {
551  float min_rgb = (float)math::Min(c[0],c[1],c[2]);
552  float max_rgb = (float)math::Max(c[0],c[1],c[2]);
553  return (max_rgb + min_rgb)/2;
554  }
555 
558  static int PerVertexBrightness(MeshType &m, float amount, const bool ProcessSelected=false)
559  {
560  RequirePerVertexColor(m);
561 
562  int counter=0;
563  VertexIterator vi;
564  for(vi=m.vert.begin();vi!=m.vert.end();++vi) //scan all the vertex...
565  {
566  if(!(*vi).IsD()) //if it has not been deleted...
567  {
568  if(!ProcessSelected || (*vi).IsS()) //if this vertex has been selected, do transormation
569  {
570  (*vi).C() = Color4b(
571  math::Clamp(int((*vi).C()[0]+amount),0,255),
572  math::Clamp(int((*vi).C()[1]+amount),0,255),
573  math::Clamp(int((*vi).C()[2]+amount),0,255),
574  255);
575  ++counter;
576  }
577  }
578  }
579  return counter;
580  }
581 
584  static int PerVertexContrast(MeshType &m, float factor, const bool ProcessSelected=false)
585  {
586  RequirePerVertexColor(m);
587 
588  int counter=0;
589  VertexIterator vi;
590  for(vi=m.vert.begin();vi!=m.vert.end();++vi) //scan all the vertex...
591  {
592  if(!(*vi).IsD()) //if it has not been deleted...
593  {
594  if(!ProcessSelected || (*vi).IsS()) //if this vertex has been selected, do transormation
595  {
596  (*vi).C() = ColorMul((*vi).C(),factor);
597  ++counter;
598  }
599  }
600  }
601  return counter;
602  }
603 
604  //Performs contrast operations on color, i.e expands or compress the histogram around
605  //the midpoint value. NewValue = (OldValue - 128) ◊ factor + 128
606  static Color4b ColorMul(Color4b c, float factor)
607  {
608  return Color4b( ValueMul(c[0], factor), ValueMul(c[1], factor), ValueMul(c[2], factor), 1);
609  }
610 
611  static int ValueMul(int value, float factor)
612  {
613  return math::Clamp<int>((int)((value - 128)*factor + 128), 0, 255);
614  }
615 
624  static int PerVertexBrightnessContrast(MeshType &m, float brightness, float contrast, const bool ProcessSelected=false)
625  {
626  RequirePerVertexColor(m);
627 
628  int counter=0;
629  VertexIterator vi;
630  for(vi=m.vert.begin();vi!=m.vert.end();++vi) //scan all the vertex...
631  {
632  if(!(*vi).IsD()) //if it has not been deleted...
633  {
634  if(!ProcessSelected || (*vi).IsS()) //if this vertex has been selected, do transormation
635  {
636  (*vi).C() = ColorBrightnessContrast((*vi).C(),brightness,contrast);
637  ++counter;
638  }
639  }
640  }
641  return counter;
642  }
643 
644  static Color4b ColorBrightnessContrast(Color4b c, float brightness, float contrast)
645  {
646  return Color4b( ValueBrightnessContrast(c[0], brightness, contrast),
647  ValueBrightnessContrast(c[1], brightness, contrast),
648  ValueBrightnessContrast(c[2], brightness, contrast), 1 );
649  }
650 
651  static int ValueBrightnessContrast(unsigned char ivalue, float brightness, float contrast)
652  {
653  float value = float(ivalue)/255.0f;
654  if (brightness < 0.0) value = value * ( 1.0 + brightness);
655  else value = value + ((1.0 - value) * brightness);
656  value = (value - 0.5) * (tan ((contrast + 1) * M_PI/4) ) + 0.5;
657  return math::Clamp<int>(255.0*value, 0, 255);
658  }
659 
664  static int PerVertexInvert(MeshType &m, const bool ProcessSelected=false)
665  {
666  RequirePerVertexColor(m);
667 
668  int counter=0;
669  for(VertexIterator vi=m.vert.begin(); vi!=m.vert.end(); ++vi) //scan all the vertex...
670  {
671  if(!(*vi).IsD()) //if it has not been deleted...
672  {
673  if(!ProcessSelected || (*vi).IsS()) //if this vertex has been selected, do transormation
674  {
675  Color4b &c=(*vi).C();
676  c=Color4b( 255-c[0],255-c[1],255-c[2], 1);
677  ++counter;
678  }
679  }
680  }
681  return counter;
682  }
683 
687  static int PerVertexGamma(MeshType &m, float gamma, const bool ProcessSelected=false)
688  {
689  RequirePerVertexColor(m);
690 
691  int counter=0;
692 
693  VertexIterator vi;
694  for(vi=m.vert.begin();vi!=m.vert.end();++vi) //scan all the vertex...
695  {
696  if(!(*vi).IsD()) //if it has not been deleted...
697  {
698  if(!ProcessSelected || (*vi).IsS()) //if this vertex has been selected, do transormation
699  {
700  (*vi).C() = ColorPow((*vi).C(), 1/gamma);
701  ++counter;
702  }
703  }
704  }
705  return counter;
706  }
707 
708  //computes the standard gamma transformation on a given color, according to NewVal = OldVal^(1/gamma)
709  static Color4b ColorPow(Color4b c, float exponent)
710  {
711  return vcg::Color4b(
712  math::Clamp((int)( ValuePow(float(c[0])/255, exponent)*255), 0, 255),
713  math::Clamp((int)( ValuePow(float(c[1])/255, exponent)*255), 0, 255),
714  math::Clamp((int)( ValuePow(float(c[2])/255, exponent)*255), 0, 255),
715  255);
716  }
717 
718  static float ValuePow(float value, float exponent)
719  {
720  return powf(value, exponent);
721  }
722 
723  //useful bit masks for RGB channels, used for Levels filter.
724  enum rgbChMask {ALL_CHANNELS = 7, RED_CHANNEL = 4, GREEN_CHANNEL = 2, BLUE_CHANNEL = 1, NO_CHANNELS = 0 };
725 
734  static int PerVertexLevels(MeshType &m, float gamma, float in_min, float in_max, float out_min, float out_max, unsigned char rgbMask, const bool ProcessSelected=false)
735  {
736  RequirePerVertexColor(m);
737 
738  int counter=0;
739  VertexIterator vi;
740  for(vi=m.vert.begin();vi!=m.vert.end();++vi) //scan all the vertex...
741  {
742  if(!(*vi).IsD()) //if it has not been deleted...
743  {
744  if(!ProcessSelected || (*vi).IsS()) //if this vertex has been selected, do transormation
745  {
746  (*vi).C() = ColorLevels((*vi).C(), gamma, in_min, in_max, out_min, out_max, rgbMask);
747  ++counter;
748  }
749  }
750  }
751  return counter;
752  }
753 
754  //Performs levels transformation on each channel set to 1 in the rgbMask.
755  static Color4b ColorLevels(Color4b c, float gamma, float in_min, float in_max, float out_min, float out_max, unsigned char rgbMask)
756  {
757  unsigned char r = c[0], g = c[1], b = c[2];
758  if(rgbMask & RED_CHANNEL) r = ValueLevels(c[0], gamma, in_min, in_max, out_min, out_max);
759  if(rgbMask & GREEN_CHANNEL) g = ValueLevels(c[1], gamma, in_min, in_max, out_min, out_max);
760  if(rgbMask & BLUE_CHANNEL) b = ValueLevels(c[2], gamma, in_min, in_max, out_min, out_max);
761  return Color4b(r, g, b, 255);
762  }
763 
764  //Transform on levels
765  static int ValueLevels(int value, float gamma, float in_min, float in_max, float out_min, float out_max)
766  {
767  float fvalue = value/255.0f;
768  // normalize
769  fvalue = math::Clamp<float>(fvalue - in_min, 0.0f, 1.0f) / math::Clamp<float>(in_max - in_min, 1.0f/255.0f, 1.0f);
770  // transform gamma
771  fvalue = powf(fvalue,1/gamma);
772  // rescale range
773  fvalue = fvalue * (out_max - out_min) + out_min;
774  //back in interval [0,255] and clamp
775  return math::Clamp<int>((int)(fvalue * 255), 0, 255);
776  }
777 
783  static int PerVertexColourisation(MeshType &m, Color4b c, float intensity, const bool ProcessSelected=false)
784  {
785  RequirePerVertexColor(m);
786 
787  int counter=0;
788  VertexIterator vi;
789  for(vi=m.vert.begin();vi!=m.vert.end();++vi)
790  {
791  if(!(*vi).IsD()) //if it has not been deleted...
792  {
793  if(!ProcessSelected || (*vi).IsS()) //if this vertex has been selected, do transormation
794  {
795  (*vi).C() = ColorApplyDiff((*vi).C(), c, intensity);
796  ++counter;
797  }
798  }
799  }
800  return counter;
801  }
802 
803  // Perform colourisation operation.
804  // For each channel C:
805  // newC = origC + intensity * (newC - origC)
806  static Color4b ColorApplyDiff(Color4b old_color, Color4b new_color, float intensity)
807  {
808  return Color4b( ValueApplyDiff(old_color[0], new_color[0], intensity),
809  ValueApplyDiff(old_color[1], new_color[1], intensity),
810  ValueApplyDiff(old_color[2], new_color[2], intensity), 255);
811  }
812 
813  static int ValueApplyDiff(int old_value, int new_value, float intensity)
814  {
815  return math::Clamp<int>((int)(old_value + intensity * (new_value - old_value)), 0, 255);
816  }
817 
818  //An useful ENUM to hold all desaturation methods.
819  enum DesaturationMethods {M_LIGHTNESS = 0, M_LUMINOSITY = 1, M_AVERAGE = 2};
820 
830  static int PerVertexDesaturation(MeshType &m, int method, const bool ProcessSelected=false)
831  {
832  RequirePerVertexColor(m);
833 
834  int counter=0;
835  VertexIterator vi;
836  for(vi=m.vert.begin();vi!=m.vert.end();++vi) //scan all the vertex...
837  {
838  if(!(*vi).IsD()) //if it has not been deleted...
839  {
840  if(!ProcessSelected || (*vi).IsS()) //if this vertex has been selected, do transormation
841  {
842  (*vi).C() = ColorDesaturate((*vi).C(), method);
843  ++counter;
844  }
845  }
846  }
847  return counter;
848  }
849 
850  //Desature the color. Ausiliary functions to calculate lightness/luminosity/average.
851  static Color4b ColorDesaturate(Color4b c, int method)
852  {
853  switch(method){
854  case M_LIGHTNESS:{
855  int val = (int)ComputeLightness(c);
856  return Color4b( val, val, val, 255);
857  }
858  case M_AVERAGE:{
859  int val = (int)ComputeAvgLightness(c);
860  return Color4b( val, val, val, 255);
861  }
862  case M_LUMINOSITY:{
863  int val = (int)ComputeLuminosity(c);
864  return Color4b( val, val, val, 255);
865  }
866  default: assert(0);
867  }
868  return Color4b(255, 255, 255, 255);
869  }
870 
871  //ausiliary function to compute average lightness. value = (R+G+B)/3
872  static float ComputeAvgLightness(Color4b c)
873  {
874  return float(c[0]+c[1]+c[2])/3.0f;
875  }
876 
877  //ausiliary function to compute luminosity. value = 0.21*R+0.71*G+0.7*B
878  static float ComputeLuminosity(Color4b c)
879  {
880  return float(0.2126f*c[0]+0.7152f*c[1]+0.0722f*c[2]);
881  }
882 
888  static int PerVertexEqualize(MeshType &m, unsigned int rgbMask, const bool ProcessSelected=false)
889  {
890  RequirePerVertexColor(m);
891 
892  //declares , resets and set up 4 histograms, for Red, Green, Blue and Lightness
893  Histogramf Hl, Hr, Hg, Hb;
894  Hl.Clear(); Hr.Clear(); Hg.Clear(); Hb.Clear();
895  Hl.SetRange(0, 255, 255); Hr.SetRange(0, 255, 255); Hg.SetRange(0, 255, 255); Hb.SetRange(0, 255, 255);
896 
897  int counter=0;
898  VertexIterator vi;
899 
900  //Scan the mesh to build the histograms
901  for(vi=m.vert.begin();vi!=m.vert.end();++vi) //scan all the vertex...
902  {
903  if(!(*vi).IsD()) //if it has not been deleted...
904  {
905  if(!ProcessSelected || (*vi).IsS()) //if this vertex has been selected, put it in the histograms
906  {
907  float v = ComputeLightness((*vi).C())+0.5; //compute and round lightness value
908  Hl.Add(v); Hr.Add((float)(*vi).C()[0]); Hg.Add((float)(*vi).C()[1]); Hb.Add((float)(*vi).C()[2]);
909  }
910  }
911  }
912 
913  //for each histogram, compute the cumulative distribution function, and build a lookup table
914  int cdf_l[256], cdf_r[256], cdf_g[256], cdf_b[256];
915  cdf_l[0] = Hl.BinCount(0); cdf_r[0] = Hr.BinCount(0); cdf_g[0] = Hg.BinCount(0); cdf_b[0] = Hb.BinCount(0);
916  for(int i=1; i<256; i++){
917  cdf_l[i] = Hl.BinCount(float(i)) + cdf_l[i-1];
918  cdf_r[i] = Hr.BinCount(float(i)) + cdf_r[i-1];
919  cdf_g[i] = Hg.BinCount(float(i)) + cdf_g[i-1];
920  cdf_b[i] = Hb.BinCount(float(i)) + cdf_b[i-1];
921  }
922 
923  //this loop aaplies the transformation to colors
924  for(vi=m.vert.begin();vi!=m.vert.end();++vi) //scan all the vertex...
925  {
926  if(!(*vi).IsD()) //if it has not been deleted...
927  {
928  if(!ProcessSelected || (*vi).IsS()) //if this vertex has been selected, do transormation
929  {
930  (*vi).C()=ColorEqualize((*vi).C(), cdf_l, cdf_r, cdf_g, cdf_b, rgbMask);
931  ++counter;
932  }
933  }
934  }
935  return counter;
936  }
937 
938  //Applies equalization to the components of the color according to rgbmask
939  static Color4b ColorEqualize(Color4b c, int cdf_l[256], int cdf_r[256], int cdf_g[256], int cdf_b[256], unsigned int rgbMask)
940  {
941  unsigned char r = c[0], g = c[1], b = c[2];
942  if(rgbMask == NO_CHANNELS) //in this case, equalization is done on lightness
943  {
944  int v = ValueEqualize(cdf_l[(int)(ComputeLightness(c)+0.5f)], cdf_l[0], cdf_l[255]);
945  return Color4b(v, v, v, 255); //return the equalized gray color
946  }
947  if(rgbMask & RED_CHANNEL) r = ValueEqualize(cdf_r[c[0]], cdf_r[0], cdf_r[255]); //Equalizes red
948  if(rgbMask & GREEN_CHANNEL) g = ValueEqualize(cdf_g[c[1]], cdf_g[0], cdf_g[255]); //Equalizes green
949  if(rgbMask & BLUE_CHANNEL) b = ValueEqualize(cdf_b[c[2]], cdf_b[0], cdf_b[255]); //Equalizes blue
950  return Color4b(r, g, b, 255); //return the equalized color
951  }
952 
953  //Compute the equalized value
954  static int ValueEqualize(int cdfValue, int cdfMin, int cdfMax)
955  {
956  return int(float((cdfValue - cdfMin)/float(cdfMax - cdfMin)) * 255.0f);
957  }
958 
964  static int PerVertexWhiteBalance(MeshType &m, Color4b userColor, const bool ProcessSelected=false)
965  {
966  RequirePerVertexColor(m);
967 
968  Color4b unbalancedWhite= userColor;
969  int counter=0;
970  VertexIterator vi;
971 
972  //in this loop the transformation is applied to the mesh
973  for(vi=m.vert.begin();vi!=m.vert.end();++vi) //scan all the vertex...
974  {
975  if(!(*vi).IsD()) //if it has not been deleted...
976  {
977  if(!ProcessSelected || (*vi).IsS()) //if this vertex has been selected, do transormation
978  {
979  (*vi).C()=ColorWhiteBalance((*vi).C(),unbalancedWhite);
980  ++counter;
981  }
982  }
983  }
984  return counter;
985  }
986 
987  //Balnce the white of the color, applying a correction factor based on the unbalancedWhite color.
988  static Color4b ColorWhiteBalance(Color4b c, Color4b unbalancedWhite)
989  {
990  //sanity check to avoid division by zero...
991  if(unbalancedWhite[0]==0) unbalancedWhite[0]=1;
992  if(unbalancedWhite[1]==0) unbalancedWhite[1]=1;
993  if(unbalancedWhite[2]==0) unbalancedWhite[2]=1;
994 
995  return Color4b(
996  math::Clamp<int>((int)(c[0]*(255.0f/unbalancedWhite[0])), 0, 255),
997  math::Clamp<int>((int)(c[1]*(255.0f/unbalancedWhite[1])), 0, 255),
998  math::Clamp<int>((int)(c[2]*(255.0f/unbalancedWhite[2])), 0, 255),
999  255);
1000  }
1001 
1002 };
1003 
1004 }// end namespace
1005 }// end namespace
1006 #endif