RGB值基色名称 [英] RGB value base color name

查看:389
本文介绍了RGB值基色名称的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧!

问题描述

我想找到颜色名称,考虑到它的rgb值。



示例RGB值是:(237,65,83)



预定义值



数组(11,'Red','#FF0000','255,0,0'),

数组(3,'Brown','#A52A2A','165,42,42')



此方法



像素是来自 LUT 的对应基色。这是结果:





左侧是来源图像,然后是原始 RGB 比较,然后进行归一化 RGB 比较(不能区分相同颜色阴影)和右侧 HSV 比较。



对于标准化的 RGB ,找到的颜色总是来自相同颜色但不同强度的 LUT 。比较选择较暗只是因为它们是 LUT 中的第一个。



正如我前面提到的,暗和灰度颜色是这个问题,应该单独处理。如果你得到类似的结果,仍然错误的检测,那么你需要添加更多的基本颜色来弥补差距。如果您根本没有类似结果,那么您很有可能是以下问题:


  1. 错误 HSV RGB 范围,每个频道

  2. 溢出



    当乘以数字时,使用的位被求和!

      8bit * 8bit * 8bit * 8bit = 32bit 

    如果数字是签名的,你在麻烦...如果在32位变量就像我上面的例子,所以你需要移位范围一点或使用



$

b $ b

只是为了确保我添加了我的 HSV / RGB 转换,以防你遇到了一些问题。



这里原来的HSV产生的转换:




I want to find color name , given its rgb value .

Example RGB value is : (237, 65, 83)

Predefined Values

array(11, 'Red', '#FF0000', '255,0,0'),

array(3, 'Brown', '#A52A2A', '165,42,42')

If i use this method distance calculation

i am getting color as brown .

But actual color is red if we test that rgb value here

Edited 1

<?php 

 $colors = array(
 array(1, 'Black', '#000000', '0,0,0'),
 array(2, 'Blue',  '#0000FF', '0,0,255'),
 array(3, 'Brown', '#A52A2A', '165,42,42'),    
 array(4, 'Cream', '#FFFFCC', '255,255,204'),   
 array(5, 'Green', '#008000', '0,128,0'),        
 array(6, 'Grey',  '#808080', '128,128,128'),
 array(7, 'Yellow', '#FFFF00', '255,255,0'),
 array(8, 'Orange', '#FFA500', '255,165,0'),          
 array(9, 'Pink', '#FFC0CB', '255,192,203'), 
 array(11, 'Red', '#FF0000', '255,0,0'),  
 array(10, 'Purple', '#800080', '128,0,128'),
 array(12, 'Tan', '#d2b48c', '210,180,140'),
 array(13, 'Turquoise', '#40E0D0', '64,224,208'),
 array(14, 'White', '#FFFFFF', '255,255,255')
   );



 $miDist = 99999999999999999 ;
 $loc = 0 ; 



 $findColor = RGBtoHSV(72, 70, 68);
 for( $i = 0 ; $i < 14 ; $i++){
  $string =  $colors[$i][3];
  $pieces = explode(',' , $string);
  $r0 = $pieces[0];
  $g0 = $pieces[1];
  $b0 = $pieces[2];

  $storedColor = RGBtoHSV($r0,$g0,$b0);

 echo $storedColor[0] ."-" . $storedColor[1] ;
 // distance between colors (regardless of intensity)


 $d = sqrt( ($storedColor[0]-$findColor[0])
      *($storedColor[0]-$findColor[0])
      + 
      ($storedColor[1]-$findColor[1])
      *($storedColor[1]-$findColor[1])



      );



  echo $colors[$i][1] ."=" .$d;
  //echo $d ;
     if( $miDist >= $d )
       {
       $miDist = $d;
       $loc = $i ;    
       } 
      echo "<br>" ;


 }

 echo $colors[$loc][1];









 function RGBtoHSV($R, $G, $B)    // RGB values:    0-255, 0-255, 0-255
 {                                // HSV values:    0-360, 0-100, 0-100
// Convert the RGB byte-values to percentages
$R = ($R / 255);
$G = ($G / 255);
$B = ($B / 255);

// Calculate a few basic values, the maximum value of R,G,B, the
//   minimum value, and the difference of the two (chroma).
$maxRGB = max($R, $G, $B);
$minRGB = min($R, $G, $B);
$chroma = $maxRGB - $minRGB;

// Value (also called Brightness) is the easiest component to calculate,
//   and is simply the highest value among the R,G,B components.
// We multiply by 100 to turn the decimal into a readable percent value.
$computedV = 100 * $maxRGB;

// Special case if hueless (equal parts RGB make black, white, or grays)
// Note that Hue is technically undefined when chroma is zero, as
//   attempting to calculate it would cause division by zero (see
//   below), so most applications simply substitute a Hue of zero.
// Saturation will always be zero in this case, see below for details.
if ($chroma == 0)
    return array(0, 0, $computedV);

// Saturation is also simple to compute, and is simply the chroma
//   over the Value (or Brightness)
// Again, multiplied by 100 to get a percentage.
$computedS = 100 * ($chroma / $maxRGB);

// Calculate Hue component
// Hue is calculated on the "chromacity plane", which is represented
//   as a 2D hexagon, divided into six 60-degree sectors. We calculate
//   the bisecting angle as a value 0 <= x < 6, that represents which
//   portion of which sector the line falls on.
if ($R == $minRGB)
    $h = 3 - (($G - $B) / $chroma);
elseif ($B == $minRGB)
    $h = 1 - (($R - $G) / $chroma);
else // $G == $minRGB
    $h = 5 - (($B - $R) / $chroma);

// After we have the sector position, we multiply it by the size of
//   each sector's arc (60 degrees) to obtain the angle in degrees.
$computedH = 60 * $h;

return array($computedH, $computedS, $computedV);
 }

 ?>

