13

私のアプリケーションでは、一連の画像 (MRC 画像) からボリュームデータを読み込み、ピクセル データをメモリに保持する必要があります (画像はグレースケールなので、1 ピクセルあたり 1 バイト)。

私の開発環境は QT フレームワーク、Windows では MinGW、Linux では GCC です。

現時点では、単純なデータ構造を使用してボリュームデータを次のように保存します。

unsigned char *volumeData;

そして、次のように 1 つの巨大な割り当てを行います。

volumeData=new unsigned char[imageXsize * imageYsize * numofImages];

以下は、次のような特定の平面の画像データにアクセスするための重要な方法です。

unsigned char* getXYPlaneSlice(int z_value);
unsigned char* getYZPlaneSlice(int x_value);
unsigned char* getZXPlaneSlice(int y_value);

私の単純なデータ構造では、上記のメソッドを簡単に実装できました。

しかし、将来的には 2000x2000x1000 (~3.7Gb) のボリューム サイズを採用する必要があるかもしれません。現在のデータ構造では、その巨大なデータを処理することはできません。

  1. 断片化を避けるには? 現在、1000x1000x200 のデータでも、アプリケーションがクラッシュして bad_alloc が発生します。これのデータ構造を変更する最良の方法は何ですか? 各チャンクのサイズが 100 MB のリンク リストのようなものを使用しますか。

  2. また、ユーザーはボリュームデータに対していくつかの画像処理フィルターを実行できる必要があり、元のピクセル値にリセットできる必要があります。つまり、ボリューム データのコピーを 2 つ保持する必要があります。現在の実装では、そのようなものです。

    unsigned char *volumeDataOriginal;

    unsigned char *volumeDataCurrent;

したがって、2000x2000x1000 のデータ範囲では、約 8Gb (各ボリュームで 4Gb) を使用します。しかし、Win32 ではアドレス空間が 4GB あります。64ビットアプリケーションを使用する必要がありますか?

編集:これは私のアプリケーションのスナップショットです ここに画像の説明を入力

基本的に、ボリューム データを (一連の画像から、MRC 形式などから) 読み込み、それらをさまざまな平面ビューアー (XY、YX、YZ。画像は XY 平面ビューアーを示しています) で表示します。上記を維持する必要があります。特定の平面に画像を表示するための 3 つのデータ アクセス方法。ユーザーはスライダー バーを使用して、選択した平面に表示する画像を変更できます)

前もって感謝します。

4

11 に答える 11

14

hdf5を見てみるべきだと思います。これは、望遠鏡、物理実験、遺伝子配列決定機などから収集された膨大な量のデータを格納するためのバイナリ形式です。このようなものを使用する利点は数多くありますが、(1) テスト済み、(2) ハイパースラブ選択をサポート、(3) 無料で圧縮できる、という 3 つの当面の考えがあります。

C/C++、java、python、matlab ライブラリが利用可能です。

于 2010-11-09T07:27:01.420 に答える
5

64ビットはおそらくこれを処理する最も簡単な方法です...ページを使用すると、ページでOSに障害が発生します。それ以外の場合は、データからアクセス パターンを知らずに多くを提案することは困難です。同じピクセル座標の値を見つけるために画像を定期的にスキャンしている場合、必要に応じて保存および再読み込みする画像へのポインターがあると言っても意味がありません。

元に戻すデータについては、提案どおりに完全なバックアップコピーを保持するか、変更を調べて効率的な実装を見つける責任がある元に戻す操作を試みることができます。たとえば、ビットを反転しただけの場合、それは非破壊的であり、変更を元に戻すには同じビット反転操作のファンクターが必要です。すべてのピクセルを同じトーンに設定することが一般的な操作 (塗りつぶし、クリアなど) である場合、そのイメージの状態をエンコードするブール値と 1 つのピクセルを使用し、完全なバッファーを元に戻すために使用できます。

于 2010-11-09T07:12:45.947 に答える
5

The simplest solution to your problem would be to use 64-bit address spaces - modern Macs support this out of the box, on Windows and Linux you will need to install the 64-bit version of the OS. I believe Qt can be used to build 64-bit apps quite nicely. 32-bit systems won't be able to support single allocations of the size you're talking about - even a Mac with 4 GB of address space available to applications won't be able to make a single 3.7 GB allocation as there will not be a contiguous space of that size available.

