Использование C++ для взаимодействия с камерой линейного сканирования на частоте 3000 Гц и обработки / отображения данных
Несколько месяцев назад я начал работать над проектом по разработке алгоритма обработки данных, полученных с устройства камеры с линейным сканированием (например, линия 384 пикселя каждые 300 мкс). Поскольку я инженер, а не программист, я начал работать с Python, чтобы минимизировать кривую обучения. С помощью SX я успешно построил приложение на Python (в итоге получилось более 2000 строк кода) и успешно создал алгоритм обработки изображений для работы с данными. Я произвел на клиента впечатление, и они хотят вывести его на новый уровень. Теперь мне нужно, чтобы это было в режиме реального времени... и это означает C++. Я получил ускоренный C++ Кенига и Му и начал читать. Так что, пожалуйста, будьте осторожны со мной. Я люблю учиться программировать, но у меня нет формального обучения. Я делаю все, что могу!
Теперь у меня есть GUI-прототип C++ (с использованием Qt), обернутый вокруг всех библиотек, необходимых для взаимодействия с камерой через интерфейс CameraLink. Код сбора данных живет в своем собственном потоке и излучает сигналы в графический интерфейс. Итак, у меня есть основы на месте. Я могу получить столько строк данных, сколько захочу, с помощью своего текущего кода, но сейчас я пытаюсь выяснить, как построить приложение на основе этого. Даже написал собственный make-файл, который работает с Qt (MOCing и т. Д.)
В любом случае, для приложения я бы хотел два режима (это ВОПРОСЫ):
(1) "Живое" представление... где данные линейного сканирования отображаются в реальном времени с помощью графического интерфейса. Я думал об использовании циклического буфера (например, Boost:: round_buffer) для хранения данных в режиме реального времени, и просто передать копию буфера (memcpy?) В графический интерфейс через излучаемый сигнал. Это надежно? Я чувствую, что копия буфера необходима, поскольку циклический буфер будет меняться каждые 300 мкс или около того, и я не знаю, может ли цикл основного события не отставать. Опять же, сбор данных живет в своем собственном потоке. Должно ли это быть более сложным, чем это? Придется ли извлекать данные из буфера при его чтении вместо использования циклического буфера? Я чувствовал, что нужно использовать кольцевой буфер, поскольку именно это изображение я хочу отображать.
(2) Режим обработки данных... где данные строчного сканирования излучаются в блоках (то есть, 384 x 384) пикселей. При скорости сканирования 300 мкс (~3333 Гц) это блок или кадр каждые 100 мс или около того. За эти 100 мс мне нужно будет нормализовать данные, обнаружить объекты, установить пороговое значение и т. Д. Я планирую запустить это на компьютере под управлением Linux, работающем в режиме реального времени с исправлением ядра. Я думаю, что это должно идти в ногу. Мне нужно будет связаться между потоками сбора и обработки данных... мне нужны сокеты для этого?
Я ищу совет о том, как начать с этих двух частей. Второй более критичен, но первый поможет мне визуализировать происходящее. В конечном счете, я хотел бы, чтобы оба режима работали одновременно. Я провел большую часть недели, занимаясь этим... но мне нужно убедиться, что я иду по правильному пути со своим планом.
1 ответ
К (1):
Имеет смысл для меня. Вы должны быть осторожны с проблемами синхронизации при доступе к одному и тому же буферу из кода GUI и кода вашего получателя в противном случае. Одним из возможных улучшений было бы немного ограничить количество обновлений GUI. Частота обновления экрана обычно составляет 50 или 60 Гц, и большинство библиотек графического интерфейса предполагают, что обновления происходят не намного чаще, чем это.
Вы также можете сократить объем копируемых данных, просто скопировав то, что будет отображаться на экране. Поэтому я бы порекомендовал, возможно, немного перевернуть это: GUI получает таймер обновления (что бы ни выглядело достаточно хорошо для ваших целей), который вытягивает новое содержимое дисплея из циклического буфера по мере необходимости. Таким образом вы сокращаете множество ненужных (то есть невидимых) обновлений экрана и буферных копий.
В зависимости от ваших потребностей вы также можете просто использовать блоки, созданные для части 2 вашего вопроса, для обновления экрана.
To (2): Во-первых, вам не нужны сокеты или что-то подобное, когда вы используете многопоточность.
Я бы порекомендовал что-то вроде пула потоков для вашей обработки: когда новые блоки станут доступными, скопируйте их в объект задачи (определяемый вами класс, который имеет код для обработки и реализует интерфейс, понятный пулу потоков), и передайте его пул потоков.
Поскольку вы используете Qt, я бы посмотрел на QThreadPool и QRunnable для этой части. Если вам нужно завершить обработку блоков в определенном порядке, все становится немного интереснее. По сути, у вас будет структура данных очереди блокировки, которую вы также будете передавать объектами QRunnable, а затем другой поток, который захватывает их там и ожидает завершения каждого из них в порядке их запуска.
Связь здесь будет ограничена потоком сбора данных, разделяющим входные данные на блоки и запускающими задачи. Если вам нужно также контролировать поток сбора данных из задач обработки данных, вам, вероятно, понадобится немного другой дизайн.
Вы также можете обойтись без использования патча ядра в реальном времени. Если библиотека, которую вы используете для доступа к своей камере линейного сканирования, буферизует свой ввод, вы просто получите несколько строк одну за другой, если пропустите обновление. Опять же, это зависит от того, насколько быстро вам нужно реагировать, но вы выполняете обработку изображений на блоках высотой в несколько строк, поэтому я ожидаю, что вы уже можете справиться с небольшой задержкой.
ЭТА: Я просто перечитал твой вопрос. Таким образом, у вас есть блоки размером всего 384x384 пикселей каждые 100 мс. Я собирался предложить использовать сигналы Qt, но здесь вы можете столкнуться с проблемами: сигналы Qt используют внутреннюю структуру данных очереди блокировки при обмене данными между потоками. К сожалению, их реализация не позволяет вам устанавливать ограничение размера, поэтому, если ваш поток графического интерфейса или поток обработки не обрабатывают их достаточно быстро (скажем, пользователь находится в модальном диалоговом окне для графического интерфейса), они вместо этого будут буферизованы и израсходуют память.
Вместо этого вы можете использовать что-то вроде этого:
Acquisition thread ==> (Blocking Queue) ==> Processing thread
По сути, ваш поток сбора данных просто закачает блоки в очередь. Поток обработки будет извлекать блоки из очереди в цикле и отправлять их в графический интерфейс для отображения, а затем обрабатывать их. Или наоборот, если вы хотите визуализации.