OpenCV书签 #互信息的原理与相似图片搜索实验

1. 介绍

互信息(Mutual Information) 是信息论中的一个概念,用于衡量两个随机变量之间的关联程度。在图像处理和计算机视觉中,互信息常被用来度量两幅图像之间的相似性。

互信息可以看成是一个随机变量中包含的关于另一个随机变量的信息量,或者说是一个随机变量由于已知另一个随机变量而减少的不肯定性对任意随机变量。比如,不会由于你知道了一个事件,反而使另一个事件的不确定度增加。

1.1 助记

这里引用知乎神贴 - 什么是互信息 中的比喻:

比如说有一天你的女神突然问你:“你猜猜我的爱豆是TFboys里的谁?”
唉,这要是猜不到,女神的心岂不是很完蛋,追求女神的路从此就凉了。
正当你非常不确定的时候,女神又说:“给你个提示吧,名字是四个字。”
此时你特别开心,因为对于答案的不确定性一下子就减少了(当然这个例子比较极端,不确定性直接降为0)

所以,互信息的意思就是,通过了知道一个变量减少了对另一个变量的不确定性。

这个不确定性减少的量也就是互信息的大小。

 

2. 魔法

使用互信息来度量两幅图像之间的相似性,只需要三步核心操作:

  1. 图像配准: Image Registration,在使用互信息进行相似性度量之前,通常需要对图像进行配准,确保它们对齐。这可以通过一些配准算法来实现,确保图像的空间位置相匹配。
  2. 计算互信息: 使用配准后的图像,计算它们之间的互信息。可以使用一些图像处理库,如 Scikit-ImageOpenCV,来实现互信息的计算。
  3. 相似性度量: 可以根据具体的需求和任务,设定一个阈值,选择互信息值高于该阈值的图像作为相似图像。

 

3. 实验

3.1 魔法拆解

第一步:图像配准

图像配准(Image Registration),在使用互信息进行相似性度量之前,通常需要对图像进行配准,确保它们对齐。这可以通过一些配准算法来实现,确保图像的空间位置相匹配。

# 读取图像
img1 = cv2.imread(img1_path)
img2 = cv2.imread(img2_path)

# 检查图像形状,保证两个图像必须具有相同的尺寸,即相同的高度和宽度
if img1.shape != img2.shape:
    # 调整图像大小,使它们具有相同的形状
    img2 = cv2.resize(img2, (img1.shape[1], img1.shape[0]))

第二步:计算互信息

使用配准后的图像,计算它们之间的互信息。可以使用一些图像处理库,如 Scikit-ImageOpenCV,来实现互信息的计算。

这里我们先使用灰度直方图来计算互信息:

# 将直方图归一化
# 即将直方图中的每个元素除以直方图的总和,以确保它们的和为1
# 这样做的目的是为了消除不同图像的像素数量差异,使得直方图的形状更具可比性
hist1 /= hist1.sum()
hist2 /= hist2.sum()

# 计算两个归一化直方图的互信息
mutual_score = np.sum(np.minimum(hist1, hist2))
print(f"图像2:{os.path.basename(img2_path)} 与 图像1:{img1_path} 的相似性指数:{mutual_score}")

这里首先使用 np.minimum 函数计算 hist1 和 hist2 中每个对应位置的元素的较小值,然后使用 np.sum 计算这些较小值的总和。这个总和即为互信息的计算结果,即互信息的大小。

第三步:相似性度量

根据具体的需求和任务,设定一个阈值,选择互信息值高于该阈值的图像作为相似图像。

# 在实际应用中,可以根据互信息的阈值来判断两幅图像是否相似
if mutual_score > 0.7:
    print("两图相似")
else:
    print("两图不相似")

 

3.2 小实验

实验场景

计算两幅灰度图像的直方图,然后通过归一化的直方图计算它们的互信息。最后,根据设定的阈值判断两幅图像是否相似。

实验代码

"""
以图搜图:互信息(Mutual Information)查找相似图像的原理与实现
实验环境:Win10 | python 3.12.1 | OpenCV 4.9.0 | numpy 1.26.3 | Matplotlib 3.8.2
实验时间:2024-01-24
实验目的:计算两幅灰度图像的直方图,然后通过归一化的直方图计算它们的互信息。最后,根据设定的阈值判断两幅图像是否相似
实例名称:mutualInformation_v1.0.py
"""

import os
import cv2
import time
import numpy as np

time_start = time.time()

# 目标图像素材库文件夹路径
database_dir = '../../P0_Doc/img_data/'

# 读取查询图像和数据库中的图像
img1_path = database_dir + 'car-101.jpg'
img2_path = database_dir + 'car-102.jpg'
img2_path = database_dir + 'car-103.jpg'

# 读取两幅图像
img1 = cv2.imread(img1_path, cv2.IMREAD_GRAYSCALE)
img2 = cv2.imread(img2_path, cv2.IMREAD_GRAYSCALE)

# 检查图像形状,保证两个图像必须具有相同的尺寸,即相同的高度和宽度
if img1.shape != img2.shape:
    # 调整图像大小,使它们具有相同的形状
    img2 = cv2.resize(img2, (img1.shape[1], img1.shape[0]))

# 使用 OpenCV 中的 calcHist 函数计算直方图
hist1 = cv2.calcHist([img1], [0], None, [256], [0, 256])
hist2 = cv2.calcHist([img2], [0], None, [256], [0, 256])

