example source code similar color matching in represent 20 colors (opencv, hsv to rgb, rgb to hsv, ycbcr to rgb, rgb to ycbcr, hsv to conic, hsv to cylindric)

What is the best way to find a similar color in 20 represent colors, when we have any color?

My represent 20 colors are bring from paint app in window.



RGB value of 20 colors are composed..

{0,0,0},  //0
 {127,127,127},  //1
 {136,0,21},
 {237,28,36},
 {255127,39},
 {255,242,0},
 {34,177,76},
 {0,162,232},
 {63,72,204},
 {163,73,164},
 {255,255,255}, //10
 {195,195,195}, //11
 {185,122,87},
 {255,174,201},
 {255,201,14},
 {239,228,176},
 {181,230,29},
 {153,217,234},
 {112,146,190},
 {200,191,231}

If any rgb color is input, firstly examine the saturation of the input color.
The lower saturation value means that close to gray family color.
Our sample code is determined to be gray when the saturation value is smaller than 20.
Input color is examined that standard deviation is lower than threshold.
The lower STD value means that close to gray family color.
Our sample code is determined to be gray when the STD value is smaller than 4.
The value of 4 is heuristic value.

When the input color is gray scale, the input color was matched only 0, 1, 10, 11 index in color table.

When the input color is not gray scale, out sample find the similarity in the following five methods.

1.
input rgb -> hsv -> conic   VS   represent 20 conic values

2.
input rgb -> hsv -> cylindric  VS  represent 20 cylidric values

3.
input rgb -> YCbCr   VS   represent 20 YCbCr values

4.
input rgb -> CbCr   VS   represent 20 CbCr values 

5.
input rgb VS represent 20 rgb

VS means uclidean distance.
The minimum distance was best mathicng color.

The input color is seletect by random.
The matching result is expressed by jpg file and imshow.



I hope  you determine what is the the best way in 5 method.
And there can be a better way.

More detail refer to example source code.

...
#define SAT_TH 20

#include < opencv2\opencv.hpp>
#include < string>
#include < stdio.h>
#include < time.h>

#ifdef _DEBUG        
#pragma comment(lib, "opencv_core249d.lib")
#pragma comment(lib, "opencv_imgproc249d.lib")   //MAT processing
#pragma comment(lib, "opencv_highgui249d.lib")
#define _DEBUG_RRRR
#else
#pragma comment(lib, "opencv_core249.lib")
#pragma comment(lib, "opencv_imgproc249.lib")
#pragma comment(lib, "opencv_highgui249.lib")
//#define _DEBUG_RRRR
#endif   

using namespace std;
using namespace cv;

struct RGB_QUAD{  
  int r, g, b;
};

RGB_QUAD base_rgb[20] = {
 {0,0,0}, //0
 {127,127,127}, //1
 {136,0,21},
 {237,28,36},
 {255127,39},
 {255,242,0},
 {34,177,76},
 {0,162,232},
 {63,72,204},
 {163,73,164},
 {255,255,255}, //10
 {195,195,195}, //11
 {185,122,87},
 {255,174,201},
 {255,201,14},
 {239,228,176},
 {181,230,29},
 {153,217,234},
 {112,146,190},
 {200,191,231}
};





string rgb_name[20] = 
 {"black",
 "gray",
 "Brown",
 "Red",
 "Orange",
 "Yellow",
 "Green",
 "Sky Blue",
 "Blue",
 "Violet",
 "White",
 "Light Gray",
 "Ocher",
 "Pink",
 "Light Orange",
 "Light Yellow",
 "Light Green",
 "Light Sky Blue",
 "Light Blue",
 "Light Violet"};


struct hsv_color {
    unsigned char h;        // Hue: 0 ~ 255 (red:0, gree: 85, blue: 171)
    unsigned char s;        // Saturation: 0 ~ 255
    unsigned char v;        // Value: 0 ~ 255
};

 hsv_color base_hsv[20] = 
 {{0, 0, 0},
 {0, 0, 127},

 {250, 255, 136},
 {255, 224, 237},
 {17, 216, 255},
 {40, 255, 255},
 {97, 206, 177},
 {141, 255, 232},
 {169, 176, 204},
 {213, 141, 164},

 {0, 0, 255},
 {0, 0, 195},

 {15, 135, 185},
 {242, 81, 255},
 {33, 241, 255},
 {35, 67, 239},
 {53, 222, 230},
 {138, 88, 234},
 {153, 104, 190},
 {180, 44, 231}};

 

struct my_xyz {
    float x; float y; float z;
};
 
