トップページ > 記事閲覧
DXLibのGrHandleをbitmapオブジェクトに変換
名前:五月花 日時: 2019/09/29 01:07

失礼いたします。 .NETでアプリケーションを作成しているのですが、DXライブラリで作成されたグラフィックハンドルをSaveDrawScreenでファイルに書き出すのではなく、 そのまま.NET上で扱えるbitmapオブジェクト等に変換(出力?)することは可能でしょうか。 現在私はOpenCVSharp(OpenCVのラッパー)の画像認識機能を利用したアプリケーションを作成しています。デスクトップ画面上の一部分を取り込み、 予め作成した画像と一致するかを判定するのですが、用途上1/60秒毎に画面を取り込む必要があります。 今はbitblt関数で取り込んでいるのですが、これはWindows10では十分な速度が出ず、Desktop Duplication APIについて調べていたときに こちらのスレッドを発見し、尋ねてみた次第です。 dxlib.xsrv.jp/cgi/patiobbs/patio.cgi?mode=view&no=3783 DXライブラリの本来の使い方から逸脱していることは承知しておりますし、またOpenCVにはビデオデバイスを取り込む機能があり 、仮想デバイスを経由してデスクトップ画面を十分な速度で取り込み、処理できることは確認していますので、要望ではなく確認と捉えていただければと思います。
メンテ

Page: 1 |

Re: DXLibのGrHandleをbitmapオブジェクトに変換 ( No.1 )
名前:管理人 日時:2019/09/29 06:26

現状ではできませんが、既存の GetDesktopScreenGraph も内部ではデスクトップ画面の メモリイメージを取得していたので、グラフィックハンドルに転送するのではなく メモリイメージをそのまま取得する関数なら簡単に追加できました // デスクトップ画面から指定領域の画像のメモリイメージの先頭アドレスとイメージの幅・高さ・ストライドを取得する // ( イメージのフォーマットはバイト順で B8G8R8X8 の 32bitカラー ) void *GetDesktopScreenGraphMemImage( int x1, int y1, int x2, int y2, int *Width, int *Height, int *Stride ) ; こちらに上記の関数を追加したバージョンをアップしましたので、よろしければダウンロードして下さい m(_ _)m https://dxlib.xsrv.jp/temp/DxLibDotNet.zip // Windows版 .NET用 C++ のDLLである C#版DXライブラリで .NET用の Bitmapオブジェクトを戻り値とすることはできませんが、 bitmap のコンストラクタには public Bitmap( int width, int height, int stride, PixelFormat format, IntPtr scan0 ) というものがあるそうなので、GetDesktopScreenGraphMemImage で取得した情報をこのコンストラクタに 渡せば簡単にデスクトップ画面の Bitmap が作ることができるのではないかと思います int Width, Height, Stride; IntPtr Image; Image = DX.GetDesktopScreenGraphMemImage( 0, 0, 640, 480, out Width, out Height, out Stride ); Bitmap DesktopImageBitmap = new Bitmap( Width, Height, Stride, PixelFormat.Format32bppRgb, Image ); C#のプログラムは殆ど書いたことが無いので間違っていたらすみません… > DXライブラリの本来の使い方から逸脱していることは承知しておりますし、またOpenCVにはビデオデバイスを取り込む機能があり > 、仮想デバイスを経由してデスクトップ画面を十分な速度で取り込み、処理できることは確認していますので、要望ではなく確認と捉えていただければと思います。 すみません、簡単に関数が追加できそうだったので追加してしまいました
メンテ
Re: DXLibのGrHandleをbitmapオブジェクトに変換 ( No.2 )
名前:五月花(解決) 日時:2019/09/29 12:42

ご返答ありがとうございます。 追加していただいた関数、またコードを用いて正常にbitmapの作成を行うことができました。 OpenCVとの干渉も特になく、作成したbitmapをOpenCVで扱うためのMat形式への変換込みで処理速度、 負荷等問題は見当たりませんでした。 仮想カメラを経由〜と書きましたが、1つのソフトで完結できるようになるので利便性が上がり、 とても助かります。ありがとうございました! (解決済みとしましたが、作成中のプログラム内で使用したところパフォーマンスが落ちた (処理1回あたり1/25~1/30秒)ので、可能性はないと思いますが万一原因がDXライブラリとOpenCVの併用に 関連するものである場合、このスレッドに追記もしくは新たに立てさせていただくかもしれません。)
メンテ
Re: DXLibのGrHandleをbitmapオブジェクトに変換 ( No.3 )
名前:kanamaru 日時:2019/10/01 09:36

