Артефакты параллельной обработки изображений - программирование
Подтвердить что ты не робот

Артефакты параллельной обработки изображений

Я захватываю изображения с веб-камеры, делаю тяжелую обработку на них, а затем показываю результат. Чтобы поддерживать высокую частоту кадров, я хочу, чтобы обработка различных кадров выполнялась параллельно.

Итак, у меня есть "Продюсер", который захватывает изображения и добавляет их в "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();
    }

Параллелизация действительно работает нормально, я получаю линейное увеличение скорости с каждым добавленным потоком (до определенной точки). Проблема в том, что я получаю артефакты на выходе, даже если работает только один поток. Артефакты выглядят так, как часть изображения находится не в нужном месте.

Пример артефакта (это без какой-либо обработки, чтобы это было ясно, но эффект тот же)

Любые идеи, что вызывает это? Благодаря

4b9b3361

Ответ 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) У вас могут быть проблемы с кодом рендеринга.