Increase test coverage to 96.6% (#61)
* gitignore *.html for test coverage reports * fix typos * Increase hashcompute.go test coverage to 100 percent * Add new Go versions. Upgrade GitHub Actions. * Add tests for missing ext hashes * Add test for hashing non-hexadecimal string * Add tests for loading empty bytes buffer * Run go fmt * Fix comments * Fix comments * Fix spelling * fix actions versions * Upgrade to actions/setup-go@v4 * Add minimum Go version for go.mod
This commit is contained in:
parent
464cef2de9
commit
d68e89bd8f
4
.github/workflows/ci.yml
vendored
4
.github/workflows/ci.yml
vendored
@ -4,12 +4,12 @@ jobs:
|
|||||||
test:
|
test:
|
||||||
strategy:
|
strategy:
|
||||||
matrix:
|
matrix:
|
||||||
go-version: [1.6.x, 1.7.x, 1.8.x, 1.9.x, 1.10.x, 1.11.x, 1.12.x, 1.13.x, 1.14.x, 1.15.x, 1.16.x, 1.17.x, 1.18.x]
|
go-version: [1.6.x, 1.7.x, 1.8.x, 1.9.x, 1.10.x, 1.11.x, 1.12.x, 1.13.x, 1.14.x, 1.15.x, 1.16.x, 1.17.x, 1.18.x, 1.19.x, 1.20.x]
|
||||||
platform: [ubuntu-latest]
|
platform: [ubuntu-latest]
|
||||||
runs-on: ${{ matrix.platform }}
|
runs-on: ${{ matrix.platform }}
|
||||||
steps:
|
steps:
|
||||||
- name: Install Go
|
- name: Install Go
|
||||||
uses: actions/setup-go@v1
|
uses: actions/setup-go@v4
|
||||||
with:
|
with:
|
||||||
go-version: ${{ matrix.go-version }}
|
go-version: ${{ matrix.go-version }}
|
||||||
- name: Set GOPATH
|
- name: Set GOPATH
|
||||||
|
4
.github/workflows/ci_gomodule.yml
vendored
4
.github/workflows/ci_gomodule.yml
vendored
@ -4,12 +4,12 @@ jobs:
|
|||||||
test:
|
test:
|
||||||
strategy:
|
strategy:
|
||||||
matrix:
|
matrix:
|
||||||
go-version: [1.11.x, 1.12.x, 1.13.x, 1.14.x, 1.15.x, 1.16.x, 1.17.x, 1.18.x]
|
go-version: [1.11.x, 1.12.x, 1.13.x, 1.14.x, 1.15.x, 1.16.x, 1.17.x, 1.18.x, 1.19.x, 1.20.x]
|
||||||
platform: [ubuntu-latest, macos-latest, windows-latest]
|
platform: [ubuntu-latest, macos-latest, windows-latest]
|
||||||
runs-on: ${{ matrix.platform }}
|
runs-on: ${{ matrix.platform }}
|
||||||
steps:
|
steps:
|
||||||
- name: Install Go
|
- name: Install Go
|
||||||
uses: actions/setup-go@v1
|
uses: actions/setup-go@v4
|
||||||
with:
|
with:
|
||||||
go-version: ${{ matrix.go-version }}
|
go-version: ${{ matrix.go-version }}
|
||||||
- name: Checkout code
|
- name: Checkout code
|
||||||
|
1
.gitignore
vendored
1
.gitignore
vendored
@ -9,6 +9,7 @@
|
|||||||
|
|
||||||
# Output of the go coverage tool, specifically when used with LiteIDE
|
# Output of the go coverage tool, specifically when used with LiteIDE
|
||||||
*.out
|
*.out
|
||||||
|
*.html
|
||||||
|
|
||||||
# Project-local glide cache, RE: https://github.com/Masterminds/glide/issues/736
|
# Project-local glide cache, RE: https://github.com/Masterminds/glide/issues/736
|
||||||
.glide/
|
.glide/
|
||||||
|
2
go.mod
2
go.mod
@ -1,3 +1,5 @@
|
|||||||
module github.com/corona10/goimagehash
|
module github.com/corona10/goimagehash
|
||||||
|
|
||||||
|
go 1.11
|
||||||
|
|
||||||
require github.com/nfnt/resize v0.0.0-20180221191011-83c6a9932646
|
require github.com/nfnt/resize v0.0.0-20180221191011-83c6a9932646
|
||||||
|
@ -14,7 +14,7 @@ import (
|
|||||||
"github.com/nfnt/resize"
|
"github.com/nfnt/resize"
|
||||||
)
|
)
|
||||||
|
|
||||||
// AverageHash fuction returns a hash computation of average hash.
|
// AverageHash function returns a hash computation of average hash.
|
||||||
// Implementation follows
|
// Implementation follows
|
||||||
// http://www.hackerfactor.com/blog/index.php?/archives/432-Looks-Like-It.html
|
// http://www.hackerfactor.com/blog/index.php?/archives/432-Looks-Like-It.html
|
||||||
func AverageHash(img image.Image) (*ImageHash, error) {
|
func AverageHash(img image.Image) (*ImageHash, error) {
|
||||||
|
@ -124,6 +124,34 @@ func TestNilHashCompute(t *testing.T) {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
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) {
|
func TestNilExtendHashCompute(t *testing.T) {
|
||||||
hash, err := ExtAverageHash(nil, 8, 8)
|
hash, err := ExtAverageHash(nil, 8, 8)
|
||||||
if err == nil {
|
if err == nil {
|
||||||
@ -348,7 +376,7 @@ func BenchmarkAverageHash(b *testing.B) {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
func BenchmarkDiffrenceHash(b *testing.B) {
|
func BenchmarkDifferenceHash(b *testing.B) {
|
||||||
file1, err := os.Open("_examples/sample3.jpg")
|
file1, err := os.Open("_examples/sample3.jpg")
|
||||||
if err != nil {
|
if err != nil {
|
||||||
b.Errorf("%s", err)
|
b.Errorf("%s", err)
|
||||||
|
@ -66,6 +66,9 @@ func TestSerialization(t *testing.T) {
|
|||||||
methods := []func(img image.Image) (*ImageHash, error){
|
methods := []func(img image.Image) (*ImageHash, error){
|
||||||
AverageHash, PerceptionHash, DifferenceHash,
|
AverageHash, PerceptionHash, DifferenceHash,
|
||||||
}
|
}
|
||||||
|
extMethods := []func(img image.Image, width int, height int) (*ExtImageHash, error){
|
||||||
|
ExtAverageHash, ExtPerceptionHash, ExtDifferenceHash,
|
||||||
|
}
|
||||||
examples := []string{
|
examples := []string{
|
||||||
"_examples/sample1.jpg", "_examples/sample2.jpg", "_examples/sample3.jpg", "_examples/sample4.jpg",
|
"_examples/sample1.jpg", "_examples/sample2.jpg", "_examples/sample3.jpg", "_examples/sample4.jpg",
|
||||||
}
|
}
|
||||||
@ -103,15 +106,17 @@ func TestSerialization(t *testing.T) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
// test for ExtIExtImageHash
|
// test for ExtIExtImageHash
|
||||||
|
for _, extMethod := range extMethods {
|
||||||
|
extMethodStr := runtime.FuncForPC(reflect.ValueOf(extMethod).Pointer()).Name()
|
||||||
sizeList := []int{8, 16}
|
sizeList := []int{8, 16}
|
||||||
for _, size := range sizeList {
|
for _, size := range sizeList {
|
||||||
hash, err := ExtPerceptionHash(img, size, size)
|
hash, err := extMethod(img, size, size)
|
||||||
checkErr(err)
|
checkErr(err)
|
||||||
|
|
||||||
hex := hash.ToString()
|
hex := hash.ToString()
|
||||||
// len(kind) == 1, len(":") == 1
|
// len(kind) == 1, len(":") == 1
|
||||||
if len(hex) != size*size/4+2 {
|
if len(hex) != size*size/4+2 {
|
||||||
t.Errorf("Got invalid hex string '%v'; %v of '%v'", hex, "ExtPerceptionHash", ex)
|
t.Errorf("Got invalid hex string '%v'; %v of '%v'", hex, extMethodStr, ex)
|
||||||
}
|
}
|
||||||
|
|
||||||
reHash, err := ExtImageHashFromString(hex)
|
reHash, err := ExtImageHashFromString(hex)
|
||||||
@ -127,6 +132,26 @@ func TestSerialization(t *testing.T) {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// test for hashing empty string
|
||||||
|
imageHash, err := ImageHashFromString("")
|
||||||
|
if imageHash != nil {
|
||||||
|
t.Errorf("Expected reHash to be nil, got %v", imageHash)
|
||||||
|
}
|
||||||
|
if err == nil {
|
||||||
|
t.Errorf("Should got error for empty string")
|
||||||
|
}
|
||||||
|
extImageHash, err := ExtImageHashFromString("")
|
||||||
|
if extImageHash != nil {
|
||||||
|
t.Errorf("Expected reHash to be nil, got %v", extImageHash)
|
||||||
|
}
|
||||||
|
if err == nil {
|
||||||
|
t.Errorf("Should got error for empty string")
|
||||||
|
}
|
||||||
|
|
||||||
|
// test for hashing invalid (non-hexadecimal) string
|
||||||
|
extImageHash, err = ExtImageHashFromString("k:g")
|
||||||
|
}
|
||||||
|
|
||||||
func TestDifferentBitSizeHash(t *testing.T) {
|
func TestDifferentBitSizeHash(t *testing.T) {
|
||||||
checkErr := func(err error) {
|
checkErr := func(err error) {
|
||||||
if err != nil {
|
if err != nil {
|
||||||
@ -231,4 +256,16 @@ func TestDumpAndLoad(t *testing.T) {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// test for loading empty bytes buffer
|
||||||
|
var b bytes.Buffer
|
||||||
|
bar := bufio.NewReader(&b)
|
||||||
|
_, err := LoadImageHash(bar)
|
||||||
|
if err == nil {
|
||||||
|
t.Errorf("Should got error for empty bytes buffer")
|
||||||
|
}
|
||||||
|
_, err = LoadExtImageHash(bar)
|
||||||
|
if err == nil {
|
||||||
|
t.Errorf("Should got error for empty bytes buffer")
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
@ -37,7 +37,7 @@ func forwardTransform(input, temp []float64, Len int) {
|
|||||||
input[Len-2], input[Len-1] = temp[halfLen-1], temp[Len-1]
|
input[Len-2], input[Len-1] = temp[halfLen-1], temp[Len-1]
|
||||||
}
|
}
|
||||||
|
|
||||||
// DCT2D function returns a result of DCT2D by using the seperable property.
|
// DCT2D function returns a result of DCT2D by using the separable property.
|
||||||
func DCT2D(input [][]float64, w int, h int) [][]float64 {
|
func DCT2D(input [][]float64, w int, h int) [][]float64 {
|
||||||
output := make([][]float64, h)
|
output := make([][]float64, h)
|
||||||
for i := range output {
|
for i := range output {
|
||||||
@ -74,7 +74,7 @@ func DCT2D(input [][]float64, w int, h int) [][]float64 {
|
|||||||
return output
|
return output
|
||||||
}
|
}
|
||||||
|
|
||||||
// DCT2DFast64 function returns a result of DCT2D by using the seperable property.
|
// DCT2DFast64 function returns a result of DCT2D by using the separable property.
|
||||||
// Fast uses static DCT tables for improved performance. Returns flattened pixels.
|
// Fast uses static DCT tables for improved performance. Returns flattened pixels.
|
||||||
func DCT2DFast64(input *[]float64) (flattens [64]float64) {
|
func DCT2DFast64(input *[]float64) (flattens [64]float64) {
|
||||||
if len(*input) != 64*64 {
|
if len(*input) != 64*64 {
|
||||||
@ -98,7 +98,7 @@ func DCT2DFast64(input *[]float64) (flattens [64]float64) {
|
|||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
// DCT2DFast256 function returns a result of DCT2D by using the seperable property.
|
// DCT2DFast256 function returns a result of DCT2D by using the separable property.
|
||||||
// DCT type II, unscaled. Algorithm by Byeong Gi Lee, 1984.
|
// DCT type II, unscaled. Algorithm by Byeong Gi Lee, 1984.
|
||||||
// Fast uses static DCT tables for improved performance. Returns flattened pixels.
|
// Fast uses static DCT tables for improved performance. Returns flattened pixels.
|
||||||
func DCT2DFast256(input *[]float64) (flattens [256]float64) {
|
func DCT2DFast256(input *[]float64) (flattens [256]float64) {
|
||||||
|
@ -58,7 +58,7 @@ func rgb2GrayDefault(colorImg image.Image, pixels []float64, s int) {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// rgb2GrayYCbCR uses *image.YCbCr which is signifiantly faster than the image.Image interface.
|
// rgb2GrayYCbCR uses *image.YCbCr which is significantly faster than the image.Image interface.
|
||||||
func rgb2GrayYCbCR(colorImg *image.YCbCr, pixels []float64, s int) {
|
func rgb2GrayYCbCR(colorImg *image.YCbCr, pixels []float64, s int) {
|
||||||
for i := 0; i < s; i++ {
|
for i := 0; i < s; i++ {
|
||||||
for j := 0; j < s; j++ {
|
for j := 0; j < s; j++ {
|
||||||
@ -67,7 +67,7 @@ func rgb2GrayYCbCR(colorImg *image.YCbCr, pixels []float64, s int) {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// rgb2GrayYCbCR uses *image.RGBA which is signifiantly faster than the image.Image interface.
|
// rgb2GrayRGBA uses *image.RGBA which is significantly faster than the image.Image interface.
|
||||||
func rgb2GrayRGBA(colorImg *image.RGBA, pixels []float64, s int) {
|
func rgb2GrayRGBA(colorImg *image.RGBA, pixels []float64, s int) {
|
||||||
for i := 0; i < s; i++ {
|
for i := 0; i < s; i++ {
|
||||||
for j := 0; j < s; j++ {
|
for j := 0; j < s; j++ {
|
||||||
|
Loading…
Reference in New Issue
Block a user