Python-OpenCV 比较图片相似度

一、图片相似比较

本文介绍的几种实现方式:

  • 暴力方式:1.按像素比较,n越小相似度越高;2.矩阵相减,用来判断两个图是不是完全一样;hashlib.md5判断两个图是否完全一样
  • 哈希算法:1.感知哈希算法;2.均值哈希算法;3.差值哈希算法
  • 灰度直方图:1.单通道直方图;2. 三通道直方图

二、暴力方式

1. 按像素比较

# -*- coding: utf-8 -*-
# !/usr/bin/python
import cv2

n = 0
img1 = cv2.imread('test.jpg', cv2.IMREAD_GRAYSCALE)
img2 = cv2.imread('test.png', cv2.IMREAD_GRAYSCALE)
height, width = img1.shape
for line in range(height):
for pixel in range(width):
if img1[line][pixel] != img2[line][pixel]:
n = n + 1
print(n)

2. OpenCV2 矩阵相减

  • 两个图片完全一样时返回值为true
  • 如果把一个图片转了格式,则结果可能不一样,因为转格式的过程中可能会进行算法处理

3. 通过hashlib.md5判断图片是否完全一样

import hashlib

a = open("test.png", "rb")
a_cp = open("test.jpg", 'rb')

cmd5 = hashlib.md5(a.read()).hexdigest()
ccmd5 = hashlib.md5(a_cp.read()).hexdigest()

print(cmd5)
if cmd5 == ccmd5:
print(True)
else:
print(False)

三、哈希算法

算法值越小,则越相似,相同图片值为0。

  • pHash:感知哈希。精确度比较高,但是速度方面较差一些。
  • aHash:平均值哈希。速度比较快,但是常常不太精确。
  • dHash:差异值哈希。精确度较高,且速度也非常快

1. 感知哈希算法(Perceptual hash algorithm)

步骤:

  1. 缩小图片
  2. 转为灰度图(256阶)
  3. 计算DCT:把图片分享成分率的集合,结果是32*32的矩阵
  4. 缩小DCT:保留左上角的8*8,代表图片的最低频率
  5. 计算缩小DCT后的像素点均值
  6. 大于均值记为1,反之记为0,组合64个信息位

7 比较图片指纹,获得汉明距离,即两个64位的hash值有多少位是不一样的,不相同位数越少,图片越相似。

代码

import cv2
import numpy as np
from itertools import chain


def pHash(img_name):
"""
get image pHash value
"""
# 加载并调整图片为32x32灰度图片
img = cv2.imread(img_name, 0)
img = cv2.resize(img, (64, 64), interpolation=cv2.INTER_CUBIC)

# 创建二维列表
h, w = img.shape[:2]
vis0 = np.zeros((h, w), np.float32)
vis0[:h, :w] = img # 填充数据

# 二维Dct变换
vis1 = cv2.dct(cv2.dct(vis0))
vis1.resize((32, 32), refcheck=False)

# 把二维list变成一维list
img_list = list(chain.from_iterable(vis1))

# 计算均值
avg = sum(img_list) * 1. / len(img_list)
avg_list = ['0' if i < avg else '1' for i in img_list]

# 得到哈希值
return ''.join(['%x' % int(''.join(avg_list[x:x + 4]), 2) for x in range(0, 32 * 32, 4)])


def hammingDist(s1, s2):
"""
计算两张图片的汉明距离
"""
assert len(s1) == len(s2)
return sum([ch1 != ch2 for ch1, ch2 in zip(s1, s2)])


if __name__ == '__main__':
HASH1 = pHash('test.jpg')
HASH2 = pHash('test.png')
distance = hammingDist(HASH1, HASH2)
print('汉明距离=%f' % distance)
out_score = 1 - distance * 1. / (32 * 32 / 4)
print('相似度=%f' % out_score)

2. 均值哈希算法

  1. 缩放图片为8*8
  2. 转换为256阶灰度图。
  3. 计算灰度图所有像素的平均值。
  4. 比较:像素值大于平均值记作1,相反记作0,总共64位。
  5. 大于均值记为1,反之记为0,组合64个信息位
  6. 比较图片指纹,获得汉明距离,即两个64位的hash值有多少位是不一样的,不相同位数越少,图片越相似。
import cv2


def aHash(img):
# 均值哈希算法
# 缩放为8*8
img = cv2.resize(img, (8, 8))
# 转换为灰度图
gray = cv2.cvtColor(img, cv2.COLOR_BGR2GRAY)
# s为像素和初值为0,hash_str为hash值初值为''
s = 0
hash_str = ''
# 遍历累加求像素和
for i in range(8):
for j in range(8):
s = s + gray[i, j]
# 求平均灰度
avg = s / 64
# 灰度大于平均值为1相反为0生成图片的hash值
for i in range(8):
for j in range(8):
if gray[i, j] > avg:
hash_str = hash_str + '1'
else:
hash_str = hash_str + '0'
return hash_str


