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 
63  typedef typename MeshType::ScalarType ScalarType;
64  typedef typename MeshType::CoordType CoordType;
65 
66  class ColorAvgInfo
67  {
68  public:
69  unsigned int r;
70  unsigned int g;
71  unsigned int b;
72  unsigned int a;
73  int cnt;
74  };
75 
78  static int PerVertexConstant(MeshType &m, Color4b vs=Color4b::White,bool selected=false)
79  {
80  RequirePerVertexColor(m);
81 
82  int cnt=0;
83  for(VertexIterator vi=m.vert.begin();vi!=m.vert.end();++vi)
84  if(!(*vi).IsD()){
85  if(!selected || (*vi).IsS())
86  {
87  (*vi).C() = vs;
88  ++cnt;
89  }
90  }
91  return cnt;
92  }
93 
96  static int PerFaceConstant(MeshType &m, Color4b vs=Color4b::White,bool selected=false)
97  {
98  RequirePerFaceColor(m);
99  int cnt=0;
100  for(FaceIterator fi=m.face.begin();fi!=m.face.end();++fi)
101  if(!(*fi).IsD()){
102  if(!selected || (*fi).IsS())
103  {
104  (*fi).C() = vs;
105  ++cnt;
106  }
107  }
108  return cnt;
109  }
110 
116  static void PerVertexFromFace( MeshType &m)
117  {
118  RequirePerFaceColor(m);
119  RequirePerVertexColor(m);
120 
121  ColorAvgInfo csi;
122  csi.r=0; csi.g=0; csi.b=0; csi.cnt=0;
123  SimpleTempData<typename MeshType::VertContainer, ColorAvgInfo> TD(m.vert,csi);
124 
125  FaceIterator fi;
126  for(fi=m.face.begin();fi!=m.face.end();++fi)
127  if(!(*fi).IsD())
128  for(int j=0;j<3;++j)
129  {
130  TD[(*fi).V(j)].r+=(*fi).C()[0];
131  TD[(*fi).V(j)].g+=(*fi).C()[1];
132  TD[(*fi).V(j)].b+=(*fi).C()[2];
133  TD[(*fi).V(j)].a+=(*fi).C()[3];
134  ++TD[(*fi).V(j)].cnt;
135  }
136 
137  VertexIterator vi;
138  for(vi=m.vert.begin();vi!=m.vert.end();++vi)
139  if(!(*vi).IsD() && TD[*vi].cnt>0 )
140  {
141  (*vi).C()[0] = TD[*vi].r / TD[*vi].cnt;
142  (*vi).C()[1] = TD[*vi].g / TD[*vi].cnt;
143  (*vi).C()[2] = TD[*vi].b / TD[*vi].cnt;
144  (*vi).C()[3] = TD[*vi].a / TD[*vi].cnt;
145  }
146  }
147 
151  static void PerFaceFromVertex( MeshType &m)
152  {
153  RequirePerFaceColor(m);
154  RequirePerVertexColor(m);
155 
156  FaceIterator fi;
157  for(fi=m.face.begin();fi!=m.face.end();++fi) if(!(*fi).IsD())
158  {
159  Color4f avg = (Color4f::Construct((*fi).V(0)->C()) +
160  Color4f::Construct((*fi).V(1)->C()) +
161  Color4f::Construct((*fi).V(2)->C()) )/ 3.0;
162  (*fi).C().Import(avg);
163  }
164  }
165 
170  static void PerVertexQualityRamp(MeshType &m, float minq=0, float maxq=0)
171  {
172  RequirePerVertexQuality(m);
173  RequirePerVertexColor(m);
174 
175  if(minq==maxq)
176  {
177  std::pair<float,float> minmax = Stat<MeshType>::ComputePerVertexQualityMinMax(m);
178  minq=minmax.first;
179  maxq=minmax.second;
180  }
181  for(VertexIterator vi=m.vert.begin();vi!=m.vert.end();++vi)
182  if(!(*vi).IsD())
183  (*vi).C().SetColorRamp(minq,maxq,(*vi).Q());
184  }
185 
186 
191  static void PerVertexQualityRampParula(MeshType &m, float minq=0, float maxq=0)
192  {
193  RequirePerVertexQuality(m);
194  RequirePerVertexColor(m);
195 
196  if(minq==maxq)
197  {
198  std::pair<float,float> minmax = Stat<MeshType>::ComputePerVertexQualityMinMax(m);
199  minq=minmax.first;
200  maxq=minmax.second;
201  }
202  for(VertexIterator vi=m.vert.begin();vi!=m.vert.end();++vi)
203  if(!(*vi).IsD())
204  (*vi).C().SetColorRampParula(minq,maxq,(*vi).Q());
205  }
206 
211  static void PerFaceQualityRamp(MeshType &m, float minq=0, float maxq=0, bool selected=false)
212  {
213  RequirePerFaceColor(m);
214  RequirePerFaceQuality(m);
215 
216  if(minq==maxq)
217  {
218  std::pair<float,float> minmax = Stat<MeshType>::ComputePerFaceQualityMinMax(m);
219  minq=minmax.first;
220  maxq=minmax.second;
221  }
222  for(FaceIterator fi=m.face.begin();fi!=m.face.end();++fi) if(!(*fi).IsD())
223  if(!selected || (*fi).IsS())
224  (*fi).C().SetColorRamp(minq,maxq,(*fi).Q());
225  }
226 
231  static void PerEdgeQualityRamp(MeshType &m, float minq=0, float maxq=0, bool selected=false)
232  {
233  RequirePerEdgeColor(m);
234  RequirePerEdgeQuality(m);
235 
236  if(minq==maxq)
237  {
238  std::pair<float,float> minmax = Stat<MeshType>::ComputePerEdgeQualityMinMax(m);
239  minq=minmax.first;
240  maxq=minmax.second;
241  }
242  for(EdgeIterator ei=m.edge.begin();ei!=m.edge.end();++ei) if(!(*ei).IsD())
243  if(!selected || (*ei).IsS())
244  (*ei).C().SetColorRamp(minq,maxq,(*ei).Q());
245  }
246 
251  static void PerVertexQualityGray(MeshType &m, float minq, float maxq)
252  {
253  RequirePerVertexColor(m);
254  RequirePerVertexQuality(m);
255  if(minq==maxq)
256  {
257  std::pair<float,float> minmax = Stat<MeshType>::ComputePerVertexQualityMinMax(m);
258  minq=minmax.first;
259  maxq=minmax.second;
260  }
261  for(VertexIterator vi=m.vert.begin();vi!=m.vert.end();++vi)
262  if(!(*vi).IsD())
263  (*vi).C().SetGrayShade( ((*vi).Q()-minq)/(maxq-minq));
264  }
265 
270  static void PerFaceQualityGray(MeshType &m, float minq=0, float maxq=0)
271  {
272  RequirePerFaceColor(m);
273  RequirePerFaceQuality(m);
274 
275  if(minq==maxq)
276  {
277  std::pair<float,float> minmax = Stat<MeshType>::ComputePerFaceQualityMinMax(m);
278  minq=minmax.first;
279  maxq=minmax.second;
280  }
281  for(FaceIterator fi=m.face.begin();fi!=m.face.end();++fi) if(!(*fi).IsD())
282  (*fi).C().SetGrayShade( ((*fi).Q()-minq)/(maxq-minq));
283  }
284 
296  static void PerVertexBorderFlag( MeshType &m, Color4b BorderColor=Color4b::Blue, Color4b InternalColor=Color4b::White, Color4b MixColor=Color4b::Cyan)
297  {
298  RequirePerVertexColor(m);
299 
300  Color4b BaseColor = Color4b::Green;
301 
302  PerVertexConstant(m,BaseColor);
303  for(FaceIterator fi=m.face.begin();fi!=m.face.end();++fi) if(!(*fi).IsD())
304  for(int j=0;j<3;++j)
305  {
306  if((*fi).IsB(j)){
307  if( (*fi).V(j)->C() == BaseColor) (*fi).V(j)->C() = BorderColor;
308  if( (*fi).V(j)->C() == InternalColor) (*fi).V(j)->C() = MixColor;
309  if( (*fi).V1(j)->C() == BaseColor) (*fi).V1(j)->C() = BorderColor;
310  if( (*fi).V1(j)->C() == InternalColor) (*fi).V1(j)->C() = MixColor;
311  } else
312  {
313  if( (*fi).V(j)->C() == BaseColor) (*fi).V(j)->C() = InternalColor;
314  if( (*fi).V(j)->C() == BorderColor) (*fi).V(j)->C() = MixColor;
315  if( (*fi).V1(j)->C() == BaseColor) (*fi).V1(j)->C() = InternalColor;
316  if( (*fi).V1(j)->C() == BorderColor) (*fi).V1(j)->C() = MixColor;
317  }
318  }
319 
320  }
321 
326  static void PerFaceRandomConnectedComponent( MeshType &m)
327  {
328  RequirePerFaceColor(m);
329  RequireFFAdjacency(m);
330 
331  std::vector< std::pair<int, typename MeshType::FacePointer> > CCV;
332  int ScatterSize= std::min (100,tri::Clean<MeshType>::ConnectedComponents(m, CCV)); // number of random color to be used. Never use too many.
333 
334  ConnectedComponentIterator<MeshType> ci;
335  for(unsigned int i=0;i<CCV.size();++i)
336  {
337  Color4b BaseColor = Color4b::Scatter(ScatterSize, i%ScatterSize,.4f,.7f);
338  std::vector<typename MeshType::FacePointer> FPV;
339  for(ci.start(m,CCV[i].second);!ci.completed();++ci)
340  (*ci)->C()=BaseColor;
341  }
342  }
343 
348  static void PerFaceRandom(MeshType &m)
349  {
350  RequirePerFaceColor(m);
351  FaceIterator fi;
352  Color4b BaseColor = Color4b::Black;
353  PerFaceConstant(m,BaseColor);
354  int id_num=0;
355  for(fi=m.face.begin();fi!=m.face.end();++fi)
356  if(!(*fi).IsD())
357  {
358  id_num++;
359  if((*fi).C() == BaseColor) (*fi).C() = Color4b::Scatter(50, id_num%50,.4f,.7f);
360  for(int j=0;j<3;++j)
361  if((*fi).IsF(j))
362  {
363  assert(!IsBorder((*fi),j));
364  (*fi).FFp(j)->C()= (*fi).C();
365  }
366  }
367  }
368 
375 static void PerVertexPerlinNoise(MeshType& m, CoordType period, CoordType offset = CoordType(0, 0, 0), bool onSelected = false)
376 {
377  RequirePerVertexColor(m);
378 
379  CoordType p[3];
380 
381  for(VertexIterator vi = m.vert.begin(); vi!=m.vert.end(); ++vi)
382  if(!(*vi).IsD())
383  if ((!onSelected) || ((*vi).IsS()))
384  {
385  // perlin noise is defined in 022
386  p[0] = (vi->P()/period[0])+offset;
387  p[1] = (vi->P()/period[1])+offset;
388  p[2] = (vi->P()/period[2])+offset;
389  (*vi).C() = Color4b( int(127+128.0*math::Perlin::Noise(p[0][0],p[0][1],p[0][2])),
390  int(127+128.0*math::Perlin::Noise(p[1][0],p[1][1],p[1][2])),
391  int(127+128.0*math::Perlin::Noise(p[2][0],p[2][1],p[2][2])),
392  255 );
393  }
394 
395 }
396 
397 
402 static void PerVertexPerlinColoring(MeshType& m, ScalarType period, CoordType offset = CoordType(0, 0, 0), Color4b color1 = Color4b::Black, Color4b color2 = Color4b::White, bool onSelected = false)
403 {
404  RequirePerVertexColor(m);
405 
406  CoordType p;
407 
408  for (VertexIterator vi = m.vert.begin(); vi != m.vert.end(); ++vi)
409  if (!(*vi).IsD())
410  if ((!onSelected) || ((*vi).IsS()))
411  {
412  // perlin noise is defined in 022
413  p = (vi->P() / period) + offset;
414  double factor = (math::Perlin::Noise(p[0], p[1], p[2]) + 1.0) / 2.0;
415 
416  int rr = (color1[0] * factor) + (color2[0] * (1.0 - factor));
417  int gg = (color1[1] * factor) + (color2[1] * (1.0 - factor));
418  int bb = (color1[2] * factor) + (color2[2] * (1.0 - factor));
419  int aa = (color1[3] * factor) + (color2[3] * (1.0 - factor));
420 
421  (*vi).C() = Color4b(rr, gg, bb, aa);
422  }
423 }
424 
428 static void PerVertexAddNoise(MeshType& m, int noiseBits, bool onSelected=false)
429 {
430  RequirePerVertexColor(m);
431 
432  if(noiseBits>8) noiseBits = 8;
433  if(noiseBits<1) return;
434 
435  math::SubtractiveRingRNG randomGen = math::SubtractiveRingRNG(time(NULL));
436  for(VertexIterator vi = m.vert.begin(); vi!=m.vert.end(); ++vi)
437  if(!(*vi).IsD())
438  if ((!onSelected) || ((*vi).IsS()))
439  {
440  (*vi).C()[0] = math::Clamp<int>((*vi).C()[0] + randomGen.generate(int(2*pow(2.0f,noiseBits))) - int(pow(2.0f,noiseBits)),0,255);
441  (*vi).C()[1] = math::Clamp<int>((*vi).C()[1] + randomGen.generate(int(2*pow(2.0f,noiseBits))) - int(pow(2.0f,noiseBits)),0,255);
442  (*vi).C()[2] = math::Clamp<int>((*vi).C()[2] + randomGen.generate(int(2*pow(2.0f,noiseBits))) - int(pow(2.0f,noiseBits)),0,255);
443  }
444 
445 }
446 
447 
450 static int PerVertexThresholding(MeshType &m, float threshold, Color4b c1 = Color4<unsigned char>::Black, Color4b c2 = Color4<unsigned char>::White, const bool ProcessSelected=false)
451 {
452  RequirePerVertexColor(m);
453 
454  int counter=0;
455  VertexIterator vi;
456  for(vi=m.vert.begin();vi!=m.vert.end();++vi) //scan all the vertex...
457  {
458  if(!(*vi).IsD()) //if it has not been deleted...
459  {
460  if(!ProcessSelected || (*vi).IsS()) //if this vertex has been selected, do transormation
461  {
462  float value = ComputeLightness((*vi).C());
463 
464  if(value<=threshold) (*vi).C() = c1;
465  else (*vi).C() = c2;
466  ++counter;
467  }
468  }
469  }
470  return counter;
471 }
472 
473 // Computes the lightness value for a specified color. lightness = 0.5*(Max(R,G,B)+Min(R,G,B))
474 static float ComputeLightness(Color4b c)
475 {
476  float min_rgb = (float)math::Min(c[0],c[1],c[2]);
477  float max_rgb = (float)math::Max(c[0],c[1],c[2]);
478  return (max_rgb + min_rgb)/2;
479 }
480 
483 static int PerVertexBrightness(MeshType &m, float amount, const bool ProcessSelected=false)
484 {
485  RequirePerVertexColor(m);
486 
487  int counter=0;
488  VertexIterator vi;
489  for(vi=m.vert.begin();vi!=m.vert.end();++vi) //scan all the vertex...
490  {
491  if(!(*vi).IsD()) //if it has not been deleted...
492  {
493  if(!ProcessSelected || (*vi).IsS()) //if this vertex has been selected, do transormation
494  {
495  (*vi).C() = Color4b(
496  math::Clamp(int((*vi).C()[0]+amount),0,255),
497  math::Clamp(int((*vi).C()[1]+amount),0,255),
498  math::Clamp(int((*vi).C()[2]+amount),0,255),
499  255);
500  ++counter;
501  }
502  }
503  }
504  return counter;
505 }
506 
509 static int PerVertexContrast(MeshType &m, float factor, const bool ProcessSelected=false)
510 {
511  RequirePerVertexColor(m);
512 
513  int counter=0;
514  VertexIterator vi;
515  for(vi=m.vert.begin();vi!=m.vert.end();++vi) //scan all the vertex...
516  {
517  if(!(*vi).IsD()) //if it has not been deleted...
518  {
519  if(!ProcessSelected || (*vi).IsS()) //if this vertex has been selected, do transormation
520  {
521  (*vi).C() = ColorMul((*vi).C(),factor);
522  ++counter;
523  }
524  }
525  }
526  return counter;
527 }
528 
529 //Performs contrast operations on color, i.e expands or compress the histogram around
530 //the midpoint value. NewValue = (OldValue - 128) ◊ factor + 128
531 static Color4b ColorMul(Color4b c, float factor)
532 {
533  return Color4b( ValueMul(c[0], factor), ValueMul(c[1], factor), ValueMul(c[2], factor), 1);
534 }
535 
536 static int ValueMul(int value, float factor)
537 {
538  return math::Clamp<int>((int)((value - 128)*factor + 128), 0, 255);
539 }
540 
549 static int PerVertexBrightnessContrast(MeshType &m, float brightness, float contrast, const bool ProcessSelected=false)
550 {
551  RequirePerVertexColor(m);
552 
553  int counter=0;
554  VertexIterator vi;
555  for(vi=m.vert.begin();vi!=m.vert.end();++vi) //scan all the vertex...
556  {
557  if(!(*vi).IsD()) //if it has not been deleted...
558  {
559  if(!ProcessSelected || (*vi).IsS()) //if this vertex has been selected, do transormation
560  {
561  (*vi).C() = ColorBrightnessContrast((*vi).C(),brightness,contrast);
562  ++counter;
563  }
564  }
565  }
566  return counter;
567 }
568 
569 static Color4b ColorBrightnessContrast(Color4b c, float brightness, float contrast)
570 {
571  return Color4b( ValueBrightnessContrast(c[0], brightness, contrast),
572  ValueBrightnessContrast(c[1], brightness, contrast),
573  ValueBrightnessContrast(c[2], brightness, contrast), 1 );
574 }
575 
576 static int ValueBrightnessContrast(unsigned char ivalue, float brightness, float contrast)
577 {
578  float value = float(ivalue)/255.0f;
579  if (brightness < 0.0) value = value * ( 1.0 + brightness);
580  else value = value + ((1.0 - value) * brightness);
581  value = (value - 0.5) * (tan ((contrast + 1) * M_PI/4) ) + 0.5;
582  return math::Clamp<int>(255.0*value, 0, 255);
583 }
584 
589 static int PerVertexInvert(MeshType &m, const bool ProcessSelected=false)
590 {
591  RequirePerVertexColor(m);
592 
593  int counter=0;
594  for(VertexIterator vi=m.vert.begin(); vi!=m.vert.end(); ++vi) //scan all the vertex...
595  {
596  if(!(*vi).IsD()) //if it has not been deleted...
597  {
598  if(!ProcessSelected || (*vi).IsS()) //if this vertex has been selected, do transormation
599  {
600  Color4b &c=(*vi).C();
601  c=Color4b( 255-c[0],255-c[1],255-c[2], 1);
602  ++counter;
603  }
604  }
605  }
606  return counter;
607 }
608 
612 static int PerVertexGamma(MeshType &m, float gamma, const bool ProcessSelected=false)
613 {
614  RequirePerVertexColor(m);
615 
616  int counter=0;
617 
618  VertexIterator vi;
619  for(vi=m.vert.begin();vi!=m.vert.end();++vi) //scan all the vertex...
620  {
621  if(!(*vi).IsD()) //if it has not been deleted...
622  {
623  if(!ProcessSelected || (*vi).IsS()) //if this vertex has been selected, do transormation
624  {
625  (*vi).C() = ColorPow((*vi).C(), 1/gamma);
626  ++counter;
627  }
628  }
629  }
630  return counter;
631 }
632 
633 //computes the standard gamma transformation on a given color, according to NewVal = OldVal^(1/gamma)
634 static Color4b ColorPow(Color4b c, float exponent)
635 {
636  return vcg::Color4b(
637  math::Clamp((int)( ValuePow(float(c[0])/255, exponent)*255), 0, 255),
638  math::Clamp((int)( ValuePow(float(c[1])/255, exponent)*255), 0, 255),
639  math::Clamp((int)( ValuePow(float(c[2])/255, exponent)*255), 0, 255),
640  255);
641 }
642 
643 static float ValuePow(float value, float exponent)
644 {
645  return powf(value, exponent);
646 }
647 
648 //useful bit masks for RGB channels, used for Levels filter.
649 enum rgbChMask {ALL_CHANNELS = 7, RED_CHANNEL = 4, GREEN_CHANNEL = 2, BLUE_CHANNEL = 1, NO_CHANNELS = 0 };
650 
659 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)
660 {
661  RequirePerVertexColor(m);
662 
663  int counter=0;
664  VertexIterator vi;
665  for(vi=m.vert.begin();vi!=m.vert.end();++vi) //scan all the vertex...
666  {
667  if(!(*vi).IsD()) //if it has not been deleted...
668  {
669  if(!ProcessSelected || (*vi).IsS()) //if this vertex has been selected, do transormation
670  {
671  (*vi).C() = ColorLevels((*vi).C(), gamma, in_min, in_max, out_min, out_max, rgbMask);
672  ++counter;
673  }
674  }
675  }
676  return counter;
677 }
678 
679 //Performs levels transformation on each channel set to 1 in the rgbMask.
680 static Color4b ColorLevels(Color4b c, float gamma, float in_min, float in_max, float out_min, float out_max, unsigned char rgbMask)
681 {
682  unsigned char r = c[0], g = c[1], b = c[2];
683  if(rgbMask & RED_CHANNEL) r = ValueLevels(c[0], gamma, in_min, in_max, out_min, out_max);
684  if(rgbMask & GREEN_CHANNEL) g = ValueLevels(c[1], gamma, in_min, in_max, out_min, out_max);
685  if(rgbMask & BLUE_CHANNEL) b = ValueLevels(c[2], gamma, in_min, in_max, out_min, out_max);
686  return Color4b(r, g, b, 255);
687 }
688 
689 //Transform on levels
690 static int ValueLevels(int value, float gamma, float in_min, float in_max, float out_min, float out_max)
691 {
692  float fvalue = value/255.0f;
693  // normalize
694  fvalue = math::Clamp<float>(fvalue - in_min, 0.0f, 1.0f) / math::Clamp<float>(in_max - in_min, 1.0f/255.0f, 1.0f);
695  // transform gamma
696  fvalue = powf(fvalue,1/gamma);
697  // rescale range
698  fvalue = fvalue * (out_max - out_min) + out_min;
699  //back in interval [0,255] and clamp
700  return math::Clamp<int>((int)(fvalue * 255), 0, 255);
701 }
702 
708 static int PerVertexColourisation(MeshType &m, Color4b c, float intensity, const bool ProcessSelected=false)
709 {
710  RequirePerVertexColor(m);
711 
712  int counter=0;
713  VertexIterator vi;
714  for(vi=m.vert.begin();vi!=m.vert.end();++vi)
715  {
716  if(!(*vi).IsD()) //if it has not been deleted...
717  {
718  if(!ProcessSelected || (*vi).IsS()) //if this vertex has been selected, do transormation
719  {
720  (*vi).C() = ColorApplyDiff((*vi).C(), c, intensity);
721  ++counter;
722  }
723  }
724  }
725  return counter;
726 }
727 
728 // Perform colourisation operation.
729 // For each channel C:
730 // newC = origC + intensity * (newC - origC)
731 static Color4b ColorApplyDiff(Color4b old_color, Color4b new_color, float intensity)
732 {
733  return Color4b( ValueApplyDiff(old_color[0], new_color[0], intensity),
734  ValueApplyDiff(old_color[1], new_color[1], intensity),
735  ValueApplyDiff(old_color[2], new_color[2], intensity), 255);
736 }
737 
738 static int ValueApplyDiff(int old_value, int new_value, float intensity)
739 {
740  return math::Clamp<int>((int)(old_value + intensity * (new_value - old_value)), 0, 255);
741 }
742 
743 //An useful ENUM to hold all desaturation methods.
744 enum DesaturationMethods {M_LIGHTNESS = 0, M_LUMINOSITY = 1, M_AVERAGE = 2};
745 
755 static int PerVertexDesaturation(MeshType &m, int method, const bool ProcessSelected=false)
756 {
757  RequirePerVertexColor(m);
758 
759  int counter=0;
760  VertexIterator vi;
761  for(vi=m.vert.begin();vi!=m.vert.end();++vi) //scan all the vertex...
762  {
763  if(!(*vi).IsD()) //if it has not been deleted...
764  {
765  if(!ProcessSelected || (*vi).IsS()) //if this vertex has been selected, do transormation
766  {
767  (*vi).C() = ColorDesaturate((*vi).C(), method);
768  ++counter;
769  }
770  }
771  }
772  return counter;
773 }
774 
775 //Desature the color. Ausiliary functions to calculate lightness/luminosity/average.
776 static Color4b ColorDesaturate(Color4b c, int method)
777 {
778  switch(method){
779  case M_LIGHTNESS:{
780  int val = (int)ComputeLightness(c);
781  return Color4b( val, val, val, 255);
782  }
783  case M_AVERAGE:{
784  int val = (int)ComputeAvgLightness(c);
785  return Color4b( val, val, val, 255);
786  }
787  case M_LUMINOSITY:{
788  int val = (int)ComputeLuminosity(c);
789  return Color4b( val, val, val, 255);
790  }
791  default: assert(0);
792  }
793 }
794 
795 //ausiliary function to compute average lightness. value = (R+G+B)/3
796 static float ComputeAvgLightness(Color4b c)
797 {
798  return float(c[0]+c[1]+c[2])/3.0f;
799 }
800 
801 //ausiliary function to compute luminosity. value = 0.21*R+0.71*G+0.7*B
802 static float ComputeLuminosity(Color4b c)
803 {
804  return float(0.2126f*c[0]+0.7152f*c[1]+0.0722f*c[2]);
805 }
806 
812 static int PerVertexEqualize(MeshType &m, unsigned int rgbMask, const bool ProcessSelected=false)
813 {
814  RequirePerVertexColor(m);
815 
816  //declares , resets and set up 4 histograms, for Red, Green, Blue and Lightness
817  Histogramf Hl, Hr, Hg, Hb;
818  Hl.Clear(); Hr.Clear(); Hg.Clear(); Hb.Clear();
819  Hl.SetRange(0, 255, 255); Hr.SetRange(0, 255, 255); Hg.SetRange(0, 255, 255); Hb.SetRange(0, 255, 255);
820 
821  int counter=0;
822  VertexIterator vi;
823 
824  //Scan the mesh to build the histograms
825  for(vi=m.vert.begin();vi!=m.vert.end();++vi) //scan all the vertex...
826  {
827  if(!(*vi).IsD()) //if it has not been deleted...
828  {
829  if(!ProcessSelected || (*vi).IsS()) //if this vertex has been selected, put it in the histograms
830  {
831  float v = ComputeLightness((*vi).C())+0.5; //compute and round lightness value
832  Hl.Add(v); Hr.Add((float)(*vi).C()[0]); Hg.Add((float)(*vi).C()[1]); Hb.Add((float)(*vi).C()[2]);
833  }
834  }
835  }
836 
837  //for each histogram, compute the cumulative distribution function, and build a lookup table
838  int cdf_l[256], cdf_r[256], cdf_g[256], cdf_b[256];
839  cdf_l[0] = Hl.BinCount(0); cdf_r[0] = Hr.BinCount(0); cdf_g[0] = Hg.BinCount(0); cdf_b[0] = Hb.BinCount(0);
840  for(int i=1; i<256; i++){
841  cdf_l[i] = Hl.BinCount(float(i)) + cdf_l[i-1];
842  cdf_r[i] = Hr.BinCount(float(i)) + cdf_r[i-1];
843  cdf_g[i] = Hg.BinCount(float(i)) + cdf_g[i-1];
844  cdf_b[i] = Hb.BinCount(float(i)) + cdf_b[i-1];
845  }
846 
847  //this loop aaplies the transformation to colors
848  for(vi=m.vert.begin();vi!=m.vert.end();++vi) //scan all the vertex...
849  {
850  if(!(*vi).IsD()) //if it has not been deleted...
851  {
852  if(!ProcessSelected || (*vi).IsS()) //if this vertex has been selected, do transormation
853  {
854  (*vi).C()=ColorEqualize((*vi).C(), cdf_l, cdf_r, cdf_g, cdf_b, rgbMask);
855  ++counter;
856  }
857  }
858  }
859  return counter;
860 }
861 
862 //Applies equalization to the components of the color according to rgbmask
863 static Color4b ColorEqualize(Color4b c, int cdf_l[256], int cdf_r[256], int cdf_g[256], int cdf_b[256], unsigned int rgbMask)
864 {
865  unsigned char r = c[0], g = c[1], b = c[2];
866  if(rgbMask == NO_CHANNELS) //in this case, equalization is done on lightness
867  {
868  int v = ValueEqualize(cdf_l[(int)(ComputeLightness(c)+0.5f)], cdf_l[0], cdf_l[255]);
869  return Color4b(v, v, v, 255); //return the equalized gray color
870  }
871  if(rgbMask & RED_CHANNEL) r = ValueEqualize(cdf_r[c[0]], cdf_r[0], cdf_r[255]); //Equalizes red
872  if(rgbMask & GREEN_CHANNEL) g = ValueEqualize(cdf_g[c[1]], cdf_g[0], cdf_g[255]); //Equalizes green
873  if(rgbMask & BLUE_CHANNEL) b = ValueEqualize(cdf_b[c[2]], cdf_b[0], cdf_b[255]); //Equalizes blue
874  return Color4b(r, g, b, 255); //return the equalized color
875 }
876 
877 //Compute the equalized value
878 static int ValueEqualize(int cdfValue, int cdfMin, int cdfMax)
879 {
880  return int(float((cdfValue - cdfMin)/float(cdfMax - cdfMin)) * 255.0f);
881 }
882 
888 static int PerVertexWhiteBalance(MeshType &m, Color4b userColor, const bool ProcessSelected=false)
889 {
890  RequirePerVertexColor(m);
891 
892  Color4b unbalancedWhite= userColor;
893  int counter=0;
894  VertexIterator vi;
895 
896  //in this loop the transformation is applied to the mesh
897  for(vi=m.vert.begin();vi!=m.vert.end();++vi) //scan all the vertex...
898  {
899  if(!(*vi).IsD()) //if it has not been deleted...
900  {
901  if(!ProcessSelected || (*vi).IsS()) //if this vertex has been selected, do transormation
902  {
903  (*vi).C()=ColorWhiteBalance((*vi).C(),unbalancedWhite);
904  ++counter;
905  }
906  }
907  }
908  return counter;
909 }
910 
911 //Balnce the white of the color, applying a correction factor based on the unbalancedWhite color.
912 static Color4b ColorWhiteBalance(Color4b c, Color4b unbalancedWhite)
913 {
914  //sanity check to avoid division by zero...
915  if(unbalancedWhite[0]==0) unbalancedWhite[0]=1;
916  if(unbalancedWhite[1]==0) unbalancedWhite[1]=1;
917  if(unbalancedWhite[2]==0) unbalancedWhite[2]=1;
918 
919  return Color4b(
920  math::Clamp<int>((int)(c[0]*(255.0f/unbalancedWhite[0])), 0, 255),
921  math::Clamp<int>((int)(c[1]*(255.0f/unbalancedWhite[1])), 0, 255),
922  math::Clamp<int>((int)(c[2]*(255.0f/unbalancedWhite[2])), 0, 255),
923  255);
924 }
925 
926 };
927 
928 }// end namespace
929 }// end namespace
930 #endif