|
点击上方蓝字关注我们!背景以图搜图,是日常生活中我们经常会用到,例如在选购一款商品时,想要对比价格,往往会在各个购物app上通过搜图的形式来看同一款产品的价格;当你碰到某种不认识的植物时,也可以通过以图搜图的方式来获取该种植物的名称。而这些功能大都是通过计算图像的相似度来实现的。通过计算待搜索图片与图片数据库中图片之间的相似度,并对相似度进行排序为用户推荐相似图像的搜索结果。同时,通过检测图片是否相似也可用于判断商标是否侵权,图像作品是否抄袭等。本文将介绍几种比较常用的相似图像检测方法,其中包括了基于哈希算法,基于直方图,基于特征匹配,基于BOW+Kmeans以及基于卷积网络的图像相似度计算方法。技术实现相似图像的检测过程简单说来就是对图片数据库的每张图片进行编码或抽取特征(一般形式为特征向量),形成数字数据库。对于待检测图片,进行与图片数据库中同样方式的编码或特征提取,然后计算该编码或该特征向量和数据库中图像的编码或向量的距离,作为图像之间的相似度,并对相似度进行排序,将相似度靠前或符合需求的图像显示出来。哈希算法哈希算法可对每张图像生成一个“指纹”(fingerprint)字符串,然后比较不同图像的指纹。结果越接近,就说明图像越相似。常用的哈希算法有三种:1均值哈希算法(ahash)均值哈希算法就是利用图片的低频信息。将图片缩小至8*8,总共64个像素。这一步的作用是去除图片的细节,只保留结构、明暗等基本信息,摒弃不同尺寸、比例带来的图片差异。将缩小后的图片,转为64级灰度。计算所有64个像素的灰度平均值,将每个像素的灰度,与平均值进行比较。大于或等于平均值,记为1;小于平均值,记为0。将上一步的比较结果,组合在一起,就构成了一个64位的整数,这就是这张图片的指纹。defaHash(img):img=cv2.resize(img,(8,8))gray=cv2.cvtColor(img,cv2.COLOR_BGR2GRAY)np_mean=np.mean(gray)ahash_01=(gray>np_mean)+0ahash_list=ahash_01.reshape(1,-1)[0].tolist()ahash_str=''.join([str(x)forxinahash_list])returnahash_str均值哈希算法计算速度快,不受图片尺寸大小的影响,但是缺点就是对均值敏感,例如对图像进行伽马校正或直方图均衡就会影响均值,从而影响最终的hash值。2感知哈希算法(phash)感知哈希算法是一种比均值哈希算法更为健壮的算法,与均值哈希算法的区别在于感知哈希算法是通过DCT(离散余弦变换)来获取图片的低频信息。先将图像缩小至32*32,并转化成灰度图像来简化DCT的计算量。通过DCT变换,得到32*32的DCT系数矩阵,保留左上角的8*8的低频矩阵(这部分呈现了图片中的最低频率)。再计算8*8矩阵的DCT的均值,然后将低频矩阵中大于等于DCT均值的设为”1”,小于DCT均值的设为“0”,组合在一起,就构成了一个64位的整数,组成了图像的指纹。defpHash(img):img=cv2.resize(img,(32,32))gray=cv2.cvtColor(img,cv2.COLOR_BGR2GRAY)dct=cv2.dct(np.float32(gray))dct_roi=dct[0:8,0:8]avreage=np.mean(dct_roi)phash_01=(dct_roi>avreage)+0phash_list=phash_01.reshape(1,-1)[0].tolist()phash_str=''.join([str(x)forxinphash_list])returnphash_str感知哈希算法能够避免伽马校正或颜色直方图被调整带来的影响。对于变形程度在25%以内的图片也能精准识别。3差异值哈希算法(dhash)差异值哈希算法将图像收缩小至8*9,共72的像素点,然后把缩放后的图片转化为256阶的灰度图。通过计算每行中相邻像素之间的差异,若左边的像素比右边的更亮,则记录为1,否则为0,共形成64个差异值,组成了图像的指纹。defdHash(img):img=cv2.resize(img,(9,8))gray=cv2.cvtColor(img,cv2.COLOR_BGR2GRAY)hash_str0=[]foriinrange(8):hash_str0.append(gray[:,i]>gray[:,i+1])hash_str1=np.array(hash_str0)+0hash_str2=hash_str1.Thash_str3=hash_str2.reshape(1,-1)[0].tolist()dhash_str=''.join([str(x)forxinhash_str3])returndhash_str相对于pHash,dHash的速度要快的多,相比aHash,dHash在效率几乎相同的情况下的效果要更好,它是基于渐变实现的。最后通过计算图像指纹之间的汉明距,算出彼此的相似度:defhammingDist(hashstr1,hashstr2):assertlen(hashstr1)==len(hashstr1)returnsum([ch1!=ch2forch1,ch2inzip(hashstr1,hashstr1)])单通道直方图和三直方图单通道图,俗称灰度图,每个像素点只能有有一个值表示颜色,它的像素值在0到255之间,0是黑色,255是白色,中间值是一些不同等级的灰色。三通道图,每个像素点都有3个值表示(如RGB图),所以就是3通道。图像的直方图用来表征该图像像素值的分布情况。用一定数目的小区间(bin)来指定表征像素值的范围,每个小区间会得到落入该小区间表示范围的像素数目。可以通过计算图像直方图的重合度,来判断图像之间的相似度。defcalculate_single(img1,img2):hist1=cv2.calcHist([img1],[0],None,[256],[0.0,255.0])hist1=cv2.normalize(hist1,hist1,0,1,cv2.NORM_MINMAX,-1)hist2=cv2.calcHist([img2],[0],None,[256],[0.0,255.0])hist2=cv2.normalize(hist2,hist2,0,1,cv2.NORM_MINMAX,-1)degree=0foriinrange(len(hist1)):ifhist1[i]!=hist2[i]:degree=degree+(1-abs(hist1[i]-hist2[i])/max(hist1[i],hist2[i]))else:degree=degree+1degree=degree/len(hist1)returndegreedefclassify_hist_of_three(img1,img2,size=(256,256)):image1=cv2.resize(img1,size)image2=cv2.resize(img2,size)sub_image1=cv2.split(img1)sub_image2=cv2.split(img2)sub_data=0forim1,im2inzip(sub_img1,sub_img2):sub_data+=calculate_single(im1,im2)sub_data=sub_data/3returnsub_data直方图能够很好的归一化,比如256个bin条,那么即使是不同分辨率的图像都可以直接通过其直方图来计算相似度,计算量适中。比较适合描述难以自动分割的图像。基于特征提取与匹配的方法1ORB特征ORB特征是将FAST特征点的检测方法与BRIEF特征描述子结合起来,并在它们原来的基础上做了改进与优化。ORB特征提取速度快,提取的特征直接是二元编码形式,无需使用哈希学习方法就可以直接利用汉明距离快速计算相似度。在大多数情况下,去重效果能够与 SIFT/SURF 持平。defORB_img_similarity(img1_path,img2_path)rb=cv2.ORB_create()img1=cv2.imread(img1_path,cv2.IMREAD_GRAYSCALE)img2=cv2.imread(img2_path,cv2.IMREAD_GRAYSCALE)kp1,des1=orb.detectAndCompute(img1,None)kp2,des2=orb.detectAndCompute(img2,None)bf=cv2.BFMatcher(cv2.NORM_HAMMING)matches=bf.knnMatch(des1,trainDescriptors=des2,k=2)matchNum=[mfor(m,n)inmatchesifm.distance0)*1,axis=0)idf=np.array(np.log((1.0*len(image_paths)+1)/(1.0*nbr_occurences+1)),'float32')im_features=im_features*idfim_features=preprocessing.normalize(im_features,norm='l2')joblib.dump((im_features,image_paths,idf,numWords,voc),"bow.pkl",compress=3)基于卷积网络的相似图像检测在ImageNet中的卷积网络结构(vgg16)基础上,在第7层(4096个神经元)和output层之间多加一个全连接层,并选用sigmoid激活函数使得输出值在0-1之间,设定阈值0.5之后可以转成01二值向量作为二值检索向量。这样,对所有的图片做卷积网络前向运算,得到第7层4096维特征向量和代表图像类别分桶的第8层output。对于待检测的图片,同样得到4096维特征向量和128维01二值检索向量,在数据库中查找二值检索向量对应的图片,比对4096维特征向量之间距离,重新排序即得到最终结果。其流程如下:database='dataset'index='models/vgg_featureCNN.h5'img_list=get_imlist(database)features=[]names=[]model=VGGNet()fori,img_pathinenumerate(img_list):norm_feat=model.vgg_extract_feat(img_path)img_name=os.path.split(img_path)[1]features.append(norm_feat)names.append(img_name)feats=np.array(features)output=indexh5f=h5py.File(output,'w')h5f.create_dataset('dataset_features',data=feats)h5f.create_dataset('dataset_names',data=np.string_(names))h5f.close()model=VGGNet()queryVec=model.vgg_extract_feat(imgs)scores=np.dot(queryVec,feats.T)rank_ID=np.argsort(scores)[::-1]rank_score=scores[rank_ID]效果展示下边展示了不同方法针对一张图标,在同一数据库中进行相似图像检测的效果:从检测结果中可以看出,针对上述的数据,基于vgg16和sift特征的检索结果会更加的准确和稳定,基于直方图检索出的图与待检测的图也都比较相似,而基于BOW和哈希算法检索出的结果表现则不稳定,基于orb特征检索出来的图和待检测图差距很大,效果很不理想。但这不能说明某种方法一定不好,而是针对特定数据而言的,同种方法在不同数据库中的表现也存在着差异。在实践过程中,为了保证检测效果的稳定性,应选取性能较好较稳的方法。总结相似图片的检测方法有很多,但不是每种方法都适应于你的应用场景,各种方法在不同的数据上的表现也具有很大的差异。因此,可以根据自身数据的特点和不同方法的特性来综合考虑。也可以根据需求将不同的方法进行结合,进一步提升相似图像检测的准确性和稳定性。如果检测相似图片是为了分析商标是否侵权或是作品是否抄袭等,可适当的设置相似度的阈值,进行筛选。
|
|