無限ビットマップ[終了]


10

実行時にビットマップを作成したいのですが。ビットマップはすべての側でスケーラブルである必要があり、ピクセルアクセスは効率的に静かである必要があります。

いくつかの図http://img546.imageshack.us/img546/4995/maptm.jpg

図に示されているコマンドの間と後に、Map.setPixel()とMap.getPixel()はビットマップに保存されたデータを設定/返す必要があります。

setPixel()/ getPixelができるだけ高速になるような方法でメモリを割り当てる方法の概念だけの実装は期待していません。


灰色のフィールドは常にポイント(0,0)ですか、それとも他の座標でもかまいませんか?
ファルコン

2
詳細が必要です。設定されたピクセルはまばらになりますか?とを高速extendXにするために、メソッドをどのくらい遅く作成する用意がsetPixelありgetPixelますか?
Peter Taylor、

1
ビットマップが大きすぎてメモリに収まりませんか?高速な操作は何ですか-拡張、setPixel()、getPixel()?

1
@ファルコン:いいえ、利用可能な十分な時間があります
SecStone 2011

3
質問は含まれている画像に大きく依存しているため、この質問をトピック外として閉じることに投票します。現在書かれているように、それはあまり意味がありません。

回答:


9

extend()操作をかなり高速にする必要がある場合は、クワッドツリーが適している可能性があります。実際には、明示的な拡張操作も必要ありません。確かに、個々のピクセルへのランダムアクセスに最適なパフォーマンスは得られませんが、あなたのコメントは、主な操作がピクセルを反復していることを示しています。反復が常にマトリックスのレイアウトと同じ方法で発生しない場合)。

あなたの要件は、実際には、Game of Lifeのようなセルオートマトンを実装しようとしているように聞こえます。無限のグリッドにGame of Lifeを実装するための非常に高性能な方法であるHashlifeを調べてみてください。これはクワッドツリーに基づいていますが、ゲームルールの局所性に基づいていくつかの非常にスマートな追加の最適化を行うことに注意してください。


このアイデアをありがとう!私はいくつかのテストを行い、結果を報告します。
SecStone 2011

2

@SecStoneは、拡張操作に利用できる十分な時間があるため、ピクセルを一定時間でアクセスできるため、ピクセルを格納する最も簡単で最も効率的な方法は、単一のフラットアレイまたは2次元アレイを使用することです。


4
この拡張への対処方法について良い提案をしていただければ、賛成票を投じます。
Doc Brown

@Doc Brown:時間があれば、アレイをシフトしてください。または、チャンクと、ポイントツーアレイおよびチャンクインデックス(一定の時間で実行される)のトランスレーター関数を使用して何かを処理することもできます。
ファルコン

2

手で

メモリがそれほどまばらなリソースではない場合は、より大きなチャンクで作業することを検討します。
ここにいくつかの擬似コードがあります。

class Chunk {
    Chunk new(int size) {...}
    void setPixel(int x, int y, int value) {...}
    int getPixel(int x, int y) {...}
}

class Grid {
    Map<int, Map<Chunk>> chunks;
    Grid new(int chunkSize) {...}
    void setPixel(int x, int y, int value) {
         getChunk(x,y).setPixel(x % chunkSize, y % chunkSize, value);//actually the modulo could be right in Chunk::setPixel and getPixel for more safety
    }
    int getPixel(int x, int y) { /*along the lines of setPixel*/ }
    private Chunk getChunk(int x, int y) {
         x /= chunkSize;
         y /= chunkSize;
         Map<Chunk> row = chunks.get(y);
         if (row == null) chunks.set(y, row = new Map<Chunk>());
         Chunk ret = row.get(x);
         if (ret == null) row.set(x, ret = new Chunk(chunkSize));
         return ret;
    }
}

この実装は非常に単純です。
1つは、getPixelにチャンクを作成することです(基本的に、その位置にチャンクが定義されていない場合は、単純に0を返すだけでかまいません)。2つ目は、十分に高速でスケーラブルなMapの実装があるという前提に基づいています。私の知る限り、すべてのまともな言語には1つあります。

また、チャンクサイズでプレイする必要があります。密なビットマップの場合、チャンクサイズは大きい方が適切です。まばらなビットマップの場合、チャンクサイズは小さいほど良いです。実際、非常にまばらなものの場合、「チャンクサイズ」は1が最適です。「チャンク」自体を廃止し、データ構造をピクセルのintマップのintマップに減らします。

既製品

別の解決策は、いくつかのグラフィックライブラリを調べることです。実際には、2Dバッファーを別の2Dバッファーに描画するのが非常に得意です。つまり、より大きなバッファを割り当てて、それに対応する座標でオリジナルを描画することになります。

