英文:
How to compare images with go?
问题
在Go的image包中,我没有看到可以用来比较两个图像的方法。在Go中是否可以像ImageMagick一样进行图像比较?
英文:
In go image package, i don't see any methods which can be used to compare two images? Is it possible to do image comparison in go similar to ImageMagick?
答案1
得分: 12
如果你想比较两个图像并将其简化为一个数字,可以使用以下方法。这在遗传算法中非常有用,例如你想比较一组候选图像,并选择与参考图像最不相似的图像:
- 访问每个像素,将其分解为其组成部分:R、G、B 和 A(在 Go 语言中使用
image.At(x,y).RGBA()
) - 从参考图像中减去 RGBA 值与相应像素值的差异。
- 对差异进行平方,然后相加。
- 对总和进行平方根运算。
这个数字将给出图像之间差异的大致概念。
如果你知道这两个图像都是 image.RGBA
的实例(或者你可以将它们转换为 image.RGBA
),那么你可以更快地完成比较:直接从 RGBA.Pix
中获取字节。这就是我在这里所做的,它的速度大约是每个像素对使用 img.At(x,y).RGBA()
的速度的 10 倍:
func FastCompare(img1, img2 *image.RGBA) (int64, error) {
if img1.Bounds() != img2.Bounds() {
return 0, fmt.Errorf("image bounds not equal: %+v, %+v", img1.Bounds(), img2.Bounds())
}
accumError := int64(0)
for i := 0; i < len(img1.Pix); i++ {
accumError += int64(sqDiffUInt8(img1.Pix[i], img2.Pix[i]))
}
return int64(math.Sqrt(float64(accumError))), nil
}
func sqDiffUInt8(x, y uint8) uint64 {
d := uint64(x) - uint64(y)
return d * d
}
希望对你有帮助!
英文:
If you are trying to compare two images and just need to boil it down to a single number, the following will work. This is useful in (for example) genetic algorithms, where you want to compare a set of candidates and choose the one that differs the least from a reference image:
- Visit every pixel, break it down into its parts: R, G, B, & A (in go:
image.At(x,y).RGBA()
) - Subtract the RGBA vals from their corresponding pixel vals in the reference image.
- Square the differences, add them up.
- Take the square root of the total sum.
This number will give you a rough idea of how much the images differ.
If you know that the two images are both instances of image.RGBA
(or you can convert them), then you can do something even faster: just grab the bytes directly from RGBA.Pix
. That's what I do here, and it's roughly 10x faster than doing img.At(x,y).RGBA()
for every pixel pair:
func FastCompare(img1, img2 *image.RGBA) (int64, error) {
if img1.Bounds() != img2.Bounds() {
return 0, fmt.Errorf("image bounds not equal: %+v, %+v", img1.Bounds(), img2.Bounds())
}
accumError := int64(0)
for i := 0; i < len(img1.Pix); i++ {
accumError += int64(sqDiffUInt8(img1.Pix[i], img2.Pix[i]))
}
return int64(math.Sqrt(float64(accumError))), nil
}
func sqDiffUInt8(x, y uint8) uint64 {
d := uint64(x) - uint64(y)
return d * d
}
答案2
得分: 4
尝试使用https://github.com/vitali-fedulov/images3。我编写了这个软件包,以便能够找到近似重复的图片。该算法还有一个在线演示,你可以通过它来了解该软件包是否符合你的需求。
英文:
Try https://github.com/vitali-fedulov/images3. I wrote
this package to be able to find near duplicates. There is a live web-demo with the same algorithm, so you can get an idea how well the package suites your needs.
答案3
得分: 0
受到George的回答的启发。
下面的函数不是很快,但它允许您对图像的差异进行可视化评估。
func ImgCompare(img1, img2 image.Image) (int64, image.Image, error) {
bounds1 := img1.Bounds()
bounds2 := img2.Bounds()
if bounds1 != bounds2 {
return math.MaxInt64, nil, fmt.Errorf("图像边界不相等:%+v, %+v", img1.Bounds(), img2.Bounds())
}
accumError := int64(0)
resultImg := image.NewRGBA(image.Rect(
bounds1.Min.X,
bounds1.Min.Y,
bounds1.Max.X,
bounds1.Max.Y,
))
draw.Draw(resultImg, resultImg.Bounds(), img1, image.Point{0, 0}, draw.Src)
for x := bounds1.Min.X; x < bounds1.Max.X; x++ {
for y := bounds1.Min.Y; y < bounds1.Max.Y; y++ {
r1, g1, b1, a1 := img1.At(x, y).RGBA()
r2, g2, b2, a2 := img2.At(x, y).RGBA()
diff := int64(sqDiffUInt32(r1, r2))
diff += int64(sqDiffUInt32(g1, g2))
diff += int64(sqDiffUInt32(b1, b2))
diff += int64(sqDiffUInt32(a1, a2))
if diff > 0 {
accumError += diff
resultImg.Set(
bounds1.Min.X+x,
bounds1.Min.Y+y,
color.RGBA{R: 255, A: 255})
}
}
}
return int64(math.Sqrt(float64(accumError))), resultImg, nil
}
func sqDiffUInt32(x, y uint32) uint64 {
d := uint64(x) - uint64(y)
return d * d
}
英文:
Inspired by George's answer.
The function below is not so fast, but it allows you to visually assess the difference in images.
func ImgCompare(img1, img2 image.Image) (int64, image.Image, error) {
bounds1 := img1.Bounds()
bounds2 := img2.Bounds()
if bounds1 != bounds2 {
return math.MaxInt64, nil, fmt.Errorf("image bounds not equal: %+v, %+v", img1.Bounds(), img2.Bounds())
}
accumError := int64(0)
resultImg := image.NewRGBA(image.Rect(
bounds1.Min.X,
bounds1.Min.Y,
bounds1.Max.X,
bounds1.Max.Y,
))
draw.Draw(resultImg, resultImg.Bounds(), img1, image.Point{0, 0}, draw.Src)
for x := bounds1.Min.X; x < bounds1.Max.X; x++ {
for y := bounds1.Min.Y; y < bounds1.Max.Y; y++ {
r1, g1, b1, a1 := img1.At(x, y).RGBA()
r2, g2, b2, a2 := img2.At(x, y).RGBA()
diff := int64(sqDiffUInt32(r1, r2))
diff += int64(sqDiffUInt32(g1, g2))
diff += int64(sqDiffUInt32(b1, b2))
diff += int64(sqDiffUInt32(a1, a2))
if diff > 0 {
accumError += diff
resultImg.Set(
bounds1.Min.X+x,
bounds1.Min.Y+y,
color.RGBA{R: 255, A: 255})
}
}
}
return int64(math.Sqrt(float64(accumError))), resultImg, nil
}
func sqDiffUInt32(x, y uint32) uint64 {
d := uint64(x) - uint64(y)
return d * d
}
答案4
得分: 0
以下是翻译好的内容:
使用当前的两个答案之一,图像需要具有相同的大小,否则比较将失败。这里的第三个答案使用了vitali-fedulov/images,它没有任何方法来获取两个图像之间的差异,只有一个返回布尔值的Similar
函数,用于确定两个图像是否相似。此外,Rosetta Code的答案在图像大小不同的情况下也会失败。
因此,如果我要实现自己的解决方案,首先我需要缩小较大的图像。我找到了x/image/draw和nfnt/resize来实现这个目的,但我想也许我可以找到一些能够按需缩放图像、对每个图像进行哈希处理并获取哈希差异的包。这里有一个corona10/goimagehash的例子:
package main
import (
"github.com/corona10/goimagehash"
"image/jpeg"
"os"
)
func hash(name string) (*goimagehash.ImageHash, error) {
f, err := os.Open(name)
if err != nil {
return nil, err
}
defer f.Close()
i, err := jpeg.Decode(f)
if err != nil {
return nil, err
}
return goimagehash.AverageHash(i)
}
示例:
package main
func main() {
a, err := hash("mb.jpg")
if err != nil {
panic(err)
}
b, err := hash("hqdefault.jpg")
if err != nil {
panic(err)
}
d, err := a.Distance(b)
if err != nil {
panic(err)
}
println(d)
}
英文:
With two of the current answers here, the images need to be the same size, or the comparison fails. A third answer here uses vitali-fedulov/images, which doesn't have any method to get the
difference between two images, only a Similar
function that returns a bool
determining if two images are similar. Further, the answer at Rosetta Code also fails if the images are different sizes.
So if I was to implement my own solution, first I would need to scale down the larger image. I found x/image/draw and nfnt/resize for that purpose, but I thought maybe I could find something, to kill two birds with one stone. To that end, I did find some packages that scale the images as needed, take a hash of each, and get the difference of the hashes. Here is corona10/goimagehash:
package main
import (
"github.com/corona10/goimagehash"
"image/jpeg"
"os"
)
func hash(name string) (*goimagehash.ImageHash, error) {
f, err := os.Open(name)
if err != nil {
return nil, err
}
defer f.Close()
i, err := jpeg.Decode(f)
if err != nil {
return nil, err
}
return goimagehash.AverageHash(i)
}
Example:
package main
func main() {
a, err := hash("mb.jpg")
if err != nil {
panic(err)
}
b, err := hash("hqdefault.jpg")
if err != nil {
panic(err)
}
d, err := a.Distance(b)
if err != nil {
panic(err)
}
println(d)
}
通过集体智慧和协作来改善编程学习和解决问题的方式。致力于成为全球开发者共同参与的知识库,让每个人都能够通过互相帮助和分享经验来进步。
评论