繁体   English   中英

并行图像处理工件

[英]parallel image processing artifacts

我从网络摄像头捕获图像,对它们进行一些繁重的处理,然后显示结果。 为了保持帧率高,我想让不同帧的处理并行运行。

所以,我有一个'制片人',它捕捉图像并将它们添加到'inQueue'; 它还需要来自'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();
    }

并行化实际上工作正常,我确实通过每个添加的线程(当然达到某个点)获得线性速度增加。 问题是我在输出中得到了工件,即使只运行一个线程。 工件看起来像图片的一部分不在正确的位置。

工件的示例(这是没有任何处理以保持清晰,但效果是相同的)

有什么想法导致这个? 谢谢

Displaimer:这篇文章不应该完全描述答案,而是提供一些关于为什么要显示工件的提示。

一个快速的分析表明,这个实际上是一个部分的,垂直镜像的框架片段。 我将其复制,镜像并将其放回图像上,并添加了一个可怕的标记来显示其位置:

在此输入图像描述

有两件事立即引起注意:

  • 人工制品大致位于“正确”的位置,只是位置也是垂直镜像的;
  • 图像略有不同,表明它可能属于不同的帧。

自从我玩原始捕获并遇​​到类似问题已经有一段时间了,但我记得,根据驱动程序的实现方式(或设置 - 在设置特定的成像设备进行隔行扫描时发生这个特殊问题)在“自上而下”和“自下而上”扫描之间交替填充帧缓冲 - 一旦帧满,“光标”就会恢复方向。

在我看来,你正在遇到竞争条件/缓冲区欠载情况,在这种情况下,从帧缓冲区到应用程序的转移发生在设备传输完整帧之前。

在这种情况下,您将收到一个部分图像,并且仍未刷新的区域将显示先前传输的帧的一些。

如果我不得不打赌,我会说工件可能出现在顺序上,而不是在同一个位置,而是在特定方向(向上或向下)“波动”,但始终作为镜像位。

好吧,我认为问题出在这里。 代码部分不保证您将在两个队列之间通过此处的一个线程进行访问。 outQueue中按顺序实际上没有接收到由inQueue弹出的图像

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);
        }

}

与@OnoSendai类似,我并没有试图解决所述的确切问题。 我不得不写一个应用程序,我只是没有时间。 但是,我将立即更改的两件事是使用ConcurrentQueue类,以便您具有线程安全性。 而且,我会使用Task库函数来在不同的处理器内核上创建并行任务。 这些可以在System.Net和System.Net.Task命名空间中找到。

此外,垂直翻转像这样的块看起来不仅仅是一件神器给我。 如果在你提到的单个线程中执行时也会发生这种情况,那么我肯定会重新关注等式中的“重处理”部分。

祝好运! 照顾自己。

您可能有两个问题:

1)并行性不能确保以正确的顺序将图像添加到出队列中。 我想在图像6和7之前显示图像8会产生一些伪影。 在消费者线程中,您必须等待之前的消费者已将其图像发布到输出队列以发布下一个图像。 由于其固有的同步机制,任务可以极大地帮助实现这一目标。

2)您可能在渲染代码中遇到问题。

暂无
暂无

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM