簡體   English   中英

計算均方,絕對偏差和自定義相似性度量 - Python / NumPy

[英]Compute mean squared, absolute deviation and custom similarity measure - Python/NumPy

我有一個大圖像作為2D數組(讓我們假設它是一個500 x 1000像素的灰度圖像)。 我有一個小圖像(比方說是15 x 15像素)。 我想將小圖像滑過大圖像,並且對於小圖像的給定位置,我想計算小圖像和大圖像的下部分之間的相似性度量。

我想靈活地選擇一種相似度。 例如,我可能想要計算均方偏差或平均絕對偏差或其他東西(只是一些采用相同大小的兩個矩陣並返回實數的操作)。

結果應該是2D數組。 我想有效地執行此操作(這意味着我想避免循環)。

作為相似性的度量,我計划使用兩個圖像的顏色之間的平方偏差。 但是,正如我所提到的,如果我可以更改度量(例如使用顏色之間的相關性),那將是很好的。

numpy中有功能嗎?

導入模塊

首先,讓我們導入將在本文中列出的各種方法中使用的所有相關模塊/功能 -

from skimage.util import view_as_windows
from skimage.feature import match_template
import cv2
from cv2 import matchTemplate as cv2m
from scipy.ndimage.filters import uniform_filter as unif2d
from scipy.signal import convolve2d as conv2

A.針對MAD,MSD的基於視圖的方法

基於Skimage的計算方法mean absolute deviation

使用scikit-image獲得slided 4D array of views ,然后使用np.mean進行平均計算 -

def skimage_views_MAD_v1(img, tmpl):
    return np.abs(view_as_windows(img, tmpl.shape) - tmpl).mean(axis=(2,3))

使用scikit-image獲得4D視圖數組,然后使用np.einsum進行平方平均計算 -

def skimage_views_MAD_v2(img, tmpl):
    subs = np.abs(view_as_windows(img, tmpl.shape) - tmpl)
    return np.einsum('ijkl->ij',subs)/float(tmpl.size)

基於Skimage的計算mean squared deviation的方法:

使用類似的技術,我們將有兩種方法用於mean squared deviation -

def skimage_views_MSD_v1(img, tmpl):
    return ((view_as_windows(img, tmpl.shape) - tmpl)**2).mean(axis=(2,3))

def skimage_views_MSD_v2(img, tmpl):
    subs = view_as_windows(img, tmpl.shape) - tmpl
    return np.einsum('ijkl,ijkl->ij',subs, subs)/float(tmpl.size)

B.基於卷積的MSD方法

基於卷積的計算mean squared deviations的方法:

Convolution可用於以一些調整的方式計算均方偏差。 對於平方偏差之和的情況,在每個窗口內我們執行元素減法,然后對每個減法進行平方,然后對所有這些進行求和。

讓我們仔細考慮一維示例 -

a : [a1, a2, a3, a4, a5, a6, a7, a8] # Image array
b : [b1, b2, b3]                     # Template array

對於第一個窗口操作,我們將:

(a1-b1)**2 + (a2-b2)**2 + (a3-b3)**2

我們用(ab)**2公式:

(a - b)**2 = a**2 - 2*a*b +b**2

因此,我們將有第一個窗口:

(a1**2 - 2*a1*b1 +b1**2) + (a2**2 - 2*a2*b2 +b2**2) + (a3**2 - 2*a3*b3 +b3**2)

同樣,對於第二個窗口:

(a2**2 - 2*a2*b1 +b1**2) + (a3**2 - 2*a3*b2 +b2**2) + (a4**2 - 2*a4*b3 +b3**2)

等等。

所以,我們對這些計算有三個部分 -

  • 平滑和滑動窗口中的那些。

  • b的平方和那些的總和。 所有窗戶都保持不變。

  • 最后一塊拼圖是: (2*a1*b1, 2*a2*b2, 2*a3*b3)(2*a2*b1, 2*a3*b2, 2*a4*b3)等等第一,第二等窗戶。 這可以通過一個來計算2D卷積用a和翻轉的版本b具體根據定義convolution運行以滑動從另一個方向的內核,並且計算的elementwise乘法和每個窗口內求和元件,因此翻轉這里需要的。

將這些想法擴展到2D案例並使用Scipy's convolve2duniform_filter ,我們還有兩種方法來計算mean squared deviations ,就像這樣 -

