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