2013-02-01 93 views
5

我正在尝试编写一个简短的一个,它将读取一个PNG文件,并与另一个(R,G,B)交换一个通道可能的选择。Golang - 使用图像和图像/ PNG交换图片的PNG频道

但是我找不到,如何从image.At(x,y)返回的color.Color对象中提取整数。写回它可能会更容易与图像。设置(x,y,颜色),一旦我可以用交换通道构建新的RGBA颜色。

在这里,我现在(你几乎可以跳到最后循环):

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 ???? 
    } 
} 
} 

我真的很新的去编程一般,所以请考虑在你的答案。谢谢。

+1

我使用RGBA PNG图像(它们是从Blender3D导出的法线贴图)。 – Zoltan

回答

7

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

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

为了避开,首先定义所有可设置像素的图像类型满足

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

下一页看pic是否实现该接口(去会恐慌,如果它不界面 - 使用picSet ,好形式,如果你烦恼)

// 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) 
    } 
} 
+0

正是我在找什么..非常感谢!对不起,对调色板图像的额外工作.. :)我应该把它包括在帖子的主体中。真棒。 只是出于好奇:似乎我必须在内部循环内编写一个开关/外壳测试来检查我们选择替换哪个通道..它感觉(非常)低效..可能有其他方法吗? – Zoltan

+0

只有3种可能的开关可供使用,所以我会编写3个功能来完成这些功能,并使用功能表来计算出您需要的功能。 (或使用开关选择功能指针)。然后你可以通过指针调用循环中选定的函数,这将是很好的和快速的。 –

+0

确实..再次感谢! – Zoltan