func main() { image1Ptr := flag.String("i1", "", "input image 1") image2Ptr := flag.String("i2", "", "input image 2") image1OutPtr := flag.String("o1", "", "output image 1") image2OutPtr := flag.String("o2", "", "output image 2") flag.Parse() if *image1Ptr == "" { log.Fatal("Need to provide i1 flag for path to image 1") } if *image2Ptr == "" { log.Fatal("Need to provide i2 flag for path to image 2") } image1File, err := os.Open(*image1Ptr) if err != nil { log.Fatalf("Failed to open image %s, %v", *image1Ptr, err) } defer image1File.Close() image2File, err := os.Open(*image2Ptr) if err != nil { log.Fatalf("Failed to open image %s, %v", *image2Ptr, err) } defer image2File.Close() im1, _, err := image.Decode(image1File) if err != nil { log.Fatalf("Failed to decode image %v", err) } im2, _, err := image.Decode(image2File) if err != nil { log.Fatalf("Failed to decode image %v", err) } hash1 := phash.GreyscaleDctMatrix(im1) hash2 := phash.GreyscaleDctMatrix(im2) distance := phash.HammingDistance(hash1, hash2) log.Printf("Image 1 hash: %x\n", hash1) log.Printf("Image 2 hash: %x\n", hash2) log.Printf("Hamming Distance: %d", distance) if *image1OutPtr != "" { im1 := phash.HashToImage(hash1) out, _ := os.Create(*image1OutPtr) var opt jpeg.Options opt.Quality = 100 _ = jpeg.Encode(out, im1, &opt) // put quality to 80% } if *image2OutPtr != "" { im2 := phash.HashToImage(hash2) out, _ := os.Create(*image2OutPtr) var opt jpeg.Options opt.Quality = 100 _ = jpeg.Encode(out, im2, &opt) // put quality to 80% } }
func TestDifferentImagesDoNotMatch(t *testing.T) { Convey("Given some cats, suns and girls are loaded", t, func() { catBig := getImgBag(catsDir, "cat_big.jpg", 0) catMedium := getImgBag(catsDir, "cat_medium.jpg", 0) catSmall := getImgBag(catsDir, "cat_small.jpg", 0) catSky := getImgBag(catsDir, "cat_sky.jpg", 0) catSmile := getImgBag(catsDir, "smiling.jpg", 0) cats := []*ImageBag{catBig, catMedium, catSmall, catSky, catSmile} lenaToshop := getImgBag(lenaDir, "lena_std.jpg", 0) lenaNkd := getImgBag(lenaDir, "l_hires.jpg", 0) lenaVintage := getImgBag(lenaDir, "lena.bmp", 0) girls := []*ImageBag{lenaToshop, lenaNkd, lenaVintage} sunBig := getImgBag(catsDir, "sun_big.jpg", 0) sunSmall := getImgBag(catsDir, "sun_small.jpg", 0) suns := []*ImageBag{sunBig, sunSmall} Convey("When DCT Hashes are computed", func() { catBig.ImageDigest.ComputeGreyscaleDct() catMedium.ImageDigest.ComputeGreyscaleDct() catSmall.ImageDigest.ComputeGreyscaleDct() catSky.ImageDigest.ComputeGreyscaleDct() catSmile.ImageDigest.ComputeGreyscaleDct() lenaToshop.ImageDigest.ComputeGreyscaleDct() lenaNkd.ImageDigest.ComputeGreyscaleDct() lenaVintage.ImageDigest.ComputeGreyscaleDct() sunBig.ImageDigest.ComputeGreyscaleDct() sunSmall.ImageDigest.ComputeGreyscaleDct() Convey("Then cats do not look like girls", func() { PairExecuteFor2ImagesList(cats, girls, func(cat, girl *ImageBag) { distance := phash.HammingDistance(cat.Phash, girl.Phash) So(distance, ShouldBeGreaterThan, Treshold) }) }) Convey("And suns do not look like cats", func() { PairExecuteFor2ImagesList(suns, cats, func(sun, cat *ImageBag) { distance := phash.HammingDistance(sun.Phash, cat.Phash) So(distance, ShouldBeGreaterThan, Treshold) }) }) Convey("And girls do not look like suns", func() { PairExecuteFor2ImagesList(girls, suns, func(girl, sun *ImageBag) { distance := phash.HammingDistance(sun.Phash, girl.Phash) So(distance, ShouldBeGreaterThan, Treshold) }) }) }) Convey("When DCTMatrix Hashes are computed", func() { fmt.Println("catBig") catBig.ImageDigest.ComputeGreyscaleDctMatrix() fmt.Println("catMedium") catMedium.ImageDigest.ComputeGreyscaleDctMatrix() fmt.Println("catSmall") catSmall.ImageDigest.ComputeGreyscaleDctMatrix() fmt.Println("catSky") catSky.ImageDigest.ComputeGreyscaleDctMatrix() fmt.Println("catSmile") catSmile.ImageDigest.ComputeGreyscaleDctMatrix() fmt.Println("lenaToshop") lenaToshop.ImageDigest.ComputeGreyscaleDctMatrix() fmt.Println("lenankd") lenaNkd.ImageDigest.ComputeGreyscaleDctMatrix() fmt.Println("lenavintage") lenaVintage.ImageDigest.ComputeGreyscaleDctMatrix() fmt.Println("sunbig") sunBig.ImageDigest.ComputeGreyscaleDctMatrix() fmt.Println("sunsmall") sunSmall.ImageDigest.ComputeGreyscaleDctMatrix() Convey("Then cats do not look like girls", func() { PairExecuteFor2ImagesList(cats, girls, func(cat, girl *ImageBag) { distance := hammingDist(cat, girl) So(distance, ShouldBeGreaterThan, Treshold) }) }) Convey("And suns do not look like cats", func() { PairExecuteFor2ImagesList(suns, cats, func(sun, cat *ImageBag) { distance := hammingDist(sun, cat) So(distance, ShouldBeGreaterThan, Treshold) }) }) Convey("And girls do not look like suns", func() { PairExecuteFor2ImagesList(girls, suns, func(girl, sun *ImageBag) { distance := hammingDist(sun, girl) So(distance, ShouldBeGreaterThan, Treshold) }) }) }) /* Convey("When CDCT Hashes are computed", func() { catBig.ComputeImageHashPhash(false) catMedium.ComputeImageHashPhash(false) catSmall.ComputeImageHashPhash(false) catSky.ComputeImageHashPhash(false) catSmile.ComputeImageHashPhash(false) // lenaToshop.ComputeImageHashPhash(false) lenaVintage.ComputeImageHashPhash(false) lenaNkd.ComputeImageHashPhash(false) girls := []*ImageBag{lenaVintage, lenaNkd} sunBig.ComputeImageHashPhash(false) sunSmall.ComputeImageHashPhash(false) Convey("Then cats do not look like girls", func() { PairExecuteFor2ImagesList(cats, girls, func(cat, girl *ImageBag) { distance := phash.HammingDistance(cat.CPhash, girl.CPhash) So(distance, ShouldBeGreaterThan, Treshold) }) }) Convey("And suns do not look like cats", func() { PairExecuteFor2ImagesList(suns, cats, func(sun, cat *ImageBag) { distance := phash.HammingDistance(sun.CPhash, cat.CPhash) So(distance, ShouldBeGreaterThan, Treshold) }) }) Convey("And girls do not look like suns", func() { PairExecuteFor2ImagesList(girls, suns, func(girl, sun *ImageBag) { distance := phash.HammingDistance(sun.CPhash, girl.CPhash) So(distance, ShouldBeGreaterThan, Treshold) }) }) })*/ SkipConvey("When Radon Hashes are computed", func() { catBig.ComputeImageHashRadon(false) catMedium.ComputeImageHashRadon(false) catSmall.ComputeImageHashRadon(false) catSky.ComputeImageHashRadon(false) catSmile.ComputeImageHashRadon(false) lenaToshop.ComputeImageHashRadon(false) lenaNkd.ComputeImageHashRadon(false) lenaVintage.ComputeImageHashRadon(false) sunBig.ComputeImageHashRadon(false) sunSmall.ComputeImageHashRadon(false) Convey("Then cats do not look like girls", func() { PairExecuteFor2ImagesList(cats, girls, func(cat, girl *ImageBag) { crossCoorrelation, _ := radon.DiffByCrossCorr(cat.ImageDigest.Radon.Digest.Coeffs, girl.ImageDigest.Radon.Digest.Coeffs, 0.8) So(crossCoorrelation, ShouldBeFalse) }) }) Convey("And suns do not look like cats", func() { PairExecuteFor2ImagesList(suns, cats, func(sun, cat *ImageBag) { crossCoorrelation, _ := radon.DiffByCrossCorr(sun.ImageDigest.Radon.Digest.Coeffs, cat.ImageDigest.Radon.Digest.Coeffs, 0.8) So(crossCoorrelation, ShouldBeFalse) }) }) Convey("And girls do not look like suns", func() { PairExecuteFor2ImagesList(girls, suns, func(girl, sun *ImageBag) { crossCoorrelation, _ := radon.DiffByCrossCorr(girl.ImageDigest.Radon.Digest.Coeffs, sun.ImageDigest.Radon.Digest.Coeffs, 0.8) So(crossCoorrelation, ShouldBeFalse) }) }) }) }) }
func TestAffineTransformedImagesMatch(t *testing.T) { Convey("Given some cats are loaded", t, func() { catBig := getImgBag(catsDir, "cat_big.jpg", 0) catMedium := getImgBag(catsDir, "cat_medium.jpg", 0) catSmall := getImgBag(catsDir, "cat_small.jpg", 0) /* Convey("When DCT Hashes are computed", func() { catBig.ImageDigest.ComputeGreyscaleDct() catMedium.ImageDigest.ComputeGreyscaleDct() catSmall.ImageDigest.ComputeGreyscaleDct() Convey("Then the hashes should not be zero", func() { So(catBig.Phash, ShouldNotEqual, 0) So(catMedium.Phash, ShouldNotEqual, 0) So(catSmall.Phash, ShouldNotEqual, 0) }) Convey("And Hamming Distance should be under threshold ", func() { distance := phash.HammingDistance(catBig.Phash, catMedium.Phash) So(distance, ShouldBeLessThanOrEqualTo, Treshold) distance = phash.HammingDistance(catBig.Phash, catSmall.Phash) So(distance, ShouldBeLessThanOrEqualTo, Treshold) distance = phash.HammingDistance(catMedium.Phash, catSmall.Phash) So(distance, ShouldBeLessThanOrEqualTo, Treshold) }) }) */ Convey("When DCTMatrix Hashes are computed", func() { catBig.ImageDigest.ComputeGreyscaleDctMatrix() catMedium.ImageDigest.ComputeGreyscaleDctMatrix() catSmall.ImageDigest.ComputeGreyscaleDctMatrix() Convey("Then the hashes should not be zero", func() { So(catBig.PhashMatrix, ShouldNotEqual, 0) So(catMedium.PhashMatrix, ShouldNotEqual, 0) So(catSmall.PhashMatrix, ShouldNotEqual, 0) }) Convey("Then Hamming Distance should be under threshold ", func() { distance := hammingDist(catBig, catMedium) So(distance, ShouldBeLessThanOrEqualTo, Treshold) distance = hammingDist(catBig, catSmall) So(distance, ShouldBeLessThanOrEqualTo, Treshold) distance = hammingDist(catMedium, catSmall) So(distance, ShouldBeLessThanOrEqualTo, Treshold) }) }) /* Convey("When CDCT Hashes are computed", func() { catBig.ComputeImageHashPhash(false) catMedium.ComputeImageHashPhash(false) catSmall.ComputeImageHashPhash(false) Convey("Then the hashes should not be zero", func() { So(catBig.CPhash, ShouldNotEqual, 0) So(catMedium.CPhash, ShouldNotEqual, 0) So(catSmall.CPhash, ShouldNotEqual, 0) }) Convey("Then Hamming Distance should be under threshold ", func() { distance := phash.HammingDistance(catBig.CPhash, catMedium.CPhash) So(distance, ShouldBeLessThanOrEqualTo, Treshold) distance = phash.HammingDistance(catBig.CPhash, catSmall.CPhash) So(distance, ShouldBeLessThanOrEqualTo, Treshold) distance = phash.HammingDistance(catMedium.CPhash, catSmall.CPhash) So(distance, ShouldBeLessThanOrEqualTo, Treshold) }) }) */ SkipConvey("When Radon Hashes are computed", func() { catBig.ComputeImageHashRadon(false) catMedium.ComputeImageHashRadon(false) Convey("Then the hashes should not be zero", func() { So(len(catBig.ImageDigest.Radon.Digest.Coeffs), ShouldNotEqual, 0) So(catBig.ImageDigest.Radon.Digest.Coeffs, ShouldNotBeNil) So(len(catMedium.ImageDigest.Radon.Digest.Coeffs), ShouldNotEqual, 0) So(catMedium.ImageDigest.Radon.Digest.Coeffs, ShouldNotBeNil) }) Convey("Then the Cross Correlation for threshold should be true", func() { crossCoorrelation, _ := radon.DiffByCrossCorr(catBig.ImageDigest.Radon.Digest.Coeffs, catMedium.ImageDigest.Radon.Digest.Coeffs, 0.01) So(crossCoorrelation, ShouldBeTrue) }) }) }) Convey("Given some suns are loaded", t, func() { sunBig := getImgBag(catsDir, "sun_big.jpg", 0) sunSmall := getImgBag(catsDir, "sun_small.jpg", 0) Convey("When DCT Hashes are computed", func() { sunBig.ImageDigest.ComputeGreyscaleDct() sunSmall.ImageDigest.ComputeGreyscaleDct() Convey("Then the hashes should not be zero", func() { So(sunBig.Phash, ShouldNotEqual, 0) So(sunSmall.Phash, ShouldNotEqual, 0) }) Convey("And Hamming Distance should be under threshold ", func() { distance := phash.HammingDistance(sunBig.Phash, sunSmall.Phash) So(distance, ShouldBeLessThanOrEqualTo, Treshold) }) }) Convey("When DCTMatrix Hashes are computed", func() { fmt.Println("checking sunBig") sunBig.ImageDigest.ComputeGreyscaleDctMatrix() fmt.Println("checking sunSmall") sunSmall.ImageDigest.ComputeGreyscaleDctMatrix() Convey("Then the hashes should not be zero", func() { So(sunBig.PhashMatrix, ShouldNotEqual, 0) So(sunSmall.PhashMatrix, ShouldNotEqual, 0) }) Convey("Then Hamming Distance should be under threshold ", func() { distance := hammingDist(sunBig, sunSmall) So(distance, ShouldBeLessThanOrEqualTo, Treshold) distance = hammingDist(sunBig, sunSmall) So(distance, ShouldBeLessThanOrEqualTo, Treshold) }) }) /* Convey("When CDCT Hashes are computed", func() { sunBig.ComputeImageHashPhash(false) sunSmall.ComputeImageHashPhash(false) Convey("Then the hashes should not be zero", func() { So(sunBig.CPhash, ShouldNotEqual, 0) So(sunSmall.CPhash, ShouldNotEqual, 0) }) Convey("Then Hamming Distance should be under threshold ", func() { distance := phash.HammingDistance(sunBig.CPhash, sunSmall.CPhash) So(distance, ShouldBeLessThanOrEqualTo, Treshold) distance = phash.HammingDistance(sunBig.CPhash, sunSmall.CPhash) So(distance, ShouldBeLessThanOrEqualTo, Treshold) }) })*/ Convey("When Radon Hashes are computed", func() { sunBig.ComputeImageHashRadon(false) sunSmall.ComputeImageHashRadon(false) Convey("Then the hashes should not be zero", func() { So(len(sunBig.ImageDigest.Radon.Digest.Coeffs), ShouldNotEqual, 0) So(sunBig.ImageDigest.Radon.Digest.Coeffs, ShouldNotBeNil) So(len(sunSmall.ImageDigest.Radon.Digest.Coeffs), ShouldNotEqual, 0) So(sunSmall.ImageDigest.Radon.Digest.Coeffs, ShouldNotBeNil) }) SkipConvey("Then the Cross Correlation for threshold should be true", func() { crossCoorrelation, _ := radon.DiffByCrossCorr(sunBig.ImageDigest.Radon.Digest.Coeffs, sunSmall.ImageDigest.Radon.Digest.Coeffs, -0.1) So(crossCoorrelation, ShouldBeTrue) crossCoorrelation, _ = radon.DiffByCrossCorr(sunBig.ImageDigest.Radon.Digest.Coeffs, sunSmall.ImageDigest.Radon.Digest.Coeffs, 0.0) So(crossCoorrelation, ShouldBeTrue) }) }) }) }
func hammingDist(a, b *ImageBag) uint64 { dist := phash.HammingDistance(a.PhashMatrix, b.PhashMatrix) fmt.Printf("dist between %s and %s is %d", a.Filename, b.Filename, dist) return dist }