m00*_*0se 16 c# parallel-processing image emgucv
我从网络摄像头捕获图像,对它们进行一些繁重的处理,然后显示结果.为了保持帧率高,我想让不同帧的处理并行运行.
所以,我有一个'制片人',它捕捉图像并将它们添加到'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++;
}
}
}
Run Code Online (Sandbox Code Playgroud)
有不同的'消费者'从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);
}
}
}
}
Run Code Online (Sandbox Code Playgroud)
其余的重要代码是这一部分:
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();
}
Run Code Online (Sandbox Code Playgroud)
并行化实际上工作正常,我确实通过每个添加的线程(当然达到某个点)获得线性速度增加.问题是我在输出中得到了工件,即使只运行一个线程.工件看起来像图片的一部分不在正确的位置.
有什么想法导致这个?谢谢
Displaimer:这篇文章不应该完全描述答案,而是提供一些关于为什么要显示工件的提示.
一个快速的分析表明,这个实际上是一个部分的,垂直镜像的框架片段.我将其复制,镜像并将其放回图像上,并添加了一个可怕的标记来显示其位置:
有两件事立即引起注意:
自从我玩原始捕获并遇到类似问题已经有一段时间了,但我记得,根据驱动程序的实现方式(或设置 - 在设置特定的成像设备进行隔行扫描时发生这个特殊问题)在"自上而下"和"自下而上"扫描之间交替填充帧缓冲 - 一旦帧满,"光标"就会恢复方向.
在我看来,你正在遇到竞争条件/缓冲区欠载情况,在这种情况下,从帧缓冲区到应用程序的转移发生在设备传输完整帧之前.
在这种情况下,您将收到一个部分图像,并且仍未刷新的区域将显示先前传输的帧的一些.
如果我必须下注,我会说工件可能出现在顺序上,而不是在同一个位置,而是在特定方向(向上或向下)"波动",但始终作为镜像位.
嗯,我想问题就出在这里。该代码段不保证您将被两个队列之间的一个线程访问。图像是由inQueue弹出的,实际上并不是在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);
}
}
Run Code Online (Sandbox Code Playgroud)