my_xyz base_hsv_conic[20] = {
 {0.00, 0.00, 0.00},
 {0.00, 0.00, 127.00},
 {134.97, -16.74, 136.00},
 {208.19, -0.04, 237.00},
 {197.33, 87.85, 255.00},
 {140.86, 212.56, 255.00},
 {-104.47, 97.63, 177.00},
 {-219.29, -75.73, 232.00},
 {-73.40, -120.16, 204.00},
 {46.29, -77.98, 164.00},
 {0.00, 0.00, 255.00},
 {0.00, 0.00, 195.00},
 {91.33, 35.38, 185.00},
 {76.88, -25.52, 255.00},
 {165.63, 175.07, 255.00},
 {40.86, 47.69, 239.00},
 {52.43, 193.25, 230.00},
 {-78.07, -20.65, 234.00},
 {-62.70, -45.54, 190.00},
 {-10.91, -38.34, 231.00}};

my_xyz base_hsv_cylindric[20] = {
 {0.00, 0.00, 0.00},
 {0.00, 0.00, 127.00},
 {253.06, -31.38, 136.00},
 {224.00, -0.04, 237.00},
 {197.33, 87.85, 255.00},
 {140.86, 212.56, 255.00},
 {-150.50, 140.66, 177.00},
 {-241.03, -83.24, 232.00},
 {-91.75, -150.19, 204.00},
 {71.98, -121.24, 164.00},
 {0.00, 0.00, 255.00},
 {0.00, 0.00, 195.00},
 {125.88, 48.77, 185.00},
 {76.88, -25.52, 255.00},
 {165.63, 175.07, 255.00},
 {43.59, 50.88, 239.00},
 {58.13, 214.26, 230.00},
 {-85.07, -22.51, 234.00},
 {-84.14, -61.12, 190.00},
 {-12.05, -42.32, 231.00}
};

struct my_ycbcr {
    float Y; float Cb; float Cr;
};

my_ycbcr base_ycbcr[20] = {
 {0.00, 128.00, 128.00},
 {127.00, 128.00, 128.00},
 {43.00, 115.59, 112.31},
 {91.00, 96.96, 88.77},
 {68.00, 89.63, 79.50},
 {218.00, 4.98, -27.48},
 {122.00, 102.04, 95.19},
 {121.00, 190.64, 207.17},
 {84.00, 195.72, 213.58},
 {110.00, 158.47, 166.51},
 {255.00, 128.00, 128.00},
 {195.00, 128.00, 128.00},
 {136.00, 100.35, 93.05},
 {201.00, 128.00, 128.00},
 {195.00, 25.86, -1.09},
 {225.00, 100.35, 93.05},
 {192.00, 36.02, 11.75},
 {199.00, 147.75, 152.96},
 {140.00, 156.21, 163.66},
 {198.00, 146.62, 151.54}
};





#define SAT_TH 20
#define STD_TH 5

#define MININ3(x,y,z)  ( (y) <= (z) ? ((x) <= (y) ? (x) : (y)) : ((x) <= (z) ? (x) : (z)) )
#define MAXIN3(x,y,z)  ( (y) >= (z) ? ((x) >= (y) ? (x) : (y)) : ((x) >= (z) ? (x) : (z)) )


int findColsetColorIndexInHSV_conic(int R, int G, int B);
int findColsetColorIndexInHSV_cylindric(int R, int G, int B);
int findColsetColorIndexInYCbCr(int R, int G, int B);
int findColsetColorIndexInYCbCr2(int R, int G, int B);

int findColsetColorIndexInRGB(int R, int G, int B);
int findColsetColorIndexInGRAYScale(int R, int G, int B);
float uDist(float a1, float b1, float c1, float a2, float b2, float c2);
float uDist2(float a1, float b1, float a2, float b2);


hsv_color RGB2HSV(unsigned char r, unsigned char g, unsigned char b);
my_ycbcr rgb2ycbcr(unsigned char r, unsigned char g, unsigned char b);
my_xyz HSV2conic(unsigned char h, unsigned char s, unsigned char v);
my_xyz HSV2Cylindric(unsigned char h, unsigned char s, unsigned char v);
void matchingColorinTable(int inR, int inG, int inB);
float getSTDrgb(int R, int G, int B);

