Python+Opencv识别两张相似图片

/

要识别两张相似图像,我们从感性上来谈是怎么样的一个过程?首先我们会区分这两张相片的类型,例如是风景照,还是人物照。风景照中,是沙漠还是海洋,人物照中,两个人是不是都是国字脸,还是瓜子脸(还是倒瓜子脸……哈哈……)。

那么从机器的角度来说也是这样的,先识别图像的特征,然后再相比。

很显然,在没有经过训练的计算机(即建立模型),那么计算机很难区分什么是海洋,什么是沙漠。但是计算机很容易识别到图像的像素值。

因此,在图像识别中,颜色特征是最为常用的。(其余常用的特征还有纹理特征、形状特征和空间关系特征等)

其中又分为

直方图

颜色集

颜色矩

聚合向量

相关图

直方图计算法

这里先用直方图进行简单讲述。

先借用一下恋花蝶的图片,

从肉眼来看,这两张图片大概也有八成是相似的了。
在Python中利用opencv中的calcHist()方法获取其直方图数据,返回的结果是一个列表,使用matplotlib,画出了这两张图的直方图数据图。如下:

是的,我们可以明显的发现,两张图片的直方图还是比较重合的。所以利用直方图判断两张图片的是否相似的方法就是,计算其直方图的重合程度即可。计算方法如下:

其中gi和si是分别指两条曲线的第i个点。

最后计算得出的结果就是就是其相似程度。

不过,这种方法有一个明显的弱点,就是他是按照颜色的全局分布来看的,无法描述颜色的局部分布和色彩所处的位置。

也就是假如一张图片以蓝色为主,内容是一片蓝天,而另外一张图片也是蓝色为主,但是内容却是妹子穿了蓝色裙子,那么这个算法也很可能认为这两张图片的相似的。

缓解这个弱点有一个方法就是利用Image的crop方法把图片等分,然后再分别计算其相似度,最后综合考虑。

图像指纹与汉明距离

在介绍下面其他判别相似度的方法前,先补充一些概念。第一个就是图像指纹

图像指纹和人的指纹一样,是身份的象征,而图像指纹简单点来讲,就是将图像按照一定的哈希算法,经过运算后得出的一组二进制数字。

说到这里,就可以顺带引出汉明距离的概念了。

假如一组二进制数据为101,另外一组为111,那么显然把第一组的第二位数据0改成1就可以变成第二组数据111,所以两组数据的汉明距离就为1

简单点说,汉明距离就是一组二进制数据变成另一组数据所需的步骤数,显然,这个数值可以衡量两张图片的差异,汉明距离越小,则代表相似度越高。汉明距离为0,即代表两张图片完全一样。

如何计算得到汉明距离,请看下面三种哈希算法

平均哈希法(aHash)

此算法是基于比较灰度图每个像素与平均值来实现的

一般步骤:

1.缩放图片,一般大小为8*8,64个像素值。

2.转化为灰度图

3.计算平均值:计算进行灰度处理后图片的所有像素点的平均值,直接用numpy中的mean()计算即可。

4.比较像素灰度值:遍历灰度图片每一个像素,如果大于平均值记录为1,否则为0.

5.得到信息指纹:组合64个bit位,顺序随意保持一致性。

最后比对两张图片的指纹,获得汉明距离即可。

感知哈希算法(pHash)

平均哈希算法过于严格,不够精确,更适合搜索缩略图,为了获得更精确的结果可以选择感知哈希算法,它采用的是DCT(离散余弦变换)来降低频率的方法

一般步骤:

缩小图片:32 * 32是一个较好的大小,这样方便DCT计算

转化为灰度图

计算DCT:利用Opencv中提供的dct()方法,注意输入的图像必须是32位浮点型,所以先利用numpy中的float32进行转换

缩小DCT:DCT计算后的矩阵是32 * 32,保留左上角的8 * 8,这些代表的图片的最低频率

