2

私はiOS用のopencvプロジェクトに取り組んでいます。キャプチャされて表示されたフレームを使用して開発を開始するための簡単なプロジェクトが与えられました。メモリの問題が発生し始め、元のプロジェクトのセットアップにまでさかのぼるまで、それがどのように機能するかについてあまり注意を払いませんでした. キャプチャ/表示コードを書き直す予定ですが、そもそもなぜそれが機能したのかわかりません。メソッドを呼び出す再生/一時停止ボタンがありました

- (IBAction)play_pause:(id)sender
{
  play = !play;
  while(play)
  {
    if (_videoCapture && _videoCapture->grab())
    {
        (*_videoCapture) >> _display_frame;
        //process frame
        self.imageView.image = [UIImage imageWithCVMat:_display_frame];
    }
  }
}

play は、アプリケーションが再生中か一時停止中かを示す単なるグローバル bool です。奇妙なことに、処理は無限ループ内で行われる必要があり、抜け道はありません。play がループ内で変更されることはありません。それにもかかわらず、アプリケーションが実行されているとき、再生/一時停止ボタンは応答性を維持し、再生ブールを反転して実行を一時停止することができます。それだけでなく、他のブール値 (use_greyscale など) は他のボタンで反転でき、その値はループ内で変化します。アプリケーションがフリーズし、新しいフレームを画面に描画することさえないと思っていました。アプリケーションは、その寿命のほとんどの間、その関数内に閉じ込められたままになり、描画や UIControl などの他のタスクを実行できなくなります。これが可能な唯一の方法は、IBAction 呼び出しが独自のスレッドで実行されている場合のようです。ソース コードにスレッド化の証拠が見つかりません。Apple が UI でスレッドを処理する方法を誰か説明できますか? メインの runloop スレッドは 1 つだけで、追加のスレッドは自動的に作成されないという印象を受けました。それが本当なら、この行動はどのように説明できますか?

サイドノート-

最終的にこれを調査したのは、[UIImage imageWithCVMat:_display_frame] が自動解放されたオブジェクトを返すことでした。これはすべてループ内で行われるため、実行を一時停止しないとオブジェクトを解放できず、クラッシュが発生していました。

4

2 に答える 2

3

機能する理由は、cv::VideoCapture::grab()メソッドの実装が現在の実行ループを実行して、フレームを取得するまでスレッドを一時停止するためです。

アプリケーションを起動すると、main関数は という名前の関数UIApplicationMainを実行します。この関数は を実行しCFRunLoopRunます。がCFRunLoopRunメイン スレッドで実行されると、メインの実行ループが実行されます。これは、システムから受信したすべての UI イベントを処理し、ユーザー インターフェイスを更新する実行ループです。実行ループの詳細については、Apple Threading Programming Guideを参照してください。

そのため、無限ループを実行すると、コードが実行ループに戻ることはなく、待機中のイベントを処理できません。しかし、あなたの場合、grabメソッドは有効期限の遅延で実行ループを再度実行します。したがって、実行ループは、遅延が期限切れになるまで受信イベントを処理し (コードを再度呼び出す可能性があります)、コードに戻り、実行ループを再度実行します。

ボタンをタッチして一時停止するときにコールスタックを見ると、次のように表示されます。

メイン関数 → 実行ループ → イベント処理 → あなたのコード → OpenCV → 実行ループ → イベント処理 → あなたのコード

実行ループはそれ自体の内部で実行されていますが、実行ループは再入可能であるため、これはまったく問題ありません。スクロール ビューは実際にその動作を使用します。 をスクロールするUIScrollViewと、スクロールを終了するまでいくつかのイベントを無視するために、実行ループが別のモードで再度実行されます。

しかし、OpenCV の開発者がコードを書いたときにこれを念頭に置いていたかどうかはわかりません。したがって、フレームをバックグラウンド スレッド/キューにロードする方がよいと思います。

于 2012-06-25T18:02:30.540 に答える
0

おっしゃる通り、iOS には「自動スレッド化」はありません。Grand Central Dispatch (GCD) を使用するとスレッド化がはるかに簡単になりますが、自動的には行われません。

いくつかのデバッグ コードを記述し、while ループで [NSThread isMainThread] をテストして、play_pause がメイン UI スレッドで実際に実行されているかどうかを確認できます。

于 2012-06-25T17:18:47.383 に答える