一般的な戦略として:「動的に増大するメモリブロック」がある場合、使い尽くされたら、その複数を割り当てることをお勧めします。これはかなりメモリを消費しますが、割り当てとコピーのコストを大幅に削減ます。ほとんどのベクター実装では、サイズを超えるとサイズの2倍が割り当てられます。したがって、特に既製のソリューションを使用する場合は、1ピクセルしか要求されなかったため、バッファを1ピクセルだけ拡張しないでください。割り当てられたメモリは安価です。再割り当て、コピー、解放にはコストがかかります。


1

アドバイスのいくつかのポイント:

  • これを何らかの整数型の配列(または...の配列の配列)として実装する場合、コピー時にビットをシフトする必要がないように、バッキング配列を毎回ビット/ピクセル数ずつ増やす必要があります。欠点は、より多くのスペースを使用することですが、ビットマップが大きくなると、無駄なスペースの割合が減少します。

  • Mapベースのデータ構造を使用する場合、getPixelとのsetPixel呼び出しのX、Y座標引数を再配置するだけで、ビットマップの増大の問題を解決できます。

  • マップベースのデータ構造を使用する場合は、「1」のマップエントリのみが必要です。「ゼロ」は、エントリがないことで示されます。これにより、特にビットマップがほとんどゼロである場合に、かなりのスペースが節約されます。

  • マップのマップを使用する必要はありません。intX、Yのペアを単一のとしてエンコードできlongます。類似のプロセスを使用して、配列の配列を配列にマップできます。


最後に、3つのことのバランスを取る必要があります。

  1. パフォーマンスgetPixelsetPixel
  2. extend*操作のパフォーマンス、および
  3. スペース使用率。

1

より複雑なものを試す前に、すべてをメモリに保持できない場合を除いて、物事を単純に保ち、2次元配列と座標系の原点に関する情報を併用してください。それを拡張するには、たとえばC ++ std :: vectorのように同じ方法を使用します。実際の配列のサイズと配列の容量を区別し、制限に達したときに容量をチャンクで拡張します。ここでの「容量」は、間隔(from_x、to_x)、(from_y、to_y)で定義する必要があります。

これは時々メモリの完全な再割り当てを必要とするかもしれませんが、これがあまり頻繁に起こらない限り、それはあなたの目的に十分な速さかもしれません(実際、これを試して/プロファイルする必要があります)。


1

ピクセルアクセスを行うための絶対最速の方法は、個別にアドレス指定可能なピクセルの2次元配列です。

拡張機能については、毎回再割り当てとコピーを行う単純な実装から始めます(とにかくそのコードが必要になるため)。プロファイリングで多くの時間を費やしていることが示されない場合は、さらに調整する必要はありません。

プロファイリングにより、再割り当ての数を抑える必要があり、メモリの制約がない場合は、各方向にパーセンテージで過剰に割り当て、原点へのオフセットを保存することを検討してください。(たとえば、新しいビットマップを1x1で開始し、それを保持するために9x9配列を割り当てる場合、初期値xyオフセットはになります4。)ここでのトレードオフは、ピクセルアクセス中にオフセットを適用するために追加の計算を行わなければならないことです。

拡張機能が非常に高価であることが判明した場合、次のいずれかまたは両方を試すことができます。

  • 垂直拡張と水平拡張を異なる方法で処理します。配列を垂直方向に任意の方向に拡張するには、新しいブロックを割り当て、既存の配列全体の単一のコピーを新しいメモリの適切なオフセットに実行します。既存のデータが新しいブロック内で隣接していないため、行ごとに1回その操作を実行する必要がある水平方向の拡張と比較してください。

  • 延長の最も頻繁な量と方向を追跡します。その情報を使用して、新しいサイズとオフセットを選択します。これにより、拡張機能の再割り当てとコピーを行う必要が生じる可能性が低くなります。

個人的には、ピクセルアクセスと拡張の比率が低い場合を除いて、どちらかが必要になるとは思いません。


1
  • 一定サイズのタイリング(たとえば、256x256ですが、タイルの数は無限です)
  • 負のピクセル座標を許可するビットマップラッパーを提供します(既存の座標値への参照を再計算/同期する必要なく、画像を4方向すべてに拡大できるという印象を与えるため)
    • ただし、実際のビットマップクラス(ラッパーの下で機能)は、絶対(負でない)座標のみをサポートする必要があります。
  • ラッパーの下で、メモリマップI / Oを使用してタイルレベル(ブロックレベル)のアクセスを提供します。
  • 加え、Map.setPixel()そしてMap.getPixel()一度に単一のピクセルを変更し、また方法を提供するコピー及び修正画素の矩形時。これにより、発信者は、発信者が利用できる情報に応じて、より効率的なアクセス形式を選択できます。
    • 商用ライブラリは、1行のピクセル、1列のピクセル、分散/収集の更新、および算術/論理ブリッター演算を1ステップで更新する方法も提供します(データのコピーを最小限に抑えるため)。

