2

画像処理作業を行う Python コードをいくつか書きましたが、実行に膨大な時間がかかります。ここ数時間を最適化に費やしましたが、私の能力は限界に達したと思います。

プロファイラーからの出力を見ると、以下の関数がコード全体の時間の大部分を占めています。高速化できる方法はありますか?

def make_ellipse(x, x0, y, y0, theta, a, b):
    c = np.cos(theta)
    s = np.sin(theta)
    a2 = a**2
    b2 = b**2
    xnew = x - x0
    ynew = y - y0
    ellipse = (xnew * c + ynew * s)**2/a2 + (xnew * s - ynew * c)**2/b2 <= 1

    return ellipse

コンテキストを提供するために、かなり大きなグリッド サイズで および からの出力として呼び出され、他のすべてのパラメーターは単純な整数値としてx呼び出さyれます。np.meshgrid

その関数には多くの時間がかかっているように見えますが、おそらくコードの残りの部分も高速化できる方法があります。残りのコードはこの要点にまとめました。

どんなアイデアでもありがたく受け取られます。numba を使用して主要な機能を試してみましautojitたが、あまり役に立ちません。

4

4 に答える 4

3

make_ellipse をその呼び出し元と組み合わせて最適化してみましょう。

a最初に、とbが多くの呼び出しで同じであることに注意してください。make_ellipse は毎回それらを二乗するので、代わりに呼び出し元にそれをさせてください。

2 番目に、私のシステムでnp.cos(np.arctan(theta))1 / np.sqrt(1 + theta**2)どちらが少し速いかを確認してください。同様のトリックを使用して、シータまたは cos(シータ) (またはその逆) からサインを計算できます。

第 3 に、具体的ではありませんが、最終的な楕円式の評価の一部を短絡することを考えてください。たとえば、(xnew * c + ynew * s)**2/a2が 1 より大きい場合、楕円の値は False でなければなりません。これが頻繁に発生する場合は、それらの位置での楕円の (コストのかかる) 計算の後半を「マスク」することができます。これを完全に計画したわけではありませんが、可能性のある手がかりについては numpy.ma を参照してください。

于 2013-07-03T15:01:51.013 に答える
2

すべてのケースで速度が向上するわけではありませんが、楕円が画像全体を占めていない場合は、楕円内の点の検索を境界の四角形に制限する必要があります。私は数学が苦手なので、グーグルで検索し、逆正接トリックの@JohnZwinckのきちんとした余弦を再利用して、この関数を考え出しました。

def ellipse_bounding_box(x0, y0, theta, a, b):
    x_tan_t = -b * np.tan(theta) /  a
    if np.isinf(x_tan_t) :
        x_cos_t = 0
        x_sin_t = np.sign(x_tan_t)
    else :
        x_cos_t = 1 / np.sqrt(1 + x_tan_t*x_tan_t)
        x_sin_t = x_tan_t * x_cos_t
    x = x0 + a*x_cos_t*np.cos(theta) - b*x_sin_t*np.sin(theta)

    y_tan_t = b / np.tan(theta) /  a
    if np.isinf(y_tan_t):
        y_cos_t = 0
        y_sin_t = np.sign(y_tan_t)
    else:
        y_cos_t = 1 / np.sqrt(1 + y_tan_t*y_tan_t)
        y_sin_t = y_tan_t * y_cos_t
    y = y0 + b*y_sin_t*np.cos(theta) + a*y_cos_t*np.sin(theta)

    return np.sort([-x, x]), np.sort([-y, y])

元の関数を次のように変更できます。

def make_ellipse(x, x0, y, y0, theta, a, b):
    c = np.cos(theta)
    s = np.sin(theta)
    a2 = a**2
    b2 = b**2
    x_box, y_box = ellipse_bounding_box(x0, y0, theta, a, b)
    indices = ((x >= x_box[0]) & (x <= x_box[1]) & 
               (y >= y_box[0]) & (y <= y_box[1]))
    xnew = x[indices] - x0
    ynew = y[indices] - y0
    ellipse = np.zeros_like(x, dtype=np.bool)
    ellipse[indices] = ((xnew * c + ynew * s)**2/a2 +
                        (xnew * s - ynew * c)**2/b2 <= 1)
    return ellipse
于 2013-07-03T16:08:04.650 に答える
2

x と y 以外はすべて整数なので、配列計算の数を最小限に抑えることができます。ほとんどの時間は次のステートメントに費やされていると思います。

ellipse = (xnew * c + ynew * s)**2/a2 + (xnew * s - ynew * c)**2/b2 <= 1

このように単純に書き直すと、配列操作の数が減るはずです。

a = float(a)
b = float(b)
ellipse = (xnew * (c/a) + ynew * (s/a))**2 + (xnew * (s/b) - ynew * (c/b))**2 <= 1

12 回だった配列操作が 10 回になりました (さらに 4 つのスカラー操作)。numba の jit がこれを試したかどうかはわかりません。最初にすべてのブロードキャストを実行してから、結果の操作を jit するだけかもしれません。この場合、一般的な操作を一度に実行できるように並べ替えると役立ちます。

さらに進むと、これを次のように書き直すことができます。

ellipse = ((xnew + ynew * (s/c)) * (c/a))**2 + ((xnew * (s/c) - ynew) * (c/b))**2 <= 1

または

t = numpy.tan(theta)
ellipse = ((xnew + ynew * t) * (b/a))**2 + (xnew * t - ynew)**2 <= (b/c)**2

もう 1 つの配列操作をスカラーに置き換え、他のスカラー操作を削除して、9 つの配列操作と 2 つのスカラー操作を取得します。

いつものように、丸め誤差を避けるために、入力の範囲に注意してください。

残念ながら、2 つの加数のいずれかが比較の右辺よりも大きい場合、現在の合計を計算して早期に救済する良い方法はありません。これは明らかなスピードアップですが、コーディングには cython (または c/c++) が必要です。

于 2013-07-03T18:01:36.470 に答える