将RGBA图像转换为灰度Golang
我目前正在开发一个程序,将RGBA图像转换为灰度 我早些时候问了一个问题,并被引导到下面的答案- 这是我原来的问题- 我已经编辑了我的代码,所以它现在可以成功运行了——但是输出的图像不是我想要的。它被转换成灰度,但是像素都被弄乱了,看起来像旧电视上的噪音将RGBA图像转换为灰度Golang,go,colors,rgb,image-manipulation,grayscale,Go,Colors,Rgb,Image Manipulation,Grayscale,我目前正在开发一个程序,将RGBA图像转换为灰度 我早些时候问了一个问题,并被引导到下面的答案- 这是我原来的问题- 我已经编辑了我的代码,所以它现在可以成功运行了——但是输出的图像不是我想要的。它被转换成灰度,但是像素都被弄乱了,看起来像旧电视上的噪音 package main import ( "image" "image/color" "image/jpeg" "log" "os" ) type ImageSet interface { Set(x, y int, c color.C
package main
import (
"image"
"image/color"
"image/jpeg"
"log"
"os"
)
type ImageSet interface {
Set(x, y int, c color.Color)
}
func main() {
file, err := os.Open("flower.jpg")
if err != nil {
log.Fatal(err)
}
defer file.Close()
img, err := jpeg.Decode(file)
if err != nil {
log.Fatal(os.Stderr, "%s: %v\n", "flower.jpg", err)
}
b := img.Bounds()
imgSet := image.NewRGBA(b)
for y := 0; y < b.Max.Y; y++ {
for x := 0; x < b.Max.X; x++ {
oldPixel := img.At(x, y)
r, g, b, a:= oldPixel.RGBA()
r = (r+g+b)/3
pixel := color.RGBA{uint8(r), uint8(r), uint8(r), uint8(a)}
imgSet.Set(x, y, pixel)
}
}
outFile, err := os.Create("changed.jpg")
if err != nil {
log.Fatal(err)
}
defer outFile.Close()
jpeg.Encode(outFile, imgSet, nil)
}
我目前得到以下错误
.\rgbtogray.go:36: cannot use y (type uint32) as type int in argument to imgSet.Set
我在回答中遗漏了什么吗?任何提示都值得欣赏。是一种返回alpha预乘红色、绿色、蓝色和alpha值的方法,所有值均为uint32
,但仅在[0,0xffff]
范围内(仅使用32位中的16位)。这意味着您可以添加这些组件,它们不会溢出(每个组件的最大值适合16位,所以它们的总和适合32位)
这里需要注意的一点是:结果也将是alpha预乘,除以3后,它仍将在[0..0xffff]
的范围内。因此,通过执行uint8(r)
类型转换,您只需保留最低的8位,与整数相比,这似乎只是一个随机值。你应该取最高的8位
但没那么快。我们在这里要做的是将彩色图像转换为灰度图像,这将丢失“颜色”信息,我们需要的基本上是每个像素的亮度。您提出的解决方案称为“平均法”,其结果相当糟糕,因为它将所有R、G和B分量的权重相等,即使这些颜色具有不同的波长,从而对整个像素的亮度产生不同的影响。请在此处阅读更多信息:
对于真实的RGB->灰度转换,必须使用以下权重:
Y = 0.299 * R + 0.587 * G + 0.114 * B
您可以在维基百科上阅读这些权重(和变量)背后的更多信息:。这就是所谓的光度法,这将提供最好的灰度图像
到目前为止还不错,我们有光度,我们如何从这里得到一个color.color
值?一个选项是使用颜色值,其中为所有组件指定相同的亮度(可以保留alpha)。如果您打算使用返回的,这可能是最好的方法,因为在设置颜色时不需要进行颜色转换(因为它与图像的颜色模型匹配)
另一个诱人的选择是使用,它是一种颜色(实现color.color
接口),并按照我们现在的方式对颜色建模:使用Y
,使用uint8
存储。另一种方法是基本上“相同”,但使用16位将Y
存储为uint16
。对于这些,最好也使用具有匹配颜色模型的图像,例如or(尽管这不是要求)
因此,转换应为:
oldPixel := img.At(x, y)
r, g, b, _ := oldPixel.RGBA()
lum := 0.299*float64(r) + 0.587*float64(g) + 0.114*float64(b)
pixel := color.Gray{uint8(lum / 256)}
imgSet.Set(x, y, pixel)
请注意,我们需要将R、G、B分量转换为float64
,以便能够乘以权重。由于r
、g
、b
已经属于uint32
类型,我们可以用整数运算(无溢出)来代替它
不必详细说明,因为标准库已经有了解决方案,这里是:
oldPixel := img.At(x, y)
r, g, b, _ := oldPixel.RGBA()
lum := (19595*r + 38470*g + 7471*b + 1<<15) >> 24
imgSet.Set(x, y, color.Gray{uint8(lum)})
它与上一个亮度加权模型相同,使用整数算法。或者在一行中:
imgSet.Set(x, y, color.GrayModel.Convert(img.At(x, y)))
要获得更高的16位灰度分辨率,请执行以下操作:
imgSet.Set(x, y, color.Gray16Model.Convert(img.At(x, y)))
最后一点注意:由于您是在
image.NewRGBA()
返回的图像上绘制的,因此它的类型为*image.RGBA
。您不需要检查它是否有一个Set()
方法,因为image.RGBA
是一个静态类型(不是接口),并且它确实有一个Set()
方法,它是在编译时检查的。您确实需要检查的情况是,您是否有一个作为接口的通用类型的映像,但此接口不包含/“规定”Set()
方法;但是实现此接口的动态类型可能会提供这一点。答案不错!我不想进入亮度,但我可能应该包括GrayModel
。嗨,我没有意识到有更好的方法来转换为灰度。我已将您的代码放入我的代码中,它返回一个关于y的错误。错误如下所示,.\rgbtogray.go:36:无法在imgSet.Set的参数中使用y(类型uint32)作为int类型。还有什么提示吗?@benjano你确定你正确复制了我的代码吗?听起来你错过了传递x
和y
到imgSet.Set()
。如果没有,请准确地说明是什么导致了错误。我很确定,我已经编辑了我的答案,以显示我的代码处于当前状态。问题可能是变量名为y,它是坐标变量吗?我已经将其更改为Z,现在代码运行,但它只返回一个空白图像
imgSet.Set(x, y, color.GrayModel.Convert(img.At(x, y)))
imgSet.Set(x, y, color.Gray16Model.Convert(img.At(x, y)))