gpt4 book ai didi

c++ - 从单线程到多线程图像处理

转载 作者:太空宇宙 更新时间:2023-11-03 22:45:36 26 4
gpt4 key购买 nike

我正在将我的代码从单线程转换为多线程进程,然后我到达了我正在努力的那部分代码:

其中 srcimJIllumTrans 只是大小为 1288*728A 的图像 是常数值

for(auto j=0;j<rows;j++)
for(auto i=0;i<cols;i++)
imJ.at<float>(j,i)= A+((src.at<uchar>(j,i)-A)/std::max(IllumTrans.at<float>(j,i), 0.1f));

我正在处理 RGB 图像,我正在做的是为每个 channel 运行相同的代码(上面的双 for 循环),我认为如果我在多线程进程中运行它会很好。

我是这样启动多线程版本的

cv::Mat imJ=cv::Mat::zeros(rows, cols, CV_32FC3);
cv::MatConstIterator<cv::Vec3b> it_src = src.begin();
cv::MatConstIterator<cv::Vec3f> it_IllumTrans = IllumTrans.begin();
imJ.forEach<cv::Vec3f>
(
[A, &IllumTrans, rows, cols](cv::Vec3f &pixel, const int* po) -> void
{
pixel[0]= A+(((*it_src)[0]-A[0])/std::max((*it_IllumTrans)[0], 0.1f));
pixel[1]= A+(((*it_src)[0]-A[1])/std::max((*it_IllumTrans)[1], 0.1f));
pixel[2]= A+(((*it_src)[0]-A[2])/std::max((*it_IllumTrans)[2], 0.1f));
it_src++;
it_IllumTrans++;
}
);

但是我没有成功,它说'MatIterator' is not a member of ‘cv'

您如何看待我的新版本,这样做是否正确?

编译信息:

g++ -std=c++1z -Wall -Weffc++ -Ofast -march=native test4.cpp -o test4 -fopenmp `pkg-config --cflags --libs opencv`

最佳答案

我在使用 cv::Mat::forEach 的实现中看到的主要问题是共享迭代器的顺序 it_srcit_IllumTrans一旦 lambda 在多个线程上并行运行,它将是不确定的。您最终会遇到无法预料的困惑局面。


一种处理方法是使用 cv::Mat::forEach 调用的函数的位置参数,并使用它来获取指向两个输入中正确位置的指针(迭代器在这里会有点矫枉过正)Mat

cv::Mat3f variant_1(cv::Mat3b const& source
, cv::Mat3f const& illum_trans
, cv::Vec3f const& offset)
{
CV_Assert(!source.empty());
CV_Assert(!illum_trans.empty());
CV_Assert(source.size() == illum_trans.size());

cv::Mat3f result(source.size());

result.forEach([&source, &illum_trans, &offset](cv::Vec3f &pixel, const int p[]) {
cv::Vec3b const* it_src(source.ptr<cv::Vec3b>(p[0], p[1]));
cv::Vec3f const* it_trans(illum_trans.ptr<cv::Vec3f>(p[0], p[1]));

for (int32_t ch(0); ch < 3; ++ch) {
pixel[ch] = offset[ch]
+ (((*it_src)[ch] - offset[ch])
/ std::max((*it_trans)[ch], 0.1f));
}
});

return result;
}

第二种方法是使用 parallel_for_ ,并按行在线程之间划分图像。这意味着我们只需每行获取一次指针(我们需要考虑输入图像可能不连续),其余时间只需递增它们。

class ParallelVariant2
: public cv::ParallelLoopBody
{
public:
ParallelVariant2(cv::Mat3b const& source
, cv::Mat3f& destination
, cv::Mat3f const& illum_trans
, cv::Vec3f const& offset)
: source_(source)
, destination_(destination)
, illum_trans_(illum_trans)
, offset_(offset)
{
CV_Assert(!source.empty());
CV_Assert(!illum_trans.empty());
CV_Assert(source.size() == illum_trans.size());

destination_.create(source.size());
}

virtual void operator()(const cv::Range& range) const
{
for (int32_t row(range.start); row < range.end; ++row) {
cv::Vec3b const* it_src(source_.ptr<cv::Vec3b>(row));
cv::Vec3f* it_dst(destination_.ptr<cv::Vec3f>(row));
cv::Vec3f const* it_trans(illum_trans_.ptr<cv::Vec3f>(row));

for (int32_t col(0); col < source_.cols; ++col) {
for (int32_t ch(0); ch < 3; ++ch) {
(*it_dst)[ch] = offset_[ch]
+ (((*it_src)[ch] - offset_[ch])
/ std::max((*it_trans)[ch], 0.1f));
}
++it_src; ++it_dst; ++it_trans;
}
}
}

private:
cv::Mat3b const& source_;
cv::Mat3f& destination_;
cv::Mat3f const& illum_trans_;
cv::Vec3f const& offset_;
};

cv::Mat3f variant_2(cv::Mat3b const& source
, cv::Mat3f const& illum_trans
, cv::Vec3f const& offset)
{
cv::Mat3f result;
ParallelVariant2 impl(source, result, illum_trans, offset);
cv::parallel_for_(cv::Range(0, source.rows), impl);
return result;
}

一个简短的测试程序。

#include <opencv2/opencv.hpp>
#include <cstdint>

// paste implementations here

int main()
{
cv::Mat3b source(1024, 1024);
cv::randu(source, 0, 256);

cv::Mat3f illum_trans(1024, 1024);
cv::randu(illum_trans, 0, 1.0);

cv::Vec3f offset(0.1f, 0.2f, 0.3f);

cv::Mat3f result_1 = variant_1(source, illum_trans, offset);
cv::Mat3f result_2 = variant_2(source, illum_trans, offset);

std::cout << std::equal(result_1.begin(), result_1.end(), result_2.begin()) << "\n";

return 0;
}

关于c++ - 从单线程到多线程图像处理,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/47912722/

26 4 0
Copyright 2021 - 2024 cfsdn All Rights Reserved 蜀ICP备2022000587号
广告合作:1813099741@qq.com 6ren.com