在Python中测量两个RGB图像的相似度

3

我有两张尺寸相同的RGB图像,希望计算它们的相似度。我考虑从欧几里得距离开始:

import scipy.spatial.distance as dist
import cv2

im1 = cv2.imread("im1.jpg")
im2 = cv2.imread("im2.jpg")

>> im1.shape
(820, 740, 3)

>> dist.euclidean(im1,im2)

ValueError: Input vector should be 1-D.

我知道 dist.euclidean 需要一个一维数组,而im1im2是三维的,但是否有可以用于三维数组的函数?或者是否可能将im1im2转换为保留图像信息的一维数组?


1
numpy.reshape()是你的好朋友。不过,对于图像来说,你的相似度测量可能没有太大用处。 - ypnos
@ypnos,谢谢!是的,你说得对,用欧几里得距离进行图像比较给出了荒谬的结果。我猜这个方法并不适合用于图像比较...你有什么其他建议吗? - HappyPy
@nathancy 谢谢,我会仔细阅读这些链接。 - HappyPy
1
@HappyPy,SSIM代表两个输入图像之间的结构相似性指数。该值可以在[-1, 1]范围内取值,其中值为1表示“完美匹配”。这种方法将为您提供两个图像之间的定量测量。它还返回两个输入图像之间的实际图像差异,但对于您的情况,我认为您只对实际图像相似度的“分数”感兴趣。该方法来自论文Image Quality Assessment: From Error Visibility to Structural Similarity - nathancy
1
这个方法已经在 scikit-image 中实现,作为 compare_ssim - nathancy
3个回答

7

灰度解决方案(?)

我认为可以尝试使用灰度图像来解决问题,而不是RGB图像。虽然这是我的假设,但还是值得一试。

首先,我会给出一个与您的代码相关的简单例子,并且使用2D离散傅里叶变换的图像相似度测量方法,并将其转换为灰度图像。这个DFT分析将有它自己的部分。

(如果您正在进行中,我表示歉意。我只是想确保我的工作能够保存下来。)

由于我的假设,我将使用一些RGB图像来尝试您的方法,然后查看是否通过转换为灰度图像来解决问题。如果使用灰度可以解决问题,我们可以通过同时比较每个通道的组合来找到图像相似度并分析由灰度解决方案带来的信息损失量。

方法

确保我拥有所有必要的库/软件包/任何您想称之为的东西。

> python -m pip install opencv-python
> python -m pip install scipy
> python -m pip install numpy

请注意,在此试验中,我使用了一些PNG图像,这些图像是在尝试(下文所述)使用2D DFT时创建的。
确保我遇到相同的问题。
>>> import scipy.spatial.distance as dist
>>> import cv2
>>>
>>> im1 = cv2.imread("rhino1_clean.png")
>>> im2 = cv2.imread("rhino1_streak.png")
>>>
>>> im1.shape
(178, 284, 3)
>>>
>>> dist.euclidean(im1, im2)
## Some traceback stuff ##
ValueError: Input vector should be 1-D.

现在,让我们尝试使用灰度。如果这行得通,我们可以简单地找到每个RGB通道的距离。我希望它能够奏效,因为我想进行信息损失分析。

让我们转换为灰度:

>>> im1_gray = cv2.cvtColor(im1, cv2.COLOR_BGR2GRAY)
>>> im2_gray = cv2.cvtColor(im2, cv2.COLOR_BGR2GRAY)

>>> im1_gray.shape
(178, 284)

一个简单的 dist.euclidean(im1_gray, im2,gray) 将导致相同的 ValueError: 输入向量应该为 1-D。 异常,但我知道灰度图像数组 (由像素行组成的数组) 的结构,因此我执行以下操作。

>>> dists = []
>>> for i in range(0, len(im1_gray)):
...   dists.append(dist.euclidean(im1_gray[i], im2_gray[i]))
...
>>> sum_dists = sum(dists)
>>> ave_dist = sum_dists/len(dists)
>>> ave_dist
2185.9891304058297

顺便提一下,这是两张原始图片:

rhino1_clean.jpg

rhino1_streak.jpg

灰度图像已被处理(经过调整),现在尝试一下彩色图像

