尺度不变特征转换(Scale-invariant feature transform或SIFT)是一种电脑视觉的算法用来侦测与描述影像中的局部性特征,它在空间尺度中寻找极值点,并提取出其位置、尺度、旋转不变量,此算法由 David Lowe在1999年所发表,2004年完善总结。
其应用范围包含物体辨识、机器人地图感知与导航、影像缝合、3D模型建立、手势辨识、影像追踪和动作比对。
局部影像特征的描述与侦测可以帮助辨识物体,SIFT 特征是基于物体上的一些局部外观的兴趣点而与影像的大小和旋转无关。对于光线、噪声、些微视角改变的容忍度也相当高。基于这些特性,它们是高度显著而且相对容易撷取,在母数庞大的特征数据库中,很容易辨识物体而且鲜有误认。使用 SIFT特征描述对于部分物体遮蔽的侦测率也相当高,甚至只需要3个以上的SIFT物体特征就足以计算出位置与方位。在现今的电脑硬件速度下和小型的特征数据库条件下,辨识速度可接近即时运算。SIFT特征的信息量大,适合在海量数据库中快速准确匹配。
SIFT算法的特点:
SIFT算法可以解决的问题:
目标的自身状态、场景所处的环境和成像器材的成像特性等因素影响图像配准/目标识别跟踪的性能。而SIFT算法在一定程度上可解决:
SIFT算法的实质是在不同的尺度空间上查找关键点(特征点),并计算出关键点的方向。SIFT所查找到的关键点是一些十分突出,不会因光照,仿射变换和噪音等因素而变化的点,如角点、边缘点、暗区的亮点及亮区的暗点等。
SIFT算法分解为如下四步:
SIFT的缺点
SIFT在图像的不变特征提取方面拥有无与伦比的优势,但并不完美,仍然存在:
关于算法理论请查看:https://blog.csdn.net/wsp_1138886114/article/details/81368890
关于算法实现请查看:https://blog.csdn.net/wsp_1138886114/article/details/90484345
它的作用是对每张图像生成一个“指纹”(fingerprint)字符串,然后比较不同图像的指纹。结果越接近,就说明图像越相似。
Hash算法
实现步骤:
当然你也可以使用这个代码 https://github.com/hjaurum/DHash/blob/master/dHash.py
或者查看这个:https://www.cnblogs.com/dcb3688/p/4610660.html
我使用Numpy矩阵计算优化算法运行速度,使用矩阵运算加快运行原理自行百度。
一个小例子:https://blog.csdn.net/wsp_1138886114/article/details/80960895
import cv2
import time
import numpy as np
#均值哈希算法
def aHash(img):
img=cv2.resize(img,(8,8))
gray=cv2.cvtColor(img,cv2.COLOR_BGR2GRAY)
np_mean = np.mean(gray) # 求numpy.ndarray平均值
ahash_01 = (gray>np_mean)+0 # 大于平均值=1,否则=0
ahash_list = ahash_01.reshape(1,-1)[0].tolist() # 展平->转成列表
ahash_str = ''.join([str(x) for x in ahash_list])
return ahash_str
def pHash(img):
img = cv2.resize(img, (32, 32)) # 默认interpolation=cv2.INTER_CUBIC
gray = cv2.cvtColor(img, cv2.COLOR_BGR2GRAY)
dct = cv2.dct(np.float32(gray))
dct_roi = dct[0:8, 0:8] # opencv实现的掩码操作
avreage = np.mean(dct_roi)
phash_01 = (dct_roi>avreage)+0
phash_list = phash_01.reshape(1,-1)[0].tolist()
phash_str = ''.join([str(x) for x in phash_list])
return phash_str
def dHash(img):
img=cv2.resize(img,(9,8))
gray=cv2.cvtColor(img,cv2.COLOR_BGR2GRAY)
#每行前一个像素大于后一个像素为1,相反为0,生成哈希
hash_str0 = []
for i in range(8):
hash_str0.append(gray[:, i] > gray[:, i + 1])
hash_str1 = np.array(hash_str0)+0
hash_str2 = hash_str1.T
hash_str3 = hash_str2.reshape(1,-1)[0].tolist()
dhash_str = ''.join([str(x) for x in hash_str3])
return dhash_str
def hammingDist(s1, s2):
assert len(s1) == len(s2)
return sum([ch1 != ch2 for ch1, ch2 in zip(s1, s2)])
# 通过得到RGB每个通道的直方图来计算相似度
def classify_hist_with_split(image1, image2, size=(256, 256)):
# 将图像resize后,分离为RGB三个通道,再计算每个通道的相似值
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
# 计算单通道的直方图的相似值
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 degre
if __name__ == '__main__':
raw_img1 = './eeee/000001_flip_img.png'
raw_img2 = './eeee/000004_flip_img.png'
img1 = cv2.imread(raw_img1)
img2 = cv2.imread(raw_img2)
start = time.time()
ahash_str1 = aHash(img1)
ahash_str2 = aHash(img2)
phash_str1 = pHash(img1)
phash_str2 = pHash(img2)
dhash_str1 = dHash(img1)
dhash_str2 = dHash(img2)
a_score = 1 - hammingDist(ahash_str1, ahash_str2) * 1. / (32 * 32 / 4)
p_score = 1 - hammingDist(phash_str1, phash_str2) * 1. / (32 * 32 / 4)
d_score = 1 - hammingDist(dhash_str1, dhash_str2) * 1. / (32 * 32 / 4)
n = classify_hist_with_split(img1, img2)
print('三直方图算法相似度:', n)
end = time.time()
print('a_score:{},p_score:{},d_score{}'.format(a_score,p_score,d_score))
print("Total Spend time:", str((end - start) / 60)[0:6] + "分钟")
import cv2,os,csv
import numpy as np
import matplotlib.pyplot as plt
def get_img(Img_path):
image_paths = []
for (dir, dirnames, filenames) in os.walk(Img_path):
for img_file in filenames:
ext = ['.jpg','.png','.jpeg','.tif']
if img_file.endswith(tuple(ext)):
image_paths.append(dir+'/'+img_file)
return image_paths
def Calculate_MSE(img):
img = cv2.resize(img,(32,32))
gray = cv2.cvtColor(img, cv2.COLOR_BGR2GRAY)
line_MSEs = []
for i in range(32):
avg = np.mean(gray[i,:])
line_MSE = np.square(gray[i, :]-avg)
line_MSEs.append(line_MSE)
return np.array(line_MSEs)
def Img1_Img2(line_MSEs1,line_MSEs2,Confident):
Diff_value = np.abs(line_MSEs1-line_MSEs2)
fingle = np.array(Diff_value<(1-Confident)*np.max(Diff_value))+0
similar = fingle.reshape(1,-1)[0].tolist()
similar = sum(similar)/len(similar)
if similar == 0.0:
similar = 1
print('similar;', similar)
return Diff_value,similar
def main(Module_path,Imgpath,csv_path):
Module_paths = get_img(Module_path)
Imgpaths = get_img(Imgpath)
Result = []
Result_CSV = []
for module_img in Module_paths:
img1 = cv2.imread(module_img)
line_MSEs1 = Calculate_MSE(img1)
print('Doing...',module_img)
for Imgpath in Imgpaths:
img2 = cv2.imread(Imgpath)
line_MSEs2 = Calculate_MSE(img2)
Diff_value,similar = Img1_Img2(line_MSEs1, line_MSEs2,Confident)
Result.append((line_MSEs1,line_MSEs2,Diff_value,module_img,Imgpath))
Result_CSV.append((module_img,Imgpath,similar))
with open(csv_path, 'w', newline='') as csv_file:
csv_writer = csv.writer(csv_file)
csv_writer.writerow(('模板图','被检测的图片','相似度百分比'))
csv_writer.writerows(Result_CSV)
return Result
if __name__ == '__main__':
Module_path = 'D:\\python_script\\hhhh' # 被查重的图片模板
Imgpath = 'D:\\python_script\\construct' # 需要筛选的图片目录
Target_path = 'D:\\python_script\\jjjj' # 筛选之后存放图片的目录
csv_path = 'D:\\python_script\\iiii\\Contrast1.csv' # 匹配信息保存
Confident = 0.8 # 置信度
Result= main(Module_path,Imgpath,csv_path)
#========================以下代码用于可视化========================================
for i in range(len(Result)):
line_MSEs1 = Result[i][0].reshape(1, -1)[0].tolist()
line_MSEs2 = Result[i][1].reshape(1, -1)[0].tolist()
Diff_value = Result[i][2].reshape(1, -1)[0].tolist()
imgName3 = Result[i][3]
imgName4 = Result[i][4]
plt.figure("Diff_Pic")
plt.plot(range(1024),line_MSEs1,marker="*",label="$img1$")
plt.plot(range(1024),line_MSEs2,marker="^",label="$img2$")
plt.plot(range(1024), Diff_value, marker="o", label="Diff_value")
plt.title(str(imgName3)+str(imgName4))
plt.legend()
plt.show()
鸣谢
https://www.jianshu.com/p/193f0089b7a2
https://blog.csdn.net/u010977034/article/details/82733137
https://www.cnblogs.com/dcb3688/p/4610660.html