void main()
{

 

 srand( time(0) );
 printf( "%d \n", rand( ) );

 
 //draw represent 20 colors 
 int colorN = 20;
 Mat ColorTable(50,50*colorN, CV_8UC3);
 for(int i=0; i< colorN; ++i)
 {
  rectangle(ColorTable, Rect(i*50, 0, i*50+50, 50), CV_RGB(base_rgb[i].r, base_rgb[i].g, base_rgb[i].b),-1 );
 }
 imshow("ColorTable", ColorTable );
 imwrite("colortable.jpg", ColorTable);

 

 for(int i=0; i< 200; ++i)
 {
  int r = rand() %255;
  int g = rand() %255;
  int b = rand() %255;
  printf("%d %d %d \n", r,g,b);

  //matching
  matchingColorinTable(r, g, b);

  waitKey(0);
 }
 

 

 
}


void matchingColorinTable(int inR, int inG, int inB)
{
 

 //matching
 //step 1. get rgb standard deviation
 hsv_color hsv;
 hsv = RGB2HSV(inR, inG, inB);

 float std = getSTDrgb(inR, inG, inB);
 //printf("std = %lf \n", std );

 int findClosetColorIndex=-1;
 if(STD_TH > std)
 //if(SAT_TH > hsv.s )
 {
  //input rgb can be gray scale color.
  findClosetColorIndex = findColsetColorIndexInGRAYScale(inR, inG, inB);

  Mat inColor(50,50*2, CV_8UC3);
  rectangle(inColor, Rect(0, 0, 50, 50), CV_RGB(inR, inG, inB),-1 );
  rectangle(inColor, Rect(50, 0, 50, 50), CV_RGB(base_rgb[findClosetColorIndex].r, base_rgb[findClosetColorIndex].g, base_rgb[findClosetColorIndex].b),-1 );
  imshow("your color and matched color", inColor );
  char str[100];
  sprintf(str,"gray_%d_%d_%d.jpg", inR, inG, inB );
  imwrite( str, inColor);
  

 }else{
  //step 2. find closest color by uclidiean distant
  Mat inColor(50*5,50*2, CV_8UC3);
  //#1 hsv conic
  rectangle(inColor, Rect(0, 0, 50, 50), CV_RGB(inR, inG, inB),-1 );
  findClosetColorIndex = findColsetColorIndexInHSV_conic(inR, inG, inB);
  rectangle(inColor, Rect(50, 0, 50, 50), CV_RGB(base_rgb[findClosetColorIndex].r, base_rgb[findClosetColorIndex].g, base_rgb[findClosetColorIndex].b),-1 );
  rectangle(inColor, Rect(0, 0, 100, 50), CV_RGB(0,0,0),1 );

  //#2 hsv cylidric
  rectangle(inColor, Rect(0, 50*1, 50, 50), CV_RGB(inR, inG, inB),-1 );
  findClosetColorIndex = findColsetColorIndexInHSV_cylindric(inR, inG, inB);
  rectangle(inColor, Rect(50, 50*1, 50, 50), CV_RGB(base_rgb[findClosetColorIndex].r, base_rgb[findClosetColorIndex].g, base_rgb[findClosetColorIndex].b),-1 );
  rectangle(inColor, Rect(0, 50, 100, 50), CV_RGB(0,0,0),1 );

  //#3 ycbcr
  rectangle(inColor, Rect(0, 50*2, 50, 50), CV_RGB(inR, inG, inB),-1 );
  findClosetColorIndex = findColsetColorIndexInYCbCr(inR, inG, inB);
  rectangle(inColor, Rect(50, 50*2, 50, 50), CV_RGB(base_rgb[findClosetColorIndex].r, base_rgb[findClosetColorIndex].g, base_rgb[findClosetColorIndex].b),-1 );
  rectangle(inColor, Rect(0, 100, 100, 50), CV_RGB(0,0,0),1 );


  //#4 cbcr
  rectangle(inColor, Rect(0, 50*3, 50, 50), CV_RGB(inR, inG, inB),-1 );
  findClosetColorIndex = findColsetColorIndexInYCbCr2(inR, inG, inB);
  rectangle(inColor, Rect(50, 50*3, 50, 50), CV_RGB(base_rgb[findClosetColorIndex].r, base_rgb[findClosetColorIndex].g, base_rgb[findClosetColorIndex].b),-1 );
  rectangle(inColor, Rect(0, 150, 100, 50), CV_RGB(0,0,0),1 );


  //#5 rgb
  rectangle(inColor, Rect(0, 50*4, 50, 50), CV_RGB(inR, inG, inB),-1 );
  findClosetColorIndex = findColsetColorIndexInRGB(inR, inG, inB);
  rectangle(inColor, Rect(50, 50*4, 50, 50), CV_RGB(base_rgb[findClosetColorIndex].r, base_rgb[findClosetColorIndex].g, base_rgb[findClosetColorIndex].b),-1 );
  rectangle(inColor, Rect(0, 200, 100, 50), CV_RGB(0,0,0),1 );

  
  imshow("your color and matched color", inColor );
  char str[100];
  sprintf(str,"%d_%d_%d.jpg", inR, inG, inB );
  imwrite( str, inColor);
  

 }
 

 //draw input color and matched color
 printf("find color index = %d\n", findClosetColorIndex);
 //print input hsv, match hsv 
 printf("input hsv=%d, %d, %d => match %d, %d, %d \n", hsv.h, hsv.s, hsv.v,
  base_hsv[findClosetColorIndex].h, 
  base_hsv[findClosetColorIndex].s, 
  base_hsv[findClosetColorIndex].v );



}



