DOBON.NET DOBON.NETプログラミング掲示板過去ログ

PictureBoxに補間して縮小表示した画像(500万画素クラス)をpaintイベントで速やかに再描画させたい

環境/言語:[OS : Windows XP Home Edition / 言語 : Visual Basic .NET]
分類:[.NET]

はじめまして。EXCEL VBAを経て、VB.NETにチャレンジしているところですが、非常に奥が深いですね。

【解決したい問題】

500万画素級のjpgファイルをPictureBoxに描画し、ウィンドウが隠れたりした場合再描画させたいと考えています。
Tipsにあった情報で、リサイズ方法を指定してのPictureBoxへの描画はできました。
また、これもTipsにあった情報で、paintイベントを用いた、再描画もできました。
ただ、毎回リサイズして描画しているため、遅くて実用的ではありません。
これを毎回リサイズせず、実用的な速度で再描画する方法は無いかと、試行錯誤、WEB検索もしましたが、発見できませんでした。

【解決するために何をしたか】

試行錯誤してみた内容は
1.PictureBox1.Invalidate()では、画像が消されてしまう。
2.それならと、Dim g As Graphics = Graphics.FromImage(PictureBox1.Image)で、PictureBox.Imageプロパティに設定する形で描画すると、再描画は PictureBox1.Invalidate()で速やかに行われるが、肝心の画像の方はg.DrawImage(PictureBox.Image,0,0,x,y)
では縮小されず原寸のまま。
3.g.DrawImageで描画したbitmap自体を取得できないか考えたが、image.FromHbitmap(hdc)とかで可能なのかさっぱり分からない。
といった、ところです。

こんな基本的な事が、容易に実現できない筈はないと思うのですが...
ご教示のほど、お願いします。
■No9319に返信(mitarashiさんの記事)
> はじめまして。EXCEL VBAを経て、VB.NETにチャレンジしているところですが、非常に奥が深いですね。
>
> 【解決したい問題】
>
> 500万画素級のjpgファイルをPictureBoxに描画し、ウィンドウが隠れたりした場合再描画させたいと考えています。
> Tipsにあった情報で、リサイズ方法を指定してのPictureBoxへの描画はできました。
> また、これもTipsにあった情報で、paintイベントを用いた、再描画もできました。
> ただ、毎回リサイズして描画しているため、遅くて実用的ではありません。
> これを毎回リサイズせず、実用的な速度で再描画する方法は無いかと、試行錯誤、WEB検索もしましたが、発見できませんでした。
>
> 【解決するために何をしたか】
>
> 試行錯誤してみた内容は
> 1.PictureBox1.Invalidate()では、画像が消されてしまう。
> 2.それならと、Dim g As Graphics = Graphics.FromImage(PictureBox1.Image)で、PictureBox.Imageプロパティに設定する形で描画すると、再描画は PictureBox1.Invalidate()で速やかに行われるが、肝心の画像の方はg.DrawImage(PictureBox.Image,0,0,x,y)
> では縮小されず原寸のまま。
> 3.g.DrawImageで描画したbitmap自体を取得できないか考えたが、image.FromHbitmap(hdc)とかで可能なのかさっぱり分からない。
> といった、ところです。
>
> こんな基本的な事が、容易に実現できない筈はないと思うのですが...
> ご教示のほど、お願いします。
>


本当に簡単に表示するのでしたら、以下のTipsが参考になります。

http://dobon.net/vb/dotnet/graphics/pictureboximage.html


縦横比を維持したまま、拡大/縮小(リサイズ?)したいということで
あれば一度 Bitmapオブジェクトを作成した方が簡単です。
以下は C#ですが、サンプルを載せておきます。


//Bitmapオブジェクトの作成
Bitmap bmp = new Bitmap("C:\Blue hills.jpg");
//PictureBoxの大きさを画像に合わせて設定する
PictureBox1.Width = bmp.Width * 80 / 100; //画像の大きさを元画像の80%に
PictureBox1.Height = bmp.Height * 80 / 100; //画像の大きさを元画像の80%に
//画像の大きさをPictureBoxに合わせる
PictureBox1.SizeMode = PictureBoxSizeMode.StretchImage;
//画像を表示する
PictureBox1.Image = bmp;
アドバイスありがとうございます。
しかしながら、カメラマニアの端くれとして、Bicubic法による補間縮小にこだわっております。補間縮小しても、Graphicsに対して行われる表示が縮小されているだけで、ビットマップ自体は縮小されていない様です。縮小されたビットマップは取得できないのでしょうか?ご教示下さい。


■No9320に返信(morさんの記事)
>
> 本当に簡単に表示するのでしたら、以下のTipsが参考になります。
>
> http://dobon.net/vb/dotnet/graphics/pictureboximage.html
>
>
> 縦横比を維持したまま、拡大/縮小(リサイズ?)したいということで
> あれば一度 Bitmapオブジェクトを作成した方が簡単です。
> 以下は C#ですが、サンプルを載せておきます。
>
>
> //Bitmapオブジェクトの作成
> Bitmap bmp = new Bitmap("C:\Blue hills.jpg");
> //PictureBoxの大きさを画像に合わせて設定する
> PictureBox1.Width = bmp.Width * 80 / 100; //画像の大きさを元画像の80%に
> PictureBox1.Height = bmp.Height * 80 / 100; //画像の大きさを元画像の80%に
> //画像の大きさをPictureBoxに合わせる
> PictureBox1.SizeMode = PictureBoxSizeMode.StretchImage;
> //画像を表示する
> PictureBox1.Image = bmp;
>
■No9346に返信(mitarashiさんの記事)
> アドバイスありがとうございます。
> しかしながら、カメラマニアの端くれとして、Bicubic法による補間縮小にこだわっております。補間縮小しても、Graphicsに対して行われる表示が縮小されているだけで、ビットマップ自体は縮小されていない様です。縮小されたビットマップは取得できないのでしょうか?ご教示下さい。


Bitmap bmp = new Bitmap("画像のパス");
としておき、

再描画必要なところで、
// 新しいBitmapオブジェクトの生成
// ここでは、PictureBoxと同じ大きさのBitmapを作成する
Bitmap bmp2 = new Bitmap(pictureBox1.Width, pictureBox1.Height);
// Graphicsオブジェクトの取得
Graphics g = Graphics.FromImage(bmp2);
// 補完方法の指定
g.InterpolationMode = System.Drawing.Drawing2D.InterpolationMode.Bicubic;
// 補完した画像の描画
g.DrawImage(bmp, 0, 0, pictureBox1.Width, pictureBox1.Height);
// PictureBoxへの表示
pictureBox1.Image = bmp2;
pictureBox1.Invalidate();

でどうでしょうか。
これで遅いと感じるようであれば、私にはお力にはなれません。

また、以下のTipsも参考になるでしょうか。
http://dobon.net/vb/dotnet/graphics/pictureboximageanddrawimage.html
■No9348に返信(morさんの記事)
下記のVB.NETのコードに載せ替えて動きました。ふつうのアプリケーションの動きに比べるとややぎくしゃくしますが、当方で最初に書いたコードでは、手前のウィンドウが空白の跡を引くような動作ですので、格段に良くなりました。ありがとうございました。
ところで、下記のコードだと、bmp2に何かを代入している様には思えないのですが、解説していただけると幸いです。

Private Sub Form1_Load(ByVal sender As System.Object, ByVal e As System.EventArgs) Handles MyBase.Load
Dim bmp As Bitmap = New Bitmap("test.jpg")
Dim bmp2 As Bitmap = New Bitmap(PictureBox1.Width, PictureBox1.Height)
Dim g As Graphics = Graphics.FromImage(bmp2)

g.InterpolationMode = _
System.Drawing.Drawing2D.InterpolationMode.HighQualityBicubic
g.DrawImage(bmp, 0, 0, PictureBox1.Width, PictureBox1.Height)
PictureBox1.Image = bmp2
g.Dispose()

End Sub

