0
点赞
收藏
分享

微信扫一扫

[OpenCV实战]45 基于OpenCV实现图像哈希算法


目前有许多算法来衡量两幅图像的相似性,本文主要介绍在工程领域最常用的图像相似性算法评价算法:图像哈希算法(img hash)。图像哈希算法通过获取图像的哈希值并比较两幅图像的哈希值的汉明距离来衡量两幅图像是否相似。两幅图像越相似,其哈希值的汉明距离越小,通过这种方式就能够比较两幅图像是否相似。在实际应用中,图像哈希算法可以用于图片检索,重复图片剔除,以图搜图以及图片相似度比较。

为什么图像哈希算法能够评估两幅图像的相似性,这就需要从哈希值说起,哈希值计算算法的本质就是对原始数据进行有损压缩,有损压缩后的固定字长能够作为唯一标识来标识原始数据,这个唯一标识就是哈希值。通常改变原始数据任意一个部分,哈希值都将不同。关于哈希值的具体介绍见:

​​通俗地理解哈希函数​​

但是计算图像哈希值的方法并不唯一,因此有不同的图像哈希计算方法。OpenCV contrib库中的img_hash模块提供计算两种图像的哈希值并比较两张图像相似性的算法。img_hash模块主要移植自PHash库,其官方代码仓库介绍见:

​​Image Hashing algorithms​​

img_hash模块提供了多种图像哈希算法,具体介绍如下:

  • 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))

PHash是工程实践最常用的图像哈希算法。本文主要介绍img_hash中的几种哈希算法的使用,关于图像哈希进一步介绍见:

​​图片哈希概述(image hash)​​

本文需要OpenCV contrib库,OpenCV contrib库的编译安装见:

​​OpenCV_contrib库在windows下编译使用指南​​

本文所有代码见:

​​OpenCV-Practical-Exercise​​

文章目录

  • ​​1 方法说明与代码实现​​
  • ​​1.1 方法说明​​
  • ​​1.2 代码实现​​
  • ​​1.3 方法比较与选择​​
  • ​​2 参考​​
  • ​​2.1 参考代码​​
  • ​​2.2 相关文档​​
  • ​​2.3 相关文献​​

1 方法说明与代码实现

1.1 方法说明

图像哈希算法计算过程如下图所示,以pHash为例,先将将图片缩小到8x8的尺寸,总共64个像素,然后通过pHash函数计算hash值,得到一个唯一标识码hash码,hash码包括8个uint8数值,组合在一起,就构成了一个64位的整数。

[OpenCV实战]45 基于OpenCV实现图像哈希算法_代码实现

pHash可以比较不同大小图像的hash值。通过计算两幅图像hash码的汉明距离,得到结果如下表所示:

img1

img2

img3

img1

0

img2

1.0

0

img3

29.0

30.0

0

汉明距离越小,表示两幅图像越接近,可以看到img1和img2最相近,img2为img1的灰色版本。

OpenCV img_hash模块各种哈希算法的特点和文献如下所示:

  1. ​​AverageHash​​ 基于像素均值计算哈希值,一种快速的图像哈希算法,但仅适用于简单情况。
  2. ​​PHash​​ AverageHash的改进版,比AverageHash慢,但可以适应更多的情况。
  3. ​​MarrHildrethHash​​ 基于Marr-Hildreth边缘算子计算哈希值,速度最慢,但更具区分性。
  4. ​​RadialVarianceHash​​ 基于Radon变换计算哈希值
  5. ​​BlockMeanHash​​ 基于块均值计算哈希值,与MarrHildrethHash在同一篇文章介绍。
  6. ​​ColorMomentHash​​ 基于颜色矩计算哈希值,与RadialVarianceHash在同一篇文章介绍。

本文提供img_hash模块的C++和Python代码示例。实际调用方法如下:

C++

// 创建AverageHash类
Ptr<AverageHash> func= AverageHash::create;
// 计算图a的哈希值
func->compute(a, hashA);
// 计算图b的哈希值
func->compute(b, hashB);
// 比较两张图像哈希值的距离
func->compare(hashA, hashB);

Python

# 创建类
hashFun = cv2.img_hash.AverageHash_create()
# 计算图a的哈希值
hashA = hashFun.compute(a)
# 计算图b的哈希值
hashB = hashFun.compute(b)
# 比较两张图像哈希值的距离
hashFun.compare(hashA, hashB)

1.2 代码实现

C++和Python实现都分别提供,结果如1.1所示,但是C++代码用了类模板。通过ImgHashBase基础类,能够实现代码重复使用。

代码测试的图像已经在1.1部分展示,img1为基准图像,img2为img1的灰色版本,img3是另外一张完全不同的彩色图。

C++

#include <opencv2/opencv.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();
// 计算图a的哈希值
func->compute(a, hashA);
tick.stop();
cout << "compute1: " << tick.getTimeMilli() << " ms" << endl;