按照这个SO答案的步骤,让我们尝试一下。


信息保留

根据这里的分析(存档),我们来看一下我们的信息损失情况。(注意这将是一个非常天真的分析,但我想试一试)

灰度比颜色信息

让我们只看颜色和灰度。稍后,我们可以查看是否保留了有关距离的信息。

使用灰度与所有三个通道进行比较的不同距离测量方法的比较 - 使用一组图像的距离总和比率进行比较。

我不知道如何对这些距离进行熵测量,但我的直觉告诉我,如果我使用灰度和使用彩色通道计算距离,我应该得到类似的距离比率,如果我没有丢失任何信息。


当我看到这个问题时,我的第一个想法是使用2D离散傅里叶变换,我相信Python、NumPy或OpenCV中都有。基本上,您的DFT的前几个分量将与图像中的大形状相关。(这里是一个相关的研究论文:link。我没仔细看 - 欢迎其他人提出建议。)

因此,让我查找一个在Python中易于使用的2D DFT,并回头编写一些可工作的代码。

(如果您在进展中看到这个,我很抱歉。我只是想确保我的工作被保存。)

首先,您需要确保您安装了PILPillowNumPy。看起来您已经有了NumPy,但这是一些说明。(请注意,我目前在Windows上)……

> python -m pip install opencv-python
> python -m pip install numpy
> python -m pip install pillow

现在,这里有5张图片 -

  1. 犀牛图片,rhino1_clean.jpg (来源);

rhino1_clean.jpg

我用微软画图为同一张图片加上了一些黑色条纹,rhino1_streak.jpg

rhino1_streak.jpg

另一张犀牛图片,rhino2_clean.jpg (来源);

rhino2_clean.jpg

第一张河马图片hippo1_clean.jpg (来源);

hippo1_clean.jpg

第二张河马图片,hippo2_clean.jpg (来源)。

hippo2_clean.jpg

所有图片均采用公平使用。

好的,现在,为了进一步说明,让我们进入Python交互终端。

>python

>>> import PIL
>>> import numpy as np

首先,如果我们使用灰度PNG图像,生活将更加轻松-PNG是直接位图(而不是压缩图像),灰度因为我不必用通道显示所有细节。

>>> rh_img_1_cln = PIL.Image.open("rhino1_clean.jpg")
>>> rh_img_1_cln.save("rhino1_clean.png")
>>> rh_img_1_cln_gs = PIL.Image.open("rhino1_clean.png").convert('LA')
>>> rh_img_1_cln_gs.save("rhino1_clean_gs.png")

为其他四个图像跟着类似的步骤。我使用了PIL变量名,分别是rh_img_1_stk, rh_img_2_cln, hp_img_1_cln, hp_img_2_cln。对于这些灰度图像,我最终得到了以下文件名,并将在后续使用:rhino1_streak_gs.pngrhino2_clean_gs.pnghippo1_clean_gs.pnghippo2_clean_gs.png
现在,让我们获取DFT的系数。以下代码(参考自这个SO答案)将用于第一张干净的犀牛图像。
首先让我们"查看"图像数组。这将显示出左上角列的网格版本,值越高为白色,值越低为黑色。
请注意,在开始输出此数组之前,我将设置事物为默认的numpy,参见https://docs.scipy.org/doc/numpy/reference/generated/numpy.set_printoptions.html
>>> np.set_printoptions(edgeitems=3,infstr='inf',
... linewidth=75, nanstr='nan', precision=8,
... suppress=False, threshold=1000, formatter=None)

>>> rh1_cln_gs_array = np.array(rh_img_1_cln_gs)
>>> for i in {0,1,2,3,4}:
...   print(rh1_cln_gs_array[i][:13])
...
[93 89 78 87 68 74 58 51 73 96 90 75 86]
[85 93 64 64 76 49 19 52 65 76 86 81 76]
[107  87  71  62  54  31  32  49  51  55  81  87  69]
[112  93  94  72  57  45  58  48  39  49  76  86  76]
[ 87 103  90  65  88  61  44  57  34  55  70  80  92]

