news 2026/9/13 13:19:29

8位/16位RGB颜色对照与换算:从网页到嵌入式RGB565与PWM调光

作者头像

张小明

前端开发工程师

1.2k 24
文章封面图
8位/16位RGB颜色对照与换算:从网页到嵌入式RGB565与PWM调光

最近在整理嵌入式屏幕显示项目的时候,我又把那份积灰的颜色RGB值对应表翻了出来。本来是顺手查一个橙色,结果发现“8位”“16位”这两个词在不同圈子里的含义完全不是一回事。很多朋友拿到一份别人发的“RGB值对应表”,在网页里调得好好的,一搬到单片机、图像处理或者HDR流程里就翻车,颜色不是偏色就是整个变成黑色。这篇文章把我自己整理和验证过的颜色值、换算逻辑和各种踩坑记录一起放出来,从8位通道、16位通道、RGB565一直讲到PWM调灯和Python取色,争取让你看完之后能直接拿表干活。

1. 先分清“8位”和“16位”:颜色值的三种常见含义

1.1 8位通道:网页和工具里默认的RGB

我们日常说的rgb(10, 92, 140)、#FFA500这类写法,默认就是“每通道8位”。R、G、B三个通道各占8个bit,取值范围是0到255,三个通道合起来就是24位真彩色,能表示大约1677万种颜色。这在网页CSS、普通PNG/JPG、Python的PIL库、大多数截屏取色工具里都是同一个标准。

这里要先提醒一个容易搞混的概念:8位通道和8位图像完全是两码事。8位图像通常说的是索引色模式,整张图最多只能有256种颜色,每个像素用一个8位数字去查调色板,典型的就是老式GIF。而8位通道指的是每个红绿蓝通道有256个等级,不是全图只有256色。拿到颜色表的时候,先看清楚你手里的“8位”到底是哪种语境,否则后面所有换算都会跟着错。

1.2 16位通道:图像处理和HDR里的“深色域”

16位通道的意思是R、G、B每个通道占16个bit,取值范围0到65535,三个通道合起来是48位。这种格式常见于16位TIFF、16位PNG、专业相机RAW、HDR合成、医学影像、遥感图像处理。如果你在Photoshop里打开一张16位/通道的图,或者在MATLAB里读一个16位深度图像,就会发现颜色值不再是0到255,而是动辄几万的大数。

16位通道的出现是为了保留更多亮部和暗部细节,避免在多次调整层级的时候出现色阶断裂。但它也带来一个麻烦:网页上查到的颜色值全是0到255,拿到16位环境里不能直接用,得先做线性扩展。至于怎么扩展,我后面专门写一节。需要先说明的是,16位/通道并没有一个全球统一的整数编码方式,有的软件用0到65535,有的软件显示0到32768,还有的用半浮点数。所以不要一看到“16位”就默认上限是65535,要先确认你正在用的工具到底采用哪种映射。

1.3 RGB565:嵌入式、单片机里的另一种“16位”

如果你在网上搜“8位 16位 颜色”搜到了单片机屏幕相关的内容,那这里的“16位”通常指的是RGB565,而不是16位通道。STM32、ESP32、Arduino驱动TFT LCD、OLED屏时候很常用这种模式。RGB565把16位总色深拆成R占5位、G占6位、B占5位,一共正好16位,也就是两个字节。因为人眼对绿色最敏感,所以绿色多分了一位。这种格式能显示2的16次方等于65536种颜色,远不如24位真彩色丰富,但在小屏幕上大部分情况下够用,而且省内存、传输快。

还有一种类似的格式叫ARGB1555,也就是1位透明度加5位红、5位绿、5位蓝,也是16位,常见于一些老的嵌入式UI资源。看到这里你应该明白了,同样是“16位”,可能是每通道65536级的深色域,也可能只是RGB565高五位中六位低五位的紧凑屏显格式。我见过不少人在这一步踩坑,拿着48位源图的像素值直接往RGB565的屏驱里填,结果红色变成黑色、高光全部发紫,就是这个概念没理清。

为了看得更清楚,我把这几种格式整理成一张对比表:

