3

良い一日、

Webcam クラスには 1 秒あたり約 30 フレームがあり、これらのフレームはすべてベクトル (キューなど) に保存されます。次に、3 つの非同期スレッドがキューを読み取り、ジョブを実行しようとします (これらの画像を保存するため)。キューがオーバーフローするのはなぜですか? 問題は、これらのスレッドが Web カメラよりも遅いことです。

Procedure TSaveThread.Execute;
begin
   while not terminated  do
   begin
      elElement:=NIL;

      EnterCriticalSection(CritSect);
         if iElementsLength>=0 then
         begin
            elElement:=vElements[iElementsLength];
            Dec(iElementsLength);
         end;
      LeaveCriticalSection(CritSect);

      if elElement<>NIL then
      begin
         JpegImg.Assign(elElement.bmWebcam) ;
         JpegImg.SaveToFile('Save\'+elElement.sTime+'.jpg') ;
         elElement.Free;
      end;

      Sleep(20);
   end;
end;

キューに追加された画像。

//------------------------------------------------------------------------------
Procedure TWebcam.OnSave(Sender:TObject; bmWebcam:TBitmap);
begin
   EnterCriticalSection(CritSect);
      inc(iElementsLength);
      vElements[iElementsLength]:=TElement.Create(bmWebcam);
   LeaveCriticalSection(CritSect);
end;

スレッドの作成。

for i:=0 to 2 do
    TSaveThread.Create(false);

問題は、これらのスレッドがこれらすべての画像を保存できないということです。なんで?スレッドを改善するにはどうすればよいですか?

Delphi バージョン: Delphi XE2

ウェブカメラのフレーム サイズ: 1280x760 または 960x600 ソース コード全体: http://pastebin.com/8SekN4TE

4

2 に答える 2

15

次のプログラムを書きました。

{$APPTYPE CONSOLE}

{$R *.res}

uses
  System.SysUtils, Vcl.Graphics, Vcl.Imaging.jpeg, 
  System.IOUtils, System.Diagnostics;

var
  i: Integer;
  bmp: TBitmap;
  jpeg: TJPEGImage;
  Stopwatch: TStopwatch;

begin
  bmp := TBitmap.Create;
  bmp.SetSize(1280, 760);
  jpeg := TJPEGImage.Create;
  Stopwatch := TStopwatch.StartNew;
  for i := 1 to 100 do begin
    jpeg.Assign(bmp);
    jpeg.SaveToFile('C:\desktop\temp\'+TPath.GetRandomFileName);
  end;
  Writeln(Stopwatch.ElapsedMilliseconds);
  Readln;
end.

1280x760 ピクセルのビットマップを JPEG 画像に変換し、ディスクに保存します。100回そうします。私のマシンでは、これには 9 秒かかります。これは、1 秒あたり 11 枚の画像のスループットです。JPEG への変換ステップをスキップしてビットマップを直接保存すると、毎秒 150 画像のスループットを得ることができます。明らかにJPEGへの変換がボトルネックです。

毎秒 30 枚の画像を探しています。マルチスレッドは役に立ちますが、クアッド コア マシンを使用していると思われます。ウェブカメラ用に 1 つのプロセッサ、保存用に 3 つのプロセッサ。そのため、使用可能なスレッドが 3 つしかない場合は、必要な毎秒 30 フレームのスループットに到達するのに苦労することになるでしょう。私のマシンの理論上のピークは 33 です。1 秒あたり 30 フレームに達しない場合、キューは明らかにオーバーフローします。

明らかな結論は、より高速な JPEG 変換ライブラリを見つける必要があるということです。そのようなライブラリが存在することは確かです。たとえば、私はそれlibjpegがはるかに高速であるべきだと思います。

既存のコードに関しては、明らかな欠陥がいくつかあります。

  1. Sleep通常は避けるべきです。あなたの場合、イメージをプルする最後の試みが成功した場合、スリープするのは自殺行為です。そうしないでください。実際のスレッド化されたキューを使用する必要があります。同期オブジェクトで適切なブロッキング待機を許可するもの。イベント オブジェクトとお気に入りのスレッド化されていないキューを使用して自分で作成するのは、実際には非常に簡単です。
  2. を呼び出している間、ロックを保持していますTElement.Create(bmWebcam)。それがスケーリングの妨げになります。TElement.Create(bmWebcam)ロック外のローカル変数に割り当てます。そして、ロック内の共有データに割り当てます。

したがって、まず最初に への呼び出しを削除することで、これらのアイデアを確認できますSleep。そして、次TWebcam.OnSaveのように変更します。

Procedure TWebcam.OnSave(Sender:TObject; bmWebcam:TBitmap);
var
  NewElement: TElement;
begin
  NewElement := TElement.Create(bmWebcam);
  EnterCriticalSection(CritSect);
    inc(iElementsLength);
    vElements[iElementsLength] := NewElement;
  LeaveCriticalSection(CritSect);
end;

これらの提案は少しは役に立ちますが、根本的な問題、つまり JPEG 変換に取り組む必要があると思います。

于 2012-12-12T13:59:34.280 に答える
4

MultiThreadding はメディア (ハードドライブ) を高速化しません。

実際、並列書き込みアクセスでは速度が低下する可能性があります。

最初に、メディア (ハードディスク) が 33 ミリ秒未満で画像を保存できるかどうかを測定する必要があります。これは、33.333 ミリ秒ごとに Web カメラから新しい画像が取得されるためです。

そうでない場合、これを実行することは期待できません。

(および/または)する必要があります

  • より多くのハードディスクを使用する (例: スレッドごとに 1 つ)
  • より多くのキャッシュを使用する (例: キャッシュ コントローラー)
  • より高速なハードディスク (SSD など) を使用する
  • 小さい画像を使用する (解像度を下げる)
  • いくつかの画像をドロップ

速くする必要がある場合は、時間を無駄にしないでください

if elElement<>NIL then
  begin
     JpegImg.Assign(elElement.bmWebcam) ;
     JpegImg.SaveToFile('Save\' + elElement.sTime + '.jpg' );
     elElement.Free;
  end
else
  Sleep(20);

OTL はメディアの高速化には役立ちませんが、はるかにクリーンになります :o)

だからあなたは見なければならない

于 2012-12-12T13:52:29.200 に答える