1 Star 0 Fork 0

h79 / goutils

加入 Gitee
与超过 1200万 开发者一起发现、参与优秀开源项目,私有仓库也完全免费 :)
免费加入
克隆/下载
phash.go 3.77 KB
一键复制 编辑 原始数据 按行查看 历史
huqiuyun 提交于 2022-06-01 16:22 . init
package images
import (
"bufio"
"github.com/disintegration/imaging"
"image"
"io"
"math"
"os"
)
// PHash 感知哈希算法, 值越小相识度越高,10之内可以简单判断这两张图片内容一致
type PHash struct {
size int
smallerSize int
c []float64
hash string
}
func NewPHash() *PHash {
return NewPHashBy(32, 8)
}
func NewPHashBy(size int, smallerSize int) *PHash {
hash := &PHash{
size: size,
smallerSize: smallerSize,
}
hash.initCoefficients()
return hash
}
func (hash *PHash) initCoefficients() {
hash.c = make([]float64, hash.size)
for i := 1; i < hash.size; i++ {
hash.c[i] = 1
}
hash.c[0] = 1 / math.Sqrt(2.0)
}
func (hash *PHash) OpenFile(filename string) error {
file, err := os.Open(filename)
if err != nil {
return err
}
return hash.Open(bufio.NewReader(file))
}
func (hash *PHash) Open(reader io.Reader) error {
img, _, err := image.Decode(reader)
if err != nil {
return err
}
return hash.OpenImage(img)
}
func (hash *PHash) OpenImage(img image.Image) error {
/*
* 1. Reduce size. Like Average Hash, pHash starts with a small image.
* However, the image is larger than 8x8; 32x32 is a good size. This is
* really done to simplify the DCT computation and not because it is
* needed to reduce the high frequencies.
*/
img = imaging.Resize(img, hash.size, hash.size, imaging.Linear)
/*
* 2. Reduce color. The image is reduced to a grayscale just to further
* simplify the number of computations.
*/
img = imaging.Grayscale(img)
var col = make([][]float64, hash.size)
for x := 0; x < img.Bounds().Dx(); x++ {
col[x] = make([]float64, hash.size)
for y := 0; y < img.Bounds().Dy(); y++ {
_, _, b, _ := img.At(x, y).RGBA()
col[x][y] = float64(b >> 8)
}
}
/*
* 3. Compute the DCT. The DCT separates the image into a collection of
* frequencies and scalars. While JPEG uses an 8x8 DCT, this algorithm
* uses a 32x32 DCT.
*/
dct := DCT2D(col, hash.size, hash.size)
/*
* 4. Reduce the DCT. This is the magic step. While the DCT is 32x32,
* just keep the top-left 8x8. Those represent the lowest frequencies in
* the picture.
*/
/*
* 5. Compute the average value. Like the Average Hash, compute the mean
* DCT value (using only the 8x8 DCT low-frequency values and excluding
* the first term since the DC coefficient can be significantly
* different from the other values and will throw off the average).
*/
total := float64(0)
for x := 0; x < hash.smallerSize; x++ {
for y := 0; y < hash.smallerSize; y++ {
total += dct[x][y]
}
}
total -= dct[0][0]
avg := total / float64(hash.smallerSize*hash.smallerSize-1)
/*
* 6. Further reduce the DCT. This is the magic step. Set the 64 hash
* bits to 0 or 1 depending on whether each of the 64 DCT values is
* above or below the average value. The result doesn't tell us the
* actual low frequencies; it just tells us the very-rough relative
* scale of the frequencies to the mean. The result will not vary as
* long as the overall structure of the image remains the same; this can
* survive gamma and color histogram adjustments without a problem.
*/
hash.hash = ""
for x := 0; x < hash.smallerSize; x++ {
for y := 0; y < hash.smallerSize; y++ {
if x != 0 && y != 0 {
if dct[x][y] > avg {
hash.hash += "1"
} else {
hash.hash += "0"
}
}
}
}
return nil
}
// Returns a 'binary string' (like. 001010111011100010) which is easy to do
// a hamming distance on.
func (hash *PHash) String() string {
return hash.hash
}
// Match 值越小相识度越高,10之内可以简单判断这两张图片内容一致
func (hash *PHash) Match(dest *PHash) int {
s1 := hash.String()
s2 := dest.String()
counter := 0
for k := 0; k < len(s1); k++ {
if s1[k] != s2[k] {
counter++
}
}
return counter
}
Go
1
https://gitee.com/h79/goutils.git
git@gitee.com:h79/goutils.git
h79
goutils
goutils
v1.20.57

搜索帮助

53164aa7 5694891 3bd8fe86 5694891