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 <limits>
28 #include <math.h>
29 #include <time.h>
30 #include <vcg/space/color4.h>
31 #include <vcg/math/histogram.h>
32 #include <vcg/math/perlin_noise.h>
33 #include <vcg/math/random_generator.h>
34 #include <vcg/complex/algorithms/clean.h>
35 #include <vcg/complex/algorithms/stat.h>
36
37 namespace vcg {
38 namespace tri {
39
40 /*!
41 \ingroup trimesh
42
43 \headerfile color.h vcg/complex/algorithms/update/color.h
44
45 \brief Generation and processing of per-vertex and per-face colors according to various strategy.
46
47 This class is used to compute per face or per vertex color with respect to a number of algorithms.
48 There is a wide range of algorithms for processing vertex color in a \i photoshop-like mode (changing for example contrast, white balance, gamma)
49 Basic Tools for mapping quality into a color according to standard color ramps are here.
50 */
51
52 template <class MeshType>
53 class UpdateColor
54 {
55 public:
56 typedef typename MeshType::VertexType VertexType;
57 typedef typename MeshType::VertexPointer VertexPointer;
58 typedef typename MeshType::VertexIterator VertexIterator;
59 typedef typename MeshType::FaceType FaceType;
60 typedef typename MeshType::FacePointer FacePointer;
61 typedef typename MeshType::FaceIterator FaceIterator;
62 typedef typename MeshType::EdgeIterator EdgeIterator;
63
64 typedef typename MeshType::ScalarType ScalarType;
65 typedef typename MeshType::CoordType CoordType;
66
67 class ColorAvgInfo
68 {
69 public:
70 unsigned int r;
71 unsigned int g;
72 unsigned int b;
73 unsigned int a;
74 int cnt;
75 };
76
77 /*! \brief This function colores all (or the selected) the vertices of a mesh.
78 */
79 static int PerVertexConstant(MeshType &m, Color4b vs=Color4b::White,bool selected=false)
80 {
81 RequirePerVertexColor(m);
82
83 int cnt=0;
84 for(VertexIterator vi=m.vert.begin();vi!=m.vert.end();++vi)
85 if(!(*vi).IsD()){
86 if(!selected || (*vi).IsS())
87 {
88 (*vi).C() = vs;
89 ++cnt;
90 }
91 }
92 return cnt;
93 }
94
95 /*! \brief This function colores all (or the selected) faces of a mesh.
96 */
97 static int PerFaceConstant(MeshType &m, Color4b vs=Color4b::White,bool selected=false)
98 {
99 RequirePerFaceColor(m);
100 int cnt=0;
101 for(FaceIterator fi=m.face.begin();fi!=m.face.end();++fi)
102 if(!(*fi).IsD()){
103 if(!selected || (*fi).IsS())
104 {
105 (*fi).C() = vs;
106 ++cnt;
107 }
108 }
109 return cnt;
110 }
111
112 /** \brief Transfer face color onto vertex color
113
114 Plain average of the color of the faces incident on a given vertex.
115 No adjacency required.
116 */
PerVertexFromFace(MeshType & m)117 static void PerVertexFromFace( MeshType &m)
118 {
119 RequirePerFaceColor(m);
120 RequirePerVertexColor(m);
121
122 ColorAvgInfo csi;
123 csi.r=0; csi.g=0; csi.b=0; csi.cnt=0;
124 SimpleTempData<typename MeshType::VertContainer, ColorAvgInfo> TD(m.vert,csi);
125
126 FaceIterator fi;
127 for(fi=m.face.begin();fi!=m.face.end();++fi)
128 if(!(*fi).IsD())
129 for(int j=0;j<3;++j)
130 {
131 TD[(*fi).V(j)].r+=(*fi).C()[0];
132 TD[(*fi).V(j)].g+=(*fi).C()[1];
133 TD[(*fi).V(j)].b+=(*fi).C()[2];
134 TD[(*fi).V(j)].a+=(*fi).C()[3];
135 ++TD[(*fi).V(j)].cnt;
136 }
137
138 VertexIterator vi;
139 for(vi=m.vert.begin();vi!=m.vert.end();++vi)
140 if(!(*vi).IsD() && TD[*vi].cnt>0 )
141 {
142 (*vi).C()[0] = TD[*vi].r / TD[*vi].cnt;
143 (*vi).C()[1] = TD[*vi].g / TD[*vi].cnt;
144 (*vi).C()[2] = TD[*vi].b / TD[*vi].cnt;
145 (*vi).C()[3] = TD[*vi].a / TD[*vi].cnt;
146 }
147 }
148
149 /*! \brief Transfer vertex color onto face color
150 Plain average of the color of the vertexes on a given face.
151 */
PerFaceFromVertex(MeshType & m)152 static void PerFaceFromVertex( MeshType &m)
153 {
154 RequirePerFaceColor(m);
155 RequirePerVertexColor(m);
156
157 FaceIterator fi;
158 for(fi=m.face.begin();fi!=m.face.end();++fi) if(!(*fi).IsD())
159 {
160 Color4f avg = (Color4f::Construct((*fi).V(0)->C()) +
161 Color4f::Construct((*fi).V(1)->C()) +
162 Color4f::Construct((*fi).V(2)->C()) )/ 3.0;
163 (*fi).C().Import(avg);
164 }
165 }
166
167 /*! \brief This function colores all the faces of a mesh with a hue color shade dependent on the quality.
168
169 If no range of quality is passed it is automatically computed.
170 */
171 static void PerVertexQualityRamp(MeshType &m, float minq=0, float maxq=0)
172 {
173 RequirePerVertexQuality(m);
174 RequirePerVertexColor(m);
175
176 if(minq==maxq)
177 {
178 std::pair<float,float> minmax = Stat<MeshType>::ComputePerVertexQualityMinMax(m);
179 minq=minmax.first;
180 maxq=minmax.second;
181 }
182 for(VertexIterator vi=m.vert.begin();vi!=m.vert.end();++vi)
183 if(!(*vi).IsD())
184 (*vi).C().SetColorRamp(minq,maxq,(*vi).Q());
185 }
186
187
188 /*! \brief This function colores all the faces of a mesh with a hue color shade dependent on the quality.
189
190 If no range of quality is passed it is automatically computed.
191 */
192 static void PerVertexQualityRampParula(MeshType &m, float minq=0, float maxq=0)
193 {
194 RequirePerVertexQuality(m);
195 RequirePerVertexColor(m);
196
197 if(minq==maxq)
198 {
199 std::pair<float,float> minmax = Stat<MeshType>::ComputePerVertexQualityMinMax(m);
200 minq=minmax.first;
201 maxq=minmax.second;
202 }
203 for(VertexIterator vi=m.vert.begin();vi!=m.vert.end();++vi)
204 if(!(*vi).IsD())
205 (*vi).C().SetColorRampParula(minq,maxq,(*vi).Q());
206 }
207
208 /*! \brief This function colores all the faces of a mesh with a hue color shade dependent on the quality.
209
210 If no range of quality is passed it is automatically computed.
211 */
212 static void PerFaceQualityRamp(MeshType &m, float minq=0, float maxq=0, bool selected=false)
213 {
214 RequirePerFaceColor(m);
215 RequirePerFaceQuality(m);
216
217 if(minq==maxq)
218 {
219 std::pair<float,float> minmax = Stat<MeshType>::ComputePerFaceQualityMinMax(m);
220 minq=minmax.first;
221 maxq=minmax.second;
222 }
223 for(FaceIterator fi=m.face.begin();fi!=m.face.end();++fi) if(!(*fi).IsD())
224 if(!selected || (*fi).IsS())
225 (*fi).C().SetColorRamp(minq,maxq,(*fi).Q());
226 }
227
228 /*! \brief This function colores all the edges of a mesh with a hue color shade dependent on the quality.
229
230 If no range of quality is passed it is automatically computed.
231 */
232 static void PerEdgeQualityRamp(MeshType &m, float minq=0, float maxq=0, bool selected=false)
233 {
234 RequirePerEdgeColor(m);
235 RequirePerEdgeQuality(m);
236
237 if(minq==maxq)
238 {
239 std::pair<float,float> minmax = Stat<MeshType>::ComputePerEdgeQualityMinMax(m);
240 minq=minmax.first;
241 maxq=minmax.second;
242 }
243 for(EdgeIterator ei=m.edge.begin();ei!=m.edge.end();++ei) if(!(*ei).IsD())
244 if(!selected || (*ei).IsS())
245 (*ei).C().SetColorRamp(minq,maxq,(*ei).Q());
246 }
247
248 /*! \brief This function colores all the vertices of a mesh with a gray shade dependent on the quality.
249
250 If no range of quality is passed it is automatically computed.
251 */
PerVertexQualityGray(MeshType & m,float minq,float maxq)252 static void PerVertexQualityGray(MeshType &m, float minq, float maxq)
253 {
254 RequirePerVertexColor(m);
255 RequirePerVertexQuality(m);
256 if(minq==maxq)
257 {
258 std::pair<float,float> minmax = Stat<MeshType>::ComputePerVertexQualityMinMax(m);
259 minq=minmax.first;
260 maxq=minmax.second;
261 }
262 for(VertexIterator vi=m.vert.begin();vi!=m.vert.end();++vi)
263 if(!(*vi).IsD())
264 (*vi).C().SetGrayShade( ((*vi).Q()-minq)/(maxq-minq));
265 }
266
267 /*! \brief This function colores all the faces of a mesh with a gray shade dependent on the quality.
268
269 If no range of quality is passed it is automatically computed.
270 */
271 static void PerFaceQualityGray(MeshType &m, float minq=0, float maxq=0)
272 {
273 RequirePerFaceColor(m);
274 RequirePerFaceQuality(m);
275
276 if(minq==maxq)
277 {
278 std::pair<float,float> minmax = Stat<MeshType>::ComputePerFaceQualityMinMax(m);
279 minq=minmax.first;
280 maxq=minmax.second;
281 }
282 for(FaceIterator fi=m.face.begin();fi!=m.face.end();++fi) if(!(*fi).IsD())
283 (*fi).C().SetGrayShade( ((*fi).Q()-minq)/(maxq-minq));
284 }
285
286 /** \brief Color the vertexes of the mesh that are on the border
287
288 It uses the information in the Vertex flags, and not necessarily any topology.
289 So it just require that you have correctly computed the flags; one way could be the following one:
290 \code
291 vcg::tri::UpdateTopology<Mesh>::FaceFace(m.cm);
292 vcg::tri::UpdateFlags<Mesh>::FaceBorderFromFF(m.cm);
293 vcg::tri::UpdateFlags<Mesh>::VertexBorderFromFaceBorder (m.cm);
294 vcg::tri::UpdateColor<Mesh>::PerVertexBorderFlag(m.cm);
295 \endcode
296 */
297 static void PerVertexBorderFlag( MeshType &m, Color4b BorderColor=Color4b::Blue, Color4b InternalColor=Color4b::White, Color4b MixColor=Color4b::Cyan)
298 {
299 RequirePerVertexColor(m);
300
301 Color4b BaseColor = Color4b::Green;
302
303 PerVertexConstant(m,BaseColor);
304 for(FaceIterator fi=m.face.begin();fi!=m.face.end();++fi) if(!(*fi).IsD())
305 for(int j=0;j<3;++j)
306 {
307 if((*fi).IsB(j)){
308 if( (*fi).V(j)->C() == BaseColor) (*fi).V(j)->C() = BorderColor;
309 if( (*fi).V(j)->C() == InternalColor) (*fi).V(j)->C() = MixColor;
310 if( (*fi).V1(j)->C() == BaseColor) (*fi).V1(j)->C() = BorderColor;
311 if( (*fi).V1(j)->C() == InternalColor) (*fi).V1(j)->C() = MixColor;
312 } else
313 {
314 if( (*fi).V(j)->C() == BaseColor) (*fi).V(j)->C() = InternalColor;
315 if( (*fi).V(j)->C() == BorderColor) (*fi).V(j)->C() = MixColor;
316 if( (*fi).V1(j)->C() == BaseColor) (*fi).V1(j)->C() = InternalColor;
317 if( (*fi).V1(j)->C() == BorderColor) (*fi).V1(j)->C() = MixColor;
318 }
319 }
320
321 }
322
323 /*! \brief This function colores the faces of connected components of a mesh randomly.
324
325 It require FaceFace Adjacency becouse it relies on the output of the ConnecteComponents();
326 */
PerFaceRandomConnectedComponent(MeshType & m)327 static void PerFaceRandomConnectedComponent( MeshType &m)
328 {
329 RequirePerFaceColor(m);
330 RequireFFAdjacency(m);
331
332 std::vector< std::pair<int, typename MeshType::FacePointer> > CCV;
333 int ScatterSize= std::min (100,tri::Clean<MeshType>::ConnectedComponents(m, CCV)); // number of random color to be used. Never use too many.
334
335 ConnectedComponentIterator<MeshType> ci;
336 for(unsigned int i=0;i<CCV.size();++i)
337 {
338 Color4b BaseColor = Color4b::Scatter(ScatterSize, i%ScatterSize,.4f,.7f);
339 std::vector<typename MeshType::FacePointer> FPV;
340 for(ci.start(m,CCV[i].second);!ci.completed();++ci)
341 (*ci)->C()=BaseColor;
342 }
343 }
344
345 /*! \brief This function colores the face of a mesh randomly.
346
347 Note: The faux bit is used to color polygonal faces uniformly
348 */
PerFaceRandom(MeshType & m)349 static void PerFaceRandom(MeshType &m)
350 {
351 RequirePerFaceColor(m);
352 FaceIterator fi;
353 Color4b BaseColor = Color4b::Black;
354 PerFaceConstant(m,BaseColor);
355 int id_num=0;
356 for(fi=m.face.begin();fi!=m.face.end();++fi)
357 if(!(*fi).IsD())
358 {
359 id_num++;
360 if((*fi).C() == BaseColor) (*fi).C() = Color4b::Scatter(50, id_num%50,.4f,.7f);
361 for(int j=0;j<3;++j)
362 if((*fi).IsF(j))
363 {
364 assert(!IsBorder((*fi),j));
365 (*fi).FFp(j)->C()= (*fi).C();
366 }
367 }
368 }
369
370 /*! \brief Perlin Noise.
371
372 Simple Perlin noise. To make things weirder each color band can have its own offset and frequency.
373 Period is expressed in absolute terms.
374 So as period it is meaningful could be to use something in the range of 1/10 of the bbox diag.
375 */
376 static void PerVertexPerlinNoise(MeshType& m, CoordType period, CoordType offset = CoordType(0, 0, 0), bool onSelected = false)
377 {
378 RequirePerVertexColor(m);
379
380 CoordType p[3];
381
382 for(VertexIterator vi = m.vert.begin(); vi!=m.vert.end(); ++vi)
383 if(!(*vi).IsD())
384 if ((!onSelected) || ((*vi).IsS()))
385 {
386 // perlin noise is defined in 022
387 p[0] = (vi->P()/period[0])+offset;
388 p[1] = (vi->P()/period[1])+offset;
389 p[2] = (vi->P()/period[2])+offset;
390 (*vi).C() = Color4b( int(127+128.0*math::Perlin::Noise(p[0][0],p[0][1],p[0][2])),
391 int(127+128.0*math::Perlin::Noise(p[1][0],p[1][1],p[1][2])),
392 int(127+128.0*math::Perlin::Noise(p[2][0],p[2][1],p[2][2])),
393 255 );
394 }
395
396 }
397
398
399 /*! \brief Perlin Color mixing.
400
401 Simple Perlin color mixing. Color 1 and 2 are mixed according the perlin noise function, with period and offset.
402 */
403 static void PerVertexPerlinColoring(MeshType& m, ScalarType period, CoordType offset = CoordType(0, 0, 0), Color4b color1 = Color4b::Black, Color4b color2 = Color4b::White, bool onSelected = false)
404 {
405 RequirePerVertexColor(m);
406
407 CoordType p;
408
409 for (VertexIterator vi = m.vert.begin(); vi != m.vert.end(); ++vi)
410 if (!(*vi).IsD())
411 if ((!onSelected) || ((*vi).IsS()))
412 {
413 // perlin noise is defined in 022
414 p = (vi->P() / period) + offset;
415 double factor = (math::Perlin::Noise(p[0], p[1], p[2]) + 1.0) / 2.0;
416
417 int rr = (color1[0] * factor) + (color2[0] * (1.0 - factor));
418 int gg = (color1[1] * factor) + (color2[1] * (1.0 - factor));
419 int bb = (color1[2] * factor) + (color2[2] * (1.0 - factor));
420 int aa = (color1[3] * factor) + (color2[3] * (1.0 - factor));
421
422 (*vi).C() = Color4b(rr, gg, bb, aa);
423 }
424 }
425
426 /*! \brief Simple Noise adding function.
427 It simply add signed noise to the color of the mesh. The noise has uniform distribution and the amplitude is +/-2^(noisebits-1).
428 */
429 static void PerVertexAddNoise(MeshType& m, int noiseBits, bool onSelected=false)
430 {
431 RequirePerVertexColor(m);
432
433 if(noiseBits>8) noiseBits = 8;
434 if(noiseBits<1) return;
435
436 math::SubtractiveRingRNG randomGen = math::SubtractiveRingRNG(time(NULL));
437 for(VertexIterator vi = m.vert.begin(); vi!=m.vert.end(); ++vi)
438 if(!(*vi).IsD())
439 if ((!onSelected) || ((*vi).IsS()))
440 {
441 (*vi).C()[0] = math::Clamp<int>((*vi).C()[0] + randomGen.generate(int(2*pow(2.0f,noiseBits))) - int(pow(2.0f,noiseBits)),0,255);
442 (*vi).C()[1] = math::Clamp<int>((*vi).C()[1] + randomGen.generate(int(2*pow(2.0f,noiseBits))) - int(pow(2.0f,noiseBits)),0,255);
443 (*vi).C()[2] = math::Clamp<int>((*vi).C()[2] + randomGen.generate(int(2*pow(2.0f,noiseBits))) - int(pow(2.0f,noiseBits)),0,255);
444 }
445
446 }
447
448
449 /*! \brief Reduces vertex color the mesh to two colors according to a threshold.
450 */
451 static int PerVertexThresholding(MeshType &m, float threshold, Color4b c1 = Color4<unsigned char>::Black, Color4b c2 = Color4<unsigned char>::White, const bool ProcessSelected=false)
452 {
453 RequirePerVertexColor(m);
454
455 int counter=0;
456 VertexIterator vi;
457 for(vi=m.vert.begin();vi!=m.vert.end();++vi) //scan all the vertex...
458 {
459 if(!(*vi).IsD()) //if it has not been deleted...
460 {
461 if(!ProcessSelected || (*vi).IsS()) //if this vertex has been selected, do transormation
462 {
463 float value = ComputeLightness((*vi).C());
464
465 if(value<=threshold) (*vi).C() = c1;
466 else (*vi).C() = c2;
467 ++counter;
468 }
469 }
470 }
471 return counter;
472 }
473
474 // Computes the lightness value for a specified color. lightness = 0.5*(Max(R,G,B)+Min(R,G,B))
ComputeLightness(Color4b c)475 static float ComputeLightness(Color4b c)
476 {
477 float min_rgb = (float)math::Min(c[0],c[1],c[2]);
478 float max_rgb = (float)math::Max(c[0],c[1],c[2]);
479 return (max_rgb + min_rgb)/2;
480 }
481
482 /*! \brief Apply the brightness filter, with the given amount, to the mesh.
483 */
484 static int PerVertexBrightness(MeshType &m, float amount, const bool ProcessSelected=false)
485 {
486 RequirePerVertexColor(m);
487
488 int counter=0;
489 VertexIterator vi;
490 for(vi=m.vert.begin();vi!=m.vert.end();++vi) //scan all the vertex...
491 {
492 if(!(*vi).IsD()) //if it has not been deleted...
493 {
494 if(!ProcessSelected || (*vi).IsS()) //if this vertex has been selected, do transormation
495 {
496 (*vi).C() = Color4b(
497 math::Clamp(int((*vi).C()[0]+amount),0,255),
498 math::Clamp(int((*vi).C()[1]+amount),0,255),
499 math::Clamp(int((*vi).C()[2]+amount),0,255),
500 255);
501 ++counter;
502 }
503 }
504 }
505 return counter;
506 }
507
508 /*! \brief Apply Contrast filter to the mesh with the given contrast factor.
509 */
510 static int PerVertexContrast(MeshType &m, float factor, const bool ProcessSelected=false)
511 {
512 RequirePerVertexColor(m);
513
514 int counter=0;
515 VertexIterator vi;
516 for(vi=m.vert.begin();vi!=m.vert.end();++vi) //scan all the vertex...
517 {
518 if(!(*vi).IsD()) //if it has not been deleted...
519 {
520 if(!ProcessSelected || (*vi).IsS()) //if this vertex has been selected, do transormation
521 {
522 (*vi).C() = ColorMul((*vi).C(),factor);
523 ++counter;
524 }
525 }
526 }
527 return counter;
528 }
529
530 //Performs contrast operations on color, i.e expands or compress the histogram around
531 //the midpoint value. NewValue = (OldValue - 128) ◊ factor + 128
ColorMul(Color4b c,float factor)532 static Color4b ColorMul(Color4b c, float factor)
533 {
534 return Color4b( ValueMul(c[0], factor), ValueMul(c[1], factor), ValueMul(c[2], factor), 1);
535 }
536
ValueMul(int value,float factor)537 static int ValueMul(int value, float factor)
538 {
539 return math::Clamp<int>((int)((value - 128)*factor + 128), 0, 255);
540 }
541
542 /*! \brief Apply Brightness and Contrast filter to the mesh, with the given contrast factor and brightness amount.
543
544 Performs contrast and brightness operations on color, i.e NewValue = (OldValue - 128) * contrast + 128 + amount
545 The result is clamped just one time after all computations; this get a more accurate result.
546
547 The formula used here is the one of GIMP.
548
549 */
550 static int PerVertexBrightnessContrast(MeshType &m, float brightness, float contrast, const bool ProcessSelected=false)
551 {
552 RequirePerVertexColor(m);
553
554 int counter=0;
555 VertexIterator vi;
556 for(vi=m.vert.begin();vi!=m.vert.end();++vi) //scan all the vertex...
557 {
558 if(!(*vi).IsD()) //if it has not been deleted...
559 {
560 if(!ProcessSelected || (*vi).IsS()) //if this vertex has been selected, do transormation
561 {
562 (*vi).C() = ColorBrightnessContrast((*vi).C(),brightness,contrast);
563 ++counter;
564 }
565 }
566 }
567 return counter;
568 }
569
ColorBrightnessContrast(Color4b c,float brightness,float contrast)570 static Color4b ColorBrightnessContrast(Color4b c, float brightness, float contrast)
571 {
572 return Color4b( ValueBrightnessContrast(c[0], brightness, contrast),
573 ValueBrightnessContrast(c[1], brightness, contrast),
574 ValueBrightnessContrast(c[2], brightness, contrast), 1 );
575 }
576
ValueBrightnessContrast(unsigned char ivalue,float brightness,float contrast)577 static int ValueBrightnessContrast(unsigned char ivalue, float brightness, float contrast)
578 {
579 float value = float(ivalue)/255.0f;
580 if (brightness < 0.0) value = value * ( 1.0 + brightness);
581 else value = value + ((1.0 - value) * brightness);
582 value = (value - 0.5) * (tan ((contrast + 1) * M_PI/4) ) + 0.5;
583 return math::Clamp<int>(255.0*value, 0, 255);
584 }
585
586 /*! \brief Invert the colors of the mesh.
587
588 \return the number of changed vertexes (the selected ones)
589 */
590 static int PerVertexInvert(MeshType &m, const bool ProcessSelected=false)
591 {
592 RequirePerVertexColor(m);
593
594 int counter=0;
595 for(VertexIterator vi=m.vert.begin(); vi!=m.vert.end(); ++vi) //scan all the vertex...
596 {
597 if(!(*vi).IsD()) //if it has not been deleted...
598 {
599 if(!ProcessSelected || (*vi).IsS()) //if this vertex has been selected, do transormation
600 {
601 Color4b &c=(*vi).C();
602 c=Color4b( 255-c[0],255-c[1],255-c[2], 1);
603 ++counter;
604 }
605 }
606 }
607 return counter;
608 }
609
610 /*! \brief Apply the gamma correction filter, with the given gamma exponet, to the mesh.
611 \return the number of changed vertexes (the selected ones)
612 */
613 static int PerVertexGamma(MeshType &m, float gamma, const bool ProcessSelected=false)
614 {
615 RequirePerVertexColor(m);
616
617 int counter=0;
618
619 VertexIterator vi;
620 for(vi=m.vert.begin();vi!=m.vert.end();++vi) //scan all the vertex...
621 {
622 if(!(*vi).IsD()) //if it has not been deleted...
623 {
624 if(!ProcessSelected || (*vi).IsS()) //if this vertex has been selected, do transormation
625 {
626 (*vi).C() = ColorPow((*vi).C(), 1/gamma);
627 ++counter;
628 }
629 }
630 }
631 return counter;
632 }
633
634 //computes the standard gamma transformation on a given color, according to NewVal = OldVal^(1/gamma)
ColorPow(Color4b c,float exponent)635 static Color4b ColorPow(Color4b c, float exponent)
636 {
637 return vcg::Color4b(
638 math::Clamp((int)( ValuePow(float(c[0])/255, exponent)*255), 0, 255),
639 math::Clamp((int)( ValuePow(float(c[1])/255, exponent)*255), 0, 255),
640 math::Clamp((int)( ValuePow(float(c[2])/255, exponent)*255), 0, 255),
641 255);
642 }
643
ValuePow(float value,float exponent)644 static float ValuePow(float value, float exponent)
645 {
646 return powf(value, exponent);
647 }
648
649 //useful bit masks for RGB channels, used for Levels filter.
650 enum rgbChMask {ALL_CHANNELS = 7, RED_CHANNEL = 4, GREEN_CHANNEL = 2, BLUE_CHANNEL = 1, NO_CHANNELS = 0 };
651
652 /*! \brief Adjusts color levels of the mesh
653
654 \return the number of changed vertexes (the selected ones)
655
656 Adjusts color levels of the mesh. Filter can be applied to all RGB channels or to each channel separately.
657 in_min, gamma and in_max are respectively the black point, the gray point and the white point.
658 out_min and out_max are the output level for black and white respectively.
659 */
660 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)
661 {
662 RequirePerVertexColor(m);
663
664 int counter=0;
665 VertexIterator vi;
666 for(vi=m.vert.begin();vi!=m.vert.end();++vi) //scan all the vertex...
667 {
668 if(!(*vi).IsD()) //if it has not been deleted...
669 {
670 if(!ProcessSelected || (*vi).IsS()) //if this vertex has been selected, do transormation
671 {
672 (*vi).C() = ColorLevels((*vi).C(), gamma, in_min, in_max, out_min, out_max, rgbMask);
673 ++counter;
674 }
675 }
676 }
677 return counter;
678 }
679
680 //Performs levels transformation on each channel set to 1 in the rgbMask.
ColorLevels(Color4b c,float gamma,float in_min,float in_max,float out_min,float out_max,unsigned char rgbMask)681 static Color4b ColorLevels(Color4b c, float gamma, float in_min, float in_max, float out_min, float out_max, unsigned char rgbMask)
682 {
683 unsigned char r = c[0], g = c[1], b = c[2];
684 if(rgbMask & RED_CHANNEL) r = ValueLevels(c[0], gamma, in_min, in_max, out_min, out_max);
685 if(rgbMask & GREEN_CHANNEL) g = ValueLevels(c[1], gamma, in_min, in_max, out_min, out_max);
686 if(rgbMask & BLUE_CHANNEL) b = ValueLevels(c[2], gamma, in_min, in_max, out_min, out_max);
687 return Color4b(r, g, b, 255);
688 }
689
690 //Transform on levels
ValueLevels(int value,float gamma,float in_min,float in_max,float out_min,float out_max)691 static int ValueLevels(int value, float gamma, float in_min, float in_max, float out_min, float out_max)
692 {
693 float fvalue = value/255.0f;
694 // normalize
695 fvalue = math::Clamp<float>(fvalue - in_min, 0.0f, 1.0f) / math::Clamp<float>(in_max - in_min, 1.0f/255.0f, 1.0f);
696 // transform gamma
697 fvalue = powf(fvalue,1/gamma);
698 // rescale range
699 fvalue = fvalue * (out_max - out_min) + out_min;
700 //back in interval [0,255] and clamp
701 return math::Clamp<int>((int)(fvalue * 255), 0, 255);
702 }
703
704 /*! \brief Colorize the mesh toward a given color.
705 \return the number of changed vertexes (the selected ones)
706
707 Colors the mesh. Color is blended to the mesh with the given intensity (0..1 ranged).
708 */
709 static int PerVertexColourisation(MeshType &m, Color4b c, float intensity, const bool ProcessSelected=false)
710 {
711 RequirePerVertexColor(m);
712
713 int counter=0;
714 VertexIterator vi;
715 for(vi=m.vert.begin();vi!=m.vert.end();++vi)
716 {
717 if(!(*vi).IsD()) //if it has not been deleted...
718 {
719 if(!ProcessSelected || (*vi).IsS()) //if this vertex has been selected, do transormation
720 {
721 (*vi).C() = ColorApplyDiff((*vi).C(), c, intensity);
722 ++counter;
723 }
724 }
725 }
726 return counter;
727 }
728
729 // Perform colourisation operation.
730 // For each channel C:
731 // newC = origC + intensity * (newC - origC)
ColorApplyDiff(Color4b old_color,Color4b new_color,float intensity)732 static Color4b ColorApplyDiff(Color4b old_color, Color4b new_color, float intensity)
733 {
734 return Color4b( ValueApplyDiff(old_color[0], new_color[0], intensity),
735 ValueApplyDiff(old_color[1], new_color[1], intensity),
736 ValueApplyDiff(old_color[2], new_color[2], intensity), 255);
737 }
738
ValueApplyDiff(int old_value,int new_value,float intensity)739 static int ValueApplyDiff(int old_value, int new_value, float intensity)
740 {
741 return math::Clamp<int>((int)(old_value + intensity * (new_value - old_value)), 0, 255);
742 }
743
744 //An useful ENUM to hold all desaturation methods.
745 enum DesaturationMethods {M_LIGHTNESS = 0, M_LUMINOSITY = 1, M_AVERAGE = 2};
746
747 /*! \brief Desaturates the mesh according the a chosen desaturation method
748
749 \return the number of changed vertexes (the selected ones)
750
751 There are three possibilities
752 - \c M_LIGHTNESS where lightness = 0.5*(Max(R,G,B)+Min(R,G,B))
753 - \c M_LUMINOSITY where luminosity = 0.21*R+0.71*G+0.7*B
754 - \c M_AVERAGE Plain Average
755 */
756 static int PerVertexDesaturation(MeshType &m, int method, const bool ProcessSelected=false)
757 {
758 RequirePerVertexColor(m);
759
760 int counter=0;
761 VertexIterator vi;
762 for(vi=m.vert.begin();vi!=m.vert.end();++vi) //scan all the vertex...
763 {
764 if(!(*vi).IsD()) //if it has not been deleted...
765 {
766 if(!ProcessSelected || (*vi).IsS()) //if this vertex has been selected, do transormation
767 {
768 (*vi).C() = ColorDesaturate((*vi).C(), method);
769 ++counter;
770 }
771 }
772 }
773 return counter;
774 }
775
776 //Desature the color. Ausiliary functions to calculate lightness/luminosity/average.
ColorDesaturate(Color4b c,int method)777 static Color4b ColorDesaturate(Color4b c, int method)
778 {
779 switch(method){
780 case M_LIGHTNESS:{
781 int val = (int)ComputeLightness(c);
782 return Color4b( val, val, val, 255);
783 }
784 case M_AVERAGE:{
785 int val = (int)ComputeAvgLightness(c);
786 return Color4b( val, val, val, 255);
787 }
788 case M_LUMINOSITY:{
789 int val = (int)ComputeLuminosity(c);
790 return Color4b( val, val, val, 255);
791 }
792 default: assert(0);
793 }
794 }
795
796 //ausiliary function to compute average lightness. value = (R+G+B)/3
ComputeAvgLightness(Color4b c)797 static float ComputeAvgLightness(Color4b c)
798 {
799 return float(c[0]+c[1]+c[2])/3.0f;
800 }
801
802 //ausiliary function to compute luminosity. value = 0.21*R+0.71*G+0.7*B
ComputeLuminosity(Color4b c)803 static float ComputeLuminosity(Color4b c)
804 {
805 return float(0.2126f*c[0]+0.7152f*c[1]+0.0722f*c[2]);
806 }
807
808 /*! \brief Histogram Color Equalization.
809 \return the number of changed vertexes (the selected ones)
810
811 Equalize the histogram of colors. It can equalize any combination of rgb channels or it can work on lightness.
812 */
813 static int PerVertexEqualize(MeshType &m, unsigned int rgbMask, const bool ProcessSelected=false)
814 {
815 RequirePerVertexColor(m);
816
817 //declares , resets and set up 4 histograms, for Red, Green, Blue and Lightness
818 Histogramf Hl, Hr, Hg, Hb;
819 Hl.Clear(); Hr.Clear(); Hg.Clear(); Hb.Clear();
820 Hl.SetRange(0, 255, 255); Hr.SetRange(0, 255, 255); Hg.SetRange(0, 255, 255); Hb.SetRange(0, 255, 255);
821
822 int counter=0;
823 VertexIterator vi;
824
825 //Scan the mesh to build the histograms
826 for(vi=m.vert.begin();vi!=m.vert.end();++vi) //scan all the vertex...
827 {
828 if(!(*vi).IsD()) //if it has not been deleted...
829 {
830 if(!ProcessSelected || (*vi).IsS()) //if this vertex has been selected, put it in the histograms
831 {
832 float v = ComputeLightness((*vi).C())+0.5; //compute and round lightness value
833 Hl.Add(v); Hr.Add((float)(*vi).C()[0]); Hg.Add((float)(*vi).C()[1]); Hb.Add((float)(*vi).C()[2]);
834 }
835 }
836 }
837
838 //for each histogram, compute the cumulative distribution function, and build a lookup table
839 int cdf_l[256], cdf_r[256], cdf_g[256], cdf_b[256];
840 cdf_l[0] = Hl.BinCount(0); cdf_r[0] = Hr.BinCount(0); cdf_g[0] = Hg.BinCount(0); cdf_b[0] = Hb.BinCount(0);
841 for(int i=1; i<256; i++){
842 cdf_l[i] = Hl.BinCount(float(i)) + cdf_l[i-1];
843 cdf_r[i] = Hr.BinCount(float(i)) + cdf_r[i-1];
844 cdf_g[i] = Hg.BinCount(float(i)) + cdf_g[i-1];
845 cdf_b[i] = Hb.BinCount(float(i)) + cdf_b[i-1];
846 }
847
848 //this loop aaplies the transformation to colors
849 for(vi=m.vert.begin();vi!=m.vert.end();++vi) //scan all the vertex...
850 {
851 if(!(*vi).IsD()) //if it has not been deleted...
852 {
853 if(!ProcessSelected || (*vi).IsS()) //if this vertex has been selected, do transormation
854 {
855 (*vi).C()=ColorEqualize((*vi).C(), cdf_l, cdf_r, cdf_g, cdf_b, rgbMask);
856 ++counter;
857 }
858 }
859 }
860 return counter;
861 }
862
863 //Applies equalization to the components of the color according to rgbmask
ColorEqualize(Color4b c,int cdf_l[256],int cdf_r[256],int cdf_g[256],int cdf_b[256],unsigned int rgbMask)864 static Color4b ColorEqualize(Color4b c, int cdf_l[256], int cdf_r[256], int cdf_g[256], int cdf_b[256], unsigned int rgbMask)
865 {
866 unsigned char r = c[0], g = c[1], b = c[2];
867 if(rgbMask == NO_CHANNELS) //in this case, equalization is done on lightness
868 {
869 int v = ValueEqualize(cdf_l[(int)(ComputeLightness(c)+0.5f)], cdf_l[0], cdf_l[255]);
870 return Color4b(v, v, v, 255); //return the equalized gray color
871 }
872 if(rgbMask & RED_CHANNEL) r = ValueEqualize(cdf_r[c[0]], cdf_r[0], cdf_r[255]); //Equalizes red
873 if(rgbMask & GREEN_CHANNEL) g = ValueEqualize(cdf_g[c[1]], cdf_g[0], cdf_g[255]); //Equalizes green
874 if(rgbMask & BLUE_CHANNEL) b = ValueEqualize(cdf_b[c[2]], cdf_b[0], cdf_b[255]); //Equalizes blue
875 return Color4b(r, g, b, 255); //return the equalized color
876 }
877
878 //Compute the equalized value
ValueEqualize(int cdfValue,int cdfMin,int cdfMax)879 static int ValueEqualize(int cdfValue, int cdfMin, int cdfMax)
880 {
881 return int(float((cdfValue - cdfMin)/float(cdfMax - cdfMin)) * 255.0f);
882 }
883
884 /*! \brief Simple white balancing filter.
885 \return the number of changed vertexes (the selected ones)
886
887 It applies a simple white balancing filter. It may works on a provided user color that is supposed to be white.
888 */
889 static int PerVertexWhiteBalance(MeshType &m, Color4b userColor, const bool ProcessSelected=false)
890 {
891 RequirePerVertexColor(m);
892
893 Color4b unbalancedWhite= userColor;
894 int counter=0;
895 VertexIterator vi;
896
897 //in this loop the transformation is applied to the mesh
898 for(vi=m.vert.begin();vi!=m.vert.end();++vi) //scan all the vertex...
899 {
900 if(!(*vi).IsD()) //if it has not been deleted...
901 {
902 if(!ProcessSelected || (*vi).IsS()) //if this vertex has been selected, do transormation
903 {
904 (*vi).C()=ColorWhiteBalance((*vi).C(),unbalancedWhite);
905 ++counter;
906 }
907 }
908 }
909 return counter;
910 }
911
912 //Balnce the white of the color, applying a correction factor based on the unbalancedWhite color.
ColorWhiteBalance(Color4b c,Color4b unbalancedWhite)913 static Color4b ColorWhiteBalance(Color4b c, Color4b unbalancedWhite)
914 {
915 //sanity check to avoid division by zero...
916 if(unbalancedWhite[0]==0) unbalancedWhite[0]=1;
917 if(unbalancedWhite[1]==0) unbalancedWhite[1]=1;
918 if(unbalancedWhite[2]==0) unbalancedWhite[2]=1;
919
920 return Color4b(
921 math::Clamp<int>((int)(c[0]*(255.0f/unbalancedWhite[0])), 0, 255),
922 math::Clamp<int>((int)(c[1]*(255.0f/unbalancedWhite[1])), 0, 255),
923 math::Clamp<int>((int)(c[2]*(255.0f/unbalancedWhite[2])), 0, 255),
924 255);
925 }
926
927 };
928
929 }// end namespace
930 }// end namespace
931 #endif
932