2

ビットマップを DirectShow DLL にリアルタイムで 25 フレーム/秒で送信する Delphi 6 アプリケーションがあります。DirectShow DLL も私のコードであり、DSPACK DirectShow コンポーネント スイートを使用して Delphi 6 で記述されています。特定のフラグが設定されている場合、ビットマップ内の各ピクセルを通過して画像の明るさとコントラストを変更する単純なコード ブロックがあります。それ以外の場合、ビットマップは変更されずに DirectShow DLL にプッシュされます (プッシュ ソース ビデオ フィルタ)。コードはメイン アプリケーションにありましたが、それを DirectShow DLL に移動しました。それがメインアプリケーションにあったとき、それはうまくいきました。期待どおりにビットマップの変更を確認できました。ただし、コードが DirectShow DLL に存在するため、次の問題があります。

  1. 以下のコード ブロックがアクティブな場合、DirectShow DLL は非常に遅くなります。私はクアッドコア i5 を持っていますが、とても遅いです。また、CPU 消費量に大きなスパイクが見られます。対照的に、メイン アプリケーションで実行されているまったく同じコードは、古いシングル コア P4 で正常に実行されました。その古いマシンでは CPU にかなりの負荷がかかりましたが、ビデオはスムーズで問題はありませんでした。画像のサイズはわずか 352 x 288 ピクセルです。

  2. 表示されているビットマップに期待される変更が見られません。DirectShow DLL のコードをトレースすると、各ピクセルの数値がコードによって適切に変更されていることがわかりますが、グラフ編集 ActiveMovie ウィンドウに表示される画像はまったく変更されていないように見えます。

  3. コードを非アクティブ化すると (リアルタイムで実行できます)、ActiveMovie ウィンドウにはガラスのように滑らかなビデオが表示され、CPU にほとんど触れずに完全にレンダリングされます。コードを再アクティブ化すると、ビデオが途切れ途切れになり、おそらく最初のフレームが表示されるまでに長い遅延があり、1 秒に 1 ~ 2 フレームしか表示されず、CPU がスパイクします。完全ではありませんが、予想をはるかに超えています。

範囲チェック、オーバーフロー チェックなどを含むすべてを使用して DirectShow DLL をコンパイルしようとしましたが、実行時に警告やエラーは発生しませんでした。次に、最速でコンパイルしようとしましたが、上記とまったく同じ問題が発生しました。何かが本当に間違っていて、何が原因かわかりません。ビットマップを変更する前にキャンバスをロックし、完了後にロックを解除することに注意してください。上記の「すべてオン」のコンパイル実行がなければ、FPU 例外が発生し、すべてのピクセル計算で静かに飲み込まれたように感じたと思いますが、前述したように、エラーや例外は発生していません。

更新:Roman Rのコメントの1つに埋め込まれているソリューションがはっきりと見えるように、これをここに入れています。ScanLine プロパティにアクセスする前にPixelFormatプロパティをpf24Bitに設定していなかったという問題。ローマンが示唆したように、これを行わないと、TBitmap コードでビットマップの一時コピーを作成する必要があります。問題の下にコード行を追加するとすぐに、変更が表示されないこととソフトページフォールトの両方がなくなりました。影響を受ける唯一のオブジェクトは、ビットマップの一時コピーへのポインターが含まれているため (仮定)、ScanLine プロパティにアクセスするために使用するポインターであるため、これは潜行的な問題です。これが、ビットマップの元のコピーで機能したため、後続の TextOut() 呼び出しがまだ機能していた理由であるに違いありません。

clip.PixelFormat := pf24bit; // The missing code line that fixes the problem.

私が参照しているコードブロックは次のとおりです。

function IntToByte(i: Integer): Byte;
begin
 if i > 255 then
   Result := 255
 else if i < 0 then
   Result := 0
 else
   Result := i;
end;

// ---------------------------------------------------------------

procedure brightnessTurboBoost(var clip: TBitmap; rangeExpansionPowerOf2: integer; shiftValue: Byte);
var
   p0: PByte;
   x,y: Integer;
begin
   if (rangeExpansionPowerOf2 = 0) and (shiftValue = 0) then
       exit; // These parameter settings will not change the pixel values.

   for y := 0 to clip.Height-1 do
   begin
       p0 := clip.scanline[y];

       // Can't just do the whole buffer as a big block of bytes since the
       //  individual scan lines may be padded for CPU alignment.
       for x := 0 to (clip.Width - 1) * 3 do
       begin
           if rangeExpansionPowerOf2 >= 1 then
               p0^ := IntToByte((p0^ shl rangeExpansionPowerOf2) + shiftValue)
           else
               p0^ := IntToByte(p0^ + shiftValue);

           Inc(p0);
       end;
   end;
end;
4

1 に答える 1

3

このコード スニペットについて、いくつか説明することがあります。

  1. まず、クラスScanlineのプロパティを使用しています。TBitmap私は長年 Delphi と連携していないので、これについては間違っているかもしれませんがScanline、実際には薄いアクセサーではないという印象を受けていますね。「画像のビットにアクセスしたい場合は、ポインタを返す前にまずそれを DIB に変換する必要がある」など、パフォーマンスに劇的な影響を与える可能性があるものを内部的に隠している可能性があります。そのため、非常にシンプルに見えるものは、キラーに見えるかもしれません。

  2. 内側のループ本体に「if rangeExpansionPowerOf2 >= 1 then」? あなたは本当にこれをずっと比較したくありません。2 つの別個の関数を作成するか、ゼロおよびゼロ以外の rangeExpansionPowerOf2 の 2 つのバージョンを使用せずにループ全体を複製し、これを 1 回だけ実行します。

  3. 「for ... to (clip.Width - 1) * 3 do」 Delphi が上限評価を最適化して 1 回だけにするかどうかはよくわかりません。これらの乗算をピクセルごとに 3 回実行している可能性がありますが、画像全体では 1 回しか実行できません。

  4. 最高のパフォーマンスIntToByteのために、ifs を回避し、一度に複数のバイトを処理するために、MMX に確実に実装されています。

画像は 352x288 しかないとあなたが言うように、#1 がパフォーマンスを台無しにしているのではないかと思います。

于 2012-01-06T18:37:12.540 に答える