如何将Color转换为KnownColor名称?自定义方法返回Unknown
问题:ARGB颜色转KnownColor名称始终返回Unknown
我的List<Color>集合里存了39000多种从图片提取的颜色,例如第一个元素是{Name = ffcecece, ARGB = (255, 206, 206, 206)}。我想把这些颜色的ARGB值转换为对应的KnownColor名称(比如Blue、Red),但自定义的GetKnownColorName方法始终返回"Unknown"。
相关代码
点击事件代码
private void label1_Click(object sender, EventArgs e) { if(colorsAdded) { for(int i =0; i < colors.Count; i++) { colorsNames.Add(GetKnownColorName(colors[i])); } } }
转换方法代码
private string GetKnownColorName(Color color) { foreach (KnownColor knownColor in Enum.GetValues(typeof(KnownColor))) { Color knownColorValue = Color.FromKnownColor(knownColor); if (knownColorValue.ToArgb() == color.ToArgb()) { return knownColor.ToString(); } } return "Unknown"; }
完整代码
using System; using System.Collections.Generic; using System.Drawing; using System.Drawing.Imaging; using System.Windows.Forms; namespace Colors_Scanner { public partial class Form1 : Form { private Bitmap imageBitmap; private Bitmap imageBitmapComparison; private Color currentColor; private List<string> colorsNames = new List<string>(); private List<Color> colors = new List<Color>(); private Bitmap bmp; private bool colorsAdded = false; // Flag to track if colors have been added public Form1() { InitializeComponent(); // Load images into PictureBox controls LoadImages(); // Initialize the bitmap for pictureBox2 bmp = new Bitmap(pictureBox2.Image); } private void LoadImages() { // Load an image into the PictureBox control imageBitmap = new Bitmap(@"D:\Ffmpeg Capture\radar1.jpg"); // Replace with your image pictureBox1.Size = imageBitmap.Size; pictureBox1.Image = imageBitmap; imageBitmapComparison = new Bitmap(@"D:\Ffmpeg Capture\colorslevels1.jpg"); // Replace with your ruler image pictureBox2.Size = imageBitmapComparison.Size; pictureBox2.Image = imageBitmapComparison; } private void pictureBox1_MouseMove(object sender, MouseEventArgs e) { if (IsMouseWithinPictureBoxBounds(e.Location, pictureBox1)) { UpdateColorLabel(e.Location); currentColor = GetColorAtCursor(e.Location, imageBitmap); pictureBox2.Invalidate(); } } private void pictureBox2_Paint(object sender, PaintEventArgs e) { Pen pen = new Pen(currentColor); e.Graphics.DrawRectangle(pen, new Rectangle(0, 0, 3, 3)); if (!colorsAdded) { // Add colors from pictureBox2 only once AddColorsFromPictureBox2(); colorsAdded = true; // Set the flag to true after adding colors } } private List<Color> AddColorsFromPictureBox2() { if (bmp != null) { for (int i = 0; i < bmp.Width; i++) { if (i == bmp.Width - 1) { break; } for (int j = 0; j < bmp.Height; j++) { Color c = bmp.GetPixel(i, j); // You can add the color to your list here colors.Add(c); } } } return colors; } private void UpdateColorLabel(Point location) { Color color = GetColorAtCursor(location, imageBitmap); label1.Text = $"RGB: ({color.R}, {color.G}, {color.B})"; label1.BackColor = color; } private Color GetColorAtCursor(Point location, Bitmap bitmap) { int x = location.X; int y = location.Y; if (IsMouseWithinPictureBoxBounds(location, pictureBox1)) { BitmapData bmpData = bitmap.LockBits(new Rectangle(0, 0, bitmap.Width, bitmap.Height), ImageLockMode.ReadOnly, bitmap.PixelFormat); try { int bytesPerPixel = Bitmap.GetPixelFormatSize(bitmap.PixelFormat) / 8; IntPtr ptr = bmpData.Scan0; int pixelOffset = y * bmpData.Stride + x * bytesPerPixel; byte b = System.Runtime.InteropServices.Marshal.ReadByte(ptr, pixelOffset); byte g = System.Runtime.InteropServices.Marshal.ReadByte(ptr, pixelOffset + 1); byte r = System.Runtime.InteropServices.Marshal.ReadByte(ptr, pixelOffset + 2); return Color.FromArgb(r, g, b); } finally { bitmap.UnlockBits(bmpData); } } return Color.Black; // Default color if outside of PictureBox bounds } private bool IsMouseWithinPictureBoxBounds(Point location, PictureBox pictureBox) { return location.X >= 0 && location.X < pictureBox.Width && location.Y >= 0 && location.Y < pictureBox.Height; } private void label1_Click(object sender, EventArgs e) { if(colorsAdded) { for(int i =0; i < colors.Count; i++) { colorsNames.Add(GetKnownColorName(colors[i])); } } } private string GetKnownColorName(Color color) { foreach (KnownColor knownColor in Enum.GetValues(typeof(KnownColor))) { Color knownColorValue = Color.FromKnownColor(knownColor); if (knownColorValue.ToArgb() == color.ToArgb()) { return knownColor.ToString(); } } return "Unknown"; } } }
原因分析
- KnownColor数量有限:KnownColor枚举仅包含约140种预定义颜色,其中还包括大量随系统主题变化的系统颜色(如
ActiveBorder、Window),而图片中的颜色是24位/32位真彩色,理论上有几百万种可能,大部分颜色本来就不在KnownColor范围内。 - 系统颜色干扰:原方法遍历了所有KnownColor,包括系统主题色,这些颜色的ARGB值会随系统设置变化,无法与固定的图片颜色匹配。
- 效率低下:遍历所有KnownColor(约140次)匹配每个颜色,对于39000个颜色来说,会产生大量不必要的计算。
解决方案
1. 只匹配固定命名颜色(排除系统颜色)
修改转换方法,跳过系统颜色,只匹配固定ARGB值的命名颜色(如Red、Blue):
private string GetKnownColorName(Color color) { foreach (KnownColor knownColor in Enum.GetValues(typeof(KnownColor))) { Color knownColorValue = Color.FromKnownColor(knownColor); // 跳过系统颜色,只匹配固定命名颜色 if (knownColorValue.IsSystemColor) continue; if (knownColorValue.ToArgb() == color.ToArgb()) { return knownColor.ToString(); } } return "Unknown"; }
2. 预缓存颜色字典提升效率
提前将所有固定命名颜色的ARGB值和名称存入字典,后续查询直接从字典获取,大幅提升效率:
// 类级别的字典缓存 private static readonly Dictionary<int, string> _knownColorMap = new Dictionary<int, string>(); // 在静态构造函数中初始化缓存 static Form1() { foreach (KnownColor knownColor in Enum.GetValues(typeof(KnownColor))) { Color knownColorValue = Color.FromKnownColor(knownColor); if (!knownColorValue.IsSystemColor) { int argb = knownColorValue.ToArgb(); if (!_knownColorMap.ContainsKey(argb)) { _knownColorMap.Add(argb, knownColor.ToString()); } } } } // 修改后的转换方法 private string GetKnownColorName(Color color) { int argb = color.ToArgb(); return _knownColorMap.TryGetValue(argb, out string name) ? name : "Unknown"; }
3. 接受“Unknown”是正常结果
如果图片中的颜色确实不是预定义的KnownColor,返回"Unknown"是合理的。如果需要对这些颜色进行命名,可以考虑使用颜色近似匹配(比如计算颜色相似度,找到最接近的KnownColor)。
内容的提问来源于stack exchange,提问作者Daniel Lip
相关产品推荐
相关产品推荐

