- PR -

VB PoitureBoxで表示時にOutOfMemoryException

投稿者投稿内容
みかげ☆
会議室デビュー日: 2006/11/14
投稿数: 9
投稿日時: 2006-11-14 14:31
入力したTIFF画像を選択矩形でトリミングして出力する簡単なソフトを
作成しようと思いました。
VB6.0をいつも使っておりましたが、TIFF画像を入出力するのは.NETの
ほうが楽だと聞き、初めてVB2002で開発することにしました。
画像を扱うプログラムを作成するのも初めてです。

比較的小さな画像で読込み・矩形選択が出来るところまで作成できたので
このまま進めていこうと思ったのですが、大き目の画像を読み込むと
OutOfMemoryExceptionで異常終了してしまいました。

500MB程度までのファイルは表示できるようにしたいのですが、よい
解決法または参考になることがありましたらお教えください。

念のため他のマシンにあったVB2003/VB2005でもテストしてみましたが
同じ結果でした。

●入力ファイルの可否
ファイルA 結果× サイズ:218,973,892 バイト
ファイルB 結果○ サイズ:39,781,196 バイト
ファイルC 結果○ サイズ:80,395,409 バイト
ファイルD 結果○ サイズ:144,235,772 バイト
※すべてのファイルは他のソフトで表示確認をした壊れていない
 TIFF形式画像ファイルです。

●ソースコード
Private Sub LoadFile_Click(ByVal sender As System.Object, ByVal e As System.EventArgs) Handles LoadFile.Click

Dim OpenFileDialog1 As New OpenFileDialog()

OpenFileDialog1.Filter = "TIFF ファイル|*.tif"
If OpenFileDialog1.ShowDialog() = DialogResult.OK Then
  Dim hStream As New System.IO.FileStream(OpenFileDialog1.FileName,System.IO.FileMode.Open)

  Me.PictureBox2.Image = Image.FromStream(hStream)
  hStream.Close()
End If
OpenFileDialog1.Dispose()
End Sub

●Form
Form1.PictureBox1.SizeMode=Normal
※サイズにあった表示になりませんが現状は気にしないでください。
 ちなみにプロパティをほかのものにかえてもすべて同じ結果でした。

●実行環境
×WindowsXP Pro SP2 RAM:1.5G 開発環境:VB2002SP1
×WindowsXP Pro SP2 RAM:2.0G 開発環境:VB2003SP1/VB2005


[ メッセージ編集済み 編集者: みかげ☆ 編集日時 2006-11-15 09:12 ]
みかげ☆
会議室デビュー日: 2006/11/14
投稿数: 9
投稿日時: 2006-11-16 11:00
コメントが付かないようなので、なにに問題があるのか苦慮しております。
コードの書き方が悪いといったご意見や、同一のものを作っても問題なく
表示できた等の情報だけでもお聞かせいただければ幸いです。
甕星
ぬし
会議室デビュー日: 2003/03/07
投稿数: 1185
お住まい・勤務地: 湖の見える丘の上
投稿日時: 2006-11-16 11:26
32bitのWindowsアプリケーションで利用可能なメモリ空間は2GB。連続して確保できるのは、実質1GB程度。500MBの画像データを編集するためには、その数倍のメモリ空間が必要なので、よほど条件が良いか、64bitアプリケーションじゃない限り、全てのデータをメモリ上に展開して編集するのは無理がある。全てのデータをメモリ上に展開してから編集を行うようなライブラリでは対応できない。

よって規定のライブラリでは対応できず、自前でライブラリを作るか、大容量データも扱えるようなサードパーティのライブラリを用いる事になるかと・・・
みかげ☆
会議室デビュー日: 2006/11/14
投稿数: 9
投稿日時: 2006-11-16 13:02
甕星様、難しい中コメントありがとうございます。

32bitアプリケーションとしては500MBといった容量は無理に近いということを
理解いたしました。

PictureBoxも中身では画像形式の変換をしているでしょうし4倍程度までの
メモリを消費しているのではないかということも十分考えられるかと思います。

