forked from hybridgroup/gocv
-
Notifications
You must be signed in to change notification settings - Fork 0
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
Merge pull request hybridgroup#167 from special/img_hash
Add contrib/img_hash module
- Loading branch information
Showing
11 changed files
with
449 additions
and
8 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,133 @@ | ||
// What it does: | ||
// | ||
// This example calculates perceptual hashes for a pair of images, | ||
// and prints the hashes and calculated similarity between them. | ||
// A variety of algorithms are supported. | ||
// | ||
// How to run: | ||
// | ||
// img-similarity [-flags] [image1.jpg] [image2.jpg] | ||
// | ||
// go run ./cmd/img-similarity/main.go -all images/space_shuttle.jpg images/toy.jpg | ||
// | ||
// +build example | ||
|
||
package main | ||
|
||
import ( | ||
"flag" | ||
"fmt" | ||
"strings" | ||
|
||
"gocv.io/x/gocv" | ||
"gocv.io/x/gocv/contrib" | ||
) | ||
|
||
var ( | ||
useAll = flag.Bool("all", false, "Compute all hashes") | ||
usePHash = flag.Bool("phash", false, "Compute PHash") | ||
useAverage = flag.Bool("average", false, "Compute AverageHash") | ||
useBlockMean0 = flag.Bool("blockmean0", false, "Compute BlockMeanHash mode 0") | ||
useBlockMean1 = flag.Bool("blockmean1", false, "Compute BlockMeanHash mode 1") | ||
useColorMoment = flag.Bool("colormoment", false, "Compute ColorMomentHash") | ||
useMarrHildreth = flag.Bool("marrhildreth", false, "Compute MarrHildrethHash") | ||
useRadialVariance = flag.Bool("radialvariance", false, "Compute RadialVarianceHash") | ||
) | ||
|
||
func setupHashes() []contrib.ImgHashBase { | ||
var hashes []contrib.ImgHashBase | ||
|
||
if *usePHash || *useAll { | ||
hashes = append(hashes, contrib.PHash{}) | ||
} | ||
if *useAverage || *useAll { | ||
hashes = append(hashes, contrib.AverageHash{}) | ||
} | ||
if *useBlockMean0 || *useAll { | ||
hashes = append(hashes, contrib.BlockMeanHash{}) | ||
} | ||
if *useBlockMean1 || *useAll { | ||
hashes = append(hashes, contrib.BlockMeanHash{Mode: contrib.BlockMeanHashMode1}) | ||
} | ||
if *useColorMoment || *useAll { | ||
hashes = append(hashes, contrib.ColorMomentHash{}) | ||
} | ||
if *useMarrHildreth || *useAll { | ||
// MarrHildreth has default parameters for alpha/scale | ||
hashes = append(hashes, contrib.NewMarrHildrethHash()) | ||
} | ||
if *useRadialVariance || *useAll { | ||
// RadialVariance has default parameters too | ||
hashes = append(hashes, contrib.NewRadialVarianceHash()) | ||
} | ||
|
||
// If no hashes were selected, behave as if all hashes were selected | ||
if len(hashes) == 0 { | ||
*useAll = true | ||
return setupHashes() | ||
} | ||
|
||
return hashes | ||
} | ||
|
||
func main() { | ||
flag.Usage = func() { | ||
fmt.Println("How to run:\n\timg-similarity [-flags] [image1.jpg] [image2.jpg]") | ||
flag.PrintDefaults() | ||
} | ||
|
||
printHashes := flag.Bool("print", false, "print hash values") | ||
flag.Parse() | ||
if flag.NArg() < 2 { | ||
flag.Usage() | ||
return | ||
} | ||
|
||
// read images | ||
inputs := flag.Args() | ||
images := make([]gocv.Mat, len(inputs)) | ||
|
||
for i := 0; i < 2; i++ { | ||
img := gocv.IMRead(inputs[i], gocv.IMReadColor) | ||
if img.Empty() { | ||
fmt.Printf("cannot read image %s\n", inputs[i]) | ||
return | ||
} | ||
defer img.Close() | ||
|
||
images[i] = img | ||
} | ||
|
||
// construct all of the hash types in a list. normally, you'd only use one of these. | ||
hashes := setupHashes() | ||
|
||
// compute and compare the images for each hash type | ||
for _, hash := range hashes { | ||
results := make([]gocv.Mat, len(images)) | ||
|
||
for i, img := range images { | ||
results[i] = gocv.NewMat() | ||
defer results[i].Close() | ||
hash.Compute(img, &results[i]) | ||
if results[i].Empty() { | ||
fmt.Printf("error computing hash for %s\n", inputs[i]) | ||
return | ||
} | ||
} | ||
|
||
// compare for similarity; this returns a float64, but the meaning of values is | ||
// unique to each algorithm. | ||
similar := hash.Compare(results[0], results[1]) | ||
|
||
// make a pretty name for the hash | ||
name := strings.TrimPrefix(fmt.Sprintf("%T", hash), "contrib.") | ||
fmt.Printf("%s: similarity %g\n", name, similar) | ||
|
||
if *printHashes { | ||
// print hash result for each image | ||
for i, path := range inputs { | ||
fmt.Printf("\t%s = %x\n", path, results[i].ToBytes()) | ||
} | ||
} | ||
} | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,43 @@ | ||
#include "img_hash.h" | ||
|
||
void pHashCompute(Mat inputArr, Mat outputArr) { | ||
cv::img_hash::pHash(*inputArr, *outputArr); | ||
} | ||
double pHashCompare(Mat a, Mat b) { | ||
return cv::img_hash::PHash::create()->compare(*a, *b); | ||
} | ||
|
||
void averageHashCompute(Mat inputArr, Mat outputArr) { | ||
cv::img_hash::averageHash(*inputArr, *outputArr); | ||
} | ||
double averageHashCompare(Mat a, Mat b) { | ||
return cv::img_hash::AverageHash::create()->compare(*a, *b); | ||
} | ||
|
||
void blockMeanHashCompute(Mat inputArr, Mat outputArr, int mode) { | ||
cv::img_hash::blockMeanHash(*inputArr, *outputArr, mode); | ||
} | ||
double blockMeanHashCompare(Mat a, Mat b, int mode) { | ||
return cv::img_hash::BlockMeanHash::create(mode)->compare(*a, *b); | ||
} | ||
|
||
void colorMomentHashCompute(Mat inputArr, Mat outputArr) { | ||
cv::img_hash::colorMomentHash(*inputArr, *outputArr); | ||
} | ||
double colorMomentHashCompare(Mat a, Mat b) { | ||
return cv::img_hash::ColorMomentHash::create()->compare(*a, *b); | ||
} | ||
|
||
void marrHildrethHashCompute(Mat inputArr, Mat outputArr, float alpha, float scale) { | ||
cv::img_hash::marrHildrethHash(*inputArr, *outputArr, alpha, scale); | ||
} | ||
double marrHildrethHashCompare(Mat a, Mat b, float alpha, float scale) { | ||
return cv::img_hash::MarrHildrethHash::create(alpha, scale)->compare(*a, *b); | ||
} | ||
|
||
void radialVarianceHashCompute(Mat inputArr, Mat outputArr, double sigma, int numOfAngleLine) { | ||
cv::img_hash::radialVarianceHash(*inputArr, *outputArr, sigma, numOfAngleLine); | ||
} | ||
double radialVarianceHashCompare(Mat a, Mat b, double sigma, int numOfAngleLine) { | ||
return cv::img_hash::RadialVarianceHash::create(sigma, numOfAngleLine)->compare(*a, *b); | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,108 @@ | ||
package contrib | ||
|
||
//#include <stdlib.h> | ||
//#include "img_hash.h" | ||
import "C" | ||
|
||
import ( | ||
"gocv.io/x/gocv" | ||
) | ||
|
||
type ImgHashBase interface { | ||
Compare(a, b gocv.Mat) float64 | ||
Compute(inputArr gocv.Mat, outputArr *gocv.Mat) | ||
} | ||
|
||
type PHash struct{} | ||
|
||
func (hash PHash) Compute(input gocv.Mat, output *gocv.Mat) { | ||
C.pHashCompute(C.Mat(input.Ptr()), C.Mat(output.Ptr())) | ||
} | ||
|
||
func (hash PHash) Compare(a, b gocv.Mat) float64 { | ||
return float64(C.pHashCompare(C.Mat(a.Ptr()), C.Mat(b.Ptr()))) | ||
} | ||
|
||
type AverageHash struct{} | ||
|
||
func (hash AverageHash) Compute(input gocv.Mat, output *gocv.Mat) { | ||
C.averageHashCompute(C.Mat(input.Ptr()), C.Mat(output.Ptr())) | ||
} | ||
|
||
func (hash AverageHash) Compare(a, b gocv.Mat) float64 { | ||
return float64(C.averageHashCompare(C.Mat(a.Ptr()), C.Mat(b.Ptr()))) | ||
} | ||
|
||
type BlockMeanHash struct { | ||
Mode BlockMeanHashMode | ||
} | ||
|
||
type BlockMeanHashMode int | ||
|
||
const ( | ||
BlockMeanHashMode0 BlockMeanHashMode = iota | ||
BlockMeanHashMode1 | ||
BlockMeanHashModeDefault = BlockMeanHashMode0 | ||
) | ||
|
||
func (hash BlockMeanHash) Compute(input gocv.Mat, output *gocv.Mat) { | ||
C.blockMeanHashCompute(C.Mat(input.Ptr()), C.Mat(output.Ptr()), C.int(hash.Mode)) | ||
} | ||
|
||
func (hash BlockMeanHash) Compare(a, b gocv.Mat) float64 { | ||
return float64(C.blockMeanHashCompare(C.Mat(a.Ptr()), C.Mat(b.Ptr()), C.int(hash.Mode))) | ||
} | ||
|
||
// TODO: BlockMeanHash.GetMean isn't implemented, because it requires state from the last | ||
// call to Compute, and there's no easy way to keep it. | ||
|
||
type ColorMomentHash struct{} | ||
|
||
func (hash ColorMomentHash) Compute(input gocv.Mat, output *gocv.Mat) { | ||
C.colorMomentHashCompute(C.Mat(input.Ptr()), C.Mat(output.Ptr())) | ||
} | ||
|
||
func (hash ColorMomentHash) Compare(a, b gocv.Mat) float64 { | ||
return float64(C.colorMomentHashCompare(C.Mat(a.Ptr()), C.Mat(b.Ptr()))) | ||
} | ||
|
||
type MarrHildrethHash struct { | ||
Alpha float32 | ||
Scale float32 | ||
} | ||
|
||
func NewMarrHildrethHash() MarrHildrethHash { | ||
return MarrHildrethHash{2.0, 1.0} | ||
} | ||
|
||
func (hash MarrHildrethHash) Compute(input gocv.Mat, output *gocv.Mat) { | ||
C.marrHildrethHashCompute(C.Mat(input.Ptr()), C.Mat(output.Ptr()), | ||
C.float(hash.Alpha), C.float(hash.Scale)) | ||
} | ||
|
||
func (hash MarrHildrethHash) Compare(a, b gocv.Mat) float64 { | ||
return float64(C.marrHildrethHashCompare(C.Mat(a.Ptr()), C.Mat(b.Ptr()), | ||
C.float(hash.Alpha), C.float(hash.Scale))) | ||
} | ||
|
||
type RadialVarianceHash struct { | ||
Sigma float64 | ||
NumOfAngleLine int | ||
} | ||
|
||
func NewRadialVarianceHash() RadialVarianceHash { | ||
return RadialVarianceHash{1, 180} | ||
} | ||
|
||
func (hash RadialVarianceHash) Compute(input gocv.Mat, output *gocv.Mat) { | ||
C.radialVarianceHashCompute(C.Mat(input.Ptr()), C.Mat(output.Ptr()), | ||
C.double(hash.Sigma), C.int(hash.NumOfAngleLine)) | ||
} | ||
|
||
func (hash RadialVarianceHash) Compare(a, b gocv.Mat) float64 { | ||
return float64(C.radialVarianceHashCompare(C.Mat(a.Ptr()), C.Mat(b.Ptr()), | ||
C.double(hash.Sigma), C.int(hash.NumOfAngleLine))) | ||
} | ||
|
||
// TODO: RadialVariance getFeatures, getHash, getPixPerLine, getProjection are not | ||
// implemented here, because they're stateful. |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,29 @@ | ||
#ifndef _OPENCV3_IMG_HASH_H_ | ||
#define _OPENCV3_IMG_HASH_H_ | ||
|
||
#ifdef __cplusplus | ||
#include <opencv2/opencv.hpp> | ||
#include <opencv2/img_hash.hpp> | ||
extern "C" { | ||
#endif | ||
|
||
#include "../core.h" | ||
|
||
void pHashCompute(Mat inputArr, Mat outputArr); | ||
double pHashCompare(Mat a, Mat b); | ||
void averageHashCompute(Mat inputArr, Mat outputArr); | ||
double averageHashCompare(Mat a, Mat b); | ||
void blockMeanHashCompute(Mat inputArr, Mat outputArr, int mode); | ||
double blockMeanHashCompare(Mat a, Mat b, int mode); | ||
void colorMomentHashCompute(Mat inputArr, Mat outputArr); | ||
double colorMomentHashCompare(Mat a, Mat b); | ||
void marrHildrethHashCompute(Mat inputArr, Mat outputArr, float alpha, float scale); | ||
double marrHildrethHashCompare(Mat a, Mat b, float alpha, float scale); | ||
void radialVarianceHashCompute(Mat inputArr, Mat outputArr, double sigma, int numOfAngleLine); | ||
double radialVarianceHashCompare(Mat a, Mat b, double sigma, int numOfAngleLine); | ||
|
||
#ifdef __cplusplus | ||
} | ||
#endif | ||
|
||
#endif //_OPENCV3_IMG_HASH_H_ |
Oops, something went wrong.