16

ウェブカメラから画像をキャプチャし、重い処理を行ってから結果を表示します。フレームレートを高く保つために、異なるフレームの処理を並行して実行したいと考えています。

したがって、画像をキャプチャして「inQueue」に追加する「Producer」があります。また、「outQueue」から画像を取得して表示します。

public class Producer
{
    Capture capture;
    Queue<Image<Bgr, Byte>> inQueue;
    Queue<Image<Bgr, Byte>> outQueue;
    Object lockObject;
    Emgu.CV.UI.ImageBox screen;
    public int frameCounter = 0;

    public Producer(Emgu.CV.UI.ImageBox screen, Capture capture, Queue<Image<Bgr, Byte>> inQueue, Queue<Image<Bgr, Byte>> outQueue, Object lockObject)
    {
        this.screen = screen;
        this.capture = capture;
        this.inQueue = inQueue;
        this.outQueue = outQueue;
        this.lockObject = lockObject;
    }

    public void produce()
    {
        while (true)
        {
            lock (lockObject)
            {
                inQueue.Enqueue(capture.QueryFrame());

                if (inQueue.Count == 1)
                {
                    Monitor.PulseAll(lockObject);
                }
                if (outQueue.Count > 0)
                {
                    screen.Image = outQueue.Dequeue();                      
                }
            }
            frameCounter++;
        }           
    }
}

inQueue から画像を取得し、何らかの処理を行い、それらを outQueue に追加するさまざまな「消費者」がいます。

public class Consumer
{
    Queue<Image<Bgr, Byte>> inQueue;
    Queue<Image<Bgr, Byte>> outQueue;
    Object lockObject;
    string name;

    Image<Bgr, Byte> image;

    public Consumer(Queue<Image<Bgr, Byte>> inQueue, Queue<Image<Bgr, Byte>> outQueue, Object lockObject, string name)
    {
        this.inQueue = inQueue;
        this.outQueue = outQueue;
        this.lockObject = lockObject;
        this.name = name;
    }

    public void consume()
    {
        while (true)
        {
            lock (lockObject)
            {
                if (inQueue.Count == 0)
                {
                    Monitor.Wait(lockObject);
                    continue;
                }                
                image = inQueue.Dequeue();   
            }

            // Do some heavy processing with the image

            lock (lockObject)
            {
                outQueue.Enqueue(image);
            }

        }
    }
}

残りの重要なコードは次のセクションです。

    private void Form1_Load(object sender, EventArgs e)
    {
        Consumer[] c = new Consumer[consumerCount];
        Thread[] t = new Thread[consumerCount];

        Object lockObj = new object();
        Queue<Image<Bgr, Byte>> inQueue = new Queue<Image<Bgr, Byte>>();
        Queue<Image<Bgr, Byte>> outQueue = new Queue<Image<Bgr, Byte>>();

        p = new Producer(screen1, capture, inQueue, outQueue, lockObj);

        for (int i = 0; i < consumerCount; i++)
        {
            c[i] = new Consumer(inQueue, outQueue, lockObj, "c_" + Convert.ToString(i));
        }
        for (int i = 0; i < consumerCount; i++)
        {
            t[i] = new Thread(c[i].consume);
            t[i].Start();
        }

        Thread pt = new Thread(p.produce);
        pt.Start();
    }

並列化は実際にはうまく機能します。追加されたスレッドごとに(もちろん特定のポイントまで)線形速度が向上します。問題は、1 つのスレッドしか実行していない場合でも、出力にアーティファクトが含まれることです。アーティファクトは、画像の一部が適切な場所にないように見えます。

アーティファクトの例 (分かりやすいように加工はしていませんが、効果は同じです)

これを引き起こす原因は何ですか?ありがとう

4

4 に答える 4

1

さて、問題はここだと思います。コードのセクションは、ここで 2 つのキュー間で 1 つのスレッドによってアクセスされることを保証するものではありません。画像はinQueueで pop されますが、実際にはoutQueueで順番に受信されません

while (true)
{
        lock (lockObject)
        {
            if (inQueue.Count == 0)
            {
                Monitor.Wait(lockObject);
                continue;
            }                
            image = inQueue.Dequeue();   
        }

        // Do some heavy processing with the image

        lock (lockObject)
        {
            outQueue.Enqueue(image);
        }

}
于 2014-01-09T01:15:57.113 に答える
0

次の 2 つの問題が発生する可能性があります。

1) 並列処理では、画像が正しい順序で出力キューに追加されることが保証されません。画像 6 と 7 の前に画像 8 を表示すると、アーティファクトが発生する可能性があると思います。コンシューマ スレッドでは、前のコンシューマがそのイメージをアウト キューにポストして次のイメージをポストするのを待つ必要があります。タスクは固有の同期メカニズムにより、そのために非常に役立ちます。

2) レンダリング コードにも問題がある可能性があります。

于 2014-01-16T00:27:35.457 に答える