// Copyright 2017 The goimagehash Authors. All rights reserved. // Use of this source code is governed by a BSD-style // license that can be found in the LICENSE file. package goimagehash import ( "bufio" "bytes" "errors" "image" _ "image/jpeg" "os" "reflect" "runtime" "testing" ) func TestNewImageHash(t *testing.T) { for _, tt := range []struct { datas [][]uint8 hash1 Kind hash2 Kind distance int err error }{ {[][]uint8{{1, 0, 1, 1}, {0, 0, 0, 0}}, Unknown, Unknown, 3, nil}, {[][]uint8{{0, 0, 0, 0}, {0, 0, 0, 0}}, Unknown, Unknown, 0, nil}, {[][]uint8{{0, 0, 0, 0}, {0, 0, 0, 1}}, Unknown, Unknown, 1, nil}, {[][]uint8{{0, 0, 0, 0}, {0, 0, 0, 1}}, Unknown, AHash, -1, errors.New("Image hashes's kind should be identical")}, } { data1 := tt.datas[0] data2 := tt.datas[1] hash1 := NewImageHash(0, tt.hash1) hash2 := NewImageHash(0, tt.hash2) for i := 0; i < len(data1); i++ { if data1[i] == 1 { hash1.leftShiftSet(i) } } for i := 0; i < len(data2); i++ { if data2[i] == 1 { hash2.leftShiftSet(i) } } dis, err := hash1.Distance(hash2) if dis != tt.distance { t.Errorf("Distance between %v and %v expected as %d but got %d", data1, data2, tt.distance, dis) } if err != nil && err.Error() != tt.err.Error() { t.Errorf("Expected err %s, actual %s", tt.err, err) } } } func TestSerialization(t *testing.T) { checkErr := func(err error) { if err != nil { t.Errorf("%v", err) } } methods := []func(img image.Image) (*ImageHash, error){ AverageHash, PerceptionHash, DifferenceHash, } examples := []string{ "_examples/sample1.jpg", "_examples/sample2.jpg", "_examples/sample3.jpg", "_examples/sample4.jpg", } for _, ex := range examples { file, err := os.Open(ex) checkErr(err) defer file.Close() img, _, err := image.Decode(file) checkErr(err) for _, method := range methods { methodStr := runtime.FuncForPC(reflect.ValueOf(method).Pointer()).Name() hash, err := method(img) checkErr(err) hex := hash.ToString() // len(kind) == 1, len(":") == 1, len(hash) == 16 if len(hex) != 18 { t.Errorf("Got invalid hex string '%v'; %v of '%v'", hex, methodStr, ex) } reHash, err := ImageHashFromString(hex) checkErr(err) distance, err := hash.Distance(reHash) checkErr(err) if distance != 0 { t.Errorf("Original and unserialized objects should be identical, got distance=%v; %v of '%v'", distance, methodStr, ex) } } // test for ExtIExtImageHash sizeList := []int{8, 16} for _, size := range sizeList { hash, err := PerceptionHashExtend(img, size, size) checkErr(err) hex := hash.ToString() // len(kind) == 1, len(":") == 1 if len(hex) != size*size/4+2 { t.Errorf("Got invalid hex string '%v'; %v of '%v'", hex, "PerceptionHashExtend", ex) } reHash, err := ExtImageHashFromString(hex) checkErr(err) distance, err := hash.Distance(reHash) checkErr(err) if distance != 0 { t.Errorf("Original and unserialized objects should be identical, got distance=%v; %v of '%v'", distance, "PerceptionHashExtend", ex) } } } } 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, 32) hash2, _ := DifferenceHashExtend(img, 32, 32) _, err = hash1.Distance(hash2) if err == nil { t.Errorf("Should got error with different kinds of hashes") } hash3, _ := AverageHashExtend(img, 31, 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 { t.Errorf("%v", err) } } methods := []func(img image.Image) (*ImageHash, error){ AverageHash, PerceptionHash, DifferenceHash, } examples := []string{ "_examples/sample1.jpg", "_examples/sample2.jpg", "_examples/sample3.jpg", "_examples/sample4.jpg", } for _, ex := range examples { file, err := os.Open(ex) checkErr(err) defer file.Close() img, _, err := image.Decode(file) checkErr(err) for _, method := range methods { hash, err := method(img) checkErr(err) var b bytes.Buffer foo := bufio.NewWriter(&b) err = hash.Dump(foo) checkErr(err) foo.Flush() bar := bufio.NewReader(&b) reHash, err := LoadImageHash(bar) checkErr(err) distance, err := hash.Distance(reHash) checkErr(err) 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 extMethods := []func(img image.Image, width, height int) (*ExtImageHash, error){ AverageHashExtend, PerceptionHashExtend, DifferenceHashExtend, } sizeList := []int{8, 16} for _, size := range sizeList { for _, method := range extMethods { hash, err := method(img, size, size) checkErr(err) var b bytes.Buffer foo := bufio.NewWriter(&b) err = hash.Dump(foo) checkErr(err) foo.Flush() bar := bufio.NewReader(&b) reHash, err := LoadImageHashExtend(bar) checkErr(err) distance, err := hash.Distance(reHash) checkErr(err) if distance != 0 { t.Errorf("Original and unserialized objects should be identical, got distance=%v", distance) } if hash.Bits() != size*size || reHash.Bits() != size*size { t.Errorf("Hash bits should be 64 but got, %v, %v", hash.Bits(), reHash.Bits()) } } } } }