Implement image hash modules (#688)
* first commit * first commit * adjust code layout * round mean value * add missed header * remove useless header * remove useless header * first commit * first commit * first commit * Encapsule function averageHash by class * remove export macro * encapsulate phash algo by class * first commit * fix bugs of createHash and fillBlcoks * 1 : add create function 2 : add overload functions * implement get set functions * fix bug--destination depth should be CV_32F * first commit * first commit * 1 : fix bug--forgot '"' 2 : forgot to include iostream * fix warnings * remove tab * remove trailing white space * remove trailing white space * remove trailing white space * remove trailing white space * remove trailing white space * remove trailing white space * first commit * remove trailing white space * remove trailing white space * remove trailing white space * reduce redundance operation * add explanation of img_hash * remove useless comments * remove trailing space * first commit * fix missed symbol * add new defgroup and change all defgroup to ihash * fix namespace confliction * change namespace from ihash to img_hash * change ihash to img_hash * change include guard * forbid implicit conversion * first commit * 1 : declare function findFeatureVector 2 : forward declare test class--RavialVarHashTester as friend * first commit * replace auto with explicit type * export some symbols, for initialization and testing * remove trailing white space * add namespace cv * fix type cast warning and define default constrcutor/destructor * declare and define RadialVarHashTester in namespace * remove default constructor/destructor * exports functions findFeatures and destructor * remove trailing white space * fix bug--wrong definition of destructor * remove trailing white space * implement findFeatureVector * add test case for findFeatureVector * 1 : fix bug--forgot to allocate space for input 2 : fix bug--compare the results of pixPerLine with wrong matrix * remove trailing space * implement hashCalculate * add test case for hashCalculate * remove trailing white space * avoid hiding parameter * refine codes and keep the original range * adjust expected hash value since the range of hash change * add comment * reduce scope * remove trailing white space * adjust format * add new function compare * implement compute functions * use array as buffer of cv::Mat, avoid memory allocation * remove trailing whitespace * 1 : implement cross-correlation rather than using matchTemplate since the results of matchTemplate are weird 2 : remove gamma param, although the paper said PHash apply gamma correction on the image, but the codes do not do that(I think it is a bug of PHash) 3 : create function can specify sigma and numOfAngleLine 4 : Use blurImg to replace normalizeImg 5 : remove useless parameters which related to gamma correction * add example of radial variance hash * use buffer to avoid memory allocation and use enum to specify hash size * remove useless header * fix bug--constructor only accept two params * add comments * transpose projection matrix, friendlier for cache hit * use pointer to access projection value * function able to specify sigma and numOfAngleLine * add get/set functions * implement image hash algo--block mean hash * include block mean hash and add comments * remove trailing whitespace * fix warning--compare with sign and unsigned value * implement destructor and change mode type to size_t * add example of block mean hash * compress the bits of hash * function--blockMeanHash able to set mode * fix type cast warning and style * change expected result to bool * compress the hash value from 16 bytes to 8 bytes * update comments * compress hash from 16 bytes to 8 bytes * use peak value of cross correlation as comparison result * add limit header * first commit * add group and header file of color moment hash * should not use auto, it is c++11 feature * support python binding * implement destructor of AverageHash * support python binding * support python binding * support python binding * change type to inputArray and outputArray, easier for python binding * all algorithms support input type CV_8UC4,CV_8UC3 and CV_8UC1 * Provide better instructions * Make it more pleasant to read * Add information of speed comparsion * remove trailing white space * remove useless blank line * refine comments of example * fix link error * refine title * 1 : implement function "getDefaultName" 2 : adjust style * Update README.md 1 : Fix wrong branch 2 : Add another solution to download img_hash * img_hash: refactored interfaces to use pImpl * remove trailing white space * img_hash: use type-safe pImpl * change name, easier to find the source file * 1 : narrow scope of ImgHashImpl 2 : use static cast to replace dynamic cast because class hierarchy of img_hash is very straightforward * should not declare ImgHashImpl api in the header file of ImgHashBase, this increase chance of breaking ABI * should not declare ImgHashImpl api in the header file of ImgHashBase, this increase chance of breaking ABI * fix warning, because unelaborated friend declaration is a C++11 extension * first commit * fix bug, except of windows, other platforms cannot access private member. pimpl only accessable by the class itself, therefore I think declare everything as public is quite safe * first commit comparison and computation charts * update chart linkpull/1247/head
parent
8ef2f71799
commit
a4a8b84e93
30 changed files with 2139 additions and 0 deletions
@ -0,0 +1,3 @@ |
||||
set(the_description "Image hash algorithms") |
||||
set(OPENCV_MODULE_IS_PART_OF_WORLD OFF) |
||||
ocv_define_module(img_hash opencv_imgproc opencv_core WRAP java python) |
@ -0,0 +1,5 @@ |
||||
Image Hashing algorithms |
||||
======================== |
||||
|
||||
This module is intended to port the algorithms from PHash library and implement other image hash |
||||
algorithm do not exist in PHash library yet. |
After Width: | Height: | Size: 110 KiB |
After Width: | Height: | Size: 44 KiB |
After Width: | Height: | Size: 46 KiB |
@ -0,0 +1,23 @@ |
||||
@misc{lookslikeit, |
||||
author={Krawetz, Neal}, |
||||
title={Looks Like It}, |
||||
url={http://www.hackerfactor.com/blog/?/archives/432-Looks-Like-It.html} |
||||
} |
||||
|
||||
@article{tang2012perceptual, |
||||
title={Perceptual hashing for color images using invariant moments}, |
||||
author={Tang, Zhenjun and Dai, Yumin and Zhang, Xianquan}, |
||||
journal={Appl. Math}, |
||||
volume={6}, |
||||
number={2S}, |
||||
pages={643S--650S}, |
||||
year={2012}, |
||||
url={http://www.phash.org/docs/pubs/thesis_zauner.pdf} |
||||
} |
||||
|
||||
@article{zauner2010implementation, |
||||
title={Implementation and benchmarking of perceptual image hash functions}, |
||||
author={Zauner, Christoph}, |
||||
year={2010}, |
||||
publisher={na} |
||||
} |
@ -0,0 +1,78 @@ |
||||
// This file is part of OpenCV project.
|
||||
// It is subject to the license terms in the LICENSE file found in the top-level directory
|
||||
// of this distribution and at http://opencv.org/license.html.
|
||||
|
||||
#ifndef OPENCV_IMG_HASH_H |
||||
#define OPENCV_IMG_HASH_H |
||||
|
||||
#include "opencv2/img_hash/average_hash.hpp" |
||||
#include "opencv2/img_hash/block_mean_hash.hpp" |
||||
#include "opencv2/img_hash/color_moment_hash.hpp" |
||||
#include "opencv2/img_hash/marr_hildreth_hash.hpp" |
||||
#include "opencv2/img_hash/phash.hpp" |
||||
#include "opencv2/img_hash/radial_variance_hash.hpp" |
||||
|
||||
/**
|
||||
@defgroup img_hash The module brings implementations of different image hashing algorithms. |
||||
|
||||
Provide algorithms to extract the hash of images and fast way to figure out most similar images in |
||||
huge data set. |
||||
|
||||
Namespace for all functions is cv::img_hash. |
||||
|
||||
### Supported Algorithms |
||||
|
||||
- Average hash (also called Different hash) |
||||
- PHash (also called Perceptual hash) |
||||
- Marr Hildreth Hash |
||||
- Radial Variance Hash |
||||
- Block Mean Hash (modes 0 and 1) |
||||
- Color Moment Hash (this is the one and only hash algorithm resist to rotation attack(-90~90 degree)) |
||||
|
||||
You can study more about image hashing from following paper and websites: |
||||
|
||||
- "Implementation and benchmarking of perceptual image hash functions" @cite zauner2010implementation |
||||
- "Looks Like It" @cite lookslikeit |
||||
|
||||
### Code Example |
||||
|
||||
@include samples/hash_samples.cpp |
||||
|
||||
### Performance under different attacks |
||||
|
||||
 |
||||
|
||||
### Speed comparison with PHash library (100 images from ukbench) |
||||
|
||||
 |
||||
 |
||||
|
||||
As you can see, hash computation speed of img_hash module outperform [PHash library](http://www.phash.org/) a lot.
|
||||
|
||||
PS : I do not list out the comparison of Average hash, PHash and Color Moment hash, because I cannot |
||||
find them in PHash. |
||||
|
||||
### Motivation |
||||
|
||||
Collects useful image hash algorithms into opencv, so we do not need to rewrite them by ourselves |
||||
again and again or rely on another 3rd party library(ex : PHash library). BOVW or correlation |
||||
matching are good and robust, but they are very slow compare with image hash, if you need to deal |
||||
with large scale CBIR(content based image retrieval) problem, image hash is a more reasonable |
||||
solution. |
||||
|
||||
### More info |
||||
|
||||
You can learn more about img_hash modules from following links, these links show you how to find |
||||
similar image from ukbench dataset, provide thorough benchmark of different attacks(contrast, blur, |
||||
noise(gaussion,pepper and salt), jpeg compression, watermark, resize). |
||||
|
||||
* [Introduction to image hash module of opencv](http://qtandopencv.blogspot.my/2016/06/introduction-to-image-hash-module-of.html)
|
||||
* [Speed up image hashing of opencv(img_hash) and introduce color moment hash](http://qtandopencv.blogspot.my/2016/06/speed-up-image-hashing-of-opencvimghash.html)
|
||||
|
||||
### Contributors |
||||
|
||||
Tham Ngap Wei, thamngapwei@gmail.com |
||||
|
||||
*/ |
||||
|
||||
#endif // OPENCV_IMG_HASH_H
|
@ -0,0 +1,39 @@ |
||||
// This file is part of OpenCV project.
|
||||
// It is subject to the license terms in the LICENSE file found in the top-level directory
|
||||
// of this distribution and at http://opencv.org/license.html.
|
||||
|
||||
#ifndef OPENCV_AVERAGE_HASH_HPP |
||||
#define OPENCV_AVERAGE_HASH_HPP |
||||
|
||||
#include "img_hash_base.hpp" |
||||
|
||||
namespace cv { |
||||
namespace img_hash { |
||||
|
||||
//! @addtogroup img_hash
|
||||
//! @{
|
||||
|
||||
/** @brief Computes average hash value of the input image
|
||||
|
||||
This is a fast image hashing algorithm, but only work on simple case. For more details, please |
||||
refer to @cite lookslikeit |
||||
*/ |
||||
class CV_EXPORTS_W AverageHash : public ImgHashBase |
||||
{ |
||||
public: |
||||
CV_WRAP static Ptr<AverageHash> create(); |
||||
protected: |
||||
AverageHash() {} |
||||
}; |
||||
|
||||
/** @brief Calculates img_hash::AverageHash in one call
|
||||
@param inputArr input image want to compute hash value, type should be CV_8UC4, CV_8UC3 or CV_8UC1. |
||||
@param outputArr Hash value of input, it will contain 16 hex decimal number, return type is CV_8U |
||||
*/ |
||||
CV_EXPORTS_W void averageHash(cv::InputArray inputArr, cv::OutputArray outputArr); |
||||
|
||||
//! @}
|
||||
|
||||
}} // cv::img_hash::
|
||||
|
||||
#endif // OPENCV_AVERAGE_HASH_HPP
|
@ -0,0 +1,52 @@ |
||||
// This file is part of OpenCV project.
|
||||
// It is subject to the license terms in the LICENSE file found in the top-level directory
|
||||
// of this distribution and at http://opencv.org/license.html.
|
||||
|
||||
#ifndef OPENCV_BLOCK_MEAN_HASH_HPP |
||||
#define OPENCV_BLOCK_MEAN_HASH_HPP |
||||
|
||||
#include "img_hash_base.hpp" |
||||
|
||||
namespace cv { |
||||
namespace img_hash { |
||||
|
||||
//! @addtogroup img_hash
|
||||
//! @{
|
||||
|
||||
enum BlockMeanHashMode |
||||
{ |
||||
BLOCK_MEAN_HASH_MODE_0 = 0, //!< use fewer block and generate 16*16/8 uchar hash value
|
||||
BLOCK_MEAN_HASH_MODE_1 = 1, //!< use block blocks(step sizes/2), generate 31*31/8 + 1 uchar hash value
|
||||
}; |
||||
|
||||
/** @brief Image hash based on block mean.
|
||||
|
||||
See @cite zauner2010implementation for details. |
||||
*/ |
||||
class CV_EXPORTS_W BlockMeanHash : public ImgHashBase |
||||
{ |
||||
public: |
||||
/** @brief Create BlockMeanHash object
|
||||
@param mode |
||||
*/ |
||||
CV_WRAP void setMode(int mode); |
||||
CV_WRAP std::vector<double> getMean() const; |
||||
CV_WRAP static Ptr<BlockMeanHash> create(int mode = BLOCK_MEAN_HASH_MODE_0); |
||||
protected: |
||||
BlockMeanHash() {} |
||||
}; |
||||
|
||||
/** @brief Computes block mean hash of the input image
|
||||
@param inputArr input image want to compute hash value, type should be CV_8UC4, CV_8UC3 or CV_8UC1. |
||||
@param outputArr Hash value of input, it will contain 16 hex decimal number, return type is CV_8U |
||||
@param mode |
||||
*/ |
||||
CV_EXPORTS_W void blockMeanHash(cv::InputArray inputArr, |
||||
cv::OutputArray outputArr, |
||||
int mode = BLOCK_MEAN_HASH_MODE_0); |
||||
|
||||
//! @}
|
||||
|
||||
}} // cv::img_hash::
|
||||
|
||||
#endif // OPENCV_BLOCK_MEAN_HASH_HPP
|
@ -0,0 +1,41 @@ |
||||
// This file is part of OpenCV project.
|
||||
// It is subject to the license terms in the LICENSE file found in the top-level directory
|
||||
// of this distribution and at http://opencv.org/license.html.
|
||||
|
||||
#ifndef OPENCV_COLOR_MOMENT_HASH_HPP |
||||
#define OPENCV_COLOR_MOMENT_HASH_HPP |
||||
|
||||
#include "img_hash_base.hpp" |
||||
|
||||
namespace cv { |
||||
namespace img_hash { |
||||
|
||||
//! @addtogroup img_hash
|
||||
//! @{
|
||||
|
||||
/** @brief Image hash based on color moments.
|
||||
|
||||
See @cite tang2012perceptual for details. |
||||
*/ |
||||
class CV_EXPORTS_W ColorMomentHash : public ImgHashBase |
||||
{ |
||||
public: |
||||
CV_WRAP static Ptr<ColorMomentHash> create(); |
||||
protected: |
||||
ColorMomentHash() {} |
||||
}; |
||||
|
||||
/** @brief Computes color moment hash of the input, the algorithm
|
||||
is come from the paper "Perceptual Hashing for Color Images |
||||
Using Invariant Moments" |
||||
@param inputArr input image want to compute hash value, |
||||
type should be CV_8UC4, CV_8UC3 or CV_8UC1. |
||||
@param outputArr 42 hash values with type CV_64F(double) |
||||
*/ |
||||
CV_EXPORTS_W void colorMomentHash(cv::InputArray inputArr, cv::OutputArray outputArr); |
||||
|
||||
//! @}
|
||||
|
||||
}} // cv::img_hash::
|
||||
|
||||
#endif // OPENCV_COLOR_MOMENT_HASH_HPP
|
@ -0,0 +1,46 @@ |
||||
// This file is part of OpenCV project.
|
||||
// It is subject to the license terms in the LICENSE file found in the top-level directory
|
||||
// of this distribution and at http://opencv.org/license.html.
|
||||
|
||||
#ifndef OPENCV_IMG_HASH_BASE_HPP |
||||
#define OPENCV_IMG_HASH_BASE_HPP |
||||
|
||||
#include "opencv2/core.hpp" |
||||
|
||||
namespace cv { |
||||
namespace img_hash { |
||||
|
||||
//! @addtogroup img_hash
|
||||
//! @{
|
||||
|
||||
/** @brief The base class for image hash algorithms
|
||||
*/ |
||||
class CV_EXPORTS_W ImgHashBase : public Algorithm |
||||
{ |
||||
public: |
||||
class ImgHashImpl; |
||||
|
||||
~ImgHashBase(); |
||||
/** @brief Computes hash of the input image
|
||||
@param inputArr input image want to compute hash value |
||||
@param outputArr hash of the image |
||||
*/ |
||||
CV_WRAP void compute(cv::InputArray inputArr, cv::OutputArray outputArr); |
||||
/** @brief Compare the hash value between inOne and inTwo
|
||||
@param hashOne Hash value one |
||||
@param hashTwo Hash value two |
||||
@return value indicate similarity between inOne and inTwo, the meaning |
||||
of the value vary from algorithms to algorithms |
||||
*/ |
||||
CV_WRAP double compare(cv::InputArray hashOne, cv::InputArray hashTwo) const; |
||||
protected: |
||||
ImgHashBase(); |
||||
protected: |
||||
Ptr<ImgHashImpl> pImpl; |
||||
}; |
||||
|
||||
//! @}
|
||||
|
||||
} } // cv::img_hash::
|
||||
|
||||
#endif // OPENCV_IMG_HASH_BASE_HPP
|
@ -0,0 +1,64 @@ |
||||
// This file is part of OpenCV project.
|
||||
// It is subject to the license terms in the LICENSE file found in the top-level directory
|
||||
// of this distribution and at http://opencv.org/license.html.
|
||||
|
||||
#ifndef OPENCV_MARR_HILDRETH_HASH_HPP |
||||
#define OPENCV_MARR_HILDRETH_HASH_HPP |
||||
|
||||
#include "img_hash_base.hpp" |
||||
|
||||
namespace cv { |
||||
namespace img_hash { |
||||
|
||||
//! @addtogroup img_hash
|
||||
//! @{
|
||||
|
||||
/** @brief Marr-Hildreth Operator Based Hash, slowest but more discriminative.
|
||||
|
||||
See @cite zauner2010implementation for details. |
||||
*/ |
||||
class CV_EXPORTS_W MarrHildrethHash : public ImgHashBase |
||||
{ |
||||
public: |
||||
/**
|
||||
* @brief self explain |
||||
*/ |
||||
CV_WRAP float getAlpha() const; |
||||
|
||||
/**
|
||||
* @brief self explain |
||||
*/ |
||||
CV_WRAP float getScale() const; |
||||
|
||||
/** @brief Set Mh kernel parameters
|
||||
@param alpha int scale factor for marr wavelet (default=2). |
||||
@param scale int level of scale factor (default = 1) |
||||
*/ |
||||
CV_WRAP void setKernelParam(float alpha, float scale); |
||||
|
||||
/**
|
||||
@param alpha int scale factor for marr wavelet (default=2). |
||||
@param scale int level of scale factor (default = 1) |
||||
*/ |
||||
CV_WRAP static Ptr<MarrHildrethHash> create(float alpha = 2.0f, float scale = 1.0f); |
||||
protected: |
||||
MarrHildrethHash() {} |
||||
}; |
||||
|
||||
/** @brief Computes average hash value of the input image
|
||||
@param inputArr input image want to compute hash value, |
||||
type should be CV_8UC4, CV_8UC3, CV_8UC1. |
||||
@param outputArr Hash value of input, it will contain 16 hex |
||||
decimal number, return type is CV_8U |
||||
@param alpha int scale factor for marr wavelet (default=2). |
||||
@param scale int level of scale factor (default = 1) |
||||
*/ |
||||
CV_EXPORTS_W void marrHildrethHash(cv::InputArray inputArr, |
||||
cv::OutputArray outputArr, |
||||
float alpha = 2.0f, float scale = 1.0f); |
||||
|
||||
//! @}
|
||||
|
||||
}} // cv::img_hash::
|
||||
|
||||
#endif // OPENCV_MARR_HILDRETH_HASH_HPP
|
@ -0,0 +1,41 @@ |
||||
// This file is part of OpenCV project.
|
||||
// It is subject to the license terms in the LICENSE file found in the top-level directory
|
||||
// of this distribution and at http://opencv.org/license.html.
|
||||
|
||||
#ifndef OPENCV_PHASH_HPP |
||||
#define OPENCV_PHASH_HPP |
||||
|
||||
#include "img_hash_base.hpp" |
||||
|
||||
namespace cv { |
||||
namespace img_hash { |
||||
|
||||
//! @addtogroup img_hash
|
||||
//! @{
|
||||
|
||||
/** @brief pHash
|
||||
|
||||
Slower than average_hash, but tolerant of minor modifications |
||||
|
||||
This algorithm can combat more variation than averageHash, for more details please refer to @cite lookslikeit |
||||
*/ |
||||
class CV_EXPORTS_W PHash : public ImgHashBase |
||||
{ |
||||
public: |
||||
CV_WRAP static Ptr<PHash> create(); |
||||
protected: |
||||
PHash() {} |
||||
}; |
||||
|
||||
/** @brief Computes pHash value of the input image
|
||||
@param inputArr input image want to compute hash value, |
||||
type should be CV_8UC4, CV_8UC3, CV_8UC1. |
||||
@param outputArr Hash value of input, it will contain 8 uchar value |
||||
*/ |
||||
CV_EXPORTS_W void pHash(cv::InputArray inputArr, cv::OutputArray outputArr); |
||||
|
||||
//! @}
|
||||
|
||||
} } // cv::img_hash::
|
||||
|
||||
#endif // OPENCV_PHASH_HPP
|
@ -0,0 +1,58 @@ |
||||
// This file is part of OpenCV project.
|
||||
// It is subject to the license terms in the LICENSE file found in the top-level directory
|
||||
// of this distribution and at http://opencv.org/license.html.
|
||||
|
||||
#ifndef OPENCV_RADIAL_VARIANCE_HASH_HPP |
||||
#define OPENCV_RADIAL_VARIANCE_HASH_HPP |
||||
|
||||
#include "img_hash_base.hpp" |
||||
|
||||
namespace cv { |
||||
namespace img_hash { |
||||
|
||||
//! @addtogroup img_hash
|
||||
//! @{
|
||||
|
||||
|
||||
/** @brief Image hash based on Radon transform.
|
||||
|
||||
See @cite tang2012perceptual for details. |
||||
*/ |
||||
class CV_EXPORTS_W RadialVarianceHash : public ImgHashBase |
||||
{ |
||||
public: |
||||
CV_WRAP static Ptr<RadialVarianceHash> create(double sigma = 1, int numOfAngleLine = 180); |
||||
|
||||
CV_WRAP int getNumOfAngleLine() const; |
||||
CV_WRAP double getSigma() const; |
||||
|
||||
CV_WRAP void setNumOfAngleLine(int value); |
||||
CV_WRAP void setSigma(double value); |
||||
|
||||
// internals
|
||||
std::vector<double> getFeatures(); |
||||
cv::Mat getHash(); |
||||
Mat getPixPerLine(Mat const &input); |
||||
Mat getProjection(); |
||||
protected: |
||||
RadialVarianceHash() {} |
||||
}; |
||||
|
||||
/** @brief Computes radial variance hash of the input image
|
||||
@param inputArr input image want to compute hash value, |
||||
type should be CV_8UC4, CV_8UC3, CV_8UC1. |
||||
@param outputArr Hash value of input |
||||
@param sigma Gaussian kernel standard deviation |
||||
@param numOfAngleLine The number of angles to consider |
||||
*/ |
||||
CV_EXPORTS_W void radialVarianceHash(cv::InputArray inputArr, |
||||
cv::OutputArray outputArr, |
||||
double sigma = 1, |
||||
int numOfAngleLine = 180); |
||||
|
||||
|
||||
//! @}
|
||||
|
||||
}} // cv::img_hash::
|
||||
|
||||
#endif // OPENCV_RADIAL_VARIANCE_HASH_HPP
|
@ -0,0 +1,53 @@ |
||||
#include "opencv2/core.hpp" |
||||
#include "opencv2/core/ocl.hpp" |
||||
#include "opencv2/highgui.hpp" |
||||
#include "opencv2/img_hash.hpp" |
||||
|
||||
#include <iostream> |
||||
|
||||
using namespace cv; |
||||
using namespace cv::img_hash; |
||||
using namespace std; |
||||
|
||||
template <typename T> |
||||
inline void test_one(const std::string &title, const Mat &a, const Mat &b) |
||||
{ |
||||
cout << "=== " << title << " ===" << endl; |
||||
TickMeter tick; |
||||
Mat hashA, hashB; |
||||
Ptr<ImgHashBase> func; |
||||
func = T::create(); |
||||
|
||||
tick.reset(); tick.start(); |
||||
func->compute(a, hashA); |
||||
tick.stop(); |
||||
cout << "compute1: " << tick.getTimeMilli() << " ms" << endl; |
||||
|
||||
tick.reset(); tick.start(); |
||||
func->compute(b, hashB); |
||||
tick.stop(); |
||||
cout << "compute2: " << tick.getTimeMilli() << " ms" << endl; |
||||
|
||||
cout << "compare: " << func->compare(hashA, hashB) << endl << endl;; |
||||
} |
||||
|
||||
int main(int argc, char **argv) |
||||
{ |
||||
if (argc != 3) |
||||
{ |
||||
cerr << "must input the path of input image and target image. ex : hash_samples lena.jpg lena2.jpg" << endl; |
||||
return -1; |
||||
} |
||||
ocl::setUseOpenCL(false); |
||||
|
||||
Mat input = imread(argv[1]); |
||||
Mat target = imread(argv[2]); |
||||
|
||||
test_one<AverageHash>("AverageHash", input, target); |
||||
test_one<PHash>("PHash", input, target); |
||||
test_one<MarrHildrethHash>("MarrHildrethHash", input, target); |
||||
test_one<RadialVarianceHash>("RadialVarianceHash", input, target); |
||||
test_one<BlockMeanHash>("BlockMeanHash", input, target); |
||||
|
||||
return 0; |
||||
} |
@ -0,0 +1,86 @@ |
||||
// This file is part of OpenCV project.
|
||||
// It is subject to the license terms in the LICENSE file found in the top-level directory
|
||||
// of this distribution and at http://opencv.org/license.html.
|
||||
|
||||
#include "precomp.hpp" |
||||
|
||||
using namespace cv; |
||||
using namespace std; |
||||
using namespace img_hash; |
||||
|
||||
namespace { |
||||
|
||||
class AverageHashImpl : public ImgHashBase::ImgHashImpl |
||||
{ |
||||
private: |
||||
cv::Mat bitsImg; |
||||
cv::Mat grayImg; |
||||
cv::Mat resizeImg; |
||||
|
||||
public: |
||||
|
||||
virtual void compute(cv::InputArray inputArr, cv::OutputArray outputArr) |
||||
{ |
||||
cv::Mat const input = inputArr.getMat(); |
||||
CV_Assert(input.type() == CV_8UC4 || |
||||
input.type() == CV_8UC3 || |
||||
input.type() == CV_8U); |
||||
|
||||
cv::resize(input, resizeImg, cv::Size(8,8)); |
||||
if(input.type() == CV_8UC3) |
||||
{ |
||||
cv::cvtColor(resizeImg, grayImg, CV_BGR2GRAY); |
||||
} |
||||
else if(input.type() == CV_8UC4) |
||||
{ |
||||
cv::cvtColor(resizeImg, grayImg, CV_BGRA2GRAY); |
||||
} |
||||
else |
||||
{ |
||||
grayImg = resizeImg; |
||||
} |
||||
|
||||
uchar const imgMean = static_cast<uchar>(cvRound(cv::mean(grayImg)[0])); |
||||
cv::compare(grayImg, imgMean, bitsImg, CMP_GT); |
||||
bitsImg /= 255; |
||||
outputArr.create(1, 8, CV_8U); |
||||
cv::Mat hash = outputArr.getMat(); |
||||
uchar *hash_ptr = hash.ptr<uchar>(0); |
||||
uchar const *bits_ptr = bitsImg.ptr<uchar>(0); |
||||
std::bitset<8> bits; |
||||
for(size_t i = 0, j = 0; i != bitsImg.total(); ++j) |
||||
{ |
||||
for(size_t k = 0; k != 8; ++k) |
||||
{ |
||||
//avoid warning C4800, casting do not work
|
||||
bits[k] = bits_ptr[i++] != 0; |
||||
} |
||||
hash_ptr[j] = static_cast<uchar>(bits.to_ulong()); |
||||
} |
||||
} |
||||
|
||||
virtual double compare(cv::InputArray hashOne, cv::InputArray hashTwo) const |
||||
{ |
||||
return norm(hashOne, hashTwo, NORM_HAMMING); |
||||
} |
||||
}; |
||||
|
||||
} // namespace::
|
||||
|
||||
//==================================================================================================
|
||||
|
||||
namespace cv { namespace img_hash { |
||||
|
||||
Ptr<AverageHash> AverageHash::create() |
||||
{ |
||||
Ptr<AverageHash> res(new AverageHash()); |
||||
res->pImpl = makePtr<AverageHashImpl>(); |
||||
return res; |
||||
} |
||||
|
||||
void averageHash(cv::InputArray inputArr, cv::OutputArray outputArr) |
||||
{ |
||||
AverageHashImpl().compute(inputArr, outputArr); |
||||
} |
||||
|
||||
}} // cv::img_hash::
|
@ -0,0 +1,167 @@ |
||||
// This file is part of OpenCV project.
|
||||
// It is subject to the license terms in the LICENSE file found in the top-level directory
|
||||
// of this distribution and at http://opencv.org/license.html.
|
||||
|
||||
#include "precomp.hpp" |
||||
|
||||
using namespace cv; |
||||
using namespace cv::img_hash; |
||||
using namespace std; |
||||
|
||||
namespace { |
||||
|
||||
enum
|
||||
{ |
||||
imgWidth = 256, |
||||
imgHeight = 256, |
||||
blockWidth = 16, |
||||
blockHeigth = 16, |
||||
blockPerCol = imgHeight / blockHeigth, |
||||
blockPerRow = imgWidth / blockWidth, |
||||
rowSize = imgHeight - blockHeigth, |
||||
colSize = imgWidth - blockWidth |
||||
}; |
||||
|
||||
class BlockMeanHashImpl : public ImgHashBase::ImgHashImpl |
||||
{ |
||||
public: |
||||
BlockMeanHashImpl(int mode) |
||||
{ |
||||
setMode(mode); |
||||
} |
||||
|
||||
~BlockMeanHashImpl() {} |
||||
|
||||
virtual void compute(cv::InputArray inputArr, cv::OutputArray outputArr) |
||||
{ |
||||
cv::Mat const input = inputArr.getMat(); |
||||
CV_Assert(input.type() == CV_8UC4 || |
||||
input.type() == CV_8UC3 || |
||||
input.type() == CV_8U); |
||||
|
||||
cv::resize(input, resizeImg_, cv::Size(imgWidth,imgHeight)); |
||||
if(input.type() == CV_8UC3) |
||||
{ |
||||
cv::cvtColor(resizeImg_, grayImg_, CV_BGR2GRAY); |
||||
} |
||||
else if(input.type() == CV_8UC4) |
||||
{ |
||||
cv::cvtColor(resizeImg_, grayImg_, CV_BGRA2GRAY); |
||||
} |
||||
else |
||||
{ |
||||
grayImg_ = resizeImg_; |
||||
} |
||||
|
||||
int pixColStep = blockWidth; |
||||
int pixRowStep = blockHeigth; |
||||
int numOfBlocks = 0; |
||||
switch(mode_) |
||||
{ |
||||
case BLOCK_MEAN_HASH_MODE_0: |
||||
{ |
||||
numOfBlocks = blockPerCol * blockPerRow; |
||||
break; |
||||
} |
||||
case BLOCK_MEAN_HASH_MODE_1: |
||||
{ |
||||
pixColStep /= 2; |
||||
pixRowStep /= 2; |
||||
numOfBlocks = (blockPerCol*2-1) * (blockPerRow*2-1); |
||||
break; |
||||
} |
||||
default: |
||||
break; |
||||
} |
||||
|
||||
mean_.resize(numOfBlocks); |
||||
findMean(pixRowStep, pixColStep); |
||||
outputArr.create(1, numOfBlocks/8 + numOfBlocks % 8, CV_8U); |
||||
cv::Mat hash = outputArr.getMat(); |
||||
createHash(hash); |
||||
} |
||||
|
||||
virtual double compare(cv::InputArray hashOne, cv::InputArray hashTwo) const |
||||
{ |
||||
return norm(hashOne, hashTwo, NORM_HAMMING); |
||||
} |
||||
|
||||
void setMode(int mode) |
||||
{ |
||||
CV_Assert(mode == BLOCK_MEAN_HASH_MODE_0 || mode == BLOCK_MEAN_HASH_MODE_1); |
||||
mode_ = mode; |
||||
} |
||||
|
||||
void createHash(cv::Mat &hash) |
||||
{ |
||||
double const median = cv::mean(grayImg_)[0]; |
||||
uchar *hashPtr = hash.ptr<uchar>(0); |
||||
std::bitset<8> bits = 0; |
||||
for(size_t i = 0; i < mean_.size(); ++i) |
||||
{ |
||||
size_t const residual = i%8; |
||||
bits[residual] = mean_[i] < median ? 0 : 1; |
||||
if(residual == 7) |
||||
{ |
||||
*hashPtr = static_cast<uchar>(bits.to_ulong()); |
||||
++hashPtr; |
||||
}else if(i == mean_.size() - 1) |
||||
{ |
||||
*hashPtr = bits[residual]; |
||||
} |
||||
} |
||||
} |
||||
void findMean(int pixRowStep, int pixColStep) |
||||
{ |
||||
size_t blockIdx = 0; |
||||
for(int row = 0; row <= rowSize; row += pixRowStep) |
||||
{ |
||||
for(int col = 0; col <= colSize; col += pixColStep) |
||||
{ |
||||
mean_[blockIdx++] = cv::mean(grayImg_(cv::Rect(col, row, blockWidth, blockHeigth)))[0]; |
||||
} |
||||
} |
||||
} |
||||
|
||||
cv::Mat grayImg_; |
||||
std::vector<double> mean_; |
||||
int mode_; |
||||
cv::Mat resizeImg_; |
||||
}; |
||||
|
||||
inline BlockMeanHashImpl *getLocalImpl(ImgHashBase::ImgHashImpl *ptr) |
||||
{ |
||||
BlockMeanHashImpl * impl = static_cast<BlockMeanHashImpl*>(ptr); |
||||
CV_Assert(impl); |
||||
return impl; |
||||
} |
||||
|
||||
} |
||||
|
||||
//==================================================================================================
|
||||
|
||||
namespace cv { namespace img_hash { |
||||
|
||||
Ptr<BlockMeanHash> BlockMeanHash::create(int mode) |
||||
{ |
||||
Ptr<BlockMeanHash> res(new BlockMeanHash); |
||||
res->pImpl = makePtr<BlockMeanHashImpl>(mode); |
||||
return res; |
||||
} |
||||
|
||||
void BlockMeanHash::setMode(int mode) |
||||
{ |
||||
getLocalImpl(pImpl)->setMode(mode); |
||||
} |
||||
|
||||
std::vector<double> BlockMeanHash::getMean() const |
||||
{ |
||||
return getLocalImpl(pImpl)->mean_; |
||||
} |
||||
|
||||
void blockMeanHash(cv::InputArray inputArr, cv::OutputArray outputArr, int mode) |
||||
{ |
||||
BlockMeanHashImpl(mode).compute(inputArr, outputArr); |
||||
} |
||||
|
||||
}} // cv::img_hash::
|
@ -0,0 +1,95 @@ |
||||
// This file is part of OpenCV project.
|
||||
// It is subject to the license terms in the LICENSE file found in the top-level directory
|
||||
// of this distribution and at http://opencv.org/license.html.
|
||||
|
||||
#include "precomp.hpp" |
||||
|
||||
using namespace cv; |
||||
using namespace cv::img_hash; |
||||
using namespace std; |
||||
|
||||
namespace { |
||||
|
||||
class ColorMomentHashImpl : public ImgHashBase::ImgHashImpl |
||||
{ |
||||
public: |
||||
~ColorMomentHashImpl() {} |
||||
|
||||
virtual void compute(cv::InputArray inputArr, cv::OutputArray outputArr) |
||||
{ |
||||
cv::Mat const input = inputArr.getMat(); |
||||
CV_Assert(input.type() == CV_8UC4 || |
||||
input.type() == CV_8UC3 || |
||||
input.type() == CV_8U); |
||||
|
||||
if(input.type() == CV_8UC3) |
||||
{ |
||||
colorImg_ = input; |
||||
} |
||||
else if(input.type() == CV_8UC4) |
||||
{ |
||||
cv::cvtColor(input, colorImg_, CV_BGRA2BGR); |
||||
} |
||||
else |
||||
{ |
||||
cv::cvtColor(input, colorImg_, CV_GRAY2BGR); |
||||
} |
||||
|
||||
cv::resize(colorImg_, resizeImg_, cv::Size(512,512), 0, 0, |
||||
INTER_CUBIC); |
||||
cv::GaussianBlur(resizeImg_, blurImg_, cv::Size(3,3), 0, 0); |
||||
|
||||
cv::cvtColor(blurImg_, colorSpace_, CV_BGR2HSV); |
||||
cv::split(colorSpace_, channels_); |
||||
outputArr.create(1, 42, CV_64F); |
||||
cv::Mat hash = outputArr.getMat(); |
||||
hash.setTo(0); |
||||
computeMoments(hash.ptr<double>(0)); |
||||
|
||||
cv::cvtColor(blurImg_, colorSpace_, CV_BGR2YCrCb); |
||||
cv::split(colorSpace_, channels_); |
||||
computeMoments(hash.ptr<double>(0) + 21); |
||||
} |
||||
|
||||
virtual double compare(cv::InputArray hashOne, cv::InputArray hashTwo) const |
||||
{ |
||||
return norm(hashOne, hashTwo, NORM_L2) * 10000; |
||||
} |
||||
|
||||
private: |
||||
void computeMoments(double *inout) |
||||
{ |
||||
for(size_t i = 0; i != channels_.size(); ++i) |
||||
{ |
||||
cv::HuMoments(cv::moments(channels_[i]), inout); |
||||
inout += 7; |
||||
} |
||||
} |
||||
|
||||
private: |
||||
cv::Mat blurImg_; |
||||
cv::Mat colorImg_; |
||||
std::vector<cv::Mat> channels_; |
||||
cv::Mat colorSpace_; |
||||
cv::Mat resizeImg_; |
||||
}; |
||||
|
||||
} |
||||
|
||||
//==================================================================================================
|
||||
|
||||
namespace cv { namespace img_hash { |
||||
|
||||
Ptr<ColorMomentHash> ColorMomentHash::create() |
||||
{ |
||||
Ptr<ColorMomentHash> res(new ColorMomentHash); |
||||
res->pImpl = makePtr<ColorMomentHashImpl>(); |
||||
return res; |
||||
} |
||||
|
||||
void colorMomentHash(cv::InputArray inputArr, cv::OutputArray outputArr) |
||||
{ |
||||
ColorMomentHashImpl().compute(inputArr, outputArr); |
||||
} |
||||
|
||||
} } // cv::img_hash::
|
@ -0,0 +1,28 @@ |
||||
// This file is part of OpenCV project.
|
||||
// It is subject to the license terms in the LICENSE file found in the top-level directory
|
||||
// of this distribution and at http://opencv.org/license.html.
|
||||
|
||||
#include "precomp.hpp" |
||||
|
||||
namespace cv { |
||||
namespace img_hash{ |
||||
|
||||
ImgHashBase::ImgHashBase() |
||||
{ |
||||
} |
||||
|
||||
ImgHashBase::~ImgHashBase() |
||||
{ |
||||
} |
||||
|
||||
void ImgHashBase::compute(cv::InputArray inputArr, cv::OutputArray outputArr) |
||||
{ |
||||
pImpl->compute(inputArr, outputArr); |
||||
} |
||||
|
||||
double ImgHashBase::compare(cv::InputArray hashOne, cv::InputArray hashTwo) const |
||||
{ |
||||
return pImpl->compare(hashOne, hashTwo); |
||||
} |
||||
|
||||
} } // cv::img_hash::
|
@ -0,0 +1,212 @@ |
||||
// This file is part of OpenCV project.
|
||||
// It is subject to the license terms in the LICENSE file found in the top-level directory
|
||||
// of this distribution and at http://opencv.org/license.html.
|
||||
|
||||
#include "precomp.hpp" |
||||
|
||||
using namespace cv; |
||||
using namespace cv::img_hash; |
||||
using namespace std; |
||||
|
||||
namespace { |
||||
|
||||
void getMHKernel(float alpha, float level, cv::Mat &kernel) |
||||
{ |
||||
int const sigma = static_cast<int>(4*std::pow(alpha,level)); |
||||
|
||||
float const ratio = std::pow(alpha, -level); |
||||
kernel.create(2*sigma+1, 2*sigma+1, CV_32F); |
||||
for(int row = 0; row != kernel.rows; ++row) |
||||
{ |
||||
float const ydiff = static_cast<float>(row - sigma); |
||||
float const ypos = ratio * ydiff; |
||||
float const yposPow2 = ypos * ypos; |
||||
float *kPtr = kernel.ptr<float>(row); |
||||
for(int col = 0; col != kernel.cols; ++col) |
||||
{ |
||||
float const xpos = ratio * static_cast<float>((col - sigma)); |
||||
float const a = xpos * xpos + yposPow2; |
||||
kPtr[col] = (2-a)*std::exp(a/2); |
||||
} |
||||
} |
||||
} |
||||
|
||||
void fillBlocks(cv::Mat const &freImg, cv::Mat &blocks) |
||||
{ |
||||
//TODO : use forEach may provide better speed, however,
|
||||
//it is quite tedious to apply without lambda
|
||||
blocks.setTo(0); |
||||
for(int row = 0; row != blocks.rows; ++row) |
||||
{ |
||||
float *bptr = blocks.ptr<float>(row); |
||||
int const rOffset = row*16; |
||||
for(int col = 0; col != blocks.cols; ++col) |
||||
{ |
||||
cv::Rect const roi(rOffset,col*16,16,16); |
||||
bptr[col] = |
||||
static_cast<float>(cv::sum(freImg(roi))[0]); |
||||
} |
||||
} |
||||
} |
||||
|
||||
void createHash(cv::Mat const &blocks, cv::Mat &hash) |
||||
{ |
||||
int hash_index = 0; |
||||
int bit_index = 0; |
||||
uchar hashbyte = 0; |
||||
uchar *hashPtr = hash.ptr<uchar>(0); |
||||
for (int row=0; row < 29; row += 4) |
||||
{ |
||||
for (int col=0; col < 29; col += 4) |
||||
{ |
||||
cv::Rect const roi(col,row,3,3); |
||||
cv::Mat const blockROI = blocks(roi); |
||||
float const avg = |
||||
static_cast<float>(cv::sum(blockROI)[0]/9.0); |
||||
for(int i = 0; i != blockROI.rows; ++i) |
||||
{ |
||||
float const *bptr = blockROI.ptr<float>(i); |
||||
for(int j = 0; j != blockROI.cols; ++j) |
||||
{ |
||||
hashbyte <<= 1; |
||||
if (bptr[j] > avg) |
||||
{ |
||||
hashbyte |= 0x01; |
||||
} |
||||
++bit_index; |
||||
if ((bit_index%8) == 0) |
||||
{ |
||||
hash_index = (bit_index/8) - 1; |
||||
hashPtr[hash_index] = hashbyte; |
||||
hashbyte = 0x00; |
||||
} |
||||
} |
||||
} |
||||
} |
||||
} |
||||
} |
||||
|
||||
class MarrHildrethHashImpl : public ImgHashBase::ImgHashImpl |
||||
{ |
||||
public: |
||||
|
||||
MarrHildrethHashImpl(float alpha = 2.0f, float scale = 1.0f) : alphaVal(alpha), scaleVal(scale) |
||||
{ |
||||
getMHKernel(alphaVal, scaleVal, mhKernel); |
||||
blocks.create(31,31, CV_32F); |
||||
} |
||||
|
||||
~MarrHildrethHashImpl() { } |
||||
|
||||
virtual void compute(cv::InputArray inputArr, cv::OutputArray outputArr) |
||||
{ |
||||
cv::Mat const input = inputArr.getMat(); |
||||
CV_Assert(input.type() == CV_8UC4 || |
||||
input.type() == CV_8UC3 || |
||||
input.type() == CV_8U); |
||||
|
||||
if(input.type() == CV_8UC3) |
||||
{ |
||||
cv::cvtColor(input, grayImg, CV_BGR2GRAY); |
||||
} |
||||
else if(input.type() == CV_8UC4) |
||||
{ |
||||
cv::cvtColor(input, grayImg, CV_BGRA2GRAY); |
||||
} |
||||
else |
||||
{ |
||||
grayImg = input; |
||||
} |
||||
//pHash use Canny-deritch filter to blur the image
|
||||
cv::GaussianBlur(grayImg, blurImg, cv::Size(7, 7), 0); |
||||
cv::resize(blurImg, resizeImg, cv::Size(512, 512), 0, 0, INTER_CUBIC); |
||||
cv::equalizeHist(resizeImg, equalizeImg); |
||||
|
||||
//extract frequency info by mh kernel
|
||||
cv::filter2D(equalizeImg, freImg, CV_32F, mhKernel); |
||||
fillBlocks(freImg, blocks); |
||||
|
||||
outputArr.create(1, 72, CV_8U); |
||||
cv::Mat hash = outputArr.getMat(); |
||||
createHash(blocks, hash); |
||||
} |
||||
|
||||
virtual double compare(cv::InputArray hashOne, cv::InputArray hashTwo) const |
||||
{ |
||||
return norm(hashOne, hashTwo, NORM_HAMMING); |
||||
} |
||||
|
||||
float getAlpha() const |
||||
{ |
||||
return alphaVal; |
||||
} |
||||
|
||||
float getScale() const |
||||
{ |
||||
return scaleVal; |
||||
} |
||||
|
||||
void setKernelParam(float alpha, float scale) |
||||
{ |
||||
alphaVal = alpha; |
||||
scaleVal = scale; |
||||
getMHKernel(alphaVal, scaleVal, mhKernel); |
||||
} |
||||
|
||||
friend class MarrHildrethHash; |
||||
|
||||
private: |
||||
float alphaVal; |
||||
cv::Mat blocks; |
||||
cv::Mat blurImg; |
||||
cv::Mat equalizeImg; |
||||
cv::Mat freImg; //frequency response image
|
||||
cv::Mat grayImg; |
||||
cv::Mat mhKernel; |
||||
cv::Mat resizeImg; |
||||
float scaleVal; |
||||
}; |
||||
|
||||
inline MarrHildrethHashImpl *getLocalImpl(ImgHashBase::ImgHashImpl *ptr) |
||||
{ |
||||
MarrHildrethHashImpl * impl = static_cast<MarrHildrethHashImpl*>(ptr); |
||||
CV_Assert(impl); |
||||
return impl; |
||||
} |
||||
|
||||
} |
||||
|
||||
//==================================================================================================
|
||||
|
||||
namespace cv { namespace img_hash { |
||||
|
||||
float MarrHildrethHash::getAlpha() const |
||||
{ |
||||
return getLocalImpl(pImpl)->getAlpha(); |
||||
} |
||||
|
||||
float MarrHildrethHash::getScale() const |
||||
{ |
||||
return getLocalImpl(pImpl)->getScale(); |
||||
} |
||||
|
||||
void MarrHildrethHash::setKernelParam(float alpha, float scale) |
||||
{ |
||||
getLocalImpl(pImpl)->setKernelParam(alpha, scale); |
||||
} |
||||
|
||||
Ptr<MarrHildrethHash> MarrHildrethHash::create(float alpha, float scale) |
||||
{ |
||||
Ptr<MarrHildrethHash> res(new MarrHildrethHash); |
||||
res->pImpl = makePtr<MarrHildrethHashImpl>(alpha, scale); |
||||
return res; |
||||
} |
||||
|
||||
void marrHildrethHash(cv::InputArray inputArr, |
||||
cv::OutputArray outputArr, |
||||
float alpha, float scale) |
||||
{ |
||||
MarrHildrethHashImpl(alpha, scale).compute(inputArr, outputArr); |
||||
} |
||||
|
||||
} } // cv::img_hash::
|
@ -0,0 +1,93 @@ |
||||
// This file is part of OpenCV project.
|
||||
// It is subject to the license terms in the LICENSE file found in the top-level directory
|
||||
// of this distribution and at http://opencv.org/license.html.
|
||||
|
||||
#include "precomp.hpp" |
||||
|
||||
using namespace cv; |
||||
using namespace cv::img_hash; |
||||
using namespace std; |
||||
|
||||
namespace { |
||||
|
||||
class PHashImpl : public ImgHashBase::ImgHashImpl |
||||
{ |
||||
public: |
||||
virtual void compute(cv::InputArray inputArr, cv::OutputArray outputArr) |
||||
{ |
||||
cv::Mat const input = inputArr.getMat(); |
||||
CV_Assert(input.type() == CV_8UC4 || |
||||
input.type() == CV_8UC3 || |
||||
input.type() == CV_8U); |
||||
|
||||
cv::resize(input, resizeImg, cv::Size(32,32)); |
||||
if(input.type() == CV_8UC3) |
||||
{ |
||||
cv::cvtColor(resizeImg, grayImg, CV_BGR2GRAY); |
||||
} |
||||
else if(input.type() == CV_8UC4) |
||||
{ |
||||
cv::cvtColor(resizeImg, grayImg, CV_BGRA2GRAY); |
||||
} |
||||
else |
||||
{ |
||||
grayImg = resizeImg; |
||||
} |
||||
|
||||
grayImg.convertTo(grayFImg, CV_32F); |
||||
cv::dct(grayFImg, dctImg); |
||||
dctImg(cv::Rect(0, 0, 8, 8)).copyTo(topLeftDCT); |
||||
topLeftDCT.at<float>(0, 0) = 0; |
||||
float const imgMean = static_cast<float>(cv::mean(topLeftDCT)[0]); |
||||
|
||||
cv::compare(topLeftDCT, imgMean, bitsImg, CMP_GT); |
||||
bitsImg /= 255; |
||||
outputArr.create(1, 8, CV_8U); |
||||
cv::Mat hash = outputArr.getMat(); |
||||
uchar *hash_ptr = hash.ptr<uchar>(0); |
||||
uchar const *bits_ptr = bitsImg.ptr<uchar>(0); |
||||
std::bitset<8> bits; |
||||
for(size_t i = 0, j = 0; i != bitsImg.total(); ++j) |
||||
{ |
||||
for(size_t k = 0; k != 8; ++k) |
||||
{ |
||||
//avoid warning C4800, casting do not work
|
||||
bits[k] = bits_ptr[i++] != 0; |
||||
} |
||||
hash_ptr[j] = static_cast<uchar>(bits.to_ulong()); |
||||
} |
||||
} |
||||
|
||||
virtual double compare(cv::InputArray hashOne, cv::InputArray hashTwo) const |
||||
{ |
||||
return norm(hashOne, hashTwo, NORM_HAMMING); |
||||
} |
||||
|
||||
private: |
||||
cv::Mat bitsImg; |
||||
cv::Mat dctImg; |
||||
cv::Mat grayFImg; |
||||
cv::Mat grayImg; |
||||
cv::Mat resizeImg; |
||||
cv::Mat topLeftDCT; |
||||
}; |
||||
|
||||
} // namespace::
|
||||
|
||||
//==================================================================================================
|
||||
|
||||
namespace cv { namespace img_hash { |
||||
|
||||
Ptr<PHash> PHash::create() |
||||
{ |
||||
Ptr<PHash> res(new PHash); |
||||
res->pImpl = makePtr<PHashImpl>(); |
||||
return res; |
||||
} |
||||
|
||||
void pHash(cv::InputArray inputArr, cv::OutputArray outputArr) |
||||
{ |
||||
PHashImpl().compute(inputArr, outputArr); |
||||
} |
||||
|
||||
} } // cv::img_hash::
|
@ -0,0 +1,29 @@ |
||||
// This file is part of OpenCV project.
|
||||
// It is subject to the license terms in the LICENSE file found in the top-level directory
|
||||
// of this distribution and at http://opencv.org/license.html.
|
||||
|
||||
#ifndef OPENCV_IMG_HASH_PRECOMP_H |
||||
#define OPENCV_IMG_HASH_PRECOMP_H |
||||
|
||||
#include "opencv2/core.hpp" |
||||
#include "opencv2/core/base.hpp" |
||||
#include "opencv2/imgproc.hpp" |
||||
#include "opencv2/imgproc/types_c.h" |
||||
#include "opencv2/img_hash.hpp" |
||||
|
||||
#include <bitset> |
||||
#include <iostream> |
||||
|
||||
namespace cv{ namespace img_hash { |
||||
|
||||
class ImgHashBase::ImgHashImpl |
||||
{ |
||||
public: |
||||
virtual void compute(cv::InputArray inputArr, cv::OutputArray outputArr) = 0; |
||||
virtual double compare(cv::InputArray hashOne, cv::InputArray hashTwo) const = 0; |
||||
virtual ~ImgHashImpl() {} |
||||
}; |
||||
|
||||
}} // cv::img_hash::
|
||||
|
||||
#endif // OPENCV_IMG_HASH_PRECOMP_H
|
@ -0,0 +1,361 @@ |
||||
// This file is part of OpenCV project.
|
||||
// It is subject to the license terms in the LICENSE file found in the top-level directory
|
||||
// of this distribution and at http://opencv.org/license.html.
|
||||
|
||||
#include "precomp.hpp" |
||||
|
||||
using namespace cv; |
||||
using namespace cv::img_hash; |
||||
using namespace std; |
||||
|
||||
namespace { |
||||
|
||||
enum
|
||||
{ |
||||
hashSize = 40, |
||||
}; |
||||
|
||||
inline float roundingFactor(float val) |
||||
{ |
||||
return val >= 0 ? 0.5f : -0.5f; |
||||
} |
||||
|
||||
inline int createOffSet(int length) |
||||
{ |
||||
float const center = static_cast<float>(length/2); |
||||
return static_cast<int>(std::floor(center + roundingFactor(center))); |
||||
} |
||||
|
||||
class RadialVarianceHashImpl : public ImgHashBase::ImgHashImpl |
||||
{ |
||||
public: |
||||
cv::Mat blurImg_; |
||||
std::vector<double> features_; |
||||
cv::Mat grayImg_; |
||||
int numOfAngelLine_; |
||||
cv::Mat pixPerLine_; |
||||
cv::Mat projections_; |
||||
double sigma_; |
||||
|
||||
RadialVarianceHashImpl(double sigma, int numOfAngleLine) |
||||
: numOfAngelLine_(numOfAngleLine), sigma_(sigma) |
||||
{ |
||||
} |
||||
|
||||
~RadialVarianceHashImpl() {} |
||||
|
||||
virtual void compute(cv::InputArray inputArr, cv::OutputArray outputArr) |
||||
{ |
||||
cv::Mat const input = inputArr.getMat(); |
||||
CV_Assert(input.type() == CV_8UC4 || |
||||
input.type() == CV_8UC3 || |
||||
input.type() == CV_8U); |
||||
|
||||
if(input.type() == CV_8UC3) |
||||
{ |
||||
cv::cvtColor(input, grayImg_, CV_BGR2GRAY); |
||||
} |
||||
else if(input.type() == CV_8UC4) |
||||
{ |
||||
cv::cvtColor(input, grayImg_, CV_BGRA2GRAY); |
||||
} |
||||
else |
||||
{ |
||||
grayImg_ = input; |
||||
} |
||||
|
||||
cv::GaussianBlur(grayImg_, blurImg_, cv::Size(0,0), sigma_, sigma_); |
||||
radialProjections(blurImg_); |
||||
findFeatureVector(); |
||||
outputArr.create(1, hashSize, CV_8U); |
||||
cv::Mat hash = outputArr.getMat(); |
||||
hashCalculate(hash); |
||||
} |
||||
|
||||
virtual double compare(cv::InputArray hashOne, cv::InputArray hashTwo) const |
||||
{ |
||||
cv::Mat const hashOneF = hashOne.getMat(); |
||||
cv::Mat const hashTwoF = hashTwo.getMat(); |
||||
CV_Assert(hashOneF.cols == hashSize && hashOneF.cols == hashTwoF.cols); |
||||
|
||||
float bufferOne[hashSize]; |
||||
cv::Mat hashFloatOne(1, hashSize, CV_32F, bufferOne); |
||||
hashOneF.convertTo(hashFloatOne, CV_32F); |
||||
|
||||
float bufferTwo[hashSize]; |
||||
cv::Mat hashFloatTwo(1, hashSize, CV_32F, bufferTwo); |
||||
hashTwoF.convertTo(hashFloatTwo, CV_32F); |
||||
|
||||
int const pixNum = hashFloatOne.rows * hashFloatOne.cols; |
||||
cv::Scalar hOneMean, hOneStd, hTwoMean, hTwoStd; |
||||
cv::meanStdDev(hashFloatOne, hOneMean, hOneStd); |
||||
cv::meanStdDev(hashFloatTwo, hTwoMean, hTwoStd); |
||||
|
||||
// Compute covariance and correlation coefficient
|
||||
hashFloatOne -= hOneMean; |
||||
hashFloatTwo -= hTwoMean; |
||||
double max = std::numeric_limits<double>::min(); |
||||
for(int i = 0; i != hashSize; ++i) |
||||
{ |
||||
double const covar = (hashFloatOne).dot(hashFloatTwo) / pixNum; |
||||
double const corre = covar / (hOneStd[0] * hTwoStd[0] + 1e-20); |
||||
max = std::max(corre, max); |
||||
//move last value to first position, first value to second position,
|
||||
//second value to third position and so on
|
||||
float const preValue = bufferTwo[hashSize-1]; |
||||
std::copy_backward(bufferTwo, bufferTwo + hashSize - 1, bufferTwo + hashSize); |
||||
bufferTwo[0] = preValue; |
||||
} |
||||
|
||||
//return peak correlation coefficient
|
||||
return max; |
||||
} |
||||
|
||||
int getNumOfAngleLine() const |
||||
{ |
||||
return numOfAngelLine_; |
||||
} |
||||
double getSigma() const |
||||
{ |
||||
return sigma_; |
||||
} |
||||
|
||||
void setNumOfAngleLine(int value) |
||||
{ |
||||
CV_Assert(value > 0); |
||||
numOfAngelLine_ = value; |
||||
} |
||||
void setSigma(double value) |
||||
{ |
||||
CV_Assert(value >= 1.0); |
||||
sigma_ = value; |
||||
} |
||||
|
||||
void afterHalfProjections(cv::Mat const &input, int D, int xOff, int yOff) |
||||
{ |
||||
int *pplPtr = pixPerLine_.ptr<int>(0); |
||||
int const init = 3*numOfAngelLine_/4; |
||||
for(int k = init, j = 0; k < numOfAngelLine_; ++k, j += 2) |
||||
{ |
||||
float const theta = k*3.14159f/numOfAngelLine_; |
||||
float const alpha = std::tan(theta); |
||||
uchar *projDown = projections_.ptr<uchar>(k); |
||||
uchar *projUp = projections_.ptr<uchar>(k-j); |
||||
for(int x = 0; x < D; ++x) |
||||
{ |
||||
float const y = alpha*(x-xOff); |
||||
int const yd = static_cast<int>(std::floor(y + roundingFactor(y))); |
||||
if((yd + yOff >= 0)&&(yd + yOff < input.rows) && (x < input.cols)) |
||||
{ |
||||
projDown[x] = input.at<uchar>(yd+yOff, x); |
||||
pplPtr[k] += 1; |
||||
} |
||||
if ((yOff - yd >= 0)&&(yOff - yd < input.cols)&& |
||||
(2*yOff - x >= 0)&&(2*yOff- x < input.rows)&& |
||||
(k != init)) |
||||
{ |
||||
projUp[x] = |
||||
input.at<uchar>(-(x-yOff)+yOff, -yd+yOff); |
||||
pplPtr[k-j] += 1; |
||||
} |
||||
} |
||||
} |
||||
} |
||||
|
||||
void findFeatureVector() |
||||
{ |
||||
features_.resize(numOfAngelLine_); |
||||
double sum = 0.0; |
||||
double sumSqd = 0.0; |
||||
int const *pplPtr = pixPerLine_.ptr<int>(0); |
||||
for(int k=0; k < numOfAngelLine_; ++k) |
||||
{ |
||||
double lineSum = 0.0; |
||||
double lineSumSqd = 0.0; |
||||
//original implementation of pHash may generate zero pixNum, this
|
||||
//will cause NaN value and make the features become less discriminative
|
||||
//to avoid this problem, I add a small value--0.00001
|
||||
double const pixNum = pplPtr[k] + 0.00001; |
||||
double const pixNumPow2 = pixNum * pixNum; |
||||
uchar const *projPtr = projections_.ptr<uchar>(k); |
||||
for(int i = 0; i < projections_.cols; ++i) |
||||
{ |
||||
double const value = projPtr[i]; |
||||
lineSum += value; |
||||
lineSumSqd += value * value; |
||||
} |
||||
features_[k] = (lineSumSqd/pixNum) - |
||||
(lineSum*lineSum)/(pixNumPow2); |
||||
sum += features_[k]; |
||||
sumSqd += features_[k]*features_[k]; |
||||
} |
||||
double const numOfALPow2 = numOfAngelLine_ * numOfAngelLine_; |
||||
double const mean = sum/numOfAngelLine_; |
||||
double const var = std::sqrt((sumSqd/numOfAngelLine_) - (sum*sum)/(numOfALPow2)); |
||||
for(int i = 0; i < numOfAngelLine_; ++i) |
||||
{ |
||||
features_[i] = (features_[i] - mean)/var; |
||||
} |
||||
} |
||||
|
||||
void firstHalfProjections(cv::Mat const &input, int D, int xOff, int yOff) |
||||
{ |
||||
int *pplPtr = pixPerLine_.ptr<int>(0); |
||||
for(int k = 0; k < numOfAngelLine_/4+1; ++k) |
||||
{ |
||||
float const theta = k*3.14159f/numOfAngelLine_; |
||||
float const alpha = std::tan(theta); |
||||
uchar *projOne = projections_.ptr<uchar>(k); |
||||
uchar *projTwo = projections_.ptr<uchar>(numOfAngelLine_/2-k); |
||||
for(int x = 0; x < D; ++x) |
||||
{ |
||||
float const y = alpha*(x-xOff); |
||||
int const yd = static_cast<int>(std::floor(y + roundingFactor(y))); |
||||
if((yd + yOff >= 0)&&(yd + yOff < input.rows) && (x < input.cols)) |
||||
{ |
||||
projOne[x] = input.at<uchar>(yd+yOff, x); |
||||
pplPtr[k] += 1; |
||||
} |
||||
if((yd + xOff >= 0) && (yd + xOff < input.cols) && |
||||
(k != numOfAngelLine_/4) && (x < input.rows)) |
||||
{ |
||||
projTwo[x] = |
||||
input.at<uchar>(x, yd+xOff); |
||||
pplPtr[numOfAngelLine_/2-k] += 1; |
||||
} |
||||
} |
||||
} |
||||
} |
||||
|
||||
void hashCalculate(cv::Mat &hash) |
||||
{ |
||||
double temp[hashSize]; |
||||
double max = 0; |
||||
double min = 0; |
||||
size_t const featureSize = features_.size(); |
||||
//constexpr is a better choice
|
||||
double const sqrtTwo = 1.4142135623730950488016887242097; |
||||
for(int k = 0; k < hash.cols; ++k) |
||||
{ |
||||
double sum = 0; |
||||
for(size_t n = 0; n < featureSize; ++n) |
||||
{ |
||||
sum += features_[n]*std::cos((3.14159*(2*n+1)*k)/(2*featureSize)); |
||||
} |
||||
temp[k] = k == 0 ? sum/std::sqrt(featureSize) : |
||||
sum*sqrtTwo/std::sqrt(featureSize); |
||||
if(temp[k] > max) |
||||
{ |
||||
max = temp[k]; |
||||
} |
||||
else if(temp[k] < min) |
||||
{ |
||||
min = temp[k]; |
||||
} |
||||
} |
||||
|
||||
double const range = max - min; |
||||
if(range != 0) |
||||
{ |
||||
//std::transform is a better choice if lambda supported
|
||||
uchar *hashPtr = hash.ptr<uchar>(0); |
||||
for(int i = 0; i < hash.cols; ++i) |
||||
{ |
||||
hashPtr[i] = static_cast<uchar>((255*(temp[i] - min)/range)); |
||||
} |
||||
} |
||||
else |
||||
{ |
||||
hash = 0; |
||||
} |
||||
} |
||||
|
||||
void radialProjections(cv::Mat const &input) |
||||
{ |
||||
int const D = (input.cols > input.rows) ? input.cols : input.rows; |
||||
//Different with PHash, this part reverse the row size and col size,
|
||||
//because cv::Mat is row major but not column major
|
||||
projections_.create(numOfAngelLine_, D, CV_8U); |
||||
projections_ = 0; |
||||
pixPerLine_.create(1, numOfAngelLine_, CV_32S); |
||||
pixPerLine_ = 0; |
||||
int const xOff = createOffSet(input.cols); |
||||
int const yOff = createOffSet(input.rows); |
||||
|
||||
firstHalfProjections(input, D, xOff, yOff); |
||||
afterHalfProjections(input, D, xOff, yOff); |
||||
} |
||||
}; |
||||
|
||||
inline RadialVarianceHashImpl *getLocalImpl(ImgHashBase::ImgHashImpl *ptr) |
||||
{ |
||||
RadialVarianceHashImpl * impl = static_cast<RadialVarianceHashImpl*>(ptr); |
||||
CV_Assert(impl); |
||||
return impl; |
||||
} |
||||
|
||||
} // namespace::
|
||||
|
||||
//==================================================================================================
|
||||
|
||||
namespace cv { namespace img_hash { |
||||
|
||||
Ptr<RadialVarianceHash> RadialVarianceHash::create(double sigma, int numOfAngleLine) |
||||
{ |
||||
Ptr<RadialVarianceHash> res(new RadialVarianceHash); |
||||
res->pImpl = makePtr<RadialVarianceHashImpl>(sigma, numOfAngleLine); |
||||
return res; |
||||
} |
||||
|
||||
int RadialVarianceHash::getNumOfAngleLine() const |
||||
{ |
||||
return getLocalImpl(pImpl)->getNumOfAngleLine(); |
||||
} |
||||
|
||||
double RadialVarianceHash::getSigma() const |
||||
{ |
||||
return getLocalImpl(pImpl)->getSigma(); |
||||
} |
||||
|
||||
void RadialVarianceHash::setNumOfAngleLine(int value) |
||||
{ |
||||
getLocalImpl(pImpl)->setNumOfAngleLine(value); |
||||
} |
||||
|
||||
void RadialVarianceHash::setSigma(double value) |
||||
{ |
||||
getLocalImpl(pImpl)->setSigma(value); |
||||
} |
||||
|
||||
std::vector<double> RadialVarianceHash::getFeatures() |
||||
{ |
||||
getLocalImpl(pImpl)->findFeatureVector(); |
||||
return getLocalImpl(pImpl)->features_; |
||||
} |
||||
|
||||
cv::Mat RadialVarianceHash::getHash() |
||||
{ |
||||
cv::Mat hash; |
||||
getLocalImpl(pImpl)->hashCalculate(hash); |
||||
return hash; |
||||
} |
||||
|
||||
Mat RadialVarianceHash::getPixPerLine(Mat const &input) |
||||
{ |
||||
getLocalImpl(pImpl)->radialProjections(input); |
||||
return getLocalImpl(pImpl)->pixPerLine_; |
||||
} |
||||
|
||||
Mat RadialVarianceHash::getProjection() |
||||
{ |
||||
return getLocalImpl(pImpl)->projections_; |
||||
} |
||||
|
||||
void radialVarianceHash(cv::InputArray inputArr, |
||||
cv::OutputArray outputArr, |
||||
double sigma, int numOfAngleLine) |
||||
{ |
||||
RadialVarianceHashImpl(sigma, numOfAngleLine).compute(inputArr, outputArr); |
||||
} |
||||
|
||||
}} // cv::img_hash::
|
@ -0,0 +1,58 @@ |
||||
// This file is part of OpenCV project.
|
||||
// It is subject to the license terms in the LICENSE file found in the top-level directory
|
||||
// of this distribution and at http://opencv.org/license.html.
|
||||
|
||||
#include "test_precomp.hpp" |
||||
|
||||
#include <bitset> |
||||
|
||||
using namespace cv; |
||||
|
||||
class CV_AverageHashTest : public cvtest::BaseTest |
||||
{ |
||||
public: |
||||
CV_AverageHashTest(); |
||||
~CV_AverageHashTest(); |
||||
protected: |
||||
void run(int /* idx */); |
||||
}; |
||||
|
||||
CV_AverageHashTest::CV_AverageHashTest(){} |
||||
CV_AverageHashTest::~CV_AverageHashTest(){} |
||||
|
||||
void CV_AverageHashTest::run(int ) |
||||
{ |
||||
cv::Mat const input = (cv::Mat_<uchar>(8, 8) << |
||||
1, 5, 4, 6, 3, 2, 7, 8, |
||||
2, 4, 8, 9, 2, 1, 4, 3, |
||||
3, 4, 5, 7, 9, 8, 7, 6, |
||||
1, 2, 3, 4, 5, 6, 7, 8, |
||||
8, 7, 2, 3, 6, 4, 5, 1, |
||||
3, 4, 1, 2, 9, 8, 4, 2, |
||||
6, 7, 8, 9, 7, 4, 3, 2, |
||||
8, 7, 6, 5, 4, 3, 2, 1); |
||||
cv::Mat hash; |
||||
cv::img_hash::averageHash(input, hash); |
||||
bool const expectResult[] = |
||||
{ |
||||
0,0,0,1,0,0,1,1, |
||||
0,0,1,1,0,0,0,0, |
||||
0,0,0,1,1,1,1,1, |
||||
0,0,0,0,0,1,1,1, |
||||
1,1,0,0,1,0,0,0, |
||||
0,0,0,0,1,1,0,0, |
||||
1,1,1,1,1,0,0,0, |
||||
1,1,1,0,0,0,0,0 |
||||
}; |
||||
uchar const *hashPtr = hash.ptr<uchar>(0); |
||||
for(int i = 0; i != hash.cols; ++i) |
||||
{ |
||||
std::bitset<8> const bits = hashPtr[i]; |
||||
for(int j = 0; j != 8; ++j) |
||||
{ |
||||
EXPECT_EQ(bits[j], expectResult[i*8+j]); |
||||
} |
||||
} |
||||
} |
||||
|
||||
TEST(average_hash_test, accuracy) { CV_AverageHashTest test; test.safe_run(); } |
@ -0,0 +1,212 @@ |
||||
// This file is part of OpenCV project.
|
||||
// It is subject to the license terms in the LICENSE file found in the top-level directory
|
||||
// of this distribution and at http://opencv.org/license.html.
|
||||
|
||||
#include "test_precomp.hpp" |
||||
|
||||
#include <bitset> |
||||
|
||||
using namespace cv; |
||||
using namespace cv::img_hash; |
||||
|
||||
/**
|
||||
*The expected results of this test case are come from the Phash library, |
||||
*I use it as golden model |
||||
*/ |
||||
class CV_BlockMeanHashTest : public cvtest::BaseTest |
||||
{ |
||||
public: |
||||
CV_BlockMeanHashTest(); |
||||
protected: |
||||
void run(int /* idx */); |
||||
|
||||
void testMeanMode0(); |
||||
void testMeanMode1(); |
||||
void testHashMode0(); |
||||
void testHashMode1(); |
||||
|
||||
cv::Mat input; |
||||
cv::Mat hash; |
||||
Ptr<cv::img_hash::BlockMeanHash> bmh; |
||||
}; |
||||
|
||||
CV_BlockMeanHashTest::CV_BlockMeanHashTest() |
||||
{ |
||||
input.create(256, 256, CV_8U); |
||||
for(int row = 0; row != input.rows; ++row) |
||||
{ |
||||
uchar value = static_cast<uchar>(row); |
||||
for(int col = 0; col != input.cols; ++col) |
||||
{ |
||||
input.at<uchar>(row, col) = value++; |
||||
} |
||||
} |
||||
bmh = BlockMeanHash::create(BLOCK_MEAN_HASH_MODE_0); |
||||
} |
||||
|
||||
void CV_BlockMeanHashTest::testMeanMode0() |
||||
{ |
||||
std::vector<double> const &features = bmh->getMean(); |
||||
double const expectResult[] = |
||||
{15,31,47,63,79,95,111,127,143,159,175,191,207,223,239,135, |
||||
31,47,63,79,95,111,127,143,159,175,191,207,223,239,135,15, |
||||
47,63,79,95,111,127,143,159,175,191,207,223,239,135,15,31, |
||||
63,79,95,111,127,143,159,175,191,207,223,239,135,15,31,47, |
||||
79,95,111,127,143,159,175,191,207,223,239,135,15,31,47,63, |
||||
95,111,127,143,159,175,191,207,223,239,135,15,31,47,63,79, |
||||
111,127,143,159,175,191,207,223,239,135,15,31,47,63,79,95, |
||||
127,143,159,175,191,207,223,239,135,15,31,47,63,79,95,111, |
||||
143,159,175,191,207,223,239,135,15,31,47,63,79,95,111,127, |
||||
159,175,191,207,223,239,135,15,31,47,63,79,95,111,127,143, |
||||
175,191,207,223,239,135,15,31,47,63,79,95,111,127,143,159, |
||||
191,207,223,239,135,15,31,47,63,79,95,111,127,143,159,175, |
||||
207,223,239,135,15,31,47,63,79,95,111,127,143,159,175,191, |
||||
223,239,135,15,31,47,63,79,95,111,127,143,159,175,191,207, |
||||
239,135,15,31,47,63,79,95,111,127,143,159,175,191,207,223, |
||||
135,15,31,47,63,79,95,111,127,143,159,175,191,207,223,239,}; |
||||
for(size_t i = 0; i != features.size(); ++i) |
||||
{ |
||||
ASSERT_NEAR(features[i], expectResult[i], 0.0001); |
||||
} |
||||
} |
||||
|
||||
void CV_BlockMeanHashTest::testMeanMode1() |
||||
{ |
||||
std::vector<double> const &features = bmh->getMean(); |
||||
double const expectResult[] = |
||||
{15,23,31,39,47,55,63,71,79,87,95,103,111,119,127,135,143,151,159,167,175,183,191,199,207,215,223,231,239,219,135, |
||||
23,31,39,47,55,63,71,79,87,95,103,111,119,127,135,143,151,159,167,175,183,191,199,207,215,223,231,239,219,135,43, |
||||
31,39,47,55,63,71,79,87,95,103,111,119,127,135,143,151,159,167,175,183,191,199,207,215,223,231,239,219,135,43,15, |
||||
39,47,55,63,71,79,87,95,103,111,119,127,135,143,151,159,167,175,183,191,199,207,215,223,231,239,219,135,43,15,23, |
||||
47,55,63,71,79,87,95,103,111,119,127,135,143,151,159,167,175,183,191,199,207,215,223,231,239,219,135,43,15,23,31, |
||||
55,63,71,79,87,95,103,111,119,127,135,143,151,159,167,175,183,191,199,207,215,223,231,239,219,135,43,15,23,31,39, |
||||
63,71,79,87,95,103,111,119,127,135,143,151,159,167,175,183,191,199,207,215,223,231,239,219,135,43,15,23,31,39,47, |
||||
71,79,87,95,103,111,119,127,135,143,151,159,167,175,183,191,199,207,215,223,231,239,219,135,43,15,23,31,39,47,55, |
||||
79,87,95,103,111,119,127,135,143,151,159,167,175,183,191,199,207,215,223,231,239,219,135,43,15,23,31,39,47,55,63, |
||||
87,95,103,111,119,127,135,143,151,159,167,175,183,191,199,207,215,223,231,239,219,135,43,15,23,31,39,47,55,63,71, |
||||
95,103,111,119,127,135,143,151,159,167,175,183,191,199,207,215,223,231,239,219,135,43,15,23,31,39,47,55,63,71,79, |
||||
103,111,119,127,135,143,151,159,167,175,183,191,199,207,215,223,231,239,219,135,43,15,23,31,39,47,55,63,71,79,87, |
||||
111,119,127,135,143,151,159,167,175,183,191,199,207,215,223,231,239,219,135,43,15,23,31,39,47,55,63,71,79,87,95, |
||||
119,127,135,143,151,159,167,175,183,191,199,207,215,223,231,239,219,135,43,15,23,31,39,47,55,63,71,79,87,95,103, |
||||
127,135,143,151,159,167,175,183,191,199,207,215,223,231,239,219,135,43,15,23,31,39,47,55,63,71,79,87,95,103,111, |
||||
135,143,151,159,167,175,183,191,199,207,215,223,231,239,219,135,43,15,23,31,39,47,55,63,71,79,87,95,103,111,119, |
||||
143,151,159,167,175,183,191,199,207,215,223,231,239,219,135,43,15,23,31,39,47,55,63,71,79,87,95,103,111,119,127, |
||||
151,159,167,175,183,191,199,207,215,223,231,239,219,135,43,15,23,31,39,47,55,63,71,79,87,95,103,111,119,127,135, |
||||
159,167,175,183,191,199,207,215,223,231,239,219,135,43,15,23,31,39,47,55,63,71,79,87,95,103,111,119,127,135,143, |
||||
167,175,183,191,199,207,215,223,231,239,219,135,43,15,23,31,39,47,55,63,71,79,87,95,103,111,119,127,135,143,151, |
||||
175,183,191,199,207,215,223,231,239,219,135,43,15,23,31,39,47,55,63,71,79,87,95,103,111,119,127,135,143,151,159, |
||||
183,191,199,207,215,223,231,239,219,135,43,15,23,31,39,47,55,63,71,79,87,95,103,111,119,127,135,143,151,159,167, |
||||
191,199,207,215,223,231,239,219,135,43,15,23,31,39,47,55,63,71,79,87,95,103,111,119,127,135,143,151,159,167,175, |
||||
199,207,215,223,231,239,219,135,43,15,23,31,39,47,55,63,71,79,87,95,103,111,119,127,135,143,151,159,167,175,183, |
||||
207,215,223,231,239,219,135,43,15,23,31,39,47,55,63,71,79,87,95,103,111,119,127,135,143,151,159,167,175,183,191, |
||||
215,223,231,239,219,135,43,15,23,31,39,47,55,63,71,79,87,95,103,111,119,127,135,143,151,159,167,175,183,191,199, |
||||
223,231,239,219,135,43,15,23,31,39,47,55,63,71,79,87,95,103,111,119,127,135,143,151,159,167,175,183,191,199,207, |
||||
231,239,219,135,43,15,23,31,39,47,55,63,71,79,87,95,103,111,119,127,135,143,151,159,167,175,183,191,199,207,215, |
||||
239,219,135,43,15,23,31,39,47,55,63,71,79,87,95,103,111,119,127,135,143,151,159,167,175,183,191,199,207,215,223, |
||||
219,135,43,15,23,31,39,47,55,63,71,79,87,95,103,111,119,127,135,143,151,159,167,175,183,191,199,207,215,223,231, |
||||
135,43,15,23,31,39,47,55,63,71,79,87,95,103,111,119,127,135,143,151,159,167,175,183,191,199,207,215,223,231,239,}; |
||||
for(size_t i = 0; i != features.size(); ++i) |
||||
{ |
||||
ASSERT_NEAR(features[i], expectResult[i], 0.0001); |
||||
} |
||||
} |
||||
|
||||
void CV_BlockMeanHashTest::testHashMode0() |
||||
{ |
||||
bool const expectResult[] = |
||||
{0,0,0,0,0,0,0,0,1,1,1,1,1,1,1,1, |
||||
0,0,0,0,0,0,0,1,1,1,1,1,1,1,1,0, |
||||
0,0,0,0,0,0,1,1,1,1,1,1,1,1,0,0, |
||||
0,0,0,0,0,1,1,1,1,1,1,1,1,0,0,0, |
||||
0,0,0,0,1,1,1,1,1,1,1,1,0,0,0,0, |
||||
0,0,0,1,1,1,1,1,1,1,1,0,0,0,0,0, |
||||
0,0,1,1,1,1,1,1,1,1,0,0,0,0,0,0, |
||||
0,1,1,1,1,1,1,1,1,0,0,0,0,0,0,0, |
||||
1,1,1,1,1,1,1,1,0,0,0,0,0,0,0,0, |
||||
1,1,1,1,1,1,1,0,0,0,0,0,0,0,0,1, |
||||
1,1,1,1,1,1,0,0,0,0,0,0,0,0,1,1, |
||||
1,1,1,1,1,0,0,0,0,0,0,0,0,1,1,1, |
||||
1,1,1,1,0,0,0,0,0,0,0,0,1,1,1,1, |
||||
1,1,1,0,0,0,0,0,0,0,0,1,1,1,1,1, |
||||
1,1,0,0,0,0,0,0,0,0,1,1,1,1,1,1, |
||||
1,0,0,0,0,0,0,0,0,1,1,1,1,1,1,1, |
||||
}; |
||||
|
||||
for(int i = 0; i != hash.cols; ++i) |
||||
{ |
||||
std::bitset<8> const bits = hash.at<uchar>(0, i); |
||||
for(size_t j = 0; j != bits.size(); ++j) |
||||
{ |
||||
EXPECT_EQ(expectResult[i*8+j], bits[j]); |
||||
} |
||||
} |
||||
} |
||||
|
||||
void CV_BlockMeanHashTest::testHashMode1() |
||||
{ |
||||
bool const expectResult[] = |
||||
{0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1, |
||||
0,0,0,0,0,0,0,0,0,0,0,0,0,0,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,0, |
||||
0,0,0,0,0,0,0,0,0,0,0,0,0,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,0,0, |
||||
0,0,0,0,0,0,0,0,0,0,0,0,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,0,0,0, |
||||
0,0,0,0,0,0,0,0,0,0,0,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,0,0,0,0, |
||||
0,0,0,0,0,0,0,0,0,0,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,0,0,0,0,0, |
||||
0,0,0,0,0,0,0,0,0,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,0,0,0,0,0,0, |
||||
0,0,0,0,0,0,0,0,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,0,0,0,0,0,0,0, |
||||
0,0,0,0,0,0,0,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,0,0,0,0,0,0,0,0, |
||||
0,0,0,0,0,0,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,0,0,0,0,0,0,0,0,0, |
||||
0,0,0,0,0,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,0,0,0,0,0,0,0,0,0,0, |
||||
0,0,0,0,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,0,0,0,0,0,0,0,0,0,0,0, |
||||
0,0,0,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,0,0,0,0,0,0,0,0,0,0,0,0, |
||||
0,0,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,0,0,0,0,0,0,0,0,0,0,0,0,0, |
||||
0,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,0,0,0,0,0,0,0,0,0,0,0,0,0,0, |
||||
1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, |
||||
1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, |
||||
1,1,1,1,1,1,1,1,1,1,1,1,1,1,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,1, |
||||
1,1,1,1,1,1,1,1,1,1,1,1,1,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,1,1, |
||||
1,1,1,1,1,1,1,1,1,1,1,1,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,1,1,1, |
||||
1,1,1,1,1,1,1,1,1,1,1,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,1,1,1,1, |
||||
1,1,1,1,1,1,1,1,1,1,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,1,1,1,1,1, |
||||
1,1,1,1,1,1,1,1,1,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,1,1,1,1,1,1, |
||||
1,1,1,1,1,1,1,1,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,1,1,1,1,1,1,1, |
||||
1,1,1,1,1,1,1,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,1,1,1,1,1,1,1,1, |
||||
1,1,1,1,1,1,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,1,1,1,1,1,1,1,1,1, |
||||
1,1,1,1,1,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,1,1,1,1,1,1,1,1,1,1, |
||||
1,1,1,1,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,1,1,1,1,1,1,1,1,1,1,1, |
||||
1,1,1,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,1,1,1,1,1,1,1,1,1,1,1,1, |
||||
1,1,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,1,1,1,1,1,1,1,1,1,1,1,1,1, |
||||
1,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,1,1,1,1,1,1,1,1,1,1,1,1,1,1, |
||||
}; |
||||
|
||||
for(int i = 0; i != hash.cols; ++i) |
||||
{ |
||||
std::bitset<8> const bits = hash.at<uchar>(0, i); |
||||
if(i != hash.cols-1) |
||||
{ |
||||
for(size_t j = 0; j != bits.size(); ++j) |
||||
{ |
||||
EXPECT_EQ(expectResult[i*8+j], bits[j]); |
||||
} |
||||
} |
||||
else |
||||
{ |
||||
//when mode == 1, there will be 961 block mean
|
||||
//that is why we only check one bit at here
|
||||
EXPECT_EQ(expectResult[i*8], bits[0]); |
||||
} |
||||
} |
||||
} |
||||
|
||||
void CV_BlockMeanHashTest::run(int) |
||||
{ |
||||
bmh->compute(input, hash); |
||||
testMeanMode0(); |
||||
testHashMode0(); |
||||
|
||||
bmh->setMode(BLOCK_MEAN_HASH_MODE_1); |
||||
bmh->compute(input, hash); |
||||
testMeanMode1(); |
||||
testHashMode1(); |
||||
} |
||||
|
||||
TEST(block_mean_hash_test, accuracy) { CV_BlockMeanHashTest test; test.safe_run(); } |
@ -0,0 +1,7 @@ |
||||
// This file is part of OpenCV project.
|
||||
// It is subject to the license terms in the LICENSE file found in the top-level directory
|
||||
// of this distribution and at http://opencv.org/license.html.
|
||||
|
||||
#include "test_precomp.hpp" |
||||
|
||||
CV_TEST_MAIN("cv") |
@ -0,0 +1,60 @@ |
||||
// This file is part of OpenCV project.
|
||||
// It is subject to the license terms in the LICENSE file found in the top-level directory
|
||||
// of this distribution and at http://opencv.org/license.html.
|
||||
|
||||
#include "test_precomp.hpp" |
||||
|
||||
using namespace cv; |
||||
|
||||
class CV_MarrHildrethTest : public cvtest::BaseTest |
||||
{ |
||||
public: |
||||
CV_MarrHildrethTest(); |
||||
~CV_MarrHildrethTest(); |
||||
protected: |
||||
void run(int /* idx */); |
||||
}; |
||||
|
||||
CV_MarrHildrethTest::CV_MarrHildrethTest(){} |
||||
CV_MarrHildrethTest::~CV_MarrHildrethTest(){} |
||||
|
||||
void CV_MarrHildrethTest::run(int ) |
||||
{ |
||||
cv::Mat_<uchar> input(512,512); |
||||
int val = 0; |
||||
for(int row = 0; row != input.rows; ++row) |
||||
{ |
||||
for(int col = 0; col != input.cols; ++col) |
||||
{ |
||||
input.at<uchar>(row, col) = val % 256; |
||||
++val; |
||||
} |
||||
} |
||||
|
||||
cv::Mat hash; |
||||
cv::img_hash::marrHildrethHash(input, hash); |
||||
uchar const expectResult[] = |
||||
{ |
||||
252, 126, 63, 31, 143, 199, 227, 241, |
||||
248, 252, 126, 63, 31, 143, 199, 227, |
||||
241, 248, 252, 126, 63, 31, 143, 199, |
||||
227, 241, 248, 252, 126, 63, 31, 143, |
||||
199, 227, 241, 248, 31, 143, 199, 227, |
||||
241, 248, 252, 126, 63, 252, 126, 63, |
||||
31, 143, 199, 227, 241, 248, 252, 126, |
||||
63, 31, 143, 199, 227, 241, 248, 252, |
||||
126, 63, 31, 143, 199, 227, 241, 248 |
||||
}; |
||||
uchar const *hashPtr = hash.ptr<uchar>(0); |
||||
for(int i = 0; i != 72; ++i) |
||||
{ |
||||
if(hashPtr[i] != expectResult[i]) |
||||
{ |
||||
ts->printf(cvtest::TS::LOG, "Wrong hash value \n"); |
||||
ts->set_failed_test_info(cvtest::TS::FAIL_INVALID_TEST_DATA); |
||||
return; |
||||
} |
||||
} |
||||
} |
||||
|
||||
TEST(marr_hildreth_test, accuracy) { CV_MarrHildrethTest test; test.safe_run(); } |
@ -0,0 +1,58 @@ |
||||
// This file is part of OpenCV project.
|
||||
// It is subject to the license terms in the LICENSE file found in the top-level directory
|
||||
// of this distribution and at http://opencv.org/license.html.
|
||||
|
||||
#include "test_precomp.hpp" |
||||
|
||||
#include <bitset> |
||||
|
||||
using namespace cv; |
||||
|
||||
class CV_PHashTest : public cvtest::BaseTest |
||||
{ |
||||
public: |
||||
CV_PHashTest(); |
||||
~CV_PHashTest(); |
||||
protected: |
||||
void run(int /* idx */); |
||||
}; |
||||
|
||||
CV_PHashTest::CV_PHashTest(){} |
||||
CV_PHashTest::~CV_PHashTest(){} |
||||
|
||||
void CV_PHashTest::run(int ) |
||||
{ |
||||
cv::Mat input(32, 32, CV_8U); |
||||
cv::Mat hash; |
||||
|
||||
uchar value = 0; |
||||
uchar *inPtr = input.ptr<uchar>(0); |
||||
for(size_t i = 0; i != 32*32; ++i) |
||||
{ |
||||
inPtr[i] = value++; |
||||
} |
||||
|
||||
cv::img_hash::pHash(input, hash); |
||||
bool const expectResult[] = |
||||
{ |
||||
1,0,1,1,1,1,1,1, |
||||
0,1,1,1,1,1,1,1, |
||||
1,1,1,1,1,1,1,1, |
||||
0,1,1,1,1,1,1,1, |
||||
1,1,1,1,1,1,1,1, |
||||
0,1,1,1,1,1,1,1, |
||||
1,1,1,1,1,1,1,1, |
||||
0,1,1,1,1,1,1,1, |
||||
}; |
||||
uchar const *hashPtr = hash.ptr<uchar>(0); |
||||
for(int i = 0; i != hash.cols; ++i) |
||||
{ |
||||
std::bitset<8> const bits = hashPtr[i]; |
||||
for(int j = 0; j != 8; ++j) |
||||
{ |
||||
EXPECT_EQ(bits[j], expectResult[i*8+j]); |
||||
} |
||||
} |
||||
} |
||||
|
||||
TEST(average_phash_test, accuracy) { CV_PHashTest test; test.safe_run(); } |
@ -0,0 +1,20 @@ |
||||
#ifdef __GNUC__ |
||||
# pragma GCC diagnostic ignored "-Wmissing-declarations" |
||||
# if defined __clang__ || defined __APPLE__ |
||||
# pragma GCC diagnostic ignored "-Wmissing-prototypes" |
||||
# pragma GCC diagnostic ignored "-Wextra" |
||||
# endif |
||||
#endif |
||||
|
||||
#ifndef __OPENCV_TEST_PRECOMP_HPP__ |
||||
#define __OPENCV_TEST_PRECOMP_HPP__ |
||||
|
||||
#include <iostream> |
||||
#include "opencv2/ts.hpp" |
||||
#include "opencv2/img_hash.hpp" |
||||
#include "opencv2/imgproc.hpp" |
||||
#include "opencv2/core/cvdef.h" |
||||
#include "opencv2/core.hpp" |
||||
#include "opencv2/highgui.hpp" |
||||
|
||||
#endif |
@ -0,0 +1,150 @@ |
||||
// This file is part of OpenCV project.
|
||||
// It is subject to the license terms in the LICENSE file found in the top-level directory
|
||||
// of this distribution and at http://opencv.org/license.html.
|
||||
|
||||
#include "test_precomp.hpp" |
||||
|
||||
using namespace cv; |
||||
using namespace cv::img_hash; |
||||
|
||||
|
||||
/**
|
||||
*The expected results of this test case are come from the phash library, |
||||
*I use it as golden model |
||||
*/ |
||||
class CV_RadialVarianceHashTest : public cvtest::BaseTest |
||||
{ |
||||
public: |
||||
CV_RadialVarianceHashTest(); |
||||
protected: |
||||
void run(int /* idx */); |
||||
|
||||
//this test case do not use the original "golden data"
|
||||
//of pHash library, I add a small value to nb_pixels in
|
||||
//the function "ph_feature_vector" to avoid NaN value
|
||||
void testComputeHash(); |
||||
void testFeatures(); |
||||
void testHash(); |
||||
void testPixPerLine(); |
||||
void testProjection(); |
||||
|
||||
cv::Mat input; |
||||
Ptr<cv::img_hash::RadialVarianceHash> rvh; |
||||
}; |
||||
|
||||
CV_RadialVarianceHashTest::CV_RadialVarianceHashTest() |
||||
{ |
||||
input.create(8, 8, CV_8U); |
||||
uchar *inPtr = input.ptr<uchar>(0); |
||||
for(size_t i = 0; i != input.total(); ++i) |
||||
{ |
||||
inPtr[i] = static_cast<uchar>(i); |
||||
} |
||||
rvh = RadialVarianceHash::create(1, 10); |
||||
} |
||||
|
||||
void CV_RadialVarianceHashTest::testComputeHash() |
||||
{ |
||||
cv::Mat hashOne(1, 40, CV_8U); |
||||
uchar buffer[] = |
||||
{ |
||||
52, 41, 49, 64, 40, 67, 76, 71, 69, |
||||
55, 58, 68, 72, 78, 63, 73, 66, 77, |
||||
60, 57, 48, 59, 62, 74, 70, 47, 46, |
||||
51, 45, 44, 42, 61, 54, 75, 50, 79, |
||||
65, 43, 53, 56 |
||||
}; |
||||
cv::Mat hashTwo(1, 40, CV_8U, buffer); |
||||
for(uchar i = 0; i != 40; ++i) |
||||
{ |
||||
hashOne.at<uchar>(0, i) = i; |
||||
} |
||||
|
||||
double const actual = rvh->compare(hashOne, hashTwo); |
||||
ASSERT_NEAR(0.481051, actual, 0.0001); |
||||
} |
||||
|
||||
void CV_RadialVarianceHashTest::testFeatures() |
||||
{ |
||||
std::vector<double> const &features = rvh->getFeatures(); |
||||
double const expectResult[] = |
||||
{-1.35784,-0.42703,0.908487,-1.39327,1.17313, |
||||
1.47515,-0.0156121,0.774335,-0.116755,-1.02059}; |
||||
for(size_t i = 0; i != features.size(); ++i) |
||||
{ |
||||
ASSERT_NEAR(features[i], expectResult[i], 0.0001); |
||||
} |
||||
} |
||||
|
||||
void CV_RadialVarianceHashTest::testHash() |
||||
{ |
||||
cv::Mat const hash = rvh->getHash(); |
||||
uchar const expectResult[] = |
||||
{ |
||||
127, 92, 0, 158, 101, |
||||
88, 14, 136, 227, 160, |
||||
127, 94, 27, 118, 240, |
||||
166, 153, 96, 254, 162, |
||||
127, 162, 255, 96, 153, |
||||
166, 240, 118, 27, 94, |
||||
127, 160, 227, 136, 14, |
||||
88, 101, 158, 0, 92 |
||||
}; |
||||
for(int i = 0; i != hash.cols; ++i) |
||||
{ |
||||
EXPECT_EQ(hash.at<uchar>(0, i), expectResult[i]); |
||||
} |
||||
} |
||||
|
||||
void CV_RadialVarianceHashTest::testPixPerLine() |
||||
{ |
||||
cv::Mat const pixPerLine = rvh->getPixPerLine(input); |
||||
uchar const expectResult[] = |
||||
{ |
||||
8,8,8,0,8,15,7,5,8,8, |
||||
}; |
||||
bool const equal = |
||||
std::equal(expectResult, expectResult + pixPerLine.total(), |
||||
pixPerLine.ptr<int>(0)); |
||||
if(equal == false) |
||||
{ |
||||
ts->printf(cvtest::TS::LOG, "Wrong pixel per line value \n"); |
||||
ts->set_failed_test_info(cvtest::TS::FAIL_INVALID_TEST_DATA); |
||||
} |
||||
} |
||||
|
||||
void CV_RadialVarianceHashTest::testProjection() |
||||
{ |
||||
cv::Mat const proj = rvh->getProjection(); |
||||
uchar const expectResult[] = |
||||
{ |
||||
32, 33, 34, 35, 36, 37, 38, 39, |
||||
16, 17, 18, 27, 36, 37, 46, 47, |
||||
0, 9, 18, 19, 36, 45, 46, 55, |
||||
0, 0, 0, 0, 0, 0, 0, 0, |
||||
2, 10, 18, 27, 36, 44, 53, 61, |
||||
4, 59, 51, 44, 36, 29, 22, 14, |
||||
0, 58, 51, 43, 36, 30, 22, 15, |
||||
0, 0, 58, 43, 36, 21, 6, 0, |
||||
56, 49, 42, 43, 36, 21, 22, 15, |
||||
40, 41, 42, 35, 36, 29, 22, 23 |
||||
}; |
||||
bool const equal = |
||||
std::equal(expectResult, expectResult + proj.total(), |
||||
proj.ptr<uchar>(0)); |
||||
if(equal == false) |
||||
{ |
||||
ts->printf(cvtest::TS::LOG, "Wrong projection value \n"); |
||||
ts->set_failed_test_info(cvtest::TS::FAIL_INVALID_TEST_DATA); |
||||
} |
||||
} |
||||
|
||||
void CV_RadialVarianceHashTest::run(int) |
||||
{ |
||||
testPixPerLine(); |
||||
testProjection(); |
||||
testFeatures(); |
||||
testComputeHash(); |
||||
} |
||||
|
||||
TEST(radial_variance_hash_test, accuracy) { CV_RadialVarianceHashTest test; test.safe_run(); } |
Loading…
Reference in new issue