# 计算两个哈希值之间的差异
def cmpHash(hash1, hash2):
n = 0
# hash长度不同返回-1,此时不能比较
if len(hash1) != len(hash2):
return -1
# 如果hash长度相同遍历长度
for i in range(len(hash1)):
if hash1[i] != hash2[i]:
n = n + 1
return n


if __name__ == "__main__":
# 均值、差值、感知哈希算法三种算法值越小,则越相似,相同图片值为0

img1 = cv2.imread("test.jpg")
img2 = cv2.imread("test.png")
hash1 = aHash(img1)
hash2 = aHash(img2)
n1 = cmpHash(hash1, hash2)
print('均值哈希算法相似度aHash:', n1)

3. 差值哈希算法

在比较的一步:像素值大于后一个像素值记作1,相反记作0。本行不与下一行对比,每行9个像素,八个差值,有8行,总共64位

import cv2


# 差值感知算法
def dHash(img):
# 缩放8*8
img = cv2.resize(img, (9, 8), interpolation=cv2.INTER_CUBIC)
# 转换灰度图
gray = cv2.cvtColor(img, cv2.COLOR_BGR2GRAY)
hash_str = ''
# 每行前一个像素大于后一个像素为1,相反为0,生成哈希
for i in range(8):
for j in range(8):
if gray[i, j] > gray[i, j + 1]:
hash_str = hash_str + '1'
else:
hash_str = hash_str + '0'
return hash_str


# 计算两个哈希值之间的差异
def cmpHash(hash1, hash2):
n = 0
# hash长度不同返回-1,此时不能比较
if len(hash1) != len(hash2):
return -1
# 如果hash长度相同遍历长度
for i in range(len(hash1)):
if hash1[i] != hash2[i]:
n = n + 1
return n


if __name__ == "__main__":
# 均值、差值、感知哈希算法三种算法值越小,则越相似,相同图片值为0

img1 = cv2.imread("test.jpg")
img2 = cv2.imread("test.png")
hash1 = dHash(img1)
hash2 = dHash(img2)
n1 = cmpHash(hash1, hash2)
print('均值哈希算法相似度aHash:', n1)

四、灰度直方图

1. 单通道的直方图相似比较

# -*- coding: utf-8 -*-
# !/usr/bin/python
import cv2


# 计算单通道的直方图的相似值
def calculate(image1, image2):
hist1 = cv2.calcHist([image1], [0], None, [256], [0.0, 255.0])
hist2 = cv2.calcHist([image2], [0], None, [256], [0.0, 255.0])
# 计算直方图的重合度
degree = 0
for i in range(len(hist1)):
if hist1[i] != hist2[i]:
degree = degree + (1 - abs(hist1[i] - hist2[i]) / max(hist1[i], hist2[i]))
else:
degree = degree + 1
degree = degree / len(hist1)
return degree


if __name__ == '__main__':
img1 = cv2.imread('test.jpg')
img2 = cv2.imread('test.png')
degree = calculate(img1, img2)
print(degree)

2. 计算每个通道的直方图来计算相似度

# -*- coding: utf-8 -*-
# !/usr/bin/python
import cv2


# 计算单通道的直方图的相似值
def calculate(image1, image2):
hist1 = cv2.calcHist([image1], [0], None, [256], [0.0, 255.0])
hist2 = cv2.calcHist([image2], [0], None, [256], [0.0, 255.0])
# 计算直方图的重合度
degree = 0
for i in range(len(hist1)):
if hist1[i] != hist2[i]:
degree = degree + (1 - abs(hist1[i] - hist2[i]) / max(hist1[i], hist2[i]))
else:
degree = degree + 1
degree = degree / len(hist1)
return degree


# 通过得到每个通道的直方图来计算相似度
def classify_hist_with_split(image1, image2, size=(256, 256)):
# 将图像resize后,分离为三个通道,再计算每个通道的相似值
image1 = cv2.resize(image1, size)
image2 = cv2.resize(image2, size)
sub_image1 = cv2.split(image1)
sub_image2 = cv2.split(image2)
sub_data = 0
for im1, im2 in zip(sub_image1, sub_image2):
sub_data += calculate(im1, im2)
sub_data = sub_data / 3
return sub_data


if __name__ == '__main__':
img1 = cv2.imread('test.jpg')
img2 = cv2.imread('test.png')
degree = classify_hist_with_split(img1, img2)
print(degree)

未经允许不得转载!Python-OpenCV 比较图片相似度