Background
画像の類似度を比較する手法として画像をハッシュ値に変換してハミング距離で計算する方法があります。今回はOpenCVでハッシュ値を変換しようと思います。
ImageHash
アルゴリズムの元ネタはhackerfactorのようですが、これをベースにしたライブラリはPythonでは imagehash
が主流みたいです。種類としては ahash
phash
dhash
whash
の4つが存在します。
テストデータ
必要なパッケージ
from PIL import Image
import imagehash
name | code | result |
---|---|---|
average hashing | imagehash.average_hash(Image.open('Mandrill.png')) | 01833f3cbc98ebfc |
perception hashing | imagehash.phash(Image.open('Mandrill.png')) | df20607d1fa0d88f |
difference hashing | imagehash.dhash(Image.open('Mandrill.png')) | 1fabea6869305668 |
wavelet hashing | imagehash.whash(Image.open('Mandrill.png')) | 01433f3cbc98e2fc |
で、Python版のOpenCVにハッシュ値を変換するモジュールがあるかというと、、、、ありません
OpenCV(C++)
ですが、C++版にはモジュールが入っています。メソッドの種類は上記で挙げたImageHashとは異なります。
- AverageHash
- PHash
- MarrHildrethHash
- RadialVarianceHash
- BlockMeanHash
- ColorMomentHash
とりあえず、メソッドを元に出力値を出してみます。
Development
The module brings implementations of different image hashing algorithms.にあるコードを参考に実装します。
LDLIBS = -lopencv_core -lopencv_imgcodecs -lopencv_imgproc -lopencv_img_hash
#include <opencv2/opencv.hpp>
#include <opencv2/imgproc.hpp>
#include <opencv2/img_hash.hpp>
#include <cxxabi.h>
template <typename T> inline void output_hash(const cv::Mat &src)
{
cv::Mat dst;
cv::Ptr<cv::img_hash::ImgHashBase> func;
func = T::create();
func->compute(src, dst);
std::string name = abi::__cxa_demangle(typeid(T).name(), 0, 0, 0);
std::cout << name << "\t" << dst << std::endl;
}
int main( int argc, char** argv )
{
cv::Mat imageMat = cv::imread("./Mandrill.png", cv::IMREAD_COLOR );
output_hash<cv::img_hash::AverageHash>(imageMat);
output_hash<cv::img_hash::PHash>(imageMat);
output_hash<cv::img_hash::MarrHildrethHash>(imageMat);
output_hash<cv::img_hash::RadialVarianceHash>(imageMat);
output_hash<cv::img_hash::BlockMeanHash>(imageMat);
output_hash<cv::img_hash::ColorMomentHash>(imageMat);
return 0;
}
Result
cv::img_hash::AverageHash [ 9, 129, 239, 54, 53, 3, 195, 229]
cv::img_hash::PHash [249, 4, 6, 190, 240, 133, 27, 249]
cv::img_hash::MarrHildrethHash [240, 120, 59, 19, 49, 56, 156, 14, 89, 5, 137, 192, 228, 177, 255, 211, 208, 27, 37, 143, 188, 28, 14, 71, 98, 109, 192, 102, 77, 196, 227, 241, 57, 121, 242, 27, 164, 237, 191, 31, 143, 199, 100, 214, 115, 100, 236, 196, 193, 240, 56, 127, 108, 182, 108, 252, 60, 29, 142, 6, 147, 192, 75, 3, 137, 193, 229, 240, 126, 114, 112, 201]
cv::img_hash::RadialVarianceHash [ 69, 62, 255, 42, 193, 80, 150, 70, 0, 104, 38, 66, 73, 101, 61, 64, 81, 81, 82, 52, 56, 71, 52, 60, 63, 84, 65, 56, 70, 68, 72, 67, 65, 74, 78, 65, 55, 67, 63, 70]
cv::img_hash::BlockMeanHash [ 2, 96, 2, 64, 6, 96, 6, 230, 102, 238, 116, 126, 240, 111, 240, 14, 241, 7, 97, 7, 67, 3, 139, 3, 139, 177, 63, 190, 255, 63, 247, 15]
cv::img_hash::ColorMomentHash [0.002503659152184849, 2.471939932761336e-08, 4.467702288443572e-11, 2.753929884994291e-11, 3.907790057002085e-22, -4.24497491111126e-15, -8.834156975661139e-22, 0.001648706395707542, 4.497051261680711e-09, 3.146007555704344e-11, 1.673749393818875e-11, 3.312616975083822e-23, 1.117844884043978e-15, 3.826433279309091e-22, 0.000964146950520186, 3.507447150328149e-09, 4.712173150861858e-13, 1.510627115300825e-12, -1.25450023857693e-24, 3.736683447133399e-17, 2.250099503660287e-25, 0.001264405759998173, 4.013957543909037e-09, 7.996919537752477e-13, 9.256331740589708e-13, -5.433089299218264e-25, -2.969719150917409e-17, -5.822668758123276e-25, 0.001221333669445427, 6.085465009873859e-10, 2.11537441698962e-14, 4.10817520446575e-12, -6.598046593374036e-25, 8.906807344893997e-17, 1.015545954823273e-24, 0.001350832788790067, 7.253752695174111e-10, 4.409337297754683e-13, 1.21565621533617e-12, -5.606880832077259e-26, -2.143611937655263e-17, 8.882584320430919e-25]
ImageHash
と共通としてあるメソッドがAverageHash``PHash
の2つですが結構値が異なります。
aHash:01833f3cbc98ebfc
pHash:df20607d1fa0d88f
をそれぞれ16進数から10進数に直すと [1, 131, 63, 60, 188, 152, 235, 252]
[223, 32, 96, 125, 31, 160, 216, 143]
になります。
同じメソッド名でもモジュールが異なるとアルゴリズムが少し違うみたいです。
おわりに
ハッシュ値は出力できたので次は画像の類似度を計算してみます。どのようにハッシュ値にしているかはPerceptual Hashを使って画像の類似度を計算してみるが一番丁寧に説明していて参考になります。