两个图像的互信息和联合熵 - MATLAB

Jor*_*rge 23 matlab image image-processing entropy information-theory

我有两个黑白图像,我需要计算互信息.

Image 1 = X 
Image 2 = Y
Run Code Online (Sandbox Code Playgroud)

我知道互信息可以定义为:

MI = entropy(X) + entropy(Y) - JointEntropy(X,Y)
Run Code Online (Sandbox Code Playgroud)

MATLAB已经具有内置函数来计算熵但不计算联合熵.我想真正的问题是:我如何计算两幅图像的联合熵?

这是我想要找到的联合熵的图像示例:

X =

0    0    0    0    0    0
0    0    1    1    0    0
0    0    1    1    0    0
0    0    0    0    0    0
0    0    0    0    0    0

Y =

0    0    0    0    0    0 
0    0    0.38 0.82 0.38 0.04 
0    0    0.32 0.82 0.68 0.17
0    0    0.04 0.14 0.11 0 
0    0    0    0    0    0
Run Code Online (Sandbox Code Playgroud)

ray*_*ica 60

要计算关节熵,您需要计算两个图像之间的联合直方图.联合直方图与普通1D直方图基本相同,但第一维记录第一图像的强度,第二维记录第二图像的强度.这与通常称为共生矩阵的非常相似.在(i,j)联合直方图中的位置,它告诉您我们遇到的i在第一张图像中具有强度的强度值和j在第二张图像中具有强度的强度值.

重要的是,这记录了我们在相同的相应位置看到这对强度的次数.例如,如果我们有一个联合直方图计数(7,3) = 2,这意味着当我们扫描两个图像时,当我们遇到7第二个图像中相同位置的强度时,我们3总共遇到了强度2.

构建联合直方图非常简单.

  1. 首先,创建一个256 x 256矩阵(假设您的图像是无符号的8位整数)并将它们初始化为全零.此外,您需要确保两个图像的大小(宽度和高度)相同.
  2. 完成后,请查看每个图像的第一个像素,我们将其表示为左上角.具体来说,请查看此位置的第一张和第二张图像的强度.第一图像的强度将用作行,而第二图像的强度将用作列.
  3. 在矩阵中找到此位置,并在矩阵中增加此点1.
  4. 对图像中的其余位置重复此操作.
  5. 完成后,将所有条目除以任一图像中的元素总数(请记住它们应该是相同的大小).这将为我们提供两个图像之间的联合概率分布.

人们倾向于用for循环来做这件事,但众所周知,for循环非常慢,如果可能的话应该避免.但是,您可以通过以下方式在MATLAB中轻松完成此操作而无需循环.让我们假设im1im2是要比较的第一和第二图像.我们所能做的是转换im1im2入载体.然后我们可以accumarray用来帮助我们计算联合直方图. accumarray是MATLAB中最强大的功能之一.您可以将其视为微缩MapReduce范例.简而言之,每个数据输入都有一个键和一个相关的值.目标accumarray是将所有属于同一个键的值合并,并对所有这些值执行某些操作.在我们的例子中,"关键"是强度值,值本身是1每个强度值的值.然后,我们将要添加的所有值高达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; %// Should be the same size as indrow
jointHistogram = accumarray([indrow indcol], 1);
jointProb = jointHistogram / numel(indrow);
Run Code Online (Sandbox Code Playgroud)

使用时accumarray,第一个输入是键,第二个输入是值.一个注意事项accumarray是,如果每个键具有相同的值,您可以简单地为第二个输入分配一个常量,这就是我所做的和它的完成1.通常,这是一个与第一个输入具有相同行数的数组.另外,请特别注意前两行.0图像中不可避免地存在强度,但由于MATLAB开始索引1,我们需要将两个数组都偏移1.

现在我们有联合直方图,计算联合熵非常简单.它类似于1D中的熵,除了现在我们只是对整个联合概率矩阵求和.请记住,您的联合直方图很可能会有很多0条目.我们需要确保跳过这些或log2操作将是未定义的.让我们现在摆脱任何零条目:

indNoZero = jointHistogram ~= 0;
jointProb1DNoZero = jointProb(indNoZero);
Run Code Online (Sandbox Code Playgroud)