术语R/G/B 通道位数取值示例总位数常见场景
8位/通道 RGB8888/8/80-25524位网页、普通PNG/JPG、CSS
16位/通道16/16/160-6553548位图像处理、HDR、16位PNG/TIFF
RGB5655/6/5R:0-31 G:0-63 B:0-3116位单片机、屏幕驱动、嵌入式UI
ARGB15551位透明+5/5/50-3116位旧嵌入式图标、资源包
8位索引色非通道概念整图最多256色8位老GIF、低端屏显

2. 常用颜色8位RGB对照表与十六进制HEX速查

2.1 基础色和常用色对照表

先放一张最基础的8位通道颜色表,这里面的R、G、B值就是网页里最常见的写法。注意“绿”和“青”不要搞混,绿色是纯绿light,青色是绿加蓝,很多人做LED灯的时候把0,255,0和0,255,255当成同一个颜色,调出来完全不是自己要的效果。

颜色英文名HEX8位RGB
黑色Black#0000000,0,0
白色White#FFFFFF255,255,255
红色Red#FF0000255,0,0
绿色Green#00FF000,255,0
蓝色Blue#0000FF0,0,255
黄色Yellow#FFFF00255,255,0
青色Cyan#00FFFF0,255,255
品红Magenta#FF00FF255,0,255
橙色Orange#FFA500255,165,0
深红Dark Red#8B0000139,0,0
金色Gold#FFD700255,215,0
黄绿Yellow Green#9ACD32154,205,50
深绿Dark Green#0064000,100,0
海军蓝Navy#0000800,0,128
道奇蓝Dodger Blue#1E90FF30,144,255
蓝绿Teal#0080800,128,128
紫色Purple#800080128,0,128
粉色Pink#FFC0CB255,192,203
棕色Brown#A52A2A165,42,42
银色Silver#C0C0C0192,192,192
灰色Gray#808080128,128,128
暗灰Dark Gray#A9A9A9169,169,169
亮灰Light Gray#D3D3D3211,211,211
橄榄Olive#808000128,128,0
栗色Maroon#800000128,0,0
珊瑚Coral#FF7F50255,127,80
砖红Firebrick#B22222178,34,34
靛蓝Indigo#4B008275,0,130

这张表足够覆盖日常网页配色、PPT取色、LED灯调试等大部分场景。如果你只是做UI设计,看到这里其实可以用了。但注意,电脑屏幕上显示出来的rgb(10, 92, 140),在其他设备上未必是同一个颜色,原因不在颜色表,而在显示设备。

2.2 为什么同一个RGB值在不同设备上看起来不一样

颜色值本身是一个标准化的数字,但最终呈现受三个因素影响:屏幕色域、色彩管理、gamma曲线。入门级显示器通常是sRGB色域,部分高端显示器是P3色域,同样一个RGB值在P3屏幕上会更鲜艳。系统色彩管理如果没做好,浏览器和图片软件之间也会出现色差。gamma就更直接了,RGB数值的256个等级并不是线性亮度,而是经过编码的非线性曲线。如果拿一个完全线性的屏幕去显示普通sRGB图片,中间调会显得很暗。

所以在做跨设备颜色匹配的时候,光靠RGB表是不够的,至少要保证图片带ICC色彩配置文件,设备经过校色,并且整个过程都在同一个色彩空间里。真正确认颜色是否准确,不是肉眼看屏幕,而是用色度计测量。这个提醒放在这一节,是想让你明白:颜色表只是数字参考,不是最终实物保证。

3. 16位颜色值到底怎么换算:通道、RGB565与PWM调光

3.1 8位转16位通道的标准公式

8位通道的0到255,要扩展成16位通道的0到65535,我推荐直接用乘以257的做法。因为65535除以255刚好等于257,这是整数比例里最精确的换算关系。公式就是v16 = v8 * 257,反过来v8 = v16 / 257,取整就行。

用橙色来举例:#FFA500对应的8位RGB是255,165,0。换算后R等于255乘以257,等于65535;G等于165乘以257,等于42405;B还是0。所以在16位通道环境下,应该填65535,42405,0,而不是像很多人那样填255,165,0。直接把255填进16位通道的图里,会让整张图几乎接近黑色,因为255在65535的尺度下只有不到0.4%的亮度。