(陽気な答えには賛成しないでください...)


0

最も柔軟で信頼性の高い実装は、x座標、y座標、およびビット値を与える構造体を持つリンクリストです。私は最初にそれを構築し、それを機能させるでしょう。

次に、それが遅すぎたり大きすぎたりする場合は、通常の高速化方法を試してください:配列、行列、ビットマップ、圧縮、キャッシュ、反転、「1」値のみの保存など。

バグのある高速な実装を修正するよりも、遅い正しい実装を速くする方が簡単です。また、「高速」の2番目の実装をテストしている間、それを比較するための参照標準があります。

そして、誰が知っているか、あなたは遅いバージョンが十分に速いことを発見するでしょう。構造全体がメモリに収まる限り、物事はすでに驚くほど高速です。


3
-1:「速くする前に機能させる」というのは、可能な限り最悪の実装から始めるのに良い理由ではありません。その上、基礎となるデータ構造で完全に変更する必要のないコードは実際にはないので、そのレベルでの反復は、ここではまったく暗示的な提案です。
Michael Borgwardt、2011

それがAPIを持っている理由です。APIは基礎となる実装を隠します。SetValue(MyMatrix、X、Y、Value)およびGetValue(MyMatrix、X、Y)は、MyMatrixが1または2次元配列であるか、リンクリストであるか、ディスク上でキャッシュされているか、SQLテーブルであるかなどを非表示にします。呼び出し元は再コンパイルが必要になる場合がありますが、コードを変更する必要はありません。
アンディキャンフィールド2011

0

Pythonで次の実装を提案します。

class Map(dict): pass

次の利点があります。

  1. 経由の取得/設定アクセスmap[(1,2)]が考えられO(1)ます。
  2. グリッドを明示的に拡張する必要がなくなります。
  3. バグのためのスペースはほとんどありません。
  4. 必要に応じて、3Dに簡単にアップグレードできます。

0

あなたがいる場合、実際に任意のサイズのビットマップを必要とする-と私は1x1のから1000000x1000000に平均何がアップamdの、そしてそれはオンデマンドで拡張可能必要があります... 1つの可能な方法は、データベースを使用することです。最初は直感に反するように見えるかもしれませんが、実際に持っているのはストレージの問題です。データベースを使用すると、個々のピクセルにアクセスして、本質的に任意の量のデータを保存できます。私は必ずしもSQL dbを意味しているわけではありません。

それはあなたの目的に十分な速さでしょうか?このビットマップを使用して何をしているのかについてはここにコンテキストがないので、私は答えられません。ただし、これが画面表示の場合は、通常、すべてのデータではなく、画面のスクロール時に表示される追加のスキャン行のみをプルバックする必要があることを考慮してください。

とはいえ、何かおかしなことをしているのではないでしょうか。代わりに、ベクターベースのグラフィックを使用してメモリ内の個々のエンティティを追跡し、ビットマップを画面に必要な大きさだけレンダリングする必要がありますか?


おそらくOSGeoマップサーバーでしょう。
2010年

0

適切に機能させるための手順は次のとおりです。

  1. あるインターフェイスから別のインターフェイスへの転送を使用して、一緒にビットマップを表すオブジェクトのツリーを構築する
  2. ビットマップのすべての「拡張」は、それ自体のメモリを割り当て、それに別のインターフェイスを提供します
  3. 実装例は次のようになります。

    template<class T, class P>
    class ExtendBottom {
    public:
       ExtendBottom(T &t, int count) : t(t), count(count),k(t.XSize(), count) { }
       P &At(int x, int y) const { if (y<t.YSize()) return t.At(x,y); else return k.At(x, y-t.YSize()); }
       int XSize() const { return t.XSize(); }
       int YSize() const { return t.YSize()+count; }
    private:
       T &t;
       int count;
       MemoryBitmap k;
    };
    

明らかに実際の実装では、XSize()とYSize()は機能しませんが、インデックス番号を一致させるには、MinX()、MaxX()、MinY()、MaxY()などが必要です。

弊社のサイトを使用することにより、あなたは弊社のクッキーポリシーおよびプライバシーポリシーを読み、理解したものとみなされます。
Licensed under cc by-sa 3.0 with attribution required.