解决方案

So if you want distance between 2 colors (r0,g0,b0) and (r1,g1,b1) to detect closest color regardless of its intensity (that is what base color means in this case) you should

  1. Normalize color vectors to common size
  2. Compute distance
  3. Scale the result back

// variables
int r0,g0,b0,c0;
int r1,g1,b1,c1,d;
// color sizes
c0=sqrt(r0*r0+g0*g0+b0*b0);
c1=sqrt(r1*r1+g1*g1+b1*b1);
// distance between normalized colors
d = sqrt((r0*c1-r1*c0)^2 + (g0*c1-g1*c0)^2 + (b0*c1-b1*c0)^2) / (c0*c1);

This approach will get unstable comparing dark colors so you can add simple condition like

if (c0<treshold)  color is dark 

And compare such color only agains the shades of gray or return unknown color. Our vision works similarly we can not safely recognize dark colors ...

Anyway HSV color space is much better for color comparison because it better resembles human color recognition. so convert RGB -> HSV and compute distance ignoring the value V which is the intensity of color...

In HSV you need to handle H as a periodic full circle value so the change can be only half of circle big. S tells you if it is color or grayscale which has to be handled separately and V is the intensity.

// variables
int h0,s0,v0;
int h1,s1,v1,d,q;
q=h1-h0;
if (q<-128) q+=256; // use shorter angle
if (q>+128) q-=256; // use shorter angle
         q*=q; d =q;
q=s1-s0; q*=q; d+=q;
if (s0<32)          // grayscales
    {
    d=0;            // ignore H,S
    if (s1>=32) continue; // compare only to gray-scales so ignore this color
    }
q=v1-v0; q*=q; d+=q;

Some things to compare ...

You should do a visual check of your source to actually see what is happening otherwise you will go in circles without any results. For example I just now coded this in C++/VCL/mine image class:

union color
    {
    DWORD dd; WORD dw[2]; byte db[4];
    int i; short int ii[2];
    color(){}; color(color& a){ *this=a; }; ~color(){}; color* operator = (const color *a) { dd=a->dd; return this; }; /*color* operator = (const color &a) { ...copy... return this; };*/
    };

    enum{ // this is inside my picture:: class
        _x=0,   // dw
        _y=1,

        _b=0,   // db
        _g=1,
        _r=2,
        _a=3,

        _v=0,   // db
        _s=1,
        _h=2,
        };