计算平均值:计算缩小DCT后的所有像素点的平均值。

进一步减小DCT:大于平均值记录为1,反之记录为0.

得到信息指纹:组合64个信息位,顺序随意保持一致性。

最后比对两张图片的指纹,获得汉明距离即可。

dHash算法

相比pHash,dHash的速度要快的多,相比aHash,dHash在效率几乎相同的情况下的效果要更好,它是基于渐变实现的。

步骤:

缩小图片:收缩到9*8的大小,以便它有72的像素点

转化为灰度图

计算差异值:dHash算法工作在相邻像素之间,这样每行9个像素之间产生了8个不同的差异,一共8行,则产生了64个差异值

获得指纹:如果左边的像素比右边的更亮,则记录为1,否则为0.

最后比对两张图片的指纹,获得汉明距离即可。

整个的代码实现如下:

# -*- coding: utf-8 -*-

#feimengjuan

# 利用python实现多种方法来实现图像识别


import cv2

import numpy as np

from matplotlib import pyplot as plt


# 最简单的以灰度直方图作为相似比较的实现

def classify_gray_hist(image1,image2,size = (256,256)):

# 先计算直方图

# 几个参数必须用方括号括起来

# 这里直接用灰度图计算直方图,所以是使用第一个通道,

# 也可以进行通道分离后,得到多个通道的直方图

# bins 取为16

image1 = cv2.resize(image1,size)

image2 = cv2.resize(image2,size)

hist1 = cv2.calcHist([image1],[0],None,[256],[0.0,255.0])

hist2 = cv2.calcHist([image2],[0],None,[256],[0.0,255.0])

# 可以比较下直方图

plt.plot(range(256),hist1,'r')

plt.plot(range(256),hist2,'b')

plt.show()

# 计算直方图的重合度

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 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



# 平均哈希算法计算

def classify_aHash(image1,image2):

image1 = cv2.resize(image1,(8,8))

image2 = cv2.resize(image2,(8,8))

gray1 = cv2.cvtColor(image1,cv2.COLOR_BGR2GRAY)

gray2 = cv2.cvtColor(image2,cv2.COLOR_BGR2GRAY)

hash1 = getHash(gray1)

hash2 = getHash(gray2)

return Hamming_distance(hash1,hash2)



def classify_pHash(image1,image2):

image1 = cv2.resize(image1,(32,32))

image2 = cv2.resize(image2,(32,32))

gray1 = cv2.cvtColor(image1,cv2.COLOR_BGR2GRAY)

gray2 = cv2.cvtColor(image2,cv2.COLOR_BGR2GRAY)

# 将灰度图转为浮点型,再进行dct变换

dct1 = cv2.dct(np.float32(gray1))

dct2 = cv2.dct(np.float32(gray2))

# 取左上角的8*8,这些代表图片的最低频率

# 这个操作等价于c++中利用opencv实现的掩码操作

# 在python中进行掩码操作,可以直接这样取出图像矩阵的某一部分

dct1_roi = dct1[0:8,0:8]

dct2_roi = dct2[0:8,0:8]

hash1 = getHash(dct1_roi)

hash2 = getHash(dct2_roi)

return Hamming_distance(hash1,hash2)



# 输入灰度图,返回hash

def getHash(image):

avreage = np.mean(image)

hash = []

for i in range(image.shape[0]):

for j in range(image.shape[1]):

if image[i,j] > avreage:

hash.append(1)

else:

hash.append(0)

return hash





# 计算汉明距离

def Hamming_distance(hash1,hash2):

num = 0

for index in range(len(hash1)):

if hash1[index] != hash2[index]:

num += 1

return num





if __name__ == '__main__':

img1 = cv2.imread('10.jpg')

cv2.imshow('img1',img1)

img2 = cv2.imread('11.jpg')

cv2.imshow('img2',img2)

degree = classify_gray_hist(img1,img2)

