改变单个像素的颜色 - Golang image [英] Change color of a single pixel - Golang image

查看:48
本文介绍了改变单个像素的颜色 - Golang image的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧!

问题描述

我想打开 jpeg 图像文件,对其进行编码,更改一些像素颜色,然后按原样保存.

I want to open jpeg image file, encode it, change some pixel colors, and then save it back as it was.

我想做这样的事情

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 方法.

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.

推荐答案

成功解码 image.Decode()(还有特定的解码函数,如 jpeg.Decode()) 返回值 image.Image.image.Image 是一个定义图像只读视图的接口:它不提供更改/绘制图像的方法.

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.

image 包提供了几个 image.Image 实现,它们允许您更改/绘制图像,通常使用 Set(x, y int,c color.Color) 方法.

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() 但是不保证返回的图像是 image 包中定义的任何图像类型,甚至不保证动态类型的图像有一个 Set() 方法(可能,但不能保证).注册的自定义图像解码器可能会返回一个 image.Image 值作为自定义实现(意味着不是在 image 包中定义的图像类型).

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).

如果(动态类型的)图像确实有 Set() 方法,您可以使用 type assertion 并使用其 Set() 方法对其进行绘制.可以这样做:

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
}

如果图像没有 Set() 方法,您可以选择通过实现实现 image.Image 的自定义类型来覆盖其视图",但是在它的 At(x, y int) color.Color 方法(它返回/提供像素的颜色)中,如果图像可变,您将返回您将设置的新颜色,并返回像素原始图像,您不会更改图像.

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.

使用嵌入最容易实现image.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)
}

使用:极其简单.像您一样加载图像,但在保存时,请提供我们的 MyImg 类型的值,该值将在编码器要求时提供更改的图像内容(颜色):

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)

如果您必须更改许多像素,将所有像素都包含在 At() 方法中是不切实际的.为此,我们可以扩展我们的 MyImg 以让我们的 Set() 实现存储我们想要更改的像素.示例实现:

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)
}

使用:

// 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,在其上绘制原始图像,然后继续改变你想要的像素.

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.

要将图像绘制到另一个图像上,您可以使用 image/draw 包.

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)

以上代码仅用于演示.在真实"图像中,Image.Bounds() 可能会返回一个不是从 (0;0) 点开始的矩形,在这种情况下,需要进行一些调整使其发挥作用.

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.

这篇关于改变单个像素的颜色 - Golang image的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!

查看全文
登录 关闭
扫码关注1秒登录
发送“验证码”获取 | 15天全站免登陆