none
カメラ画像のファイル保存に関して RRS feed

  • 質問

  • USBカメラからのデータを撮りながらそのデータを保存するプログラムを考えています。
    今考えているのは、カメラからデータを受けとったら、CFile等でファイル保存するのを
    ワーカースレッドの中で繰り返そうと思います。(毎秒60枚ほど)

    環境はWinXP、VC++2005、SDI、MFCです。DirectXは使っていません。

    もっと、効率がよく、早い方法等ございましたら、ご教授お願いいたします。
    2010年1月24日 8:56

回答

  • >ディスクへのDMAとかはできないのでしょうか。
    できません。
    Applicationから制御することではありません。

    試行錯誤になりますが、以下を検討してみてはいかがでしょうか。

      1.カメラの台数に応じて、ファイル保存Thread数を増やす。
      2.保存先のファイルは、予め1GB程度で確保しておき、そこに画像データを書き込んでいく。
        (サイズは適当ですので、要件に合わせて適宜調整してください。)
      3.余計、無駄な処理がないか、Source Codeを見直す。

    2010年1月28日 14:26
  • MFCならCFile::SetLengthを使います。
    APIならSetFilePointer(Ex) + SetEndOfFileで可能です。 

    [CFile::SetLength]
      http://msdn.microsoft.com/ja-jp/library/069dywd4(VS.80).aspx

    [SetFilePointer]
      http://msdn.microsoft.com/ja-jp/library/cc429788.aspx

    [SetFilePointerEx]
      http://msdn.microsoft.com/ja-jp/library/cc429791.aspx

    [SetEndOfFile]
      http://msdn.microsoft.com/ja-jp/library/cc429774.aspx
    2010年2月1日 22:45
  • 釈迦に説法かもしれませんが、

    ファイルのオープン/クローズと言うのは思ったよりもオーバーヘッドが大きいので、
    複数のファイルに保存しようとするとオープン/クローズの回数が増えて処理が重くなります。
    とりあえず、画像を一つのファイルに溜めておいて、撮影終了後にばらす方が早くなりそうな
    気がしますけれど、それでは駄目なんでしょうか。

    解決した時は、参考になったレスポンスの所にある[回答としてマーク]ボタンをクリックしてスレッドを締めましょう。
    2010年2月2日 2:30
  • カメラからの映像受信と、ハードディスクへの書き出しは、別スレッドにしてあるんですよね?その上で、2プロセス起動して2つのカメラから受信した場合に、ハードディスクへの書き出しが追いつかず、取りこぼしが発生しているんですよね?

    だったら、ハードディスクへの転送速度が問題なので、ファイル保存スレッドの数を増やす→1プロセスが確保できるメモリ量は決まっているので効果無し、ファイル領域を予め確保する→比較的まとまった領域が確保されるが転送速度の問題なので効果無し、だと思うのですが。

    複数のハードディスクを付けて、書き出し先を別にすることで、ヘッドの移動、シークといった動作を1つのプロセスからの要求に専念させることにより、効果を期待できます。また、RAID1や RAID5では、データを複数のディスクに分散させるので、1データあたりの書き出しに要する時間が減ることで単位時間あたりの転送速度の向上が見込め、やはり効果が見込めます。


    しかし、USB 2.0 の転送速度が60MB/sec.、シリアル ATA の転送速度が150MB/sec. なので、転送速度だけを比べたら、大丈夫そうなんだけど。640*480*24bit として 921600バイト。これが1秒間に60枚だから、55296000=55MB/sec.。2つにしても110MB/sec. なので、余裕がありそうなんだけど...


    Jitta@わんくま同盟
    2010年2月2日 12:17
  • S-ATA とか DMA66 とか、接続インターフェースの件は、今回あまり関係ないかと思います。
    HDD 自体の読み書き性能が、当時のインターフェース性能を上回るわけがありませんし、
    バーストの読み書きならともかく、細切れのアクセスでは、ファイルシステムの都合上、かなり実行速度が落ちます。

    最近の HDD なら、S-ATA2 のものでも、バースト転送 100MB/s を超えることもあるようですが、
    1MB のファイルを順次作成などだと、せいぜい 50MB/s くらいしかでません。これは実測値です。
    専用のドライブを用い、1MB 丸ごとバッファを用意しての書き込みの測定でこの結果ですから、
    他のプロセスのアクセスもある中、ビットマップを書き込むために、
    まずヘッダ書き込み、データ書き込み、などと
    小さいサイズで順次書き込みした場合、これより落ちる可能性もあります。

    親投稿を見る限り、1フレームごとにファイルとして作成しようとしているように見えます。
    ファイル作成するたびに MFT の書き換えなど余分なものが付随しますので、
    すでに提案されているように、単一ファイルに結合して書き込み、
    少しでも余計なアクセスを減らさないと間に合わないかと思います。
    また、書き込みサイズも最低 1MByte 単位にするなど、
    バースト転送が期待できるように、内部でメモリ上にバッファリングするとよいと思います。
    また、すでに提案されていますが、先にディスク領域を予約しておくと、
    そのサイズを超えるまでは、サイズ変化による MFT の書き換えの必要がなくなり、
    多少書き込み速度が上がることが期待できます。

    RAID を薦めて人もいますが、今回問題となっているのは、
    バースト転送の能力というより、アクセスタイム能力ではないかと思います。
    RAID では何台並列化しようと、バースト転送能力は上がっても、
    アクセスタイムは短くはなりません。 (ハードウェアキャッシュによる影響を除く)
    オーバヘッドが入る分、アクセスタイムに関しては、単体で使用するよりも長くなってしまいます。
    つまり、細切れの小さいデータを順次書き込みする場合、RAID は逆にパフォーマンス低下につながる場合もあります。

    細切れの小さいデータを順次書き込みする場合に、ハードウェアとして速度を稼ぎたければ、
    RAID 構成にせずに、複数台の HDD にソフトウェア的に書き込みを振り分けることです。
    例えば、2台用意し、奇数番目のファイルは 1つめの HDD に、偶数番目のファイルは 2 つめの HDD に、などです。
    S-ATA 以降または、SCSI 系なら複数の HDD に並列アクセス可能ですので、
    単純にほぼ HDD 台数分の性能アップになります。
    もちろん、書き込み側は FILE_FLAG_OVERLAPPED を用いて非同期転送するか、
    HDD 1台ごとに 1スレッド用意して処理する必要があります。

    それから、RAID 0 がストライピングで、RAID 1 がミラーリングです。説明が逆のような気がします。
    あと、SSD は書き込みはあまり速くないはずです。SSD が大きく有利になるのは、細切れの読み込みのときだけかと思います。

    2010年2月4日 15:39

