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.
This commit is contained in:
Paolo Cignoni 2008-08-12 08:02:53 +00:00
parent 9e214da6ff
commit eb307140bb
1 changed files with 256 additions and 248 deletions

View File

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