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;
}
...