2013-02-01 121 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