For undo I would look at using memory-mapped files and copy-on-write to copy the block:

http://en.wikipedia.org/wiki/Copy-on-write

This means you don't actually have to copy all the original data, the system will make copies of pages as they are written to. This will greatly aid performance if your images are significantly bigger than real memory and you're not changing every part of the image. It looks like boost::map_file with "private" access might be helpful for this.

If you really, really need to support 32-bit systems, your only alternative is to break those big blocks down somehow, typically into planes or sub-volumes. Both are horrible to work with when it comes to applying 3D filters etc. though, so I really would avoid this if you can.

If you do go the sub-volume route, one trick is to save all the sub-volumes in memory-mapped files, and map them into your address space only when you need them. When unmapped from the address space they should stay around in the unified buffer cache until purged, effectively this means you can utilise more RAM than you have address space (particularly on Windows where 32-bit applications only get 2 GB of address space by default).

Finally, on 32-bit Windows you can also look at the /3GB switch in boot.ini. This allows you to allocate 3 GB of address space to applications rather than the normal 2 GB. From the problem you describe I don't think this will give you enough address space, however it may help you with some smaller volumes. Note that the /3GB switch can cause problems with some drivers as it reduces the amount of address space available to the kernel.

于 2010-11-18T22:55:28.730 に答える
4

メモリマップトファイルを使用して、メモリが限られている大きなデータセットを管理できます。ただし、ファイルサイズが4GBになる場合は、64ビットにすることをお勧めします。Boostプロジェクトには、探しているものに非常に近いパフォーマンスを発揮する優れたマルチプラットフォームメモリマッピングライブラリがあります。

http://en.wikipedia.org/wiki/Memory-mapped_filehttp://www.boost.org/doc/libs/1_44_0/libs/iostreams/doc/classes/mapped_file.html 始めましょう。以下のサンプルコード-

#include <boost/iostreams/device/mapped_file.hpp>
boost::iostreams::mapped_file_source input_source;
input_source.open(std::string(argv[1]));
const char *data = input_source.data();
long size = input_source.size();
input_source.close();

ありがとう、ネイサン

于 2010-11-15T21:37:17.463 に答える
3

STXXL:特大データセット用の標準テンプレートライブラリを使用します。

私は最初にSOでそれについて聞いた:)

于 2010-11-16T23:55:21.363 に答える
3

私が検討する 1 つのオプションは、メモリ マッピングです。すべての画像をマッピングするのではなく、遅延ロードされる画像のリンク リストを維持します。フィルターがイメージ リストを介して機能するため、必要に応じて読み込みます。読み込み段階で、同じサイズの匿名 (または固定一時ファイル) ブロックをマップし、そこにイメージをバックアップとしてコピーします。フィルターを適用すると、このコピーにバックアップするだけです。@Tony が上で述べたように、64 ビットが最適なオプションであり、マルチプラットフォームのメモリ マップ ファイルの場合は、ブースト インタープロセスを参照してください。

于 2010-11-09T09:18:53.110 に答える
1

2つのレベルの構造を使用できます。単一の画像または(はるかに優れた)一連の画像へのポインタの配列です。したがって、1つのメモリブロックに20枚の画像を保持し、20枚の画像ブロックへのポインタを配列に配置することができます。ランダムアクセスを実行する場合、これは(リンクリストと比較して)依然として高速です。

次に、単純なページングアルゴリズムを実装できます。最初は、配列内のすべてのポインターがNULLです。最初に画像ブロックにアクセスするときは、そのブロックの20枚の画像をメモリにロードし、ポインタを配列に書き込みます。これらの画像への次のアクセスは何もロードしません。

多くの画像ブロックを読み込んで読み込んだためにメモリが少なくなった場合は、使用量が最も少ない画像ブロックを削除できます(それぞれをカウントするカウンターの値を入力するポインターの横に2番目のフィールドを追加する必要があります画像ブロックをロードするとき)。カウンタが最も低いイメージブロックは最も使用されていないものであり、削除できます(メモリは新しいブロックに再利用され、ポインタはNULLに設定されます)。

于 2010-11-16T18:25:16.827 に答える
1

主な問題は、おそらく、データへの完全なランダム アクセスが必要な場合です。

