From 47321c08d357931ffda21995a14d90befb12fd00 Mon Sep 17 00:00:00 2001 From: Dong-hee Na Date: Sun, 17 Mar 2019 15:42:07 +0900 Subject: [PATCH] imagehash: Create a new API Bits() (#25) --- README.md | 13 +++++++++++++ _examples/examples.go | 2 ++ _examples/load_and_dump.go | 10 ++++++++-- hashcompute.go | 6 +++--- imagehash.go | 30 ++++++++++++++++++++++++------ imagehash_test.go | 33 +++++++++++++++++++++++++++++++++ 6 files changed, 83 insertions(+), 11 deletions(-) diff --git a/README.md b/README.md index 03570da..0ee631c 100644 --- a/README.md +++ b/README.md @@ -39,6 +39,19 @@ func main() { hash2, _ = goimagehash.DifferenceHash(img2) distance, _ = hash1.Distance(hash2) fmt.Printf("Distance between images: %v\n", distance) + hash3, _ = goimagehash.AverageHashExtend(img1, 16) + hash4, _ = goimagehash.AverageHashExtend(img2, 16) + distance, _ = hash3.Distance(hash4) + fmt.Printf("Distance between images: %v\n", distance) + fmt.Printf("hash3 bit size: %v\n", hash3.Bits()) + fmt.Printf("hash4 bit size: %v\n", hash4.Bits()) + + var b bytes.Buffer + foo := bufio.NewWriter(&b) + _ = hash4.Dump(foo) + foo.Flush() + bar := bufio.NewReader(&b) + hash5, _ := goimagehash.LoadImageHashExtend(bar) } ``` diff --git a/_examples/examples.go b/_examples/examples.go index 67c056f..d13c398 100644 --- a/_examples/examples.go +++ b/_examples/examples.go @@ -29,4 +29,6 @@ func main() { hash2, _ = goimagehash.PerceptionHash(img2) distance, _ = hash1.Distance(hash2) fmt.Printf("Distance between images: %v\n", distance) + fmt.Println(hash1.Bits()) + fmt.Println(hash2.Bits()) } diff --git a/_examples/load_and_dump.go b/_examples/load_and_dump.go index b639bef..1a34115 100644 --- a/_examples/load_and_dump.go +++ b/_examples/load_and_dump.go @@ -4,9 +4,10 @@ import ( "bufio" "bytes" "fmt" - "github.com/corona10/goimagehash" "image/jpeg" "os" + + "github.com/corona10/goimagehash" ) func main() { @@ -20,9 +21,9 @@ func main() { img2, _ := jpeg.Decode(file2) hash1, _ := goimagehash.AverageHashExtend(img1, 15) hash2, _ := goimagehash.AverageHashExtend(img2, 15) + hash1024, _ := goimagehash.AverageHashExtend(img2, 32) distance, _ := hash1.Distance(hash2) fmt.Printf("Distance between images: %v\n", distance) - err := hash1.Dump(foo) if err != nil { fmt.Println(err) @@ -33,6 +34,8 @@ func main() { if err != nil { fmt.Println(err) } + distance, err = hash1.Distance(hash1024) + fmt.Println(err) distance, _ = hash1.Distance(hash3) fmt.Printf("Distance between hash1 and hash3: %v\n", distance) distance, _ = hash2.Distance(hash3) @@ -40,4 +43,7 @@ func main() { fmt.Println(hash1.ToString()) fmt.Println(hash2.ToString()) fmt.Println(hash3.ToString()) + fmt.Println(hash1.Bits()) + fmt.Println(hash2.Bits()) + fmt.Println(hash3.Bits()) } diff --git a/hashcompute.go b/hashcompute.go index 877f3a3..4ac46f7 100644 --- a/hashcompute.go +++ b/hashcompute.go @@ -112,7 +112,7 @@ func PerceptionHashExtend(img image.Image, hashSize int) (*ExtImageHash, error) phash[indexOfArray] |= 1 << uint(indexOfBit) } } - return NewExtImageHash(phash, PHash), nil + return NewExtImageHash(phash, PHash, imgSize), nil } // AverageHashExtend function returns ahash of which the size can be set larger than uint64 @@ -142,7 +142,7 @@ func AverageHashExtend(img image.Image, hashSize int) (*ExtImageHash, error) { ahash[indexOfArray] |= 1 << uint(indexOfBit) } } - return NewExtImageHash(ahash, AHash), nil + return NewExtImageHash(ahash, AHash, imgSize), nil } // DifferenceHashExtend function returns dhash of which the size can be set larger than uint64 @@ -175,5 +175,5 @@ func DifferenceHashExtend(img image.Image, hashSize int) (*ExtImageHash, error) idx++ } } - return NewExtImageHash(dhash, DHash), nil + return NewExtImageHash(dhash, DHash, imgSize), nil } diff --git a/imagehash.go b/imagehash.go index 9072807..4a89550 100644 --- a/imagehash.go +++ b/imagehash.go @@ -26,6 +26,7 @@ type ImageHash struct { type ExtImageHash struct { hash []uint64 kind Kind + bits int } const ( @@ -46,6 +47,11 @@ func NewImageHash(hash uint64, kind Kind) *ImageHash { return &ImageHash{hash: hash, kind: kind} } +// Bits method returns an actual hash bit size +func (h *ImageHash) Bits() int { + return 64 +} + // Distance method returns a distance between two hashes. func (h *ImageHash) Distance(other *ImageHash) (int, error) { if h.GetKind() != other.GetKind() { @@ -146,8 +152,13 @@ func (h *ImageHash) ToString() string { } // NewExtImageHash function creates a new big hash -func NewExtImageHash(hash []uint64, kind Kind) *ExtImageHash { - return &ExtImageHash{hash: hash, kind: kind} +func NewExtImageHash(hash []uint64, kind Kind, bits int) *ExtImageHash { + return &ExtImageHash{hash: hash, kind: kind, bits: bits} +} + +// Bits method returns an actual hash bit size +func (h *ExtImageHash) Bits() int { + return h.bits } // Distance method returns a distance between two big hashes @@ -156,13 +167,18 @@ func (h *ExtImageHash) Distance(other *ExtImageHash) (int, error) { return -1, errors.New("Extended Image hashes's kind should be identical") } + if h.Bits() != other.Bits() { + msg := fmt.Sprintf("Extended image hash should has an identical bit size but got %v vs %v", h.Bits(), other.Bits()) + return -1, errors.New(msg) + } + lHash := h.GetHash() rHash := other.GetHash() if len(lHash) != len(rHash) { return -1, errors.New("Extended Image hashes's size should be identical") } - var distance int + distance := 0 for idx, lh := range lHash { rh := rHash[idx] hamming := lh ^ rh @@ -186,9 +202,10 @@ func (h *ExtImageHash) Dump(w io.Writer) error { type D struct { Hash []uint64 Kind Kind + Bits int } enc := gob.NewEncoder(w) - err := enc.Encode(D{Hash: h.hash, Kind: h.kind}) + err := enc.Encode(D{Hash: h.hash, Kind: h.kind, Bits: h.bits}) if err != nil { return err } @@ -200,6 +217,7 @@ func LoadImageHashExtend(b io.Reader) (*ExtImageHash, error) { type E struct { Hash []uint64 Kind Kind + Bits int } var e E dec := gob.NewDecoder(b) @@ -207,7 +225,7 @@ func LoadImageHashExtend(b io.Reader) (*ExtImageHash, error) { if err != nil { return nil, err } - return &ExtImageHash{hash: e.Hash, kind: e.Kind}, nil + return &ExtImageHash{hash: e.Hash, kind: e.Kind, bits: e.Bits}, nil } const extStrFmt = "%1s:%s" @@ -248,7 +266,7 @@ func ExtImageHashFromString(s string) (*ExtImageHash, error) { case "w": kind = WHash } - return NewExtImageHash(hash, kind), nil + return NewExtImageHash(hash, kind, len(hash)*64), nil } // ToString returns a hex representation of big hash diff --git a/imagehash_test.go b/imagehash_test.go index ae90f2f..ed47fb8 100644 --- a/imagehash_test.go +++ b/imagehash_test.go @@ -127,6 +127,31 @@ func TestSerialization(t *testing.T) { } } +func TestDifferentBitSizeHash(t *testing.T) { + checkErr := func(err error) { + if err != nil { + t.Errorf("%v", err) + } + } + file, err := os.Open("_examples/sample1.jpg") + checkErr(err) + defer file.Close() + + img, _, err := image.Decode(file) + checkErr(err) + + hash1, _ := AverageHashExtend(img, 32) + hash2, _ := DifferenceHashExtend(img, 32) + _, err = hash1.Distance(hash2) + if err == nil { + t.Errorf("Should got error with different kinds of hashes") + } + hash3, _ := AverageHashExtend(img, 31) + _, err = hash1.Distance(hash3) + if err == nil { + t.Errorf("Should got error with different bits of hashes") + } +} func TestDumpAndLoad(t *testing.T) { checkErr := func(err error) { if err != nil { @@ -168,6 +193,10 @@ func TestDumpAndLoad(t *testing.T) { if distance != 0 { t.Errorf("Original and unserialized objects should be identical, got distance=%v", distance) } + + if hash.Bits() != 64 || reHash.Bits() != 64 { + t.Errorf("Hash bits should be 64 but got, %v, %v", hash.Bits(), reHash.Bits()) + } } // test for ExtIExtImageHash @@ -195,6 +224,10 @@ func TestDumpAndLoad(t *testing.T) { if distance != 0 { t.Errorf("Original and unserialized objects should be identical, got distance=%v", distance) } + + if hash.Bits() != hashSize*hashSize || reHash.Bits() != hashSize*hashSize { + t.Errorf("Hash bits should be 64 but got, %v, %v", hash.Bits(), reHash.Bits()) + } } } }