改变单个像素的颜色——Golang图像
Change color of a single pixel - Golang image
我想打开 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 方法。
谁能解释一下如何做到这一点?非常感谢。
图像解码returns一个image interface,它有一个Bounds
方法来获得图像像素宽度和高度。
img, _, err := image.Decode(imgfile)
if err != nil {
fmt.Println(err.Error())
}
size := img.Bounds().Size()
获得宽度和高度后,您可以使用两个嵌套的 for 循环(一个用于 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 图像,其中 returns 一个 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)
image.Image默认是不可变的,但是draw.Image是可变的。
如果您将类型转换为 draw.Image,那应该会为您提供一个 Set 方法
img.(draw.Image).Set(0,0, color.RGBA{85, 165, 34, 1})
解码成功image.Decode()
(and also specific decoding functions like jpeg.Decode()
) return a value of image.Image
。 image.Image
是定义图像的只读视图的接口:它不提供更改/绘制图像的方法。
image
包提供了几个 image.Image
实现,允许您更改/绘制图像,通常使用 Set(x, y int, c color.Color)
方法。
然而,image.Decode()
并不能保证 returned 图像将是 image
包中定义的任何图像类型,甚至是动态类型图像有一个 Set()
方法(可能,但不能保证)。已注册的自定义图像解码器可能 return 您的 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()
方法,您可以选择 "override its view" 通过实现实现 image.Image
的自定义类型,但在其 At(x, y int) color.Color
方法中(returns / 提供像素的颜色)你 return 如果图像是可变的,你将设置的新颜色,以及 return 原始图像的像素,你将不改变图像。
实施 image.Image
接口最简单的方法是利用 嵌入 ,因此您只需实施所需的更改。这是如何完成的:
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)
以上代码只是为了演示。在 "real-life" 图像中 Image.Bounds()
可能 return 一个不从 (0;0)
点开始的矩形,在这种情况下,需要进行一些调整才能使其工作。
我想打开 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 方法。
谁能解释一下如何做到这一点?非常感谢。
图像解码returns一个image interface,它有一个Bounds
方法来获得图像像素宽度和高度。
img, _, err := image.Decode(imgfile)
if err != nil {
fmt.Println(err.Error())
}
size := img.Bounds().Size()
获得宽度和高度后,您可以使用两个嵌套的 for 循环(一个用于 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 图像,其中 returns 一个 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)
image.Image默认是不可变的,但是draw.Image是可变的。
如果您将类型转换为 draw.Image,那应该会为您提供一个 Set 方法
img.(draw.Image).Set(0,0, color.RGBA{85, 165, 34, 1})
解码成功image.Decode()
(and also specific decoding functions like jpeg.Decode()
) return a value of image.Image
。 image.Image
是定义图像的只读视图的接口:它不提供更改/绘制图像的方法。
image
包提供了几个 image.Image
实现,允许您更改/绘制图像,通常使用 Set(x, y int, c color.Color)
方法。
image.Decode()
并不能保证 returned 图像将是 image
包中定义的任何图像类型,甚至是动态类型图像有一个 Set()
方法(可能,但不能保证)。已注册的自定义图像解码器可能 return 您的 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()
方法,您可以选择 "override its view" 通过实现实现 image.Image
的自定义类型,但在其 At(x, y int) color.Color
方法中(returns / 提供像素的颜色)你 return 如果图像是可变的,你将设置的新颜色,以及 return 原始图像的像素,你将不改变图像。
实施 image.Image
接口最简单的方法是利用 嵌入 ,因此您只需实施所需的更改。这是如何完成的:
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)
以上代码只是为了演示。在 "real-life" 图像中 Image.Bounds()
可能 return 一个不从 (0;0)
点开始的矩形,在这种情况下,需要进行一些调整才能使其工作。