def convolution_MSD(img, tmpl):
    n = tmpl.shape[0]
    sums = conv2(img**2,np.ones((n,n)),'valid')
    out = sums + (tmpl**2).sum() -2*conv2(img,tmpl[::-1,::-1],'valid')
    return out/(n*n)

def uniform_filter_MSD(img, tmpl):
    n = tmpl.shape[0]
    hWSZ = (n-1)//2
    sums = unif2d(img.astype(float)**2,size=(n,n))[hWSZ:-hWSZ,hWSZ:-hWSZ]
    out = sums + (tmpl**2).mean() -2*conv2(img,tmpl[::-1,::-1],'valid')/float(n*n)
    return out

C.基於Skimage的NCC方法

基於Skimage的計算normalized cross-correlation

def skimage_match_template(img, tmpl):
    return match_template(img, tmpl)

請注意,由於這些是互相關值,因此圖像和模板之間的緊密度將以高輸出值為特征。


D.基於OpenCV的各種相似性度量方法

OpenCV提供各種template-matching方法來分類模板 -

def opencv_generic(img, tmpl, method_string ='SQDIFF'):
    # Methods : 
    # 'CCOEFF' : Correlation coefficient
    # 'CCOEFF_NORMED' : Correlation coefficient normalized
    # 'CCORR' : Cross-correlation
    # 'CCORR_NORMED' : Cross-correlation normalized
    # 'SQDIFF' : Squared differences
    # 'SQDIFF_NORMED' : Squared differences normalized

    method = eval('cv2.TM_' + method_string)
    return cv2m(img.astype('uint8'),tmpl.astype('uint8'),method)

E.基於視圖的方法:自定義功能

我們可以使用本文前面所示的4D視圖,並沿着最后兩個軸執行自定義相似性度量為NumPy ufuncs。

因此,我們將滑動窗口視為之前使用的4D陣列,如此 -

img_4D = view_as_windows(img, tmpl.shape)

請注意,作為輸入圖像的視圖,它不會再花費在內存上。 但是后來的操作會根據這些操作本身進行復制。 比較操作會導致更少的內存占用(確切地說是Linux上的8倍)。

然后,我們在img_4Dtmpl之間執行預期的操作,這在線性映射操作中將導致broadcasting之后的另一個4D陣列。 我們稱之為img_sub 接下來,最有可能的是,我們會進行一些縮減操作,以便為我們提供2D輸出。 同樣,在大多數情況下,可以在此處使用其中一個NumPy ufuncs 我們需要在img_sub的最后兩個軸上使用這個img_sub 同樣,許多ufunc允許我們一次在多個軸上工作。 例如,早先我們一次性使用最后兩個軸的mean計算。 否則,我們需要一個接一個地沿着這兩個軸操作。

作為如何使用let的例子考慮一個自定義函數:

mean((img_W**tmpl)*tmpl - 2*img*tmpl**2)

在這里,我們有img_W作為imgtmpl的滑動窗口,像往常一樣是在img的高度和寬度上滑動的模板。

使用兩個嵌套循環實現,我們將:

def func1(a,b):
    m1,n1 = a.shape
    m2,n2 = b.shape
    mo,no = m1-m2+1, n1-n2+1
    out = np.empty((mo,no))
    for i in range(mo):
        for j in range(no):
            out[i,j] = ((a[i:i+m2,j:j+n2]**2)*b - 2*a[i:i+m2,j:j+n2]*(b**2)).mean()
    return out

現在,使用view_as_windows ,我們將有一個矢量化解決方案:

def func2(a,b):
    a4D = view_as_windows(img, tmpl.shape)
    return ((a4D**2)*b - 2*a4D*(b**2)).mean(axis=(2,3))

運行時測試 -

In [89]: # Sample image(a) and template(b)
    ...: a = np.random.randint(4,9,(50,100))
    ...: b = np.random.randint(2,9,(15,15))
    ...: 

In [90]: %timeit func1(a,b)
1 loops, best of 3: 147 ms per loop

In [91]: %timeit func2(a,b)
100 loops, best of 3: 17.8 ms per loop

基准測試:均方偏差

體積大小的數據集:

In [94]: # Inputs
    ...: img = np.random.randint(0,255,(50,100))
    ...: tmpl = np.random.randint(0,255,(15,15))
    ...: 

