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メソッドがないため、実用的な解決策を思い付くことができません。
誰かがこれを行う方法を説明できますか?どうもありがとう。
デコードが成功すると 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)
_ポイントで始まらない長方形を返す場合があります。その場合、それを機能させるには調整が必要になります。
画像デコードは、画像のピクセル幅と高さを取得するための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)
image.Imageはデフォルトで不変ですが、draw.Imageは可変です。
Draw.Imageに型変換を行うと、Setメソッドが得られるはずです。
img.(draw.Image).Set(0,0, color.RGBA{85, 165, 34, 1})