float uDist(float a1, float b1, float c1, float a2, float b2, float c2)
{
 float dist = sqrt( (a1-a2)*(a1-a2)+(b1-b2)*(b1-b2)+(c1-c2)*(c1-c2) );

 return dist;
}


float uDist2(float a1, float b1, float a2, float b2)
{
 float dist = sqrt( (a1-a2)*(a1-a2)+(b1-b2)*(b1-b2));
 return dist;
}

int findColsetColorIndexInGRAYScale(int R, int G, int B)
{
 
 int index=-1;
 float minDist=1000000;
 for(int i=0; i< 20; ++i)
 {
  //non gray scale pass
  if( !( (base_rgb[i].r == base_rgb[i].b) && (base_rgb[i].b == base_rgb[i].g ) ) )
   continue;

  float dist = uDist(R, G, B, base_rgb[i].r, base_rgb[i].g, base_rgb[i].b);
  if(dist < minDist)
  {
   minDist = dist;
   index = i;
  }
 }


 return index;
}

int findColsetColorIndexInHSV_conic(int R, int G, int B)
{
 int index=-1;
 float minDist=1000000;
 for(int i=0; i< 20; ++i)
 {
  //gray scale pass
  if( ( (base_rgb[i].r == base_rgb[i].b) && (base_rgb[i].b == base_rgb[i].g ) ) )
   continue;
  //rgb to hsv
  hsv_color hsv;
  hsv = RGB2HSV(R, G, B);
  //hsv to conic
  my_xyz xyz;
  xyz = HSV2conic(hsv.h, hsv.s, hsv.v);
  //distance
  float dist = uDist(xyz.x, xyz.y, xyz.z, base_hsv_conic[i].x, base_hsv_conic[i].y, base_hsv_conic[i].z);
  //min dist
  if(dist < minDist)
  {
   minDist = dist;
   index = i;
  }
 }
 return index;
}

int findColsetColorIndexInHSV_cylindric(int R, int G, int B)
{
 int index=-1;
 float minDist=1000000;
 for(int i=0; i< 20; ++i)
 {
  //gray scale pass
  if( ( (base_rgb[i].r == base_rgb[i].b) && (base_rgb[i].b == base_rgb[i].g ) ) )
   continue;
  //rgb to hsv
  hsv_color hsv;
  hsv = RGB2HSV(R, G, B);
  //hsv to conic
  my_xyz xyz;
  xyz = HSV2Cylindric(hsv.h, hsv.s, hsv.v);
  //distance
  float dist = uDist(xyz.x, xyz.y, xyz.z, base_hsv_cylindric[i].x, base_hsv_cylindric[i].y, base_hsv_cylindric[i].z);
  //min dist
  if(dist < minDist)
  {
   minDist = dist;
   index = i;
  }
 }
 return index;
}

int findColsetColorIndexInYCbCr(int R, int G, int B)
{
 int index=-1;
 float minDist=1000000;
 
 for(int i=0; i< 20; ++i)
 {
  //gray scale pass
  if( ( (base_rgb[i].r == base_rgb[i].b) && (base_rgb[i].b == base_rgb[i].g ) ) )
   continue;
  //rgb to ycbcr
  my_ycbcr ycbcr = rgb2ycbcr(R, G, B);
  //distance
  float dist = uDist(ycbcr.Y, ycbcr.Cb, ycbcr.Cr, base_ycbcr[i].Y, base_ycbcr[i].Cb, base_ycbcr[i].Cr);
  //min dist
  if(dist < minDist)
  {
   minDist = dist;
   index = i;
  }
 }
 
 return index;
}