# 将直方图归一化
# 即将直方图中的每个元素除以直方图的总和,以确保它们的和为1
# 这样做的目的是为了消除不同图像的像素数量差异,使得直方图的形状更具可比性
hist1 /= hist1.sum()
hist2 /= hist2.sum()

# 计算两个归一化直方图的互信息
# 首先,使用 np.minimum 函数计算了 hist1 和 hist2 中每个对应位置的元素的较小值,然后使用 np.sum 计算了这些较小值的总和。这个总和即为互信息的计算结果
mutual_score = np.sum(np.minimum(hist1, hist2))
print(f"图像2:{os.path.basename(img2_path)} 与 图像1:{img1_path} 的相似性指数:{mutual_score}")

# 在实际应用中,可以根据互信息的阈值来判断两幅图像是否相似
if mutual_score > 0.7:
    print("两图相似")
else:
    print("两图不相似")

time_end = time.time()
print(f"耗时:{time_end - time_start}")

输出打印:

图像2:car-103.jpg 与 图像1../../P0_Doc/img_data/car-101.jpg 的相似性指数:0.6439836025238037
两图不相似
耗时:0.0687875747680664

示例中的互信息计算,利用了灰度直方图比较方法,通过比较两幅图像的灰度直方图,评估它们的相似性。
注意: 如果这里不进行图像配准,不保证等高等宽,互信息的相似度也会产生差异。

插播: 直方图是一种统计图像中像素值分布的方法,它可以描述图像中不同灰度级别的像素数量。关于直方图的原理与实验,可详见:OpenCV书签 #直方图算法的原理与相似图片搜索实验

 

为什么要用灰度直方图呢?有什么关联和区别?

互信息和灰度直方图算法是两个不同的概念,虽然在图像处理中它们有时候会交叉使用,但是还是需要注意:

互信息(Mutual Information):

  • 互信息是一种信息论中的概念,用于衡量两个随机变量之间的关联程度。
  • 在图像处理中,互信息通常用于度量两幅图像之间的相似性。它考虑的是两个变量的联合概率分布与各自边缘概率分布之间的关系。
  • 互信息越高,表示两个随机变量之间的关联越强。

灰度直方图算法(Image Histogram):

  • 灰度直方图是一种统计图像中像素值分布的方法,它描述了图像中不同灰度级别的像素数量。
  • 在灰度直方图算法中,图像的直方图表示了图像中每个灰度级别的像素数量。
  • 直方图比较是通过比较两幅图像的灰度直方图来评估它们的相似性。常见的比较方法包括交叉相关性、巴氏距离等。

虽然互信息的计算可以涉及到灰度直方图,但两者并不等同。互信息是一种更广泛的概念,而灰度直方图算法是一种具体的图像表示和相似性度量方法。

 

4. 测试

4.1 实验素材

MutualInformation-001

4.2 实验过程

纵向比较

通过 sklearn.metrics 提供的各类互信息比较算法,纵向比较两图的相似性。

实验代码
"""
以图搜图:互信息(Mutual Information)查找相似图像的原理与实现
实验环境:Win10 | python 3.12.1 | OpenCV 4.9.0 | numpy 1.26.3 | Matplotlib 3.8.2
实验时间:2024-01-24
实验目的:通过 sklearn.metrics 提供的各类互信息比较算法,纵向比较两图的相似性
实例名称:mutualInformation_v2.1.py
"""

import time
import cv2
from sklearn import metrics as mr
# from sklearn.metrics.cluster import normalized_mutual_info_score
# from sklearn.metrics.cluster import adjusted_mutual_info_score