僕は少しopencvを使ったことがあるのですが、Matなら、メモリイメージ?からBitmapを経由せずに直接Matに変換できそうです。 Matのメソッドのputかsetarrayという関数を使えばいいと思います。 パフォーマンスはわかりませんが、Bitmapを経由しないので、よくなる気がします。
メンテ
Re: DXLibのGrHandleをbitmapオブジェクトに変換 ( No.4 )
名前:五月花 日時:2019/10/02 14:54

kanamaruさんコメントありがとうございます。確かにbitmapを経由しないほうが余計な処理がなく良さそうです。 put=setarrayでOpenCVとOpanCVSharpで関数名が異なるだけのようでした。 GetDesktopScreenGraphMemImageでScan0(最初の位置のメモリアドレス?)が取得できるので、 同サイズのMatを作成してSetAllayで流し込めばいけるのかなと思いましたが、どのようにすればよいのかわからず… byteの1次元配列を作って、Marshal.Copy()でXRGB値の取得、ならできたのですが、 これでは型がモノクロのものになってしまう(CV_8UC1)ので、ループ処理で適切に 値を入れていかないといけないんだろうなーというところで止まってます。 (SetArrayを使ったコードはおなくなりになりました) Dim ptr As IntPtr = Image_desktop ' Scan0 Dim bytes As Integer = Math.Abs(Stride) * Height Dim rgbValues As Byte() = New Byte(bytes - 1) {} Marshal.Copy(ptr, rgbValues, 0, bytes) Dim result_mat As Mat = New Mat(Width, Height, MatType.CV_8U, rgbValues)
メンテ
Re: DXLibのGrHandleをbitmapオブジェクトに変換 ( No.5 )
名前:kanamaru 日時:2019/10/04 17:52

色々試した結果、直接Matに変換できました。ImShowで表示もできるので正常に変換できているはず。 Imageという変数は、管理人さんが提示しているプログラムのものです。 var mat = new Mat(Height,Width,MatType.CV_8UC4,Image);
メンテ
Re: DXLibのGrHandleをbitmapオブジェクトに変換 ( No.6 )
名前:五月花(解決) 日時:2019/10/09 02:15

おー、そのまま入れてやればよかったんですね…お手数おかけします。 試してて気づきましたが、私の書いたコード、幅と高さが逆でしたね… こちらでも試したところ正常に変換できているようです。 画像認識…テンプレートマッチングを使っているのですが、今は24bit(RGB)画像を テンプレートとしているので、cvtColorを用いてBGRXからBGRに変換したところ この処理に時間がかかりました(目測で40ms弱、十分な速度ではない)。 テンプレートも32bit化すれば多分問題ないと思いますが、setarrayを使う方を もう少し模索するのもいいかもしれないと思っています。
メンテ
Re: DXLibのGrHandleをbitmapオブジェクトに変換 ( No.7 )
名前:管理人(解決) 日時:2019/10/06 07:21

> 画像認識…テンプレートマッチングを使っているのですが、今は24bit(RGB)画像を > テンプレートとしているので、cvtColorを用いてRGBXからRGBに変換したところ > この処理に時間がかかりました(目測で40ms弱、十分な速度ではない)。 もう解決とされていますが、GetDesktopScreenGraphMemImage の内部で 32 と書いているところを 24 にするだけで RGB にできたので、GetDesktopScreenGraphMemImage にビット数を指定する引数を追加してみました // デスクトップ画面から指定領域の画像のメモリイメージの先頭アドレスとイメージの幅・高さ・ストライドを取得する // ( イメージのフォーマットは ColorBitDepth = 32( バイト順で B8G8R8X8 の 32bitカラー ) ColorBitDepth = 24( バイト順で B8G8R8 の 24bitカラー ) ) void *GetDesktopScreenGraphMemImage( int x1, int y1, int x2, int y2, int *Width, int *Height, int *Stride, int ColorBitDepth = 32 ) ; 追加した引数 ColorBitDepth を 24 にすると、戻り値のフォーマットが RGB になりますので、 よろしければお試しください m(_ _)m https://dxlib.xsrv.jp/temp/DxLibDotNet.zip // Windows版 .NET用
メンテ
Re: DXLibのGrHandleをbitmapオブジェクトに変換 ( No.8 )
名前:kanamaru 日時:2019/10/06 11:14

