4

シーンの範囲イメージがあります。画像をトラバースし、検出ウィンドウの下での深さの平均変化を計算します。検出ウィンドウは、現在の場所の周囲のピクセルの平均深度に基づいてサイズが変わります。単純な応答イメージを作成するために平均変化を累積します。

ほとんどの時間は for ループで費やされます。私のマシンで 512x52 の画像を処理するには、約 40 秒以上かかります。スピードアップを期待していました。画像をトラバースするためのより効率的/高速な方法はありますか? 各ピクセルにアクセスするためのより良い pythonic/numpy/scipy の方法はありますか? それともcythonを習いに行こうか?

編集: skimage.io.imread() の代わりに scipy.misc.imread() を使用して、実行時間を約 18 秒に短縮しました。違いがわからないので、調べてみます。

コードの簡略版は次のとおりです。

import matplotlib.pylab as plt
import numpy as np
from skimage.io import imread
from skimage.transform import integral_image, integrate
import time

def intersect(a, b):
    '''Determine the intersection of two rectangles'''
    rect = (0,0,0,0)
    r0 = max(a[0],b[0])
    c0 = max(a[1],b[1])
    r1 = min(a[2],b[2])
    c1 = min(a[3],b[3])
    # Do we have a valid intersection?
    if r1 > r0 and  c1 > c0: 
         rect = (r0,c0,r1,c1)
    return rect

# Setup data
depth_src = imread("test.jpg", as_grey=True)
depth_intg = integral_image(depth_src)   # integrate to find sum depth in region
depth_pts = integral_image(depth_src > 0)  # integrate to find num points which have depth
boundary = (0,0,depth_src.shape[0]-1,depth_src.shape[1]-1) # rectangle to intersect with

# Image to accumulate response
out_img = np.zeros(depth_src.shape)

# Average dimensions of bbox/detection window per unit length of depth
model = (0.602,2.044)  # width, height

start_time = time.time()
for (r,c), junk in np.ndenumerate(depth_src):
    # Find points around current pixel      
    r0, c0, r1, c1 = intersect((r-1, c-1, r+1, c+1), boundary)

    # Calculate average of depth of points around current pixel
    scale =  integrate(depth_intg, r0, c0, r1, c1) * 255 / 9.0 

    # Based on average depth, create the detection window
    r0 = r - (model[0] * scale/2)
    c0 = c - (model[1] * scale/2)
    r1 = r + (model[0] * scale/2)
    c1 = c + (model[1] * scale/2)

    # Used scale optimised detection window to extract features
    r0, c0, r1, c1 = intersect((r0,c0,r1,c1), boundary)
    depth_count = integrate(depth_pts,r0,c0,r1,c1)
    if depth_count:
         depth_sum = integrate(depth_intg,r0,c0,r1,c1)
         avg_change = depth_sum / depth_count
         # Accumulate response
         out_img[r0:r1,c0:c1] += avg_change
print time.time() - start_time, " seconds"

plt.imshow(out_img)
plt.gray()
plt.show()
4

1 に答える 1

3

マイケル、興味深い質問です。あなたが抱えている主なパフォーマンスの問題は、画像の各ピクセルに2つの integrate() 関数が計算されていることです.1つはサイズ3x3で、もう1つは事前に知られていないサイズです。この方法で個々の積分を計算することは、使用する numpy 関数に関係なく、非常に非効率的です。これはアルゴリズムの問​​題であり、実装の問題ではありません。サイズ N N の画像を考えてみましょう。その画像内の任意のサイズ K K のすべての積分は、およそ 4*N N 操作のみを使用して計算できます。(単純に予想されるように) N N Kではありません。K. その方法は、最初に各行のウィンドウ K のスライド合計のイメージを計算し、次に各列の結果のスライド合計を計算することです。各スライディング サムを更新して次のピクセルに移動するには、現在のウィンドウで最新のピクセルを追加し、前のウィンドウで最も古いピクセルを減算するだけでよいため、ウィンドウ サイズに関係なく、ピクセルごとに 2 つの操作が必要です。これを (行と列に対して) 2 回行う必要があるため、1 ピクセルあたり 4 回の操作になります。

numpy にスライディング ウィンドウの合計が組み込まれているかどうかはわかりませんが、この回答では、ストライド トリックを使用してそれを行う方法がいくつか提案されています: https://stackoverflow.com/a/12713297/1828289。列に対する 1 つのループと行に対する 1 つのループ (スライスを使用して行/列を抽出する) で同じことを確実に達成できます。

例:

# img is a 2D ndarray
# K is the size of sums to calculate using sliding window
row_sums = numpy.zeros_like(img)
for i in range( img.shape[0] ):
    if i > K:
        row_sums[i,:] = row_sums[i-1,:] - img[i-K-1,:] + img[i,:]
    elif i > 1:
        row_sums[i,:] = row_sums[i-1,:] + img[i,:]
    else: # i == 0
        row_sums[i,:] = img[i,:]

col_sums = numpy.zeros_like(img)
for j in range( img.shape[1] ):
    if j > K:
        col_sums[:,j] = col_sums[:,j-1] - row_sums[:,j-K-1] + row_sums[:,j]
    elif j > 1:
        col_sums[:,j] = col_sums[:,j-1] + row_sums[:,j]
    else: # j == 0
        col_sums[:,j] = row_sums[:,j]

# here col_sums[i,j] should be equal to numpy.sum(img[i-K:i, j-K:j]) if i >=K and j >= K
# first K rows and columns in col_sums contain partial sums and can be ignored

それをあなたのケースにどのように適用するのが最善ですか?3x3 (平均深さ) といくつかの大きなサイズの積分を事前に計算し、3x3 の値を使用して、検出ウィンドウの大きなサイズの 1 つを選択することをお勧めします (あなたの意図を理解していると仮定します)。アルゴリズム)。必要なより大きなサイズの範囲が制限されているか、人為的に制限しても問題なく機能する可能性があります。最も近いサイズを選択してください。スライド合計を使用してすべての積分を一緒に計算することは非常に効率的であるため、特に一部のサイズが大きい場合、特定のピクセルで決して使用しない多くのサイズに対して計算する価値があるとほぼ確信しています。

PS これはマイナーな追加ですが、すべてのピクセルに対して intersect() を呼び出すことを避けたい場合があります: (a) 最大整数サイズよりもエッジから離れたピクセルのみを処理するか、(b) の画像にマージンを追加します。すべての辺の最大整数サイズ、マージンをゼロまたは nan で埋める、または (c) (最良の方法) スライスを使用してこれを自動的に処理します。ndarray の境界外のスライス インデックスは、自動的に境界に制限されます。もちろん、負のインデックスはラップされています。

編集:スライディング ウィンドウの合計の例を追加

于 2012-11-22T09:02:56.227 に答える