#degree = classify_hist_with_split(img1,img2)

#degree = classify_aHash(img1,img2)

#degree = classify_pHash(img1,img2)

print degree

cv2.waitKey(0)

import cv2
import numpy as np
import os
from matplotlib import pyplot as plt
 
def classify_gray_hist(image1, image2, size=(256, 256)):
    image1 = cv2.resize(image1, size)
    image2 = cv2.resize(image2, size)
    hist1 = cv2.calcHist([image1], [0], None, [256], [0.0, 255.0])
    hist2 = cv2.calcHist([image2], [0], None, [256], [0.0, 255.0])
 
    plt.plot(range(256), hist1, 'r')
    plt.plot(range(256), hist2, 'b')
    plt.show()
    #计算直方图的重合度
    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 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
 
# 平均哈希算法计算
def classify_aHash(image1,image2):
    image1 = cv2.resize(image1, (8, 8))
    image2 = cv2.resize(image2, (8, 8))
    gray1 = cv2.cvtColor(image1, cv2.COLOR_BGR2GRAY)
    gray2 = cv2.cvtColor(image2, cv2.COLOR_BGR2GRAY)
    hash1 = getHash(gray1)
    hash2 = getHash(gray2)
    return Hamming_distance(hash1, hash2)
# 感知哈希算法
def classify_pHash(image1,image2):
    image1 = cv2.resize(image1, (32, 32))
    image2 = cv2.resize(image2, (32, 32))
    gray1 = cv2.cvtColor(image1, cv2.COLOR_BGR2GRAY)
    gray2 = cv2.cvtColor(image2, cv2.COLOR_BGR2GRAY)
    # 将灰度图转为浮点型,再进行dct变换
    dct1 = cv2.dct(np.float32(gray1))
    dct2 = cv2.dct(np.float32(gray2))
    # 取左上角的8*8,这些代表图片的最低频率
    # 这个操作等价于c++中利用opencv实现的掩码操作
    # 在python中进行掩码操作,可以直接这样取出图像矩阵的某一部分
    dct1_roi = dct1[0:8, 0:8]
    dct2_roi = dct2[0:8, 0:8]
    hash1 = getHash(dct1_roi)
    hash2 = getHash(dct2_roi)
    return Hamming_distance(hash1, hash2)
 
# 输入灰度图,返回hash
def getHash(image):
    avreage = np.mean(image)
    hash = []
    for i in range(image.shape[0]):
        for j in range(image.shape[1]):
            if image[i, j] > avreage:
                hash.append(1)
            else:
                hash.append(0)
    return hash
 
# 计算汉明距离
def Hamming_distance(hash1,hash2):
    num = 0
    for index in range(len(hash1)):
        if hash1[index] != hash2[index]:
            num += 1
    return num
 
 
 
 
if __name__ == "__main__":
    path1 = r"C:UserschenxuDesktopimageimage_25.png"
    path2 = r"C:UserschenxuDesktopori_matchcold_welding"
 
    file_lst = os.listdir(path2)
    image_total = len(file_lst)
    image1 = cv2.imread(path1)
    for image_num in range(1,image_total+1):
        addr2 = path2 + "/" + str(image_num) + ".png"
        image2 = cv2.imread(addr2)
 
        # degree = classify_gray_hist(image1, image2)
        # degree = classify_hist_with_split(image1, image2)
        # degree = classify_aHash(image1, image2)     #平均哈希算法计算
        degree = classify_pHash(image1, image2)
        print(image_num)
        print("-----")
        print(degree)
        print("================")

经过反复的实验,发现对于我的需求,使用平均哈希法(aHash)效果最好,下面是针对我们的需求具体的代码:

import csv
import glob
import os
import cv2
import numpy as np
try:
  from scipy.misc import imread
except ImportError:
  from imageio import imread
 
