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

画像の分割・拡大

環境/言語:[WindosXP・VB.NET・NET Framework2.0]
分類:[.NET]

あるビットマップ(256×256)を4分割した後、それぞれを4倍に拡大(64×64→256×256)し、
それをさらに4分割し…と5回繰り返し、1枚のビットマップから1024枚のビットマップを作る処理を作成しています。

とりあえずGraphics.DrawImageを使って作成してみたのですが、この処理をどうにかもっと高速化したいのです。

そこで以下の方法を試してみました。


1.ビットマップをMemoryStreamに読み込み、そこから切り出したデータを元に拡大したデータをバイト配列に入れてビットマップを作成。

→バイナリデータを1バイトずつ読み込みコピーするので、ほとんど処理時間は短縮できず。


2.TextureBrushに元のビットマップを設定し、Graphics.FillRectangleで拡大描画。

→処理自体は非常に高速になったが、切り出した画像のフチ(右と下だけ)がスムージングされた画像になってしまう。


3.WndowsAPIのStretchBltを使用して切り出し・拡大。

対象となるビットマップをいったんピクチャーボックスなどに表示(実際の画面上に描画)してやらないと使用できない。
今回の処理では画面は表示せずすべてバックグランドで処理したい。



これで手詰まりになってしまいました。
どなたか他の方法をご存知の方おられませんでしょうか?
あとTextureBrushのスムージングについて何かご存じ(そういう仕様である、回避方法)の方おられましたら教えていただけませんでしょうか。

よろしくお願いします。
> 対象となるビットマップをいったんピクチャーボックスなどに表示(実際の画面上に描画)してやらないと使用できない。
> 今回の処理では画面は表示せずすべてバックグランドで処理したい。
>
> これで手詰まりになってしまいました。
> どなたか他の方法をご存知の方おられませんでしょうか?

  ここのサイトのTipsにある・・・
  画像を動的に作成する
  http://dobon.net/vb/dotnet/graphics/createimage.html

  を流用すると、PictureBoxを経由せず、操作できます。

> あとTextureBrushのスムージングについて何かご存じ(そういう仕様である、回避方法)の方おられましたら教えていただけませんでしょうか。

  これは・・・
  http://dobon.net/vb/dotnet/graphics/scaling.html
  http://dobon.net/vb/dotnet/graphics/interpolationmode.html
  この辺の記事でしょうか。

以上。参考まで。
■No24008に返信(オショウさんの記事)

オショウさん、ありがとうございます。

>>対象となるビットマップをいったんピクチャーボックスなどに表示(実際の画面上に描画)してやらないと使用できない。
>>今回の処理では画面は表示せずすべてバックグランドで処理したい。
>
>   ここのサイトのTipsにある・・・
>   画像を動的に作成する
>   http://dobon.net/vb/dotnet/graphics/createimage.html
>
>   を流用すると、PictureBoxを経由せず、操作できます。

1と2の方法ではPictureBoxは経由していません。
3の方法でStretchBltを使用してデバイスコンテキストに切り出したビットマップをコピーしようとする場合に画面に描画されている必要があるということです。
また、紹介していただいたTipsの画像表示はDrawImageを使用してのものですよね…
これだと速度的に難があるので別の方法を模索しているわけです。


>>あとTextureBrushのスムージングについて何かご存じ(そういう仕様である、回避方法)の方おられましたら教えていただけませんでしょうか。
>
>   これは・・・
>   http://dobon.net/vb/dotnet/graphics/scaling.html
>   http://dobon.net/vb/dotnet/graphics/interpolationmode.html
>   この辺の記事でしょうか。
>

スケーリングにはDrawImageを使わずTextureBrushオブジェクトのScaleTransformを使用しています。
その際にスムージングがかかっているのでは…と考えているのですが。
InterpolationModeはFillRectangleの実行時のスムージング方法を設定している?
色々変えてみましたが、やはりスムージングがかかってしまいダメでした。
■No24003に返信(昆布巻きさんの記事)
> あるビットマップ(256×256)を4分割した後、それぞれを4倍に拡大(64×64→256×256)し、
> それをさらに4分割し…と5回繰り返し、1枚のビットマップから1024枚のビットマップを作る処理を作成しています。

はじめまして、まことと申します。

