如何比较图像?

时间:2015-09-20 15:06:49

标签: go

在go image包中,我没有看到任何可用于比较两张图片的方法?是否可以像ImageMagick一样进行图像比较?

4 个答案:

答案 0 :(得分:5)

如果您正在尝试比较两个图像,只需将其煮沸为一个数字,以下内容将起作用。这在(例如)遗传算法中非常有用,在遗传算法中,您希望比较一组候选者并选择与参考图像差异最小的候选者:

  1. 访问每个像素,将其分解为其部分:R,G,B和& A(在go:image.At(x,y).RGBA()
  2. 从参考图像中相应的像素值中减去RGBA值。
  3. 平衡差异,加起来。
  4. 取总和的平方根。
  5. 这个数字可以让您大致了解图像的差异。

    如果您知道这两个图像都是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
    }
    

答案 1 :(得分:2)

尝试https://github.com/vitali-fedulov/images。我写 此程序包可以找到几乎重复的内容。有一个具有相同算法的实时网络演示,因此您可以了解该软件包如何满足您的需求。

答案 2 :(得分:1)

受到乔治回答的启发。 下面的功能并不是很快,但是它可以让您直观地评估图像的差异。

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
}

答案 3 :(得分:1)

这里有两个当前的答案,图像需要大小相同,否则比较失败。这里的第三个答案使用 vitali-fedulov/images,它没有任何方法来获取 两个图像之间的差异,只有 Similar 函数返回 bool 确定两个图像是否相似。此外,如果图像大小不同,answer at Rosetta Code 也会失败。

因此,如果我要实现自己的解决方案,首先我需要缩小较大的图像。我为此找到了 x/image/drawnfnt/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)
}