root_dir_1 = "C:/Users/chenxu/Desktop/image"   #目录1:无标签数据
root_dir_2 = "C:/Users/chenxu/Desktop/ori_match/"  #目录2:有标签数据
suffix = ".png"
target_lst = []
token2target = {}
data = []
 
 
# 平均哈希算法计算
def classify_aHash(image1,image2):
    image1 = cv2.resize(image1, (8, 8))
    image2 = cv2.resize(image2, (8, 8))
    gray1 = cv2.cvtColor(image1, cv2.COLOR_BGR2GRAY)
    gray2 = cv2.cvtColor(image2, cv2.COLOR_BGR2GRAY)
    hash1 = getHash(gray1)
    hash2 = getHash(gray2)
    return Hamming_distance(hash1, hash2)
 
def classify_pHash(image1,image2):
    image1 = cv2.resize(image1, (32, 32))
    image2 = cv2.resize(image2, (32, 32))
    gray1 = cv2.cvtColor(image1, cv2.COLOR_BGR2GRAY)
    gray2 = cv2.cvtColor(image2, cv2.COLOR_BGR2GRAY)
    # 将灰度图转为浮点型,再进行dct变换
    dct1 = cv2.dct(np.float32(gray1))
    dct2 = cv2.dct(np.float32(gray2))
    # 取左上角的8*8,这些代表图片的最低频率
    # 这个操作等价于c++中利用opencv实现的掩码操作
    # 在python中进行掩码操作,可以直接这样取出图像矩阵的某一部分
    dct1_roi = dct1[0:8, 0:8]
    dct2_roi = dct2[0:8, 0:8]
    hash1 = getHash(dct1_roi)
    hash2 = getHash(dct2_roi)
    return Hamming_distance(hash1, hash2)
 
# 输入灰度图,返回hash
def getHash(image):
    avreage = np.mean(image)
    hash = []
    for i in range(image.shape[0]):
        for j in range(image.shape[1]):
            if image[i, j] > avreage:
                hash.append(1)
            else:
                hash.append(0)
    return hash
 
# 计算汉明距离
def Hamming_distance(hash1,hash2):
    num = 0
    for index in range(len(hash1)):
        if hash1[index] != hash2[index]:
            num += 1
    return num
 
if __name__ == "__main__":
    file_lst = os.listdir(root_dir_1)
    image_nums = len(file_lst)   #未标记图片总数
 
    for img_num in range(image_nums):
        addr1 = root_dir_1 + '/' + "image_" + str(img_num) + suffix
        img_1 = cv2.imread(addr1)
        label = 0       #找到相应类别标记位
        target_lst = []  #存储对应7种分类对应数字名
        for dir_name in glob.glob(os.path.join(root_dir_2, "*")):
            target = os.path.split(dir_name)[-1]   #依次取每个类别下图片与未分类图片进行比较
            if target not in target_lst:            #若相似度很高,则将未分类图片标记为此分类图片对应的类别
                target_lst.append(target)
                token2target[len(target_lst)-1] = target
            for img_name in glob.glob(os.path.join(dir_name, "*" + suffix)):
                img_2 = cv2.imread(img_name)
                degree = classify_aHash(img_1, img_2)  #degree越小,相似度越高
                num = len(target_lst) - 1
                if degree < 3:           #degree=0,1,2,若限制degree=0,会有很多图片无法匹配到相应类别
                    d1 = tuple(str(num))
                    data.append(d1)
                    label = 1
                    break
            if label == 1:
                break
        if label == 0:
            d2 = tuple("N")  #如果没发现相应的分类,则标记为N
            data.append(d2)
        print(data)
    print(token2target)
    print(data)
    #将生成data导出为CSV
    with open("./pic_match.csv", "w", newline="", encoding='utf-8-sig') as datacsv:
        csvwriter = csv.writer(datacsv, dialect=("excel"))
        csvwriter.writerows(data)

