// 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 ( "image" "image/jpeg" "os" "testing" ) func TestHashCompute(t *testing.T) { for _, tt := range []struct { img1 string img2 string method func(img image.Image) (*ImageHash, error) name string distance int }{ {"_examples/sample1.jpg", "_examples/sample1.jpg", AverageHash, "AverageHash", 0}, {"_examples/sample2.jpg", "_examples/sample2.jpg", AverageHash, "AverageHash", 0}, {"_examples/sample3.jpg", "_examples/sample3.jpg", AverageHash, "AverageHash", 0}, {"_examples/sample4.jpg", "_examples/sample4.jpg", AverageHash, "AverageHash", 0}, {"_examples/sample1.jpg", "_examples/sample2.jpg", AverageHash, "AverageHash", 40}, {"_examples/sample1.jpg", "_examples/sample3.jpg", AverageHash, "AverageHash", 0}, {"_examples/sample1.jpg", "_examples/sample4.jpg", AverageHash, "AverageHash", 34}, {"_examples/sample2.jpg", "_examples/sample3.jpg", AverageHash, "AverageHash", 40}, {"_examples/sample2.jpg", "_examples/sample4.jpg", AverageHash, "AverageHash", 6}, {"_examples/sample1.jpg", "_examples/sample1.jpg", DifferenceHash, "DifferenceHash", 0}, {"_examples/sample2.jpg", "_examples/sample2.jpg", DifferenceHash, "DifferenceHash", 0}, {"_examples/sample3.jpg", "_examples/sample3.jpg", DifferenceHash, "DifferenceHash", 0}, {"_examples/sample4.jpg", "_examples/sample4.jpg", DifferenceHash, "DifferenceHash", 0}, {"_examples/sample1.jpg", "_examples/sample2.jpg", DifferenceHash, "DifferenceHash", 40}, {"_examples/sample1.jpg", "_examples/sample3.jpg", DifferenceHash, "DifferenceHash", 2}, {"_examples/sample1.jpg", "_examples/sample4.jpg", DifferenceHash, "DifferenceHash", 38}, {"_examples/sample2.jpg", "_examples/sample3.jpg", DifferenceHash, "DifferenceHash", 42}, {"_examples/sample2.jpg", "_examples/sample4.jpg", DifferenceHash, "DifferenceHash", 20}, {"_examples/sample1.jpg", "_examples/sample1.jpg", PerceptionHash, "PerceptionHash", 0}, {"_examples/sample2.jpg", "_examples/sample2.jpg", PerceptionHash, "PerceptionHash", 0}, {"_examples/sample3.jpg", "_examples/sample3.jpg", PerceptionHash, "PerceptionHash", 0}, {"_examples/sample4.jpg", "_examples/sample4.jpg", PerceptionHash, "PerceptionHash", 0}, {"_examples/sample1.jpg", "_examples/sample2.jpg", PerceptionHash, "PerceptionHash", 34}, {"_examples/sample1.jpg", "_examples/sample3.jpg", PerceptionHash, "PerceptionHash", 0}, {"_examples/sample1.jpg", "_examples/sample4.jpg", PerceptionHash, "PerceptionHash", 30}, {"_examples/sample2.jpg", "_examples/sample3.jpg", PerceptionHash, "PerceptionHash", 34}, {"_examples/sample2.jpg", "_examples/sample4.jpg", PerceptionHash, "PerceptionHash", 20}, } { file1, err := os.Open(tt.img1) if err != nil { } defer file1.Close() file2, err := os.Open(tt.img2) if err != nil { t.Errorf("%s", err) } defer file2.Close() img1, err := jpeg.Decode(file1) if err != nil { t.Errorf("%s", err) } img2, err := jpeg.Decode(file2) if err != nil { t.Errorf("%s", err) } hash1, err := tt.method(img1) if err != nil { t.Errorf("%s", err) } hash2, err := tt.method(img2) if err != nil { t.Errorf("%s", err) } dis1, err := hash1.Distance(hash2) if err != nil { t.Errorf("%s", err) } dis2, err := hash2.Distance(hash1) if err != nil { t.Errorf("%s", err) } if dis1 != dis2 { t.Errorf("Distance should be identical %v vs %v", dis1, dis2) } if dis1 != tt.distance { t.Errorf("%s: Distance between %v and %v is expected %v but got %v", tt.name, tt.img1, tt.img2, tt.distance, dis1) } } } func TestNilHashCompute(t *testing.T) { hash, err := AverageHash(nil) if err == nil { t.Errorf("Error should be got.") } if hash != nil { t.Errorf("Nil hash should be got. but got %v", hash) } hash, err = DifferenceHash(nil) if err == nil { t.Errorf("Error should be got.") } if hash != nil { t.Errorf("Nil hash should be got. but got %v", hash) } hash, err = PerceptionHash(nil) if err == nil { t.Errorf("Error should be got.") } if hash != nil { t.Errorf("Nil hash should be got. but got %v", hash) } } func TestExtendHashCompute(t *testing.T) { file, err := os.Open("_examples/sample1.jpg") if err != nil { t.Errorf("%s", err) } defer file.Close() img, err := jpeg.Decode(file) if err != nil { t.Errorf("%s", err) } hash, err := ExtPerceptionHash(img, 0, 8) if err == nil { t.Errorf("Error should be got.") } if hash != nil { t.Errorf("Nil hash should be got. but got %v", hash) } hash, err = ExtPerceptionHash(img, 16, 2) if err != nil { t.Errorf("%s", err) } if hash == nil { t.Errorf("Hash should be got.") } } func TestNilExtendHashCompute(t *testing.T) { hash, err := ExtAverageHash(nil, 8, 8) if err == nil { t.Errorf("Error should be got.") } if hash != nil { t.Errorf("Nil hash should be got. but got %v", hash) } hash, err = ExtDifferenceHash(nil, 8, 8) if err == nil { t.Errorf("Error should be got.") } if hash != nil { t.Errorf("Nil hash should be got. but got %v", hash) } hash, err = ExtPerceptionHash(nil, 8, 8) if err == nil { t.Errorf("Error should be got.") } if hash != nil { t.Errorf("Nil hash should be got. but got %v", hash) } hash, err = ExtPerceptionHash(nil, 8, 9) if err == nil { t.Errorf("Error should be got.") } if hash != nil { t.Errorf("Nil hash should be got. but got %v", hash) } } func BenchmarkDistanceIdentical(b *testing.B) { h1 := &ImageHash{hash: 0xe48ae53c05e502f7} h2 := &ImageHash{hash: 0xe48ae53c05e502f7} for i := 0; i < b.N; i++ { h1.Distance(h2) } } func BenchmarkDistanceDifferent(b *testing.B) { h1 := &ImageHash{hash: 0xe48ae53c05e502f7} h2 := &ImageHash{hash: 0x678be53815e510f7} // 8 bits flipped for i := 0; i < b.N; i++ { h1.Distance(h2) } } func TestExtImageHashCompute(t *testing.T) { for _, tt := range []struct { img1 string img2 string width int height int method func(img image.Image, width, height int) (*ExtImageHash, error) name string distance int }{ {"_examples/sample1.jpg", "_examples/sample1.jpg", 8, 8, ExtAverageHash, "ExtAverageHash", 0}, {"_examples/sample2.jpg", "_examples/sample2.jpg", 8, 8, ExtAverageHash, "ExtAverageHash", 0}, {"_examples/sample3.jpg", "_examples/sample3.jpg", 8, 8, ExtAverageHash, "ExtAverageHash", 0}, {"_examples/sample4.jpg", "_examples/sample4.jpg", 8, 8, ExtAverageHash, "ExtAverageHash", 0}, {"_examples/sample1.jpg", "_examples/sample2.jpg", 8, 8, ExtAverageHash, "ExtAverageHash", 40}, {"_examples/sample1.jpg", "_examples/sample3.jpg", 8, 8, ExtAverageHash, "ExtAverageHash", 0}, {"_examples/sample1.jpg", "_examples/sample4.jpg", 8, 8, ExtAverageHash, "ExtAverageHash", 34}, {"_examples/sample2.jpg", "_examples/sample3.jpg", 8, 8, ExtAverageHash, "ExtAverageHash", 40}, {"_examples/sample2.jpg", "_examples/sample4.jpg", 8, 8, ExtAverageHash, "ExtAverageHash", 6}, {"_examples/sample1.jpg", "_examples/sample1.jpg", 16, 16, ExtAverageHash, "ExtAverageHash", 0}, {"_examples/sample2.jpg", "_examples/sample2.jpg", 16, 16, ExtAverageHash, "ExtAverageHash", 0}, {"_examples/sample3.jpg", "_examples/sample3.jpg", 16, 16, ExtAverageHash, "ExtAverageHash", 0}, {"_examples/sample4.jpg", "_examples/sample4.jpg", 16, 16, ExtAverageHash, "ExtAverageHash", 0}, {"_examples/sample1.jpg", "_examples/sample2.jpg", 16, 16, ExtAverageHash, "ExtAverageHash", 143}, {"_examples/sample1.jpg", "_examples/sample3.jpg", 16, 16, ExtAverageHash, "ExtAverageHash", 3}, {"_examples/sample1.jpg", "_examples/sample4.jpg", 16, 16, ExtAverageHash, "ExtAverageHash", 148}, {"_examples/sample2.jpg", "_examples/sample3.jpg", 16, 16, ExtAverageHash, "ExtAverageHash", 146}, {"_examples/sample2.jpg", "_examples/sample4.jpg", 16, 16, ExtAverageHash, "ExtAverageHash", 31}, {"_examples/sample1.jpg", "_examples/sample1.jpg", 17, 17, ExtAverageHash, "ExtAverageHash", 0}, {"_examples/sample2.jpg", "_examples/sample2.jpg", 17, 17, ExtAverageHash, "ExtAverageHash", 0}, {"_examples/sample3.jpg", "_examples/sample3.jpg", 17, 17, ExtAverageHash, "ExtAverageHash", 0}, {"_examples/sample4.jpg", "_examples/sample4.jpg", 17, 17, ExtAverageHash, "ExtAverageHash", 0}, {"_examples/sample1.jpg", "_examples/sample1.jpg", 8, 4, ExtPerceptionHash, "ExtPerceptionHash", 0}, {"_examples/sample2.jpg", "_examples/sample2.jpg", 8, 4, ExtPerceptionHash, "ExtPerceptionHash", 0}, {"_examples/sample3.jpg", "_examples/sample3.jpg", 8, 4, ExtPerceptionHash, "ExtPerceptionHash", 0}, {"_examples/sample4.jpg", "_examples/sample4.jpg", 8, 4, ExtPerceptionHash, "ExtPerceptionHash", 0}, {"_examples/sample1.jpg", "_examples/sample2.jpg", 8, 4, ExtPerceptionHash, "ExtPerceptionHash", 32}, {"_examples/sample1.jpg", "_examples/sample3.jpg", 8, 4, ExtPerceptionHash, "ExtPerceptionHash", 2}, {"_examples/sample1.jpg", "_examples/sample4.jpg", 8, 4, ExtPerceptionHash, "ExtPerceptionHash", 30}, {"_examples/sample2.jpg", "_examples/sample3.jpg", 8, 4, ExtPerceptionHash, "ExtPerceptionHash", 34}, {"_examples/sample2.jpg", "_examples/sample4.jpg", 8, 4, ExtPerceptionHash, "ExtPerceptionHash", 20}, {"_examples/sample1.jpg", "_examples/sample1.jpg", 16, 4, ExtPerceptionHash, "ExtPerceptionHash", 0}, {"_examples/sample2.jpg", "_examples/sample2.jpg", 16, 4, ExtPerceptionHash, "ExtPerceptionHash", 0}, {"_examples/sample3.jpg", "_examples/sample3.jpg", 16, 4, ExtPerceptionHash, "ExtPerceptionHash", 0}, {"_examples/sample4.jpg", "_examples/sample4.jpg", 16, 4, ExtPerceptionHash, "ExtPerceptionHash", 0}, {"_examples/sample1.jpg", "_examples/sample2.jpg", 16, 4, ExtPerceptionHash, "ExtPerceptionHash", 124}, {"_examples/sample1.jpg", "_examples/sample3.jpg", 16, 4, ExtPerceptionHash, "ExtPerceptionHash", 14}, {"_examples/sample1.jpg", "_examples/sample4.jpg", 16, 4, ExtPerceptionHash, "ExtPerceptionHash", 122}, {"_examples/sample2.jpg", "_examples/sample3.jpg", 16, 4, ExtPerceptionHash, "ExtPerceptionHash", 120}, {"_examples/sample2.jpg", "_examples/sample4.jpg", 16, 4, ExtPerceptionHash, "ExtPerceptionHash", 102}, {"_examples/sample1.jpg", "_examples/sample1.jpg", 8, 8, ExtDifferenceHash, "ExtDifferenceHash", 0}, {"_examples/sample2.jpg", "_examples/sample2.jpg", 8, 8, ExtDifferenceHash, "ExtDifferenceHash", 0}, {"_examples/sample3.jpg", "_examples/sample3.jpg", 8, 8, ExtDifferenceHash, "ExtDifferenceHash", 0}, {"_examples/sample4.jpg", "_examples/sample4.jpg", 8, 8, ExtDifferenceHash, "ExtDifferenceHash", 0}, {"_examples/sample1.jpg", "_examples/sample2.jpg", 8, 8, ExtDifferenceHash, "ExtDifferenceHash", 40}, {"_examples/sample1.jpg", "_examples/sample3.jpg", 8, 8, ExtDifferenceHash, "ExtDifferenceHash", 2}, {"_examples/sample1.jpg", "_examples/sample4.jpg", 8, 8, ExtDifferenceHash, "ExtDifferenceHash", 38}, {"_examples/sample2.jpg", "_examples/sample3.jpg", 8, 8, ExtDifferenceHash, "ExtDifferenceHash", 42}, {"_examples/sample2.jpg", "_examples/sample4.jpg", 8, 8, ExtDifferenceHash, "ExtDifferenceHash", 20}, {"_examples/sample1.jpg", "_examples/sample1.jpg", 16, 16, ExtDifferenceHash, "ExtDifferenceHash", 0}, {"_examples/sample2.jpg", "_examples/sample2.jpg", 16, 16, ExtDifferenceHash, "ExtDifferenceHash", 0}, {"_examples/sample3.jpg", "_examples/sample3.jpg", 16, 16, ExtDifferenceHash, "ExtDifferenceHash", 0}, {"_examples/sample4.jpg", "_examples/sample4.jpg", 16, 16, ExtDifferenceHash, "ExtDifferenceHash", 0}, {"_examples/sample1.jpg", "_examples/sample2.jpg", 16, 16, ExtDifferenceHash, "ExtDifferenceHash", 137}, {"_examples/sample1.jpg", "_examples/sample3.jpg", 16, 16, ExtDifferenceHash, "ExtDifferenceHash", 13}, {"_examples/sample1.jpg", "_examples/sample4.jpg", 16, 16, ExtDifferenceHash, "ExtDifferenceHash", 124}, {"_examples/sample2.jpg", "_examples/sample3.jpg", 16, 16, ExtDifferenceHash, "ExtDifferenceHash", 140}, {"_examples/sample2.jpg", "_examples/sample4.jpg", 16, 16, ExtDifferenceHash, "ExtDifferenceHash", 109}, {"_examples/sample1.jpg", "_examples/sample1.jpg", 17, 17, ExtDifferenceHash, "ExtDifferenceHash", 0}, {"_examples/sample2.jpg", "_examples/sample2.jpg", 17, 17, ExtDifferenceHash, "ExtDifferenceHash", 0}, {"_examples/sample3.jpg", "_examples/sample3.jpg", 17, 17, ExtDifferenceHash, "ExtDifferenceHash", 0}, {"_examples/sample4.jpg", "_examples/sample4.jpg", 17, 17, ExtDifferenceHash, "ExtDifferenceHash", 0}, } { file1, err := os.Open(tt.img1) if err != nil { t.Errorf("%s", err) } defer file1.Close() file2, err := os.Open(tt.img2) if err != nil { t.Errorf("%s", err) } defer file2.Close() img1, err := jpeg.Decode(file1) if err != nil { t.Errorf("%s", err) } img2, err := jpeg.Decode(file2) if err != nil { t.Errorf("%s", err) } hash1, err := tt.method(img1, tt.width, tt.height) if err != nil { t.Errorf("%s", err) } hash2, err := tt.method(img2, tt.width, tt.height) if err != nil { t.Errorf("%s", err) } dis1, err := hash1.Distance(hash2) if err != nil { t.Errorf("%s", err) } dis2, err := hash2.Distance(hash1) if err != nil { t.Errorf("%s", err) } if dis1 != dis2 { t.Errorf("Distance should be identical %v vs %v", dis1, dis2) } if dis1 != tt.distance { t.Errorf("%s: Distance between %v and %v is expected %v but got %v: %v %v", tt.name, tt.img1, tt.img2, tt.distance, dis1, hash1, hash2) } } } func BenchmarkExtImageHashDistanceDifferent(b *testing.B) { h1 := &ExtImageHash{hash: []uint64{0xe48ae53c05e502f7}} h2 := &ExtImageHash{hash: []uint64{0x678be53815e510f7}} // 8 bits flipped for i := 0; i < b.N; i++ { _, err := h1.Distance(h2) if err != nil { b.Errorf("%s", err) } } } func BenchmarkPerceptionHash(b *testing.B) { file1, err := os.Open("_examples/sample3.jpg") if err != nil { b.Errorf("%s", err) } defer file1.Close() img1, err := jpeg.Decode(file1) if err != nil { b.Errorf("%s", err) } for i := 0; i < b.N; i++ { _, err := ExtPerceptionHash(img1, 8, 8) if err != nil { b.Errorf("%s", err) } } } func BenchmarkAverageHash(b *testing.B) { file1, err := os.Open("_examples/sample3.jpg") if err != nil { b.Errorf("%s", err) } defer file1.Close() img1, err := jpeg.Decode(file1) if err != nil { b.Errorf("%s", err) } for i := 0; i < b.N; i++ { _, err := ExtAverageHash(img1, 8, 8) if err != nil { b.Errorf("%s", err) } } } func BenchmarkDifferenceHash(b *testing.B) { file1, err := os.Open("_examples/sample3.jpg") if err != nil { b.Errorf("%s", err) } defer file1.Close() img1, err := jpeg.Decode(file1) if err != nil { b.Errorf("%s", err) } for i := 0; i < b.N; i++ { _, err := ExtDifferenceHash(img1, 8, 8) if err != nil { b.Errorf("%s", err) } } }