更改单个像素的颜色 - Go lang图像 [英] Change color of a single pixel - Go lang 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)
推迟outFile。关闭()
jpeg.Encode(outFile,img,nil)
因为我在编码图像文件后得到的默认图像类型没有Set方法。
任何人都可以解释如何做到这一点吗?非常感谢。
成功解码 image.Decode()
(以及特定的解码功能,如 jpeg.Decode()
)返回 image.Image
。 image.Image
是一个界面,它定义了图像的只读视图:它不提供在图像上更改/绘制的方法。
image
包提供了几个 image.Image
实现,允许您更改/绘制在图像上,通常使用 Set(x,y int,c color.Color)
方法。但是并不能保证返回的图像是图像$ c $中定义的任何图像类型c>包,甚至图像的动态类型都有一个 Set()
方法(它可能,但不能保证)。注册的自定义图像解码器可能会返回一个 image.Image
值作为自定义实现(这意味着不是在图像$ c $中定义的图像类型如果(动态类型的)图像确实有一个 Set()
方法,您可以使用键入断言并使用它的 Set()
方法来绘制它。这是如何实现的:
type可变接口{
Set(x,y int,c color)颜色)
}
imgfile,err:= os.Open(unchanged.jpg)
if err!= nil {
panic(err.Error( ))
}
推迟imgfile.Close()
img,err:= jpeg.Decode(imgfile)
if err!= nil {
恐慌(err.Error())
}
if cimg,ok:= img。(Changeable); OK {
// cimg的类型是Changeable,您可以调用它的Set()方法(绘制它)
cimg.Set(0,0,color.RGBA {85,165,34,255 })
cimg.Set(0,1,color.RGBA {255,0,0,025})
//完成后,像往常一样保存img
} else {
//没有运气......看到您的选项在
下面
如果图像确实没有 Set()
方法,您可以通过实现一个实现 image.Image $ c的自定义类型来选择覆盖其视图 $ c>,但是在它的 At(x,y int)color.Color
方法中(它返回/提供像素的颜色),你返回你要设置的新颜色如果图像可以改变,并返回原始图像的像素,你不会改变图像。
实现 image.Image
界面最容易通过使用嵌入完成,因此您只需要实现所需的更改。这是如何实现的:
键入MyImg struct {
//嵌入image.Image,MyImg将执行image.Image
//因为Image的字段和方法会被提升:
image.Image
}
func(m * MyImg)At(x,y int)color.Color {
//已更改部分:特定坐标的自定义颜色:
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}
}
//未更改部分:原始图像的颜色:
返回m.Image.At(x,y)
}
使用它:非常简单。像你一样加载图像,但保存时,请提供我们的 MyImg
类型的值,以便在需要时提供已更改的图像内容(颜色)编码器:
jpeg.Encode(outFile,& MyImg {img},nil)
$ c $如果你不得不改变很多像素,那么在 At()
方法。为此,我们可以扩展我们的 MyImg
以使我们的 Set()
实现存储我们想要改变的像素。示例实现:
类型MyImg结构{
image.Image
自定义地图[image.Point]颜色.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
$ b func(m * MyImg)At(x,y int)color.Color {
//显式更改部分:已更改像素的自定义颜色:
如果c:= m.custom [image.Point {x,y}]; c!= nil {
return c
}
//未改变部分:原始图像的颜色:
返回m.Image.At(x,y)
}
使用它:
//像往常一样加载图像,然后
my:= NewMyImg(img)
my.Set(0,0,color.RGBA {85,165, 34,1})
my.Set(0,1,color.RGBA {255,0,0,055})
//当保存时,保存'my'而不是原文:
jpeg.Encode(outFile,my,nil)
如果您必须改变许多像素,那么创建一个支持改变其像素的新图像可能会更有利可图,例如 image.RGBA
,绘制原图图像,然后继续改变你想要的像素。
要将图像绘制到另一个图像上,可以使用 image / draw
包。
cimg:= image.NewRGBA(img.Bounds())
draw.Draw(cimg,img.Bounds(),img,image.Point {},绘制。 Over)
//现在你有cimg,它包含原始图像并且是可更改的
//(它有一个Set()方法)
cimg.Set(0,0 ,color.RGBA {85,165,34,255})
cimg.Set(0,1,color.RGBA {255,0,0,255})
//并且当保存时,保存'cimg'当然:
jpeg.Encode(outFile,cimg,nil)
上面的代码仅供演示。在真实生活图像中, Image.Bounds()
可能会返回一个不在(0; 0)
点,在这种情况下,需要进行一些调整才能使其工作。
I want to open jpeg image file, encode it, change some pixel colors, and then save it back as it was.
I'd like to do something like this
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)
I just can't come up with a working solution, since default image type that I get after encoding image-file doesn't have Set method.
Can anyone explain how to do this? Thanks a lot.
解决方案 On successful decoding image.Decode()
(and also specific decoding functions like jpeg.Decode()
) return a value of image.Image
. image.Image
is an interface which defines a read-only view of an image: it does not provide methods to change / draw on the image.
The image
package provides several image.Image
implementations which allow you to change / draw on the image, usually with a Set(x, y int, c color.Color)
method.
image.Decode()
however does not give you any guarantee that the returned image will be any of the image types defined in the image
package, or even that the dynamic type of the image has a Set()
method (it may, but no guarantee). Registered custom image decoders may return you an image.Image
value being a custom implementation (meaning not an image type defined in the image
package).
If the (dynamic type of the) image does have a Set()
method, you may use type assertion and use its Set()
method to draw on it. This is how it can be done:
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
}
If the image does not have a Set()
method, you may choose to "override its view" by implementing a custom type which implements image.Image
, but in its At(x, y int) color.Color
method (which returns / supplies the colors of pixels) you return the new colors that you would set if the image would be changeable, and return the pixels of the original image where you would not change the image.
Implementing the image.Image
interface is easiest done by utilizing embedding, so you only need to implement the changes you want. This is how it can be done:
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)
}
Using it: extremely simple. Load the image as you did, but when saving, provide a value of our MyImg
type which will take care of providing the altered image content (colors) when it is asked by the encoder:
jpeg.Encode(outFile, &MyImg{img}, nil)
If you have to change many pixels, it's not practical to include all in the At()
method. For that we can extend our MyImg
to have our Set()
implementation which stores the pixels that we want to change. Example implementation:
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)
}
Using it:
// 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)
If you have to change many pixels, then it might be more profitable to just create a new image which supports changing its pixels, e.g. image.RGBA
, draw the original image on it and then proceed to change pixels you want to.
To draw an image onto another, you can use the image/draw
package.
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)
The above code is just for demonstration. In "real-life" images Image.Bounds()
may return a rectangle that does not start at (0;0)
point, in which case some adjustment would be needed to make it work.
这篇关于更改单个像素的颜色 - Go lang图像的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!