Skip to content

Commit

Permalink
Merge pull request hybridgroup#167 from special/img_hash
Browse files Browse the repository at this point in the history
Add contrib/img_hash module
  • Loading branch information
deadprogram authored Apr 12, 2018
2 parents 3fd1b7f + 2f688bf commit 218ebfe
Show file tree
Hide file tree
Showing 11 changed files with 449 additions and 8 deletions.
2 changes: 1 addition & 1 deletion .travis.yml
Original file line number Diff line number Diff line change
Expand Up @@ -41,7 +41,7 @@ before_cache:

script:
- export CGO_CPPFLAGS="-I${HOME}/usr/include"
- export CGO_LDFLAGS="-L${HOME}/usr/lib -lopencv_core -lopencv_videoio -lopencv_face -lopencv_imgproc -lopencv_highgui -lopencv_imgcodecs -lopencv_objdetect -lopencv_features2d -lopencv_video -lopencv_dnn -lopencv_xfeatures2d -lopencv_plot -lopencv_tracking"
- export CGO_LDFLAGS="-L${HOME}/usr/lib -lopencv_core -lopencv_videoio -lopencv_face -lopencv_imgproc -lopencv_highgui -lopencv_imgcodecs -lopencv_objdetect -lopencv_features2d -lopencv_video -lopencv_dnn -lopencv_xfeatures2d -lopencv_plot -lopencv_tracking -lopencv_img_hash"
- export GOCV_CAFFE_TEST_FILES="${HOME}/testdata"
- export GOCV_TENSORFLOW_TEST_FILES="${HOME}/testdata"
- echo "Ensuring code is well formatted"; ! gofmt -s -d . | read
Expand Down
2 changes: 1 addition & 1 deletion Makefile
Original file line number Diff line number Diff line change
Expand Up @@ -61,7 +61,7 @@ astyle:

install: download build clean

CMDS=basic-drawing caffe-classifier captest capwindow counter faceblur facedetect find-circles hand-gestures mjpeg-streamer motion-detect pose saveimage savevideo showimage ssd-facedetect tf-classifier tracking version
CMDS=basic-drawing caffe-classifier captest capwindow counter faceblur facedetect find-circles hand-gestures img-similarity mjpeg-streamer motion-detect pose saveimage savevideo showimage ssd-facedetect tf-classifier tracking version
cmds:
for cmd in $(CMDS) ; do \
go build -o build/$$cmd cmd/$$cmd/main.go ;
Expand Down
2 changes: 1 addition & 1 deletion appveyor.yml
Original file line number Diff line number Diff line change
Expand Up @@ -26,7 +26,7 @@ install:
- cd c:\gopath\src\gocv.io\x\gocv
- go get -d .
- set CGO_CPPFLAGS=-IC:\opencv\build\install\include
- set CGO_LDFLAGS=-LC:\opencv\build\install\x64\mingw\lib -lopencv_core341 -lopencv_face341 -lopencv_videoio341 -lopencv_imgproc341 -lopencv_highgui341 -lopencv_imgcodecs341 -lopencv_objdetect341 -lopencv_features2d341 -lopencv_video341 -lopencv_dnn341 -lopencv_xfeatures2d341 -lopencv_plot341 -lopencv_tracking341
- set CGO_LDFLAGS=-LC:\opencv\build\install\x64\mingw\lib -lopencv_core341 -lopencv_face341 -lopencv_videoio341 -lopencv_imgproc341 -lopencv_highgui341 -lopencv_imgcodecs341 -lopencv_objdetect341 -lopencv_features2d341 -lopencv_video341 -lopencv_dnn341 -lopencv_xfeatures2d341 -lopencv_plot341 -lopencv_tracking341 -lopencv_img_hash341
- set GOCV_CAFFE_TEST_FILES=C:\opencv\testdata
- set GOCV_TENSORFLOW_TEST_FILES=C:\opencv\testdata
- go env
Expand Down
6 changes: 6 additions & 0 deletions cmd/README.md
Original file line number Diff line number Diff line change
Expand Up @@ -50,6 +50,12 @@ Count the number of fingers being held up in front of the camera by looking for

https://github.com/hybridgroup/gocv/blob/master/cmd/hand-gestures/main.go

## Img-similarity

Compute and compare perceptual hashes for a pair of images, with a variety of algorithms.

https://github.com/hybridgroup/gocv/blob/master/cmd/img-similarity/main.go

## MJPEG-Streamer

Opens a video capture device, then streams MJPEG from it that you can view in any browser.
Expand Down
133 changes: 133 additions & 0 deletions cmd/img-similarity/main.go
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())
}
}
}
}
43 changes: 43 additions & 0 deletions contrib/img_hash.cpp
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);
}
108 changes: 108 additions & 0 deletions contrib/img_hash.go
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.
29 changes: 29 additions & 0 deletions contrib/img_hash.h
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_
Loading

0 comments on commit 218ebfe

Please sign in to comment.