4分割して4倍して5回繰り返すと…すみません、最終的に元の画像の何倍に
なればいいんでしょうか?(^^;

当方C#しか使えないので読みにくいかとは思いますが、以下の様なコーディング
を行う事で、結果的に元画像を縦横2倍にして4等分にした画像を保存できます。

※元画像の特定領域を拡大コピーして保存するイメージです。

速度的にご満足頂けるかは分かりかねますが、一応オフスクリーン(メモリ内)で
処理しています。

private void button1_Click(object sender, EventArgs e)
{
    using (Image srcImg = Image.FromFile(@"b:\sample.bmp"))
    using (Bitmap dstImg = new Bitmap(srcImg.Width, srcImg.Height))  // ここも変更する必要有り?
    using (Graphics dstGrp = Graphics.FromImage(dstImg))
    {
        int captWidth = srcImg.Width / 2;   // この辺を最終的な拡大サイズに合わせて
        int captHeight = srcImg.Height / 2; // 変更する必要があります。
        Rectangle dstRect = new Rectangle(0, 0, dstImg.Width, dstImg.Height);

        for (int y = 0; y < dstImg.Height; y += captHeight)
        {
            for (int x = 0; x < dstImg.Width; x += captWidth)
            {
                dstGrp.DrawImage(srcImg, dstRect, x, y, captWidth, captHeight, GraphicsUnit.Pixel);
                dstImg.Save(string.Format(@"b:\{0}{1}.bmp", x, y));
            }
        }
    }
}
速度面の性能アップが必要だと言うことですので、テストして
みました。

Core2Quad 2.84 GHz、メモリ3GB搭載の環境で・・・
2分割2倍拡大で行ってみましたが、約5秒強でした。

しかしながら、5回目の時点でのMemoryStreamのメモリ占有域
は、約500MBに達しました。

では、4分割4倍拡大の場合・・・単純に2GB必要になると
思うのですが・・・メモリスワップ発生して速度低下していま
せんか?

以上。参考までに。
間違いを修正

MemoryStreamでは、物理メモリが無くなった時点でエラーしますので
4分割4倍拡大では、搭載メモリが3GB(32ビットOS)では、
メモリ不足でエラーします。

64ビットOSにして、8GBほどメモリ搭載すれば、可能かな?

よって、物理的なストリーム(HDD)に読み書きしながら行うこと
になりますので、どう考えても遅いかと。

※ DrawImageが遅いとは到底思えませんが・・・

以上。
■No24014に返信(まことさんの記事)

まことさん、ありがとうございます。

> 速度的にご満足頂けるかは分かりかねますが、一応オフスクリーン(メモリ内)で
> 処理しています。

この方法(DrawImageを使用)だと速度的に難があるので別の方法を模索中です。
■No24016に返信(オショウさんの記事)

オショウさん、ありがとうございます。

> Core2Quad 2.84 GHz、メモリ3GB搭載の環境で・・・
> 2分割2倍拡大で行ってみましたが、約5秒強でした。
>
> しかしながら、5回目の時点でのMemoryStreamのメモリ占有域
> は、約500MBに達しました。
>
> では、4分割4倍拡大の場合・・・単純に2GB必要になると
> 思うのですが・・・メモリスワップ発生して速度低下していま
> せんか?

わざわざテストしていただきありがとうございます。
ですがMemoryStreamは分割ごとにメモリを解放しているのでスワップは発生していません。

実際には分割・拡大以外にもDBへの登録などを行っているので分割・拡大のみの処理時間を測定したわけではないのですが、
DrawImageを使わずにTextureBrushを使ってFillRectangleで描画する(分割後の画像がスムージングされてしまいますが)か、
API関数を使用する(ピクチャーボックスに描画する必要があるが)かの方法ですと全体の処理が約5倍ほど速くなります。

DrawImage以外の処理部分も最適化を試みてはいるのですが、やはりこれが一番のボトルネックになっているようなので…
> ですがMemoryStreamは分割ごとにメモリを解放しているのでスワップは発生していません。

  と言うことは、1回の拡大処理毎にHDD等に保存されていると言うこと
  でしょうか?

> 実際には分割・拡大以外にもDBへの登録などを行っているので分割・拡大のみの処理時間を測定したわけではないのですが、
> DrawImageを使わずにTextureBrushを使ってFillRectangleで描画する(分割後の画像がスムージングされてしまいますが)か、

  処理時間を測定しなくて、『どこが遅い』のでしょうか?

> API関数を使用する(ピクチャーボックスに描画する必要があるが)かの方法です
と全体の処理が約5倍ほど速くなります。

  APIでもメモリ上だけで描画と言うかビットマップの処理は可能
  ですが。

> DrawImage以外の処理部分も最適化を試みてはいるのですが、やはりこれが一番のボトルネックになっているようなので…

  聞けば聞くほどよく解らなくなってきました。

  一体、どういう方法でどの程度時間がかかったものが、どう変更したら
  どの程度高速化したのでしょうか?

  私は自分の環境でですが具体的に測定した時間を提示しています。

  このまま議論しても解決に至るかどうかは、結局、あなた次第。

以上。
  • 題名: Re[3]: 画像の分割・拡大
  • 著者: オショウ
  • 日時: 2009/02/17 15:50:48
  • ID: 24021
  • この記事の返信元:
  • この記事への返信:
    • (なし)
  • ツリーを表示
その際にスムージングがかかっているのでは…と考えているのですが。
> InterpolationModeはFillRectangleの実行時のスムージング方法を設定している?
> 色々変えてみましたが、やはりスムージングがかかってしまいダメでした。

  DrawImage では、InterpolationMode設定の効果はありますネ!
  FillRectangleでは、速度面では、DrawImageの60%程度(高速化)
  があるのは実測して解りましたが、画質に関しては影響が無いよう
  です。

  因みに、DrawImageの場合、拡大率は、ScaleTransformで設定して
  おられたのですよね?

  画質的にFillRectangle使えないなら、DrawImageでInterpolationMode
  の設定とScaleTransform使うしかないかと。

  後は、APIですべて置き換える・・・

以上。
■No24020に返信(オショウさんの記事)

すみません、説明の仕方がまずかったです。
というか私自身が処理の流れを少し勘違いしていました。
(別の人が作ったソースでDrawImageの部分をどうにかしてくれと言われてもので…)

申し訳ありませんがもう一度処理の流れを説明させていただきます。


1.元になる1枚の画像(1024×1024)をBitmapオブジェクトに読み込み、サムネイルを作成してDBに保存。

2-1.元の画像の(0,0)位置から(512,512)までの部分を切り出し、これを(256×256)に縮小し、MemoryStreamにImageFormatをpngに指定して保存後、MemoryStreamの内容をDBに保存。MemoryStreamを解放。
2-2.元の画像の(0,512)位置から(512,1024)までの部分を切り出し、これを(256×256)に縮小し…
2-3.元の画像の(512,0)位置から(1024,512)までの部分を切り出し、これを(256×256)に縮小し…
2-4.元の画像の(512,512)位置から(1024,1024)までの部分を切り出し、これを(256×256)に縮小し…

上記2の手順で元の画像を4分割した画像4枚が保存される。

3-1.元の画像の(0,0)位置から(256,256)までの部分を切り出し、これを(256×256)のままでMemoryStreamにImageFormatをpngに指定して保存後、MemoryStreamの内容をDBに保存。MemoryStreamを解放。
3-2.元の画像の(0,256)位置から(256,512)までの部分を切り出し、これを(256×256)のままで…

3-16.元の画像の(768,768)位置から(1024,1024)までの部分を切り出し、これを(256×256)のままで…

上記3の手順で元の画像を16分割した画像16枚が保存される。

4-1.元の画像の(0,0)位置から(128,128)までの部分を切り出し、これを(256×256)に拡大し、MemoryStreamにImageFormatをpngに指定して保存後、MemoryStreamの内容をDBに保存。MemoryStreamを解放。
4-2.元の画像の(0,128)位置から(128,256)までの部分を切り出し、これを(256×256)に拡大し…

4-64.元の画像の(896,896)位置から(1024,1024)までの部分を切り出し、これを(256×256)に拡大し…

上記4の手順で元の画像を64分割した画像64枚が保存される。


このようにして5段階1024分割まで行い、計1365枚の画像をDBに保存します。

あくまで元の画像から切り出しているのでMemoryStreamが大量のメモリを消費するわけではありません。


一般的なDrawImageを使った方法だと、当方の環境(Core2Duo1.2GHz,メモリ2GB)で処理開始から終了まで(進捗状況の更新などの処理も含めて)1画像あたり約3分30秒かかっています。
------------------------------------------------------------------------------------
Dim g As Graphics = Graphics.FromImage(bmpTo)
Dim rect As New Rectangle(0, 0, 256, 256)
g.DrawImage(bmpFrom, rect, sngOffsetX, sngOffsetY, sngWidth, sngHeight, GraphicsUnit.Pixel)
------------------------------------------------------------------------------------


TextureBrushを使ってFillRectangleで描画する方法ですと約1分です。
------------------------------------------------------------------------------------
Dim g As Graphics = Graphics.FromImage(bmpTo)
Dim rect As New Rectangle(sngOffsetX, sngOffsetY, sngWidth, sngHeight)
Using objTextureBrush As New TextureBrush(bmpFrom, Drawing2D.WrapMode.Clamp, rect)
  objTextureBrush.ScaleTransform(256 / sngWidth, 256 / sngHeight)
  g.FillRectangle(objTextureBrush, 0, 0, 256, 256)
End Using
------------------------------------------------------------------------------------


>   APIでもメモリ上だけで描画と言うかビットマップの処理は可能
>   ですが。

先日色々調べた結果無理と判断したのですが、もう一度調べてみたところ下記のページが見つかりました。

http://72.14.235.132/search?q=cache:RVB_mjt28YMJ:social.msdn.microsoft.com/forums/ja-JP/vbgeneralja/thread/ffbc8193-210d-42ce-bb4f-139904c556fa/+DrawImage%E3%81%8C%E9%81%85%E3%81%84&hl=ja&ct=clnk&cd=4&gl=jp&lr=lang_ja&client=firefox-a

このページを参考に下記のようなソースを書いてみましたが、真っ白なビットマップが作成されただけ上手くいきませんでした。(PictureBoxへの表示は出来ました)
------------------------------------------------------------------------------------
Dim bmp As Bitmap = CType(pict.Image, Bitmap)
Dim bmpNew As New Bitmap(256, 256)
Dim grDest As Graphics = Graphics.FromImage(bmpNew)
Dim grSrc As Graphics = Graphics.FromImage(bmp)
Dim hdcDest As IntPtr = grDest.GetHdc()
Dim hdcSrc As IntPtr = grSrc.GetHdc()
Dim hBitmap As IntPtr = bmp.GetHbitmap()
Dim oldBitmap As IntPtr = SelectObject(hdcSrc, hBitmap)
BitBlt(hdcDest, 0, 0, bmp.Width, bmp.Height, hdcSrc, 0, 0, &HCC0020)
SelectObject(hdcSrc, oldBitmap)
bmpNew.Save("c:\hoge.bmp")
DeleteObject(hBitmap)
grSrc.ReleaseHdc(hdcSrc)
grDest.ReleaseHdc(hdcDest)
grSrc.Dispose()
grDest.Dispose()
------------------------------------------------------------------------------------

どのようにすればAPIでビットマップの処理が可能となるのでしょうか。。。
> どのようにすればAPIでビットマップの処理が可能となるのでしょうか。。。

Public Enum TernaryRasterOperations As UInteger
SRCCOPY = &HCC0020 ' dest = source
SRCPAINT = &HEE0086 ' dest = source OR dest
SRCAND = &H8800C6 ' dest = source AND dest
SRCINVERT = &H660046 ' dest = source XOR dest
SRCERASE = &H440328 ' dest = source AND (NOT dest )
NOTSRCCOPY = &H330008 ' dest = (NOT source)
NOTSRCERASE = &H1100A6 ' dest = (NOT src) AND (NOT dest)
MERGECOPY = &HC000CA ' dest = (source AND pattern)
MERGEPAINT = &HBB0226 ' dest = (NOT source) OR dest
PATCOPY = &HF00021 ' dest = pattern
PATPAINT = &HFB0A09 ' dest = DPSnoo
PATINVERT = &H5A0049 ' dest = pattern XOR dest
DSTINVERT = &H550009 ' dest = (NOT dest)
BLACKNESS = &H42 ' dest = BLACK
WHITENESS = &HFF0062 ' dest = WHITE
End Enum

<DllImport("Gdi32.dll", EntryPoint:="SelectObject", CallingConvention:=CallingConvention.StdCall, CharSet:=CharSet.Auto, ExactSpelling:=True)> _
Public Shared Function SelectObject(ByVal hdc As IntPtr, ByVal hObject As IntPtr) As IntPtr
End Function

<DllImport("gdi32.dll")> _
Public Shared Function DeleteObject(ByVal hObject As IntPtr) As Boolean
End Function

<DllImport("gdi32.dll")> _
Public Shared Function BitBlt(ByVal hdc As IntPtr, ByVal nXDest As Integer, ByVal nYDest As Integer, _
ByVal nWidth As Integer, ByVal nHeight As Integer, ByVal hdcSrc As IntPtr, _
ByVal nXSrc As Integer, ByVal nYSrc As Integer, ByVal dwRop As TernaryRasterOperations) As Boolean
End Function

<DllImport("gdi32.dll")> _
Public Shared Function StretchBlt(ByVal hdcDest As IntPtr, ByVal nXOriginDest As Integer, ByVal nYOriginDest As Integer, _
ByVal nWidthDest As Integer, ByVal nHeightDest As Integer, ByVal hdcSrc As IntPtr, ByVal nXOriginSrc As Integer, _
ByVal nYOriginSrc As Integer, ByVal nWidthSrc As Integer, ByVal nHeightSrc As Integer, ByVal dwRop As TernaryRasterOperations) As Boolean
End Function

  と宣言して・・・

Using bmpOrg As Bitmap = Image.FromFile(Application.StartupPath + "\TEST_ORG.BMP")
Using bmpNew As Bitmap = New Bitmap(bmpOrg.Width, bmpOrg.Height)
Using grSrc As Graphics = Graphics.FromImage(bmpOrg)
Using grDest As Graphics = Graphics.FromImage(bmpNew)
Dim hdcSrc As IntPtr = grSrc.GetHdc()
Dim hdcDest As IntPtr = grDest.GetHdc()
Dim hBitmap As IntPtr = bmpOrg.GetHbitmap()
Dim oldBitmap As IntPtr = SelectObject(hdcSrc, hBitmap)

BitBlt(hdcDest, 0, 0, bmpOrg.Width, bmpOrg.Height, hdcSrc, 0, 0, TernaryRasterOperations.SRCCOPY)

SelectObject(hdcSrc, oldBitmap)

DeleteObject(hBitmap)

grSrc.ReleaseHdc(hdcSrc)
grDest.ReleaseHdc(hdcDest)
End Using
End Using

bmpNew.Save(Application.StartupPath + "\CONV.BMP")

End Using
End Using

● 違い解りますか?

以上。
  • 題名: Re[9]: 画像の分割・拡大
  • 著者: 昆布巻き
  • 日時: 2009/02/17 17:51:19
  • ID: 24025
  • この記事の返信元:
  • この記事への返信:
    • (なし)
  • ツリーを表示
■No24024に返信(オショウさんの記事)

オショウさん、ありがとうございます。


> ● 違い解りますか?

ReleaseHdcを行った後にファイルを保存する必要があるということですか?
確かにビットマップのコピーが出来ました。
一度これを応用して拡大・分割を試してみます。
■No24022に返信(昆布巻きさんの記事)

■No24018に返信(昆布巻きさんの記事)
> この方法(DrawImageを使用)だと速度的に難があるので別の方法を模索中です。

拡大処理じゃなくて縮小処理だったんですね(^^; 掲示板に書き込む内容を用意
してる間に新情報がずらずらと出て来たので1から考えなおしてみました。

で、ちょっと汚いソースですがこんな感じで実現出来まして、当方の環境(CoreDuo 1.83GHz,MEM 2G)
では約27秒前後で終了しました…DrawImage でやってもStretchBltでやってもです。

DrawImage は安定して27秒台をキープしていたのですが、StretchBltだと1分以上
掛る場合もありましたが、その辺はよく分かりません。

つまり、DrawImageが元凶とはならないのでは?と言う事です。


■ DrawImage 版
private void button1_Click(object sender, EventArgs e)
{
    using (Bitmap srcImg = new Bitmap(@"C:\sample\sample.bmp"))
    using (Bitmap dstImg = new Bitmap(256, 256))
    using (Graphics dstGrp = Graphics.FromImage(dstImg))
    {
        dstGrp.InterpolationMode = System.Drawing.Drawing2D.InterpolationMode.NearestNeighbor;
        Rectangle dstRect = new Rectangle(0, 0, dstImg.Width, dstImg.Height);

        this.Text = string.Empty;
        DateTime st = DateTime.Now;

        for (int cnt = 2; cnt < 64; cnt+=cnt)
        {
            Size captSize = new Size(srcImg.Width / cnt, srcImg.Height / cnt);

            for (int y = 0; y < srcImg.Height; y += captSize.Height)
            {
                for (int x = 0; x < srcImg.Width; x += captSize.Width)
                {
                    dstGrp.DrawImage(srcImg, dstRect, x, y, captSize.Width, captSize.Height, GraphicsUnit.Pixel);
                    dstImg.Save(string.Format(@"c:\sample\{0}-({1},{2}).bmp", captSize.Width, x, y));
                }
            }
        }
        
        DateTime et = DateTime.Now;
        TimeSpan ts = et - st;
        this.Text = ts.ToString();
    }
}


■ StretchBlt版 (各種宣言は省略)
private void button2_Click(object sender, EventArgs e)
{
    using (Bitmap srcImg = new Bitmap(@"C:\sample\sample.bmp"))
    using (Bitmap dstImg = new Bitmap(256, 256))
    using (Graphics srcGrp = Graphics.FromImage(srcImg))
    using (Graphics dstGrp = Graphics.FromImage(dstImg))
    {
        Rectangle dstRect = new Rectangle(0, 0, dstImg.Width, dstImg.Height);

        this.Text = string.Empty;
        DateTime st = DateTime.Now;

        IntPtr srcHbmp = srcImg.GetHbitmap();

        for (int cnt = 2; cnt < 64; cnt += cnt)
        {
            Size captSize = new Size(srcImg.Width / cnt, srcImg.Height / cnt);

            for (int y = 0; y < srcImg.Height; y += captSize.Height)
            {
                for (int x = 0; x < srcImg.Width; x += captSize.Width)
                {
                    IntPtr srcHdc = srcGrp.GetHdc();
                    IntPtr dstHdc = dstGrp.GetHdc();
                    SelectObject(srcHdc, srcHbmp);
                    try
                    {
                        StretchBlt(dstHdc, 0, 0, dstRect.Width, dstRect.Height,
                                   srcHdc, x, y, captSize.Width, captSize.Height,
                                   TernaryRasterOperations.SRCCOPY);
                    }
                    finally
                    {
                        DeleteObject(srcHbmp);
                        srcGrp.ReleaseHdc();
                        dstGrp.ReleaseHdc();
                    }

                    dstImg.Save(string.Format(@"c:\sample\{0}-({1},{2}).bmp", captSize.Width, x, y));
                }
            }
        }

        DateTime et = DateTime.Now;
        TimeSpan ts = et - st;
        this.Text = ts.ToString();
    }
}
■No24026に返信(まことさんの記事)

追伸です。

>         dstGrp.InterpolationMode = System.Drawing.Drawing2D.InterpolationMode.NearestNeighbor;

DrawImage でも上記の設定をしないと処理速度が重くなりました。(2分強)

DrawImage が遅いと仰られてる原因は恐らくこれではありませんか?

InterpolationMode プロパティのデフォルト値でも何かしらのスムージング処理
をしているようです。
■No24027に返信(まことさんの記事)

まことさん、ありがとうございます。


> DrawImage は安定して27秒台をキープしていたのですが、StretchBltだと1分以上
> 掛る場合もありましたが、その辺はよく分かりません。
>
> つまり、DrawImageが元凶とはならないのでは?と言う事です。

当方の環境でもStretchBltの方がかなり遅くなってしまいました。。。

このページを見てStretchBltを使用してみようと考えたのですが、なぜか情報通りの結果が出ず。
http://standardization.at.webry.info/200810/article_2.html



>> dstGrp.InterpolationMode = System.Drawing.Drawing2D.InterpolationMode.NearestNeighbor;
>
> DrawImage でも上記の設定をしないと処理速度が重くなりました。(2分強)
>
> DrawImage が遅いと仰られてる原因は恐らくこれではありませんか?

InterpolationModeプロパティの変更も試してはみましたが、多少は速くなるもののFillRectangleほどの高速化のもなりませんね。。。


そろそろ完全に手詰まりっぽいです。
■No24028に返信(昆布巻きさんの記事)

> 当方の環境でもStretchBltの方がかなり遅くなってしまいました。。。

StretchBlt は拡大処理が入る関係上、BitBltよりも処理が重くなります。

さらにGetHdcやReleaseHdc、更にSelectObjectやDeleteObjectなども都度
実行していますので、その辺りのオーバーヘッドも気になる所です。


> InterpolationModeプロパティの変更も試してはみましたが、
> 多少は速くなるもののFillRectangleほどの高速化のもなりませんね。。。

ちなみにTextureBrush時のスムージング制御は、No.23870の魔界の仮面弁士さん
の書込みを見るに対応していない様です。

つまりスムージングを制御する方が大事なのであれば、TextureBrushによる
FillRectangleは使えないと言う事になってしまいます。

一応、先のコードを FillRectangle に改造してみたのですが、なんと37秒強
とDrawImage版(27秒台)よりも遅い結果が出ちゃいました(^_^;

描画速度で稼ぐ時間よりも、TextureBrushの生成で消費される時間の方が多い
のかもしれません。

しかし腑に落ちないのはDrawImageの処理速度ですねぇ…、C#とVBでは処理に
違いが有るのでしょうか。(.NET Frameworkでそれは無いと思いたい)


さらなる高速化を図るには…処理をスレッド化して512〜64サイズと32サイズ
とで分けてみるとかは如何でしょうか。

しかし何となく手詰まりな感じがしてまいりました(~_~;

private void button1_Click(object sender, EventArgs e)
{
    using (Bitmap srcImg = new Bitmap(@"C:\sample\sample.bmp"))
    using (Bitmap dstImg = new Bitmap(256, 256))
    using (Graphics dstGrp = Graphics.FromImage(dstImg))
    {
        dstGrp.InterpolationMode = System.Drawing.Drawing2D.InterpolationMode.NearestNeighbor;
        Rectangle dstRect = new Rectangle(0, 0, dstImg.Width, dstImg.Height);

        this.Text = string.Empty;
        DateTime st = DateTime.Now;

        for (int cnt = 2; cnt < 64; cnt += cnt)
        {
            Size captSize = new Size(srcImg.Width / cnt, srcImg.Height / cnt);

            for (int y = 0; y < srcImg.Height; y += captSize.Height)
            {
                for (int x = 0; x < srcImg.Width; x += captSize.Width)
                {
                    TextureBrush tb = new TextureBrush(srcImg, new Rectangle(x, y, captSize.Width, captSize.Height));
                    tb.ScaleTransform(256 / captSize.Width, 256 / captSize.Height);
                    dstGrp.FillRectangle(tb, 0, 0, 256, 256);
                    dstImg.Save(string.Format(@"c:\sample\{0}-({1},{2}).bmp", captSize.Width, x, y));
                }
            }
        }

        DateTime et = DateTime.Now;
        TimeSpan ts = et - st;
        this.Text = ts.ToString();
    }
}
  • 題名: Re[12]: 画像の分割・拡大
  • 著者: 昆布巻き
  • 日時: 2009/02/17 20:19:26
  • ID: 24030
  • この記事の返信元:
  • この記事への返信:
    • (なし)
  • ツリーを表示
■No24029に返信(まことさんの記事)

まことさん、ありがとうございます。


> ちなみにTextureBrush時のスムージング制御は、No.23870の魔界の仮面弁士さん
> の書込みを見るに対応していない様です。
>
> つまりスムージングを制御する方が大事なのであれば、TextureBrushによる
> FillRectangleは使えないと言う事になってしまいます。

むぅ…これが出来ればすぐに解決出来ていた問題なんですがね…


> さらなる高速化を図るには…処理をスレッド化して512〜64サイズと32サイズ
> とで分けてみるとかは如何でしょうか。

すみません、書き忘れていましたがそれは一番最初に試しました。
若干(10秒程度?)の高速化はあったものの、全体で3分30秒かかる処理を1分以内に抑えろという依頼だったので別の方法を考えていました。

FillRectangleが処理時間だけで言えば劇的に速かったので、これに匹敵する方法があればよかったのですが…手詰まりですかね。。。
なんだか矛盾してきたようなので、ちょっと整理させてください。

----------------------------------------------------------------------
> 3.WndowsAPIのStretchBltを使用して切り出し・拡大。
> 対象となるビットマップをいったんピクチャーボックスなどに表示(実際の画面上に描画)してやらないと使用できない。
> 今回の処理では画面は表示せずすべてバックグランドで処理したい。

> 3の方法でStretchBltを使用してデバイスコンテキストに切り出したビットマップをコピーしようとする場合に画面に描画されている必要があるということです。

> API関数を使用する(ピクチャーボックスに描画する必要があるが)かの方法ですと全体の処理が約5倍ほど速くなります。
----------------------------------------------------------------------

書込みの前半部分では StretchBlt を使えば5倍速くなるような事を書かれていますが…

----------------------------------------------------------------------
> 当方の環境でもStretchBltの方がかなり遅くなってしまいました。。。
----------------------------------------------------------------------

後半では遅くなったんですよね…これはどう言う事でしょうか?

5倍速くなるコード(ピクチャーボックスを使った)も良ければ提示して頂けませんか?


また高速化が目的と言う事なので、今一度処理全体を通してどこがボトルネック
になっているのかを、処理ブロックごとに時間計測してみては如何でしょう。

最後に、可能で有れば1364回(?)ループしながら保存している部分のソースコード
を動作可能な状態で提示して頂ければ、新しい方法が思い浮かぶかもしれません。

その際はぜひ"図表モード"での書込みをお願いします。
> 一般的なDrawImageを使った方法だと、当方の環境(Core2Duo1.2GHz,メモリ2GB)で処理開始から終了まで(進捗状況の更新などの処理も含めて)1画像あたり約3分30秒かかっています。

  この部分ですが・・・
  その3分30秒の内訳は?

  分割しMemoryStreamに入れ終わるまでの時間と
  その後のDBに保存の時間

  当方の環境で、SQL Server 2005 に、以下のURLのものを参考に
  DBを構築して再度行ってみました。

  http://codezine.jp/article/detail/3026

  ざっくり・・・
  分割に、5秒強
  DB保存に、15秒強

  ただし、DB領域が不足すると領域確保機能が働きますので、その
  場合は、保存に40秒強でした。

  よって、分割の時間的負荷より、DB保存に時間が費やされている
  のが大半かと思いますが。

  参考まで

以上。
■No24033に返信(まことさんの記事)

まことさん、ありがとうございます。


すみません、説明の仕方が下手(というか間違っています)で混乱させてしまい申し訳ありません。


> DrawImageを使わずにTextureBrushを使ってFillRectangleで描画する(分割後の画像がスムージングされてしまいますが)か、

↑これが約5倍。

> API関数を使用する(ピクチャーボックスに描画する必要があるが)かの方法

↑テスト用のプロジェクトにおいてStretchBltを使ってピクチャーボックスに表示させながら別プロジェクトに拡大コピー出来ることは確認しただけ。

自分で書いててもなんか混乱しちゃってます。申し訳ありません。


> また高速化が目的と言う事なので、今一度処理全体を通してどこがボトルネック
> になっているのかを、処理ブロックごとに時間計測してみては如何でしょう。

そうですね。もう一度最初から頭の中を整理しつつ考えなおしてみます。


> 最後に、可能で有れば1364回(?)ループしながら保存している部分のソースコード
> を動作可能な状態で提示して頂ければ、新しい方法が思い浮かぶかもしれません。

元々のソースがあまり綺麗ではなく、その処理の部分だけを抜き出して動作可能な状態でっていうのがすぐにはちょっと難しいですね。。。
まことさんが■No24026で提示してくださったような流れで書き直していますので、それが出来ましたらもう一度全体・処理ごとの時間を計測したうえで掲示板にあげさせていただきます。
■No24034に返信(オショウさんの記事)

オショウさん、ありがとうございます。

>   よって、分割の時間的負荷より、DB保存に時間が費やされている
>   のが大半かと思いますが。

だとしたらFillRectangleに変更するだけであれだけ速くなるのもおかしいような。。。

何にせよ■No24033のまことさんへの返信に書いたように、一度頭の中とソースを整理したうえでボトルネックがどこにあるかなどを再調査しようかと思います。
  • 題名: Re[5]: 画像の分割・拡大
  • 著者: 昆布巻き
  • 日時: 2009/02/18 0:50:44
  • ID: 24037
  • この記事の返信元:
  • この記事への返信:
    • (なし)
  • ツリーを表示
すみません、■No24035の訂正です。

別プロジェクトに拡大コピー出来ること

ではなく

別ピクチャーボックスに拡大コピー出来ること
  • 題名: Re[10]: 画像の分割・拡大
  • 著者: オショウ
  • 日時: 2009/02/18 10:43:56
  • ID: 24039
  • この記事の返信元:
  • この記事への返信:
    • (なし)
  • ツリーを表示
再度、仕様手順に則って、時間計測してみました。
尚、極力メモリを使わない方法に変更しています。

数値は、ms(ミリ秒)です。

自動拡張機能オン
512 size 237 237 4365 4655 493
256 size 919 896 991 1200 1728
128 size 3487 3526 3492 3785 3847
64 size 13775 13775 13671 14864 14948
32 size 55177 55012 55602 61815 61910
合計 73595 73446 78121 86319 82926

自動拡張機能オフ(DBサイズ固定)
512 size 4397 269 234 235 236
256 size 958 1207 898 898 896
128 size 3794 4553 3515 3517 3507
64 size 14799 14886 14574 13819 14225
32 size 57270 57301 54798 55121 53615
合計 81218 78216 74019 73590 72479

SQL Server 2005 のメモリ使用量は、初期状態 64MB
から、上記5回実行後、1.5GB まで増加。

同一マシン上で処理を行った場合、搭載メモリ上の都合で
スワップあるのでは?
また別サーバー上のDBでも当然メモリ使用量の増加で、
画像変換よりもDB保存に時間を要する状態に陥っている
のではと推測します。

環境がどうなのか。またご使用のPCのスペックに依存す
る部分が大きいと思いますので、DrawImageがどうの・・・
と言うことではないと思いますが、全てを見直す必要があ
るかと。

※ DBに画像保存するのは、やめて保存パスを文字列と
  して管理した方が、当然、早くなります。

以上。

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