それでも220MB程度で問題になるのはあまりに小さいんじゃないかなあと
思ったりもします。ある程度限度がわかればそれ以上のファイルは
入力ではじけばいいとは思いますが、漠然と想定した範囲よりはかなり
小さすぎると感じた次第です。主観でしかないですが…

他のコメントが付かないところを見ますと、「そんな簡単な方法なんてないよ」
といわれている気もしますので、可能な範囲で容量制限するなりしたいと
思います。
個人的な範囲のアプリケーションなのでサードパーティのライブラリまでは
手が出るかどうか分かりませんが、一応調べてみようと思います。
unibon
ぬし
会議室デビュー日: 2002/08/22
投稿数: 1532
お住まい・勤務地: 美人谷        良回答(20pt)
投稿日時: 2006-11-16 16:22
引用:

みかげ☆さんの書き込み (2006-11-16 11:00) より:
コメントが付かないようなので、なにに問題があるのか苦慮しております。
コードの書き方が悪いといったご意見や、同一のものを作っても問題なく
表示できた等の情報だけでもお聞かせいただければ幸いです。


以下、余談になりますが。

質問文が詳しすぎるんでしょうね。質問が簡素すぎると「もっと詳しく書け」みたいなだけのコメントが付いてそれなりに盛り上がりますが、詳細に書くと大勢の興味を持たれないんですよ、きっと。

質問の際は、詳細に書いてから、その後、あえてちょっと手を抜いたように加工して投稿するのが、コツです。
#そんなコツはいやですが。

なお、昔から、開発環境に付属のコンポーネントは、メモリー使用量を気にせずオンメモリーですべてやる、というのが基本のようです。メモリー消費量や速度はサードパーティーが向上させたものを出す、という風にしないとサードパーティーの参入の余地がなくなるからでしょう。
こばさん
大ベテラン
会議室デビュー日: 2004/03/17
投稿数: 147
投稿日時: 2006-11-16 16:36
 System.Windows.Forms.PictureBox じゃなくて System.Drawing.Bitmap に放り込んでも OutOfMemoryException になりますか?
(手元にデカイTIFFがないので試せてないですが)

 用途(求める精度)がハッキリ分からないのですが、トリミング程度のことであれば200M超の TIFF ファイルを直接いじるのでなく、読み込み時に小さいサイズに変換かけて、画面上ではその小さいサイズのものでいじらせて、書き込みの段に裏で200M超の本体に手を加えて保存という風で

 仮に 200M 超が PictureBox に入ったとしても実に効率が悪い気がしますが
みかげ☆
会議室デビュー日: 2006/11/14
投稿数: 9
投稿日時: 2006-11-17 09:37
unibon様、コメントありがとうございます。

引用:
質問が簡素すぎると「もっと詳しく書け」みたいなだけのコメントが付いてそれなりに盛り上がりますが、詳細に書くと大勢の興味を持たれないんですよ、きっと。



なるほど、コツがあるのですね
性格上、なかなか難しいことですが参考にさせていただきます。
#実際そういうスレッドは多いのは理解してましたが…コツだったのですねぇ
みかげ☆
会議室デビュー日: 2006/11/14
投稿数: 9
投稿日時: 2006-11-17 10:01
こば様、コメントありがとうございます。
#二重敬称かとおもいますのでさんは省略いたしました

引用:
System.Windows.Forms.PictureBox じゃなくて System.Drawing.Bitmap に放り込んでも OutOfMemoryException になりますか?



最初に提示いたしましたソースではファイルを一旦System.IO.FileStreamに
格納してからForm1.PictureBox1.Imageに入れていますので、
System.Drawing.Bitmapにしても(たぶん)問題は発生しないものと思われます。
実際に試してみますので少々お時間をください。

引用:
用途(求める精度)がハッキリ分からないのですが、トリミング程度のことであれば200M超の TIFF ファイルを直接いじるのでなく、読み込み時に小さいサイズに変換かけて、画面上ではその小さいサイズのものでいじらせて、書き込みの段に裏で200M超の本体に手を加えて保存という風で