早速、試してみました。以下の通りにすれば、追加された機能を使って、Matを生成できます。Matのコンストラクタの第三引数に注意してください。 IntPtr Image; Image = DX.GetDesktopScreenGraphMemImage(0, 0, 640, 480, out Width, out Height, out Stride,24); var mat = new Mat(Height,Width,MatType.CV_8UC3,Image);//CV_8UC4ではなくCV_8UC3を使う。
メンテ
Re: DXLibのGrHandleをbitmapオブジェクトに変換 ( No.9 )
名前:五月花(解決) 日時:2019/10/08 22:34

管理人様、kanamaru様、関数への機能追加、検証ありがとうございます。 こちらでも試してみたところ、kanamaru様の記載しているコードで問題なく動作することを確認できました。 作成しているアプリケーションへの導入でパフォーマンスが落ちる原因をまだ特定できていませんが、 早く解消して使いたいです。 改めて管理人様、kanamaru様、私のわがままを聞いてくださりありがとうございました。
メンテ
Re: DXLibのGrHandleをbitmapオブジェクトに変換 ( No.10 )
名前:五月花(解決) 日時:2019/10/09 01:02

パフォーマンスが低下する大きな原因を恐らく特定できたのでご報告を。 .NETにはいくつかのタイマーが用意されており、私はDispatcherTimerを使用していたのですが、 このタイマーのTickイベント内でGetDesktopScreenGraphMemImage関数を実行した場合、 処理時間が大幅に増加することを確認いたしました。デフォルトで使用できるSystem.Windows.Forms.Timer を使用した場合、このような現象は発生しませんでした。メモリイメージを取得する処理と競合 していたのでしょうか…よくわからないです。 ともあれ、これで関数を使用することができます。せっかく関数を作っていただいたのに使えないまま… という状況を回避できてホッとしています…
メンテ
Re: DXLibのGrHandleをbitmapオブジェクトに変換 ( No.11 )
名前:五月花 日時:2019/10/15 10:26

何度もすみません… GetDesktopScreenGraphMemImage関数についてなのですが、これをマルチディスプレイに対応させることは可能でしょうか。 DXライブラリのソースコードを拝見し、デスクトップ画面をグラフィックハンドルに転送する関数GetDesktopScreenGraphでは、 デスクトップウィンドウハンドルの作成、DIBを作成してBitbltでコピー…とされているのを確認できました (DirectXとは全く関係なかったのですね。過ぎた要望でした…)。 その中のデスクトップの矩形を取得する部分で、 // デスクトップの矩形を取得 WinAPIData.Win32Func.GetWindowRectFunc( DesktopHWND, &DesktopRect ) ; RectW = DesktopRect.right - DesktopRect.left ; RectH = DesktopRect.bottom - DesktopRect.top ; とあるのですが、GetWindowRect()ではプライマリディスプレイの矩形が取得されるので、 GetSystemMetrics()を使用して2枚目以降のディスプレイを含んだ矩形を取得すれば いけないかなと… docs.microsoft.com/en-us/windows/win32/api/winuser/nf-winuser-getsystemmetrics SM_XVIRTUALSCREEN、SM_YVIRTUALSCREEN  …左端と上端の座標 SM_CXVIRTUALSCREEN、SM_CYVIRTUALSCREEN …幅と高さ
メンテ
Re: DXLibのGrHandleをbitmapオブジェクトに変換 ( No.12 )
名前:管理人 日時:2019/10/16 00:08

