如何实现PictureBox1像素色与PictureBox2色尺匹配高亮及颜色名称提取
问题解决方案
一、修复PictureBox2颜色匹配高亮问题
常见问题根源
- 直接使用颜色精确相等匹配,忽略了图像压缩或色尺本身的细微色差
- 绘制高亮时未正确清理上一次的绘制痕迹,导致残留黑色块
- 未处理PictureBox的
SizeMode缩放,获取的坐标与实际图像像素不匹配
修正后的实现代码
首先在窗体构造函数中开启双缓冲避免绘制闪烁:
public YourForm() { InitializeComponent(); pictureBox2.DoubleBuffered = true; }
然后处理PictureBox1的鼠标移动事件,获取目标颜色并触发PictureBox2重绘:
private Color? currentTargetColor = null; private void pictureBox1_MouseMove(object sender, MouseEventArgs e) { // 校验鼠标位置是否在PictureBox1范围内 if (e.X < 0 || e.X >= pictureBox1.Width || e.Y < 0 || e.Y >= pictureBox1.Height) { currentTargetColor = null; pictureBox2.Invalidate(); return; } Bitmap targetBmp = pictureBox1.Image as Bitmap; if (targetBmp == null) return; // 计算实际图像像素坐标(适配SizeMode.Zoom) float scaleX = (float)targetBmp.Width / pictureBox1.Width; float scaleY = (float)targetBmp.Height / pictureBox1.Height; int imgX = (int)(e.X * scaleX); int imgY = (int)(e.Y * scaleY); // 确保坐标不超出图像边界 imgX = Math.Clamp(imgX, 0, targetBmp.Width - 1); imgY = Math.Clamp(imgY, 0, targetBmp.Height - 1); currentTargetColor = targetBmp.GetPixel(imgX, imgY); pictureBox2.Invalidate(); } // 处理PictureBox2的Paint事件,绘制高亮区域 private void pictureBox2_Paint(object sender, PaintEventArgs e) { if (currentTargetColor == null) return; Color targetColor = currentTargetColor.Value; Bitmap rulerBmp = pictureBox2.Image as Bitmap; if (rulerBmp == null) return; // 设置颜色匹配容差,解决细微色差问题 int tolerance = 10; // 遍历色尺像素,匹配容差内的颜色并绘制半透明高亮 for (int y = 0; y < rulerBmp.Height; y++) { for (int x = 0; x < rulerBmp.Width; x++) { Color pixel = rulerBmp.GetPixel(x, y); if (Math.Abs(pixel.R - targetColor.R) <= tolerance && Math.Abs(pixel.G - targetColor.G) <= tolerance && Math.Abs(pixel.B - targetColor.B) <= tolerance) { e.Graphics.FillRectangle(new SolidBrush(Color.FromArgb(128, Color.Yellow)), x, y, 1, 1); } } } }
核心修复点
- 用颜色容差匹配替代精确相等,适配图像色差
- 通过
Invalidate()触发重绘,自动清理上一次绘制内容 - 处理
SizeMode缩放,确保获取的是实际图像像素坐标 - 使用半透明高亮色,保留原色尺可见性,避免黑色块残留
二、提取PictureBox2色尺的颜色名称列表
基于遍历KnownColor的GetNames方法,以下是提取色尺所有唯一颜色名称的实现:
// 遍历KnownColor生成颜色-名称字典 private Dictionary<Color, string> GetKnownColorNames() { var colorDict = new Dictionary<Color, string>(); foreach (KnownColor knownColor in Enum.GetValues(typeof(KnownColor))) { Color color = Color.FromKnownColor(knownColor); // 排除系统颜色和透明色 if (!color.IsSystemColor && color.A == 255) { if (!colorDict.ContainsKey(color)) { colorDict.Add(color, color.Name); } } } return colorDict; } // 提取色尺中的所有唯一颜色名称 private List<string> ExtractRulerColorNames() { Bitmap rulerBmp = pictureBox2.Image as Bitmap; if (rulerBmp == null) return new List<string>(); var knownColors = GetKnownColorNames(); var uniqueNames = new HashSet<string>(); int matchTolerance = 5; for (int y = 0; y < rulerBmp.Height; y++) { for (int x = 0; x < rulerBmp.Width; x++) { Color pixel = rulerBmp.GetPixel(x, y); // 查找容差内匹配的已知颜色 var matchedColor = knownColors.FirstOrDefault(kvp => Math.Abs(kvp.Key.R - pixel.R) <= matchTolerance && Math.Abs(kvp.Key.G - pixel.G) <= matchTolerance && Math.Abs(kvp.Key.B - pixel.B) <= matchTolerance); if (!string.IsNullOrEmpty(matchedColor.Value)) { uniqueNames.Add(matchedColor.Value); } } } return uniqueNames.ToList(); } // 调用示例(如按钮点击事件) private void btnExtractNames_Click(object sender, EventArgs e) { List<string> colorNames = ExtractRulerColorNames(); textBox1.Text = string.Join(Environment.NewLine, colorNames); }
关键说明
- 使用
HashSet自动去重,避免重复颜色名称 - 加入匹配容差,确保色尺中近似颜色能匹配到标准命名颜色
- 过滤系统颜色和透明色,只保留通用命名颜色
内容的提问来源于stack exchange,提问作者Daniel Lip
相关产品推荐
相关产品推荐

