转载自微信公众号:火炉点点
前置知识点
RGBA:RGB 是三元色(red、green、blue)这个好理解,A 是 alpha 指透明度。
色彩模型:这个概念其实日常生活中接触的不少,常见的色彩模型有:
RGB:光混合配色体系;
CMY:颜色混合配色体系,C - Cyan 青,M - Magenta 品红,Y - Yellow 黄,印刷时常用这个配色体系;
CMYK:这个是在 CMY 基础上加个表示黑色的 K,因为只用 CMY 配出来的黑色是灰黑色,不够黑;
HSV:每一种颜色都是由色相(Hue,简H),饱和度(Saturation,简S)和色明度(Value,简V)所表示的;
HSI:HSI色彩空间是从人的视觉系统出发,用色调(Hue)、色饱和度(Saturation或Chroma)和亮度 (Intensity或Brightness)来描述色彩;
Transparent:透明的;它在 image 库中表示一个完全透明的面积无限大的图像。
Opaque:不透明的;它在 image 库中表示一个完全不透明的面积无限大的图像。
Palette:调色板,一组颜色;图像处理中为了节省空间,因此数据区只存储当前像素点在调色板中的索引,而表示颜色的 RBG 值保存在调色板中。
坐标轴:这个和我们在数学中的坐标轴在“方向”上有点不一样,这个原点在左上角,X 轴向右,Y 轴向下。
玩几个栗子
先画个正弦曲线热热身
func TestSin(t *testing.T) {
picture := image.NewRGBA(image.Rect(0, 0, 800, 300))
for i := 0; i <= 800; i++ {
// 将 0-8pi 的区间放到 800pix 里;将 sin 函数振幅放大50倍
high := int(50 * math.Sin(0.01 * math.Pi * float64(i)))
// 把 sin 曲线的横坐标放在 y=150pix 的地方。
// 注意图片的坐标原点在左上角,所以这里是 150-high
picture.SetRGBA(i, 150-high, color.RGBA{R: 241, G: 228, B: 86, A: 255})
}
w , err := os.Create("./sin.jpeg")
if err != nil {
t.Fatal(err)
}
defer w.Close()
jpeg.Encode(w, picture, nil)
}
还可以再炫点吗?想了想我们看到不少图片都是某个物体基于某个曲线划过的区域绘制而成。这样说,听起来太不感性了,我们现在想象有一把刷子(长 151 个像素),刷子的中点一直在正弦曲线上,刷子和水平线的夹角为曲线上该点横坐标(三角函数的横坐标为角度),那么刷子扫过的区域就被刷子染色了。看看这样会是什么效果, 先来看看图:
有没有感觉里面有“佛”,哈哈(有妹子说像苗族的首饰,好吧,可能我比较直男吧[掩面笑])。下面来看看代码:
type brush struct {
// 刷子长为 2 * radius + 1
radius, middle int
colors []color.RGBA
}
func pen(redius int) *brush {
colors := make([]color.RGBA, redius*2 + 1)
rand.Seed(time.Now().Unix())
// 我也不知道这刷子怎么样才会好看,那就随机吧
for i := 0; i < cap(colors); i++ {
c := color.RGBA{}
rgba := rand.Uint32()
log.Println(rgba)
c.R = uint8((rgba & 0xff000000) >> 24)
c.G = uint8((rgba & 0x00ff0000) >> 16)
c.B = uint8((rgba & 0x0000ff00) >> 8)
c.A = uint8((rgba & 0x000000ff))
colors[i] = c
}
return &brush{radius: redius, middle: redius, colors: colors}
}
func TestPainting(t *testing.T) {
r := image.Rect(0, 0, 800, 300)
picture := image.NewRGBA(r)
brush := pen(50)
for i := 0; i < 800; i++ {
high := int(50 * math.Sin(0.01 * math.Pi * float64(i)))
// 刷子中点位置,由于图片坐标系原点在左上角,所以这里是减
pivot := image.Pt(i, 150-high)
picture.SetRGBA(pivot.X, pivot.Y, brush.colors[brush.middle])
for j := 1; j <= brush.radius; j++ {
xOffset := float64(j) * math.Cos(0.01 * math.Pi * float64(i))
yOffset := float64(j) * math.Sin(0.01 * math.Pi * float64(i))
// 刷子上对应点的颜色
bc1 := brush.colors[brush.middle + j]
bc2 := brush.colors[brush.middle - j]
// 图片上对应点的颜色
pc1 := picture.At(pivot.X + int(xOffset), pivot.Y - int(yOffset))
pc2 := picture.At(pivot.X - int(xOffset), pivot.Y + int(yOffset))
cr1, cg1, cb1, ca1 := pc1.RGBA()
cr2, cg2, cb2, ca2 := pc2.RGBA()
// 将刷子对应点的颜色与图片上对应点的颜色相加
rgba1 := color.RGBA{R: bc1.R + uint8(cr1), G: bc1.G + uint8(cg1), B: bc1.B + uint8(cb1), A: bc1.A + uint8(ca1)}
rgba2 := color.RGBA{R: bc2.R + uint8(cr2), G: bc2.G + uint8(cg2), B: bc2.B + uint8(cb2), A: bc2.A + uint8(ca2)}
picture.Set(pivot.X + int(xOffset), pivot.Y - int(yOffset), rgba1)
picture.Set(pivot.X - int(xOffset), pivot.Y + int(yOffset), rgba2)
}
}
w, err := os.Create("./picture.jpeg")
if err != nil {
t.Fatal(err)
}
defer w.Close()
if err = jpeg.Encode(w, picture, nil); err != nil {
t.Fatal(err)
}
}
试过 sin 函数之后想看看有没有更漂亮的,于是查了一番发现个 Lissajours-Figure 挺好玩的(曲线方程可以查看百度百科),可以根据参数不同生成好多不同的形状,看图:
代码如下:
func TestLissajous(t *testing.T) {
picture := image.NewRGBA(image.Rect(0, 0, 1000, 1000))
a, b, p , q, s := 500.00, 500.00, 22.00, 15.00, math.Pi/6
r := 0.00
for r <= 2*math.Pi {
x := int(a * math.Sin(p * r))
y := int(b * math.Sin(q * r + s))
picture.SetRGBA(500+x, 500-y, color.RGBA{R: 234, G: 225, B: 86, A: 255})
r += 0.0001
}
w, err := os.Create("./lissajours.jpeg")
if err != nil {
t.Fatal(err)
}
defer w.Close()
jpeg.Encode(w, picture, nil)
}
再来个❤️型线,就打完收工:
func TestHeart(t *testing.T) {
picture := image.NewRGBA(image.Rect(0, 0, 600, 600))
s := 0.00
for s <= 2*math.Pi {
r := 100*(1 - math.Sin(s))
x := r * math.Cos(s)
y := r * math.Sin(s)
picture.SetRGBA(300+int(x), 200-int(y), color.RGBA{R: 255, G: 1, B: 1, A: 255})
s += 0.0001
}
w, err := os.Create("./heart.jpeg")
if err != nil {
t.Fatal(err)
}
defer w.Close()
jpeg.Encode(w, picture, nil)
}