Go image/color/palette 包详解
概述
image/color/palette 是一个独立的 Go 标准库包,提供预定义的标准调色板。它包含两个导出的变量:Plan9(256 色调色板)和 WebSafe(216 色调色板),这些调色板在颜色量化和 GIF 编码等场景中非常有用。
包导入
import "image/color/palette"
基本使用
1. 使用 Plan9 调色板
package main
import (
"fmt"
"image/color/palette"
)
func main() {
// Plan9 是一个 []color.Color 类型的切片
fmt.Printf("Plan9 调色板颜色数量:%d\n", len(palette.Plan9))
// 访问第一个颜色
firstColor := palette.Plan9[0]
fmt.Printf("第一个颜色:%v\n", firstColor)
}
运行结果:
Plan9 调色板颜色数量:256
第一个颜色:{0 0 0 255}
2. 使用 WebSafe 调色板
package main
import (
"fmt"
"image/color/palette"
)
func main()
// WebSafe 是一个 []color.Color 类型的切片
fmt.Printf("WebSafe 调色板颜色数量:%d\n", len(palette.WebSafe))
// 访问第一个颜色
firstColor := palette.WebSafe[0]
fmt.Printf("第一个颜色:%v\n", firstColor)
}
运行结果:
WebSafe 调色板颜色数量:216
第一个颜色:{0 0 0 255}
一、变量详解
Plan9
定义:
var Plan9 = []color.Color{...}
说明:
- 颜色数量:256 种颜色
- 设计原理:将 24 位 RGB 颜色空间划分为 4×4×4 的细分网格
- 子立方体:每个子立方体包含 4 个阴影级别
- 颜色分布:
- 16 个灰色阴影
- 每种原色(红、绿、蓝)13 个阴影
- 每种二次色(青、品红、黄)13 个阴影
- 优势:更好地表示连续色调图像
- 历史:曾用于 Plan 9 操作系统
技术细节:
RGB 空间细分:
- R 轴:4 个级别 (0, 85, 170, 255)
- G 轴:4 个级别 (0, 85, 170, 255)
- B 轴:4 个级别 (0, 85, 170, 255)
- 总计:4 × 4 × 4 = 64 个子立方体
- 每个子立方体:4 个阴影级别
- 总颜色数:64 × 4 = 256
WebSafe
定义:
var WebSafe = []color.Color{...}
说明:
- 颜色数量:216 种颜色
- 设计原理:RGB 分量各取 6 个固定值
- 颜色值:每个分量取以下 6 个值之一:
0x00(0)0x33(51)0x66(102)0x99(153)0xCC(204)0xFF(255)
- 别名:Netscape Color Cube
- 历史:由早期 Netscape Navigator 浏览器推广
- 用途:确保在不同显示器上颜色显示一致
技术细节:
RGB 分量组合:
- R: 6 个值 (0x00, 0x33, 0x66, 0x99, 0xCC, 0xFF)
- G: 6 个值 (0x00, 0x33, 0x66, 0x99, 0xCC, 0xFF)
- B: 6 个值 (0x00, 0x33, 0x66, 0x99, 0xCC, 0xFF)
- 总颜色数:6 × 6 × 6 = 216
二、典型示例
示例 1:创建使用 Plan9 调色板的 GIF 图像
package main
import (
"image"
"image/color"
"image/color/palette"
"image/gif"
"os"
)
func main() {
// 创建 100x100 的灰度图像
img := image.NewPaletted(
image.Rect(0, 0, 100, 100),
palette.Plan9,
)
// 使用调色板中的颜色填充
for y := 0; y < 100; y++ {
for x := 0; x < 100; x++ {
// 选择调色板中的颜色索引
colorIndex := uint8((x + y) % len(palette.Plan9))
img.SetColorIndex(x, y, colorIndex)
}
}
// 创建 GIF 文件
file, _ := os.Create("output_plan9.gif")
defer file.Close()
// 编码为 GIF
gif.Encode(file, img, &gif.Options{
NumColors: 256,
})
}
示例 2:创建使用 WebSafe 调色板的 GIF 图像
package main
import (
"image"
"image/color/palette"
"image/gif"
"os"
)
func main() {
// 创建使用 WebSafe 调色板的图像
img := image.NewPaletted(
image.Rect(0, 0, 100, 100),
palette.WebSafe,
)
// 创建渐变效果
for y := 0; y < 100; y++ {
for x := 0; x < 100; x++ {
// 根据位置选择 WebSafe 调色板中的颜色
r := uint8(x * 255 / 100)
g := uint8(y * 255 / 100)
b := uint8((x + y) * 255 / 200)
// 找到最接近的 WebSafe 颜色索引
colorIndex := findClosestColor(r, g, b, palette.WebSafe)
img.SetColorIndex(x, y, colorIndex)
}
}
// 保存为 GIF
file, _ := os.Create("output_websafe.gif")
defer file.Close()
gif.Encode(file, img, nil)
}
// findClosestColor 找到调色板中最接近的颜色
func findClosestColor(r, g, b uint8, pal []color.Color) uint8 {
minDistance := float64(1000000)
closestIndex := uint8(0)
for i, c := range pal {
rc, gc, bc, _ := c.RGBA()
rc >>= 8
gc >>= 8
bc >>= 8
// 计算欧几里得距离
distance := float64((int(r) - int(rc)) * (int(r) - int(rc)) +
(int(g) - int(gc)) * (int(g) - int(gc)) +
(int(b) - int(bc)) * (int(b) - int(bc)))
if distance < minDistance {
minDistance = distance
closestIndex = uint8(i)
}
}
return closestIndex
}
示例 3:比较两种调色板的颜色分布
package main
import (
"fmt"
"image/color/palette"
)
func main() {
fmt.Println("=== Plan9 调色板 ===")
fmt.Printf("总颜色数:%d\n", len(palette.Plan9))
// 显示前 16 个颜色
fmt.Println("\n前 16 个颜色 (RGBA):")
for i := 0; i < 16 && i < len(palette.Plan9); i++ {
r, g, b, a := palette.Plan9[i].RGBA()
fmt.Printf("[%2d] R:%3d G:%3d B:%3d A:%3d\n",
i, r>>8, g>>8, b>>8, a>>8)
}
fmt.Println("\n=== WebSafe 调色板 ===")
fmt.Printf("总颜色数:%d\n", len(palette.WebSafe))
// 显示前 16 个颜色
fmt.Println("\n前 16 个颜色 (RGBA):")
for i := 0; i < 16 && i < len(palette.WebSafe); i++ {
r, g, b, a := palette.WebSafe[i].RGBA()
fmt.Printf("[%2d] R:%3d G:%3d B:%3d A:%3d\n",
i, r>>8, g>>8, b>>8, a>>8)
}
}
运行结果:
=== Plan9 调色板 ===
总颜色数:256
前 16 个颜色 (RGBA):
[ 0] R: 0 G: 0 B: 0 A:255
[ 1] R: 0 G: 0 B: 85 A:255
[ 2] R: 0 G: 0 B:170 A:255
[ 3] R: 0 G: 0 B:255 A:255
[ 4] R: 0 G: 85 B: 0 A:255
[ 5] R: 0 G: 85 B: 85 A:255
[ 6] R: 0 G: 85 B:170 A:255
[ 7] R: 0 G: 85 B:255 A:255
[ 8] R: 0 G:170 B: 0 A:255
[ 9] R: 0 G:170 B: 85 A:255
[10] R: 0 G:170 B:170 A:255
[11] R: 0 G:170 B:255 A:255
[12] R: 0 G:255 B: 0 A:255
[13] R: 0 G:255 B: 85 A:255
[14] R: 0 G:255 B:170 A:255
[15] R: 0 G:255 B:255 A:255
=== WebSafe 调色板 ===
总颜色数:216
前 16 个颜色 (RGBA):
[ 0] R: 0 G: 0 B: 0 A:255
[ 1] R: 0 G: 0 B: 51 A:255
[ 2] R: 0 G: 0 B:102 A:255
[ 3] R: 0 G: 0 B:153 A:255
[ 4] R: 0 G: 0 B:204 A:255
[ 5] R: 0 G: 0 B:255 A:255
[ 6] R: 0 G: 51 B: 0 A:255
[ 7] R: 0 G: 51 B: 51 A:255
[ 8] R: 0 G: 51 B:102 A:255
[ 9] R: 0 G: 51 B:153 A:255
[10] R: 0 G: 51 B:204 A:255
[11] R: 0 G: 51 B:255 A:255
[12] R: 0 G:102 B: 0 A:255
[13] R: 0 G:102 B: 51 A:255
[14] R: 0 G:102 B:102 A:255
[15] R: 0 G:102 B:153 A:255
示例 4:颜色量化 - 将真彩色图像转换为调色板图像
package main
import (
"image"
"image/color"
"image/color/palette"
"image/draw"
"image/gif"
"image/png"
"os"
)
func main() {
// 打开 PNG 图像
file, _ := os.Open("input.png")
defer file.Close()
srcImg, _ := png.Decode(file)
// 使用 Plan9 调色板创建新的调色板图像
dstImg := image.NewPaletted(srcImg.Bounds(), palette.Plan9)
// 绘制图像并自动进行颜色量化
draw.Draw(dstImg, dstImg.Bounds(), srcImg, image.Point{}, draw.Src)
// 保存为 GIF
outFile, _ := os.Create("output_quantized.gif")
defer outFile.Close()
gif.Encode(outFile, dstImg, nil)
}
三、实际应用场景
1. GIF 动画制作
package main
import (
"image"
"image/color/palette"
"image/gif"
"os"
)
func main() {
// 创建多帧 GIF 动画
var frames []*image.Paletted
var delays []int
// 生成 10 帧动画
for frame := 0; frame < 10; frame++ {
img := image.NewPaletted(
image.Rect(0, 0, 100, 100),
palette.WebSafe,
)
// 每帧绘制不同内容
for y := 0; y < 100; y++ {
for x := 0; x < 100; x++ {
colorIndex := uint8((x + y + frame*10) % len(palette.WebSafe))
img.SetColorIndex(x, y, colorIndex)
}
}
frames = append(frames, img)
delays = append(delays, 10) // 每帧延迟 10ms
}
// 创建 GIF 动画
file, _ := os.Create("animation.gif")
defer file.Close()
gif.EncodeAll(file, &gif.GIF{
Image: frames,
Delay: delays,
})
}
2. 颜色映射可视化
package main
import (
"image"
"image/color/palette"
"image/draw"
"image/png"
"os"
)
func main() {
// 创建显示调色板所有颜色的图像
width := 256
height := 4 // Plan9 有 256 色,分成 4 行显示
img := image.NewRGBA(image.Rect(0, 0, width, height*16))
// 绘制 Plan9 调色板的颜色样本
for i, c := range palette.Plan9 {
x := (i % width)
y := (i / width) * 16
rect := image.Rect(x, y, x+16, y+16)
draw.Draw(img, rect, &image.Uniform{c}, image.Point{}, draw.Src)
}
// 保存为 PNG
file, _ := os.Create("palette_plan9.png")
defer file.Close()
png.Encode(file, img)
}
3. 性能优化 - 减少颜色数量
package main
import (
"image"
"image/color/palette"
"image/draw"
"image/jpeg"
"image/png"
"os"
)
func compressImage(inputPath, outputPath string) error {
// 打开原始图像
file, err := os.Open(inputPath)
if err != nil {
return err
}
defer file.Close()
srcImg, _, err := image.Decode(file)
if err != nil {
return err
}
// 使用 WebSafe 调色板(仅 216 色)
dstImg := image.NewPaletted(srcImg.Bounds(), palette.WebSafe)
draw.Draw(dstImg, dstImg.Bounds(), srcImg, image.Point{}, draw.Src)
// 保存为 PNG(调色板图像会自动压缩)
outFile, err := os.Create(outputPath)
if err != nil {
return err
}
defer outFile.Close()
return png.Encode(outFile, dstImg)
}
四、最佳实践
1. 选择合适的调色板
// 场景 1:需要更多颜色细节 -> 使用 Plan9 (256 色)
img1 := image.NewPaletted(bounds, palette.Plan9)
// 场景 2:需要网络兼容性 -> 使用 WebSafe (216 色)
img2 := image.NewPaletted(bounds, palette.WebSafe)
// 场景 3:照片类图像 -> Plan9 更好(连续色调)
// 场景 4:图形/图标 -> WebSafe 足够(颜色较少)
2. 颜色量化技巧
// 技巧 1:使用 draw.Draw 自动进行颜色量化
srcImg := loadFullColorImage()
dstImg := image.NewPaletted(bounds, palette.Plan9)
draw.Draw(dstImg, dstImg.Bounds(), srcImg, image.Point{}, draw.Src)
// 技巧 2:手动选择最接近的颜色(更精确但更慢)
func findClosestColor(target color.Color, pal []color.Color) uint8 {
// 实现颜色距离计算
}
3. GIF 编码优化
// 优化 1:指定颜色数量
gif.Encode(file, img, &gif.Options{
NumColors: 256, // 最多使用 256 色
Quantizer: nil, // 使用默认量化器
Drawer: nil, // 使用默认绘制器
})
// 优化 2:使用自定义调色板
gif.Encode(file, img, &gif.Options{
NumColors: len(palette.WebSafe),
})
五、与其他包配合
1. 与 image/color 配合
package main
import (
"image/color"
"image/color/palette"
)
// 将颜色转换为调色板索引
func colorToIndex(c color.Color, pal []color.Color) uint8 {
cr, cg, cb, _ := c.RGBA()
minDist := uint32(1000000)
index := uint8(0)
for i, pc := range pal {
pr, pg, pb, _ := pc.RGBA()
dist := (cr-pr)*(cr-pr) + (cg-pg)*(cg-pg) + (cb-pb)*(cb-pb)
if dist < minDist {
minDist = dist
index = uint8(i)
}
}
return index
}
2. 与 image/draw 配合
package main
import (
"image"
"image/color/palette"
"image/draw"
)
func main() {
// 创建源图像
src := image.NewRGBA(image.Rect(0, 0, 100, 100))
// 创建目标调色板图像
dst := image.NewPaletted(image.Rect(0, 0, 100, 100), palette.Plan9)
// 使用 draw.Draw 进行颜色量化
draw.Draw(dst, dst.Bounds(), src, image.Point{}, draw.Src)
}
3. 与 image/gif 配合
package main
import (
"image"
"image/color/palette"
"image/gif"
"os"
)
func createGIF() {
// 创建调色板图像
img := image.NewPaletted(image.Rect(0, 0, 200, 200), palette.WebSafe)
// 填充颜色
for i := range img.Pix {
img.Pix[i] = uint8(i % len(palette.WebSafe))
}
// 编码为 GIF
file, _ := os.Create("output.gif")
defer file.Close()
gif.Encode(file, img, nil)
}
六、快速参考
变量总览
| 变量名 | 类型 | 颜色数 | 描述 |
|---|---|---|---|
Plan9 | []color.Color | 256 | Plan 9 操作系统的 256 色调色板 |
WebSafe | []color.Color | 216 | Web 安全色调色板(Netscape Color Cube) |
颜色分布对比
| 特性 | Plan9 | WebSafe |
|---|---|---|
| 总颜色数 | 256 | 216 |
| RGB 细分 | 4×4×4 | 6×6×6 |
| 灰色阴影 | 16 个 | 6 个 |
| 原色阴影 | 13 个/色 | 6 个/色 |
| 适用场景 | 连续色调图像 | 网络图形 |
| 历史来源 | Plan 9 操作系统 | Netscape Navigator |
使用场景推荐
| 场景 | 推荐调色板 | 原因 |
|---|---|---|
| 照片转换 | Plan9 | 更好的连续色调表示 |
| GIF 动画 | Plan9/WebSafe | 取决于颜色需求 |
| 网页图形 | WebSafe | 跨平台颜色一致性 |
| 图标/Logo | WebSafe | 颜色数量足够 |
| 艺术图像 | Plan9 | 更多颜色细节 |
七、注意事项
1. 调色板限制
// 注意:调色板图像最多支持 256 色
img := image.NewPaletted(bounds, palette.Plan9) // ✓ 256 色
img := image.NewPaletted(bounds, palette.WebSafe) // ✓ 216 色
// 如果自定义调色板超过 256 色,GIF 编码会失败
2. 颜色精度损失
// 从真彩色转换到调色板会有精度损失
srcImg := loadTrueColorImage() // 数百万色
dstImg := image.NewPaletted(bounds, palette.WebSafe) // 仅 216 色
draw.Draw(dstImg, dstImg.Bounds(), srcImg, image.Point{}, draw.Src)
// 某些颜色可能无法精确表示
3. 性能考虑
// 颜色量化是计算密集型操作
// 对于大图像,考虑:
// 1. 使用更快的量化算法
// 2. 降低图像分辨率
// 3. 使用更少的颜色数量
八、完整示例:创建调色板颜色样本图
package main
import (
"image"
"image/color"
"image/color/palette"
"image/draw"
"image/png"
"os"
)
func main() {
// 创建显示两种调色板的图像
width := 256
height := 16 * 2 // 两行,每行 16 像素高
img := image.NewRGBA(image.Rect(0, 0, width, height))
// 绘制 Plan9 调色板(上半部分)
for i, c := range palette.Plan9 {
if i >= 256 {
break
}
x := i % 16 * 16
y := i / 16 * 16
rect := image.Rect(x, y, x+16, y+16)
draw.Draw(img, rect, &image.Uniform{c}, image.Point{}, draw.Src)
}
// 绘制 WebSafe 调色板(下半部分)
for i, c := range palette.WebSafe {
if i >= 216 {
break
}
x := i % 16 * 16
y := 16 + i/16*8
rect := image.Rect(x, y, x+16, y+8)
draw.Draw(img, rect, &image.Uniform{c}, image.Point{}, draw.Src)
}
// 保存图像
file, _ := os.Create("palette_comparison.png")
defer file.Close()
png.Encode(file, img)
}
最后更新: 2026-04-04
Go 版本: 1.21+
包文档: https://pkg.go.dev/image/color/palette