> (DirectXとは全く関係なかったのですね。過ぎた要望でした…)。 そういえばそうですね 元々がグラフィックハンドルにデスクトップの画像を取り込む関数だったので、グラフィックハンドルが 関係なくなると DirectX とは全く無関係になるということですね (^ ^;; ただ、別に DirectX が絡んでいないといけないというルールはありませんのでお気になさらず ( mp4 の再生機能なども DirectX とは関係ない MediaFoundation という機能を使用していますし ) > とあるのですが、GetWindowRect()ではプライマリディスプレイの矩形が取得されるので、 > GetSystemMetrics()を使用して2枚目以降のディスプレイを含んだ矩形を取得すれば > いけないかなと… ご情報ありがとうございます GetSystemMetrics でディスプレイの矩形を取得するようにしてみたところ すんなりセカンドディスプレイの画像も取得できました! こちらに GetSystemMetrics を使用するようにしたバージョンをアップしましたので、よろしければお試しください m(_ _)m https://dxlib.xsrv.jp/temp/DxLibDotNet.zip // Windows版 .NET用
メンテ
Re: DXLibのGrHandleをbitmapオブジェクトに変換 ( No.13 )
名前:五月花 日時:2019/10/16 22:01

ありがとうございます。早速確認してみたところ、こちらでも画像取得ができることを確認できました。 DXライブラリ掲示板の明確なルールはないようなので、各々の良心に委ねていると捉えています。 要望ばかりではなく相互扶助も意識していければ…と思いますが、すみません、この件に関して不具合が ありましたので報告を… GetDesktopScreenGraphMemImage にビット数を指定する引数を追加していただきましたが、これに24を 指定した場合、横幅が4の倍数でない場合にキャプチャ画像が乱れます。 www13. plala.or.jp/kymats/study/MULTIMEDIA/create_dib_24.html     ↑このスペースを消してください こちらのページによると、bitmapは「ピクセル列の一行分のバイト数が4の倍数でなければならない」 という制約があり、24bitのDIBを作成する際は値(Stride?)の補正が必要なようです。 補正については、検索してみたところ stride = 4 * ((width * bytesPerPixel + 3) / 4); というのをよく見かけました。 お手すきの際に検証していただけると助かります…よろしくおねがいします。
メンテ
Re: DXLibのGrHandleをbitmapオブジェクトに変換 ( No.14 )
名前:管理人 日時:2019/10/17 00:32

> DXライブラリ掲示板の明確なルールはないようなので、各々の良心に委ねていると捉えています。 > 要望ばかりではなく相互扶助も意識していければ…と思いますが いえ、要望ばかりでも問題ありません (・・; ユーザー同士の交流の場、というよりはバグのご報告を頂いたり機能追加の要望を頂いたりするのが主な掲示板ですので… > こちらのページによると、bitmapは「ピクセル列の一行分のバイト数が4の倍数でなければならない」 > という制約があり、24bitのDIBを作成する際は値(Stride?)の補正が必要なようです。 ご指摘ありがとうございます 最近32bitカラーを扱うことが主だったので失念していました orz 修正版をアップしましたので、よろしければお試しください m(_ _;m https://dxlib.xsrv.jp/temp/DxLibDotNet.zip // Windows版 .NET用
メンテ
Re: DXLibのGrHandleをbitmapオブジェクトに変換 ( No.15 )
名前:五月花(解決) 日時:2019/10/18 10:44

修正ありがとうございます。仕事が早い… DXライブラリは基本的に32bitで扱いますものね。 試してみたところ、横幅が4の倍数以外の場合でも正常にbitmap画像が生成されることを確認できました。 またOpenCV(Sharp)のMat生成については、stepという引数がStrideに相当するようで、以下のように 指定することでMatを生成することができました。 Image = DX.GetDesktopScreenGraphMemImage(0, 0, 100, 100, Width, Height, Stride, 24) Dim src As Mat = New Mat(Height, Width, MatType.CV_8UC3, Image, Stride) 今回は本当にありがとうございました。掲示板についてはいい感じに付き合っていこうと思います。
メンテ

Page: 1 |

題名
名前
コメント
パスワード (記事メンテ時に使用)

   クッキー保存