十六进制颜色代码对照表:RGB/HSV互转与工程配色实战

发布时间:2026/9/17 14:23:50
十六进制颜色代码对照表:RGB/HSV互转与工程配色实战
调颜色这件事说简单也简单就是一个井号加上六位字符说麻烦也麻烦末尾两位数改一下屏幕上那块红就从警示红变成了番茄红。我从做界面、做视觉检测、再到跟硬件和印刷厂对色前后跟十六进制代码打了十几年交道最深的感受是颜色这东西光靠眼睛记不住必须落到一份能查、能抄、能复现的对照表上。这份表就是颜色十六进制代码对照表——它把看上去的颜色翻译成机器认得的数字让屏幕、代码、传感器、油墨这四套语言勉强能对上话。它解决的问题很具体设计师给一张图你得写出能落地的色号摄像头抓到一个色块你得判断它是不是你要找的那个红改配置文件的主题色你得知道#1E1E1E和#121212差在哪里。适合看这份内容的人也挺广前端和 UI 同学、做 Qt/QML 的桌面端开发者、玩 OpenCV 的视觉爱好者、做组态和上位机的工控人还有那些本来只是在网上搜电阻阻值对照表、结果被颜色码绊了一跤的电子工程师。基础好的人可以直接跳到转换公式和踩坑清单完全零基础的也能从第一章慢慢看下来把颜色这件事从头理清楚。1. 十六进制颜色到底是什么为什么工程师离不开它1.1 加法混色与 #RRGGBB 的字节真相屏幕是不会调颜料的。红、绿、蓝三个子像素各自发光靠亮度叠加骗过眼睛这套玩法叫加法混色——和拿颜料往纸上抹的减法混色是两条路这也解释了为什么屏幕上的颜色和喷墨打印机打出来的总会有点对不上。每个子像素的亮度用一个字节表示取值 0 到 255三个通道拼起来就是 24 位能表达 16 777 216 种组合这也就是大家常说的一千六百万色的来历。把这三个十进制数写成六位字符就是十六进制代码。规则特别直白#RRGGBB里前两位是红色分量中间两位绿色最后两位蓝色每两位是一个字节的十六进制写法。举个例子#FF6B35拆开就是 R255、G107、B53整体偏橙是很多 UI 里在用的活力橙。你可以把它想象成三个旋钮0 代表这个灯完全关掉255 代表全功率输出中间就是各种半亮状态。理解了这个三旋钮模型后面所有的换算、取阈值、配色调整其实都只是在这三个旋钮上做加减。还有一个细节值得提前说清楚#FF6B35里的字母大小写都合法#ff6b35一模一样。但团队协作时我强烈建议统一大写因为你在代码和配置文件里用grep搜色号的时候大小写混着写会漏掉一半结果。这个坑我吃过不止一次。1.2 十六进制而非十进制的三个理由有人会问十进制写得挺明白的为什么非得用十六进制这不是历史包袱是有实打实的工程理由。第一是长度整齐。一个字节最大值 255十进制要写 1 到 3 位0, 5, 99, 255参差不齐十六进制永远是两位00到FF六个通道字符排下来刚好对齐肉眼扫表的时候不会串行。第二是位对齐。十六进制和二进制是 4 位一组的关系F就是1111C就是1100。做颜色通道提取、位移掩码这类操作时(color 16) 0xFF拿红色分量是随手就来的事用十进制反而要先除再取余绕一大圈。第三是紧凑。二十四个比特的信息塞进六个字符写进 CSS、QML、配置文件、JSON 主题里都不占地方复制粘贴也不容易断行。做视觉检测的时候一串#FF0000写在注释里当参考值比写RGB(255,0,0)还短一点。顺便说一句对照表这种输入到输出快速映射的东西在工程界遍地都是。电子工程师翻 0603 电阻阻值对照表、PCB 线宽电流对照表、元器件库对照表硬件的老同事翻 PT1000 热电阻温度对照表纺织那边甚至还有纹板对照表。它们和颜色对照表是同一个物种把人类不擅长心算的换算关系提前算好、排好、印好用的时候一眼就查得到。理解了这一点你就明白为什么下面我会花很大篇幅把表列全——表列全了工作才快。1.3 位数差异#RGB、#RRGGBB、#RRGGBBAA 和 #AARRGGBB实际项目里你会遇到四种写法混起来是新手最容易翻车的地方。#RGB是三位缩写规则是每一位重复一次#F60等于#FF6600#09C等于#0099CC。这个写法在老的设计稿和短链图片里很常见省字符用的。有些老的解析器不支持三位写法所以正式代码里我一般直接写六位图省事反而容易出问题。#RRGGBB是六位标准写法绝大多数场景都用它。#RRGGBBAA是八位带透明度透明度放在最后两位这是 CSS Color 4 规范里的写法浏览器和现代前端工具链都支持。AA从00全透明到FF全不透明#FF000080就是半透明的红。而#AARRGGBB是另一种八位写法透明度在最前面Android、Flutter 以及不少桌面框架里用。0x80FF0000也是半透明红和上面那个#FF000080表达的是同一件事但字符顺序完全相反。这一条几乎每个跨端项目都有人踩前端给的色号直接塞进安卓代码红的透明度和位置全反了颜色看起来像咖啡色。注意动手改色之前先确认目标平台用的是哪种八位顺序别用猜的。2. 高频颜色十六进制对照表2.1 基础色与十六个命名色速查先上最常用的一批。这批色号的特点是名字和色号对应关系固定CSS 里也认这些名字所以直接写red和写#FF0000效果一样但写色号更不容易被主题覆盖影响。颜色名十六进制RGB黑 Black#0000000, 0, 0白 White#FFFFFF255, 255, 255红 Red#FF0000255, 0, 0绿 Lime#00FF000, 255, 0蓝 Blue#0000FF0, 0, 255黄 Yellow#FFFF00255, 255, 0青 Cyan#00FFFF0, 255, 255品红 Magenta#FF00FF255, 0, 255灰 Gray#808080128, 128, 128银 Silver#C0C0C0192, 192, 192栗 Maroon#800000128, 0, 0橄榄 Olive#808000128, 128, 0深绿 Green#0080000, 128, 0紫 Purple#800080128, 0, 128蓝绿 Teal#0080800, 128, 128藏青 Navy#0000800, 0, 128橙 Orange#FFA500255, 165, 0金 Gold#FFD700255, 215, 0粉 Pink#FFC0CB255, 192, 203棕 Brown#A52A2A165, 42, 42Indigo#4B008275, 0, 130天蓝 SkyBlue#87CEEB135, 206, 235番茄红 Tomato#FF6347255, 99, 71深灰 DarkGray#A9A9A9169, 169, 169这里有个很实用的观察十六个基础命名色里除了灰、银、橙、金、粉、棕这几个剩下的都是00和FF和80的组合。80就是十进制 128正好是半功率。所以那批深色版基础色都是把对应通道砍一半得到的。记住这个规律你能自己推导出一整列备用色。2.2 十六级灰阶与中性色界面里用得最多的其实不是彩色是灰。文字、边框、分割线、卡片背景全是灰阶的活。十六级灰阶对照表几乎是每个 UI 工程师的常备工具十六进制十进制灰度典型用途#0000000纯黑标题、阴影#11111117深色底主文字#22222234深色模式卡片底#33333351正文主文字#44444468次级标题#55555585副文字#666666102辅助说明文字#777777119占位符文字#888888136禁用文字#999999153弱边框、图标#AAAAAA170分割线加重#BBBBBB187普通边框#CCCCCC204浅分割线#DDDDDD221卡片描边#EEEEEE238浅色底、hover 态#FFFFFF255白底、反白文字为什么这套重复字符的灰阶这么流行因为它有两个好处一是手写方便#333三位缩写也是合法色号二是相邻两级之间的视觉差距比较均匀肉眼能分辨但仍属于同一色系不会像随机的灰度值那样跳。如果设计稿上标的是百分比灰换算也不难百分比乘以 255 取整再转十六进制。60% 灰就是 0.6 × 255 ≈ 153也就是#99999980% 灰是 204也就是#CCCCCC。95% 灰是 242 左右接近#F2F2F2很多设计系统里的浅底就是它。2.3 设计系统的语义色现在的项目很少一个色号一个色号地硬写都会先定义一套语义色。下面这张表是我这些年整理下来的通用参考左边是语义右边是几条主流设计体系的取值你可以按团队习惯选一条或者自己调。语义Material 体系常见前端框架取值用途说明主色 Primary#2196F3#2563EB按钮、链接、选中态成功 Success#4CAF50#22C55E提交成功、通过状态警告 Warning#FFC107#F59E0B提示、注意、待处理错误 Error#F44336#EF4444校验失败、危险操作信息 Info#03A9F4#1890FF中性提示、帮助说明主文字#212121#1F2329正文标题次文字#757575#646A73辅助说明浅底#F5F5F5#F7F8FA页面背景深色底#121212#1E1E1E夜间模式背景关于深色底有两个经验值特别值得记住#121212是 Material 官方推荐的深色模式背景不是纯黑#1E1E1E是 VS Code 默认深色主题的底#0D1117是另一套很流行的编辑器深色底。它们都刻意避开了#000000因为纯黑底配白字对比度过高长时间看眼睛会累而且 OLED 屏上纯黑和白字之间容易出现拖影。这个选择背后的逻辑比色号本身更值得记住。再补一组编辑器主题常用值写代码的人往往一眼就认出来Monokai 的底是#272822Solarized Dark 的底是#002B36One Dark 的底是#282C34。图表配色也类似很多图表库的默认九色是#5470C6、#91CC75、#FAC858、#EE6666、#73C0DE、#3BA272、#FC8452、#9A60B4、#EA7CCC这套色号的好处是相邻色在色相环上拉开得够远折线图里九条线一起画也不容易看混。2.4 场景化配色建议颜色选得好不好往往不取决于色号本身而取决于用在什么场景。做告警状态的时候红黄绿的语义几乎是全球通用的但要注意色盲用户纯红#FF0000和纯绿#00FF00对红绿色盲来说区分度很低最好配合图标或者形状一起用或者把红换成偏橙的#F44336、绿换成偏青的#10B981明度上也拉开差距。做数据可视化的时候我一般会先确定顺序色和分类色两类。顺序色用于表达大小比如热力图从浅到深拉一条同色系的渐变比如#EFF6FF→#93C5FD→#2563EB分类色用于表达类别用色相区分但饱和度不要都拉满留一两个低饱和的做对比会更清爽。做硬件相关界面时还有个细节如果你做的软件要跟实物指示灯的配色对应别直接抄屏幕上的#00FF00去描述灯的颜色屏幕绿和 LED 绿完全不是一回事。屏幕上的#00FF00是纯绿发光而常见的贴片 LED 因为材料和封装差异实际看起来会偏黄绿或者偏青绿这个时候应该以实物测出来的色为准或者干脆在软件里用接近的中性色避免用户对不上。3. 自己动手算RGB、HSL、HSV 与十六进制互转3.1 手算十六进制十进制到两位十六进制虽然工具遍地都是但会手算有个实实在在的好处你看到#6B能立刻反应过来大概是一百零几调色的时候心里有数不用反复试。算法就一句话除以 16商是高位的十六进制字符余数是低位字符。十六进制字符只有0-9和A-F其中A10、B11、C12、D13、E14、F15。举几个例子过一遍就熟了。107 除以 16商 6 余 1111 写作B所以是6B。255 除以 16商 15 余 15就是FF。53 除以 16商 3 余 5就是35。所以前面那个#FF6B35的三段分别就是 255、107、53。反过来也简单6B就是 6 × 16 11 107。心算时可以借助几个锚点40是 6480是 128C0是 192FF是 255。记住这四个锚点看到任何两位十六进制都能估出大概位置误差不超过十几。A0是 160E0是 224这两个也值得记。再补充一个反向场景有时候你只拿到十六进制需要告诉别人 RGB 值。比如#1E90FF拆开是1E、90、FF1E 3090 144FF 255所以是 RGB(30, 144, 255)这个色叫道奇蓝在很多代码高亮主题里当函数名颜色用。3.2 RGB 与 HSL/HSV 的换算思路十六进制和 RGB 是一一对应的但人眼判断颜色不是靠三个分量而是靠这是什么色调、多浓、多亮。所以从 RGB 派生出两套更符合直觉的模型HSL 和 HSV。HSL 里H 是色相一圈 0 到 360 度红在 0 度绿在 120 度蓝在 240 度正好对应三原色S 是饱和度0 是灰100% 是最艳L 是亮度0 是黑50% 是正常100% 是白。HSV 里 H 一样S 含义接近V 是明度和 L 的区别在于L 到 100% 时无论什么色相都是白的而 V 到 100% 时只是这个色相最亮的状态不一定发白。做图像处理的人更爱用 HSV因为把 V 单独拎出来以后光照强弱主要影响 V而 H 相对保持这样用 H 做颜色判定白天晚上都能用同一个阈值范围省事很多。换算公式我列一下方便你在没有库的情况下手写。先把 RGB 归一化到 0 到 1记最大值 Cmax、最小值 Cmin差值 Δ Cmax − Cmin。那么 L (Cmax Cmin) / 2S Δ / (1 − |2L − 1|)Δ 为 0 时 S 取 0。H 分三种情况如果 Δ 为 0说明是灰H 取 0如果最大的是 RH 60 × (((G − B) / Δ) mod 6)如果最大的是 GH 60 × ((B − R) / Δ 2)如果最大的是 BH 60 × ((R − G) / Δ 4)。算完如果 H 是负数就加 360。反过来的公式也常用给定 H、S、L先算 C (1 − |2L − 1|) × SX C × (1 − |((H / 60) mod 2) − 1|)m L − C / 2然后按 H 落在哪个 60 度区间选择 (C, X, 0)、(X, C, 0) 这类组合最后每个分量加 m 再乘 255 就回到 RGB。不用背理解色相是角度、饱和是浓度、亮度是明暗这个三层结构就够了。真正要用的时候代码里一行转换函数搞定。3.3 代码实现Python、JavaScript 与命令行Python 版本我平时做图像处理和数据清洗经常用def rgb_to_hex(r, g, b, alphaNone): RGB 转十六进制alpha 可选范围 0-1 r, g, b (max(0, min(255, int(v))) for v in (r, g, b)) if alpha is None: return #{:02X}{:02X}{:02X}.format(r, g, b) a max(0, min(255, int(round(alpha * 255)))) return #{:02X}{:02X}{:02X}{:02X}.format(r, g, b, a) def hex_to_rgb(value): 十六进制转 RGB 元组兼容 3/6/8 位写法 s value.strip().lstrip(#) if len(s) 3: s .join(ch * 2 for ch in s) if len(s) not in (6, 8): raise ValueError(色号长度不对: value) return tuple(int(s[i:i 2], 16) for i in range(0, len(s), 2))注意 Python 的format里02X是大写输出想要小写就换成02x。另外一定要做范围钳制我在项目里见过有人把 300 传进去输出成了#12C...这种三位字符串整个颜色就崩了。JavaScript 版本前端随手就能用const rgbToHex (r, g, b) # [r, g, b] .map(v Math.max(0, Math.min(255, Math.round(v))).toString(16).padStart(2, 0)) .join().toUpperCase(); console.log(rgbToHex(255, 107, 53)); // #FF6B35还有一招很多人不知道Linux 和 macOS 的终端里直接就能算处理脚本的时候特别顺手printf #%02X%02X%02X\n 255 107 53要批量处理的话把printf塞进循环或者awk里就行。我处理过一批从 Excel 导出的 RGB 列表就是用 awk 一列一列转的比手工复制粘贴进在线工具快得多也不会抄错。3.4 反查给定颜色找最近的对照表项实际工作里更常见的需求不是算色号而是我拿到一个颜色它最接近我表里的哪一项。比如摄像头读到一个值你想判断它是不是库存里的某种标准色或者设计稿里有一个奇怪的色号你想找最近的规范色替换掉。最朴素的做法是算 RGB 空间的欧氏距离取最小的那个。但人眼对三个通道的敏感度不一样绿色最敏感蓝色最不敏感所以直接算距离经常会选出人眼觉得不像的颜色。实践中常用的加权是给 R、G、B 分别乘 2、4、3 的权重效果明显更符合直觉。def nearest_color(target, palette): palette: {名称: #RRGGBB} tr, tg, tb hex_to_rgb(target)[:3] best, best_score None, float(inf) for name, code in palette.items(): r, g, b hex_to_rgb(code)[:3] score 2 * (r - tr) ** 2 4 * (g - tg) ** 2 3 * (b - tb) ** 2 if score best_score: best, best_score name, score return best, best_score这套加权最近邻的思路我在做颜色传感器项目的时候用得最多。传感器读出来的原始值偏一点很正常与其精确匹配不如建一张标准色表然后找最近项容错率高得多。如果你要更学术一点可以把 RGB 先转成 Lab 空间再算距离效果更好代价是代码多几行。4. 落到项目里前端、QML、OpenCV 与工业软件怎么用4.1 前端CSS 变量与调色板治理前端里最忌讳的就是色号散落各处。今天按钮是#2563EB明天弹窗标题用了#2564EB肉眼看着一样实际是两个色号改主题的时候你得全局搜索好几遍。正确做法是把所有色号集中到一处:root { --color-primary: #2563EB; --color-primary-hover: #1D4ED8; --color-success: #16A34A; --color-warning: #D97706; --color-danger: #DC2626; --color-text-primary: #1F2329; --color-text-secondary: #646A73; --color-bg-base: #FFFFFF; --color-bg-subtle: #F7F8FA; --color-border: #E5E6EB; }命名上我建议用语义 层级 深浅三段式比如brand-primary-500、neutral-text-900。数字后缀不是随便编的它对应的是色阶明度这样以后想加一个更浅的版本直接叫-400团队一眼就知道它比-500浅。这套做法在几大设计体系里都用学过一遍到处都能套。暗色模式就简单了用属性选择器整体覆盖不用改任何组件代码[data-themedark] { --color-bg-base: #121212; --color-text-primary: #E5E6EB; --color-border: #333333; }提示暗色模式不要简单地把亮色模式的颜色取反那样出来的效果会很脏。暗色底配亮色文字时文字不要用纯白用#E5E6EB这类灰白更舒服。4.2 Qt/QML修改 Button 字体颜色的三种做法这个话题我在社区里被问过太多次因为 QML 的Button和网页的button完全不一样它本身没有color属性你写Button { color: red }只会得到一个警告颜色一点不变。第一种做法是覆写contentItem这也是最通用、最不容易出问题的做法Button { id: okButton text: qsTr(确定) contentItem: Text { text: okButton.text font: okButton.font color: okButton.enabled ? #E91E63 : #9E9E9E horizontalAlignment: Text.AlignHCenter verticalAlignment: Text.AlignVCenter elide: Text.ElideRight } }这里有几个细节。一是text要绑到okButton.text不能写死否则多语言切换的时候文字不会更新二是font也绑过去不然自定义样式会把字体吃掉三是加enabled判断禁用态用灰色这是基本的可用性考虑我见过太多项目禁用按钮和可用按钮长得一模一样用户点半天没反应。第二种是用palette。从 Qt 5.15 开始逐步引入了调色板支持到 Qt 6 之后完善了很多Button { text: qsTr(确定) palette.buttonText: #E91E63 }这个写法简洁但它有个限制能不能生效取决于你用的样式。某些默认样式对这个属性的响应并不完整所以如果你试了没效果别怀疑自己写错直接回到第一种做法覆写contentItem稳妥。第三种是自定义一个组件封装起来全项目统一用// 文件名 MyButton.qml import QtQuick.Controls Button { property color textColor: #2563EB property color disabledColor: #BBBBBB contentItem: Text { text: parent.text font: parent.font color: parent.enabled ? parent.textColor : parent.disabledColor horizontalAlignment: Text.AlignHCenter verticalAlignment: Text.AlignVCenter } }我自己的项目基本都是第三种。原因是颜色需求会变今天要蓝色按钮明天要红色危险按钮封装一层以后改一处就行。另外提一句改 QML 颜色有个坑是热身阶段看不到效果因为 QML 有缓存qmlcache目录下的旧文件可能导致你改了源文件但界面没变化遇到这种情况先清缓存再重启。4.3 OpenCV颜色识别的 BGR、HSV 与阈值调参用 OpenCV 调摄像头做颜色识别是我见过翻车率最高的场景之一原因基本集中在两个点通道顺序和颜色空间。第一个坑OpenCV 读进来的图像是 BGR 顺序不是 RGB。这意味着你从对照表里查到红色是#FF0000写进代码里要做通道对应的时候得记得第一个通道是蓝。很多人直接frame[i, j] (255, 0, 0)想画红色结果画出来是蓝色。第二个坑是阈值判定该用 HSV 而不是 RGB。RGB 三个通道都受光照影响你设的红范围灯一暗就全失效。转成 HSV 以后色相 H 基本不受明暗影响只需要把 V 的下限放低一点就能适应暗光。注意 OpenCV 里的取值范围和课本不一样H 被压到 0 到 179不是 0 到 360S 和 V 是 0 到 255。所以课本上 0 度的红对应 H0120 度的绿对应 H60240 度的蓝对应 H120。这个除以 2的换算关系必须记住。红色还有个特殊之处它跨在 0 和 180 的边界上所以要用两段区间然后取并集import cv2 as cv import numpy as np cap cv.VideoCapture(0) if not cap.isOpened(): raise RuntimeError(摄像头打不开检查索引号或占用情况) while True: ok, frame cap.read() if not ok: break hsv cv.cvtColor(frame, cv.COLOR_BGR2HSV) lower1 np.array([0, 120, 70]) upper1 np.array([10, 255, 255]) lower2 np.array([170, 120, 70]) upper2 np.array([180, 255, 255]) mask cv.bitwise_or(cv.inRange(hsv, lower1, upper1), cv.inRange(hsv, lower2, upper2)) kernel np.ones((5, 5), np.uint8) mask cv.morphologyEx(mask, cv.MORPH_OPEN, kernel) mask cv.morphologyEx(mask, cv.MORPH_CLOSE, kernel) contours, _ cv.findContours(mask, cv.RETR_EXTERNAL, cv.CHAIN_APPROX_SIMPLE) for cnt in contours: area cv.contourArea(cnt) if area 800: continue x, y, w, h cv.boundingRect(cnt) cv.rectangle(frame, (x, y), (x w, y h), (0, 255, 0), 2) cv.imshow(color contour, frame) cv.imshow(mask, mask) if cv.waitKey(1) 0xFF ord(q): break cap.release() cv.destroyAllWindows()调参上有几个经验值。S 的下限如果设得太低白墙、纸巾这种近灰色的东西也会被算进去一般从 100 到 150 起步。V 的下限太低会把阴影算进去从 50 到 70 起步比较合适。面积阈值area要根据分辨率调1080p 下调到 800 左右能滤掉大部分噪点。形态学的开运算去小噪点、闭运算补孔洞顺序别反了先开后闭是常规做法。4.4 桌面端与工业软件配置文件里的颜色值很多工业软件和桌面软件的颜色不是在设计器里点出来的而是写在配置文件里的。组态软件、上位机界面、CAD 的颜色库、甚至一些行情软件的主题文件底层都是一堆十六进制或者 RGB 分量。以 CAD 类软件为例老版本的 Pro/E 会用单独的配色文件记录外观颜色里面每一项是 RGB 三个分量不同版本用的取值范围还不一样有的是 0 到 255有的是 0 到 100 的百分比。所以拿到别人分享的颜色库文件时第一件事是确认分量范围别直接套用。Creo 之后的版本材质和外观管理方式变了文件位置和格式也不一样动手前一定要先把原文件复制一份出来备份改坏了还能还原。PCB 和 EDA 软件里也一样图层颜色、阻焊颜色、丝印颜色都能配但这里有个很重要的边界屏幕上显示的颜色和实际工艺做出来的颜色没有对应关系。你在软件里把顶层设成亮蓝不代表板厂会给你做蓝油你想做某个油墨色要给板厂报油墨编号不是报一个十六进制代码。我见过新手拿着#1F6B45去问板厂能不能做这个绿对方只能回答你说的是哪个牌号的绿油。行情类软件的自定义配色也是同一类事情。有些软件的主题配置里颜色以十六进制记录你可以直接替换成自己喜欢的值改完重启生效但不同版本字段名不一样有的写Color有的写TextColor有的甚至把颜色存成十进制整数。所以改之前先备份、先搜关键词定位、再小范围试改一个值看效果别一次性全替换。5. 从屏幕到实物颜色传感器、色卡与印刷的那点偏差5.1 颜色传感器读出的原始值如何变成十六进制颜色传感器是把光变成数的器件常见的有 TCS34725 这类 I2C 接口的芯片内部有红绿蓝加一个透明通道RGBC每个通道输出 16 位数值。还有 TCS3200 这种用频率输出、靠计脉冲读数的老方案以及一些集成在手机和穿戴设备里的环境光加色温方案。关键认知是传感器输出的原始值不是 sRGB不能直接转十六进制就当成屏幕色。中间至少要经过四步处理才能得到一个看起来对的色号。第一步是黑电平校准把传感器完全遮住读一组底噪后面所有读数都要减掉这个底数。第二步是白平衡用一张标准白纸在固定光源下读数算出三个通道的增益系数让白色读出来是接近 (255, 255, 255)。第三步是设置增益和积分时间避免某个通道饱和到 65535 顶着上限不动饱和了就完全失去分辨能力。第四步才是归一化和 gamma 校正把数值压到 0 到 255再转十六进制。def raw_to_hex(r, g, b, gains(1.0, 1.0, 1.0), offset(0, 0, 0)): 传感器原始值转十六进制gains 来自白平衡标定 vals [] for raw, gain, off in zip((r, g, b), gains, offset): v max(0.0, (raw - off) * gain) vals.append(v) peak max(vals) or 1.0 # 按最大值归一避免整体偏暗 norm [int(round(v / peak * 255)) for v in vals] return #{:02X}{:02X}{:02X}.format(*norm)这段代码只是骨架真实项目里你还需要处理环境光变化。所以同一支口红在暖光客厅里和在冷光办公室里传感器读出来能差出十几个色阶。我的做法是如果要精确对色光源必须固定如果只是做是不是红色这类粗判断那就只在 HSV 空间里比色相别纠结具体色号。5.2 色卡、CMYK 与屏幕色的换算边界这里必须说一句可能让人失望的话屏幕上的十六进制和印刷用的 CMYK 之间不存在无损转换。原因很物理。屏幕是自己发光的颜色来自三个子像素的叠加印刷是纸反射光青色、品红、黄色、黑色四种油墨各自吸收一部分波长。两种完全不同的显色机制中间只能靠 ICC 配置文件做近似映射而这个映射还依赖纸张、油墨、设备状态。所以印刷厂通常不认 RGB只认色卡编号。你拿一个#C8102E去问能不能印对方会问你对应哪个色卡的哪个号。反过来你把实物色卡对着屏幕想找个十六进制也只能找到近似值。跨色卡体系对照更是如此不同体系的编号规则、基材、测量标准都不一样硬对编号没意义正确做法是用分光光度计测出 Lab 值再用 Lab 做桥梁换算而不是拿眼睛比或者拿屏幕比。还有个容易被忽略的点是色域。同样是#FF0000在 sRGB 屏和广色域屏上显示出来的鲜艳程度不一样后者能表现出更饱和的红。所以设计交付时最好明确工作色彩空间别让实现方和设计方在两种色域下各看各的。5.3 建立你自己的颜色记录表做了几年项目以后我现在的做法是维护一份自己的 CSV 颜色表字段是名称、十六进制、RGB、用途、来源、备注。表格可以很简单但它解决的问题很实在——半年后你再看这个项目能立刻知道当初为什么选这个色是从哪抄的谁定的。更进一步我会用脚本从这份 CSV 自动生成 CSS 变量、SCSS 变量和 QML 的常量定义文件。这样做的好处是颜色只维护一份源其他语言的文件都是产物不会出现CSS 改了、QML 忘了改这种分裂。脚本很简单读 CSV 然后按模板输出字符串就行二三十行代码一次性投入后面每次加色都省事。如果项目涉及实物对色我还会在表里加一列实物参考把色卡编号、批次、拍摄条件记下来。听上去有点重但吃过一次半年后没人记得这个绿色是怎么定的的亏之后你就会觉得这一列特别值。6. 常见问题与排查技巧实录6.1 问题速查表下表是我这些年积攒下来的高频问题基本覆盖了 90% 的颜色不生效场景。现象大概率原因处理办法改了颜色没生效缓存未清、样式优先级被覆盖清缓存重启检查是否有更高优先级规则透明度看着不对#RRGGBBAA和#AARRGGBB顺序写反确认平台规范安卓系把 AA 放前面颜色比设计稿深抄色号时漏了一位或少写一位补全六位别用三位简写老设备上颜色异常老解析器不支持三位缩写或八位写法统一退化成六位六位写摄像头抓不到红色H 跨 0 和 180 边界只设了一段拆成两段区间取并集检测结果忽有忽无光照变化导致 V 超出阈值放宽 S、V 范围或改用色相为主判据传感器读数漂移未做黑电平与白平衡标定每次上电先标定固定光源打印出来偏色直接用了屏幕 RGB 去转 CMYK用色卡编号或走 ICC 流程深色模式字发虚纯白字配纯黑底对比度过高底用#121212字用#E5E6EB主题改了部分生效有些颜色写死在组件里全量搜索裸色号收归到变量6.2 我踩过的坑与几个私房技巧第一个坑是最蠢也最痛的手抄色号漏位。当年做一张状态图我把#1E90FF抄成了#1E9FF看着也是蓝色压根没注意结果整套图里就那一个色号是错的评审的时候被设计师一眼看出来。从那以后我养成两个习惯一是复制粘贴不手打二是改完必须全局搜一遍色号做去重统计看看有没有孤立的野生色号。第二个坑是八位透明度。有个跨端项目前端同事给了#FF000080安卓同事直接Color.parseColor(#FF000080)结果程序崩了——安卓的那套接口不接受这个顺序它认的是#80FF0000。这个错误的代价是半天排查时间因为报错信息不直观。第三个坑跟 QML 有关。改按钮颜色写了color: #xxx编译通过但界面毫无变化怀疑人生。后来才明白Button根本没有这个属性写上去只是个无效的附加属性。这类不报错但不生效的问题最难查因为你的第一反应是怀疑编译器或者缓存而不是怀疑属性名。第四个坑是颜色识别里的光照。同一个红色积木放在窗边和放在桌下阈值范围完全不一样。我后来改成用色相为主判据配合自适应亮度归一化才把识别率提上去。方法是先算整个画面的平均亮度然后按亮度比例调整 V 的上限这样不管是白天还是开灯识别都还过得去。几个我自己总结的小技巧一并给出来。一是团队约定色号统一大写并且不允许三位简写理由是方便搜索和对比规范比灵活重要。二是所有颜色必须走变量代码里出现裸十六进制就算不合规尤其在多人协作的项目里这条规则救过我好几次。三是做视觉检测时先在 HSV 空间标定把标定结果上下限写成常量注释在代码旁边顺手把对应的十六进制参考值也写上方便后面接手的人理解。四是深色模式的颜色不要自己闷头调先抄一套成熟方案的色阶比如 Material 或主流编辑器的深色主题在它的基础上微调比从零开始试快得多也更容易看得舒服。最后分享一个我到现在还在用的小做法建一个纯 HTML 的本地调色板页面左边是色块右边显示色号、RGB、HSL点一下自动复制到剪贴板。这玩意儿二十分钟就能写完但它把我查色号这个动作从打开搜索引擎、翻网页、复制、回去粘贴压缩成了一次点击。用得越久越觉得对照表这东西的价值不在表本身而在于你把它变成了顺手能调用的工具。