def mutual_information_score(img1_reshape, img2_reshape):
    # mutual_info_score:标准互信息分数。度量两个随机变量之间的互信息,可用于衡量它们之间的关联性
    m_time_start = time.time()
    score_info = mr.mutual_info_score(img1_reshape, img2_reshape)
    m_time_end = time.time()
    print(f"互信息 mutual_info_score:标准互信息分数:{score_info},耗时:{m_time_end - m_time_start}")

    # normalized_mutual_info_score:归一化互信息分数。用于衡量两个聚类结果之间的相似性,考虑到了数据集的随机性。通常被用作聚类性能的评估指标
    m_time_start = time.time()
    score_normal = mr.normalized_mutual_info_score(img1_reshape, img2_reshape)
    m_time_end = time.time()
    print(f"互信息 normalized_mutual_info_score:归一化互信息分数:{score_normal},耗时:{m_time_end - m_time_start}")

    # adjusted_mutual_info_score:调整的归一化互信息分数。该分数考虑到了数据集的随机性,对标准互信息进行了调整,以提供更准确的聚类性能度量
    m_time_start = time.time()
    score_adjusted = mr.adjusted_mutual_info_score(img1_reshape, img2_reshape)
    m_time_end = time.time()
    print(f"互信息 adjusted_mutual_info_score:调整的归一化互信息分数:{score_adjusted},耗时:{m_time_end - m_time_start}")

    # v_measure_score: V-Measure(V值)分数。结合了聚类的均匀性和完整性,提供了一个综合的聚类性能度量
    m_time_start = time.time()
    score_measure = mr.v_measure_score(img1_reshape, img2_reshape)
    m_time_end = time.time()
    print(f"互信息 v_measure_score:V-Measure(V值)分数:{score_measure},耗时:{m_time_end - m_time_start}")

    # homogeneity_score: 均匀性分数。用于度量每个聚类的成员是否都属于同一类别,高均匀性表示每个聚类都很“纯净”
    m_time_start = time.time()
    score_homogeneity = mr.homogeneity_score(img1_reshape, img2_reshape)
    m_time_end = time.time()
    print(f"互信息 homogeneity_score:均匀性分数:{score_homogeneity},耗时:{m_time_end - m_time_start}")

    # completeness_score: 完整性分数。度量每个真实类别的成员是否都被分配到同一聚类中,高完整性表示每个类别都被很好地捕捉
    m_time_start = time.time()
    score_completeness = mr.completeness_score(img1_reshape, img2_reshape)
    m_time_end = time.time()
    print(f"互信息 completeness_score:完整性分数:{score_completeness},耗时:{m_time_end - m_time_start}")

    # adjusted_rand_score: 调整的兰德指数。度量两个聚类结果之间的相似性,考虑了数据集的随机性
    m_time_start = time.time()
    score_adjusted_rand = mr.adjusted_rand_score(img1_reshape, img2_reshape)
    m_time_end = time.time()
    print(f"互信息 adjusted_rand_score:调整的兰德指数:{score_adjusted_rand},耗时:{m_time_end - m_time_start}")

    # fowlkes_mallows: Fowlkes-Mallows指数。度量两个聚类结果之间的精度,考虑了聚类的精确度
    m_time_start = time.time()
    score_fowlkes_mallows = mr.fowlkes_mallows_score(img1_reshape, img2_reshape)
    m_time_end = time.time()
    print(f"互信息 fowlkes_mallows_score:Fowlkes-Mallows指数:{score_fowlkes_mallows},耗时:{m_time_end - m_time_start}")

    # silhouette_score: 轮廓系数。度量每个样本在其自己的聚类中的相似性与最近的相邻聚类之间的不相似性
    # 输入是两张图片,而不是聚类数据,计算轮廓系数就不太适用了,因为轮廓系数通常用于衡量聚类的质量
    # score_silhouette = mr.silhouette_score(img1.reshape(-1, 1), img2.reshape(-1, 1))

if __name__ == "__main__":

    time_start = time.time()

    # 目标图像素材库文件夹路径
    database_dir = '../../P0_Doc/img_data/'

    # 读取查询图像和数据库中的图像
    img1_path = database_dir + 'car-101.jpg'
    img2_path = database_dir + 'car-102.jpg'
    img1_path = database_dir + 'apple-101.jpg'
    img2_path = database_dir + 'apple-102.jpg'

    print(f"互信息相似度比较,图像1:{img1_path},图像2:{img2_path}")

    # 读取图像
    img1 = cv2.imread(img1_path)
    img2 = cv2.imread(img2_path)

    # 检查图像形状,保证两个图像必须具有相同的尺寸,即相同的高度和宽度
    if img1.shape != img2.shape:
        # 调整图像大小,使它们具有相同的形状
        img2 = cv2.resize(img2, (img1.shape[1], img1.shape[0]))

    print("------------------------- 原图像 互信息相似度           -------------------------")

    # 原图应用于常见的互信息相关方法和算法及相似度
    mutual_information_score(img1.reshape(-1), img2.reshape(-1))

    # 将图像转换为灰度图像
    img1_gray = cv2.cvtColor(img1, cv2.COLOR_BGR2GRAY)
    img2_gray = cv2.cvtColor(img2, cv2.COLOR_BGR2GRAY)

    print("------------------------- 图像转换为灰度图像 互信息相似度 -------------------------")

    # 原图灰度图像应用于常见的互信息相关方法和算法及相似度
    mutual_information_score(img1_gray.reshape(-1), img2_gray.reshape(-1))

    time_end = time.time()
    print(f"耗时:{time_end - time_start}")

sklearn.metrics.cluster 提供了很多互信息相关的方法和算法,主要用于不同的评估场景,涉及到聚类的性能和相似性的测量:

互信息算法/方法说明
adjusted_mutual_info_score调整的归一化互信息分数。该分数考虑到了数据集的随机性,对标准互信息进行了调整,以提供更准确的聚类性能度量
normalized_mutual_info_score归一化互信息分数。用于衡量两个聚类结果之间的相似性,考虑到了数据集的随机性。通常被用作聚类性能的评估指标
mutual_info_score标准互信息分数。度量两个随机变量之间的互信息,可用于衡量它们之间的关联性
v_measure_scoreV-Measure(V值)分数。结合了聚类的均匀性和完整性,提供了一个综合的聚类性能度量
homogeneity_score均匀性分数。用于度量每个聚类的成员是否都属于同一类别,高均匀性表示每个聚类都很“纯净”
completeness_score完整性分数。度量每个真实类别的成员是否都被分配到同一聚类中,高完整性表示每个类别都被很好地捕捉
adjusted_rand_score调整的兰德指数。度量两个聚类结果之间的相似性,考虑了数据集的随机性
fowlkes_mallows_scoreFowlkes-Mallows指数。度量两个聚类结果之间的精度,考虑了聚类的精确度
silhouette_score轮廓系数。度量每个样本在其自己的聚类中的相似性与最近的相邻聚类之间的不相似性