void rgb2hsv(color &c)
    {
    double r,g,b,min,max,del,h,s,v,dr,dg,db;
    r=c.db[picture::_r]; r/=255.0;
    g=c.db[picture::_g]; g/=255.0;
    b=c.db[picture::_b]; b/=255.0;
    min=r; if (min>g) min=g; if(min>b) min=b;
    max=r; if (max<g) max=g; if(max<b) max=b;
    del=max-min;
    v=max;
    if (del<=1e-10) { h=0; s=0; }   // grayscale
    else{
        s=del/max;
        dr=(((max-r)/6.0)+(del/2.0))/del;
        dg=(((max-g)/6.0)+(del/2.0))/del;
        db=(((max-b)/6.0)+(del/2.0))/del;
        if      (fabs(r-max)<1e-10) h=db-dg;
        else if (fabs(g-max)<1e-10) h=(1.0/3.0)+dr-db;
        else if (fabs(b-max)<1e-10) h=(2.0/3.0)+dg-dr;
        if (h<0.0) h+=1.0;
        if (h>1.0) h-=1.0;
        }
    c.db[picture::_h]=h*255.0;
    c.db[picture::_s]=s*255.0;
    c.db[picture::_v]=v*255.0;
    }

void hsv2rgb(color &c)
    {
    int i;
    double r,g,b,h,s,v,vh,v1,v2,v3,f;
    h=c.db[picture::_h]; h/=255.0;
    s=c.db[picture::_s]; s/=255.0;
    v=c.db[picture::_v]; v/=255.0;
    if (s<=1e-10) { r=v; g=v; b=v; }    // grayscale
    else{
        vh=h*6.0;
        if (vh>=6.0) vh=0.0;
        f=floor(vh); i=f;
        v1=v*(1.0-s);
        v2=v*(1.0-s*(    vh-f));
        v3=v*(1.0-s*(1.0-vh+f));
             if (i==0) { r=v ; g=v3; b=v1; }
        else if (i==1) { r=v2; g=v ; b=v1; }
        else if (i==2) { r=v1; g=v ; b=v3; }
        else if (i==3) { r=v1; g=v2; b=v ; }
        else if (i==4) { r=v3; g=v1; b=v ; }
        else           { r=v ; g=v1; b=v2; }
        }
    c.db[picture::_r]=r*255.0;
    c.db[picture::_g]=g*255.0;
    c.db[picture::_b]=b*255.0;
    }

struct _base_color
    {
    DWORD rgb,hsv;
    const char *nam;
    _base_color(DWORD _rgb,const char *_nam){ nam=_nam; rgb=_rgb; color c; c.dd=rgb; rgb2hsv(c); hsv=c.dd; }

    _base_color(){};
    _base_color(_base_color& a){};
    ~_base_color(){};
    _base_color* operator = (const _base_color *a){};
    //_base_color* operator = (const _base_color &a);
    };
const _base_color base_color[]=
    {
    //          0x00RRGGBB
    _base_color(0x00000000,"Black"),
    _base_color(0x00808080,"Gray"),
    _base_color(0x00C0C0C0,"Silver"),
    _base_color(0x00FFFFFF,"White"),
    _base_color(0x00800000,"Maroon"),
    _base_color(0x00FF0000,"Red"),
    _base_color(0x00808000,"Olive"),
    _base_color(0x00FFFF00,"Yellow"),
    _base_color(0x00008000,"Green"),
    _base_color(0x0000FF00,"Lime"),
    _base_color(0x00008080,"Teal"),
    _base_color(0x0000FFFF,"Aqua"),
    _base_color(0x00000080,"Navy"),
    _base_color(0x000000FF,"Blue"),
    _base_color(0x00800080,"Purple"),
    _base_color(0x00FF00FF,"Fuchsia"),
    _base_color(0x00000000,"")
    };