现在,让我们运行DFT并查看结果。在开始实际变换之前,我会更改我的numpy打印选项,使事情更加美好。

>>> np.set_printoptions(formatter={'all':lambda x: '{0:.2f}'.format(x)})
>>>
>>> rh1_cln_gs_fft = np.fft.fft2(rh_img_1_cln_gs)
>>> rh1_cln_gs_scaled_fft = 255.0 * rh1_cln_gs_fft / rh1_cln_gs_fft.max()
>>> rh1_cln_gs_real_fft = np.absolute(rh1_cln_gs_scaled_fft)
>>> for i in {0,1,2,3,4}:
...   print(rh1_cln_gs_real_fft[i][:13])
...
[255.00 1.46 7.55 4.23 4.53 0.67 2.14 2.30 1.68 0.77 1.14 0.28 0.19]
[38.85 5.33 3.07 1.20 0.71 5.85 2.44 3.04 1.18 1.68 1.69 0.88 1.30]
[29.63 3.95 1.89 1.41 3.65 2.97 1.46 2.92 1.91 3.03 0.88 0.23 0.86]
[21.28 2.17 2.27 3.43 2.49 2.21 1.90 2.33 0.65 2.15 0.72 0.62 1.13]
[18.36 2.91 1.98 1.19 1.20 0.54 0.68 0.71 1.25 1.48 1.04 1.58 1.01]

现在,按照相同的步骤处理rhino1_streak.jpg的结果是:
[255.00 3.14 7.69 4.72 4.34 0.68 2.22 2.24 1.84 0.88 1.14 0.55 0.25]
[40.39 4.69 3.17 1.52 0.77 6.15 2.83 3.00 1.40 1.57 1.80 0.99 1.26]
[30.15 3.91 1.75 0.91 3.90 2.99 1.39 2.63 1.80 3.14 0.77 0.33 0.78]
[21.61 2.33 2.64 2.86 2.64 2.34 2.25 1.87 0.91 2.21 0.59 0.75 1.17]
[18.65 3.34 1.72 1.76 1.44 0.91 1.00 0.56 1.52 1.60 1.05 1.74 0.66]

我将打印出\Delta值,而不是做更全面的距离计算。如果您需要距离,请对这里显示的值的平方求和。

>>> for i in {0,1,2,3,4}:
...   print(rh1_cln_gs_real_fft[i][:13] - rh1_stk_gs_real_fft[i][:13])
...
[0.00 -1.68 -0.15 -0.49 0.19 -0.01 -0.08 0.06 -0.16 -0.11 -0.01 -0.27
 -0.06]
[-1.54 0.64 -0.11 -0.32 -0.06 -0.30 -0.39 0.05 -0.22 0.11 -0.11 -0.11 0.04]
[-0.53 0.04 0.14 0.50 -0.24 -0.02 0.07 0.30 0.12 -0.11 0.11 -0.10 0.08]
[-0.33 -0.16 -0.37 0.57 -0.15 -0.14 -0.36 0.46 -0.26 -0.07 0.13 -0.14
 -0.04]
[-0.29 -0.43 0.26 -0.58 -0.24 -0.37 -0.32 0.15 -0.27 -0.12 -0.01 -0.17
 0.35]

我会展示三个系数数组,每个数组只包含五个元素。这是为了说明如何通过显示图像相似性来工作的。说实话,这对我来说是一次实验,我们将看到效果如何。
您可以使用距离或其他指标比较这些系数。

关于信息保留的更多内容

让我们对上面提出的方法进行信息理论分析以评估信息丢失情况。 根据此处(已存档)的分析,看看我们的信息丢失情况。
祝你好运!

0

你可以试试

import scipy.spatial.distance as dist
import cv2
import numpy as np

im1 = cv2.imread("im1.jpg")
im2 = cv2.imread("im2.jpg")

dist.euclidean(im1.flatten(), im2.flatten())


0
你可以使用reshape函数将这两个图像从3D转换为1D。
import scipy.spatial.distance as dist
import cv2

im1 = cv2.imread("im1.jpg")
im2 = cv2.imread("im2.jpg")

im1.reshape(1820400)
im2.reshape(1820400)

dist.euclidean(im1,im2)

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接