不过前面提过,有些软件的16位通道不是0到65535,而是0到32768。如果遇到这种环境,就用v16 = round(v8 * 32768 / 255)来换算。比如255会变成32768,128会变成16447左右。这一点完全没有统一标准,建议拿到一种图片格式后,先读取一个已知颜色的像素值看一眼,再决定用哪个公式,这是最稳妥的办法。

3.2 RGB565计算方法和常用值

RGB565的换算就完全不是乘257了,它的每个通道位数不同,所以要做位压缩。最常用的做法是移位截断:R取8位值的高5位,G取高6位,B取高5位。写成代码就是:

uint16_t rgb565(uint8_t r, uint8_t g, uint8_t b) { return ((r >> 3) << 11) | ((g >> 2) << 5) | (b >> 3); }

这个做法的结果是纯黑0x0000,纯白0xFFFF,纯红0xF800,纯绿0x07E0,纯蓝0x001F,黄色0xFFE0,青色0x07FF,品红0xF81F。橙色255,165,0换算出来是0xFD20,灰色128,128,128换算出来是0x8410。这些值在嵌入式开发里非常常用。

如果需要更精确一点,而不是直接丢弃最低位,可以用四舍五入公式:

uint16_t rgb565_round(uint8_t r, uint8_t g, uint8_t b) { uint16_t r5 = (r * 31 + 127) / 255; uint16_t g6 = (g * 63 + 127) / 255; uint16_t b5 = (b * 31 + 127) / 255; return (r5 << 11) | (g6 << 5) | b5; }

两种算出来的结果通常只差最低1到2位,肉眼几乎看不出区别。但在做渐变、调色板这类对精度敏感的场景,建议用四舍五入版本。我实际对比过,用移位版本调试一套渐变灯光,在暗部会有可见的阶梯感,换成四舍五入版本之后平滑了很多。

3.3 PWM驱动RGB灯时的8位和16位换算

单片机驱动RGB LED灯,最常用的就是PWM控制三个颜色通道的占空比。有些平台的analogWrite只有8位精度,也就是0到255;有些定时器可以配置成16位分辨率,也就是0到65535。问题就出在这:你把网页上查到的255,165,0直接塞进16位PWM,就等于只开了不到0.4%的占空比,灯会极其暗,几乎看不出颜色。

正确的做法是先把8位通道值换算到对应PWM精度。8位PWM直接用原值:

// 8位PWM:橙色 analogWrite(redPin, 255); analogWrite(greenPin, 165); analogWrite(bluePin, 0);

16位PWM要先做线性扩展:

// 16位PWM:橙色 analogWrite(redPin, 65535); analogWrite(greenPin, 42405); analogWrite(bluePin, 0);

注意这只是把占空比数值扩展了,LED实际亮度还要看驱动电路和灯珠特性。如果LED的亮度变化明显不均匀,常见原因不是RGB值不对,而是人眼感知是非线性的,PWM占空比却接近线性。想要视觉上的平滑渐变,一般会做gamma校正,输出前加一步:

uint16_t pwm_value = (uint16_t)(pow(color_8bit / 255.0, 2.2) * 65535.0);

这个pow函数在单片机上会占用不少CPU,尤其是ESP8266这类小资源设备,建议预先把256个PWM值算成对照表,运行时候直接查表,既省时间又稳定。

4. 实操:用Python从图片提取RGB并生成双版本颜色表

4.1 读取指定像素的RGB值

现成的颜色表再全,也总有对不上的时候。我通常的做法是直接从图片里取色,然后脚本自动输出8位、16位和RGB565三套结果。Pillow库读普通PNG/JPG很简单。示例代码:

from PIL import Image img = Image.open("test.png").convert("RGB") rgb8 = img.getpixel((120, 80)) print("8位通道 RGB:", rgb8) rgb16 = tuple(v * 257 for v in rgb8) print("16位通道 RGB:", rgb16) def to_rgb565(r, g, b): r5 = (r * 31 + 127) // 255 g6 = (g * 63 + 127) // 255 b5 = (b * 31 + 127) // 255 return (r5 << 11) | (g6 << 5) | b5 print("RGB565: 0x%04X" % to_rgb565(*rgb8))

