从单线程到多线程图像处理

Posted

tags:

篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了从单线程到多线程图像处理相关的知识,希望对你有一定的参考价值。

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

其中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图像,我正在做的是为每个通道运行相同的代码(上面的双循环),如果我在多线程进程中运行它,我认为这可能很好。

我开始这样的多线程版本

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++;
  }
);

但我没有成功,它说q​​azxswpoi。

您对我的新版本有什么看法,这是正确的方法吗?

编制信息:

'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` 实现的主要问题是,一旦lambda在多个并行线程上运行,共享迭代器cv::Mat::forEachit_src的顺序将是非确定性的。你最终将会出现无法预测的乱七八糟的混乱局面。


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

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;
}

以上是关于从单线程到多线程图像处理的主要内容,如果未能解决你的问题,请参考以下文章

更快更好用的Redis6.0

在服务器上部署Flask代码(从单进程到多进程)

OO第四次总结

从单主服务器迁移到多主服务器 Apache KUDU 配置

链接到多线程调试 DLL 库时出现链接错误

从单例管理器类发布到 UI 线程