In [95]: out1 = skimage_views_MSD_v1(img, tmpl)
    ...: out2 = skimage_views_MSD_v2(img, tmpl)
    ...: out3 = convolution_MSD(img, tmpl)
    ...: out4 = uniform_filter_MSD(img, tmpl)
    ...: out5 = opencv_generic(img, tmpl, 'SQDIFF')/tmpl.size
    ...: 
    ...: print np.allclose(out1, out2)
    ...: print np.allclose(out1, out3)
    ...: print np.allclose(out1, out4)
    ...: print np.allclose(out1, out5)
    ...: 
True
True
True
True

In [96]: %timeit skimage_views_MSD_v1(img, tmpl)
    ...: %timeit skimage_views_MSD_v2(img, tmpl)
    ...: %timeit convolution_MSD(img, tmpl)
    ...: %timeit uniform_filter_MSD(img, tmpl)
    ...: %timeit opencv_generic(img, tmpl, 'SQDIFF')/tmpl.size
    ...: 
100 loops, best of 3: 8.49 ms per loop
100 loops, best of 3: 3.87 ms per loop
100 loops, best of 3: 5.96 ms per loop
100 loops, best of 3: 3.25 ms per loop
10000 loops, best of 3: 201 µs per loop

對於更大的數據量,取決於可用的系統RAM,我們可能必須回退到除了顯着的內存占用的views方法之外的方法。

讓我們用剩下的方法測試更大的數據量 -

In [97]: # Inputs
    ...: img = np.random.randint(0,255,(500,1000))
    ...: tmpl = np.random.randint(0,255,(15,15))
    ...: 

In [98]: %timeit convolution_MSD(img, tmpl)
    ...: %timeit uniform_filter_MSD(img, tmpl)
    ...: %timeit opencv_generic(img, tmpl, 'SQDIFF')/tmpl.size
    ...: 
1 loops, best of 3: 910 ms per loop
1 loops, best of 3: 483 ms per loop
100 loops, best of 3: 16.1 ms per loop

摘要

  • 當使用已知的相似性度量時,即使用基於OpenCV的模板匹配方法列出的六種方法之一,並且如果我們可以訪問OpenCV,那將是最好的。

  • 如果沒有OpenCV,對於像均方偏差這樣的特殊情況,我們可以利用卷積來獲得相當有效的方法。

  • 對於通用/自定義函數和具有相當大的數據量,我們可以查看4D視圖以獲得高效的矢量化解決方案。 對於大型數據集,我們可能希望使用一個循環並使用3D視圖而不是4D ,以減少內存占用。 對於非常大的,您可能必須回退到兩個嵌套循環。

你指的是交叉關聯操作嗎?

但是,如果您嚴格要檢查具有平方偏差的相似性,則可以在skimage中使用模板匹配,它使用更快的互相關實現。 示例: http//scikit-image.org/docs/dev/auto_examples/plot_template.html

否則,您可以使用correlate2d來實現此目的:1。對零均值信號執行互相關(意味着兩個信號/圖像應以零為中心)2。檢查局部最大值scipy.signal.argrelmax或(如果您認為只有一個匹配項使用np.argmax查找全局最大值

這是一個例子(從文檔中解除),如果需要,你可以用signal.argrelmax替換np.argmax。

from scipy import signal
from scipy import misc
lena = misc.lena() - misc.lena().mean()
template = np.copy(lena[235:295, 310:370]) # right eye
template -= template.mean()
lena = lena + np.random.randn(*lena.shape) * 50 # add noise
corr = signal.correlate2d(lena, template, boundary='symm', mode='same')
y, x = np.unravel_index(np.argmax(corr), corr.shape) # find the match

資源 :

https://docs.scipy.org/doc/scipy-0.15.1/reference/generated/scipy.signal.correlate2d.html

https://docs.scipy.org/doc/scipy/reference/generated/scipy.signal.argrelmax.html#scipy.signal.argrelmax

暫無
暫無

聲明:本站的技術帖子網頁,遵循CC BY-SA 4.0協議,如果您需要轉載,請注明本站網址或者原文地址。任何問題請咨詢:yoyou2525@163.com.

 
粵ICP備18138465號  © 2020-2024 STACKOOM.COM