这段代码会直接在终端里输出你要的三套结果。需要注意,Pillow的convert("RGB")会把很多格式强制转成8位通道。如果图片本身是16位PNG,想保留16位数据,就不能直接convert("RGB"),要检查mode,比如是“I;16”还是“RGB;16”,再按对应位深度处理。很多新手在这里踩坑,明明打开的是16位图,读出来的像素值却全是0到255,就是因为没看mode。

4.2 批量生成标准色阶和过渡色

如果你不想手动输入几十个颜色,可以直接用脚本生成一整组过渡色。比如我想生成从红色到黄色的64个渐变色,顺便输出16位和RGB565,可以这样写:

for i in range(64): r = 255 g = int(i * 255 / 63) b = 0 rgb16 = (r * 257, g * 257, b * 257) rgb565 = to_rgb565(r, g, b) print(f"#{r:02X}{g:02X}{b:02X} 8bit=({r},{g},{b}) 16bit={rgb16} 565=0x{rgb565:04X}")

生成结果直接就是一条可用的颜色表。做UI换肤、LED呼吸灯、色轮切换这类需求,用这种方法比手敲靠谱得多。生成之后建议拿到实际设备上扫一遍,因为脚本算出来的数值只代表数学上的颜色,不代表最终显示效果。

4.3 常见问题与排查技巧实录

问题现象可能原因解决办法
图片取的RGB和肉眼看到的不一样图片色彩空间不是sRGB,显示器色温不对统一转成sRGB,查看端做色彩管理
16位PNG用PIL读出来还是0-255图片被convert("RGB")强制转成8位检查img.mode,按“I;16”或“RGB;16”处理
RGB565转出来颜色发暗把8位直接塞进了565,通道位数不足先移位/四舍五入到5位6位5位,再组合
PWM调16位灯特别暗8位值没扩展就到了65535尺度用v16 = v8 * 257转换
在PS里16位通道填65535变成白色PS的16位/通道不是0-65535线性编码先看软件色阶上限,按对应范围换算

我自己实际处理16位TIFF时遇到过最坑的情况:用ImageMagick查到的像素值上限是65535,但导入MATLAB之后上限变成了65335左右,原因是文件里嵌了一个非标准的颜色空间转换。所以遇到跨软件颜色值对不上的时候,别急着怀疑颜色表,先确认两边的色彩空间和位深编码方式。

5. 一张比较全的8位/16位颜色速查表

5.1 完整对照表

这张表是我平时直接放在工程目录里当注释用的。16位通道列按0到65535线性编码,用的是v8乘以257的换算关系;RGB565列按主流单片机的移位截断方法计算。如果你用的16位通道编码是0到32768,那这列的数值需要按比例再换算,不要直接照抄。

颜色中文HEX8位RGB16位通道RGBRGB565
黑色#0000000,0,00,0,00x0000
白色#FFFFFF255,255,25565535,65535,655350xFFFF
红色#FF0000255,0,065535,0,00xF800
绿色绿#00FF000,255,00,65535,00x07E0
蓝色#0000FF0,0,2550,0,655350x001F
黄色#FFFF00255,255,065535,65535,00xFFE0
青色#00FFFF0,255,2550,65535,655350x07FF
品红品红#FF00FF255,0,25565535,0,655350xF81F
橙色#FFA500255,165,065535,42405,00xFD20
深红深红#8B0000139,0,035723,0,00x8800
金色#FFD700255,215,065535,55255,00xFEA0
黄绿黄绿#9ACD32154,205,5039578,52685,128500x9E66
深绿深绿#0064000,100,00,25700,00x0320
海军蓝藏蓝#0000800,0,1280,0,328960x0010
道奇蓝亮蓝#1E90FF30,144,2557710,37008,655350x1C9F
蓝绿蓝绿#0080800,128,1280,32896,328960x0410
紫色#800080128,0,12832896,0,328960x8010
粉色#FFC0CB255,192,20365535,49344,521710xFE19
棕色#A52A2A165,42,4242405,10794,107940xA145
银色#C0C0C0192,192,19249344,49344,493440xC618
灰色#808080128,128,12832896,32896,328960x8410
暗灰暗灰#A9A9A9169,169,16943433,43433,434330xAD55
亮灰亮灰#D3D3D3211,211,21154227,54227,542270xD69A
橄榄橄榄#808000128,128,032896,32896,00x8400
栗色栗色#800000128,0,032896,0,00x8000
珊瑚珊瑚#FF7F50255,127,8065535,32639,205600xFBEA
砖红砖红#B22222178,34,3445746,8738,87380xB104
靛蓝靛蓝#4B008275,0,13019275,0,334100x4810