最良のアプローチは、使用したいアルゴリズムについて考えることです。それらのアルゴリズムは、主にデータを一方向にのみストライドするようには記述できません。わかりました、それは常に可能であるとは限りません。

中規模のソリューションを自分でコーディングする場合は、次のようにする必要があります。

  • データ構造のスライスmmap()をメモリにマップするために使用します
  • データをクラスにカプセル化して、現在マップされていないデータへのアクセスをキャッチできるようにします
  • mmap()次に、必要なリージョンをオンデマンドで。

(実際には、mmap()ファイル全体を一度に処理する場合、これはとにかく OS が行っていることですが、少し制御することで、時間の経過とともにオンデマンドアルゴリズムがよりスマートになり、要件に適合する可能性があります)。

繰り返しますが、これらの画像ボクセルを飛び回ると面白くありません。アルゴリズムは、データを保存するために選択したすべてのソリューションに対して、データ アクセスに適合する必要があります。データが物理メモリよりも大きい場合、トータルランダムアクセスはすべてを「壊します」 。

于 2010-11-18T12:59:03.127 に答える
1

非常に大量のデータを処理する最近の傾向は、データを 64x64x64 などの小さなデータ ブリックに分割することです。ライティングを使用してボリューム レンダリングを実行する場合は、隣接するレンガを必要とせずに個々のレンガをレンダリングできるように、隣接するレンガの間に 1 ボクセルのオーバーラップが必要です。ブリックでより複雑な画像処理を行いたい場合は、(ストレージを犠牲にして) オーバーラップを増やすことができます。

このアプローチの利点は、必要なブリックのみをメモリにロードする必要があることです。ブリック ベースのボリュームのレンダリング/処理時間は、非ブリック ベースのボリュームよりも大幅に遅くはありません。

ボリューム レンダリング側からのこれに関するより複雑な議論については、Octreemizer に関する論文をチェックしてください。 これは citeseer の 1 つへのリンクです

于 2010-11-17T07:59:17.213 に答える
1

ハードウェアと OS で許可されている場合は、64 ビットに移行し、ファイルをメモリにマップします (Windows の CreateFileMapping と Linux の mmap を参照)。

Windows では、コピー オン ライトを可能にするマップされたファイルのビューを作成できます。Linuxでもその機能を利用できると確信しています。とにかく、ソース ファイルに対して読み取り専用ビューを作成すると、それが「元のデータ」になります。次に、ソース ファイルに対してコピー オン ライト ビューを作成します。これが「現在のデータ」になります。

現在のデータを変更すると、変更された基になるページがコピーされて割り当てられ、ソース データのページはそのまま残ります。「現在のデータ」に同一のデータを書き込まないようにすると、現在のデータと元のデータがメモリ ページを共有するため、最適なメモリ使用量が得られます。ただし、コピーオンライトはページ単位で機能するため、ページの配置を考慮する必要があります。

また、現在のデータから元のデータに戻すのも簡単です。「現在のデータ」のマッピングを再作成するだけです。

ファイル マッピングを使用することで、面倒なメモリ管理作業は OS によって処理されます。利用可能なすべてのメモリを非常に効率的に使用できます。通常のヒープ割り当てで達成できるよりもはるかに効率的です。

まず、Windows で使用する CreateFileView() と MapViewOfFile() を調査することから始めます。Linux の場合は mmap() がありますが、それは私の知る限りです。2000年以来、*nixには何も触れていません...

于 2010-11-18T19:18:36.970 に答える
0

SciDBを見てください。私はその専門家ではありませんが、サンプル ユース ケースそれを説明する論文から、次のようにデータを 3D (時間/バージョン管理の場合は +1D) 配列に自然にマッピングできます。

CREATE ARRAY Pixels [
    x INT,
    y INT,
    z INT,
    version INT
] (
    pixel INT
);

そして、クエリを実装するにはgetXYPlaneSlice:

Slice (Pixels, z = 3, version = 1);

SciDB はスパース配列をサポートしているため、データの一部のみが変更された場合にデータの重複を避けるために、バージョン 1 の配列全体を埋める必要はありません。次に、最新のデータをロードする必要がある場合は、 を使用してロードしversion = 0て古いバージョンを取得し、 を使用して別のロードで結果を更新できますversion = 1

于 2010-11-16T19:16:07.473 に答える