将彩色图像转换为灰度
一些数字图像处理算法如边缘检测,由图像强度(即灰度值)携带的信息就足够了。使用颜色信息(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.GrayModel
在 color.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( R , G , B )+ max( R , G , B ))/ 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
函数中所示。