img1_reshape.reshape(-1)img1_reshape.reshape(-1, 1)NumPy 中用于改变数组形状的方法。
reshape(-1) 用于展平数组,而 reshape(-1, 1) 用于将数组转换为包含单个列的二维数组。区别在于得到的形状不同。

  • img1_reshape.reshape(-1): 这将多维数组 img1_reshape 转换为一个展平的一维数组。NumPy 会根据数组的大小自动计算另一维度的大小,以确保总的元素数量不变。这通常用于将多维数组展平为一维数组,方便某些操作。
  • img1_reshape.reshape(-1, 1): 这将多维数组 img1_reshape 转换为一个包含单个列的二维数组。其中,-1 表示自动计算行数,而 1 表示只有一列。这通常用于确保某些函数接受的输入是二维数组,并且每个样本在单独的列中。

关于这些算法/方法,源码提供了使用 Demo,可以通过源码了解更多,比如:

    Examples
    --------

    Perfect labelings are both homogeneous and complete, hence have
    score 1.0::

      >>> from sklearn.metrics.cluster import normalized_mutual_info_score
      >>> normalized_mutual_info_score([0, 0, 1, 1], [0, 0, 1, 1])
      ... # doctest: +SKIP
      1.0
      >>> normalized_mutual_info_score([0, 0, 1, 1], [1, 1, 0, 0])
      ... # doctest: +SKIP
      1.0

    If classes members are completely split across different clusters,
    the assignment is totally in-complete, hence the NMI is null::

      >>> normalized_mutual_info_score([0, 0, 0, 0], [0, 1, 2, 3])
      ... # doctest: +SKIP
      0.0

实验代码输出打印:

互信息相似度比较,图像1../../P0_Doc/img_data/apple-101.jpg,图像2../../P0_Doc/img_data/apple-102.jpg
------------------------- 原图像 互信息相似度           -------------------------
互信息 mutual_info_score:标准互信息分数:0.3334500090190323,耗时:0.4178807735443115
互信息 normalized_mutual_info_score:归一化互信息分数:0.1442800195961649,耗时:0.704125165939331
互信息 adjusted_mutual_info_score:调整的归一化互信息分数:0.13897879139743463,耗时:27.81876230239868
互信息 v_measure_score:V-Measure(V值)分数:0.14428001959616493,耗时:0.6462442874908447
互信息 homogeneity_score:均匀性分数:0.1393427892877539,耗时:0.6453077793121338
互信息 completeness_score:完整性分数:0.14957997661474184,耗时:0.6043868064880371
互信息 adjusted_rand_score:调整的兰德指数:0.5983897700024668,耗时:0.39893531799316406
互信息 fowlkes_mallows_score:Fowlkes-Mallows指数:0.7874850623258953,耗时:0.3939492702484131
------------------------- 图像转换为灰度图像 互信息相似度 -------------------------
互信息 mutual_info_score:标准互信息分数:0.3114601065499291,耗时:0.12766075134277344
互信息 normalized_mutual_info_score:归一化互信息分数:0.14164731215313434,耗时:0.2203817367553711
互信息 adjusted_mutual_info_score:调整的归一化互信息分数:0.13105360189408213,耗时:6.436821222305298
互信息 v_measure_score:V-Measure(V值)分数:0.14164731215313434,耗时:0.20248985290527344
互信息 homogeneity_score:均匀性分数:0.1361761519096563,耗时:0.25429248809814453
互信息 completeness_score:完整性分数:0.14757650531529792,耗时:0.19750332832336426
互信息 adjusted_rand_score:调整的兰德指数:0.6065766924224203,耗时:0.1396336555480957
互信息 fowlkes_mallows_score:Fowlkes-Mallows指数:0.7932503296629234,耗时:0.13862109184265137
耗时:39.388914585113525

通过简单的测试,从纵向比较两图的相似性结果来看:

原图像

  • 互信息使用 adjusted_mutual_info_score 算法,相似度得分最低,耗时最长。
  • 互信息使用 fowlkes_mallows_score 算法,相似度得分最高,耗时最短。

图像灰度之后,去除了彩色像素处理,速度明显变快很多:

  • 互信息使用 adjusted_mutual_info_score 算法,相似度得分最低,耗时最长。
  • 互信息使用 fowlkes_mallows_score 算法,相似度得分最高。
  • 互信息使用 mutual_info_score 算法,耗时最短。

横向比较

通过使用某一互信息计算方法,配合相似阈值,在测试图片素材库中搜索查找与目标图像相似的图片。

实验代码
"""
以图搜图:互信息(Mutual Information)查找相似图像的原理与实现
实验环境:Win10 | python 3.12.1 | OpenCV 4.9.0 | numpy 1.26.3 | Matplotlib 3.8.2
实验时间:2024-01-24
实验目的:通过使用某一互信息计算方法,配合相似阈值,在测试图片素材库中搜索查找与目标图像相似的图片
实例名称:mutualInformation_v2.2.py
"""

import os
import time
import cv2
import matplotlib.pyplot as plt
from sklearn import metrics as mr

