透明像素颜色 – Go语言图像

huangapple go评论75阅读模式
英文:

Transparent pixel color - Go lang image

问题

我正在尝试从眼动仪结果创建热力图(显示用户在屏幕上最频繁注视的位置)。

对于用户频繁注视的像素,我想设置不透明的红色,对于稍微不频繁的橙色,依此类推。
我使用红到绿的颜色范围。
但是对于最不频繁的像素,我希望它们不仅显示绿色,还希望它们是透明的。

imgfile, err := os.Open("unchanged.jpeg")
defer imgfile.Close()
if err != nil {
    fmt.Println(err.Error())
}

decodedImg, err := jpeg.Decode(imgfile)
img := image.NewRGBA(decodedImg.Bounds())

size := img.Bounds().Size()
for x := 0; x < size.X; x++ {
    for y := 0; y < size.Y; y++ {
        img.Set(x, y, decodedImg.At(x, y))
    }
}

// 在这里使用 img.Set(...) 改变一些像素

outFile, _ := os.Create("changed.png")
defer outFile.Close()
png.Encode(outFile, img)

问题是,当我尝试使用img.Set(x, y, color.RGBA{85, 165, 34, 50})改变颜色时,它实际上并没有根据旧颜色和新颜色的平均值着色像素,而是显示一些新的、看起来奇怪的颜色。

设置 alpha RGBA 值为 50 的图像(透明):

透明像素颜色 – Go语言图像

设置 alpha RGBA 值为 255 的图像(不透明):

透明像素颜色 – Go语言图像

据我所知,我使用的是支持透明度的 png 图像。
为什么会发生这种情况?有什么解决这个问题并使最不频繁出现的像素透明的想法吗?

谢谢每一个回答。

英文:

I'm trying to create a heat map from eye-tracker results (showing where on the screen was the user looking most frequently).

For pixels that the user looked frequently onto, I want to set non-transparent red color, for a bit less-frequent orange color, etc..
I'm using red to green color-scale.
But for the pixels with the least frequency, I want them to not only show green color, I want them to be transparent.

imgfile, err := os.Open(&quot;unchanged.jpeg&quot;)
defer imgfile.Close()
if err != nil {
	fmt.Println(err.Error())
}

decodedImg, err := jpeg.Decode(imgfile)
img := image.NewRGBA(decodedImg.Bounds())

size := img.Bounds().Size()
for x := 0; x &lt; size.X; x++ {
	for y := 0; y &lt; size.Y; y++ {
		img.Set(x, y, decodedImg.At(x, y))
	}
}

// I change some pixels here with img.Set(...)

outFile, _ := os.Create(&quot;changed.png&quot;)
defer outFile.Close()
png.Encode(outFile, img)

The problem is that when I try to change color for example with img.Set(x, y, color.RGBA{85, 165, 34, 50}) it doesn't actually color the pixel as averaged value depending on old and new color, it just shows some new, strange-looking color.

Image with alpha RGBA value set to 50: (transparent)

透明像素颜色 – Go语言图像

Image with alpha RGBA value set to 255: (opaque)

透明像素颜色 – Go语言图像

I'm using png image that as far as I know does support transparency.
Why does this happen? Any ideas that can solve this problem and make the least-frequently seen pixels appear transparent?

Thanks for every answer.

答案1

得分: 4

这是因为Set()方法会设置(覆盖)指定位置像素的颜色。这不是你想要的。

你想要的是将像素的原始颜色与你选择的颜色相结合,并且希望这是最终的颜色。

为了实现这一点,首先你可以使用Image.At()查询原始颜色,然后将颜色与你想要的颜色相结合(你可以分别通过红色、绿色、蓝色和透明度通道来进行操作),最后使用Set()方法设置最终的颜色。

颜色相结合:

请注意,color.RGBA结构体的RGBA字段是经过预乘的alpha值,这意味着例如R字段保存的是颜色的红色分量乘以alpha通道的值。

你可以使用RGBA.RGBA()方法,它将所有字段作为uint32值返回,并且所有值都在0..0xffff的范围内,因此你可以对它们进行操作而不必担心溢出(uint8的最大值是255,所以即使添加两个uint8值也可能会溢出)。