//

  • 4
    点赞
  • 16
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
### 回答1: 可以使用 Python 的图像处理库来区分两张图片的差异。 例如,可以使用 Python 的 Pillow 库读取两张图片,然后将它们转换为数组,再通过 NumPy 库的相关函数来计算两个数组的差异,以此来判断两张图片是否相同。 下面是一个示例代码: ``` from PIL import Image import numpy as np # 读取图片并转换为数组 image1 = Image.open('image1.jpg') array1 = np.array(image1) image2 = Image.open('image2.jpg') array2 = np.array(image2) # 计算两个数组的差异 difference = array1 - array2 # 如果差异为 0,则两张图片相同 if not np.any(difference): print('The images are the same.') else: print('The images are different.') ``` 请注意,上面的代码仅供参考,可能并不能在所有情况下都能准确地判断两张图片的差异。对于更复杂的图片比较,可能需要使用更复杂的算法和方法。 ### 回答2: 使用Python可以使用以下步骤来区分两个图片的差异: 1. 导入必要的库,如OpenCV和NumPy: ``` import cv2 import numpy as np ``` 2. 读取两个图片: ``` img1 = cv2.imread('image1.jpg') img2 = cv2.imread('image2.jpg') ``` 3. 将两个图片转换为灰度图像: ``` gray_img1 = cv2.cvtColor(img1, cv2.COLOR_BGR2GRAY) gray_img2 = cv2.cvtColor(img2, cv2.COLOR_BGR2GRAY) ``` 4. 对比两个灰度图像的差异: ``` diff = cv2.absdiff(gray_img1, gray_img2) ``` 5. 对差异图像进行二值化处理,将差异像素设为255(白色),非差异像素设为0(黑色): ``` _, threshold = cv2.threshold(diff, 30, 255, cv2.THRESH_BINARY) ``` 6. 查找差异图像中的轮廓: ``` contours, _ = cv2.findContours(threshold, cv2.RETR_EXTERNAL, cv2.CHAIN_APPROX_SIMPLE) ``` 7. 绘制轮廓到原始图片上: ``` cv2.drawContours(img1, contours, -1, (0, 255, 0), 2) ``` 8. 保存包含轮廓的图片: ``` cv2.imwrite('image_with_contours.jpg', img1) ``` 这样,最终你会得到一张图片,其中显示了两个原始图片的差异。差异的区域会被轮廓框出来。 ### 回答3: 使用Python代码区分两个图片的差异可以通过图像处理和比较方法来实现。下面是一个简单的例子: 首先,需要安装Pillow库,它是Python中常用的图像处理库。 ```python from PIL import Image def compare_images(image1_path, image2_path): # 打开两个图片文件 image1 = Image.open(image1_path) image2 = Image.open(image2_path) # 将两个图片转换为相同的尺寸 image1 = image1.resize(image2.size) # 获取两个图片的像素值 pixels1 = list(image1.getdata()) pixels2 = list(image2.getdata()) # 计算差异像素数量 difference = 0 for i in range(len(pixels1)): # 按RGB通道计算差异 diff = abs(pixels1[i][0] - pixels2[i][0]) + \ abs(pixels1[i][1] - pixels2[i][1]) + \ abs(pixels1[i][2] - pixels2[i][2]) difference += diff # 计算差异度 total_pixels = image1.width * image1.height difference_percent = difference / (total_pixels * 3 * 255) * 100 return difference_percent ``` 上述代码中,首先使用`Image.open`函数打开两个图片文件。然后,使用`resize`方法将两个图片转换为相同的尺寸,以便后续逐个像素进行比较。接下来,使用`getdata`方法获取两个图片的像素值,并通过计算差异像素数量来得到图片的差异度。 最后,将差异度归一化为0到100的百分比的形式,并返回结果。 使用这个函数,你可以在调用时传入两个图片的文件路径,并得到他们之间的差异度,从而区分两个图片的差异。

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值