使用image.Image或*image.RGBA的Set()方法。

huangapple go评论82阅读模式
英文:

Using the Set() method of an image.Image or *image.RGBA

问题

我现在是你的中文翻译。以下是翻译好的内容:

我在这个夏天的空闲时间里,正在使用Go图像包进行一些练习。

package main

import (
	"os"
	"image"
	"image/png"
	"image/color"
	"log"
	"fmt"
	"reflect"
)

func main(){

	file , err := os.OpenFile("C:/Sources/go3x3.png", os.O_RDWR, os.FileMode(0777))
	if err != nil {
		log.Fatal(err)
	}

	img , err := png.Decode(file)
	if err != nil {
		log.Fatal(err)
	}

	img.At(0,0).RGBA()
	fmt.Println("type:", reflect.TypeOf(img))

	m := image.NewRGBA(image.Rect(0, 0, 640, 480))
	fmt.Println("type:", reflect.TypeOf(m))
	m.Set(5, 5, color.RGBA{255, 0, 0, 255})
	img.Set(0, 0, color.RGBA{136, 0, 21, 255})
}

问题在于,当我将img.Set注释掉后运行代码,得到的结果是:

type: *image.RGBA
type: *image.RGBA

但是当我取消注释后,就会出现错误,提示:

img.Set undefined (type image.Image has no field or method Set)

我猜测我可能在使用reflect时出错了,我对Go中的接口和类型定义还没有完全掌握。

英文:

I am doing some practice with the Go image package with my free time this summer.

package main

import (

	"os"
	"image"
	"image/png"
	"image/color"
	"log"
	"fmt"
	"reflect"
	
)

func main(){

	file , err := os.OpenFile("C:/Sources/go3x3.png", os.O_RDWR, os.FileMode(0777))
	if err != nil {
		log.Fatal(err)
	}
	
	img , err := png.Decode(file)
	if err != nil {
		log.Fatal(err)
	}
	
	
	img.At(0,0).RGBA()
	fmt.Println("type:", reflect.TypeOf(img))
	
	m := image.NewRGBA(image.Rect(0, 0, 640, 480))
	fmt.Println("type:", reflect.TypeOf(m))
	m.Set(5, 5, color.RGBA{255, 0, 0, 255})
	img.Set(0, 0, color.RGBA{136, 0, 21, 255})
}

The problem here is when I run it with the img.Set commented out I get this result

type: *image.RGBA
type: *image.RGBA

but when it's uncommented I get an error saying

img.Set undefined (type image.Image has no field or method Set)

I'm assuming I'm using reflect wrong, I'm still fully grasping the whole interface and type definitions in Go.

答案1

得分: 10

要扩展之前的答案,png.Decode 可能会创建多种不同的底层图像类型(*image.Gray*image.RGBA*image.Paletted*image.NRGBA 等等)。它将创建的图像作为 image.Image 接口返回,该接口提供只读访问数据的功能。

然而,大多数实际返回的图像类型都实现了 Set 方法,用于简单的写入访问。你可以通过现有的 image/draw 包中的 draw.Image 接口来安全地测试和使用该方法。代码如下:

// From image/draw:
// Image is an image.Image with a Set method to change a single pixel.
type Image interface {
        image.Image
        Set(x, y int, c color.Color)
}

func drawablePNGImage(r io.Reader) (draw.Image, error) {
    img, err := png.Decode(r)
    if err != nil {
        return nil, err
    }
    dimg, ok := img.(draw.Image)
    if !ok {
        return nil, fmt.Errorf("%T is not a drawable image type", img)
    }
    return dimg, nil
}

<kbd>Playground</kbd>(显示了调用所有 image.Image 方法和 Set 方法的示例)。

针对 Go1.17+ 进行编辑:

请注意,Go1.17 添加了 draw.RGBA64Image 并提供了 SetRGBA64 方法。与 draw.Image 类似,所有标准图像类型都实现了该方法。该方法的优点是颜色值不会被封装在 color.Color 接口类型中,因此执行多个像素操作可能更快。
还请注意,Go1.18 添加了优化,用于实现可选的 draw.RGBA64Imageimage.RGBA64Image 接口的图像的 draw.Drawdraw.DrawMask 回退实现。

英文:

To expand on
a previous answer
answer:

png.Decode may create one of several different underlying image types (*image.Gray, *image.RGBA, *image.Paletted, *image.NRGBA, etc).
It returns whatever image it created as an image.Image interface which provides read only access to the data.

However, all (most?) of the actual image types it returns do implement the Set method for simple write access.
The way you can safely test for and use this method is via the existing draw.Image interface from the image/draw package. It's just this:

// From image/draw:
// Image is an image.Image with a Set method to change a single pixel.
type Image interface {
        image.Image
        Set(x, y int, c color.Color)
}