int findColsetColorIndexInYCbCr2(int R, int G, int B)
{
 int index=-1;
 float minDist=1000000;
 
 for(int i=0; i< 20; ++i)
 {
  //gray scale pass
  if( ( (base_rgb[i].r == base_rgb[i].b) && (base_rgb[i].b == base_rgb[i].g ) ) )
   continue;
  //rgb to ycbcr
  my_ycbcr ycbcr = rgb2ycbcr(R, G, B);
  //distance
  float dist = uDist2(ycbcr.Cb, ycbcr.Cr,  base_ycbcr[i].Cb, base_ycbcr[i].Cr);
  //min dist
  if(dist < minDist)
  {
   minDist = dist;
   index = i;
  }
 }
 
 return index;
}



int findColsetColorIndexInRGB(int R, int G, int B)
{
 int index=-1;
 float minDist=1000000;
 
 for(int i=0; i< 20; ++i)
 {
  //gray scale pass
  if( ( (base_rgb[i].r == base_rgb[i].b) && (base_rgb[i].b == base_rgb[i].g ) ) )
   continue;
  //rgb to ycbcr
  
  //distance
  float dist = uDist(R, G, B, base_rgb[i].r, base_rgb[i].g, base_rgb[i].b);

  //min dist
  if(dist < minDist)
  {
   minDist = dist;
   index = i;
  }
 }

 return index;
}







hsv_color RGB2HSV(unsigned char r, unsigned char g, unsigned char b)
{
 unsigned char rgb_min, rgb_max;
 rgb_min = MININ3(b, g, r);
 rgb_max = MAXIN3(b, g, r);

 hsv_color hsv;
 hsv.v = rgb_max;
 if (hsv.v == 0) {
  hsv.h = hsv.s = 0;
  return hsv;
 }

 hsv.s = 255*(rgb_max - rgb_min)/hsv.v;
 if (hsv.s == 0) {
  hsv.h = 0;
  return hsv;
 }

 if (rgb_max == r) {
  hsv.h = 0 + 43*(g - b)/(rgb_max - rgb_min);
 } else if (rgb_max == g) {
  hsv.h = 85 + 43*(b - r)/(rgb_max - rgb_min);
 } else /* rgb_max == rgb.b */ {
  hsv.h = 171 + 43*(r - g)/(rgb_max - rgb_min);
 }

 return hsv;
}

my_xyz HSV2Cylindric(unsigned char h, unsigned char s, unsigned char v)
{
 my_xyz xyz;

 xyz.x = s*cos(2*3.1415*h/255.);
 xyz.y = s*sin(2*3.1415*h/255.);
 xyz.z = v;

 return xyz;

}


my_xyz HSV2conic(unsigned char h, unsigned char s, unsigned char v)
{
 my_xyz xyz;

 xyz.x = s*cos(2*3.1415*h/255.)*v/255.;
 xyz.y = s*sin(2*3.1415*h/255.)*v/255.;
 xyz.z = v;

 return xyz;
}


my_ycbcr rgb2ycbcr(unsigned char r, unsigned char g, unsigned char b)
{
 my_ycbcr ycbcr;

 ycbcr.Y = (299*r + 587*g + 114*b)/1000; //y
 ycbcr.Cb = 0.5643*(b - ycbcr.Y) + 128; //cb
 ycbcr.Cr = 0.7132*(b - ycbcr.Y) + 128; //cr
 


 return ycbcr;
}

float getSTDrgb(int R, int G, int B)
{
 float std;
 float mean = (R+G+B)/3;
 std = sqrt( (R-mean)*(R-mean)+(G-mean)*(G-mean)+(B-mean)*(B-mean) ) / 3;
 return std;
}


...

Comments

Popular posts from this blog

(OpenCV Study) Background subtractor MOG, MOG2, GMG example source code (BackgroundSubtractorMOG, BackgroundSubtractorMOG2, BackgroundSubtractorGMG)

OpenCV Stitching example (Stitcher class, Panorama)

Example source code of extract HOG feature from images, save descriptor values to xml file, using opencv (using HOGDescriptor )

Real-time N camera stitching Class.

Optical Flow sample source code using OpenCV

OpenCV Drawing Example, (line, circle, rectangle, ellipse, polyline, fillConvexPoly, putText, drawContours)

Video Stabilization example source code, (using cvFindHomography, cvWarpPerspective functions in openCV)

SICK LMS511 sensor data acquisition interface (source code, C++/MFC)

8 point algorithm (Matlab source code) / The method to get the Fundamental Matrix and the Essential matrix

Image warping (using opencv findHomography, warpPerspective)