void compare_colors()
    {
    picture pic0;
    int h0,s0,v0,h1,s1,v1,x,y,i,d,i0,d0;
    int r0,g0,b0,r1,g1,b1,c0,c1,q,xx;
    color c;
    pic0.resize(256*4,256);
    pic0.pf=_pf_rgba;
    for (y=0;y<256;y++)
     for (x=0;x<256;x++)
        {
        // get color from image
        c=pic0.p[y][x];
        xx=x;
        r0=c.db[picture::_r];
        g0=c.db[picture::_g];
        b0=c.db[picture::_b];
        rgb2hsv(c);
        h0=c.db[picture::_h];
        s0=c.db[picture::_s];
        v0=c.db[picture::_v];
        // naive RGB
        xx+=256;
        for (i0=-1,d0=-1,i=0;base_color[i].nam[0];i++)
            {
            // compute distance
            c.dd=base_color[i].rgb;
            r1=c.db[picture::_r];
            g1=c.db[picture::_g];
            b1=c.db[picture::_b];
            // no need for sqrt
            d=((r1-r0)*(r1-r0))+((g1-g0)*(g1-g0))+((b1-b0)*(b1-b0));
            // remember closest match
            if ((d0<0)||(d0>d)) { d0=d; i0=i; }
            }
        pic0.p[y][xx].dd=base_color[i0].rgb;
        // normalized RGB
        xx+=256;
        c0=sqrt((r0*r0)+(g0*g0)+(b0*b0));
        if (!c0) i0=0; else
         for (i0=-1,d0=-1,i=1;base_color[i].nam[0];i++)
            {
            // compute distance
            c.dd=base_color[i].rgb;
            r1=c.db[picture::_r];
            g1=c.db[picture::_g];
            b1=c.db[picture::_b];
            c1=sqrt((r1*r1)+(g1*g1)+(b1*b1));
            // no need for sqrt
            q=((r0*c1)-(r1*c0))/4; q*=q; d =q;
            q=((g0*c1)-(g1*c0))/4; q*=q; d+=q;
            q=((b0*c1)-(b1*c0))/4; q*=q; d+=q;
            d/=c1*c0; d<<=16; d/=c1*c0;
            // remember closest match
            if ((d0<0)||(d0>d)) { d0=d; i0=i; }
            }
        pic0.p[y][xx].dd=base_color[i0].rgb;
        // HSV
        xx+=256;
        for (i0=-1,d0=-1,i=0;base_color[i].nam[0];i++)
            {
            // compute distance
            c.dd=base_color[i].hsv;
            h1=c.db[picture::_h];
            s1=c.db[picture::_s];
            v1=c.db[picture::_v];
            // no need for sqrt
            q=h1-h0;
            if (q<-128) q+=256; // use shorter angle
            if (q>+128) q-=256; // use shorter angle
                     q*=q; d =q;
            q=s1-s0; q*=q; d+=q;
            if (s0<32)          // grayscales
                {
                d=0;            // ignore H,S
                if (s1>=32) continue; // compare only to grayscales
                }
            q=v1-v0; q*=q; d+=q;
            // remember closest match
            if ((d0<0)||(d0>d)) { d0=d; i0=i; }
            }
        pic0.p[y][xx].dd=base_color[i0].rgb;
        }
    pic0.bmp->Canvas->Brush->Style=bsClear;
    pic0.bmp->Canvas->Font->Color=clBlack;
    x =256; pic0.bmp->Canvas->TextOutA(5+x,5,"Naive RGB");
    x+=256; pic0.bmp->Canvas->TextOutA(5+x,5,"Normalized RGB");
    x+=256; pic0.bmp->Canvas->TextOutA(5+x,5,"HSV");
    pic0.bmp->Canvas->Brush->Style=bsSolid;
    //pic0.save("colors.png");
    }

You can ignore the pic0 stuff it is only pixel access to image. I added few quirks in RGB distance equation to shift the sub-results so they fit inside 32 bit ints to avoid overflows. As an input I use this image:

And for each pixel is then corresponding base color from LUT found. This is the result:

On the Left is the source image, then naive RGB comparison, then Normalized RGB comparison (can not distinguish between the same color shades) and on the right the HSV comparison.

For the normalized RGB the colors found are alway the first in the LUT from the same color but different intensities. The comparison selects the darker only because they are first in the LUT.

As I mentioned before dark and grayscale colors are problem with this and should be handled separately. If you got similar results and still wrong detection then you need to add more base colors to cover the gaps. If you do not have similar results at all then you got most likely a problem with:

  1. wrong HSV or RGB ranges mine are <0,255> for each channel
  2. overflow somewhere

    when multiplying numbers the bits used are summed !!! So

    8bit * 8bit * 8bit * 8bit = 32bit
    

    and if the numbers are signed you re in trouble ... if on 32bit variables just like me in the example above so you need to shift the range a bit or use FPU on <0.0,1.0> intervals.

Just to be sure I added also mine HSV/RGB conversions in case you got some problem there.

And here the original HSV generated conversion:

这篇关于RGB值基色名称的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!

查看全文
登录 关闭
扫码关注1秒登录
发送“验证码”获取 | 15天全站免登陆