5

PNGファイルを読み取り、1つのチャネルを別のチャネル(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とプログラミング全般に本当に慣れていないので、回答でそれを考慮してください。ありがとうございました。

4

1 に答える 1

7

うーん、思ったより難しかったです。誰かもっと良いアイデアを思いつくことができるでしょうか!

問題は、返される具体的な型がわからないことですpng.Decode。画像型のいずれかが返される可能性があります。image.ImageSet メソッドを持たないインターフェイスしかありません。

それを回避するには、最初に、ピクセルを設定できるすべての画像タイプが満たすインターフェースを定義します

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

次に、 がそのインターフェースを実装しているかどうかを確認picします (そうでない場合は go がパニックになります。気になる場合は 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)
    }
}

これの高性能バージョンでは、タイプ スイッチを使用してそれがどのイメージ タイプであるかを判別し、24 ビット イメージ用の uint8s と 48 ビット イメージ用の uint16s などを使用して、それぞれにカスタマイズされたコードを用意する必要があると思われます。

試してみたい場合は、完全な動作例を次に示します。ただし、プレイグラウンドでは機能しません。ダウンロードする必要があります。


更新:あなたのコメントに気づきました。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)
    }
}
于 2013-02-01T19:58:33.503 に答える