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 39 /*! 40 \ingroup trimesh 41 42 \headerfile color.h vcg/complex/algorithms/update/color.h 43 44 \brief Generation and processing of per-vertex and per-face colors according to various strategy. 45 46 This class is used to compute per face or per vertex color with respect to a number of algorithms. 47 There is a wide range of algorithms for processing vertex color in a \i photoshop-like mode (changing for example contrast, white balance, gamma) 48 Basic Tools for mapping quality into a color according to standard color ramps are here. 49 */ 50 51 template <class MeshType> 52 class UpdateColor 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 80 /*! \brief This function colors all (or the selected) the vertices of a mesh. 81 */ 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 98 /*! \brief This function colors all (or the selected) faces of a mesh. 99 */ 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 115 /*! \brief This function colors all (or the selected) tetras of a mesh. 116 */ 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 131 /** \brief Transfer tetra color onto vertex color 132 133 Plain average of the color of the tetras incident on a given vertex. 134 No adjacency required. 135 */ PerVertexFromTetra(MeshType & m)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 164 /** \brief Transfer face color onto vertex color 165 166 Plain average of the color of the faces incident on a given vertex. 167 No adjacency required. 168 */ PerVertexFromFace(MeshType & m)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 201 /*! \brief Transfer vertex color onto face color 202 Plain average of the color of the vertexes on a given face. 203 */ PerFaceFromVertex(MeshType & m)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 219 /*! \brief This function colores all the faces of a mesh with a hue color shade dependent on the quality. 220 221 If no range of quality is passed it is automatically computed. 222 */ 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 240 /*! \brief This function colores all the faces of a mesh with a hue color shade dependent on the quality. 241 242 If no range of quality is passed it is automatically computed. 243 */ 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 260 /*! \brief This function colores all the faces of a mesh with a hue color shade dependent on the quality. 261 262 If no range of quality is passed it is automatically computed. 263 */ 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 } 281 /*! \brief This function colores all the faces of a mesh with a hue color shade dependent on the quality. 282 283 If no range of quality is passed it is automatically computed. 284 */ 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 302 /*! \brief This function colores all the edges of a mesh with a hue color shade dependent on the quality. 303 304 If no range of quality is passed it is automatically computed. 305 */ 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 322 /*! \brief This function colores all the vertices of a mesh with a gray shade dependent on the quality. 323 324 If no range of quality is passed it is automatically computed. 325 */ 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 341 /*! \brief This function colores all the faces of a mesh with a gray shade dependent on the quality. 342 343 If no range of quality is passed it is automatically computed. 344 */ 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 360 /** \brief Color the vertexes of the mesh that are on the border 361 362 It uses the information in the Vertex flags, and not necessarily any topology. 363 So it just require that you have correctly computed the flags; one way could be the following one: 364 \code 365 vcg::tri::UpdateTopology<Mesh>::FaceFace(m.cm); 366 vcg::tri::UpdateFlags<Mesh>::FaceBorderFromFF(m.cm); 367 vcg::tri::UpdateFlags<Mesh>::VertexBorderFromFaceBorder (m.cm); 368 vcg::tri::UpdateColor<Mesh>::PerVertexBorderFlag(m.cm); 369 \endcode 370 */ 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 397 /*! \brief This function colores the faces of connected components of a mesh randomly. 398 399 It require FaceFace Adjacency becouse it relies on the output of the ConnecteComponents(); 400 */ PerFaceRandomConnectedComponent(MeshType & m)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 419 /*! \brief This function colores the face of a mesh randomly. 420 421 Note: The faux bit is used to color polygonal faces uniformly 422 */ PerFaceRandom(MeshType & m)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 444 /*! \brief Perlin Noise. 445 446 Simple Perlin noise. To make things weirder each color band can have its own offset and frequency. 447 Period is expressed in absolute terms. 448 So as period it is meaningful could be to use something in the range of 1/10 of the bbox diag. 449 */ 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 473 /*! \brief Perlin Color mixing. 474 475 Simple Perlin color mixing. Color 1 and 2 are mixed according the perlin noise function, with period and offset. 476 */ 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 500 /*! \brief Simple Noise adding function. 501 It simply add signed noise to the color of the mesh. The noise has uniform distribution and the amplitude is +/-2^(noisebits-1). 502 */ 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 523 /*! \brief Reduces vertex color the mesh to two colors according to a threshold. 524 */ 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)) ComputeLightness(Color4b c)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 556 /*! \brief Apply the brightness filter, with the given amount, to the mesh. 557 */ 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 582 /*! \brief Apply Contrast filter to the mesh with the given contrast factor. 583 */ 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 ColorMul(Color4b c,float factor)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 ValueMul(int value,float factor)611 static int ValueMul(int value, float factor) 612 { 613 return math::Clamp<int>((int)((value - 128)*factor + 128), 0, 255); 614 } 615 616 /*! \brief Apply Brightness and Contrast filter to the mesh, with the given contrast factor and brightness amount. 617 618 Performs contrast and brightness operations on color, i.e NewValue = (OldValue - 128) * contrast + 128 + amount 619 The result is clamped just one time after all computations; this get a more accurate result. 620 621 The formula used here is the one of GIMP. 622 623 */ 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 ColorBrightnessContrast(Color4b c,float brightness,float contrast)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 ValueBrightnessContrast(unsigned char ivalue,float brightness,float contrast)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 660 /*! \brief Invert the colors of the mesh. 661 662 \return the number of changed vertexes (the selected ones) 663 */ 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 684 /*! \brief Apply the gamma correction filter, with the given gamma exponet, to the mesh. 685 \return the number of changed vertexes (the selected ones) 686 */ 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) ColorPow(Color4b c,float exponent)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 ValuePow(float value,float exponent)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 726 /*! \brief Adjusts color levels of the mesh 727 728 \return the number of changed vertexes (the selected ones) 729 730 Adjusts color levels of the mesh. Filter can be applied to all RGB channels or to each channel separately. 731 in_min, gamma and in_max are respectively the black point, the gray point and the white point. 732 out_min and out_max are the output level for black and white respectively. 733 */ 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. ColorLevels(Color4b c,float gamma,float in_min,float in_max,float out_min,float out_max,unsigned char 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 ValueLevels(int value,float gamma,float in_min,float in_max,float out_min,float out_max)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 778 /*! \brief Colorize the mesh toward a given color. 779 \return the number of changed vertexes (the selected ones) 780 781 Colors the mesh. Color is blended to the mesh with the given intensity (0..1 ranged). 782 */ 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) ColorApplyDiff(Color4b old_color,Color4b new_color,float intensity)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 ValueApplyDiff(int old_value,int new_value,float intensity)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 821 /*! \brief Desaturates the mesh according the a chosen desaturation method 822 823 \return the number of changed vertexes (the selected ones) 824 825 There are three possibilities 826 - \c M_LIGHTNESS where lightness = 0.5*(Max(R,G,B)+Min(R,G,B)) 827 - \c M_LUMINOSITY where luminosity = 0.21*R+0.71*G+0.7*B 828 - \c M_AVERAGE Plain Average 829 */ 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. ColorDesaturate(Color4b c,int method)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 ComputeAvgLightness(Color4b c)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 ComputeLuminosity(Color4b c)878 static float ComputeLuminosity(Color4b c) 879 { 880 return float(0.2126f*c[0]+0.7152f*c[1]+0.0722f*c[2]); 881 } 882 883 /*! \brief Histogram Color Equalization. 884 \return the number of changed vertexes (the selected ones) 885 886 Equalize the histogram of colors. It can equalize any combination of rgb channels or it can work on lightness. 887 */ 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 ColorEqualize(Color4b c,int cdf_l[256],int cdf_r[256],int cdf_g[256],int cdf_b[256],unsigned int 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 ValueEqualize(int cdfValue,int cdfMin,int cdfMax)954 static int ValueEqualize(int cdfValue, int cdfMin, int cdfMax) 955 { 956 return int(float((cdfValue - cdfMin)/float(cdfMax - cdfMin)) * 255.0f); 957 } 958 959 /*! \brief Simple white balancing filter. 960 \return the number of changed vertexes (the selected ones) 961 962 It applies a simple white balancing filter. It may works on a provided user color that is supposed to be white. 963 */ 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. ColorWhiteBalance(Color4b c,Color4b unbalancedWhite)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 1007