def search_mutual_info(img_org_path, database_paths):

    # 将原图像转换为灰度图像
    img_original_gray = cv2.cvtColor(cv2.imread(img_org_path), cv2.COLOR_BGR2GRAY)

    # 遍历数据库图像并比较相似度
    similaritys = []
    for img_path in database_paths:
        # 获取相似图像文件路径
        img_target_path = os.path.join(script_dir, database_dir_path, img_path)

        # 将图像转换为灰度图像
        img_target_gray = cv2.cvtColor(cv2.imread(img_target_path), cv2.COLOR_BGR2GRAY)

        # 检查图像形状,保证两个图像必须具有相同的尺寸,即相同的高度和宽度
        if img_original_gray.shape != img_target_gray.shape:
            # 调整图像大小,使它们具有相同的形状
            img_target_gray = cv2.resize(img_target_gray, (img_original_gray.shape[1], img_original_gray.shape[0]))

        # 灰度图像应用于常见的互信息相关方法和算法及相似度
        score = mr.fowlkes_mallows_score(img_original_gray.reshape(-1), img_target_gray.reshape(-1))
        # print(f"互信息 fowlkes_mallows_score:{score}")

        # 将结果保存到列表中(仅保留相似值大于等于 0.7 的图像)
        if (score >= 0.7):
            similaritys.append((os.path.relpath(img_target_path), score))
            # print(f"图像名称:{img_target_path},与目标图像 {os.path.basename(img_resouce)} 的近似值:{ssim[0]}")

    return similaritys


def show_similar_images(similar_images, images_per_column=4):
    # 计算总共的图片数量
    num_images = len(similar_images)
    # 计算所需的行数
    num_rows = (num_images + images_per_column - 1) // images_per_column

    # 创建一个子图,每行显示 images_per_column 张图片
    fig, axes = plt.subplots(num_rows, images_per_column, figsize=(12, 15), squeeze=False)

    # 遍历每一行
    for row in range(num_rows):
        # 遍历每一列
        for col in range(images_per_column):
            # 计算当前图片在列表中的索引
            index = row * images_per_column + col
            # 检查索引是否越界
            if index < num_images:
                # 获取当前相似图片的路径和相似度
                image_path = similar_images[index][0]
                similarity = similar_images[index][1]

                # 读取图片并转换颜色通道
                image = cv2.cvtColor(cv2.imread(image_path), cv2.COLOR_BGR2RGB)

                # 在子图中显示图片
                axes[row, col].imshow(image)
                # 设置子图标题,包括图片路径和相似度
                axes[row, col].set_title(f"Similar Image: {os.path.basename(image_path)} \n Similar Score: {similarity}")
                # 关闭坐标轴
                axes[row, col].axis('off')
    # 显示整个图
    plt.show()


if __name__ == "__main__":

    time_start = time.time()

    # 获取当前执行脚本所在目录
    script_dir = os.path.dirname(__file__)

    # 目标图像素材库文件夹路径
    database_dir_path = '../../P0_Doc/img_data/'
    # 指定测试图像文件扩展名
    img_suffix = ['.jpg', '.jpeg', '.png', '.bmp', '.gif']

    # 获取测试图像库中所有文件
    all_files = os.listdir(os.path.join(script_dir, database_dir_path))
    # 筛选测试图像库中指定后缀的图像文件
    img_files = [img_file for img_file in all_files if any(img_file.endswith(suffix) for suffix in img_suffix)]
    # 获取测试图像库中所有图像的路径
    img_files_path = [os.path.join(database_dir_path, filename) for filename in img_files]

    # 获取目标测试图像的全路径
    img_org_path = os.path.join(script_dir, database_dir_path, 'apple-101.jpg')
    img_org_path = os.path.join(script_dir, database_dir_path, 'iphone15-001.jpg')
    img_org_path = os.path.join(script_dir, database_dir_path, 'car-101.jpg')

    # 原图应用于常见的互信息相关方法和算法及相似度
    img_search_results = search_mutual_info(img_org_path, img_files_path)

    # 按相似度降序排序
    img_search_results.sort(key=lambda item: item[1], reverse=True)
    for similarity in img_search_results:
        print(f"图像:{similarity[0]},与目标图像 {os.path.basename(img_org_path)} 的相似值:{similarity[1]}")

    time_end = time.time()
    print(f"测试素材库图像数量:{len(img_files)},耗时:{time_end - time_start}")

    # 显示目标相似图像
    show_similar_images(img_search_results)

第一组测试

测试结果输出打印:

图像:..\..\P0_Doc\img_data\apple-101.jpg,与目标图像 apple-101.jpg 的相似值:1.0
图像:..\..\P0_Doc\img_data\apple-103.jpg,与目标图像 apple-101.jpg 的相似值:0.8185132476782856
图像:..\..\P0_Doc\img_data\apple-105.jpg,与目标图像 apple-101.jpg 的相似值:0.8069378648696433
图像:..\..\P0_Doc\img_data\apple-102.jpg,与目标图像 apple-101.jpg 的相似值:0.7932928654393832
图像:..\..\P0_Doc\img_data\iphone15-001.jpg,与目标图像 apple-101.jpg 的相似值:0.771736183796336
图像:..\..\P0_Doc\img_data\iphone15-002.jpg,与目标图像 apple-101.jpg 的相似值:0.7715952189959523
图像:..\..\P0_Doc\img_data\iphone15-003.jpg,与目标图像 apple-101.jpg 的相似值:0.7707667748546456
图像:..\..\P0_Doc\img_data\apple-109.jpg,与目标图像 apple-101.jpg 的相似值:0.7528870440060801
图像:..\..\P0_Doc\img_data\pear-201.jpg,与目标图像 apple-101.jpg 的相似值:0.7208068021727383
图像:..\..\P0_Doc\img_data\apple-104.jpg,与目标图像 apple-101.jpg 的相似值:0.7066478416742119
测试素材库图像数量:48,耗时:7.807173013687134

互信息相似图片查找效果可视化:
MutualInformation-002

第二组测试

测试结果输出打印:

图像:..\..\P0_Doc\img_data\iphone15-001.jpg,与目标图像 iphone15-001.jpg 的相似值:1.0
图像:..\..\P0_Doc\img_data\iphone15-002.jpg,与目标图像 iphone15-001.jpg 的相似值:0.9985563921236876
图像:..\..\P0_Doc\img_data\iphone15-003.jpg,与目标图像 iphone15-001.jpg 的相似值:0.996718480433257
图像:..\..\P0_Doc\img_data\apple-102.jpg,与目标图像 iphone15-001.jpg 的相似值:0.8165921658980023
图像:..\..\P0_Doc\img_data\apple-104.jpg,与目标图像 iphone15-001.jpg 的相似值:0.7733543220682563
图像:..\..\P0_Doc\img_data\apple-101.jpg,与目标图像 iphone15-001.jpg 的相似值:0.7717483756822613
图像:..\..\P0_Doc\img_data\apple-103.jpg,与目标图像 iphone15-001.jpg 的相似值:0.7579136253684923
图像:..\..\P0_Doc\img_data\apple-105.jpg,与目标图像 iphone15-001.jpg 的相似值:0.7540519846067019
测试素材库图像数量:48,耗时:30.248269081115723

互信息相似图片查找效果可视化:
MutualInformation-003

 

5. 总结

在图像处理中,互信息可以用来衡量两幅图像的相似性,尤其在配准(registration)和图像匹配(image matching)的任务中经常被使用。通过最大化互信息,可以找到两个图像之间的最佳匹配,以便更好地对齐它们。

优点

  1. 对比度不敏感: 互信息对比度不敏感,适用于对比度较大的图像。
  2. 非线性关系: 互信息适用于具有非线性关系的图像数据,因此对于复杂的图像相似性度量具有一定优势。
  3. 不依赖特定颜色空间: 互信息在计算过程中不依赖于特定的颜色空间,因此对于不同颜色表示的图像也能够有效计算相似性。

缺点

  1. 图像配准: 如果两张图片尺寸相同,还是能在一定程度上表征两张图片的相似性的。但是,大部分情况下图片的尺寸不相同,如果把两张图片尺寸调成相同的话,又会让原来很多的信息丢失,所以很难把握。
  2. 计算复杂度: 互信息的计算涉及到直方图的操作,因此在图像尺寸较大、直方图维度较高时计算复杂度较高。
  3. 对噪声敏感: 互信息对噪声敏感,噪声可能对互信息的计算产生一定影响。
  4. 不考虑空间结构: 互信息不考虑像素在图像中的空间结构,可能对图像中物体的空间排列不敏感。

 

6. 问题

异常现象1

    from skimage.feature import mutual_info_score
ImportError: cannot import name 'mutual_info_score' from 'skimage.feature' (D:\Tp_Mylocal\20_Install\python-3.9.13\lib\site-packages\skimage\feature\__init__.py)

可以尝试安装或更新 scikit-image 依赖项:

pip install --upgrade scikit-image
Installing collected packages: pillow, packaging, numpy, networkx, lazy_loader, tifffile, scipy, imageio, scikit-image
  WARNING: Failed to write executable - trying to use .deleteme logic
ERROR: Could not install packages due to an OSError: [WinError 2] 系统找不到指定的文件。: 'C:\\Python312\\Scripts\\f2py.exe' -> 'C:\\Python312\\Scripts\\f2py.exe.deleteme'

f2py.exeFortran 到 Python 接口生成器的可执行文件。它用于将 Fortran 代码集成到 Python 中,使得可以在 Python 中调用 Fortran 编写的函数或子程序。

在正常情况下,f2py.exe 应该包含在 NumPy 库中。由于我本地环境没有,可能是之前安装出现的问题。这里重新安装 NumPy,以获取缺失的文件:

pip install --upgrade --force-reinstall numpy

还是不行的话,尝试一下在应用程序中先卸载 Python,再重装最新版的 Python。

以我的案例看,我本地的 Python 版本是 python 3.9.13,卸载后重装了最新版的 Python 3.12.1。
如果重装 python 时,选择了新的路径,这里同样需要确认或调整一下系统环境变量中关于 python 的路径指向。
然后重新安装本地运行 python 脚本缺失的依赖项,包括 scikit-learn。
然后就可以了。也没有类似需要 f2py.exe 的错误提示。
需要注意的是,目前互信息提供的相似算法都在 from sklearn.metrics.cluster 下。

安装 scikit-learn 依赖项:

pip install scikit-learn

 

异常现象2

