为了计算联合熵,您需要计算两个图像之间的联合直方图。 联合直方图本质上与普通的1D直方图相同,但第一维记录第一个图像的强度,第二维记录第二个图像的强度。 这非常类似于通常所称的
共现矩阵。 在联合直方图的位置处,它告诉您我们遇到了多少个强度值,在第一个图像中具有强度i,在第二个图像中具有强度j。
重要的是,这记录了我们在
相应位置同时看到这对强度的次数。 例如,如果我们有联合直方图计数<7,3> = 2,则意味着当我们扫描两个图像时,当我们在第二个图像的相应位置遇到强度为7时,我们遇到了强度为3的强度总共达到了2次。
构建联合直方图非常简单。
首先,创建一个
256 x 256
的矩阵(假设图像为无符号8位整数),并将它们初始化为全零。此外,您需要确保两个图像的大小相同(宽度和高度)。
完成后,查看每个图像的第一个像素,我们将其称为左上角。具体来说,查看该位置的第一张图像和第二张图像的强度。第一张图像的强度将作为行,而第二张图像的强度将作为列。
在矩阵中找到此位置,并将矩阵中的此位置增加
1
。
重复此过程,直到图像中的所有位置都处理完毕。
完成后,将所有条目除以任一图像中的元素总数(记住它们应具有相同的大小)。这将为我们提供两个图像之间的联合概率分布。
我们倾向于使用for
循环来实现此操作,但众所周知,for
循环非常慢,应尽可能避免使用。然而,在MATLAB中,您可以轻松地完成以下操作无需循环。假设im1
和im2
是您要比较的第一张和第二张图像。我们可以将im1
和im2
转换为向量。然后,我们可以使用accumarray
帮助我们计算联合直方图。 accumarray
是MATLAB中最强大的函数之一。您可以将其视为微型MapReduce范例。简单地说,每个数据输入都有一个键和一个关联值。 accumarray
的目标是将所有属于同一键的值进行分组,并对所有这些值执行某些操作。在我们的情况下,“键”将是强度值,而值本身是每个强度值的值1
。然后,我们将要添加映射到同一bin的所有1
的值,这正是我们计算直方图的方式。 accumarray
的默认行为是将所有这些值相加。具体来说,accumarray
的输出将是一个数组,其中每个位置计算映射到该键的所有值的总和。例如,第一个位置将是映射到键1的所有值的总和,第二个位置将是映射到键2的所有值的总和,依此类推。
然而,对于联合直方图,您需要确定哪些值映射到相同的强度对
(i,j)
,因此这里的键将是一对2D坐标。因此,在两个图像之间共享相同空间位置的具有第一个图像中强度
i
和第二个图像中强度
j
的任何强度都转到同一个键。因此,在2D情况下,
accumarray
的输出将是一个2D矩阵,其中每个元素
(i,j)
包含映射到键
(i,j)
的所有值的总和,类似于先前提到的1D情况,这正是我们想要的。
换句话说:
indrow = double(im1(:)) + 1;
indcol = double(im2(:)) + 1;
jointHistogram = accumarray([indrow indcol], 1);
jointProb = jointHistogram / numel(indrow);
使用
accumarray
函数,第一个输入是键,第二个输入是值。需要注意的是,如果每个键都有相同的值,可以将常数分配给第二个输入,这就是我所做的,并且它是
1
。一般来说,这是与第一个输入具有相同行数的数组。还要特别注意前两行。图像中不可避免地会出现强度为
0
的情况,但由于MATLAB从
1
开始索引,因此我们需要将两个数组偏移
1
。
现在我们拥有联合直方图,计算联合熵非常简单。它类似于1D中的熵,只是现在我们只是在整个联合概率矩阵上求和。请记住,您的联合直方图很可能会有许多0
条目。我们需要确保跳过那些或者log2
操作将未定义。现在让我们消除任何零条目:
indNoZero = jointHistogram ~= 0
jointProb1DNoZero = jointProb(indNoZero)
请注意,我搜索的是联合直方图而不是联合概率矩阵。这是因为联合直方图由整数组成,而联合概率矩阵将位于0
和1
之间。由于除法的问题,我想避免将此矩阵中的任何条目与0
进行比较,以避免数值舍入误差和不稳定性。上述方法还将我们的联合概率矩阵转换为堆叠的一维向量,这是可以接受的。
因此,联合熵可以计算如下:
jointEntropy = -sum(jointProb1DNoZero.*log2(jointProb1DNoZero))
如果我对在MATLAB中计算图像熵的理解是正确的,那么它应该会计算256个bin上的直方图/概率分布,因此你可以使用刚刚计算出来的联合熵函数。
如果我们有浮点数据呢?
到目前为止,我们假设你处理的图像具有整数值强度。那么如果我们有浮点数据呢?
accumarray
假定您正在尝试使用整数索引访问输出数组,但是我们仍然可以通过一些简单的方法达到想要的效果。您需要做的就是将每个图像中的浮点值都分配一个唯一的ID。因此,您将使用这些ID而不是整数来使用
accumarray
。为了方便地分配ID,请使用
unique
——特别是函数的第三个输出。您需要将每个图像放入
unique
中,并将其作为输入传递给
accumarray
。换句话说,您需要执行以下操作:
[~,~,indrow] = unique(im1(:));
[~,~,indcol] = unique(im2(:));
jointHistogram = accumarray([indrow indcol], 1);
jointProb = jointHistogram / numel(indrow);
indNoZero = jointHistogram ~= 0;
jointProb1DNoZero = jointProb(indNoZero);
jointEntropy = -sum(jointProb1DNoZero.*log2(jointProb1DNoZero));
请注意,使用
indrow
和
indcol
,我们直接将
unique
的第三个输出分配给这些变量,然后使用先前计算的相同联合熵代码。我们也不必像以前那样将变量偏移1,因为
unique
将从1开始分配ID。
旁白
您实际上可以使用联合概率矩阵单独为每个图像计算直方图或概率分布。如果您想计算第一张图像的直方图/概率分布,您只需要累加每行的所有列。要为第二张图像执行此操作,您只需要累加每列的所有行。因此,您可以执行:
histogramImage1 = sum(jointHistogram, 1)
histogramImage2 = sum(jointHistogram, 2)
之后,您可以自行计算它们的熵。为了双重确认,请确保将它们都转换为PDF文件,然后使用标准公式(如上所示)计算熵。
如何计算互信息?
要计算互信息,你需要两个图像的熵。你可以使用MATLAB内置的entropy
函数,但这假定有256个唯一级别。如果你想应用于存在N
个不同级别的情况,你可以使用我们在上面对联合直方图的处理方法,然后计算每个图像的直方图并计算每个图像的熵。你只需重复联合熵计算所使用的熵计算方法,但是将其分别应用于每个图像:
indNoZero = histogramImage1 ~= 0;
prob1NoZero = histogramImage1(indNoZero);
prob1NoZero = prob1NoZero / sum(prob1NoZero);
entropy1 = -sum(prob1NoZero.*log2(prob1NoZero));
indNoZero = histogramImage2 ~= 0;
prob2NoZero = histogramImage2(indNoZero);
prob2NoZero = prob2NoZero / sum(prob2NoZero);
entropy2 = -sum(prob2NoZero.*log2(prob2NoZero));
mutualInformation = entropy1 + entropy2 - jointEntropy;
希望这可以帮到你!
[0,1]
范围内,则仍然可以使用我下面编写的代码,但需要确保强度为整数。如果您有8位图像,请在执行我的操作之前使用im2uint8
。 - rayryeng256 x 256
。 - rayryengim2uint8
转换您的图像。 这将将您的图像转换为[0,255]
。 此外,如果您查看MATLAB如何实现entropy
命令,他们也使用im2uint8
。 您需要在entropy
和我们讨论的联合熵中使用相同数量的bin。 - rayryeng