So you could do something like:

func drawablePNGImage(r io.Reader) (draw.Image, error) {
	img, err := png.Decode(r)
	if err != nil {
		return nil, err
	}
	dimg, ok := img.(draw.Image)
	if !ok {
		return nil, fmt.Errorf(&quot;%T is not a drawable image type&quot;, img)
	}
	return dimg, nil
}

<kbd>Playground</kbd> (shows an example calling all the image.Image methods as well as Set).

Edit for Go1.17+:

Note that Go1.17 added draw.RGBA64Image with a SetRGBA64 method. As with draw.Image, all of the standard image types implement this. The advantage of this method is that the color values are not boxed in the color.Color interface type so doing many pixel operations can be faster.
Also note that Go1.18 added optimisations to the draw.Draw and draw.DrawMask fallback implementations for images that implement the optional draw.RGBA64Image and image.RGBA64Image interfaces.

答案2

得分: 3

reflect.TypeOf(img)可以获取接口img中值的反射类型,如果它是一个接口的话。在这种情况下,img是一个image.Image接口,其中包含一个*image.RGBA

你可以通过将img转换为*image.RGBA来修复你的代码,或者更健壮地定义一个具有正确Set方法的接口类型,并将img转换为该类型(正如@DaveC所指出的,在"image/draw"中的draw.Image接口非常适合此用途)。如果你不能确定png.Decode是否总是为你的.png文件提供*image.RGBA,那么接口类型是更可取的选择。

img.(*image.RGBA).Set(0, 0, color.RGBA{136, 0, 21, 255})

或者

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

img.(Setter).Set(0, 0, color.RGBA{136, 0, 21, 255})

或者(可能是最好的选择):

import "image/draw"

...

img.(draw.Image).Set(0, 0, color.RGBA{136, 0, 21, 255})
英文:

reflect.TypeOf(img) gives you the reflection type of the value in the interface img, if its an interface. In this case, img is an interface, an image.Image which contains an *image.RGBA.

You can fix your code by converting img to an *image.RGBA, or more robustly, define an interface type with the right Set method, and convert img to that (the draw.Image interface in &quot;image/draw&quot; works perfectly for this, as noted by @DaveC). The interface type is preferable if you aren't sure that png.Decode will always give you an *image.RGBA for the .png files you have.

img.(*image.RGBA).Set(0, 0, color.RGBA{136, 0, 21, 255})

or

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

img.(Setter).Set(0, 0, color.RGBA{136, 0, 21, 255})

or (probably best):

import &quot;image/draw&quot;

...

img.(draw.Image).Set(0, 0, color.RGBA{136, 0, 21, 255})

答案3

得分: -1

编译器是正确的,image.Image 类型是一个不包含 Set() 函数的接口。

我不是一个图像库的专家,但是我对类型的初步了解似乎表明,你可以使用 Bounds() 方法获取一个 image.Rectangle,然后像你之前的示例代码中那样创建一个新的 RGBA 类型。

// 你当前的图像处理
m := image.NewRGBA(image.Rect(0, 0, 640, 480))
fmt.Println("type:", reflect.TypeOf(m))
m.Set(5, 5, color.RGBA{255, 0, 0, 255})

// 你可以通过传递 image.Image.Bounds() 返回的 image.Rectangle 来创建一个 image.RGBA 类型
m = image.NewRGBA(img.Bounds())
m.Set(0, 0, color.RGBA{136, 0, 21, 255})

这是对你类型问题的严格回答,但我不能保证它能实现你的最终目标。

英文:

The compiler is correct, the image.Image type is an interface that does not include the Set() function.

I am not an expert at the image library but my cursory look at the types seems to suggest you can take an Image type and use the Bounds() method to get a image.Rectangle to create a new RGBA type as done previously in your example code.

// Your current image manipulation
m := image.NewRGBA(image.Rect(0, 0, 640, 480))
fmt.Println(&quot;type:&quot;, reflect.TypeOf(m))
m.Set(5, 5, color.RGBA{255, 0, 0, 255})

// You can create a image.RGBA type by passing the image.Rectangle
// returned from image.Image.Bounds()
m = image.NewRGBA(img.Bounds())
m.Set(0, 0, color.RGBA{136, 0, 21, 255})

This is a strict answer to your type issue but I don't have any gurantee it accomplishes your end goals.

huangapple
  • 本文由 发表于 2015年5月14日 09:42:43
  • 转载请务必保留本文链接:https://go.coder-hub.com/30227947.html
匿名

发表评论

匿名网友

:?: :razz: :sad: :evil: :!: :smile: :oops: :grin: :eek: :shock: :???: :cool: :lol: :mad: :twisted: :roll: :wink: :idea: :arrow: :neutral: :cry: :mrgreen:

确定