From eb307140bb9b28d231ce2b0ef3dca6b1a896b33a Mon Sep 17 00:00:00 2001 From: cignoni Date: Tue, 12 Aug 2008 08:02:53 +0000 Subject: [PATCH] Renamed (and refactored to use Stat::MinMax function) method for ramp-coloring a mesh according to its Quality from UpdateColor::VertexQuality to UpdateColor::VertexQualityRamp. Added UpdateColor::VertexQualityGray. --- vcg/complex/trimesh/update/color.h | 504 +++++++++++++++-------------- 1 file changed, 256 insertions(+), 248 deletions(-) diff --git a/vcg/complex/trimesh/update/color.h b/vcg/complex/trimesh/update/color.h index ed9b2b65..1aece016 100644 --- a/vcg/complex/trimesh/update/color.h +++ b/vcg/complex/trimesh/update/color.h @@ -64,8 +64,10 @@ Changed name from plural to singular (normals->normal) #define __VCG_TRI_UPDATE_COLOR #include #include -#include +#include #include +#include + namespace vcg { namespace tri { @@ -291,7 +293,7 @@ static void FaceQuality(UpdateMeshType &m, float minq, float maxq) (*fi).C().ColorRamp(minq,maxq,(*fi).Q()); } -static void VertexQuality(UpdateMeshType &m, float minq, float maxq) +static void VertexQualityRamp(UpdateMeshType &m, float minq, float maxq) { typename UpdateMeshType::VertexIterator vi; @@ -300,19 +302,25 @@ static void VertexQuality(UpdateMeshType &m, float minq, float maxq) (*vi).C().ColorRamp(minq,maxq,(*vi).Q()); } -static void VertexQuality(UpdateMeshType &m) +static void VertexQualityRamp(UpdateMeshType &m) +{ + std::pair minmax = Stat::ComputePerVertexQualityMinMax( m); + VertexQualityRamp(m,minmax.first,minmax.second); +} + +static void VertexQualityGray(UpdateMeshType &m, const float minq, const float maxq) { - // step 1: find the range typename UpdateMeshType::VertexIterator vi; - float minq=std::numeric_limits::max(), - maxq=-std::numeric_limits::max(); + for(vi=m.vert.begin();vi!=m.vert.end();++vi) if(!(*vi).IsD()) - { - minq=vcg::math::Min(minq,(*vi).Q()); - maxq=vcg::math::Max(maxq,(*vi).Q()); - } - VertexQuality(m,minq,maxq); + (*vi).C().SetGrayShade( ((*vi).Q()-minq)/(maxq-minq)); +} + +static void VertexQualityGray(UpdateMeshType &m) +{ + std::pair minmax = Stat::ComputePerVertexQualityMinMax( m); + VertexQualityGray(m,minmax.first,minmax.second); } //Fill the mesh with the selected color. @@ -388,7 +396,7 @@ static int Brighting(UpdateMeshType &m, float amount, const bool ProcessSelected } return counter; } - + //Apply Contrast filter to the mesh with the given contrast factor. static int Contrast(UpdateMeshType &m, float factor, const bool ProcessSelected=false) { @@ -408,7 +416,7 @@ static int Contrast(UpdateMeshType &m, float factor, const bool ProcessSelected= return counter; } -//Performs contrast operations on color, i.e expands or compress the histogram around +//Performs contrast operations on color, i.e expands or compress the histogram around //the midpoint value. NewValue = (OldValue - 128) × factor + 128 static Color4b ColorMul(Color4b c, float factor) { @@ -419,7 +427,7 @@ static int ValueMul(int value, float factor) { return math::Clamp((int)((value - 128)*factor + 128), 0, 255); } - + //Apply Contrast and Brightness filter to the mesh, with the given contrast factor and brightness amount. static int ContrastBrightness(UpdateMeshType &m, float factor, float amount, const bool ProcessSelected=false) { @@ -514,55 +522,55 @@ static float ValuePow(float value, float exponent) { return powf(value, exponent); } - -//useful bit masks for RGB channels, used for Levels filter. -enum rgbChMask {ALL_CHANNELS = 7, RED_CHANNEL = 4, GREEN_CHANNEL = 2, BLUE_CHANNEL = 1, NO_CHANNELS = 0 }; - -//Adjusts color levels of the mesh. Filter can be applied to all RGB channels or to each channel separately. -//in_min, gamma and in_max are respectively the black point, the gray point and the white point. -//out_min and out_max are the output level for black and white respectively. -static int Levels(UpdateMeshType &m, float gamma, float in_min, float in_max, float out_min, float out_max, unsigned char rgbMask, const bool ProcessSelected=false) -{ - int counter=0; - VertexIterator vi; - for(vi=m.vert.begin();vi!=m.vert.end();++vi) //scan all the vertex... - { - if(!(*vi).IsD()) //if it has not been deleted... - { - if(!ProcessSelected || (*vi).IsS()) //if this vertex has been selected, do transormation - { - (*vi).C() = ColorLevels((*vi).C(), gamma, in_min, in_max, out_min, out_max, rgbMask); - ++counter; - } - } - } - return counter; -} - -//Performs levels transformation on each channel set to 1 in the rgbMask. -static Color4b ColorLevels(Color4b c, float gamma, float in_min, float in_max, float out_min, float out_max, unsigned char rgbMask) -{ - unsigned char r = c[0], g = c[1], b = c[2]; - if(rgbMask & RED_CHANNEL) r = ValueLevels(c[0], gamma, in_min, in_max, out_min, out_max); - if(rgbMask & GREEN_CHANNEL) g = ValueLevels(c[1], gamma, in_min, in_max, out_min, out_max); - if(rgbMask & BLUE_CHANNEL) b = ValueLevels(c[2], gamma, in_min, in_max, out_min, out_max); - return Color4b(r, g, b, 255); -} - -//Transform on levels -static int ValueLevels(int value, float gamma, float in_min, float in_max, float out_min, float out_max) -{ - float fvalue = value/255.0f; - // normalize - fvalue = math::Clamp(fvalue - in_min, 0.0f, 1.0f) / math::Clamp(in_max - in_min, 1.0f/255.0f, 1.0f); - // transform gamma - fvalue = powf(fvalue,1/gamma); - // rescale range - fvalue = fvalue * (out_max - out_min) + out_min; - //back in interval [0,255] and clamp - return math::Clamp((int)(fvalue * 255), 0, 255); -} - + +//useful bit masks for RGB channels, used for Levels filter. +enum rgbChMask {ALL_CHANNELS = 7, RED_CHANNEL = 4, GREEN_CHANNEL = 2, BLUE_CHANNEL = 1, NO_CHANNELS = 0 }; + +//Adjusts color levels of the mesh. Filter can be applied to all RGB channels or to each channel separately. +//in_min, gamma and in_max are respectively the black point, the gray point and the white point. +//out_min and out_max are the output level for black and white respectively. +static int Levels(UpdateMeshType &m, float gamma, float in_min, float in_max, float out_min, float out_max, unsigned char rgbMask, const bool ProcessSelected=false) +{ + int counter=0; + VertexIterator vi; + for(vi=m.vert.begin();vi!=m.vert.end();++vi) //scan all the vertex... + { + if(!(*vi).IsD()) //if it has not been deleted... + { + if(!ProcessSelected || (*vi).IsS()) //if this vertex has been selected, do transormation + { + (*vi).C() = ColorLevels((*vi).C(), gamma, in_min, in_max, out_min, out_max, rgbMask); + ++counter; + } + } + } + return counter; +} + +//Performs levels transformation on each channel set to 1 in the rgbMask. +static Color4b ColorLevels(Color4b c, float gamma, float in_min, float in_max, float out_min, float out_max, unsigned char rgbMask) +{ + unsigned char r = c[0], g = c[1], b = c[2]; + if(rgbMask & RED_CHANNEL) r = ValueLevels(c[0], gamma, in_min, in_max, out_min, out_max); + if(rgbMask & GREEN_CHANNEL) g = ValueLevels(c[1], gamma, in_min, in_max, out_min, out_max); + if(rgbMask & BLUE_CHANNEL) b = ValueLevels(c[2], gamma, in_min, in_max, out_min, out_max); + return Color4b(r, g, b, 255); +} + +//Transform on levels +static int ValueLevels(int value, float gamma, float in_min, float in_max, float out_min, float out_max) +{ + float fvalue = value/255.0f; + // normalize + fvalue = math::Clamp(fvalue - in_min, 0.0f, 1.0f) / math::Clamp(in_max - in_min, 1.0f/255.0f, 1.0f); + // transform gamma + fvalue = powf(fvalue,1/gamma); + // rescale range + fvalue = fvalue * (out_max - out_min) + out_min; + //back in interval [0,255] and clamp + return math::Clamp((int)(fvalue * 255), 0, 255); +} + //Colors the mesh. Color is blended to the mesh with the given intensity. static int Colourisation(UpdateMeshType &m, Color4b c, float intensity, const bool ProcessSelected=false) { @@ -581,7 +589,7 @@ static int Colourisation(UpdateMeshType &m, Color4b c, float intensity, const bo } return counter; } - + //Perform colourisation operation. For each channel C: newC = origC + intensity * (newC - origC) static Color4b ColorApplyDiff(Color4b old_color, Color4b new_color, float intensity) { @@ -591,191 +599,191 @@ static Color4b ColorApplyDiff(Color4b old_color, Color4b new_color, float intens static int ValueApplyDiff(int old_value, int new_value, float intensity) { return math::Clamp((int)(old_value + intensity * (new_value - old_value)), 0, 255); -} - -//An useful ENUM to hold all desaturation methods. -enum DesaturationMethods {M_LIGHTNESS = 0, M_LUMINOSITY = 1, M_AVERAGE = 2}; - -//Desaturates the mesh according the selected method. Method belongs to DesaturationMethods's ENUM. -static int Desaturation(UpdateMeshType &m, int method, const bool ProcessSelected=false) -{ - int counter=0; - VertexIterator vi; - for(vi=m.vert.begin();vi!=m.vert.end();++vi) //scan all the vertex... - { - if(!(*vi).IsD()) //if it has not been deleted... - { - if(!ProcessSelected || (*vi).IsS()) //if this vertex has been selected, do transormation - { - (*vi).C() = ColorDesaturate((*vi).C(), method); - ++counter; - } - } - } - return counter; -} - -//Desature the color. Ausiliary functions to calculate lightness/luminosity/average. -static Color4b ColorDesaturate(Color4b c, int method) -{ - switch(method){ - case M_LIGHTNESS:{ - int val = (int)ComputeLightness(c); - return Color4b( val, val, val, 255); - } - case M_AVERAGE:{ - int val = (int)ComputeAvgLightness(c); - return Color4b( val, val, val, 255); - } - case M_LUMINOSITY:{ - int val = (int)ComputeLuminosity(c); - return Color4b( val, val, val, 255); - } - default: assert(0); - } -} - -//ausiliary function to compute average lightness. value = (R+G+B)/3 -static float ComputeAvgLightness(Color4b c) -{ - return float(c[0]+c[1]+c[2])/3.0f; -} - -//ausiliary function to compute luminosity. value = 0.21*R+0.71*G+0.7*B -static float ComputeLuminosity(Color4b c) -{ - return float(0.2126f*c[0]+0.7152f*c[1]+0.0722f*c[2]); } - -//Equalize the histogram of colors. It can equalize any combination of rgb channels or -//it can work on lightness. -static int Equalize(UpdateMeshType &m, unsigned int rgbMask, const bool ProcessSelected=false) -{ - //declares , resets and set up 4 histograms, for Red, Green, Blue and Lightness - Histogramf Hl, Hr, Hg, Hb; - Hl.Clear(); Hr.Clear(); Hg.Clear(); Hb.Clear(); - Hl.SetRange(0, 255, 255); Hr.SetRange(0, 255, 255); Hg.SetRange(0, 255, 255); Hb.SetRange(0, 255, 255); - - int counter=0; - VertexIterator vi; - - //Scan the mesh to build the histograms - for(vi=m.vert.begin();vi!=m.vert.end();++vi) //scan all the vertex... - { - if(!(*vi).IsD()) //if it has not been deleted... - { - if(!ProcessSelected || (*vi).IsS()) //if this vertex has been selected, put it in the histograms - { - int v = (int)(ComputeLightness((*vi).C())+0.5); //compute and round lightness value - Hl.Add(v); Hr.Add((*vi).C()[0]); Hg.Add((*vi).C()[1]); Hb.Add((*vi).C()[2]); - } - } - } - - //for each histogram, compute the cumulative distribution function, and build a lookup table - int cdf_l[256], cdf_r[256], cdf_g[256], cdf_b[256]; - cdf_l[0] = Hl.BinCount(0); cdf_r[0] = Hr.BinCount(0); cdf_g[0] = Hg.BinCount(0); cdf_b[0] = Hb.BinCount(0); - for(int i=1; i<256; i++){ - cdf_l[i] = Hl.BinCount(float(i)) + cdf_l[i-1]; - cdf_r[i] = Hr.BinCount(float(i)) + cdf_r[i-1]; - cdf_g[i] = Hg.BinCount(float(i)) + cdf_g[i-1]; - cdf_b[i] = Hb.BinCount(float(i)) + cdf_b[i-1]; - } - - //this loop aaplies the transformation to colors - for(vi=m.vert.begin();vi!=m.vert.end();++vi) //scan all the vertex... - { - if(!(*vi).IsD()) //if it has not been deleted... - { - if(!ProcessSelected || (*vi).IsS()) //if this vertex has been selected, do transormation - { - (*vi).C()=ColorEqualize((*vi).C(), cdf_l, cdf_r, cdf_g, cdf_b, rgbMask); - ++counter; - } - } - } - return counter; -} - -//Applies equalization to the components of the color according to rgbmask -static Color4b ColorEqualize(Color4b c, int cdf_l[256], int cdf_r[256], int cdf_g[256], int cdf_b[256], unsigned int rgbMask) -{ - unsigned char r = c[0], g = c[1], b = c[2]; - if(rgbMask == NO_CHANNELS) //in this case, equalization is done on lightness - { - int v = ValueEqualize(cdf_l[(int)(ComputeLightness(c)+0.5f)], cdf_l[0], cdf_l[255]); - return Color4b(v, v, v, 255); //return the equalized gray color - } - if(rgbMask & RED_CHANNEL) r = ValueEqualize(cdf_r[c[0]], cdf_r[0], cdf_r[255]); //Equalizes red - if(rgbMask & GREEN_CHANNEL) g = ValueEqualize(cdf_g[c[1]], cdf_g[0], cdf_g[255]); //Equalizes green - if(rgbMask & BLUE_CHANNEL) b = ValueEqualize(cdf_b[c[2]], cdf_b[0], cdf_b[255]); //Equalizes blue - return Color4b(r, g, b, 255); //return the equalized color -} - -//Compute the equalized value -static int ValueEqualize(int cdfValue, int cdfMin, int cdfMax) -{ - return int(float((cdfValue - cdfMin)/float(cdfMax - cdfMin)) * 255.0f); -} - -//applies the white balance filter. It may works with an auto regulation of white, or based on a user -//color that is supposed to be white. -static int WhiteBalance(UpdateMeshType &m, bool automatic, Color4b userColor, const bool ProcessSelected=false) -{ - Color4b unbalancedWhite; - float lightness = 0; - int counter=0; - VertexIterator vi; - - if(!automatic) unbalancedWhite = userColor; //no auto regolation required, user has provided a color. - else //else, we need to scan the mesh and pick its lighter color... - { - for(vi=m.vert.begin();vi!=m.vert.end();++vi) //scan all the vertex... - { - if(!(*vi).IsD()) //if it has not been deleted... - { - if(!ProcessSelected || (*vi).IsS()) //if this vertex has been selected... - { - //the lighter color is selected with an incremental approach... - float v = ComputeLightness((*vi).C()); - if( v > lightness){ - lightness = v; //save lightness - unbalancedWhite = (*vi).C(); //save the color - } - } - } - } - } - - //in this loop the transformation is applied to the mesh - for(vi=m.vert.begin();vi!=m.vert.end();++vi) //scan all the vertex... - { - if(!(*vi).IsD()) //if it has not been deleted... - { - if(!ProcessSelected || (*vi).IsS()) //if this vertex has been selected, do transormation - { - (*vi).C()=ColorWhiteBalance((*vi).C(),unbalancedWhite); - ++counter; - } - } - } - return counter; -} - -//Balnce the white of the color, applying a correction factor based on the unbalancedWhite color. -static Color4b ColorWhiteBalance(Color4b c, Color4b unbalancedWhite) -{ - //sanity check to avoid division by zero... - if(unbalancedWhite[0]==0) unbalancedWhite[0]=1; - if(unbalancedWhite[1]==0) unbalancedWhite[1]=1; - if(unbalancedWhite[2]==0) unbalancedWhite[2]=1; - - return Color4b( - math::Clamp((int)(c[0]*(255.0f/unbalancedWhite[0])), 0, 255), - math::Clamp((int)(c[1]*(255.0f/unbalancedWhite[1])), 0, 255), - math::Clamp((int)(c[2]*(255.0f/unbalancedWhite[2])), 0, 255), - 255); -} + +//An useful ENUM to hold all desaturation methods. +enum DesaturationMethods {M_LIGHTNESS = 0, M_LUMINOSITY = 1, M_AVERAGE = 2}; + +//Desaturates the mesh according the selected method. Method belongs to DesaturationMethods's ENUM. +static int Desaturation(UpdateMeshType &m, int method, const bool ProcessSelected=false) +{ + int counter=0; + VertexIterator vi; + for(vi=m.vert.begin();vi!=m.vert.end();++vi) //scan all the vertex... + { + if(!(*vi).IsD()) //if it has not been deleted... + { + if(!ProcessSelected || (*vi).IsS()) //if this vertex has been selected, do transormation + { + (*vi).C() = ColorDesaturate((*vi).C(), method); + ++counter; + } + } + } + return counter; +} + +//Desature the color. Ausiliary functions to calculate lightness/luminosity/average. +static Color4b ColorDesaturate(Color4b c, int method) +{ + switch(method){ + case M_LIGHTNESS:{ + int val = (int)ComputeLightness(c); + return Color4b( val, val, val, 255); + } + case M_AVERAGE:{ + int val = (int)ComputeAvgLightness(c); + return Color4b( val, val, val, 255); + } + case M_LUMINOSITY:{ + int val = (int)ComputeLuminosity(c); + return Color4b( val, val, val, 255); + } + default: assert(0); + } +} + +//ausiliary function to compute average lightness. value = (R+G+B)/3 +static float ComputeAvgLightness(Color4b c) +{ + return float(c[0]+c[1]+c[2])/3.0f; +} + +//ausiliary function to compute luminosity. value = 0.21*R+0.71*G+0.7*B +static float ComputeLuminosity(Color4b c) +{ + return float(0.2126f*c[0]+0.7152f*c[1]+0.0722f*c[2]); +} + +//Equalize the histogram of colors. It can equalize any combination of rgb channels or +//it can work on lightness. +static int Equalize(UpdateMeshType &m, unsigned int rgbMask, const bool ProcessSelected=false) +{ + //declares , resets and set up 4 histograms, for Red, Green, Blue and Lightness + Histogramf Hl, Hr, Hg, Hb; + Hl.Clear(); Hr.Clear(); Hg.Clear(); Hb.Clear(); + Hl.SetRange(0, 255, 255); Hr.SetRange(0, 255, 255); Hg.SetRange(0, 255, 255); Hb.SetRange(0, 255, 255); + + int counter=0; + VertexIterator vi; + + //Scan the mesh to build the histograms + for(vi=m.vert.begin();vi!=m.vert.end();++vi) //scan all the vertex... + { + if(!(*vi).IsD()) //if it has not been deleted... + { + if(!ProcessSelected || (*vi).IsS()) //if this vertex has been selected, put it in the histograms + { + int v = (int)(ComputeLightness((*vi).C())+0.5); //compute and round lightness value + Hl.Add(v); Hr.Add((*vi).C()[0]); Hg.Add((*vi).C()[1]); Hb.Add((*vi).C()[2]); + } + } + } + + //for each histogram, compute the cumulative distribution function, and build a lookup table + int cdf_l[256], cdf_r[256], cdf_g[256], cdf_b[256]; + cdf_l[0] = Hl.BinCount(0); cdf_r[0] = Hr.BinCount(0); cdf_g[0] = Hg.BinCount(0); cdf_b[0] = Hb.BinCount(0); + for(int i=1; i<256; i++){ + cdf_l[i] = Hl.BinCount(float(i)) + cdf_l[i-1]; + cdf_r[i] = Hr.BinCount(float(i)) + cdf_r[i-1]; + cdf_g[i] = Hg.BinCount(float(i)) + cdf_g[i-1]; + cdf_b[i] = Hb.BinCount(float(i)) + cdf_b[i-1]; + } + + //this loop aaplies the transformation to colors + for(vi=m.vert.begin();vi!=m.vert.end();++vi) //scan all the vertex... + { + if(!(*vi).IsD()) //if it has not been deleted... + { + if(!ProcessSelected || (*vi).IsS()) //if this vertex has been selected, do transormation + { + (*vi).C()=ColorEqualize((*vi).C(), cdf_l, cdf_r, cdf_g, cdf_b, rgbMask); + ++counter; + } + } + } + return counter; +} + +//Applies equalization to the components of the color according to rgbmask +static Color4b ColorEqualize(Color4b c, int cdf_l[256], int cdf_r[256], int cdf_g[256], int cdf_b[256], unsigned int rgbMask) +{ + unsigned char r = c[0], g = c[1], b = c[2]; + if(rgbMask == NO_CHANNELS) //in this case, equalization is done on lightness + { + int v = ValueEqualize(cdf_l[(int)(ComputeLightness(c)+0.5f)], cdf_l[0], cdf_l[255]); + return Color4b(v, v, v, 255); //return the equalized gray color + } + if(rgbMask & RED_CHANNEL) r = ValueEqualize(cdf_r[c[0]], cdf_r[0], cdf_r[255]); //Equalizes red + if(rgbMask & GREEN_CHANNEL) g = ValueEqualize(cdf_g[c[1]], cdf_g[0], cdf_g[255]); //Equalizes green + if(rgbMask & BLUE_CHANNEL) b = ValueEqualize(cdf_b[c[2]], cdf_b[0], cdf_b[255]); //Equalizes blue + return Color4b(r, g, b, 255); //return the equalized color +} + +//Compute the equalized value +static int ValueEqualize(int cdfValue, int cdfMin, int cdfMax) +{ + return int(float((cdfValue - cdfMin)/float(cdfMax - cdfMin)) * 255.0f); +} + +//applies the white balance filter. It may works with an auto regulation of white, or based on a user +//color that is supposed to be white. +static int WhiteBalance(UpdateMeshType &m, bool automatic, Color4b userColor, const bool ProcessSelected=false) +{ + Color4b unbalancedWhite; + float lightness = 0; + int counter=0; + VertexIterator vi; + + if(!automatic) unbalancedWhite = userColor; //no auto regolation required, user has provided a color. + else //else, we need to scan the mesh and pick its lighter color... + { + for(vi=m.vert.begin();vi!=m.vert.end();++vi) //scan all the vertex... + { + if(!(*vi).IsD()) //if it has not been deleted... + { + if(!ProcessSelected || (*vi).IsS()) //if this vertex has been selected... + { + //the lighter color is selected with an incremental approach... + float v = ComputeLightness((*vi).C()); + if( v > lightness){ + lightness = v; //save lightness + unbalancedWhite = (*vi).C(); //save the color + } + } + } + } + } + + //in this loop the transformation is applied to the mesh + for(vi=m.vert.begin();vi!=m.vert.end();++vi) //scan all the vertex... + { + if(!(*vi).IsD()) //if it has not been deleted... + { + if(!ProcessSelected || (*vi).IsS()) //if this vertex has been selected, do transormation + { + (*vi).C()=ColorWhiteBalance((*vi).C(),unbalancedWhite); + ++counter; + } + } + } + return counter; +} + +//Balnce the white of the color, applying a correction factor based on the unbalancedWhite color. +static Color4b ColorWhiteBalance(Color4b c, Color4b unbalancedWhite) +{ + //sanity check to avoid division by zero... + if(unbalancedWhite[0]==0) unbalancedWhite[0]=1; + if(unbalancedWhite[1]==0) unbalancedWhite[1]=1; + if(unbalancedWhite[2]==0) unbalancedWhite[2]=1; + + return Color4b( + math::Clamp((int)(c[0]*(255.0f/unbalancedWhite[0])), 0, 255), + math::Clamp((int)(c[1]*(255.0f/unbalancedWhite[1])), 0, 255), + math::Clamp((int)(c[2]*(255.0f/unbalancedWhite[2])), 0, 255), + 255); +} };