Traceback (most recent call last):
  File "D:\Ct_ iSpace\Wei\Python\iPython\T30_Algorithm\P2_Algo\03_MutualInformation\mutualInformation_v2.1.py", line 119, in <module>
    mutual_information_score(img1.reshape(-1), img2.reshape(-1))
  File "D:\Ct_ iSpace\Wei\Python\iPython\T30_Algorithm\P2_Algo\03_MutualInformation\mutualInformation_v2.1.py", line 89, in mutual_information_score
    score_silhouette = mr.silhouette_score(img1_reshape, img2_reshape)
                       ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
  File "D:\Ct_ iSpace\Wei\Python\iPython\T00_Env\Lib\site-packages\sklearn\utils\_param_validation.py", line 213, in wrapper
    return func(*args, **kwargs)
           ^^^^^^^^^^^^^^^^^^^^^
  File "D:\Ct_ iSpace\Wei\Python\iPython\T00_Env\Lib\site-packages\sklearn\metrics\cluster\_unsupervised.py", line 140, in silhouette_score
    return np.mean(silhouette_samples(X, labels, metric=metric, **kwds))
                   ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
  File "D:\Ct_ iSpace\Wei\Python\iPython\T00_Env\Lib\site-packages\sklearn\utils\_param_validation.py", line 186, in wrapper
    return func(*args, **kwargs)
           ^^^^^^^^^^^^^^^^^^^^^
  File "D:\Ct_ iSpace\Wei\Python\iPython\T00_Env\Lib\site-packages\sklearn\metrics\cluster\_unsupervised.py", line 267, in silhouette_samples
    X, labels = check_X_y(X, labels, accept_sparse=["csr"])
                ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
  File "D:\Ct_ iSpace\Wei\Python\iPython\T00_Env\Lib\site-packages\sklearn\utils\validation.py", line 1192, in check_X_y
    X = check_array(
        ^^^^^^^^^^^^
  File "D:\Ct_ iSpace\Wei\Python\iPython\T00_Env\Lib\site-packages\sklearn\utils\validation.py", line 989, in check_array
    raise ValueError(msg)
ValueError: Expected 2D array, got 1D array instead:
array=[253 253 253 ... 253 253 253].
Reshape your data either using array.reshape(-1, 1) if your data has a single feature or array.reshape(1, -1) if it contains a single sample.

异常原因: silhouette_score 函数期望接收的数据是一个2D数组,而 Demo 提供的是一个1维数组。因为输入是两张图片,而不是聚类数据,计算轮廓系数就不太适用了,因为轮廓系数通常用于衡量聚类的质量。

解决方案: 使用 array.reshape(-1, 1) 来将数据转换成2D数组。

 

7. 参考资料

互信息 - 百度百科
什么是「互信息」?
互信息是什么?

 

8. 系列书签

均值哈希算法: OpenCV书签 #均值哈希算法的原理与相似图片搜索实验
感知哈希算法: OpenCV书签 #感知哈希算法的原理与相似图片搜索实验
差值哈希算法: OpenCV书签 #差值哈希算法的原理与相似图片搜索实验
直方图算法: OpenCV书签 #直方图算法的原理与相似图片搜索实验
余弦相似度: OpenCV书签 #余弦相似度的原理与相似图片/相似文件搜索实验
SSIM结构相似性: OpenCV书签 #结构相似性SSIM算法的原理与图片相似性实验

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:/a/347879.html

如若内容造成侵权/违法违规/事实不符,请联系我们进行投诉反馈qq邮箱809451989@qq.com,一经查实,立即删除!

相关文章

【网站项目】基于SSM的251国外摇滚乐队交流和周边售卖系统

&#x1f64a;作者简介&#xff1a;拥有多年开发工作经验&#xff0c;分享技术代码帮助学生学习&#xff0c;独立完成自己的项目或者毕业设计。 代码可以私聊博主获取。&#x1f339;赠送计算机毕业设计600个选题excel文件&#xff0c;帮助大学选题。赠送开题报告模板&#xff…

DAY30:回溯算法332\51\37基本思路了解+总结

Leetcode: 332 重新安排行程 代码随想录 这道题目有几个难点&#xff1a; 一个行程中&#xff0c;如果航班处理不好容易变成一个圈&#xff0c;成为死循环&#xff0c;容易出现环路。有多种解法&#xff0c;字母序靠前排在前面&#xff0c;让很多同学望而退步&#xff0c;如…

yolov8上使用gpu教程

yolov8上使用gpu教程 安装Cuda和Cudnnyolov8上使用gpu 安装Cuda和Cudnn 1.查看支持的cuda版本&#xff0c;并去官网下载。 nvidia-smi2.网址&#xff1a;https://developer.nvidia.com/cuda-toolkit-archive 3.安装细节 安装的前提基础是&#xff0c;有vs的C环境。我电脑有…

多流转换 (分流,合流,基于时间的合流——双流联结 )

目录 一&#xff0c;分流 1.实现分流 2.使用侧输出流 二&#xff0c;合流 1&#xff0c;联合 2&#xff0c;连接 三&#xff0c;基于时间的合流——双流联结 1&#xff0c;窗口联结 1.1 窗口联结的调用 1.2 窗口联结的处理流程 2&#xff0c;间隔联结 2.1 间隔联…

Qt单选按钮

前言 本篇文章介绍Qt的单选按钮&#xff0c;就是QRadioButton QRadioButton是一个选项按钮&#xff0c;可以打开&#xff08;选中&#xff09;或关闭&#xff08;取消选中&#xff09;。单选按钮通常向用户提供“众多之一”的选择。 在一组单选按钮中&#xff0c;一次只能选中…

手动搭建koa+ts项目框架(apidoc文档篇)

文章目录 一、安装apidoc工具二、使用1、项目根目录新建apidoc.json2、定义接口路由上方注解对应信息3、配置静态文件访问目录4、生成api文档如有启发&#xff0c;可点赞收藏哟~ 一、安装apidoc工具 全局安装 npm i apidoc -g查看是否安装成功 apidoc -v二、使用 1、项目根…

注解@profile的使用

目录 profile介绍配置演示 profile介绍 profile 通常作用在controller类上&#xff0c;当它标记的环境有效时&#xff0c;该controller类才生效&#xff0c;例如&#xff1a;Profile(“dev”),当dev环境被激活时&#xff0c;Profile(“dev”)所注解的controller类才生效。 配置…

工作软技能第一弹,关于职场沟通、成长的那些事

引言 在谈绩效后&#xff0c;我收获了一些心得&#xff0c;在此梳理出来&#xff0c;加深印象并且共勉 基本信息 在步入职场后&#xff0c;你可能跟我一样虽然技术水平有在上升&#xff0c;但是在处理一些事情上可能偶尔没能获得预期的成果。我在通过绩效沟通以及自我反思后…

【学网攻】 第(10)节 -- 路由器单臂路由配置

系列文章目录 目录 系列文章目录 文章目录 前言 一、单臂路由是什么&#xff1f; 二、实验 1.引入 实验拓扑图 PC配置 Sw配置 Router配置 实验验证 总结 文章目录 【学网攻】 第(1)节 -- 认识网络【学网攻】 第(2)节 -- 交换机认识及使用【学网攻】 第(3)节 -- 交…

Vue+OpenLayers7入门到实战:在地图上添加缩放控件、比例尺控件和鼠标经纬度位置显示控件

返回《Vue+OpenLayers7》专栏目录:Vue+OpenLayers7 前言 本章主要介绍如何使用OpenLayers7在地图上添加地图缩放控件,比例尺显示控件和鼠标经纬度位置展示控件这三个Control控件。 二、依赖和使用 "ol": "7.5.2"使用npm安装依赖npm install ol@7.5.…

JVM实战(31)——内存溢出之请求超时

一、简介 本章&#xff0c;我们将通过实际案例讲解一个Web应用的内存溢出问题&#xff0c;该内存溢出问题的排查涉及Tomcat的一些底层原理&#xff0c;最终排查发现是由于请求超时问题导致&#xff0c;我们先来看下系统的背景。 1.1 系统背景 生产环境的一个系统发生告警&…

Linux/Doctor

Enumeration nmap 已知目标开放了22,80,8089端口&#xff0c;扫描详细情况如下 可以看到对外开放了22,80,8089三个端口 TCP/80 SSTI 访问80端口&#xff0c;有一个infodoctors.htb的电子邮件&#xff0c;点击其他的也没有什么反应&#xff0c;猜测有可能需要域名访问 在/et…

day32WEB 攻防-通用漏洞文件上传二次渲染.htaccess变异免杀

本章节知识点&#xff1a; 1 、文件上传 - 二次渲染 2 、文件上传 - 简单免杀变异 3 、文件上传 -.htaccess 妙用 4 、文件上传 -PHP 语言特性 前置知识&#xff1a; 后门代码需要用特定格式后缀解析&#xff0c;不能以图片后缀解析脚本后门代码 ( 解析漏洞除外 ) 如&…

C++ day 1

思维导图 使用C编写一个程序&#xff0c;输入一个字符串&#xff0c;统计大小写字母、数字、空格和其他符号的个数 #include <iostream>using namespace std;int main() {int capital 0;int lower 0;int digit 0;int spaces 0;int others 0;cout << "请…

人工智能时代:让AIGC成为你的外部智慧源(文末送书)

&#x1f308;个人主页&#xff1a;聆风吟 &#x1f525;系列专栏&#xff1a;数据结构、网络奇遇记 &#x1f516;少年有梦不应止于心动&#xff0c;更要付诸行动。 文章目录 &#x1f4cb;前言一. 什么是AIGC?二. AIGC如何运作&#xff1f;2.1 步骤一&#xff1a;收集数据2.…

web前端javascript笔记——(14)Navigator 、History、Location

Navigator <!DOCTYPE html><head><meta charset"UTF-8"><title></title><style></style><script type"text/javascript">/*DOM文档对象&#xff0c;通过js操作网页BOM 浏览器对象BOM可以使我们通过JS来操…

谷歌公布一个可以让 AI 进行自我判断输出内容正确性的模型训练框架 ASPIRE

谷歌开发了一款名为 ASPIRE 的训练框架&#xff0c;旨在增强人工智能&#xff08;AI&#xff09;模型的选择性预测能力。这款框架为模型引入了 “可信度” 机制&#xff0c;即模型会输出一系列答案&#xff0c;并为每个答案赋予一个正确概率评分。通过这种方式&#xff0c;ASPI…

day2 C++

封装一个矩形类(Rect)&#xff0c;拥有私有属性:宽度(width)、高度(height)&#xff0c; 定义公有成员函数: 初始化函数:void init(int w, int h) 更改宽度的函数:set_w(int w) 更改高度的函数:set_h(int h) 输出该矩形的周长和面积函数:void show() #include <iostre…

mysql生成最近24小时整点最近30天最近12个月时间临时表

文章目录 生成最近24小时整点生成最近30天生成最近12个月 在统计的时候需要按时间来展示&#xff0c;但是数据的时间不一定是连续的&#xff0c;那就需要在代码里面生成连续的时间&#xff0c;然后按时间匹配到对应的数据&#xff0c;这样比较麻烦&#xff0c;可以在sql中使用连…

写点东西《检查和更新NPM包》

写点东西《检查和更新NPM包》 检查和更新 NPM 包 TL;DR&#xff1b; 用于检查和更新软件包的 NPM 命令# [](#npm-outdated)npm outdatednpm updatenpm update --save-dev --savenpm update -g npm-check-updates 检查和更新软件包的命令npm install -g npm-check-updatesnpx np…