5.2 表格使用说明与验证建议

这张表在纯色、基础色的场景下可以直接用,但有一个前提必须提醒:16位通道列采用的是0到65535整数线性编码,也是Python、ImageMagick、很多科学计算库默认的方式。如果你的目标软件是Photoshop这种把16位/通道显示成0到32768的,那就不能直接照抄,需要再除以2左右。RGB565列用的是移位截断方式,和四舍五入方式在个别颜色上可能差1,这点差值在屏幕上基本看不出,但如果你在做逐位比较的测试用例,就要用同一套算法。

我自己拿到一张新颜色表之后,一般会先做三个验证:第一,用Python读取一个已知颜色的像素值,确认软件通道范围到底是0到255还是0到65535;第二,生成一张纯色测试图,在目标屏幕上肉眼确认颜色方向对不对;第三,用RGB565画一条从黑到白的渐变,看看有没有明显色阶断层。这三步走完,那张表基本就可以放心用了。

最后再分享一个小技巧,做嵌入式UI的时候,不要每次临时查RGB565,直接把你常用的十几个颜色定义成宏或常量数组,像纯色、半透明遮罩色、警告色、成功色放一起。我就是这么干的,后来换屏幕、换项目的时候直接复制头文件,省了非常多时间。颜色的坑永远存在于数字到显示的那一段路程里,手里有表、心里有换算、眼睛盯着设备,比背多少颜色值都管用。

版权声明: 本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若内容造成侵权/违法违规/事实不符,请联系邮箱:809451989@qq.com进行投诉反馈,一经查实,立即删除!
网站建设 2026/9/13 13:17:07

Python数据可视化实战:常用统计图场景与matplotlib避坑指南

/* MD / 富文本中的 .toc(含博客园搬家等嵌套结构);.toc-box 在侧栏,不受影响 */#content_views .toc,/* 编辑器常在目录前后插入空 p(:empty 仍占 20px),一并去掉避免顶空隙 */#content_views.markdown_views > p:empty:has(+ .toc),#content_views.markdown_views …

作者头像 李华
网站建设 2026/9/13 13:16:09

PyTorch人脸CNN实战:MTCNN检测+ResNet特征提取全链路

简介&#xff1a;本资源是一份基于CNN的人脸识别实践代码包&#xff0c;面向计算机视觉初学者与深度学习入门者&#xff0c;聚焦图像预处理、特征提取与人脸分类全流程实现。压缩包共2个Python文件&#xff08;face_recognition.py与jiance.py&#xff09;&#xff0c;总大小仅…

作者头像 李华
网站建设 2026/9/13 13:14:42

Vue开发工具链:Volar+Prettier+ESLint配置实战

/* MD / 富文本中的 .toc(含博客园搬家等嵌套结构);.toc-box 在侧栏,不受影响 */#content_views .toc,/* 编辑器常在目录前后插入空 p(:empty 仍占 20px),一并去掉避免顶空隙 */#content_views.markdown_views > p:empty:has(+ .toc),#content_views.markdown_views …

作者头像 李华
网站建设 2026/9/13 13:13:43

STM32 USART1环形队列接收:从原理到代码解决串口丢字节

简介&#xff1a;面向STM32F103与STM32F102C8T6的USART1串口环形队列工程包&#xff0c;为嵌入式开发中需要处理串口高频收发、规避数据丢失场景的开发者提供了一套完整可参考的实现。工程以标准外设库为基础&#xff0c;包含USART1初始化配置、接收中断服务程序、环形队列结构…

作者头像 李华