- ベストアンサー
カメラのフレームレート設定方法と違いについて
- カメラのフレームレートを設定する方法には、AvgTimePerFrame、GetOutputFPS、SetOutputFPSの3つの方法があります。
- AvgTimePerFrameはカメラのフレームレートを100nS単位で通知してくれる方法です。
- GetOutputFPSはカメラのフレームレートを1秒間のフレーム数で通知してくれる方法であり、SetOutputFPSはカメラのフレームレートを1秒間のフレーム数で設定する方法です。
- みんなの回答 (9)
- 専門家の回答
質問者が選んだベストアンサー
それは想像以上の現場でした。 やはり、普通だったら AvgTimePerFrameから IAMStreamConfig::SetFormat で出来るであろう、というところというわけで >現在、ワークの隙間を検査する為にUSBカメラ14台を接続し隙間44箇所の検査をする装置を検討していますが、タクトが50秒、キャプチャーを工夫して25秒まで持って行きましたけど、さすが今回はカメラ指定させてもらいました。 すべて、ロジクールのC905Mに、ということでしょうか? この場合は 同様にロジクールのC905Mを複数台持っていて、且つそれにもC++にもDirectShowにも十分詳しい人が、一部始終が同じコードで同様の結果になることを確認した後で、コードを細かくチェックし原因を究明する、というのが最も確実な方法と思います。 (現状では私に言えることはこれぐらいです。) もし全てのコードの提示が不可であれば、まず前提として ・USBカメラ14台の列挙は確実にできているか ・フレームレート以外についても変更はうまくいかないか このあたりの情報もあるといいと思います。
その他の回答 (8)
- LongSecret
- ベストアンサー率68% (22/32)
なるほど、そういう状況でしたか。 あまりに長いと把握するのも大変なのであれですが (Builderの独自拡張等を除く)C++のクラスの仕様への造詣という事でいえば、私は結構なレベルだと思うので、もしこれがコード上のバグ、という事であるならば コードをC++の便利仕様を適宜利用して整形し スパゲティコードを緩和してバグを発見しやすくする といったことは、多少は出来るかもしれません。 (下の質問はどちらかというとそういう意図が強いです) 複数台USBでつなぐとうまく機能しないカメラも存在し得る…? という情報も(真偽のほどは全く分かりませんが) http://hpcgi1.nifty.com/MADIA/Vcbbs/wwwlng.cgi?print+200409/04090030.txt そこまでのことはさすがになかった、としても、ドライバがらみの特有の問題である可能性も、0ではないのかもしれません。日本語版MSDNフォーラムにもこのような質問ページがありました。 http://social.msdn.microsoft.com/Forums/ja-JP/vcgeneralja/thread/9b5d71b4-815c-45fc-860d-e8ec64856fa2/
お礼
画像が黒と言うのは体験しています。 リトライをプログラムして回避しました。 後、現場で調子が悪いので見に行ったら、自動調光しか選択出来ないカメラを使用していて、ワークが入っていない時は明るいので、絞りが利いてしまい、ワークが入ると暗く成って、暫くすると調光されて明るくなる。 プログラムの方はそんなカメラとは知らないから、明るく成る前に撮影してしまい、画面が暗くてNG判断です。 これもリトライを付けて回避しました。 カメラは何を付けても良いとして、ハードは現場任せ、ソフトのみの提供の為に現場がどんなハードを使っているか分からず大変です。 因みに、その現場が使っていたカメラの名前がTAKO、笑っちゃいました。 現在、ワークの隙間を検査する為にUSBカメラ14台を接続し隙間44箇所の検査をする装置を検討していますが、タクトが50秒、キャプチャーを工夫して25秒まで持って行きましたけど、さすが今回はカメラ指定させてもらいました。 工業用カメラを使うのは予算的に許されません。隙間検査はキーエンスの検査ヘッドは1台150万円。 それを44箇所USBカメラで40万位であげて居るのですから性能は専門機と比べないで欲しい。 でも、現場は当然精度を言って来ますよね。 又、愚痴です。
- LongSecret
- ベストアンサー率68% (22/32)
ってことは、変更できそうですねぇ(苦笑) FRedFPSとかに至る前にも ICaptureGraphBuilder2 IGraphBuilder ICreateDevEnum IEnumMoniker IMoniker IBaseFilter などのポインタを使ったりして 色々なことを下準備としてやってると思うのですが その辺を見せていただくことは可能でしょうか?
お礼
色々ありがとうございました。 本日も色々試行錯誤しておりました。 そして、ついに禁断の(笑)USAの質問サイトを除いて見ると、私の質問と同じ質問がありました。 複数カメラで帯域が足りないのでフレームレートを下げたいと。 そしてその回答はLongSecret様のものと同じでした。 と言う事はAvgTimePerFrameに値を書き込むのが正解。 ではなぜ再度フレームレートを取得した時に333333に戻ってしまうのか。 そこで何処で333333に戻ってしまうか調べて見た所、 pStreamConfig->Release(); の様でした。 今はフレームレート読み込みと書き出し関数でpStreamConfig->の設定と解除を行っていますが、別の所で設定、プログラム終了時に解除をやればひょっとして動作するかも知れません。 もう少し試行錯誤続けてみます。
- LongSecret
- ベストアンサー率68% (22/32)
うううん C++ Builderの__propertyの仕様を把握してないのと 検証プログラム の各独自変数の型が分からないのですが それらが私の予想通りであるなら 確かに、アプローチ方法が違うのかもしれません。 Webカメラを借りられたので試してみたのですが pStreamConfig->GetStreamCaps(Index,&pmt,(BYTE*)&scc); 直後の scc.MinFrameInterval と scc.MaxFrameInterval の数値はどうなっているでしょうか? こちらではどちらも333333だったので変化させられない、という結果でしたが TinyPineさんの方でももし同じ結果が出るのであれば もしかすると、IAMStreamConfig::SetFormat で操作できる対象について、これもまた前提が間違っているのかもしれません。
補足
scc.MinFrameIntervalは333333 scc.MaxFrameIntervalは666666 と成っています。
- LongSecret
- ベストアンサー率68% (22/32)
>pVideoHeaderが無いとのエラーメッセージが出ましたので2行目を追加してやってみましたが、結果は次の読み込みで333333が返って来てしまいました。 333333が返ってくるというのはSetSizeIndexをやることによって C++Builder専用の予約語「__property」がついた __property int prSizeIndex = {read=FSizeIndex, write=SetSizeIndex}; によって、別途メンバ int FSizeIndex; //おそらくこういうことかな? が333333になる、という意味でしょうか? 下のSetSizeIndex関数の途中まででは FSizeIndexというのが出てきていないので その辺がどんな感じになっているかを含め、今一度SetSizeIndex関数の全貌が知りたいです。 そうではないのであれば、どの時点で何をチェックしたら333333になっているという意味なのかも正確にコードとして教えていただけますか?
補足
AvgTimePerFrameはフレームレートを100nS単位で返します。 即ち333333は約30FPSと言う事に成り、現在接続されているカメラの解像度が640x480モードに成って居て、そのフレームレートが30FPSですので正しい事になります。 但し先のプログラムでAvgTimePerFrameに対して666666、実際のプログラムでは近い値を取ると書かれて居たので、333333に成っては困るので少し大きい777777を設定しました。 その結果、再度AvgTimePerFrameを読み込んだ時、333333に成ってしまったと言う事です。 先程、会社で使用しているプログラムと同じ様にRedFPSとWitFPSを作りましたので、全てのプログラムを載せます。 //*** FPS読み出し ********************************************** int __fastcall TWebCam::FRedFPS( int Index ) { int iFrmSpd; //分解能コントロール IAMStreamConfig *pStreamConfig; //キャプチャピンに対してストリームコングィグをセット FCapture->FindInterface(&PIN_CATEGORY_CAPTURE, //キャプチャピン 0,//すべてのメディアタイプ FVideoCap, IID_IAMStreamConfig, (void **)&pStreamConfig ); AM_MEDIA_TYPE *pmt; VIDEO_STREAM_CONFIG_CAPS scc; //フォーマットリストの取得 HRESULT hr; // フォーマット機能の取得 機能番号はIndexに設定 hr=pStreamConfig->GetStreamCaps(Index,&pmt,(BYTE*)&scc); if(hr!=S_OK){ ShowMessage("Format change error(1)."); return 0; } // フォーマットの設定 hr = pStreamConfig->SetFormat(pmt); if(hr!=S_OK){ ShowMessage("Format change error(2)."); return 0; } VIDEOINFOHEADER *pVideoHeader = (VIDEOINFOHEADER*)pmt->pbFormat; // ビットマップ情報を BITMAPINFO 構造体にコピーする // これはテスト用、今の所、選択されたカメラ、解像度番号のタイミングが入手出来ている様だ。 // 一方GetOutputFPSはどの解像度でも15が返ってくる。アクセス箇所位置が違う? iFrmSpd = pVideoHeader->AvgTimePerFrame; // フレームタイミング入手 // ここからGetOutputFPS用のプログラム /*IAMTimelineObj* pVideoGroupObj; IAMTimeline* pTimeLine; CoCreateInstance(CLSID_AMTimeline, NULL, CLSCTX_INPROC_SERVER, IID_IAMTimeline, (void**)&pTimeLine); pTimeLine->CreateEmptyNode(&pVideoGroupObj, TIMELINE_MAJOR_TYPE_GROUP); IAMTimelineGroup* pVideoGroup; pVideoGroupObj->QueryInterface(IID_IAMTimelineGroup, (void**)&pVideoGroup); double dTmp; pVideoGroup->GetOutputFPS(&dTmp); // 読み出しフレーム/秒 */ return( iFrmSpd) ; } //*** FPS書き出し ********************************************** void __fastcall TWebCam::FWteFPS( int Index, int iPar ) { int iFrmSpd; //分解能コントロール IAMStreamConfig *pStreamConfig; //キャプチャピンに対してストリームコングィグをセット FCapture->FindInterface(&PIN_CATEGORY_CAPTURE, //キャプチャピン 0,//すべてのメディアタイプ FVideoCap, IID_IAMStreamConfig, (void **)&pStreamConfig ); AM_MEDIA_TYPE *pmt; VIDEO_STREAM_CONFIG_CAPS scc; //フォーマットリストの取得 HRESULT hr; // フォーマット機能の取得 機能番号はIndexに設定 /*hr=pStreamConfig->GetStreamCaps(Index,&pmt,(BYTE*)&scc); if(hr!=S_OK){ ShowMessage("Format change error(1)."); return; } // フォーマットの設定 hr = pStreamConfig->SetFormat(pmt); if(hr!=S_OK){ ShowMessage("Format change error(2)."); return; } VIDEOINFOHEADER *pVideoHeader = (VIDEOINFOHEADER*)pmt->pbFormat; // ビットマップ情報を BITMAPINFO 構造体にコピーする // これはテスト用、今の所、選択されたカメラ、解像度番号のタイミングが入手出来ている様だ。 // 一方GetOutputFPSはどの解像度でも15が返ってくる。アクセス箇所位置が違う? ((VIDEOINFOHEADER*)pmt->pbFormat)->AvgTimePerFrame = iPar; // フレームタイミング入手 // AvgTimePerFrameはここまで */ pStreamConfig->GetStreamCaps(Index,&pmt,(BYTE*)&scc); VIDEOINFOHEADER *pVideoHeader = (VIDEOINFOHEADER*)pmt->pbFormat; ((VIDEOINFOHEADER*)pmt->pbFormat)->AvgTimePerFrame = 777777; pStreamConfig->SetFormat(pmt); iFrmSpd = pVideoHeader->AvgTimePerFrame; return; } プロパティー void __fastcall SetSizeIndex( int Index ); // 選択サイズ void __fastcall FWteFPS( int iPar, int Index ); // 選択サイズ int __fastcall FRedFPS( int iPar ); // 選択サイズ __property int prSizeIndex = {read=FSizeIndex, write=SetSizeIndex}; __property int prCamFPS[int iPar] = {read=FRedFPS, write=FWteFPS}; 検証プログラム FCams->prCam[iCam]->prSizeIndex = iRes[iCam]; // 解像度設定、Grabbバッファー取得 iFps = FCams->prCam[iCam]->prCamFPS[iRes[iCam]]; // 解像度設定、Grabbバッファー取得 FCams->prCam[iCam]->prCamFPS[iRes[iCam]] = iFps * 3; iFps2 = FCams->prCam[iCam]->prCamFPS[iRes[iCam]]; sWrk.printf( "First %d Second %d", iFps, iFps2 ); ShowMessage( sWrk ); 宜しくお願い致します。 文字数オーバーの為原型のSetSizeIndexは載せられませんでした。
- LongSecret
- ベストアンサー率68% (22/32)
なるほど、分かりました。 これですと 前回質問の3の ーーーーーーーーーー hr = pStreamConfig->SetFormat(pmt); するまえに ((VIDEOINFOHEADER*)pmt->pbFormat)->AvgTimePerFrame = 数値; 的なことをするとどうなるのでしょうか? ーーーーーーーーーー のうち、前半部分を満たしていません。 これにからんでるとこだけ抜粋すると pStreamConfig->GetStreamCaps(Index,&pmt,(BYTE*)&scc); pStreamConfig->SetFormat(pmt); int iFrmSpd = pVideoHeader->AvgTimePerFrame; ((VIDEOINFOHEADER*)pmt->pbFormat)->AvgTimePerFrame = 666666; iFrmSpd = pVideoHeader->AvgTimePerFrame; この順番になってしまっています。 私の提案は pStreamConfig->GetStreamCaps(Index,&pmt,(BYTE*)&scc); ((VIDEOINFOHEADER*)pmt->pbFormat)->AvgTimePerFrame = 666666; pStreamConfig->SetFormat(pmt); int iFrmSpd = pVideoHeader->AvgTimePerFrame; この順番、ということです。 どういう事かと言いますと SetFormatの後でメモリ上でいくら数値をいじくりまわしても それがデバイス(カメラ)に伝わらないとダメという事です。 Debug::f(int); でintの数値を表示するとしたら 例えば int 能力 = 100; void func(){ int a = 能力; Debug::f( a ); //もちろん、100と表示される。 a = 30; int b = a; Debug::f( b ); //もちろん、30と表示されるが、「能力」が書き変わったわけではない。 } というのとまったく同じことになっているわけです。 デバイスドライバって言うのは基本的に デバイス(この場合カメラ) と、PCのメモリやCPUは離れた場所にあるのだから 通信には多かれ少なかれ時間がかかる なので、ある程度まとまった情報を 特定の関数からのみやりとりすることで そのオーバーヘッドを避ける という風に作られてるのが普通です。 そう考えれば、内部的な動作がだいたい予想できるのではないでしょうか? なお IAMStreamConfig::GetStreamCapsのMSDNの解説にもありますが pStreamConfig->GetStreamCaps(Index,&pmt,(BYTE*)&scc); として得たpmtは、情報を得るために内部的に領域が確保されてるので 使い終わったら DeleteMediaType(pmt); として解放してください。
補足
再度のご指摘ありがとうございます。 pVideoHeaderが無いとのエラーメッセージが出ましたので2行目を追加してやってみましたが、結果は次の読み込みで333333が返って来てしまいました。 pStreamConfig->GetStreamCaps(Index,&pmt,(BYTE*)&scc); VIDEOINFOHEADER *pVideoHeader = (VIDEOINFOHEADER*)pmt->pbFormat; ((VIDEOINFOHEADER*)pmt->pbFormat)->AvgTimePerFrame = 666666; pStreamConfig->SetFormat(pmt); iFrmSpd = pVideoHeader->AvgTimePerFrame;
- LongSecret
- ベストアンサー率68% (22/32)
むむ? >この関数を2回読んで見ました。 「この関数」と、いいますのは 前回質問の int __fastcall TWebCam::RedFPS( int Index ) のことでしょうか? (これをIndexを全く同じのを指定して2回、ということでしょうか?) 状況が正確に分からないと判断が難しいので、ここはひとつ IAMStreamConfig::GetStreamCaps関数を呼び出したとこから 前回質問のコードにおける // ここからGetOutputFPS用のプログラム IAMTimelineObj* pVideoGroupObj; IAMTimeline* pTimeLine; までの間の、現在のコードを正確に教えてください。
補足
http://d.hatena.ne.jp/xabre/20070909/1189347523 の記述もあるので出来るのかも知れませんね。よくわかりません。 テストしたプログラムはこれです。 質問に載せたのは会社で使っているプログラムで、ここでは家にあった会社で使ったプログラムの原型を改造したので、関数名は異なります。 //*** 選択サイズ ********************************************** void __fastcall TWebCam::SetSizeIndex( int Index ) { //分解能コントロール IAMStreamConfig *pStreamConfig; //キャプチャピンに対してストリームコングィグをセット FCapture->FindInterface(&PIN_CATEGORY_CAPTURE, //キャプチャピン 0,//すべてのメディアタイプ FVideoCap, IID_IAMStreamConfig, (void **)&pStreamConfig ); AM_MEDIA_TYPE *pmt; VIDEO_STREAM_CONFIG_CAPS scc; //フォーマットリストの取得 HRESULT hr; // フォーマット機能の取得 機能番号はIndexに設定 hr=pStreamConfig->GetStreamCaps(Index,&pmt,(BYTE*)&scc); if(hr!=S_OK){ ShowMessage("Format change error(1)."); return; } // フォーマットの設定 hr = pStreamConfig->SetFormat(pmt); if(hr!=S_OK){ ShowMessage("Format change error(2)."); return; } VIDEOINFOHEADER *pVideoHeader = (VIDEOINFOHEADER*)pmt->pbFormat; // ビットマップ情報を BITMAPINFO 構造体にコピーする ZeroMemory( &FBitmapInfo, sizeof(BITMAPINFO) ); CopyMemory( &(FBitmapInfo.bmiHeader), &(pVideoHeader->bmiHeader), sizeof(BITMAPINFOHEADER)); // 画像バッファの更新 if( FImageSize!=(long)FBitmapInfo.bmiHeader.biSizeImage ) { // FBitmapInfo.bmiHeader.biSizeImageに正しいバッファーサイズが入って来ないカメラがあるので算出 //FImageSize = FBitmapInfo.bmiHeader.biSizeImage; FImageSize = FBitmapInfo.bmiHeader.biWidth * FBitmapInfo.bmiHeader.biHeight * 3; if( FImage ) { // delete[] FImage; VirtualUnlock( FImage, FImageSize ); // メモリの開放 VirtualFree( FImage, 0 , MEM_RELEASE ); FImage = NULL; } // FImage = new BYTE[FImageSize]; FImage = (PBYTE)VirtualAlloc( NULL, FImageSize, MEM_COMMIT, PAGE_READWRITE ); if( FImage ) { if( !VirtualLock( FImage, FImageSize) ) { // メモリのロック VirtualFree( FImage, 0 , MEM_RELEASE ); FImage = NULL; throw Exception("Faild to get image memory from the system.(03)"); } } else { throw Exception("Faild to get image memory from the system.(04)"); } } // AvgTimePerFrame の読み出し、書き込み int iFrmSpd = pVideoHeader->AvgTimePerFrame; // フレームタイミング入手 ((VIDEOINFOHEADER*)pmt->pbFormat)->AvgTimePerFrame = 666666; iFrmSpd = pVideoHeader->AvgTimePerFrame; // フレームタイミング入手 // AvgTimePerFrameはここまで // ここからGetOutputFPS用のプログラム IAMTimelineObj* pVideoGroupObj; IAMTimeline* pTimeLine; 呼び出し側は if( bDbg == false ) { if( iRes[iCam] >= 0 ) { FCams->prCam[iCam]->prSizeIndex = iRes[iCam]; // 解像度設定、Grabbバッファー取得 FCams->prCam[iCam]->prSizeIndex = iRes[iCam]; // 解像度設定、Grabbバッファー取得 sSetCam( iCam ); // カメラパラメーターの設定 } } プロパティーの設定として void __fastcall SetSizeIndex( int Index ); // 選択サイズ // 選択サイズ __property int prSizeIndex = {read=FSizeIndex, write=SetSizeIndex};
- LongSecret
- ベストアンサー率68% (22/32)
ご提示いただいたMSDNのページは Visual Basic オブジェクトである IBasicVideo2.AvgTimePerFrame についてのことで、メンバ名は同じですが VIDEOINFOHEADER 構造体とは事情が異なると考えれます。 ビデオ出力フォーマットの設定 http://msdn.microsoft.com/ja-jp/library/cc353344.aspx の下の方に 特定のフレーム レートを要求するには、メディア タイプにある構造体 VIDEOINFOHEADER か VIDEOINFOHEADER2 の AvgTimePerFrame の値を変更する。デバイスは最小値と最大値の間で可能なすべての値はサポートしていないことがあるため、ドライバは使用可能な最も近い値を使う。ドライバが実際に使った値を調べるには、SetFormat を呼び出した後に IAMStreamConfig::GetFormat を呼び出す。 といった記述があるので、変更してSetFormatを呼び出すことは、自然な行動の一つなのではないかな、と思われます。
補足
今 下記の様にコーディングしてこの関数を2回読んで見ました。 1回目のiFrmSpdには333333が返って来て、2000000を設定した後のiFreSpfには2000000が返って来るのですが、再度この関数を呼び出した最初のiFrmSpdではやはり333333が返って来てしまいました。 int iFrmSpd = pVideoHeader->AvgTimePerFrame; // フレームタイミング入手 ((VIDEOINFOHEADER*)pmt->pbFormat)->AvgTimePerFrame = 2000000; iFrmSpd = pVideoHeader->AvgTimePerFrame; // フレームタイミング入手
- LongSecret
- ベストアンサー率68% (22/32)
>GetOutputFPSはカメラのフレームレートを1秒間のフレーム数で通知してくれる、 >SetOutputFPSはカメラのフレームレートを1秒間のフレーム数で設定する MSDN解説を読む限り、この二つはカメラサイドではなく、タイムラインに対して効果があります。 とりあえず、確認だけ 前回質問のNo.3の内容は試してみたが、変化がない、という事でしょうか? IAMStreamConfig::GetStreamCapsについて http://msdn.microsoft.com/ja-jp/library/cc355061.aspx IAMStreamConfig::SetFormatについて http://msdn.microsoft.com/ja-jp/library/cc355073.aspx AM_MEDIA_TYPE 構造体について http://msdn.microsoft.com/ja-jp/library/cc352096.aspx
補足
再度のご回答ありがとうございます。 前回の回答3 >((VIDEOINFOHEADER*)pmt->pbFormat)->AvgTimePerFrame = 数値; >的なことをするとどうなるのでしょうか? ですが、以下のところにこのプロパティーは読み取り専用との記述があり、その為にSetOutputFPSの実行を試みています。 ただ、私の知識からすると、このSetOutputFPS自体、どの様に使うものか理解して居りませんので、機能が少しわかれば解決策も生まれるのでは無いかと思い 新たな質問を立ち上げさせて頂きました。 http://msdn.microsoft.com/ja-jp/library/cc352251.aspx
お礼
残念ながら考えて居た事は動作しませんでした。 まぁ、FPSが設定されても、カメラのドライバーがそれを見ているとの確認は出来ないので、今日、FPSの設定は失敗との報告書を書きました。 色々ありがとうございました。