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.
- Loading branch information
Showing
8 changed files
with
309 additions
and
7 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
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_ |
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,122 @@ | ||
package contrib | ||
|
||
import ( | ||
"errors" | ||
"testing" | ||
|
||
"gocv.io/x/gocv" | ||
) | ||
|
||
const ( | ||
testImage = "../images/space_shuttle.jpg" | ||
testImage2 = "../images/toy.jpg" | ||
) | ||
|
||
func compute(path string, hash ImgHashBase) (*gocv.Mat, error) { | ||
img := gocv.IMRead(path, gocv.IMReadColor) | ||
if img.Empty() { | ||
return nil, errors.New("Invalid input") | ||
} | ||
defer img.Close() | ||
|
||
dst := gocv.NewMat() | ||
hash.Compute(img, &dst) | ||
if dst.Empty() { | ||
dst.Close() | ||
return nil, errors.New("Empty output") | ||
} | ||
|
||
return &dst, nil | ||
} | ||
|
||
func testHash(t *testing.T, hash ImgHashBase) { | ||
result, err := compute(testImage, hash) | ||
if err != nil { | ||
t.Error(err) | ||
} | ||
defer result.Close() | ||
|
||
t.Logf("%T: %x", hash, result.ToBytes()) | ||
|
||
// Load second image and make sure it doesn't compare as identical | ||
result2, err := compute(testImage2, hash) | ||
if err != nil { | ||
t.Error(err) | ||
} | ||
defer result2.Close() | ||
|
||
similar := hash.Compare(*result, *result2) | ||
t.Logf("%T: similarity %g", hash, similar) | ||
// The range and meaning of this value varies between algorithms, and | ||
// there doesn't seem to be a well defined set of default thresholds, so | ||
// "anything but zero" is the minimum smoke test. | ||
if similar == 0 { | ||
t.Error("Image similarity is zero?") | ||
} | ||
} | ||
|
||
func TestHashes(t *testing.T) { | ||
t.Run("PHash", func(t *testing.T) { testHash(t, PHash{}) }) | ||
t.Run("AverageHash", func(t *testing.T) { testHash(t, AverageHash{}) }) | ||
t.Run("BlockMeanHash", func(t *testing.T) { testHash(t, BlockMeanHash{}) }) | ||
t.Run("ColorMomentHash", func(t *testing.T) { testHash(t, ColorMomentHash{}) }) | ||
t.Run("MarrHidlrethHash", func(t *testing.T) { testHash(t, NewMarrHildrethHash()) }) | ||
t.Run("RadialVarianceHash", func(t *testing.T) { testHash(t, NewRadialVarianceHash()) }) | ||
} | ||
|
||
func BenchmarkCompute(b *testing.B) { | ||
img := gocv.IMRead(testImage, gocv.IMReadColor) | ||
if img.Empty() { | ||
b.Error("Invalid input") | ||
} | ||
defer img.Close() | ||
b.ResetTimer() | ||
|
||
compute := func(b *testing.B, hash ImgHashBase) { | ||
for i := 0; i < b.N; i++ { | ||
dst := gocv.NewMat() | ||
hash.Compute(img, &dst) | ||
if dst.Empty() { | ||
b.Error("Empty output") | ||
dst.Close() | ||
return | ||
} | ||
dst.Close() | ||
} | ||
} | ||
|
||
b.Run("PHash", func(b *testing.B) { compute(b, PHash{}) }) | ||
b.Run("AverageHash", func(b *testing.B) { compute(b, AverageHash{}) }) | ||
b.Run("BlockMeanHash", func(b *testing.B) { compute(b, BlockMeanHash{}) }) | ||
b.Run("ColorMomentHash", func(b *testing.B) { compute(b, ColorMomentHash{}) }) | ||
b.Run("MarrHidlrethHash", func(b *testing.B) { compute(b, NewMarrHildrethHash()) }) | ||
b.Run("RadialVarianceHash", func(b *testing.B) { compute(b, NewRadialVarianceHash()) }) | ||
} | ||
|
||
func BenchmarkCompare(b *testing.B) { | ||
compare := func(b *testing.B, hash ImgHashBase) { | ||
result1, err := compute(testImage, hash) | ||
if err != nil { | ||
b.Error(err) | ||
} | ||
defer result1.Close() | ||
|
||
result2, err := compute(testImage2, hash) | ||
if err != nil { | ||
b.Error(err) | ||
} | ||
defer result2.Close() | ||
|
||
b.ResetTimer() | ||
for i := 0; i < b.N; i++ { | ||
hash.Compare(*result1, *result2) | ||
} | ||
} | ||
|
||
b.Run("PHash", func(b *testing.B) { compare(b, PHash{}) }) | ||
b.Run("AverageHash", func(b *testing.B) { compare(b, AverageHash{}) }) | ||
b.Run("BlockMeanHash", func(b *testing.B) { compare(b, BlockMeanHash{}) }) | ||
b.Run("ColorMomentHash", func(b *testing.B) { compare(b, ColorMomentHash{}) }) | ||
b.Run("MarrHidlrethHash", func(b *testing.B) { compare(b, NewMarrHildrethHash()) }) | ||
b.Run("RadialVarianceHash", func(b *testing.B) { compare(b, NewRadialVarianceHash()) }) | ||
} |
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