请注意,我搜索了联合直方图而不是联合概率矩阵.这是因为联合直方图由整数组成,而联合概率矩阵位于0和之间1.由于划分,我想避免比较此矩阵中的任何条目与0数值舍入和不稳定性.以上也将我们的联合概率矩阵转换为堆叠的1D向量,这很好.

因此,联合熵可以计算为:

jointEntropy = -sum(jointProb1DNoZero.*log2(jointProb1DNoZero));
Run Code Online (Sandbox Code Playgroud)

如果我对在MATLAB中计算图像的熵的理解是正确的,它应该计算在256二进制位上的直方图/概率分布,所以你当然可以在这里使用刚刚计算出的联合熵.

如果我们有浮点数据怎么办?

到目前为止,我们假设您处理的图像具有整数值的强度.如果我们有浮点数据怎么办? accumarray假设您正在尝试使用整数索引到输出数组,但我们仍然可以通过这个小小的颠簸实现我们想要的目标.您要做的只是将两个图像中的每个浮点值分配为具有唯一ID.因此accumarray,您将使用这些ID.为了便于此ID分配,请使用unique- 特别是函数的第三个输出.您可以拍摄每张图像,将它们放入unique并使这些图像输入accumarray.换句话说,请改为:

[~,~,indrow] = unique(im1(:)); %// Change here
[~,~,indcol] = unique(im2(:)); %// Change here

%// Same code
jointHistogram = accumarray([indrow indcol], 1);
jointProb = jointHistogram / numel(indrow);
indNoZero = jointHistogram ~= 0;
jointProb1DNoZero = jointProb(indNoZero);
jointEntropy = -sum(jointProb1DNoZero.*log2(jointProb1DNoZero));
Run Code Online (Sandbox Code Playgroud)

请注意,使用indrowindcol,我们直接将unique这些变量的第三个输出分配给我们,然后使用我们之前计算的相同联合熵代码.我们也不必像以前那样unique将变量偏移1,因为它将从1开始分配ID .

在旁边

实际上,您可以使用联合概率矩阵单独计算每个图像的直方图或概率分布.如果要计算第一个图像的直方图/概率分布,则只需累加每行的所有列.要为第二个图像执行此操作,您只需累积每列的所有行.因此,您可以这样做:

histogramImage1 = sum(jointHistogram, 1);
histogramImage2 = sum(jointHistogram, 2);
Run Code Online (Sandbox Code Playgroud)

之后,您可以自己计算这两者的熵.要仔细检查,请确保将这两个转换为PDF,然后使用标准公式(如上所述)计算熵.


我如何最终计算互信息?

要最终计算互信息,您将需要两个图像的熵.您可以使用MATLAB的内置entropy函数,但这假设有256个唯一级别.您可能希望将此应用于存在N不同级别而不是256的情况,因此您可以使用我们在联合直方图上执行的操作,然后在上面的旁边代码中计算每个图像的直方图,然后计算熵对于每个图像.您只需重复联合使用的熵计算,但将其单独应用于每个图像:

%// Find non-zero elements for first image's histogram
indNoZero = histogramImage1 ~= 0;

%// Extract them out and get the probabilities
prob1NoZero = histogramImage1(indNoZero);
prob1NoZero = prob1NoZero / sum(prob1NoZero);

%// Compute the entropy
entropy1 = -sum(prob1NoZero.*log2(prob1NoZero));

%// Repeat for the second image
indNoZero = histogramImage2 ~= 0;
prob2NoZero = histogramImage2(indNoZero);
prob2NoZero = prob2NoZero / sum(prob2NoZero);
entropy2 = -sum(prob2NoZero.*log2(prob2NoZero));

%// Now compute mutual information
mutualInformation = entropy1 + entropy2 - jointEntropy;
Run Code Online (Sandbox Code Playgroud)

希望这可以帮助!

  • 这几乎是垃圾评论,但不得不这样说:这是我在SO上遇到的最好的答案之一.它简明扼要地解释了背景,代码背后的基本原理,从简单(整数)到更难的实际情况(实际价值).此外,使用`accumarray`的最好的例子之一. (3认同)
  • 使用`for`循环的不错的替代方案. (2认同)