Ответ 1
Прежде всего, спасибо за ясность вопроса.
В: Можно ли использовать (мульти) многопоточность на уровне приложений с OpenCV?
A: Да, вполне нормально использовать многопоточность на уровне приложений с OpenCV, если только вы не используете функции, которые могут использовать преимущества многопоточности, такие как размытие, изменение цветового пространства, здесь вы можете разделить изображение на несколько частей и применить глобальные функции по всей разделенной части, а затем рекомбинируйте их для получения окончательного результата.
В некоторых функциях, таких как Hough, pca_analysis, которые не могут дать правильные результаты, когда они применяются к разделенным разделам изображения и затем объединяются, применение многопоточности на уровне приложения к таким функциям может не дать правильных результатов и, следовательно, не должно выполняться.
Как уже упоминалось, ваша реализация многопоточности не даст вам преимущества, потому что вы присоединяетесь к потоку в самом цикле for. Я бы посоветовал вам использовать объекты обещания и будущие объекты (если вам нужен пример того, как это сделать, дайте мне знать в комментариях, я поделюсь фрагментом кода.
Ниже ответ потребовал много исследований, спасибо за вопрос, это действительно помогает мне добавить информацию к моим многопоточным знаниям :)
В: Если да, почему промежутки времени, напечатанные моей программой, растут с течением времени?
A: После долгих исследований я обнаружил, что создание и уничтожение потоков требует много ресурсов процессора и памяти. Когда мы инициализируем поток (в вашем коде этой строкой: thread t(blurSlowdown, nullptr);
), идентификатор записывается в область памяти, на которую указывает эта переменная, и этот идентификатор позволяет нам обращаться к потоку. Теперь в вашей программе вы создаете и уничтожаете потоки с очень высокой скоростью, теперь это то, что происходит, для программы выделен пул потоков, с помощью которого наша программа может запускать и уничтожать потоки, я буду держать его коротким и давайте рассмотрим объяснение ниже:
- Когда вы создаете поток, это создает идентификатор, который указывает на этот поток.
- Когда вы уничтожаете поток, эта память освобождается
НО
Когда вы снова создаете поток, и через некоторое время первый поток уничтожается, идентификатор этого нового потока указывает на новое местоположение(местоположение, отличное от предыдущего потока) в пуле потоков.
После многократного создания и уничтожения потока, пул потоков исчерпан, и поэтому ЦП вынужден немного замедлять циклы нашей программы, так что пул потоков снова освобождается для освобождения места для новая тема.
Intel TBB и OpenMP очень хорошо справляются с управлением пулом потоков, поэтому эта проблема может не возникать при их использовании.
В: Широко ли поддерживается TBB в 2019 году?
О: Да, вы можете использовать преимущества TBB в своей программе OpenCV, а также включить поддержку TBB при создании OpenCV.
Вот программа для реализации TBB в medianBlur:
#include "opencv2/imgproc/imgproc.hpp"
#include "opencv2/highgui/highgui.hpp"
#include <iostream>
#include <chrono>
using namespace cv;
using namespace std;
using namespace std::chrono;
class Parallel_process : public cv::ParallelLoopBody
{
private:
cv::Mat img;
cv::Mat& retVal;
int size;
int diff;
public:
Parallel_process(cv::Mat inputImgage, cv::Mat& outImage,
int sizeVal, int diffVal)
: img(inputImgage), retVal(outImage),
size(sizeVal), diff(diffVal)
{
}
virtual void operator()(const cv::Range& range) const
{
for(int i = range.start; i < range.end; i++)
{
/* divide image in 'diff' number
of parts and process simultaneously */
cv::Mat in(img, cv::Rect(0, (img.rows/diff)*i,
img.cols, img.rows/diff));
cv::Mat out(retVal, cv::Rect(0, (retVal.rows/diff)*i,
retVal.cols, retVal.rows/diff));
cv::medianBlur(in, out, size);
}
}
};
int main()
{
VideoCapture cap(0);
cv::Mat img, out;
while(1)
{
cap.read(img);
out = cv::Mat::zeros(img.size(), CV_8UC3);
// create 8 threads and use TBB
auto start1 = high_resolution_clock::now();
cv::parallel_for_(cv::Range(0, 8), Parallel_process(img, out, 9, 8));
//cv::medianBlur(img, out, 9); //Uncomment to compare time w/o TBB
auto stop1 = high_resolution_clock::now();
auto duration1 = duration_cast<microseconds>(stop1 - start1);
auto time_taken1 = duration1.count()/1000;
cout << "TBB Time: " << time_taken1 << "ms" << endl;
cv::imshow("image", img);
cv::imshow("blur", out);
cv::waitKey(1);
}
return 0;
}
На моем компьютере внедрение TBB занимает около 10 мс, а без TBB - около 40 мс.
В: Если да, что обеспечивает лучшую производительность, многопоточность на уровне приложений (если это разрешено) или TBB/OpenMP?
A: Я бы предложил использовать многопоточность TBB/OpenMP поверх POSIX (pthread/thread), потому что TBB предлагает вам лучший контроль над потоком + лучшую структуру для написания параллельного кода и внутренне он управляет pthreads. В случае, если вы используете pthreads, вам придется позаботиться о синхронизации, безопасности и т.д. В вашем коде. Но использование этих структур избавляет от необходимости обрабатывать потоки, которые могут быть очень сложными.
Изменить: Я проверил комментарии относительно несовместимости размеров изображения с номером нити, на которую вы хотите разделить обработку. Итак, вот потенциальный обходной путь (не тестировал, но должен работать), масштабируйте разрешение изображения до совместимых размеров, например:
Если разрешение изображения составляет 485 x 647, масштабируйте его до 488 x 648, затем передайте его в Parallel_process
, а затем уменьшите масштаб до исходного размера 458 x 647.
Для сравнения TBB и OpenMP проверьте этот ответ