tick.reset();
tick.start();
// 计算图b的哈希值
func->compute(b, hashB);
tick.stop();
cout << "compute2: " << tick.getTimeMilli() << " ms" << endl;

// 比较两张图像哈希值的距离
cout << "compare: " << func->compare(hashA, hashB) << endl << endl;
}

int main()
{
// 打开两张图像进行相似度比较
Mat input = imread("./image/img1.jpg");
Mat target = imread("./image/img2.jpg");

// 通过不同方法比较图像相似性
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);
test_one<ColorMomentHash>("ColorMomentHash", input, target);

system("pause");
return 0;
}

Python

# -*- coding: utf-8 -*-
"""
Created on Thu Aug 27 19:03:21 2020

@author: luohenyueji
"""

import cv2


def test_one(title, a, b):
# 创建类
if "AverageHash" == title:
hashFun = cv2.img_hash.AverageHash_create()
elif "PHash" == title:
hashFun = cv2.img_hash.PHash_create()
elif "MarrHildrethHash" == title:
hashFun = cv2.img_hash.MarrHildrethHash_create()
elif "RadialVarianceHash" == title:
hashFun = cv2.img_hash.RadialVarianceHash_create()
elif "BlockMeanHash" == title:
hashFun = cv2.img_hash.BlockMeanHash_create()
elif "ColorMomentHash" == title:
hashFun = cv2.img_hash.ColorMomentHash_create()

tick = cv2.TickMeter()
print("=== " + title + " ===")

tick.reset()
tick.start()
# # 计算图a的哈希值
hashA = hashFun.compute(a)
tick.stop()
print("compute1: " + str(tick.getTimeMilli()) + " ms")

tick.reset()
tick.start()
# 计算图b的哈希值
hashB = hashFun.compute(b)
tick.stop()
print("compute2: " + str(tick.getTimeMilli()) + " ms")
# 比较两张图像哈希值的距离
print("compare: " + str(hashFun.compare(hashA, hashB)))


def main():
inputImg = cv2.imread("./image/img1.jpg")
targetImg = cv2.imread("./image/img2.jpg")

if inputImg is None or targetImg is None:
print("check input image")
return

test_one("AverageHash", inputImg, targetImg)
test_one("PHash", inputImg, targetImg)
test_one("MarrHildrethHash", inputImg, targetImg)
test_one("RadialVarianceHash", inputImg, targetImg)
test_one("BlockMeanHash", inputImg, targetImg)
test_one("ColorMomentHash", inputImg, targetImg)


if __name__ == '__main__':
main()

1.3 方法比较与选择

以img1为基准,img2与img1对比结果和img3与img1对比结果如下表所示。可以看到RadialVarianceHash和ColorMomentHash结果与事实不符,这主要因为RadialVarianceHash和ColorMomentHash通过像素点颜色值信息来计算哈希值,img2是灰色图与img1相差过大。

此外可以看到各种图像哈希算法的计算速度,在实际中PHash是个很不错的选择,快速且效果好。

img1/img2

img1/img3

result

speed/ms

AverageHash

3

31

TRUE

0.0565

PHash

1

29

TRUE

0.072

MarrHildrethHash

28

283

TRUE

9.8433

RadialVarianceHash

0.989896

0.543267

FALSE

1.0259

BlockMeanHash

10

113

TRUE

0.694

ColorMomentHash

45.4928

16.7632

FALSE

3.39

然而,PHash常用,并不代表其他算法没用。比如如果将img1水平翻转得到img4,如下图所示。那么将会得到完全不一样的结果,如下表所示。

[OpenCV实战]45 基于OpenCV实现图像哈希算法_代码实现_02

img1/img3

img1/img4

result

AverageHash

31

36

FALSE

PHash

29

36

FALSE

MarrHildrethHash

283

301

FALSE

RadialVarianceHash

0.543267

0.285715

TRUE

BlockMeanHash

113

139

FALSE

ColorMomentHash

16.7632

0.270448

TRUE

导致以上情况的主要原因是,RadialVarianceHash和ColorMomentHash基于全局信息来计算hash值,其他算法基于局部信息来计算hash值。

总之在实际应用中,通过图像哈希值计算图像相似性比较粗糙,但是图像哈希值也是比较常用的图像相似性比较算法。现在图像相似性比较算法效果都很一般,即使用了深度学习如Siamese Network,效果也没有太大提高。因此在计算相似性前,都会进行图像对准和颜色转换,这一点是非常必要的。不过图像哈希算法在实际中计算固定场景效果还是很不错的。

2 参考

2.1 参考代码

​​Image Hashing algorithms​​

2.2 相关文档

​​OpenCV img_hash​​

​​通俗地理解哈希函数​​

​​图片哈希概述(image hash)​​

2.3 相关文献

​​AverageHash​​

​​PHash​​

​​MarrHildrethHash​​

​​RadialVarianceHash​​

​​BlockMeanHash​​

​​ColorMomentHash​​


举报

相关推荐

0 条评论