因此,当你合并两种颜色时,可以按组件进行合并。但请注意,color.RGBA中这些字段的类型是uint8,因此在合并后,你必须将结果转换为适合8位的值,因此你必须向右移8位。

一个非常简单的合并方法是计算平均值:

// 查询:
x, y := 1, 1
r, g, b, a := img.At(x, y).RGBA()

// 合并:
col := color.RGBA{85, 165, 34, 50}
r2, g2, b2, a2 := col.RGBA()

col.R = uint8((r + r2) >> 9) // 除以2后再右移8位,相当于右移9位
col.G = uint8((g + g2) >> 9)
col.B = uint8((b + b2) >> 9)
col.A = uint8((a + a2) >> 9)

// 设置新像素:
img.Set(x, y, col)

你可以将其整合到一个辅助函数中:

func combine(c1, c2 color.Color) color.Color {
	r, g, b, a := c1.RGBA()
	r2, g2, b2, a2 := c2.RGBA()

	return color.RGBA{
		uint8((r + r2) >> 9), // 除以2后再右移8位,相当于右移9位
		uint8((g + g2) >> 9),
		uint8((b + b2) >> 9),
		uint8((a + a2) >> 9),
	}
}

然后使用它:

x, y := 1, 1
img.Set(x, y, combine(img.At(x, y), color.RGBA{85, 165, 34, 50}))

如果需要实现逼真的颜色合成,请参考Alpha compositing页面。

英文:

It's because the Set() method sets (overwrites) the color of the pixel at the specified location. This is not what you want.

You want to combine the original color of the pixel with a color of your choosing, and you want this to be the final color.

For this to achieve, first you may query the original color with Image.At(), then combine the color with whatever you want to (you may do this by red, green, blue and alpha channels separately), and set the final color with the Set() method.

Combining:

Note that the R, G, B, A fields of the color.RGBA struct are alpha-premultiplied values, which means the R field for example holds the value of the color's red component multiplied by the alpha channel.

You may use the RGBA.RGBA() method which returns all the fields as uint32 values, and all will be in the range of 0..0xffff so you may do operations on them without having to worry about overflow (max value of uint8 is 255, so even by adding 2 of them could easily overflow).

So when you combine 2 colors, you may combine by components. But note that type of these fields in color.RGBA is uint8, so after combining you have to convert the result to fit into 8 bits, so you have to shift right by 8.

A very simple combining: calculating average:

// Query:
x, y := 1, 1
r, g, b, a := img.At(x, y).RGBA()

// Combine:
col := color.RGBA{85, 165, 34, 50}
r2, g2, b2, a2 := col.RGBA()

col.R = uint8((r + r2) &gt;&gt; 9) // div by 2 followed by &quot;&gt;&gt; 8&quot;  is &quot;&gt;&gt; 9&quot;
col.G = uint8((g + g2) &gt;&gt; 9)
col.B = uint8((b + b2) &gt;&gt; 9)
col.A = uint8((a + a2) &gt;&gt; 9)

// Set new pixel:
img.Set(x, y, col)

You may incorporate this into a helper function:

func combine(c1, c2 color.Color) color.Color {
	r, g, b, a := c1.RGBA()
	r2, g2, b2, a2 := c2.RGBA()

	return color.RGBA{
		uint8((r + r2) &gt;&gt; 9), // div by 2 followed by &quot;&gt;&gt; 8&quot;  is &quot;&gt;&gt; 9&quot;
		uint8((g + g2) &gt;&gt; 9),
		uint8((b + b2) &gt;&gt; 9),
		uint8((a + a2) &gt;&gt; 9),
	}
}

And using it:

x, y := 1, 1
img.Set(x, y, combine(img.At(x, y), color.RGBA{85, 165, 34, 50}))

Please check out the Alpha compositing page if you need realistic color compositing.

huangapple
  • 本文由 发表于 2016年4月13日 18:31:06
  • 转载请务必保留本文链接:https://go.coder-hub.com/36595687.html
匿名

发表评论

匿名网友

:?: :razz: :sad: :evil: :!: :smile: :oops: :grin: :eek: :shock: :???: :cool: :lol: :mad: :twisted: :roll: :wink: :idea: :arrow: :neutral: :cry: :mrgreen:

确定