简体   繁体   中英

Converting RGB Colors to closest ACI Color in C#

I am currently writing a Programm that interacts with dxf files. Therefore I need a Routine that takes RGB Color Values and gives back the closest Color in the AutoCAD Color Index (ACI)

Has anybody some Code or an example how to do that? It would be nice if it was in C#, but it is not necessary.

Thanks in advance.

Take the RGB values of all the ACI colors from some source (for example http://www.jtbworld.com/lisp/DisplayColorProperties.htm ) and create an array of ACI colors. To get an ACI color by index, simply pick the color from that list.

To do a "closest" match backwards lookup from RGB, simply do a pass over that array and return the color with minimum distance (for example by checking the squared distances of the 3 color channels: if your color is r,g,b and the aci color is R,G,B then the distance is

dist = (r-R)*(r-R) + (g-G)*(g-G) + (b-B)*(b-B);

Whichever color in the ACI array that has the smallest dist, is the closest match to r,g,b.

Edit: as has been pointed out: RGB distance isn't good as a visual/perceptive difference. To match for visual difference, convert to HSV/HSL or if you are really ambitious a more exotic color space like CIE XYZ where "distance" closely represents similarity. There are good libraries these days for color space conversion such as Colorful https://www.nuget.org/packages/Colourful/

这是将 RGB 转换为 ACI 的方法

var color = Autodesk.AutoCAD.Colors.Color(r, g, b);

I am not sure if this thread/question is still valid but I have also been looking for some way to convert a color to ACI on the internet, but failed. In my case, I need a method that preferably avoids external libraries and CAD functions.

I cannot help with C#. I normally work with Lazarus/Free Pascal and after lots of trial I came down with a function that seems to be working quite well for me. So I am posting my codes here in case they can be helpful to you or to someone else.

My Codes are as follow:

Function RGB2ACIDXFColor(MyColor : TColor) : Integer ;
Var
   OldCol, LowR, MidR, HiR : String ;
   RCol, GCol, BCol, LowCol, MidCol, HiCol : Integer ;
   StPt, HRatio, VRatio, Hemis : Integer ;
Begin
Result := 10 ;
{Break Color Component (BGR Color)}
{IntToHex & Hex2Dec are functions from Lazarus Libraries}
OldCol := IntToHex(MyColor,6) ;    
BCol := Hex2Dec(Copy(OldCol,1,2)) ;
GCol := Hex2Dec(Copy(OldCol,3,2)) ;
RCol := Hex2Dec(Copy(OldCol,5,2)) ;

{Find Color Component Priorities}
LowCol := RCol ;
LowR := 'R' ;
If (GCol < LowCol) Then
Begin
     LowCol := GCol ;
     LowR := 'G' ;
End; //If
If (BCol < LowCol) Then
Begin
     LowCol := BCol ;
     LowR := 'B' ;
End; //If

HiCol := RCol ;
HiR := 'R' ;
If (GCol > HiCol) Then
Begin
     HiCol := GCol ;
     HiR := 'G' ;
End; //If
If (BCol > HiCol) Then
Begin
     HiCol := BCol ;
     HiR := 'B' ;
End; //If

MidCol := GCol ;
MidR := 'G' ;
If ((HiR = 'G') AND (LowR = 'R')) OR
   ((HiR = 'R') AND (LowR = 'G')) Then
Begin
     MidCol := BCol ;
     MidR := 'B' ;
End; //If
If ((HiR = 'G') AND (LowR = 'B')) OR
   ((HiR = 'B') AND (LowR = 'G')) Then
Begin
     MidCol := RCol ;
     MidR := 'R' ;
End; //If

{Refer to CAD color table}
{Find Color Row}
VRatio := Round((5 * (255 - HiCol)) / 255) ;
VRatio *= 2 ;
{Find Color Hemisphere}
If (LowCol = 0) Then Hemis := 0 Else Hemis := 1 ;

{Find Color Start Column And Incrementation}
If (LowR = 'B') Then
Begin
     HRatio := Round((8 * GCol) / (GCol + RCol)) ;
     Result := 10 ;
End; //If
If (LowR = 'G') Then
Begin
     HRatio := Round((8 * RCol) / (RCol + BCol)) ;
     Result := 170 ;
End; //If
If (LowR = 'R') Then
Begin
     HRatio := Round((8 * BCol) / (BCol + GCol)) ;
     Result := 90 ;
End; //If

HRatio *= 10 ;
Result += HRatio + VRatio + Hemis ;
If (Result > 249) Then Result -= 240 ;
End; //Sub

I am sure you will be able to translate that into C#, and hope that this will be useful to somebody.

Cheers,

J-Eric J.

I wouldn't bother with a hard-coded array of ACI colors as Anders proposes. You can get an AutoCAD Color object from each legal index and extract the RGB values from that as a System.Drawing.Color with the ColorValue property.

Here's a full solution based on the rest of Anders' response, substituting Math.Pow(r - R, 2) for (r - R)*(r - R) since it seems to me to more clearly express the Pythagorean intent of the "distance" calculation.

byte r = 1, g = 203, b = 103; // input color
double minDist = double.MaxValue;
short match = 0; // this will end up with our answer
for (short i = 1; i <= 255; ++i)
{
    var color = Autodesk.AutoCAD.Colors.Color.FromColorIndex(ColorMethod.ByAci, i);
    System.Drawing.Color rgb = color.ColorValue;
    double dist =
        Math.Pow(r - rgb.R, 2) +
        Math.Pow(g - rgb.G, 2) +
        Math.Pow(b - rgb.B, 2);
    if (dist < minDist)
    {
        minDist = dist;
        match = i;
    }
}

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM