YUAN 2019-07-01
由于最近涉及到匹配相似图片的问题,所以在此记录下解决办法:差异值哈希算法 + 颜色直方图
环境要求:Python cv2库 math库
检索相似图片,第一个想到的就是差异值哈希算法
。这个算法的步骤是:
8 * 8
的尺寸大小,共64个像素的图片。但是由于64个像素对于我来说,损失的细节太多所以我选择了缩放到 33 * 32
的尺寸大小256 * 256 * 256
种颜色。并且作为一个像素类似于这样的数值:[253 255 255] 是不利于简单比较的,肉眼看着类似的颜色,但是它的三个颜色分布可能相差很多。所以将它灰度化,用 256 个不同的灰色表示现有的图片。由于现在用一种灰色表示三种颜色,原来每个像素是一个 list 现在就降维成一个数值,数值的大小还是比较容易比较的。import cv2 # 差异值哈希算法 def dhash(image): resize_height, resized_width = 32, 33 # 缩放到(resized_width, resize_height)尺寸的大小 resized_img = cv2.resize(image, (resized_width, resize_height)) # 图片灰度化 grey_resized_img = cv2.cvtColor(resized_img, cv2.COLOR_RGB2GRAY) # 差异值计算 hash_list = [] for row in range(resize_height): for col in range(resized_width - 1): # 每行前一个颜色强度大于后一个,值为1,否则值为0 if grey_resized_img[row, col] > grey_resized_img[row, col + 1]: hash_list.append('1') else: hash_list.append('0') return '' . join(hash_list) # 比较汉明距离 def hamming_distance(dhash1, dhash2): return bin(int(dhash1, base = 2) ^ int(dhash2, base = 2)).count('1') # 读取图片内容 img1 = cv2.imread(img1_path) # 读取图片内容 img2 = cv2.imread(img2_path) if hamming_distance(dhash(img1), dhash(img2)) <= 5: print('相似图片')
由于差异值哈希失去了太多的细节,适合比较原图或者缩略图。所以我再加上颜色直方图的比较计算图片间的接近程度,用以排除部分像素的微小差异。
8 * 8
的尺寸大小,共64个像素的图片。但是由于64个像素对于我来说,损失的细节太多所以我选择了缩放到 32 * 32
的尺寸大小8 * 8
,8
,1
作为数组的 key,用以统计每个颜色的像素出现次数,并且不会出现不同颜色统计到了同一个 key 值下的目的。import cv2 from math import sqrt # 颜色映射 def bgr_mapping(img_val): # 将bgr颜色分成8个区间做映射 if img_val >= 0 and img_val <= 31: return 0 if img_val >= 32 and img_val <= 63: return 1 if img_val >= 64 and img_val <= 95: return 2 if img_val >= 96 and img_val <= 127: return 3 if img_val >= 128 and img_val <= 159: return 4 if img_val >= 160 and img_val <= 191: return 5 if img_val >= 192 and img_val <= 223: return 6 if img_val >= 224: return 7 # 颜色直方图的数值计算 def calc_bgr_hist(image): if not image.size: return False hist = {} # 缩放尺寸减小计算量 image = cv2.resize(image, (32, 32)) for bgr_list in image: for bgr in bgr_list: # 颜色按照顺序映射 maped_b = bgr_mapping(bgr[0]) maped_g = bgr_mapping(bgr[1]) maped_r = bgr_mapping(bgr[2]) # 计算像素值 index = maped_b * 8 * 8 + maped_g * 8 + maped_r hist[index] = hist.get(index, 0) + 1 return hist # 计算两张图片的相似度 def compare_similar_hist(h1, h2): if not h1 or not h2: return False sum1, sum2, sum_mixd = 0, 0, 0 # 像素值key的最大数不超过512,直接循环到512,遍历取出每个像素值 for i in range(512): # 计算出现相同像素值次数的平方和 sum1 = sum1 + (h1.get(i, 0) * h1.get(i, 0)) sum2 = sum2 + (h2.get(i, 0) * h2.get(i, 0)) # 计算两个图片次数乘积的和 sum_mixd = sum_mixd + (h1.get(i, 0) * h2.get(i, 0)) # 按照余弦相似性定理计算相似度 return sum_mixd / (sqrt(sum1) * sqrt(sum2)) # 读取图片内容 img1 = cv2.imread(img1_path) # 读取图片内容 img2 = cv2.imread(img2_path) if compare_similar_hist(calc_bgr_hist(img1), calc_bgr_hist(img2)) < 0.9999: print('相似图片')
总的来说:差异值哈希算法 + 颜色直方图 解决了我的相似图片匹配问题。