基于平均哈希算法(aHash)+汉明距离的相似图片识别方案
# demo示例
import cv2 import numpy as np from PIL import Image # 计算平均哈希值 def ahash(image): # 缩放为8*8 image = cv2.resize(image, (8, 8), interpolation=cv2.INTER_CUBIC) # 转换为灰度图像 gray = cv2.cvtColor(image, cv2.COLOR_BGR2GRAY) # 计算像素平均值 avg = gray.mean() # 二值化 hash = (gray > avg).astype(np.uint8) # 转换为整数 hash = hash.flatten().tolist() hash = int(''.join(map(str, hash)), 2) return hash # 计算汉明距离 def hamming_distance(hash1, hash2): return bin(hash1 ^ hash2).count('1') # 加载图像 img1 = cv2.imread('image1.jpg') img2 = cv2.imread('image2.jpg') # 计算哈希值 hash1 = ahash(img1) hash2 = ahash(img2) # 计算汉明距离 distance = hamming_distance(hash1, hash2) # 判断相似度 if distance <= 5: print('两张图片相似') else: print('两张图片不相似')
上述代码中,首先定义了一个计算平均哈希值的函数ahash(),该函数将图像缩放为8*8大小,转换为灰度图像,计算像素平均值,然后进行二值化,最后将二进制码转换为整数。然后,定义了一个计算汉明距离的函数hamming_distance(),该函数使用异或运算和计数函数实现。最后,加载两张图像,计算哈希值和汉明距离,根据距离判断两张图像是否相似。
需要注意的是,平均哈希算法和汉明距离虽然简单,但是对图像的旋转、缩放、裁剪等变换不太敏感,可能会导致误判。因此,在实际应用中,需要根据具体情况选择合适的相似度计算方法,以提高准确性和鲁棒性。
实际图片是url格式的链接,需要从url里面读取图片,这里参考博客:https://blog.csdn.net/xuezhangjun0121/article/details/120451534
用PIL+requests读取
import requests as req from PIL import Image from io import BytesIO url = "https://img14.360buyimg.com/pop/jfs/t1/22587/13/20118/88676/63749dabE14cae977/7894c80d44c5d336.jpg" response = req.get(url) image = Image.open(BytesIO(response.content)) image.show()
但是此时image虽然读取了,并不能直接丢进上面定义的ahash()函数去计算,因为
img1 = cv2.imread('Wechat1.jpeg')
和
response = req.get(url)
image = Image.open(BytesIO(response.content))
这两种方法读取的图片格式是不同的。
1.cv2.imread()
函数读取的图像格式是numpy
数组,其中每个元素表示图像中的一个像素。numpy
数组的形状是(height, width, channels)
,其中height
表示图像的高度,width
表示图像的宽度,channels
表示图像的通道数,通常是3(表示RGB颜色空间)或1(表示灰度颜色空间)。
2.PIL
库中的Image.open()
函数读取的图像格式是PIL
库中的Image
对象,其中包含了图像的各种属性和方法。Image
对象可以使用numpy
库中的frombuffer()
函数将其转换为numpy
数组,也可以使用PIL
库中的save()
函数将其保存为图像文件。
需要注意的是,cv2.imread()
函数读取的图像格式是BGR
格式,而不是RGB
格式。因此,在使用cv2.imread()
函数读取图像后,需要使用cv2.cvtColor()
函数将图像从BGR
格式转换为RGB
格式,才能正确地显示图像。
而PIL
库中的Image.open()
函数读取的图像格式是RGB
格式,因此不需要进行额外的转换。
那么如何将Image图像转换成cv2.imread()读取的格式呢?
可以使用numpy
库中的asarray()
函数将PIL
库中的Image
对象转换为numpy
数组,然后使用cv2.cvtColor()
函数将图像从RGB
格式转换为BGR
格式,最后使用cv2.imwrite()
函数将图像保存为文件。
import requests from io import BytesIO import numpy as np from PIL import Image import cv2 # 读取图像数据 url = 'https://img14.360buyimg.com/pop/jfs/t1/22587/13/20118/88676/63749dabE14cae977/7894c80d44c5d336.jpg' response = requests.get(url) image = Image.open(BytesIO(response.content)) # 将图像数据转换为numpy数组 img_array = np.asarray(image) # 将图像从RGB格式转换为BGR格式 img = cv2.cvtColor(img_array, cv2.COLOR_RGB2BGR) # 保存图像 cv2.imwrite('image.jpg', img)
np.asarray(image)和img_array = np.frombuffer(image.tobytes(), dtype=np.uint8) 有什么区别?
1.np.asarray(image)
和img_array = np.frombuffer(image.tobytes(), dtype=np.uint8)
这两种方法都可以将PIL
库中的Image
对象转换为numpy
数组,但是它们的实现方式是不同的。
2.np.asarray(image)
函数将PIL
库中的Image
对象转换为numpy
数组,其中每个元素表示图像中的一个像素。numpy
数组的形状是(height, width, channels)
,其中height
表示图像的高度,width
表示图像的宽度,channels
表示图像的通道数,通常是3(表示RGB颜色空间)或1(表示灰度颜色空间)。np.asarray()
函数会自动根据图像的类型(RGB或灰度)创建相应的numpy
数组。
3.image.tobytes()
函数将PIL
库中的Image
对象转换为字节串,其中每个字节表示图像中的一个像素。np.frombuffer()
函数将字节串转换为numpy
数组,其中每个元素表示一个字节。np.frombuffer()
函数需要指定数组的数据类型,通常是np.uint8
(表示无符号8位整数)。
4.需要注意的是,np.asarray()
函数和np.frombuffer()
函数的返回值都是numpy
数组,但是它们的形状和数据类型可能不同。np.asarray()
函数返回的数组形状是(height, width, channels)
,数据类型是np.uint8
或np.uint16
,而np.frombuffer()
函数返回的数组形状是(height * width * channels,)
,数据类型是np.uint8
。
5.总之,这两种方法都可以将PIL
库中的Image
对象转换为numpy
数组,需要根据具体的应用场景选择合适的方法。如果需要保留图像的形状和数据类型,则可以使用np.asarray()
函数;如果只需要获取图像的字节串,则可以使用image.tobytes()
函数;如果需要将字节串转换为numpy
数组,则可以使用np.frombuffer()
函数。