web-dev-qa-db-ja.com

単一ピクセルの色を変更する-Golang画像

Jpeg画像ファイルを開いてエンコードし、ピクセルの色を変更して、元の状態に保存したいと思います。

こんなことしたい

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

img,err := jpeg.Decode(imgfile)
if err != nil {
    fmt.Println(err.Error())
}
img.Set(0, 0, color.RGBA{85, 165, 34, 1})
img.Set(1,0,....)

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

画像ファイルをエンコードした後に取得するデフォルトの画像タイプにはSetメソッドがないため、実用的な解決策を思い付くことができません。

誰かがこれを行う方法を説明できますか?どうもありがとう。

12

デコードが成功すると image.Decode() (および jpeg.Decode() などの特定のデコード関数も) _image.Image_ 。 _image.Image_は、画像の読み取り専用ビューを定義するインターフェイスです。画像を変更/描画するためのメソッドは提供されていません。

imageパッケージは、通常はSet(x, y int, c color.Color)メソッドを使用して、画像を変更/描画できるようにするいくつかの_image.Image_実装を提供します。

ただし、image.Decode()は、返される画像がimageパッケージで定義されている画像タイプのいずれかであること、または画像の動的タイプにSet()メソッド(可能性はありますが、保証はありません)。登録されたカスタム画像デコーダーは、カスタム実装である_image.Image_値を返す場合があります(つまり、imageパッケージで定義された画像タイプではありません)。

(動的タイプの)画像にSet()メソッドがある場合は、 type assertion を使用し、そのSet()メソッドを使用して描画できます。これはそれができる方法です:

_type Changeable interface {
    Set(x, y int, c color.Color)
}

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

img, err := jpeg.Decode(imgfile)
if err != nil {
    panic(err.Error())
}

if cimg, ok := img.(Changeable); ok {
    // cimg is of type Changeable, you can call its Set() method (draw on it)
    cimg.Set(0, 0, color.RGBA{85, 165, 34, 255})
    cimg.Set(0, 1, color.RGBA{255, 0, 0, 255})
    // when done, save img as usual
} else {
    // No luck... see your options below
}
_

画像にSet()メソッドがない場合は、_image.Image_を実装するカスタムタイプを実装することで「ビューをオーバーライド」することを選択できますが、そのAt(x, y int) color.Colorメソッド(これはピクセルの色を返します/提供します)画像が変更可能である場合に設定する新しい色を返し、画像を変更しない元の画像のピクセルを返します。

_image.Image_インターフェースの実装は、embeddingを利用することで最も簡単に実行できるため、必要な変更のみを実装する必要があります。これはそれができる方法です:

_type MyImg struct {
    // Embed image.Image so MyImg will implement image.Image
    // because fields and methods of Image will be promoted:
    image.Image
}

func (m *MyImg) At(x, y int) color.Color {
    // "Changed" part: custom colors for specific coordinates:
    switch {
    case x == 0 && y == 0:
        return color.RGBA{85, 165, 34, 255}
    case x == 0 && y == 1:
        return color.RGBA{255, 0, 0, 255}
    }
    // "Unchanged" part: the colors of the original image:
    return m.Image.At(x, y)
}
_

それを使用する:非常に簡単です。行ったように画像をロードしますが、保存するときは、エンコーダーから要求されたときに変更された画像コンテンツ(色)を提供するMyImgタイプの値を指定します。

_jpeg.Encode(outFile, &MyImg{img}, nil)
_

多くのピクセルを変更する必要がある場合、すべてをAt()メソッドに含めることは実用的ではありません。そのために、MyImgを拡張して、変更するピクセルを格納するSet()実装を作成できます。実装例:

_type MyImg struct {
    image.Image
    custom map[image.Point]color.Color
}

func NewMyImg(img image.Image) *MyImg {
    return &MyImg{img, map[image.Point]color.Color{}}
}

func (m *MyImg) Set(x, y int, c color.Color) {
    m.custom[image.Point{x, y}] = c
}

func (m *MyImg) At(x, y int) color.Color {
    // Explicitly changed part: custom colors of the changed pixels:
    if c := m.custom[image.Point{x, y}]; c != nil {
        return c
    }
    // Unchanged part: colors of the original image:
    return m.Image.At(x, y)
}
_

それを使用する:

_// Load image as usual, then

my := NewMyImg(img)
my.Set(0, 0, color.RGBA{85, 165, 34, 1})
my.Set(0, 1, color.RGBA{255, 0, 0, 255})

// And when saving, save 'my' instead of the original:
jpeg.Encode(outFile, my, nil)
_

多くのピクセルを変更する必要がある場合は、ピクセルの変更をサポートする新しい画像を作成する方が有利な場合があります。 _image.RGBA_ 、元の画像を描画してから、必要なピクセルの変更に進みます。

別の画像に画像を描画するには、 _image/draw_ パッケージを使用できます。

_cimg := image.NewRGBA(img.Bounds())
draw.Draw(cimg, img.Bounds(), img, image.Point{}, draw.Over)

// Now you have cimg which contains the original image and is changeable
// (it has a Set() method)
cimg.Set(0, 0, color.RGBA{85, 165, 34, 255})
cimg.Set(0, 1, color.RGBA{255, 0, 0, 255})

// And when saving, save 'cimg' of course:
jpeg.Encode(outFile, cimg, nil)
_

上記のコードはデモンストレーション用です。 「実際の」画像では、Image.Bounds()は_(0;0)_ポイントで始まらない長方形を返す場合があります。その場合、それを機能させるには調整が必要になります。

15
icza

画像デコードは、画像のピクセル幅と高さを取得するためのBoundsメソッドを持つ 画像インターフェイス を返します。

img, _, err := image.Decode(imgfile)
if err != nil {
    fmt.Println(err.Error())
}
size := img.Bounds().Size()

幅と高さが決まったら、2つのネストされたforループ(1つはx用、もう1つはy座標用)を使用してピクセルを反復処理できます。

for x := 0; x < size.X; x++ {
    for y := 0; y < size.Y; y++ {
        color := color.RGBA{
            uint8(255 * x / size.X),
            uint8(255 * y / size.Y),
            55,
            255}
        m.Set(x, y, color)
    }
}

画像の操作が完了したら、ファイルをエンコードできます。しかし理由は image.ImageにはSetメソッドがないため、RGBAメソッドを使用できるSet構造体を返す新しいRGBAイメージを作成できます。

m := image.NewRGBA(image.Rect(0, 0, width, height))
outFile, err := os.Create("changed.jpg")
if err != nil {
    log.Fatal(err)
}
defer outFile.Close()
png.Encode(outFile, m)
2
Endre Simo

image.Imageはデフォルトで不変ですが、draw.Imageは可変です。

Draw.Imageに型変換を行うと、Setメソッドが得られるはずです。

img.(draw.Image).Set(0,0, color.RGBA{85, 165, 34, 1})
0
matt.s