diff --git a/blog/image/image.md b/blog/image/image.md new file mode 100644 index 0000000..2edad44 --- /dev/null +++ b/blog/image/image.md @@ -0,0 +1,553 @@ +# #**image package** + +​ 作用:image包实现了一个基本的2D图像库. + +~~~go +func NewNRGBA + +func NewNRGBA(r Rectangle) *NRGBA +~~~ + +​ NewNRGBA返回一个新的带有给定边界的NRGBA图像。NRGBA是一个内存中的图像,它的At方法返回颜色的NRGBA值。 + +~~~go +type NRGBA struct { + + Pix []uint8 + + Stride int + + Rect Rectangle + +} + +func (*NRGBA) Set + +func (p *NRGBA) Set(x, y int, c color.Color) //设定指定位置的color。 + +func Rect + +func Rect(x0, y0, x1, y1 int) Rectangle //Rect是Rectangle的缩写{Pt(x0, y0), Pt(x1, y1)}. +~~~ + +# #**Image/draw包用法:** + +​ draw 包仅仅定义了一个操作:通过可选的蒙版图(mask image),把一个原始图片绘制到目标图片上,这个操作是出奇的灵活,可以优雅和高效的执行很多常见的图像处理任务,比如使用nil蒙版调用DrawMask,其代码如下: + +~~~go +func Draw(dst Image, r image.Rectangle, src image.Image, sp image.Point, op Op) + +func DrawMask(dst Image, r image.Rectangle, src image.Image, sp image.Point, + +mask image.Image, mp image.Point, op Op) +~~~ + +​ 第一个函数Draw是没有使用蒙版mask的调用方法,它内部其实就是调用的mask为 nil的方法,它的参数描述如下: + +​ dst 是绘图的背景图,r是背景图的绘图区域,src 是要绘制的图,sp是src对应的绘图开始点(绘制的大小 r变量定义),mask 是绘图时用的蒙版,控制替换图片的方式。mp是绘图时蒙版开始点(绘制的大小r变量定义了) + +​ 下图就是几个相关的例子: + +​ mask 蒙版是渐变 + +​ 1 + +​ 给一个矩形填充颜色: + +​ 使用 Draw方法的逻辑效果图: + +​ 2 + +​ 代码: + +~~~go + m := image.NewRGBA(image.Rect(0, 0, 640, 480)) + + blue := color.RGBA{0, 0, 255, 255} + + draw.Draw(m, m.Bounds(), &image.Uniform{blue}, image.ZP, draw.Src) +~~~ + +​ 拷贝图片的一部分效果特效如下: + +​ 3 + +​ 相关代码: + +~~~go + r := image.Rectangle{dp, dp.Add(sr.Size())} // 获得更换区域 + + draw.Draw(dst, r, src, sr.Min, draw.Src) +~~~ + +​ 如果是复制整个图片,则更简单: + +~~~go + sr = src.Bounds() // 获取要复制图片的尺寸 + + r := sr.Sub(sr.Min).Add(dp) // 目标图的要剪切区域 + + draw.Draw(dst, r, src, sr.Min, draw.Src) +~~~ + +​ 图片滚动效果如下图: + +​ ![4](C:\Users\Daug\Desktop\image\image\images\4.png) + +​ 假设我们需要把图片 m 上移20个像素. + +​ 相关代码: + +~~~go + b := m.Bounds() + + p := image.Pt(0, 20) + + // 注意,尽管第二个参数是b,但由于剪切,有效矩形变小了。 + + draw.Draw(m, b, m, b.Min.Add(p), draw.Src) + + dirtyRect := b.Intersect(image.Rect(b.Min.X, b.Max.Y-20, b.Max.X, b.Max.Y)) +~~~ + +​ 把一个图片转成RGBA格式,效果图: + +​ 5 + +​ 相关代码:1 + +~~~go + b := src.Bounds() + + m := image.NewRGBA(b) + + draw.Draw(m, b, src, b.Min, draw.Src) +~~~ + +​ 通过蒙版画特效效果图 + +​ 6 + +相关代码 + +~~~go + type circle struct { + + p image.Point + + r int + + } + + func (c *circle) ColorModel() color.Model { + + return color.AlphaModel + + } + + func (c *circle) Bounds() image.Rectangle { + + return image.Rect(c.p.X-c.r, c.p.Y-c.r, c.p.X+c.r, c.p.Y+c.r) + + } + + + + func (c *circle) At(x, y int) color.Color { + + xx, yy, rr := float64(x-c.p.X)+0.5, float64(y-c.p.Y)+0.5, float64(c.r) + + if xx*xx+yy*yy < rr*rr { + + return color.Alpha{255} + + } + + return color.Alpha{0} + + } + + draw.DrawMask(dst, dst.Bounds(), src, image.ZP, &circle{p, r}, image.ZP, draw.Over) +~~~ + +注意,一个image对象只需要实现下面几个就可,这也就是Go接口强大的地方. + +~~~go + type Image interface { + + // 返回图像的颜色模型。 + + ColorModel() color.Model + + // Bounds返回一个可以返回非零颜色的域。 + + // 边界不一定包含点(0,0)。 + + Bounds() Rectangle + + // 返回(x, y)处像素的颜色。 + + // At(Bounds().Min.X, Bounds().Min.Y) 返回网格左上角的像素的值。 + + // At(Bounds().Max.X-1, Bounds().Max.Y-1) 返回右下角的像素的值。 + + At(x, y int) color.Color + + } +~~~ + +​ 画一个字体 + +​ 效果图,画一个蓝色背景的字体。 + +​ 7 + +​ 相关伪代码: + +~~~go + src := &image.Uniform{color.RGBA{0, 0, 255, 255}} + + mask := theGlyphImageForAFont() + + mr := theBoundsFor(glyphIndex) + + draw.DrawMask(dst, mr.Sub(mr.Min).Add(p), src, image.ZP, mask, mr.Min, draw.Over) +~~~ + +# #**image/color包用法:** + +​ Color包实现了一个基本的颜色库:color是一个接口,它定义了可以被视为颜色的任何类型的最小方法集:可以转换为红色、绿色、蓝色和alpha值的颜色。转换可能是有损的,例如从CMYK或YCbCr颜色空间转换。其代码演示如下: + +~~~go +type Color interface { + + RGBA() (r, g, b, a uint32) + +} +~~~ + +​ RGBA返回字母预先乘上的红色、绿色、蓝色和颜色的alpha值。每个值的范围都在[0,0xFFFF]内,但是由uint32表示,因此乘以一个直到0xFFFF的混合因子不会溢出。 + +​ 关于返回值有三个重要的点:首先,红色,绿色和蓝色是先预乘alpha:完全饱和的红色也是25%透明,由RGBA返回75%。第二,容器有16位:100%的红色是由RGBA返回一个65535的r的值,而不是255,所以从CMYK或YCbCr转换没有损耗。第三,返回的类型是uint32,将最大值设置为65535以保证两个值相乘不会溢出。 + +​ Image/color包还定义了许多实现color接口的具体类型。例如,RGBA是一个结构体,它代表了经典的“每个容器8位颜色”,代码如下: + +~~~go +type RGBA struct { + + R, G, B, A uint8 + +} +~~~ + +​ 不过要注意的是:RGBA的R字段是8位字符预乘范围[0,255]的颜色。RGBA通过将该值乘以0x101来满足颜色接口,从而在范围[0,65535]内生成一个16位的颜色。类似地,NRGBA结构类型表示一种8位的没有alpha预乘的颜色,就像PNG图像格式所使用的那样。当直接操作'NRGBA '的字段时,其值没有预乘alpha,但当调用RGBA方法时,返回值要预乘alpha。 + +# #**Image/palette包用法:** + +​ 调色板这个包为提供为图像库提供了一个标准的调色板。 + +Image/color/palette/gen.go:运行程序时会生成palette .go文件。 + +Image/color/palette/generate.go:在生成palette.go文件时会自动运行。 + +Image/color/palette/palette.go:定义了两种调色板——Plan9和WebSafe: + +​ Plan9是一个256色的调色板,将24位RGB空间分割成4×4×4的细分,每个子立方体有4个阴影。与WebSafe相比,其想法是通过将颜色立方体切成更少的单元来降低颜色分辨率,并使用额外的空间来增加强度分辨率。这样就得到了16个灰色阴影(4个灰色子立方体,每个子立方体中有4个样本),每种原色和副色的13个阴影(3个子立方体,4个样本加上黑色),并合理地选择了覆盖颜色立方体其余部分的颜色。其优点是可以更好地表示连续色调。 + +​ WebSafe是一个216色的调色板,由于Netscape Navigator的早期版本而流行起来。它也被称为网景颜色多维数据集。 + +# #**Image/gif包用法:** + +​ gif包实现了gif图片的解码及编码。 + +~~~go +func Decode(r io.Reader) (image.Image, error) //Decode从r中读取一个GIF图像,然后返回的image.Image是第一个嵌入的图。 + +func DecodeConfig(r io.Reader) (image.Config, error) //DecodeConfig不需要解码整个图像就可以返回全局的颜色模型和GIF图片的尺寸。 + +type Config struct { + + ColorModel color.Model + + Width, Height int + +} + +//Config返回图像的颜色model和尺寸 +func Encode(w io.Writer, m image.Image, o *Options) error //将图片m按照gif模式写入w中 + +type Options struct { + + // NumColors是图片中使用颜色的最大值,它的范围是1-256 + + NumColors int + + // Quantizer经常被用来通过NumColors产生调色板,palette.Plan9 被用来替代nil Quantizer + + Quantizer draw.Quantizer + + // Drawer i用于将源图片转化为期望的调色板, draw.FloydSteinberg 用来替代一个空 Drawer. + + Drawer draw.Drawer + +} +func EncodeAll(w io.Writer, g *GIF) error //将图片按照帧与帧之间指定的循环次数和时延写入w中 + +type GIF struct { + + Image []*image.Paletted // 连续的图片 + + Delay []int // 连续的延迟时间,每一帧单位都是百分之一秒,delay中数值表示其两个图像动态展示的时间间隔 + + LoopCount int // 循环次数,如果为0则一直循环。 + +} + +func DecodeAll(r io.Reader) (*GIF, error) //DecodeAll 从r上读取一个GIF图片,并且返回顺序的帧和时间信息 +~~~ + +# #**image/jpeg包用法:** + +​ jpeg包实现了jpeg图片的编码和解码。 + +~~~go +func Decode(r io.Reader) (image.Image, error) //Decode读取一个jpeg文件,并将他作为image.Image返回 + + func DecodeConfig(r io.Reader) (image.Config, error) //无需解码整个图像,DecodeConfig变能够返回整个图像的尺寸和颜色(Config具体定义查看gif包中的定义) + + func Encode(w io.Writer, m image.Image, o *Options) error //按照4:2:0的基准格式将image写入w中,如果options为空的话,则传递默认参数 + +type Options struct { + Quality int +}//Options是编码参数,它的取值范围是1-100,值越高质量越好 + +type FormatError //用来报告一个输入不是有效的jpeg格式 + +type FormatError string + +func (e FormatError) Error() string + +type Reader //不推荐使用Reader + +type Reader interface { + io.ByteReader + io.Reader +} + +type UnsupportedError + +func (e UnsupportedError) Error() string //报告输入使用一个有效但是未实现的jpeg功能 +~~~ + +# #**image/png包用法:** + +​ image/png实现了png图像的编码和解码。 + +​ png和jpeg实现方法基本相同,都是对图像进行了编码和解码操作。 + +~~~go +func Decode(r io.Reader) (image.Image, error) //Decode从r中读取一个图片,并返回一个image.image,返回image类型取决于png图片的内容 + +func DecodeConfig(r io.Reader) (image.Config, error) //无需解码整个图像变能够获取整个图片的尺寸和颜色 + +func Encode(w io.Writer, m image.Image) error //Encode将图片m以PNG的格式写到w中。任何图片都可以被编码,但是哪些不是image.NRGBA的图片编码可能是有损的。 + +type FormatError + +func (e FormatError) Error() string //FormatError会提示一个输入不是有效PNG的错误。 + +~~~ + + 由此可见,png和jpeg使用方法类似,只是两种不同的编码和解码方式。 + +# #**应用如下:** + +## 1.生成图片: + +~~~go +package main + +import "image"import "image/color"import "image/png"import "os" + +func main() { + + //生成一个100X50的图像。 + + img := image.NewRGBA(image.Rect(0, 0, 100, 50)) + + // 在 (2, 3)处画一个红点。 + + img.Set(2, 3, color.RGBA{255, 0, 0, 255}) + + // 保存到out.png + + f, _ := os.OpenFile("out.png", os.O_WRONLY|os.O_CREATE, 0600) + + defer f.Close() + + png.Encode(f, img) + +} +~~~ + +运行结果,就是生成了一张png文件。 + +## 2.生成复杂色彩的图片: + +~~~go +package main + +import ( + + "fmt" + + "image" + + "image/color" + + "image/png" + + "math" + + "os" + +) + +type Circle struct { + + X, Y, R float64 + +} + +func (c *Circle) Brightness(x, y float64) uint8 { + + var dx, dy float64 = c.X - x, c.Y - y + + d := math.Sqrt(dx*dx+dy*dy) / c.R + + if d > 1 { + + return 0 + + } else { + + return 255 + + } + +} + +func main() { + + var w, h int = 280, 240 + + var hw, hh float64 = float64(w / 2), float64(h / 2) + + r := 40.0 + + θ := 2 * math.Pi / 3 + + cr := &Circle{hw - r*math.Sin(0), hh - r*math.Cos(0), 60} + + cg := &Circle{hw - r*math.Sin(θ), hh - r*math.Cos(θ), 60} + + cb := &Circle{hw - r*math.Sin(-θ), hh - r*math.Cos(-θ), 60} + + m := image.NewRGBA(image.Rect(0, 0, w, h)) + + for x := 0; x < w; x++ { + + for y := 0; y < h; y++ { + + c := color.RGBA{ + + cr.Brightness(float64(x), float64(y)), + + cg.Brightness(float64(x), float64(y)), + + cb.Brightness(float64(x), float64(y)), + + 255, + + } + + m.Set(x, y, c) + + } + + } + + f, err := os.OpenFile("rgb.png", os.O_WRONLY|os.O_CREATE, 0600) + + if err != nil { + + fmt.Println(err) + + return + + } + + defer f.Close() + + png.Encode(f, m) + +} +~~~ + +运行结果: + 8 + +## 3.获取一张图片的尺寸 + +~~~go +package main + +import ( + + "fmt" + + "image" + + _ "image/jpeg" + + _ "image/png" + + "os" + +) + +func main() { + + width, height := getImageDimension("rgb.png") + + fmt.Println("Width:", width, "Height:", height) + +} + +func getImageDimension(imagePath string) (int, int) { + + file, err := os.Open(imagePath) + + defer file.Close() + + if err != nil { + + fmt.Fprintf(os.Stderr, "%v\n", err) + + } + + + + image, _, err := image.DecodeConfig(file) + + if err != nil { + + fmt.Fprintf(os.Stderr, "%s: %v\n", imagePath, err) + + } + + return image.Width, image.Height + +} +~~~ + diff --git a/blog/image/images/1.png b/blog/image/images/1.png new file mode 100644 index 0000000..272e4a4 Binary files /dev/null and b/blog/image/images/1.png differ diff --git a/blog/image/images/2.png b/blog/image/images/2.png new file mode 100644 index 0000000..33e068c Binary files /dev/null and b/blog/image/images/2.png differ diff --git a/blog/image/images/3.png b/blog/image/images/3.png new file mode 100644 index 0000000..fe2899a Binary files /dev/null and b/blog/image/images/3.png differ diff --git a/blog/image/images/4.png b/blog/image/images/4.png new file mode 100644 index 0000000..c59f3c0 Binary files /dev/null and b/blog/image/images/4.png differ diff --git a/blog/image/images/5.png b/blog/image/images/5.png new file mode 100644 index 0000000..f9f064c Binary files /dev/null and b/blog/image/images/5.png differ diff --git a/blog/image/images/6.png b/blog/image/images/6.png new file mode 100644 index 0000000..259cb3a Binary files /dev/null and b/blog/image/images/6.png differ diff --git a/blog/image/images/7.png b/blog/image/images/7.png new file mode 100644 index 0000000..ccc2e2f Binary files /dev/null and b/blog/image/images/7.png differ diff --git a/blog/image/images/8.png b/blog/image/images/8.png new file mode 100644 index 0000000..8d79bbf Binary files /dev/null and b/blog/image/images/8.png differ