Golang Image(一) - 图像简要说明

1. 图像

  1. color.Color和color.Model描述的颜色
  2. image.Point和image.Rectangle描述2d几何形状

2. 颜色、颜色模型

  1. 红色,绿色和蓝色是α预乘的,完全饱和的红色也是25%透明的,
  2. 通道具有16位有效范围,100%红色返回65535而不是255
  3. 要保证颜色值调和时候,乘积不会溢出
  4. color.Model只是可以将“颜色”转换为“另一种颜色”的东西,可能会有损
// 颜色类型
type Color interface {
    // RGBA returns the alpha-premultiplied red, green, blue and alpha values
    // for the color. Each value ranges within [0, 0xFFFF], but is represented
    // by a uint32 so that multiplying by a blend factor up to 0xFFFF will not
    // overflow.
    RGBA() (r, g, b, a uint32)
}

3. 点、矩形

// 点类型
type Point struct {
    X, Y int
}
p := image.Point{2, 1}
// 矩形类型
type Rectangle struct {
    Min, Max Point
}

3.1. 点

点没有宽高和颜色,既不是像素,也不是网格

3.2. 矩形

  1. Rectangle是整数网格上的轴对齐矩形,由其左上角右下角Point定义。
  2. 方便起见,使用image.Rect(x0, y0, x1, y1),替代了image.Rectangle{image.Point{x0, y0}, image.Point{x1, y1}}
  3. Rectangle通常具有非零原点
  4. 网格上的(x,y)坐标,轴向右和向下增加
// 1. 基本矩形定义,(x0, y0, x1, y1)
r := image.Rect(2, 1, 5, 5)
// Dx and Dy return a rectangle's width and height.
fmt.Println(r.Dx(), r.Dy(), image.Pt(0, 0).In(r)) // prints 3 4 false

// 2. 矩形做向量位移操作(x左移,y上移)
r := image.Rect(2, 1, 5, 5).Add(image.Pt(-4, -2))
fmt.Println(r.Dx(), r.Dy(), image.Pt(0, 0).In(r)) // prints 3 4 true

// 3. 矩形相交,相交两个矩形会产生另一个Rectangle,它可能是空的
r := image.Rect(0, 0, 4, 3).Intersect(image.Rect(2, 2, 5, 5))
// Size returns a rectangle's width and height, as a Point.
fmt.Printf("%#v\n", r.Size()) // prints image.Point{X:2, Y:1}

4. 图像=矩形+像素点

4.1. 像素

像素(x, y),表示增量方格(x, y), (x+1, y), (x+1, y+1) and (x, y+1)框定处的颜色

4.2. 图像

  1. 图像将矩形中的每个网格方块映射到模型中的颜色。
  2. Image的边界不一定是从(0,0)点开始,正确的应该是基于边框的Min.XMin.YMax.XMax.Y来迭代
  3. 通常,程序会想要基于像素切片的图像,图像实现不必基于像素数据的内存中切片,例如,Uniform是一个巨大边界和均匀颜色的图像,其内存表示只是那种颜色。
  4. 图像类型提供Set(x,y int,c color.Color)方法,该方法允许一次一个像素地修改图像
// 图像包含颜色模型、图像区域范围、以及在这些区域内的颜色值
type Image interface {
    // ColorModel returns the Image's color model.
    ColorModel() color.Model
    // Bounds returns the domain for which At can return non-zero color.
    // The bounds do not necessarily contain the point (0, 0).
    Bounds() Rectangle
    // At returns the color of the pixel at (x, y).
    // At(Bounds().Min.X, Bounds().Min.Y) returns the upper-left pixel of the grid.
    // At(Bounds().Max.X-1, Bounds().Max.Y-1) returns the lower-right one.
    At(x, y int) color.Color
}

// 正确的迭代像素操作
b := m.Bounds()
for y := b.Min.Y; y < b.Max.Y; y++ {
    for x := b.Min.X; x < b.Max.X; x++ {
        doStuffWith(m.At(x, y))
    }
}

// RGBA类型
type RGBA struct {
    // Pix holds the image's pixels, in R, G, B, A order. The pixel at
    // (x, y) starts at Pix[(y-Rect.Min.Y)*Stride + (x-Rect.Min.X)*4].
    Pix []uint8
    // Stride is the Pix stride (in bytes) between vertically adjacent pixels.
    Stride int
    // Rect is the image's bounds.
    Rect Rectangle
}
// 创建一个RGBA图片,并设定(5,5)处的像素颜色为指定的RGBA颜色
m := image.NewRGBA(image.Rect(0, 0, 640, 480))
m.Set(5, 5, color.RGBA{255, 0, 0, 255})

4.3. SubImage方法

  1. 修改子图像的像素将影响原始图像的像素
  2. 对于适用于图像的Pix场的低级代码,Pix上的测距会影响图像边界外的像素,比如在示例中,m1.Pix覆盖的像素以蓝色阴影显示。
m0 := image.NewRGBA(image.Rect(0, 0, 8, 5))
m1 := m0.SubImage(image.Rect(1, 2, 5, 5)).(*image.RGBA)
fmt.Println(m0.Bounds().Dx(), m1.Bounds().Dx()) // prints 8, 4
fmt.Println(m0.Stride == m1.Stride)             // prints true

5. 图像格式编码

5.1. 已知图像格式

标准软件包库支持许多常见的图像格式,例如GIF,JPEG和PNG,知道源图像文件的格式,则可以直接从io.Reader解码。

import (
 "image/jpeg"
 "image/png"
 "io"
)

// convertJPEGToPNG converts from JPEG to PNG.
func convertJPEGToPNG(w io.Writer, r io.Reader) error {
    img, err := jpeg.Decode(r)
    if err != nil {
        return err
    }
    return png.Encode(w, img)
}

5.2. 未知图像格式

针对未知格式的图像数据,image.Decode函数可以检测格式,图像格式包(比如image/jpeg)通常在init函数中注册其格式,我们只需要在主包将“强调导入”这样的包仅用于格式注册的副作用即可使用。

import (
    "image"
    "image/png"
    "io"

    _ "code.google.com/p/vp8-go/webp"
    _ "image/jpeg"
)

// convertToPNG converts from any recognized format to PNG.
func convertToPNG(w io.Writer, r io.Reader) error {
    img, _, err := image.Decode(r)
    if err != nil {
        return err
    }
    return png.Encode(w, img)
}

5.3. 补充包匿名导入

在程序的main主程序包中, _ "image/jpeg"表示纯粹为其初始化副作用导入包,我们称之为包的匿名导入

如果只是导入一个包而并不使用导入的包将会导致一个编译错误,但是有时候我们只是想利用导入包而产生的副作用:它会计算包级变量的初始化表达式和执行导入包的init初始化函数,这时候我们需要抑制“unused import”编译错误,我们可以**用下划线_**来重命名导入的包。

image/png包,通过init(),在其中提供了图像image.RegisterFormat注册格式,方便主包的匿名导入,达到解析png的目的

package png // image/png

func Decode(r io.Reader) (image.Image, error)
func DecodeConfig(r io.Reader) (image.Config, error)

func init() {
    const pngHeader = "\x89PNG\r\n\x1a\n"
    image.RegisterFormat("png", pngHeader, Decode, DecodeConfig)
}

6. 图像resize

图片Resize四个主要步骤:open、decode、resize、encode

补一张图:

7. 参考