来一个嫦娥奔月祝 xdm 中秋快乐
GOLang 有很多图形库,咱们今天玩一个简单的标准图形库 image
image 包实现了一个基本的二维图像库,基本的接口叫做 Image 。
我们可以这样导入包
"image" "image/color" "image/draw" "image/jpeg" "image/png"
GO image 的官方文档: pkg.go.dev/image 在 GOLAND 开发工具里面导入image 包,我们可以看到源码目录如下
- color
实现了一个基本的颜色库
- draw
绘制提供图像组成功能
- gif
gif 图像解码器和编码器
- jpeg
JPEG 图像解码器和编码器
- png
PNG 图像解码器和编码器
- internal
imageutil包含与图像相关的包共享的代码
image.go 源码
图像是一个有限的矩形颜色网格。从颜色中获取的颜色值
type Config struct { ColorModel color.Model Width, Height int } type Image interface { ColorModel() color.Model Bounds() Rectangle At(x, y int) color.Color }
image 包里面的 image.go 是具体实现,且包里面有相应的单元测试可以模仿学习 ,一起来看看 Image
接口 , 这个 Image
接口是 image 包的基本接口,非常重要
一个图像包含颜色,它是在 image/color
包中进行描述的 , Image 接口的值是通过调用这样的函数(NewRGBA 和 NewPaletted,或通过调用解码的 io)来创建的
- ColorModel() color.Model
ColorModel返回图像的颜色模型
- Bounds() Rectangle
Bounds返回At可以为其返回非零颜色的域,边界不一定包含点(0,0)。
- At(x, y int) color.Color
At 返回像素在 (x, y) 处的颜色 ,
At(Bounds().Min.X, Bounds().Min.Y) 会返回网格左上角的像素
At(Bounds().Max.X-1, Bounds().Max.Y-1) 会返回网格右下角的像素
咱们可以通过 image 的单元测试来玩一下这个库
案例1 画一张 png 图片
我们使用“image/png”
包来进行绘制图像
// 画一张彩图 func drawEncode(){ const width, height = 500, 500 // 画一张彩图,指定好宽和高 img := image.NewNRGBA(image.Rect(0, 0, width, height)) // 一张个图像分为 上下两个部分进行渲染 for y := 0; y < height/2; y++ { for x := 0; x < width; x++ { img.Set(x, y, color.NRGBA{ R: uint8((x - y) & 255), G: uint8((x - y) << 1 & 255), B: uint8((x - y) << 2 & 255), A: 255, }) } } for y := height/2; y < height; y++ { for x := 0; x < width; x++ { img.Set(x, y, color.NRGBA{ R: uint8((x + y) & 255), G: uint8((x + y) << 1 & 255), B: uint8((x + y) << 2 & 255), A: 255, }) } } f, err := os.Create("image.png") if err != nil { log.Fatal(err) } if err := png.Encode(f, img); err != nil { f.Close() log.Fatal(err) } if err := f.Close(); err != nil { log.Fatal(err) } }
- image.NewNRGBA
创建一张图片,指定好宽和高
- img.Set(x, y, color.NRGBA{})
设置颜色
NRGBA 结构体如下:
// NRGBA represents a non-alpha-premultiplied 32-bit color. type NRGBA struct { R, G, B, A uint8 }
我们知道红绿蓝是计算机色彩的三基色
R: uint8((x - y) & 255), G: uint8((x - y) << 1 & 255), B: uint8((x - y) << 2 & 255),
通过与 和 移位 方式来计算 R G B 的值分别是多少
- R
红色
- G
绿色
- B
蓝色
- A
透明度,255 则为不透明 , 0 为全透明
效果如下:
透明度 255 的效果如下
透明度上半部分设置 255, 下半部分设置 100
案例 2 来一个应景的 嫦娥奔月
准备如下素材
1、月亮一张
moon.jpg
2、嫦娥一枚
goddess.jpg
尝试使用 golang image 里面提供的包 jpeg 来进行图片合并操作
image/jpeg func drawGoddess(){ file, err := os.Create("res.jpg") if err != nil { fmt.Println(err) } defer file.Close() file1, err := os.Open("./back.jpg") if err != nil { fmt.Println(err) } defer file1.Close() img, _ := jpeg.Decode(file1) file2, err := os.Open("./goddess.jpg") if err != nil { fmt.Println(err) } defer file2.Close() img2, _ := jpeg.Decode(file2) file3, err := os.Open("./moon.jpg") if err != nil { fmt.Println(err) } defer file3.Close() img3, _ := jpeg.Decode(file3) // 设置画布大小 1250 * 790 jpg := image.NewRGBA(image.Rect(0, 0, 1250, 790)) draw.Draw(jpg, jpg.Bounds(), img, img.Bounds().Min, draw.Over) //首先将一个图片信息存入jpg draw.Draw(jpg, jpg.Bounds(), img3, img3.Bounds().Min.Sub(image.Pt(140, 0)), draw.Over) //将另外一张图片信息存入jpg draw.Draw(jpg, jpg.Bounds(), img2, img2.Bounds().Min.Sub(image.Pt(790, 315)), draw.Over) jpeg.Encode(file, jpg, nil) }
- 创建一张图片
res.jpg
, 打开三张图片,分别是月亮,嫦娥,和背景图片 jpeg.Decode
将 月亮,嫦娥,和背景图片解码- image.NewRGBA
创建画布,设置画布大小 1250 * 790
- draw.Draw
分别将月亮,嫦娥,和背景图片 画到画布上面,并设置好合适的位置
- jpeg.Encode
将图片数据,回写到 res 的 io 中,生成 res.jpg 文件,效果如下图
案例 3 将图片做成字符画
将图片转成字符串也非常简单,思路简单如下几步:
- 读取图片,成二进制流
- 将二进制流通过 base64 加密成字符串
- 再使用 base64 将字符串转成二进制流
- 使用 “image/png” 的包解码出来,按照图片的宽高,咱们用字符来填充即可
上述第二步主要是得到一个字符串,这个字符串,可以写到文件中,也可以获取后,定义成 const,作为 demo
head.png
原图如下:
func imgToBase64()string{ srcByte, err := ioutil.ReadFile("head.png") if err != nil { log.Fatal(err) } res := base64.StdEncoding.EncodeToString(srcByte) return res } func drawDecode(str string){ // 例如知道某张图片的base64,那么就可以直接定义一个常量来进行处理 //const head = `iVBORw0KGgoAAAANSUhEUgAAABwAAAAcCAIAAAD9b0jDAAAAAXNSR0IArs4c6QAAAARnQU1BAACxjwv8YQUAAAAJcEhZcwAAEnQAABJ0Ad5mH3gAAAFpSURBVEhLtZTblcMgDESpi4JcD9W4GRfjlTTSIBOcxTnJfOzyGF09QlLOH2iEHq2WUmo7fD8I17zft6l5Feo00bb7kcig+QB6VGkwzKSsDtUVTKKSargTU1ygekqoLWASBdTSiYZKzczwq2rbIxRwhIhWodzACUpzc7OdE0TrUNyoppUn5vJME7JWT0Mwa3A9mCnbVA8HaiMYqMtPam/tsBRIn3xMTH30Tr1xjtG3vvsMSnaSnvnyFfoV/RRqbaeuZy1OhXmpevRDqB3fieGx8oT+FkeFHa5/G1CHWvV7YuHyqvWcleYWwLxmYHlJReIEULdNY/UvXFMomo9C30BxOX7bOipBQXknBwwf1HSiptdBIsdNpaZUUOzuUblwW+tcknUK9YHqv6EQTBUEQqMEeifQvrxkckg/wN53ng07/fRVEQ8nW8mBcqc/qp4i7mgVrDzHw6B+CocpDcdkRSRY9ubjrmj/izrPP5x5Eu8xcV+2AAAAAElFTkSuQmCC` // 使用 png.Decode 解码 png 图片 img, err := png.Decode(base64.NewDecoder(base64.StdEncoding, strings.NewReader(str))) if err != nil { log.Fatal(err) } levels := []string{" ", "$", "=", "@", "█"} for y := img.Bounds().Min.Y; y < img.Bounds().Max.Y; y++ { for x := img.Bounds().Min.X; x < img.Bounds().Max.X; x++ { c := color.GrayModel.Convert(img.At(x, y)).(color.Gray) level := c.Y / 51 // 51 * 5 = 255 if level == 5 { level-- } fmt.Print(levels[level]) } fmt.Print("\n") } }
- imgToBase64
将图片二进制流通过 base64 加密得到 字符串
- drawDecode
从字符串中解析出 png 图片,获取到 png 图片的属性,并使用自定义的字符进行填充
- color.GrayModel.Convert
设置颜色模式
效果如下:
██████████████████████████████████████████████████████ ████████████████████@ ████████████████████████████████ ████████████████████@ ████████████████████████████████ ████████████████████@ ████████████████████████████████ ████████@ ███$$██ = ██$@ █= @████████████████████ █████████=$█@ ██$=██$ ██$ ██$ @█@ ████████████████████ ██████████$= ██@ ███@ ██$@██@ ███ @███████████████████ ███████████ @██@ ███@ ██$@██@$███ @███████████████████ ██████████$= ██@ ███@ ██$@██@$███ @███████████████████ █████████=$█=$██ =██ ██$@██@$███ @███████████████████ ████████= ███ $██ @ ██$@██@$███ @███████████████████ ██████████████████████████████████████████████████████ ██████████████████████████████████████████████████████ ██████████████████████████████████████████████████████ ██████$█████████$ ███ ██████ ████=@████████████ ████ ██████$██████@ @████ ██████ ████=@█████@ @██████ ██████$████████$@████ ████@$ @ @██@$████████████ █ ███$@█$██ ██@@@= @$██=@█@@██==███$@███████ █ ████$████$█$ $@█ ██$█@@ ████=@█@@██$@███$@███████ █ ████$████$███ @██$█ @$@█$@ ████$@█@@██ @██ █ ████$████$██@ @█== $=████ █ $██████$@███████ █ ██=$@=@█=$ █████ ████ $███████ ██$@█ █████ █ ████$████$█@=$@███ @ @████ ███@=$@█████$@██$@█@ ████ ██████$██████=█$@██ $█@ ████ ██@ ██ ████ @███$@██@ @██ ██████$████████$@█ ███= ███ █@ ███@ @█=@████$@███@$██ ██████$████████$@$=█████=$██ = ██████ @███@ ████████ ██████████████████████████████████████████████████████ ██████████████████████████████████████████████████████ ██████████████████████████████████████████████████████ ██████████████████████████████████████████████████████ ██████████████████████████████████████████████████████
参考资料:
欢迎点赞,关注,收藏
朋友们,你的支持和鼓励,是我坚持分享,提高质量的动力
好了,本次就到这里
技术是开放的,我们的心态,更应是开放的。拥抱变化,向阳而生,努力向前行。
我是阿兵云原生,欢迎点赞关注收藏,下次见~