C# 并行图像处理伪影

C# 并行图像处理伪影,c#,image,parallel-processing,emgucv,C#,Image,Parallel Processing,Emgucv,我从一个网络摄像头中捕获图像,对它们进行一些繁重的处理,然后显示结果。为了保持高帧速率,我想让不同帧的处理并行运行 因此,我有一个“制作人”,它捕获图像并将其添加到“队列”中;它还从“outQueue”中获取图像并显示: public class Producer { Capture capture; Queue<Image<Bgr, Byte>> inQueue; Queue<Image<Bgr, Byte>> outQu

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

因此,我有一个“制作人”,它捕获图像并将其添加到“队列”中;它还从“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++;
        }           
    }
}
公共类制作人
{
捕获;
排队;
排队出队;
对象锁定对象;
Emgu.CV.UI.ImageBox屏幕;
公共整数帧计数器=0;
公共生产者(Emgu.CV.UI.ImageBox屏幕、捕获捕获、队列输入队列、队列输出队列、对象锁定对象)
{
this.screen=屏幕;
这个。捕获=捕获;
this.inQueue=inQueue;
this.outQueue=outQueue;
this.lockObject=lockObject;
}
公共产品
{
while(true)
{
锁定(锁定对象)
{
inQueue.Enqueue(capture.QueryFrame());
如果(inQueue.Count==1)
{
Monitor.Pulsell(锁定对象);
}
如果(outQueue.Count>0)
{
screen.Image=outQueue.Dequeue();
}
}
帧计数器++;
}           
}
}
有不同的“消费者”,他们从输入队列中获取图像,进行一些处理,然后将其添加到输出队列中:

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

        }
    }
}
公共类消费者
{
排队;
排队出队;
对象锁定对象;
字符串名;
图像;
公共使用者(队列输入队列、队列输出队列、对象锁定对象、字符串名称)
{
this.inQueue=inQueue;
this.outQueue=outQueue;
this.lockObject=lockObject;
this.name=名称;
}
公共消费()
{
while(true)
{
锁定(锁定对象)
{
如果(inQueue.Count==0)
{
监视器。等待(锁定对象);
继续;
}                
image=inQueue.Dequeue();
}
//对图像进行一些繁重的处理
锁定(锁定对象)
{
outQueue.Enqueue(图像);
}
}
}
}
其余的重要代码如下所示:

    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();
    }
private void Form1\u加载(对象发送方,事件参数e)
{
消费者[]c=新消费者[consumerCount];
线程[]t=新线程[consumerCount];
Object lockObj=新对象();
Queue inQueue=新队列();
Queue outQueue=新队列();
p=新生产者(屏幕1、捕获、入队、出队、锁定对象);
对于(int i=0;i
并行化实际上工作得很好,每增加一个线程,我都会得到一个线性的速度增加(当然,直到某一点)。问题是,即使只运行一个线程,我也会在输出中得到工件。这些瑕疵看起来像是图片的一部分不在正确的位置

你知道这是什么原因吗?
谢谢

好吧,我想问题就在这里。代码部分不能保证您将被两个队列之间的一个线程访问。图像是通过入队列弹出的实际上没有按出队列中的顺序接收

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

}

Displaimer:这篇文章不应该完全描述一个答案,而是给出一些提示,说明为什么要显示这个工件

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

两件事立即引起注意:

  • 工件大致定位在“正确”的位置,只是位置也是垂直镜像的
  • 图像略有不同,表明它可能属于不同的帧
我已经有一段时间没有处理原始捕获并遇到类似的问题了,但我记得这取决于驱动程序的实现方式(或设置-在为隔行扫描捕获设置特定的成像设备时,会发生此特定问题)它可能在“自上而下”和“自下而上”扫描之间交替填充帧缓冲区-帧一满,“光标”就会恢复方向

在我看来,您似乎遇到了竞争条件/缓冲区不足的情况,从帧缓冲区到应用程序的传输发生在设备传输完整帧之前

在这种情况下,您将收到一个部分图像,而仍然没有刷新的区域将显示先前传输的帧的一部分


如果我不得不打赌的话,我会说这个神器可能会按顺序出现,不是在同一个位置,而是在一个特定的方向(向上或向下)上“波动”,但总是作为镜像位出现。

类似于@OnoSendai,我并没有试图解决前面提到的确切问题。我必须写一个应用程序,但我没有时间。但是,我要马上改变的两件事是使用
ConcurrentQueue
类,这样您就有了线程安全性。并且,我将使用任务库函数在不同的处理器内核上创建并行任务。可以在System.Net和System.Net.Task命名空间中找到这些名称

而且,垂直翻转一块这样的东西在我看来不仅仅是一件艺术品。如果它也发生在您提到的在单个线程中执行时,那么我肯定会重新关注t