バックグラウンド
バッファ上で3D流域パスを実行するC++拡張機能があります。signed char
ボクセルを表すためにsの大規模なバッファを初期化するための優れたCythonラッパーがあります。Pythonでいくつかのネイティブデータ構造を(コンパイルされたcythonファイルで)初期化し、次に1つのC ++関数を呼び出してバッファーを初期化し、別の関数を呼び出して実際にアルゴリズムを実行します(Cythonでこれらを記述した可能性もありますが、 python.hに依存せずにC++ライブラリとしても機能します。)
奇妙さ
私はコードをデバッグしている最中です。RAMの使用量や速度などを測定するためにさまざまな画像サイズを試していますが、結果について非常に奇妙なことに気づきました。使用するかどうかによって異なりますpython test.py
(特に/usr/bin/python
Mac OS X 10.7の場合)。 .5 / Lion、これはpython 2.7)またはpython
実行していてimport test
、その上で関数を呼び出します(実際、私のラップトップ(OS X 10.6.latest、macports python 2.7を使用)では、結果も決定論的に異なります-各プラットフォーム/状況は異なりますが、それぞれが常にそれ自体と同じです。)いずれの場合も、同じ関数が呼び出され、ファイルからいくつかの入力データが読み込まれ、C++モジュールが実行されます。
64ビットPythonに関する注意-私はこのコードをコンパイルするためにdistutilsを使用していませんが、ここでの私の答えに似ています(つまり、明示的な-arch x86_64
呼び出しを使用しています)。これは何の意味もありません。ActivityMonitorのすべてのプロセスはと呼ばれIntel (64-bit)
ます。
ご存知かもしれませんが、分水界のポイントは、ピクセルスープ内のオブジェクトを見つけることです。2Dでは、写真でよく使用されます。ここでは、ほぼ同じ方法で3Dのしこりを見つけるために使用しています。画像内のいくつかのしこり(「粒子」)から始めて、それらの間のスペースにある逆のしこり(「セル」)を見つけたいと思います。
結果が変わる方法は、文字通り異なる数のしこりを見つけることです。まったく同じ入力データの場合:
python test.py
:
grain count: 1434
seemed to have 8000000 voxels, with average value 0.8398655
find cells:
running watershed algorithm...
found 1242 cells from 1434 original grains!
...
でも、
python
、、:import test
_test.run()
grain count: 1434
seemed to have 8000000 voxels, with average value 0.8398655
find cells:
running watershed algorithm...
found 927 cells from 1434 original grains!
...
これは、bpython
私が最初に非難したと思っていたインタラクティブなpythonシェルとと同じです。
「平均値」の数値はまったく同じであることに注意してください。これは、ボクセルの同じ割合が最初に問題空間でマークされたことを示します。つまり、入力ファイルが(おそらく)まったく同じ方法で両方の時間に初期化されたことを示します。ボクセルスペース。
また、アルゴリズムのどの部分も非決定論的ではないことに注意してください。乱数や近似値はありません。浮動小数点エラー(毎回同じである必要があります)が発生する可能性がありますが、両方の時間でまったく同じ数値に対してまったく同じ計算を実行する必要があります。Watershedは、整数の大きなバッファー(ここではsigned char
s)を使用して実行され、結果はそれらの整数のクラスターをカウントします。これらはすべて、1つの大きなC++呼び出しで実装されます。
__file__
関連するモジュールオブジェクトの属性(それ自体はインポートされたの属性です)をテストしましたが、それらはシステムにtest
インストールされているものと同じものを指しています。watershed.so
site-packages
質問
これのデバッグをどこから始めればよいのかさえわかりません。同じ入力データで同じ関数を呼び出して、異なる結果を得るにはどうすればよいでしょうか。-インタラクティブなPythonはこれを引き起こす可能性があります(たとえば、データの初期化方法を変更することによって)?-(かなり大きな)コードベースのどの部分がこれらの質問に関連していますか?
私の経験では、すべてのコードをスタックオーバーフローの質問に投稿する方がはるかに便利であり、問題がどこにあるかを知っているとは限りません。ただし、ここでは数千行のコードであり、文字通りどこから始めればよいのかわかりません。リクエストに応じて小さなスニペットを投稿できてうれしいです。
また、デバッグ戦略(チェックできるインタープリターの状態、PythonがインポートされたC++バイナリに与える影響の詳細など)を聞いてうれしいです。
コードの構造は次のとおりです。
project/
clibs/
custom_types/
adjacency.cpp (and hpp) << graph adjacency (2nd pass; irrelevant = irr)
*array.c (and h) << dynamic array of void*s
*bit_vector.c (and h) << int* as bitfield with helper functions
polyhedron.cpp (and hpp) << for voxel initialisation; convex hull result
smallest_ints.cpp (and hpp) << for voxel entity affiliation tracking (irr)
custom_types.cpp (and hpp) << wraps all files in custom_types/
delaunay.cpp (and hpp) << marshals calls to stripack.f90
*stripack.f90 (and h) << for computing the convex hulls of grains
tensors/
*D3Vector.cpp (and hpp) << 3D double vector impl with operators
watershed.cpp (and hpp) << main algorithm entry points (ini, +two passes)
pywat/
__init__.py
watershed.pyx << cython class, python entry points.
geometric_graph.py << python code for post processing (irr)
setup.py << compile and install directives
test/
test.py << entry point for testing installed lib
(マークされたファイル*
は他のプロジェクトで広く使用されており、非常によくテストされています。接尾辞が付いたファイルには、問題が発生した後irr
にのみ実行されるコードが含まれています。)
詳細
要求に応じて、次のメインスタンザtest/test.py
:
testfile = 'valid_filename'
if __name__ == "__main__":
# handles segfaults...
import faulthandler
faulthandler.enable()
run(testfile)
私のインタラクティブな呼び出しは次のようになります。
import test
test.run(test.testfile)
手がかり
ストレートインタプリタでこれを実行すると:
import faulthandler
faulthandler.enable()
import test
test.run(test.testfile)
ファイル呼び出し(つまり1242セル)から結果を取得しますが、bpythonで実行すると、クラッシュするだけです。
これが明らかに問題の原因です。すぐに正しい質問をしてくれたIgnacioVazquez-Abramsに敬意を表します。
アップデート:
私はfaulthandlergithubでバグを開き、解決に向けて取り組んでいます。人々が学ぶことができる何かを見つけたら、私はそれを答えとして投稿します。