すべての返信

  • >DirectXは使っていません。
    DirectXは使ってはならない、ということでしょうか。
    使用してよいならDirect Showはいかがでしょうか。

    >カメラからデータを受けとったら、CFile等でファイル保存するのを
    >ワーカースレッドの中で繰り返そうと思います。(毎秒60枚ほど)
    方針は良いと思います。
    ただ、画像サイズやカメラの性能によっては、60枚/sは厳しいかもしれません。

    画像受け取り用Threadと、その画像ファイル保存用Threadを作成し、
    連携しながら処理を行う手段を検討する必要がありそうですね。
    2010年1月24日 9:18
  • ご返信ありがとうございます。
    DirecXは経験が無く、本を読んで、概略等がわかる程度です。
    ちなみに、Direct Showで、コールバックで画像を取得できる関数はありますか。

    また、スレッド中の繰り返し処理に関して、While文を使用する予定ですが、
    以前、While文を使用すると、CPUに負荷がかかるようなことを聞いたことがあります。
    何か注意点はございますか。While文を使用しない方法がございましたら
    ご教授のほど、お願いいたします。

    2010年1月24日 15:31
  • >DirecXは経験が無く、本を読んで、概略等がわかる程度です。
    「USBカメラ Direct Show」で検索してみてください。
    多くの情報が見つかりますよ。
    まずは、そちらで理解を深めて頂いた方が良いと思います。

    Direct Showとの連携は使用する機能により、
    Window Messageによる通信か、Callbackになります。

    ただCallbackの場合、自分でそのCOM Interfaceを実装する必要があるかもしれません。

    >While文を使用しない方法
    例えば、Event Objectを利用する方法があります。
    しかし、もっと実装方法を詰めてから、判断したほうが良いです。

    [DirectShow]
      http://msdn.microsoft.com/en-us/library/dd375454(VS.85).aspx

    [Using Event Objects]
      http://msdn.microsoft.com/en-us/library/ms686915(VS.85).aspx

    [Synchronization]
      http://msdn.microsoft.com/en-us/library/ms686353(VS.85).aspx

    後、Video for Windowsでも実装可能かもしれません。
    こちらのが実装は楽です。
    検索してみてください。

    2010年1月25日 8:53
  • 毎秒60枚って、すでに“動画”ですが...録画済みファイルから静止画を切り取る方が、処理しやすいかも。なお、アニメでは、24~30枚/秒が標準です。また、カメラが60枚/秒以上の映像を送ってきてくれているか、確認が必要だと思います。USB の転送速度とかも。あるいは、前のフレームとの差分だけ送るってことをやるものとか、なかったっけ?


    あるテレビの技術説明より:

    液晶テレビは、動画を表現するために静止画を連続して表示させます。通常1秒間に60コマの静止画を表示します

    送ってくる映像(コマ数)は変わらないはずなのに、どうやってコマ数増やすんだろうと思っていたら、作るのか。


    Jitta@わんくま同盟
    2010年1月26日 13:09
  • 皆様、レスどうもありがとうございます。

    最初の助言通り、受信スレッドと保存スレッドを作成。
    受信はカメラのSDKを使用しています。

    カメラ1台だと大丈夫なのですが、
    多重に起動して2台で保存しようとするとハードへの書き込みが遅く
    すぐメモリが一杯になってしまいます。

    ディスクへのDMAとかはできないのでしょうか。
    アプリケーション側ではムリですか?

    よいアドバイスがあったらご教授お願いいたします。
    2010年1月27日 14:56
  • カメラ1台だと大丈夫なのですが、多重に起動して2台で保存しようとするとハードへの書き込みが遅くすぐメモリが一杯になってしまいます。

    ハードディスクを増設して、別々のハードディスクに保存すればよいと思います。

    RAID 5 にすれば、もう少し速くなります。

    また、先に提案しましたが、一旦動画として保存し、そこから切り出しを行えば、もう少しましになるかと思います。


    ところで、ざっと検索したところ、アイ・オー・データ、サンワサプライ、エレコム製の Web カメラは、最高30fps でした。要求されている60fps に届きませんが、大丈夫ですか?


    Jitta@わんくま同盟
    2010年1月28日 13:31
  • >ディスクへのDMAとかはできないのでしょうか。
    できません。
    Applicationから制御することではありません。

    試行錯誤になりますが、以下を検討してみてはいかがでしょうか。

      1.カメラの台数に応じて、ファイル保存Thread数を増やす。
      2.保存先のファイルは、予め1GB程度で確保しておき、そこに画像データを書き込んでいく。
        (サイズは適当ですので、要件に合わせて適宜調整してください。)
      3.余計、無駄な処理がないか、Source Codeを見直す。

    2010年1月28日 14:26
  • レスどうもありがとうございます。
    また、返信が遅くなり申し訳ございません。

    一点だけ、お教え願います。
    上記2.の予めHDDの容量を確保するには
    どうしたらよろしいのでしょうか?

    何卒、よろしくお願いいたします。

    2010年2月1日 15:38
  • MFCならCFile::SetLengthを使います。
    APIならSetFilePointer(Ex) + SetEndOfFileで可能です。 

    [CFile::SetLength]
      http://msdn.microsoft.com/ja-jp/library/069dywd4(VS.80).aspx

    [SetFilePointer]
      http://msdn.microsoft.com/ja-jp/library/cc429788.aspx

    [SetFilePointerEx]
      http://msdn.microsoft.com/ja-jp/library/cc429791.aspx

    [SetEndOfFile]
      http://msdn.microsoft.com/ja-jp/library/cc429774.aspx
    2010年2月1日 22:45
  • 釈迦に説法かもしれませんが、

    ファイルのオープン/クローズと言うのは思ったよりもオーバーヘッドが大きいので、
    複数のファイルに保存しようとするとオープン/クローズの回数が増えて処理が重くなります。
    とりあえず、画像を一つのファイルに溜めておいて、撮影終了後にばらす方が早くなりそうな
    気がしますけれど、それでは駄目なんでしょうか。

    解決した時は、参考になったレスポンスの所にある[回答としてマーク]ボタンをクリックしてスレッドを締めましょう。
    2010年2月2日 2:30
  • カメラからの映像受信と、ハードディスクへの書き出しは、別スレッドにしてあるんですよね?その上で、2プロセス起動して2つのカメラから受信した場合に、ハードディスクへの書き出しが追いつかず、取りこぼしが発生しているんですよね?

    だったら、ハードディスクへの転送速度が問題なので、ファイル保存スレッドの数を増やす→1プロセスが確保できるメモリ量は決まっているので効果無し、ファイル領域を予め確保する→比較的まとまった領域が確保されるが転送速度の問題なので効果無し、だと思うのですが。

    複数のハードディスクを付けて、書き出し先を別にすることで、ヘッドの移動、シークといった動作を1つのプロセスからの要求に専念させることにより、効果を期待できます。また、RAID1や RAID5では、データを複数のディスクに分散させるので、1データあたりの書き出しに要する時間が減ることで単位時間あたりの転送速度の向上が見込め、やはり効果が見込めます。


    しかし、USB 2.0 の転送速度が60MB/sec.、シリアル ATA の転送速度が150MB/sec. なので、転送速度だけを比べたら、大丈夫そうなんだけど。640*480*24bit として 921600バイト。これが1秒間に60枚だから、55296000=55MB/sec.。2つにしても110MB/sec. なので、余裕がありそうなんだけど...


    Jitta@わんくま同盟
    2010年2月2日 12:17
  • 皆様、ご教授どうもありがとうございました。

    jitta 様
    質問内容はお書きになったとおりです。
    数字上では間に合わないほうがおかしいですね。
    (たぶん間に合うはずですね)
    CFileの作成方法がおかしいのでしょうか。

    2010年2月2日 15:26
  • インターフェイスの規格的には間に合ってもHDD自体の能力がどうかと言うのもあるでしょうし、
    HDDディスク内のファイルの配置状態とかも影響するかもしれないと思います。
    かなり厳しい条件だと思いますから良い条件が揃えばうまく行くけれど、
    条件が揃わないと間に合わなくなるとかも有りそうです。

    通常のそこまでシビアでない処理であれば、問題が起きないような状況でも
    要求が厳しくなるとうまく処理しきれ無いという事も考えられると思いますから
    実際にトータルのスペックとして間に合うのかと言う部分も考える必要があると
    思います。あと、Windowsだと他にも色々動いているので必ずしもそのアプリだけが
    原因とは限らないのではないでしょうか。
    特にHDDに関しては全てのプログラムで共通に使うデバイスになりますし。
    HDDに関しては他のアプリが使用している時なんかは待たされると思ったんですが、違いましたっけ。

    ハード的に性能の良いHDDを増設してそれ専用に使うとかすると少しは改善できるかもしれません。


    解決した時は、参考になったレスポンスの所にある[回答としてマーク]ボタンをクリックしてスレッドを締めましょう。
    2010年2月3日 0:27
  • ベンチマークソフトなどでHDDの読み書き速度を計測できるものがあるので
    一度試してみてはどうでしょうか?

    >CFileの作成方法がおかしいのでしょうか。
    現在、取り扱うサイズや、現在のソースなどの情報がないので
    誰も、なんともいえないと思います

    2010年2月3日 3:11
  • PATIO さんの仰る通りで、理論値はあまり当てにはなりません。
    参考程度に留めておいたほうが良いです。

    具体的にHardwearがどの程度の実力を持っているか調べて見てはいかがでしょう。
    何がボトルネックか分からないと、効果的な対策も見出しづらいですよ。



    WindowsはReal Time OSではないですし、
    特定のApplicationだけにHDD Resourceを占有させるような処理を望むのは困難です。

    例えば、Anti VirusのReal Time Scanが動作していると、
    どうしてもファイルの読み書きにOverheadが生じてしまいます。

    対策を検討するにあたり、
    Hardwearからアプローチするにせよ、
    Softwearからアプローチするにせよ、
    どの処理に時間がかかっているかGetTickCountや
    Performance Counterで特定してみてください。

    そしてその後、まずはどちらから重点的にアプローチするのか、
    方針を決めて見てはいかがでしょうか。

    もしくは、差し支えのない範囲で現在のLogicを記述して頂ければ、
    また違った回答が得られるかもしれません。

    Hardwearに頼るなら、速さではSSD RAID 0がお勧めです。
    Costはかかりますけどね。
    2010年2月3日 12:45
  • 鵜呑みにされると困ります。AFTERNOON TEAさんからは、なんの「条件」も提示されていません。上の方で私は、「主要メーカーから発売されている USB カメラは 30fps だけど、いいの?」と書きました。これはつまり、60fps で記録すると、同じ画像が2枚出力されるということです。それで良いの?という問いだったのですが、理解していただけたのでしょうか。

    ハードディスクに関しては、シリアル ATA、すなわち、今一番速い規格です。DMA 66 のハードディスクなら、66MB/秒なので、1台のカメラがやっとになります。DMA 100 なら、1台なら余裕だけれど2台は無理、になります。

    さらに、画像にしても 640*480 と、小さめのサイズで示しましたが、大きなサイズを使っていれば値が変わってきます。また、24bit というのは色数ですが、これがグレースケールなら、必要なバイト数は減るかもしれません(階調による)。

    また、PATIOさんが書かれているように、私が書いたのはインターフェイスの規格なので、ドライブ自体の能力は、また変わってきます。瞬間的にはインターフェイス規格に間に合う書き込みが出来ても、それはドライブに用意されたバッファに書き込んでいるだけで、バッファがいっぱいになれば止まってしまうことも考えられます。


    この様に、今回の問題に関係する要素はたくさんあり、それらによって対応方法も変わります。条件を明らかにしなければ、確かな情報は提供できません。先に「余裕がありそうなんだけど」と書いたのは、PC の構成を見直して欲しいから、でした。なので、余裕があるように見える値を選んでいます。それを鵜呑みにするか、それとも自分の環境を調べるか、どっちだろう?と思ったのですが。。。


    余談:RAID 0 はミラーリング、2つのディスクに同じ情報を書き込むので、転送速度に影響はありません。RAID 1 は、データを2つに分けて、2つのディスクに書き込みます。RAID 5 は、4つにわけます(と、パリティ用に1つ)。1つのハードディスクが書き込むデータ量が少なくなります。この様にディスクの使い方が違うので、RAID 1+0、RAID 5+0 という使い方をすることもあります。

    RAID - Wikipedia 書く前に開いたのに、いったい何を読んでいたんだ?orz

    失礼いたしました。また、お騒がせして申し訳ありません。


    Jitta@わんくま同盟
    • 編集済み Jitta 2010年2月5日 13:26
    2010年2月4日 13:33
  • S-ATA とか DMA66 とか、接続インターフェースの件は、今回あまり関係ないかと思います。
    HDD 自体の読み書き性能が、当時のインターフェース性能を上回るわけがありませんし、
    バーストの読み書きならともかく、細切れのアクセスでは、ファイルシステムの都合上、かなり実行速度が落ちます。

    最近の HDD なら、S-ATA2 のものでも、バースト転送 100MB/s を超えることもあるようですが、
    1MB のファイルを順次作成などだと、せいぜい 50MB/s くらいしかでません。これは実測値です。
    専用のドライブを用い、1MB 丸ごとバッファを用意しての書き込みの測定でこの結果ですから、
    他のプロセスのアクセスもある中、ビットマップを書き込むために、
    まずヘッダ書き込み、データ書き込み、などと
    小さいサイズで順次書き込みした場合、これより落ちる可能性もあります。

    親投稿を見る限り、1フレームごとにファイルとして作成しようとしているように見えます。
    ファイル作成するたびに MFT の書き換えなど余分なものが付随しますので、
    すでに提案されているように、単一ファイルに結合して書き込み、
    少しでも余計なアクセスを減らさないと間に合わないかと思います。
    また、書き込みサイズも最低 1MByte 単位にするなど、
    バースト転送が期待できるように、内部でメモリ上にバッファリングするとよいと思います。
    また、すでに提案されていますが、先にディスク領域を予約しておくと、
    そのサイズを超えるまでは、サイズ変化による MFT の書き換えの必要がなくなり、
    多少書き込み速度が上がることが期待できます。

    RAID を薦めて人もいますが、今回問題となっているのは、
    バースト転送の能力というより、アクセスタイム能力ではないかと思います。
    RAID では何台並列化しようと、バースト転送能力は上がっても、
    アクセスタイムは短くはなりません。 (ハードウェアキャッシュによる影響を除く)
    オーバヘッドが入る分、アクセスタイムに関しては、単体で使用するよりも長くなってしまいます。
    つまり、細切れの小さいデータを順次書き込みする場合、RAID は逆にパフォーマンス低下につながる場合もあります。

    細切れの小さいデータを順次書き込みする場合に、ハードウェアとして速度を稼ぎたければ、
    RAID 構成にせずに、複数台の HDD にソフトウェア的に書き込みを振り分けることです。
    例えば、2台用意し、奇数番目のファイルは 1つめの HDD に、偶数番目のファイルは 2 つめの HDD に、などです。
    S-ATA 以降または、SCSI 系なら複数の HDD に並列アクセス可能ですので、
    単純にほぼ HDD 台数分の性能アップになります。
    もちろん、書き込み側は FILE_FLAG_OVERLAPPED を用いて非同期転送するか、
    HDD 1台ごとに 1スレッド用意して処理する必要があります。

    それから、RAID 0 がストライピングで、RAID 1 がミラーリングです。説明が逆のような気がします。
    あと、SSD は書き込みはあまり速くないはずです。SSD が大きく有利になるのは、細切れの読み込みのときだけかと思います。

    2010年2月4日 15:39