vendor: OpenCV 5.0.0 snapshot at 755e50675d97db9b7d449d8bd6b09888646f6c6e
This commit is contained in:
@@ -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_geometry opencv_core WRAP python java objc)
|
||||
@@ -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.
|
||||
Binary file not shown.
|
After Width: | Height: | Size: 110 KiB |
Binary file not shown.
|
After Width: | Height: | Size: 44 KiB |
Binary file not shown.
|
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 the 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 the 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,11 @@
|
||||
{
|
||||
"AdditionalImports" : {
|
||||
"*" : [ "\"img_hash.hpp\"" ]
|
||||
},
|
||||
"func_arg_fix" : {
|
||||
"BlockMeanHash" : {
|
||||
"create" : { "mode" : {"ctype" : "BlockMeanHashMode"} },
|
||||
"setMode" : { "mode" : {"ctype" : "BlockMeanHashMode"} }
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -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,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.
|
||||
|
||||
#include "precomp.hpp"
|
||||
|
||||
using namespace cv;
|
||||
using namespace std;
|
||||
using namespace img_hash;
|
||||
|
||||
namespace {
|
||||
|
||||
class AverageHashImpl CV_FINAL : public ImgHashBase::ImgHashImpl
|
||||
{
|
||||
private:
|
||||
cv::Mat bitsImg;
|
||||
cv::Mat grayImg;
|
||||
cv::Mat resizeImg;
|
||||
|
||||
public:
|
||||
|
||||
virtual void compute(cv::InputArray inputArr, cv::OutputArray outputArr) CV_OVERRIDE
|
||||
{
|
||||
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), 0, 0, INTER_LINEAR_EXACT);
|
||||
if(input.channels() > 1)
|
||||
cv::cvtColor(resizeImg, grayImg, COLOR_BGR2GRAY);
|
||||
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 CV_OVERRIDE
|
||||
{
|
||||
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,159 @@
|
||||
// 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 CV_FINAL : public ImgHashBase::ImgHashImpl
|
||||
{
|
||||
public:
|
||||
BlockMeanHashImpl(int mode)
|
||||
{
|
||||
setMode(mode);
|
||||
}
|
||||
|
||||
~BlockMeanHashImpl() CV_OVERRIDE {}
|
||||
|
||||
virtual void compute(cv::InputArray inputArr, cv::OutputArray outputArr) CV_OVERRIDE
|
||||
{
|
||||
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), 0, 0, INTER_LINEAR_EXACT);
|
||||
if(input.channels() > 1)
|
||||
cv::cvtColor(resizeImg_, grayImg_, COLOR_BGR2GRAY);
|
||||
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 CV_OVERRIDE
|
||||
{
|
||||
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,94 @@
|
||||
// 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 CV_FINAL : public ImgHashBase::ImgHashImpl
|
||||
{
|
||||
public:
|
||||
~ColorMomentHashImpl() CV_OVERRIDE {}
|
||||
|
||||
virtual void compute(cv::InputArray inputArr, cv::OutputArray outputArr) CV_OVERRIDE
|
||||
{
|
||||
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_, COLOR_BGRA2BGR);
|
||||
}
|
||||
else
|
||||
{
|
||||
cv::cvtColor(input, colorImg_, COLOR_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_, COLOR_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_, COLOR_BGR2YCrCb);
|
||||
cv::split(colorSpace_, channels_);
|
||||
computeMoments(hash.ptr<double>(0) + 21);
|
||||
}
|
||||
|
||||
virtual double compare(cv::InputArray hashOne, cv::InputArray hashTwo) const CV_OVERRIDE
|
||||
{
|
||||
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,205 @@
|
||||
// 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 CV_FINAL : 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() CV_OVERRIDE { }
|
||||
|
||||
virtual void compute(cv::InputArray inputArr, cv::OutputArray outputArr) CV_OVERRIDE
|
||||
{
|
||||
cv::Mat const input = inputArr.getMat();
|
||||
CV_Assert(input.type() == CV_8UC4 ||
|
||||
input.type() == CV_8UC3 ||
|
||||
input.type() == CV_8U);
|
||||
|
||||
if(input.channels() > 1)
|
||||
cv::cvtColor(input, grayImg, COLOR_BGR2GRAY);
|
||||
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 CV_OVERRIDE
|
||||
{
|
||||
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,85 @@
|
||||
// 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 CV_FINAL : public ImgHashBase::ImgHashImpl
|
||||
{
|
||||
public:
|
||||
virtual void compute(cv::InputArray inputArr, cv::OutputArray outputArr) CV_OVERRIDE
|
||||
{
|
||||
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), 0, 0, INTER_LINEAR_EXACT);
|
||||
if(input.channels() > 1)
|
||||
cv::cvtColor(resizeImg, grayImg, COLOR_BGR2GRAY);
|
||||
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 CV_OVERRIDE
|
||||
{
|
||||
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/geometry.hpp"
|
||||
#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 CV_FINAL : 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() CV_OVERRIDE {}
|
||||
|
||||
virtual void compute(cv::InputArray inputArr, cv::OutputArray outputArr) CV_OVERRIDE
|
||||
{
|
||||
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_, COLOR_BGR2GRAY);
|
||||
}
|
||||
else if(input.type() == CV_8UC4)
|
||||
{
|
||||
cv::cvtColor(input, grayImg_, COLOR_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_OVERRIDE
|
||||
{
|
||||
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.setTo(cv::Scalar::all(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_.setTo(cv::Scalar::all(0));
|
||||
pixPerLine_.create(1, numOfAngelLine_, CV_32S);
|
||||
pixPerLine_.setTo(cv::Scalar::all(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,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"
|
||||
|
||||
#include <bitset>
|
||||
|
||||
namespace opencv_test { namespace {
|
||||
|
||||
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(); }
|
||||
|
||||
}} // namespace
|
||||
@@ -0,0 +1,214 @@
|
||||
// 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>
|
||||
|
||||
namespace opencv_test { namespace {
|
||||
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(); }
|
||||
|
||||
}} // namespace
|
||||
@@ -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,62 @@
|
||||
// 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"
|
||||
|
||||
namespace opencv_test { namespace {
|
||||
|
||||
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(); }
|
||||
|
||||
}} // namespace
|
||||
@@ -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"
|
||||
|
||||
#include <bitset>
|
||||
|
||||
namespace opencv_test { namespace {
|
||||
|
||||
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(); }
|
||||
|
||||
}} // namespace
|
||||
@@ -0,0 +1,10 @@
|
||||
// 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_TEST_PRECOMP_HPP__
|
||||
#define __OPENCV_TEST_PRECOMP_HPP__
|
||||
|
||||
#include "opencv2/ts.hpp"
|
||||
#include "opencv2/img_hash.hpp"
|
||||
|
||||
#endif
|
||||
@@ -0,0 +1,154 @@
|
||||
// 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"
|
||||
|
||||
namespace opencv_test { namespace {
|
||||
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(); // TODO unused
|
||||
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);
|
||||
}
|
||||
}
|
||||
|
||||
#if 0 // unused
|
||||
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]);
|
||||
}
|
||||
}
|
||||
#endif
|
||||
|
||||
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(); }
|
||||
|
||||
}} // namespace
|
||||
Reference in New Issue
Block a user