- 2008-10-27 (月) 0:00
- 日常
[開発環境]
WindowsXP Professional SP3,
Intel PentiumD 3.00Hz,4GBメモリ,160GB HDD,
Visual Studio 2005 Professional Edition(VB.NET)
今回のメモの本題に入る前に、まずは状況を説明します。
少々長いですがお付き合いください。
10月はWebカメラソフト(WebCameraSnap)
の高速化に取り組んでいます。
高速化で空いた時間を高機能化に割り当てよう、という作戦です。
最終目標は10フレーム/秒程度出せればOKかなと思っています。
実現には様々なハードルがありますが、実現できたら楽しそうです。
(高速化にあたり、私が画像処理でよく使っているクラスや手法の所要時間を調べました。
処理時間の概算に使えて便利ですので、掲載希望の方がいらっしゃれば、参考資料として結果を掲載しようと思います)
高機能化でこんなことを実現したいと考えています。
とりあえず、リストの上から実現していくのが妥当かな。
高速化の仕事の合間に「動きの方向の識別」を試してみました。
(すみません、まだ本題には入りません^^;、もう少しです)
(参考図書)
「フルスクラッチによるグラフィックスプログラミング入門」
P.313-320,オプティカルフロー
この本には基本的な考え方が書かれていて、考え方の参考になります。
処理のエッセンスをVB.NETで実装してみました。
実験には映像が必要ですが、事務所の映像には飽きてしまいました。
しばらくは、気分転換に他の映像がよいですね。
偶然、今日録画していたTV番組に、商店街を歩いている人々の映像がありました。
外には絶対に出しませんので、研究目的で使わせていただきました。
前後2枚の画像から、動きの方向を検出できるのか調べてみました。
画像は掲載できませんが、こんな条件で実験しました。
(VB.NETのコードを見たいという人がいたら後日掲載します)
コンセプトコードは、こんなものでしょうか。
動きの方向は検出できそうです。問題は処理にかかる時間です。
「フルスクラッチによる~」では、検索方法を工夫して20倍ほど高速化できたそうです。
私の場合は、高機能化の目標がVGAサイズで10フレーム/秒として100ミリ秒。
現状 : 22秒 = 22000ミリ秒 = 100ミリ秒 x 220倍!
(220倍の高速化とは、やりがいがあって楽しそうです^^;)
画像検索の高速化にあたり、
を基本に取り組んでいます。
所要時間が100ミリ秒しかないので、Webの仕事と違った緊張感があります。
速度向上が難しい場合、フレーム率を下げる、画像処理をスレッド化する作戦、
もありますが、まずは挑戦したいと思います。
長~い、前置きこれで終わります。
お読みいただきありがとうございました。
今日のメモは画像データに関する事柄です。
画像処理では、内部的には配列やポインタ領域に対して処理を行っています。
出力時は、取り扱いがラクなのでBitmapクラスを生成して画面に表示させたり、
ファイルに保存したりしています。
今回は、Bitmapクラスを生成する時に遭遇(?)した不具合と、回避方法をメモしておきます。
Bitmapクラスの生成にあたり、以下の2通りを想定しています。
(1)新規にBitmapをサイズ指定して生成した後、LockBits後、bmpのScan0を取得し、配列からMarshal.Copy、UnlockBits
(2)配列データ(Int32型の1次元配列)をポインタ領域にMarshal.Copyし、width,height,ポインタを引数で渡しNew Bitmap()
(1)は処理に時間がかかることを除けば致命的なエラーが発生することは通常ないように思います。
(2)はBitmapインスタンス生成後も、ポインタ領域との関係が切れず、取り扱いに少々クセがあります。
以下がテストコードです。
いったん、bmpに以下のような操作を行った後に、領域を開放した場合は正常に保存できます
領域開放前にbmpを元に新しく作り直してもOKです
どうも、bmp生成時に指定したポインタ領域が参照されているようです。
この仮説を検証するため、以下の2つの実験を行いました。
両実験とも、New Bitmap()の後から、 Marshal.FreeHGlobal(argb_ptr)より前にプログラムを記述するものとします。
(1)ポインタ領域を変化させた場合にbmpが変更されるか
Marshal.WriteInt32でポインタ領域を直接操作してもよいのですが、
ここでは配列操作したあとMarshal.Copyしています
黒色に相当するカラーコードをセットしています(αは不透明)
(1)の結果
bmpは全ピクセルが黒色になっていました
(2)bmpに対して操作を行った結果が、ポインタ領域に反映されるか
Marshal.ReadInt32でもよかったのですが、ここでは配列に一気にコピーしています。
(2)の結果
ポインタ領域の内容をコピーした配列bmp_arrにはColor.Blackに相当するコードが格納されていました(&HFF000000)
結論
ポインタ領域の変化がBitmapに影響したり、Bitmapの変化がポインタ領域に影響したりしているのは間違いないようです。
New Bitmap()で引数に渡したポインタ領域は、隔離されておらず、取り扱いには十分注意が必要です。
なお、ポインタ領域を開放しない場合は、問題なくsaveできます。
ポインタ領域の受け渡しで処理を進めると処理時間はかなり短縮できますが、クラス化にあたって十分な検証が必要です。
WindowsXP Professional SP3,
Intel PentiumD 3.00Hz,4GBメモリ,160GB HDD,
Visual Studio 2005 Professional Edition(VB.NET)
今回のメモの本題に入る前に、まずは状況を説明します。
少々長いですがお付き合いください。
10月はWebカメラソフト(WebCameraSnap)
の高速化に取り組んでいます。
高速化で空いた時間を高機能化に割り当てよう、という作戦です。
最終目標は10フレーム/秒程度出せればOKかなと思っています。
実現には様々なハードルがありますが、実現できたら楽しそうです。
(高速化にあたり、私が画像処理でよく使っているクラスや手法の所要時間を調べました。
処理時間の概算に使えて便利ですので、掲載希望の方がいらっしゃれば、参考資料として結果を掲載しようと思います)
高機能化でこんなことを実現したいと考えています。
- 動きの方向の識別
- 文字・数値の識別
- 物体の識別
- 人の顔の識別
とりあえず、リストの上から実現していくのが妥当かな。
高速化の仕事の合間に「動きの方向の識別」を試してみました。
(すみません、まだ本題には入りません^^;、もう少しです)
(参考図書)
「フルスクラッチによるグラフィックスプログラミング入門」
P.313-320,オプティカルフロー
この本には基本的な考え方が書かれていて、考え方の参考になります。
処理のエッセンスをVB.NETで実装してみました。
実験には映像が必要ですが、事務所の映像には飽きてしまいました。
しばらくは、気分転換に他の映像がよいですね。
偶然、今日録画していたTV番組に、商店街を歩いている人々の映像がありました。
外には絶対に出しませんので、研究目的で使わせていただきました。
前後2枚の画像から、動きの方向を検出できるのか調べてみました。
画像は掲載できませんが、こんな条件で実験しました。
(VB.NETのコードを見たいという人がいたら後日掲載します)
- 画像サイズ:720 x 480ピクセル
- 処理ブロックサイズ:8 x 8 ピクセル
- 前後左右の検索エリア:+-16ピクセル
- ブロック毎にどの方向へ移動しているのか(動きベクトルといいます)を抽出できていました
- 所要時間:22秒(ホンマかい・・・)
- ビルド:Debug(Releaseビルドは20秒でした)
コンセプトコードは、こんなものでしょうか。
動きの方向は検出できそうです。問題は処理にかかる時間です。
「フルスクラッチによる~」では、検索方法を工夫して20倍ほど高速化できたそうです。
私の場合は、高機能化の目標がVGAサイズで10フレーム/秒として100ミリ秒。
現状 : 22秒 = 22000ミリ秒 = 100ミリ秒 x 220倍!
(220倍の高速化とは、やりがいがあって楽しそうです^^;)
画像検索の高速化にあたり、
- 調査対象を減らすこと
- 見つかったら細かく調べる
を基本に取り組んでいます。
所要時間が100ミリ秒しかないので、Webの仕事と違った緊張感があります。
速度向上が難しい場合、フレーム率を下げる、画像処理をスレッド化する作戦、
もありますが、まずは挑戦したいと思います。
長~い、前置きこれで終わります。
お読みいただきありがとうございました。
今日のメモは画像データに関する事柄です。
画像処理では、内部的には配列やポインタ領域に対して処理を行っています。
出力時は、取り扱いがラクなのでBitmapクラスを生成して画面に表示させたり、
ファイルに保存したりしています。
今回は、Bitmapクラスを生成する時に遭遇(?)した不具合と、回避方法をメモしておきます。
Bitmapクラスの生成にあたり、以下の2通りを想定しています。
(1)新規にBitmapをサイズ指定して生成した後、LockBits後、bmpのScan0を取得し、配列からMarshal.Copy、UnlockBits
(2)配列データ(Int32型の1次元配列)をポインタ領域にMarshal.Copyし、width,height,ポインタを引数で渡しNew Bitmap()
(1)は処理に時間がかかることを除けば致命的なエラーが発生することは通常ないように思います。
(2)はBitmapインスタンス生成後も、ポインタ領域との関係が切れず、取り扱いに少々クセがあります。
以下がテストコードです。
Dim w As Integer = 1024
Dim h As Integer = 768
Dim argb_step As Integer = 4
Dim size As Integer = w * h
Dim bmp_arr(size - 1) As Int32
'--------------------------------------
' 配列上に赤色のピクセルデータをセット(αは不透明)
'--------------------------------------
For j As Integer = 0 To h - 1
For k As Integer = 0 To w - 1
Dim pos As Integer = j * w + k
bmp_arr(pos) = &HFFFF0000
Next
Next
'--------------------------------------
' ピクセルデータを転送する領域を確保
'--------------------------------------
Dim argb_ptr As IntPtr = Marshal.AllocHGlobal(size * argb_step)
System.Runtime.InteropServices.Marshal.Copy(bmp_arr, 0, argb_ptr, size)
'--------------------------------------
' ポインタ領域を指定してBitmapを生成
'--------------------------------------
Dim bmp As Bitmap = New Bitmap(w, h, w * argb_step, Imaging.PixelFormat.Format32bppArgb, argb_ptr)
Dim fname1 As String = "result1.png"
Dim fname2 As String = "result2.png"
bmp.Save(fname1) ' ( OK ) 正常にファイルに保存できます
Marshal.FreeHGlobal(argb_ptr) ' Bitmapインスタンス化に指定した領域を開放する
bmp.Save(fname2) ' ( NG ) 以下のエラーが出て保存できません
'-----------------------------------------------------
' AccessViolationExceptionはハンドルされませんでした。
'
' 保護されているメモリに読み取りまたは書き込み操作を行おうとしました。
' 他のメモリが壊れていることが考えられます。
'-----------------------------------------------------
いったん、bmpに以下のような操作を行った後に、領域を開放した場合は正常に保存できます
bmp.RotateFlip(RotateFlipType.RotateNoneFlipY) ' RotateFlipType.RotateNoneFlipNoneは更新されないようでNGでした
bmp.RotateFlip(RotateFlipType.RotateNoneFlipY)
Marshal.FreeHGlobal(argb_ptr) ' Bitmapインスタンス化に指定した領域を開放する
bmp.Save(fname2) ' ( OK ) 正常にファイルに保存できます
領域開放前にbmpを元に新しく作り直してもOKです
Dim tmp As Bitmap = New Bitmap(bmp)
Marshal.FreeHGlobal(argb_ptr) ' Bitmapインスタンス化に指定した領域を開放する
tmp.Save(fname2) ' ( OK ) 正常にファイルに保存できます
どうも、bmp生成時に指定したポインタ領域が参照されているようです。
この仮説を検証するため、以下の2つの実験を行いました。
- ポインタ領域を変化させた場合にbmpが変更されるか
- bmpに対して操作を行った結果が、ポインタ領域に反映されるか
両実験とも、New Bitmap()の後から、 Marshal.FreeHGlobal(argb_ptr)より前にプログラムを記述するものとします。
(1)ポインタ領域を変化させた場合にbmpが変更されるか
Marshal.WriteInt32でポインタ領域を直接操作してもよいのですが、
ここでは配列操作したあとMarshal.Copyしています
黒色に相当するカラーコードをセットしています(αは不透明)
For j As Integer = 0 To h - 1
For k As Integer = 0 To w - 1
Dim pos As Integer = j * w + k
bmp_arr(pos) = &HFF000000
Next
Next
System.Runtime.InteropServices.Marshal.Copy(bmp_arr, 0, argb_ptr, size)
bmp.Save(fname1) ' ( OK ) 正常にファイルに保存できます
(1)の結果
bmpは全ピクセルが黒色になっていました
(2)bmpに対して操作を行った結果が、ポインタ領域に反映されるか
Marshal.ReadInt32でもよかったのですが、ここでは配列に一気にコピーしています。
Dim g As Graphics = Graphics.FromImage(bmp)
g.Clear(Color.Black)
System.Runtime.InteropServices.Marshal.Copy(argb_ptr, bmp_arr, 0, size)
Console.WriteLine("{0},{1}", bmp_arr(0), bmp_arr(size - 1))
(2)の結果
ポインタ領域の内容をコピーした配列bmp_arrにはColor.Blackに相当するコードが格納されていました(&HFF000000)
結論
ポインタ領域の変化がBitmapに影響したり、Bitmapの変化がポインタ領域に影響したりしているのは間違いないようです。
New Bitmap()で引数に渡したポインタ領域は、隔離されておらず、取り扱いには十分注意が必要です。
なお、ポインタ領域を開放しない場合は、問題なくsaveできます。
ポインタ領域の受け渡しで処理を進めると処理時間はかなり短縮できますが、クラス化にあたって十分な検証が必要です。
- Newer: v0.03 r011掲載しました
- Older: 柿がおいしい季節