Артефакты параллельной обработки изображений
Я захватываю изображения с веб-камеры, делаю тяжелую обработку на них, а затем показываю результат. Чтобы поддерживать высокую частоту кадров, я хочу, чтобы обработка различных кадров выполнялась параллельно.
Итак, у меня есть "Продюсер", который захватывает изображения и добавляет их в "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();
}
Параллелизация действительно работает нормально, я получаю линейное увеличение скорости с каждым добавленным потоком (до определенной точки). Проблема в том, что я получаю артефакты на выходе, даже если работает только один поток. Артефакты выглядят так, как часть изображения находится не в нужном месте.
Пример артефакта (это без какой-либо обработки, чтобы это было ясно, но эффект тот же)
Любые идеи, что вызывает это?
Благодаря
Ответы
Ответ 1
Displaimer: этот пост не должен полностью описывать ответ, но вместо этого дать некоторые подсказки о том, почему отображается артефакт.
Быстрый анализ показывает, что actifact является, по сути, частичным, вертикально зеркальным фрагментом кадра. Я скопировал его, отразил и поместил его обратно на изображение, и добавил ужасный маркер, чтобы показать его размещение:
![enter image description here]()
Признаются сразу две вещи:
- Артефакт примерно расположен на "правильном" месте, в котором он был бы, только то, что позиция также вертикально зеркалируется;
- Изображение немного отличается, что указывает на то, что оно может принадлежать другому кадру.
Прошло некоторое время с тех пор, как я играл с сырым захватом и столкнулся с подобной проблемой, но я помню, что в зависимости от того, как драйвер реализован (или настроен), эта проблема возникла при настройке определенного устройства формирования изображения для чересстрочной съемки), он может заполнить свой фреймбуфер, чередующийся между сканированием "сверху вниз" и "снизу вверх" - как только кадр будет заполнен, "курсор" вернет направление.
Мне кажется, что вы столкнулись с ситуацией, связанной с условиями гонки/буфером, когда передача из фреймбуфера в ваше приложение происходит до того, как весь кадр будет передан устройством.
В этом случае вы получите частичное изображение, а область, все еще не обновленная, покажет бит ранее перенесенного фрейма.
Если бы мне пришлось делать ставки, я бы сказал, что артефакт может появляться в последовательном порядке, а не на той же позиции, но "колеблется" в определенном направлении (вверх или вниз), но всегда как зеркальный бит.
Ответ 2
Ну, я думаю, проблема здесь. Раздел кода не гарантирует, что вы будете иметь доступ одним потоком здесь между двумя очередями. Изображение поп-функции 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);
}
}
Ответ 3
Подобно @OnoSendai, я не пытаюсь решить точную проблему, как указано. Мне нужно написать приложение, и у меня просто нет времени. Но две вещи, которые я бы сразу изменил, - это использовать класс ConcurrentQueue
, чтобы у вас была безопасность потоков. И я бы использовал функции библиотеки задач для создания параллельных задач на разных процессорных ядрах. Они находятся в пространствах имен System.Net и System.Net.Task.
Кроме того, вертикальное перелистывание такого фрагмента похоже на нечто большее, чем артефакт для меня. Если это также происходит при выполнении в одном потоке, как вы упомянули, я бы определенно перефокусировался на части "тяжелой обработки" уравнения.
Удачи! Будьте осторожны.
Ответ 4
У вас могут быть две проблемы:
1) Параллелизм не гарантирует, что изображения добавляются в очередь очереди в правильном порядке. Я полагаю, что показ изображения 8 перед изображением 6 и 7 может привести к некоторым артефактам. В потребительской цепочке вам нужно подождать, чтобы предыдущий потребитель разместил свой образ в очереди, чтобы опубликовать следующее изображение. Задачи могут очень помочь для этого из-за присущего им механизма синхронизации.
2) У вас могут быть проблемы с кодом рендеринга.