13

軽量の画像表示アプリケーションを作成しようとしています。ただし、.NET にはシステム メモリの制限があります。

大きなビットマップ ( 9000 x 9000 ピクセル以上、24 ビット)を読み込もうとすると、System.OutOfMemoryException が発生します。これは、2GB の RAM を搭載した Windows 2000 PC 上にあります (そのうち 1.3GB が使用されています)。また、ファイルのロードを試みるのにも多くの時間がかかります。

次のコードは、このエラーを生成します。

Image image = new Bitmap(filename);
using (Graphics gfx = this.CreateGraphics())
{
    gfx.DrawImage(image, new Point(0, 0));
}

次のコードも同様です。

Stream stream = (Stream)File.OpenRead(filename);
Image image = Image.FromStream(stream, false, false);
using (Graphics gfx = this.CreateGraphics())
{
    gfx.DrawImage(image, new Rectangle(0, 0, 100, 100), 4000, 4000, 100, 100, GraphicsUnit.Pixel);
}

また、これを行うだけで十分です。

Bitmap bitmap = new Bitmap(filename);
IntPtr handle = bitmap.GetHbitmap();

後者のコードは、GDI で使用するためのものです。これを調査しているときに、これは実際にはメモリの問題であり、.NET が 1 つの連続したメモリ ブロックで必要な量の 2 倍を割り当てようとすることがわかりました。

http://bytes.com/groups/net-c/279493-drawing-large-bitmaps

私は、他のアプリケーション (Internet Explorer、MS ペイントなど) から、大きな画像をすばやく開くことができることを知っています。私の質問は、.NET で大きなビットマップを使用するにはどうすればよいですか?

それらをストリーミングしたり、メモリ以外でロードしたりする方法はありますか?

4

7 に答える 7

9

これは 2 つの部分からなる質問です。最初の質問は、メモリ不足にならずに大きな画像をロードする方法 (1) であり、2 番目の質問はロード パフォーマンスの向上 (2) です。

(1) ファイルシステムでギガバイトを消費する巨大な画像を扱うことができる Photoshop のようなアプリケーションを考えてみましょう。画像全体をメモリに保持し、操作 (フィルター、画像処理など、または単にレイヤーを追加するだけでも) を実行するのに十分な空きメモリを保持することは、ほとんどのシステム (8GB x64 システムであっても) では不可能です。

これが、このようなアプリケーションがスワップ ファイルの概念を使用する理由です。内部的には、Photoshop は独自のファイル形式を使用しており、アプリケーションの設計に適しており、スワップからの部分的な読み込みをサポートするように構築されているため、ファイルの一部をメモリに読み込んで処理できると想定しています。

(2) ファイル形式ごとにカスタム ローダーを作成することで、Performande を (かなり) 改善できます。これには、使用するファイル形式のファイル ヘッダーと構造を読む必要があります。慣れればそれほど難しくはありませんが、メソッド呼び出しほど簡単ではありません。

たとえば、FastBitmap をグーグル検索して、ビットマップ (BMP) ファイルを非常に高速にロードする方法の例を確認できます。これには、ビットマップ ヘッダーのデコードが含まれていました。これには pInvoke が含まれており、何に直面しているかについてのアイデアを得るには、次のようなビットマップ構造を定義する必要があります。

        [StructLayout(LayoutKind.Sequential, Pack = 1)]
        public struct BITMAPFILEHEADER
        {
            public Int16 bfType;
            public Int32 bfSize;
            public Int16 bfReserved1;
            public Int16 bfReserved2;
            public Int32 bfOffBits;
        }

        [StructLayout(LayoutKind.Sequential)]
        public struct BITMAPINFO
        {
            public BITMAPINFOHEADER bmiHeader;
            public RGBQUAD bmiColors;
        }

        [StructLayout(LayoutKind.Sequential)]
        public struct BITMAPINFOHEADER
        {
            public uint biSize;
            public int biWidth;
            public int biHeight;
            public ushort biPlanes;
            public ushort biBitCount;
            public BitmapCompression biCompression;
            public uint biSizeImage;
            public int biXPelsPerMeter;
            public int biYPelsPerMeter;
            public uint biClrUsed;
            public uint biClrImportant;
        }

おそらく、DIB ( http://www.herdsoft.com/ti/davincie/imex3j8i.htm ) の作成と、データがビットマップに「上下逆さま」に格納されているなどの奇妙な点を考慮する必要があります。開くと鏡像になります:-)

これはビットマップ専用です。PNG を実行したい場合は、同様のことを行う必要がありますが、PNG ヘッダーをデコードする必要があります。これは、最も単純な形式ではそれほど難しくありませんが、完全な PNG 仕様のサポートを取得したい場合は、楽しい乗り物になります:- )

PNG は、異なるデータを見つけるために検索できる「ヘッダー」を持つチャンク ベースの形式を使用するため、ビットマップとは異なります。フォーマットで遊んでいるときに使用したいくつかのチャンクの例は

    string[] chunks =  
new string[] {"?PNG", "IHDR","PLTE","IDAT","IEND","tRNS",
"cHRM","gAMA","iCCP","sBIT","sRGB","tEXt","zTXt","iTXt",
"bKGD","hIST","pHYs","sPLT","tIME"};

また、PNG ファイルの Adler32 チェックサムについても学習する必要があります。したがって、実行したいファイル形式ごとに、さまざまな課題が追加されます。

返信でもっと完全なソースコードの例を提供できればいいのにと思いますが、それは複雑な問題であり、正直に言うと、自分でスワップを実装したことがないので、それについてあまり堅実なアドバイスをすることはできません.

簡単に言えば、BCL の画像処理能力はそれほど高くないということです。中程度の答えは、誰かがあなたに役立つ画像ライブラリを作成したかどうかを試して見つけることであり、長い答えは、袖を抜いてアプリケーションのコアを自分で作成することです.

あなたは実生活で私を知っているので、どこで私を見つけるか知っています;)

于 2009-02-20T20:11:18.443 に答える
1

1つのことが気になりました。見える部分だけでなく、全体を描いていますか?アプリケーションで表示しているよりも大きな部分の画像を描画しないでください。描画領域を制限するには、x、y、幅、および高さのパラメーターを使用してください。

于 2009-02-23T09:28:37.950 に答える
0

どうですか:

Image image = new Bitmap(filename);
using (Graphics gfx = Graphics.FromImage(image))
{    
// Stuff
}
于 2009-02-20T15:19:52.077 に答える
0

簡単な修正です。同じ問題がありました。2番目のビットマップインスタンスを作成し、コンストラクターでビットマップを渡しました。

于 2009-02-20T15:20:40.170 に答える
0

同じ寸法と色深度の新しい空白のビットマップを作成できますか? その場合、少なくとも環境がイメージを処理できることがわかります。次に、問題は画像読み込みサブシステムにあります。もちろん、示されているリンクが当てはまる可能性があります。

独自のビットマップ ローダーを作成できると思いますが、自明でない形式の場合はかなりの作業になるため、お勧めしません。

おそらく、標準ローダーでこれらの問題を回避する代替ライブラリが利用可能でしょうか?

于 2009-02-20T15:21:35.257 に答える
0

ビットマップの読み込みではなく、メモリ不足の原因となるレンダリングだとおっしゃっていますか?

もしそうなら、Bitmap.LockBits を使用してピクセルを取得し、基本的な画像リサイズを自分で作成できますか?

于 2009-02-20T15:21:42.430 に答える