【问题标题】:Golang - Swap PNG channels of a picture using Image and Image/PNGGolang - 使用 Image 和 Image/PNG 交换图片的 PNG 通道
【发布时间】:2013-02-01 17:45:08
【问题描述】:

我正在尝试编写一个简短的,它将读取一个 PNG 文件,并将一个通道与另一个 (R、G、B) 交换作为可能的选择。

但是,我不知道如何从 image.At(x,y) 返回的 color.Color 对象中提取整数。一旦我可以使用交换的通道构造新的 RGBA 颜色,使用 image.Set(x,y,color) 将其写回可能会更容易。

我现在在这里(您几乎可以跳到最后一个循环):

package main

import (
"flag"
"fmt"
//"image"
"image/color"
"image/png"
"os"
)

type Choice struct {
value string
valid bool
}

func (c *Choice) validate() {
goodchoices := []string{"R", "G", "B"}
for _, v := range goodchoices {
    if c.value == v {
        c.valid = true
    }
}
}

func main() {

var fname string
var c1 Choice
var c2 Choice

flag.StringVar(&c1.value, "c1", "", "The color channel to swap - R or G or B ")
flag.StringVar(&c2.value, "c2", "", "The color channel to swap with - R or G or B ")
flag.StringVar(&fname, "f", "", "A .png image (normal map)")
flag.Parse()

c1.validate()
c2.validate()

if c1.valid == true && c2.valid == true {
    fmt.Println("We could proceed..")
    fmt.Println("Swapping channels:", c1.value, "<->", c2.value, "In", fname) //for testing
} else {
    fmt.Println("Invalid channel... Please use R, G or B.")
    return
}

file, err := os.Open(fname)
if err != nil {
    fmt.Println(err)
    return
}
defer file.Close()

pic, err := png.Decode(file)
if err != nil {
    fmt.Fprintf(os.Stderr, "%s: %v\n", fname, err)
    return
}

b := pic.Bounds()

for y := b.Min.Y; y < b.Max.Y; y++ {
    for x := b.Min.X; x < b.Max.X; x++ {
        col := pic.At(x, y)
        ???? How do I swap the channels in col ???? 
    }
}
}

我对 Go 和编程很陌生,所以请在你的回答中考虑它。谢谢。

【问题讨论】:

  • 我使用的是 RGBA png 图片(它们是从 Blender3D 导出​​的法线贴图)。

标签: image png go


【解决方案1】:

嗯,这比我想象的要难 - 我想知道是否有人能想出一个更好的主意!

问题是你不知道png.Decode 返回的具体类型——它可能返回任何图像类型。您只有一个没有 Set 方法的 image.Image 接口。

为了解决这个问题,首先定义一个接口,所有可以设置像素的图像类型都满足

type ImageSet interface {
     Set(x, y int, c color.Color)
}

接下来看看pic 是否实现了该接口(如果不实现,go 会恐慌 - 使用 picSet,如果这让您感到困扰,请使用 ok 形式)

// Get an interface which can set pixels
picSet := pic.(ImageSet)

现在您的循环看起来像这样 - 我只交换了红色和绿色,以便您可以看到这个想法。

for y := b.Min.Y; y < b.Max.Y; y++ {
    for x := b.Min.X; x < b.Max.X; x++ {
        col := pic.At(x, y)
        r, g, b, a := col.RGBA()
        // Swap green and red
        newCol := color.RGBA{uint8(g>>8), uint8(r>>8), uint8(b>>8), uint8(a>>8)}
        picSet.Set(x, y, newCol)
    }
}

我怀疑高性能版本必须使用类型开关来确定它是哪种图像类型,然后为每个自定义代码使用 uint8s 用于 24 位图像和 uint16s 用于 48 位图像等。

Here is the complete working 示例,如果您想尝试一下。但它在操场上不起作用 - 你必须下载它。


更新:刚刚注意到您的评论。如果您知道您有一个 RGBA 图像,那么您可以使用类型断言来获取底层图像,这会使事情变得更容易。

// Get an image.RGBA if it is one
rgba, ok := pic.(*image.RGBA)
if !ok {
    fmt.Println("That wasn't an RGBA!")
    return
}

for y := b.Min.Y; y < b.Max.Y; y++ {
    for x := b.Min.X; x < b.Max.X; x++ {
        // Note type assertion to get a color.RGBA
        col := rgba.At(x, y).(color.RGBA)
        // Swap green and red
        col.G, col.R = col.R, col.G
        rgba.Set(x, y, col)
    }
}

【讨论】:

  • 正是我想要的......非常感谢!对不起,调色图像的额外工作.. :) 我应该将它包含在帖子的主体中。惊人的。只是出于好奇:看来我必须在内循环中编写一个 switch/case 测试来检查我们选择替换哪个通道..感觉(非常)低效..可能有其他方法吗?
  • 您只能执行 3 个可能的开关,因此我会编写 3 个函数来执行这些操作,并使用一个函数表来确定您需要哪个。 (或使用开关选择功能指针)。然后,您可以通过指针在循环中调用所选函数,这将是又快又好。
猜你喜欢
  • 2011-01-06
  • 1970-01-01
  • 1970-01-01
  • 2012-12-12
  • 1970-01-01
  • 2019-01-06
  • 2016-03-14
  • 2020-03-21
  • 2013-02-17
相关资源
最近更新 更多