Private Sub PictureBox1_Paint(ByVal sender As Object, _
ByVal e As System.Windows.Forms.PaintEventArgs) _
Handles PictureBox1.Paint
PictureBox1.Invalidate()
End Sub
■No9363に返信(mitarashiさんの記事)
> ■No9348に返信(morさんの記事)
> 下記のVB.NETのコードに載せ替えて動きました。ふつうのアプリケーションの動きに比べるとややぎくしゃくしますが、当方で最初に書いたコードでは、手前のウィンドウが空白の跡を引くような動作ですので、格段に良くなりました。ありがとうございました。
> ところで、下記のコードだと、bmp2に何かを代入している様には思えないのですが、解説していただけると幸いです。
>
> Private Sub Form1_Load(ByVal sender As System.Object, ByVal e As System.EventArgs) Handles MyBase.Load
> Dim bmp As Bitmap = New Bitmap("test.jpg")
> Dim bmp2 As Bitmap = New Bitmap(PictureBox1.Width, PictureBox1.Height)
> Dim g As Graphics = Graphics.FromImage(bmp2)
>
> g.InterpolationMode = _
> System.Drawing.Drawing2D.InterpolationMode.HighQualityBicubic
> g.DrawImage(bmp, 0, 0, PictureBox1.Width, PictureBox1.Height)
> PictureBox1.Image = bmp2
> g.Dispose()
>
> End Sub
>
> Private Sub PictureBox1_Paint(ByVal sender As Object, _
> ByVal e As System.Windows.Forms.PaintEventArgs) _
> Handles PictureBox1.Paint
> PictureBox1.Invalidate()
> End Sub
>


解説するほどのスキルもないので、簡単に。。

上記の例では、リサイズが必要なときに bmp2を PictureBoxの大きさで"器"だけを作っています。
> Dim bmp2 As Bitmap = New Bitmap(PictureBox1.Width, PictureBox1.Height)

その器の Graphicsに対して、bmpの画像を補完処理を指定して描画しています。
> System.Drawing.Drawing2D.InterpolationMode.HighQualityBicubic
> g.DrawImage(bmp, 0, 0, PictureBox1.Width, PictureBox1.Height)

もとの画像(bmp)はそのままに、リサイズが必要なときに、その分の画像(bmp2)を生成しています。
■No9363に返信(mitarashiさんの記事)
> ... ふつうのアプリケーションの動きに比べるとややぎくしゃくしますが、 ...

PictureBox1_Paint メソッドを削除すると直りませんか。Paint イベントのハ
ンドラで自身の Invalidate メソッドを呼び出すと、Paint イベントが発生し
続けます。永久に、ずっと。

それから、Form1_Load メソッドで bmp の Dispose を忘れていますよ。
■No9367に返信(おおたさんの記事)
> ■No9363に返信(mitarashiさんの記事)
>>... ふつうのアプリケーションの動きに比べるとややぎくしゃくしますが、 ...
>
> PictureBox1_Paint メソッドを削除すると直りませんか。Paint イベントのハ
> ンドラで自身の Invalidate メソッドを呼び出すと、Paint イベントが発生し
> 続けます。永久に、ずっと。
>
> それから、Form1_Load メソッドで bmp の Dispose を忘れていますよ。
>

morさん、おおたさんありがとうございます。
下記コードだけで動いてしまうんですね。さんざん試行錯誤して、検索しまくったのは何だったんでしょうか...
結局基本的な事が理解できていないのでしょう。いじりながら勉強していこうと思いますので、今後もよろしくお願いします。

Private Sub Form1_Load(ByVal sender As System.Object, ByVal e As System.EventArgs) Handles MyBase.Load
Dim bmp As Bitmap = New Bitmap("test.jpg")
Dim bmp2 As Bitmap = New Bitmap(PictureBox1.Width, PictureBox1.Height)
Dim g As Graphics = Graphics.FromImage(bmp2)

g.InterpolationMode = _
System.Drawing.Drawing2D.InterpolationMode.HighQualityBicubic
g.DrawImage(bmp, 0, 0, PictureBox1.Width, PictureBox1.Height)
PictureBox1.Image = bmp2
g.Dispose()
bmp.Dispose()

End Sub
■No9368に返信(mitarashiさんの記事)
解決済みにするのを怠っていました。
解決済み!

DOBON.NET | プログラミング道 | プログラミング掲示板