3 回答

TA貢獻1802條經驗 獲得超6個贊
圖像解碼返回一個圖像接口,該接口具有Bounds獲取圖像像素寬度和高度的方法。
img, _, err := image.Decode(imgfile)
if err != nil {
fmt.Println(err.Error())
}
size := img.Bounds().Size()
獲得寬度和高度后,您可以使用兩個嵌套的 for 循環(一個用于 the x,一個用于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圖像,該圖像返回一個RGBA結構體,您可以在該結構體上使用該Set方法。
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)

TA貢獻1808條經驗 獲得超4個贊
image.Image 默認是不可變的,但 draw.Image 是可變的。
如果你對 draw.Image 進行類型轉換,那應該會給你一個 Set 方法
img.(draw.Image).Set(0,0, color.RGBA{85, 165, 34, 1})

TA貢獻1848條經驗 獲得超2個贊
在成功解碼image.Decode()(以及特定的解碼函數,如jpeg.Decode())時,返回值image.Image。image.Image是一個定義圖像只讀視圖的接口:它不提供更改/繪制圖像的方法。
該image包提供了幾種image.Image實現,允許您更改/繪制圖像,通常使用一種Set(x, y int, c color.Color)方法。
image.Decode()但是不保證返回的圖像將是image包中定義的任何圖像類型,甚至Set()不保證圖像的動態類型具有方法(可能,但不能保證)。注冊的自定義圖像解碼器可能會返回一個image.Image作為自定義實現的值(意味著不是image包中定義的圖像類型)。
如果(動態類型的)圖像確實有Set()方法,則可以使用類型斷言并使用其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)點開始的矩形,在這種情況下,需要進行一些調整才能使其工作。
- 3 回答
- 0 關注
- 293 瀏覽
添加回答
舉報