97 lines
2.1 KiB
Go
97 lines
2.1 KiB
Go
package vision
|
|
|
|
import (
|
|
"fmt"
|
|
"image/color"
|
|
"sort"
|
|
|
|
"github.com/disintegration/imaging"
|
|
)
|
|
|
|
// ColorCount 记录颜色及其出现的频率
|
|
type ColorCount struct {
|
|
Color color.Color
|
|
Hex string
|
|
Count int
|
|
}
|
|
|
|
// ExtractPalette 从图像中提取调色板(主要颜色)
|
|
// n: 提取的前 n 种颜色
|
|
func (c *Canvas) ExtractPalette(n int) []ColorCount {
|
|
img := c.dc.Image()
|
|
// 为了性能,先缩小图片
|
|
resized := imaging.Resize(img, 100, 100, imaging.NearestNeighbor)
|
|
bounds := resized.Bounds()
|
|
|
|
counts := make(map[uint32]int)
|
|
for y := bounds.Min.Y; y < bounds.Max.Y; y++ {
|
|
for x := bounds.Min.X; x < bounds.Max.X; x++ {
|
|
r, g, b, a := resized.At(x, y).RGBA()
|
|
// 忽略透明度较高的像素
|
|
if a < 32768 { continue }
|
|
|
|
// 简单的颜色量化,减少颜色数量
|
|
r >>= 12
|
|
g >>= 12
|
|
b >>= 12
|
|
key := (r << 8) | (g << 4) | b
|
|
counts[key]++
|
|
}
|
|
}
|
|
|
|
palette := make([]ColorCount, 0, len(counts))
|
|
for key, count := range counts {
|
|
r := uint8((key >> 8) & 0xF) * 17
|
|
g := uint8((key >> 4) & 0xF) * 17
|
|
b := uint8(key & 0xF) * 17
|
|
c := color.RGBA{r, g, b, 255}
|
|
palette = append(palette, ColorCount{
|
|
Color: c,
|
|
Hex: RGBAToHex(c),
|
|
Count: count,
|
|
})
|
|
}
|
|
|
|
sort.Slice(palette, func(i, j int) bool {
|
|
return palette[i].Count > palette[j].Count
|
|
})
|
|
|
|
if len(palette) > n {
|
|
palette = palette[:n]
|
|
}
|
|
return palette
|
|
}
|
|
|
|
// RGBAToHex 将 RGBA 转换为 Hex 字符串
|
|
func RGBAToHex(c color.RGBA) string {
|
|
return fmt.Sprintf("#%02X%02X%02X", c.R, c.G, c.B)
|
|
}
|
|
|
|
// GetAverageColor 计算画布的平均颜色
|
|
func (c *Canvas) GetAverageColor() color.Color {
|
|
img := c.dc.Image()
|
|
bounds := img.Bounds()
|
|
var r, g, b, a uint64
|
|
w, h := bounds.Dx(), bounds.Dy()
|
|
|
|
for y := bounds.Min.Y; y < bounds.Max.Y; y++ {
|
|
for x := bounds.Min.X; x < bounds.Max.X; x++ {
|
|
pr, pg, pb, pa := img.At(x, y).RGBA()
|
|
r += uint64(pr)
|
|
g += uint64(pg)
|
|
b += uint64(pb)
|
|
a += uint64(pa)
|
|
}
|
|
}
|
|
|
|
total := uint64(w * h)
|
|
if total == 0 { return color.Transparent }
|
|
|
|
return color.RGBA64{
|
|
R: uint16(r / total),
|
|
G: uint16(g / total),
|
|
B: uint16(b / total),
|
|
A: uint16(a / total),
|
|
}
|
|
}
|