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 neces开发者_C百科sary.
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/
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;
}
}
Here is how you convert RGB to 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.
精彩评论