なるほど、メモリ上のものを縮小してしまってからPictureBox1に表示させれば
いいということですね。

最初は読み込んだ画像がPictureBox1に全体が入るように縮小表示
されることを考えており、そこで矩形選択した範囲を出力するように
したいと思っております。
PictureBox1のサイズは800×600と現状はしています。
Form1自体をリサイズすることは現状は考慮していません。
さらに上記が可能になったら、画像を拡大・縮小表示することも
可能にしていきたいと思っています。

引用:
(手元にデカイTIFFがないので試せてないですが)



たしかにテスト用データがないとソースを載せても再現できませんよね。
うかつでした
最初に書きました【入力ファイルの可否】で使用した実際のファイルを
白塗りしたものをネット上に置きました。
もしお手間でなければサンプルとしてご使用いただければと存じます。
ファイルA(×)とファイルD(○)に相当するものを同梱して
あります。
#ファイルサイズは小さいですが、解凍すると合計350MBくらいに
 なります。ご注意ください。
http://www.dekaino.net/~mikage/largewhite.zip


ちなみにVB2005で行った場合、OutOfMemoryExceptionで以下の情報が
出力されましたので付記いたします。

●エラー情報
System.OutOfMemoryException はハンドルされませんでした。
Message="メモリが不足しています。"
Source="System.Drawing"
StackTrace:
場所 System.Drawing.Graphics.CheckErrorStatus(Int32 status)
場所 System.Drawing.Graphics.DrawImage(Image image, Int32 x, Int32 y, Int32 width, Int32 height)
場所 System.Drawing.Graphics.DrawImage(Image image, Rectangle rect)
場所 System.Windows.Forms.PictureBox.OnPaint(PaintEventArgs pe)
場所 System.Windows.Forms.Control.PaintWithErrorHandling(PaintEventArgs e, Int16 layer, Boolean disposeEventArgs)
場所 System.Windows.Forms.Control.WmPaint(Message& m)
場所 System.Windows.Forms.Control.WndProc(Message& m)
場所 System.Windows.Forms.Control.ControlNativeWindow.OnMessage(Message& m)
場所 System.Windows.Forms.Control.ControlNativeWindow.WndProc(Message& m)
場所 System.Windows.Forms.NativeWindow.DebuggableCallback(IntPtr hWnd, Int32 msg, IntPtr wparam, IntPtr lparam)
場所 System.Windows.Forms.UnsafeNativeMethods.DispatchMessageW(MSG& msg)
場所 System.Windows.Forms.Application.ComponentManager.System.Windows.Forms.UnsafeNativeMethods.IMsoComponentManager.FPushMessageLoop(Int32 dwComponentID, Int32 reason, Int32 pvLoopData)
場所 System.Windows.Forms.Application.ThreadContext.RunMessageLoopInner(Int32 reason, ApplicationContext context)
場所 System.Windows.Forms.Application.ThreadContext.RunMessageLoop(Int32 reason, ApplicationContext context)
場所 System.Windows.Forms.Application.Run(Form mainForm)
場所 tiftrim.Form1.Main() 場所 F:\\proj1\\Form1.vb:行 1
場所 System.AppDomain.nExecuteAssembly(Assembly assembly, String[] args)
場所 System.AppDomain.ExecuteAssembly(String assemblyFile, Evidence assemblySecurity, String[] args)
場所 Microsoft.VisualStudio.HostingProcess.HostProc.RunUsersAssembly()
場所 System.Threading.ThreadHelper.ThreadStart_Context(Object state)
場所 System.Threading.ExecutionContext.Run(ExecutionContext executionContext, ContextCallback callback, Object state)
場所 System.Threading.ThreadHelper.ThreadStart()


[ メッセージ編集済み 編集者: みかげ☆ 編集日時 2006-11-17 11:19 ]

スキルアップ/キャリアアップ(JOB@IT)