將彩色影象轉換為灰度

一些數字影象處理演算法如邊緣檢測,由影象強度(即灰度值)攜帶的資訊就足夠了。使用顏色資訊(R, G, B 通道)可以提供稍好的結果,但演算法複雜度會增加。因此,在這種情況下,我們需要在應用這種演算法之前將彩色影象轉換為灰度影象。

以下程式碼是將任意影象轉換為 8 位灰度影象的示例。使用 net/http 包從遠端位置檢索影象,轉換為灰度,最後儲存為 PNG 影象。

package main

import (
    "image"
    "log"
    "net/http"
    "os"

    _ "image/jpeg"
    "image/png"
)

func main() {
    // Load image from remote through http
    // The Go gopher was designed by Renee French. (http://reneefrench.blogspot.com/)
    // Images are available under the Creative Commons 3.0 Attributions license.
    resp, err := http.Get("http://golang.org/doc/gopher/fiveyears.jpg")
    if err != nil {
        // handle error
        log.Fatal(err)
    }
    defer resp.Body.Close()

    // Decode image to JPEG
    img, _, err := image.Decode(resp.Body)
    if err != nil {
        // handle error
        log.Fatal(err)
    }
    log.Printf("Image type: %T", img)

    // Converting image to grayscale
    grayImg := image.NewGray(img.Bounds())
    for y := img.Bounds().Min.Y; y < img.Bounds().Max.Y; y++ {
        for x := img.Bounds().Min.X; x < img.Bounds().Max.X; x++ {
            grayImg.Set(x, y, img.At(x, y))
        }
    }

    // Working with grayscale image, e.g. convert to png
    f, err := os.Create("fiveyears_gray.png")
    if err != nil {
        // handle error
        log.Fatal(err)
    }
    defer f.Close()

    if err := png.Encode(f, grayImg); err != nil {
        log.Fatal(err)
    }
}

通過 Set(x, y int, c color.Color) 分配畫素時會發生顏色轉換,Set(x, y int, c color.Color)image.go 中實現

func (p *Gray) Set(x, y int, c color.Color) {
    if !(Point{x, y}.In(p.Rect)) {
        return
    }

    i := p.PixOffset(x, y)
    p.Pix[i] = color.GrayModel.Convert(c).(color.Gray).Y
}

其中,color.GrayModelcolor.go被定義為

func grayModel(c Color) Color {
    if _, ok := c.(Gray); ok {
        return c
    }
    r, g, b, _ := c.RGBA()

    // These coefficients (the fractions 0.299, 0.587 and 0.114) are the same
    // as those given by the JFIF specification and used by func RGBToYCbCr in
    // ycbcr.go.
    //
    // Note that 19595 + 38470 + 7471 equals 65536.
    //
    // The 24 is 16 + 8. The 16 is the same as used in RGBToYCbCr. The 8 is
    // because the return value is 8 bit color, not 16 bit color.
    y := (19595*r + 38470*g + 7471*b + 1<<15) >> 24

    return Gray{uint8(y)}
}

基於以上事實,強度 Y 使用以下公式計算:

亮度:Y = 0.299 R + 0.587 G + 0.114 B.

如果我們想要應用不同的公式/演算法來將顏色轉換為強度,例如

平均值:Y =( R + G + B )/ 3
Luma:Y = 0.2126 R + 0.7152 G + 0.0722 B
光澤:Y =(min( RGB )+ max( RGB ))/ 2

然後,可以使用以下程式碼段。

// Converting image to grayscale
grayImg := image.NewGray(img.Bounds())
for y := img.Bounds().Min.Y; y < img.Bounds().Max.Y; y++ {
    for x := img.Bounds().Min.X; x < img.Bounds().Max.X; x++ {
        R, G, B, _ := img.At(x, y).RGBA()
        //Luma: Y = 0.2126*R + 0.7152*G + 0.0722*B
        Y := (0.2126*float64(R) + 0.7152*float64(G) + 0.0722*float64(B)) * (255.0 / 65535)
        grayPix := color.Gray{uint8(Y)}
        grayImg.Set(x, y, grayPix)
    }
}

上面的計算是通過浮點乘法完成的,當然不是最有效的計算,但它足以證明這個想法。另一點是,當使用 color.Gray 作為第三個引數呼叫 Set(x, y int, c color.Color) 時,顏色模型將不會執行顏色轉換,如前一個 grayModel 函式中所示。