.NET颜色选择器控件:颜色名称不显示及颜色筛选问题咨询
问题解答
一、颜色名称显示问题
原因分析
.NET的Color.IsKnownColor仅对系统预定义的颜色(比如Color.LightGreen、Color.Red等)返回true,这些颜色属于KnownColor枚举成员,总数仅140余种。而从图片中提取的RGB颜色几乎都是非预定义值,因此会显示RGB数值而非名称。
解决方案
要给任意RGB颜色匹配名称,有两种可行思路:
- 精确匹配预定义颜色:提前创建字典,存储所有已知颜色的RGB值与名称的映射,提取颜色后先查字典找完全匹配项。
- 近似匹配最接近颜色:计算提取颜色与所有已知颜色的色差,返回最接近的颜色名称。
代码实现示例
添加颜色名称获取的辅助方法:
private string GetColorName(Color color) { // 优先检查是否为系统预定义颜色 if (color.IsKnownColor) { return color.Name; } // 扩展预定义颜色映射(可添加更多HTML/自定义颜色) var colorMap = new Dictionary<Color, string> { { Color.FromArgb(255, 240, 248, 255), "AliceBlue" }, { Color.FromArgb(255, 250, 235, 215), "AntiqueWhite" }, { Color.FromArgb(255, 0, 255, 255), "Cyan" }, // 可补充所有KnownColor对应的RGB与名称 }; // 查找色差最小的近似颜色 var closestColor = colorMap.OrderBy(c => ColorDistance(c.Key, color)).FirstOrDefault(); if (!string.IsNullOrEmpty(closestColor.Value)) { return closestColor.Value; } // 无匹配时返回RGB值 return $"RGB: {color.R}, {color.G}, {color.B}"; } // 计算RGB颜色间的欧几里得色差 private double ColorDistance(Color c1, Color c2) { int rDiff = c1.R - c2.R; int gDiff = c1.G - c2.G; int bDiff = c1.B - c2.B; return Math.Sqrt(rDiff * rDiff + gDiff * gDiff + bDiff * bDiff); }
修改CreateColorGrid中的鼠标悬浮事件逻辑,替换原有的颜色名称获取代码:
string colorName = GetColorName(color); toolTip1.SetToolTip(currentLabel, colorName);
二、仅显示肉眼可识别的颜色
原因分析
当前代码直接提取图片中出现频率最高的256种颜色,但图片中存在大量因抗锯齿、压缩产生的细微色差像素,这些差异肉眼无法分辨,导致显示的颜色数量远多于实际可见的种类。
解决方案
需要对提取的颜色进行颜色量化,将相似颜色合并为一种。常用方法包括:
- 简单阈值合并:设定色差阈值,将差值小于阈值的颜色视为同一种。
- 中位数切割法:专业的颜色压缩算法,适合将大量颜色提炼为指定数量的代表性颜色。
- K-Means聚类:通过聚类算法分组颜色,每组取中心色作为代表。
代码实现示例(简单阈值合并)
修改ProcessImage方法,在统计颜色后先合并相似颜色:
private void ProcessImage(Bitmap bmp) { var colorCounts = new Dictionary<int, int>(); // 原像素读取逻辑保持不变,此处省略... // 将统计结果转换为(Color, Count)列表 var allColors = colorCounts.Select(kvp => new { Color = Color.FromArgb(kvp.Key), Count = kvp.Value }).ToList(); // 合并相似颜色(阈值可调整,值越大合并的颜色越多) int colorThreshold = 30; var mergedColors = new List<(Color Color, int TotalCount)>(); foreach (var item in allColors) { bool merged = false; foreach (var mergedItem in mergedColors.ToList()) { if (ColorDistance(item.Color, mergedItem.Color) < colorThreshold) { // 合并到已有颜色,累加出现次数 mergedColors.Remove(mergedItem); mergedColors.Add((mergedItem.Color, mergedItem.TotalCount + item.Count)); merged = true; break; } } if (!merged) { mergedColors.Add((item.Color, item.Count)); } } // 按出现次数排序,取合并后的所有颜色 var topColors = mergedColors.OrderByDescending(m => m.TotalCount).Select(m => m.Color).ToArray(); UpdateColorGrid(topColors); }
若需要更精准的效果,建议使用成熟的颜色量化算法(如中位数切割法),可自行实现或引用相关开源库。
相关截图说明
- 显示颜色名称:初始状态下,网格单元格为淡绿色,鼠标悬浮时显示"LightGreen"名称
- 显示RGB值:拖拽图片后,单元格悬浮提示仅显示RGB数值,无颜色名称
- 示例图片颜色:肉眼可识别出约17种不同颜色
原显示颜色名称的方法
private void CreateColorGrid() { // Define the size of the grid and the color cells int gridSize = 20; int gridCols = 16; int gridRows = 16; // Initialize the grid with green transparent colors Color gridColor = Color.LightGreen; // Semi-transparent green for (int i = 0; i < gridCols * gridRows; i++) { Label lbl = new Label { AutoSize = false, Size = new Size(gridSize, gridSize), BackColor = gridColor, BorderStyle = BorderStyle.Fixed3D, Location = new Point(gridSize * (i % gridCols), gridSize * (i / gridCols)), Tag = i // Store the index or color information in the Tag property }; // Set up the tooltip for each label lbl.MouseEnter += (sender, e) => { Label currentLabel = sender as Label; if (currentLabel != null) { // Get the name of the color if it is known, otherwise show the RGB value Color color = currentLabel.BackColor; string colorName = color.IsKnownColor ? color.Name : $"RGB: {color.R}, {color.G}, {color.B}"; toolTip1.SetToolTip(currentLabel, colorName); } }; lbl.Click += (s, e) => { Label currentLabel = s as Label; if (currentLabel != null) { SelectedColor = currentLabel.BackColor; // If you want to show a tooltip when a color is selected toolTip1.Show($"Selected: {SelectedColor.Name}", currentLabel, 1000); // Show for 1 second } }; // Add the label to the UserControl's Controls collection this.Controls.Add(lbl); } }
完整原代码
using System; using System.Collections.Generic; using System.ComponentModel; using System.Data; using System.Drawing; using System.Drawing.Imaging; using System.Linq; using System.Runtime.InteropServices; using System.Text; using System.Threading.Tasks; using System.Windows.Forms; namespace ColorAnalyzer { public partial class ImageColorPciker : UserControl { public Color SelectedColor { get; private set; } private ToolTip toolTip1 = new ToolTip(); public ImageColorPciker() { InitializeComponent(); this.AllowDrop = true; this.DragEnter += ImageColorPciker_DragEnter; this.DragDrop += ImageColorPciker_DragDrop; // Create the color grid on initialization CreateColorGrid(); } private void ImageColorPciker_DragEnter(object sender, DragEventArgs e) { if (e.Data.GetDataPresent(DataFormats.FileDrop)) { e.Effect = DragDropEffects.Copy; } } private void ImageColorPciker_DragDrop(object sender, DragEventArgs e) { var files = (string[])e.Data.GetData(DataFormats.FileDrop); if (files.Length > 0) { string filename = files[0]; using (Image image = Image.FromFile(filename)) { ProcessImage((Bitmap)image); } } } private void ProcessImage(Bitmap bmp) { var colorCounts = new Dictionary<int, int>(); Rectangle rect = new Rectangle(0, 0, bmp.Width, bmp.Height); BitmapData data = bmp.LockBits(rect, ImageLockMode.ReadOnly, PixelFormat.Format32bppArgb); try { int bytesPerPixel = 4; int byteCount = bmp.Width * bmp.Height * bytesPerPixel; byte[] pixels = new byte[byteCount]; IntPtr ptrFirstPixel = data.Scan0; Marshal.Copy(ptrFirstPixel, pixels, 0, pixels.Length); int heightInPixels = data.Height; int widthInBytes = data.Width * bytesPerPixel; for (int y = 0; y < heightInPixels; y++) { int currentLine = y * data.Stride; for (int x = 0; x < widthInBytes; x = x + bytesPerPixel) { int argb = BitConverter.ToInt32(pixels, currentLine + x); if (!colorCounts.ContainsKey(argb)) colorCounts[argb] = 1; else colorCounts[argb]++; } } } finally { bmp.UnlockBits(data); } var topColors = colorCounts.OrderByDescending(kvp => kvp.Value).Take(256).Select(kvp => Color.FromArgb(kvp.Key)).ToArray(); UpdateColorGrid(topColors); } private void UpdateColorGrid(Color[] colors) { // Assuming we have already created a grid of labels called 'colorCells' int index = 0; foreach (Label lbl in this.Controls) { if (index < colors.Length) { lbl.BackColor = colors[index]; } else { lbl.BackColor = Color.LightGreen; // Default/fallback color } index++; } } private void CreateColorGrid() { // Define the size of the grid and the color cells int gridSize = 20; int gridCols = 16; int gridRows = 16; // Initialize the grid with green transparent colors Color gridColor = Color.LightGreen; // Semi-transparent green for (int i = 0; i < gridCols * gridRows; i++) { Label lbl = new Label { AutoSize = false, Size = new Size(gridSize, gridSize), BackColor = gridColor, BorderStyle = BorderStyle.Fixed3D, Location = new Point(gridSize * (i % gridCols), gridSize * (i / gridCols)), Tag = i // Store the index or color information in the Tag property }; // Set up the tooltip for each label lbl.MouseEnter += (sender, e) => { Label currentLabel = sender as Label; if (currentLabel != null) { // Get the name of the color if it is known, otherwise show the RGB value Color color = currentLabel.BackColor; string colorName = color.IsKnownColor ? color.Name : $"RGB: {color.R}, {color.G}, {color.B}"; toolTip1.SetToolTip(currentLabel, colorName); } }; lbl.Click += (s, e) => { Label currentLabel = s as Label; if (currentLabel != null) { SelectedColor = currentLabel.BackColor; // If you want to show a tooltip when a color is selected toolTip1.Show($"Selected: {SelectedColor.Name}", currentLabel, 1000); // Show for 1 second } }; // Add the label to the UserControl's Controls collection this.Controls.Add(lbl); } } private void ImageColorPciker_Load(object sender, EventArgs e) { } } }
内容的提问来源于stack exchange,提问作者Daniel Lip
相关产品推荐
相关产品推荐

