【问题标题】:What is the fastest way for executing three nested for loop?执行三个嵌套 for 循环的最快方法是什么?
【发布时间】:2017-06-28 12:20:45
【问题描述】:

我是一名图像处理程序员,我正在使用 opencv C++。作为我编写的程序的一部分,我嵌套了三个 for。第一个用于不同的图像,第二个用于图像行,第三个用于图像列。三个 for 之间没有任何依赖关系,它们可以并行执行(我的意思是,所有图像的所有像素都可以并行处理)。我不熟悉并行编程、GPU 编程、线程、tbb、并行 for 循环和……。我在互联网上找到了建议此类事情的不同链接。我想知道解决我的问题的最快方法是什么? 我的操作系统是 windows,我使用的是 Visual Studio 2015。

我的代码如下:

int prjResCol[MAX_NUMBER_OF_PROJECTOR];
int prjResRow[MAX_NUMBER_OF_PROJECTOR];
Mat prjCamCor[MAX_NUMBER_OF_PROJECTOR][2]
Mat prjImgColored[MAX_NUMBER_OF_PROJECTOR];

for (int i = 0; i < numOfProjector; i++)
{
    Mat tmp(prjResRow[i], prjResCol[i], CV_8UC3, Scalar(0, 0, 0));
    prjImgColored[i] = tmp;

    for (int ii = 0; ii < prjResRow[i]; ii++)
    {
        double* ptrPrjCamIAnd0 = prjCamCor[i][0].ptr<double>(ii);
        double* ptrPrjCamIAnd1 = prjCamCor[i][1].ptr<double>(ii);
        Vec3b* ptrPrjImgColoredI = prjImgColored[i].ptr<Vec3b>(ii);

        for (int jj = 0; jj < prjResCol[i]; jj++)
        {

            if ((ptrPrjCamIAnd0[jj] != NAN_VALUE) && (ptrPrjCamIAnd1[jj] != NAN_VALUE))
            {
                ptrPrjImgColoredI[jj] = secondImgColored.at<Vec3b>(ptrPrjCamIAnd1[jj], ptrPrjCamIAnd0[jj]);
            }

        }
    }
    imwrite(mainAdr + "\\img" + to_string(i) + ".bmp", prjImgColored[i]);
}

【问题讨论】:

  • 尝试提供一个 MCVE - 一个小而完整的示例。你遗漏了关于类型(Mat,Vec3b)几个变量(比如名字以prj开头的变量)和CV_8UC3(不管是什么)的关键信息。这些信息至关重要,因为要优化您的代码,需要有人了解这些内容是什么。
  • 你分析过它吗?你的限制是什么?几张图?什么尺寸?内循环中的哪个处理?不知道这一点就开始“优化”是没有意义的。
  • 图片最多20张,每个Mat大小约为2000*3000(rows*columns)。
  • 对于 20 张图片,第一个“for”不是问题。无需自定义优化即可非常快速地处理该大小的图像。这完全取决于您对图像所做的操作。您编写的内部循环几乎与它们一样快。分析他们,展示一些处理时间,然后我们会看到可以做什么

标签: c++ opencv image-processing parallel-processing gpu


【解决方案1】:

正如您所写,使用 Parallel For 循环迭代像素是处理大图像的最快方法。使用并行算法时会有一些开销,因此对于小图像(例如 256 X 256),您发布的传统循环可能会更好。

下面是一个用 Visual C++ 编写的示例:

// Calls the provided function for each pixel in a Bitmap object.
void ProcessImage(Bitmap* bmp, const function<void (DWORD&)>& f)
{
   int width = bmp->GetWidth();
   int height = bmp->GetHeight();

   // Lock the bitmap.
   BitmapData bitmapData;
   Rect rect(0, 0, bmp->GetWidth(), bmp->GetHeight());
   bmp->LockBits(&rect, ImageLockModeWrite, PixelFormat32bppRGB, &bitmapData);

   // Get a pointer to the bitmap data.
   DWORD* image_bits = (DWORD*)bitmapData.Scan0;

   // Call the function for each pixel in the image.
   parallel_for (0, height, [&, width](int y)
   {      
      for (int x = 0; x < width; ++x)
      {
         // Get the current pixel value.
         DWORD* curr_pixel = image_bits + (y * width) + x;

         // Call the function.
         f(*curr_pixel);
      }
   });

   // Unlock the bitmap.
   bmp->UnlockBits(&bitmapData);
}

您可以采用另一种方法来并行化您的工作流程,您可以同时对多个图像进行单线程(双 for 循环)迭代。下面是一个用 C# 编写的示例。您只需要用串行双循环代替位图翻转例程。使用适当的并行库,C++ 实现应该非常相似:

// 一个用于演示目的的简单源代码。根据需要修改此路径。 String[] files = System.IO.Directory.GetFiles(@"C:\Users\Public\Pictures\Sample Pictures", "*.jpg"); String newDir = @"C:\Users\Public\Pictures\Sample Pictures\Modified"; System.IO.Directory.CreateDirectory(newDir);

    // Method signature: Parallel.ForEach(IEnumerable<TSource> source, Action<TSource> body)
    // Be sure to add a reference to System.Drawing.dll.
    Parallel.ForEach(files, (currentFile) => 
    {
        // The more computational work you do here, the greater 
        // the speedup compared to a sequential foreach loop.
        String filename = System.IO.Path.GetFileName(currentFile);
        var bitmap = new Bitmap(currentFile);

        bitmap.RotateFlip(RotateFlipType.Rotate180FlipNone);
        bitmap.Save(Path.Combine(newDir, filename));

        // Peek behind the scenes to see how work is parallelized.
        // But be aware: Thread contention for the Console slows down parallel loops!!!

         Console.WriteLine("Processing {0} on thread {1}", filename, Thread.CurrentThread.ManagedThreadId);
         //close lambda expression and method invocation
         });

Open CV 至少从 2.4.3 版本开始支持并行 For。通过使用并行循环,您可以利用多核 CPU 的强大功能,其中每个核将迭代图像的单独子部分。

OpenCV 还支持 CUDA,这是一个由 NVIDA 创建的并行处理 API,它利用了 GPU 的强大功能。我不认为这种方法是解决这个特定问题的方法,但既然你提到你是一名图像处理程序员,那么值得研究未来的问题。

【讨论】:

  • 感谢您的回答。图片最大数量为20。每个Mat大小约为2000*3000(行*列)。
  • parallel for 这种处理基本没用,图片这么少。
  • 虽然 20 张 600 万像素的图像相当小,但如果实施得当,并行处理仍然可以减少处理时间(考虑到轻微的开销)。例如,如果有人使用 Intel I7 处理(8 核),并且正在执行并行工作流。 (并行处理多个图像),如果 PC 没有被推动执行任何其他密集任务,您将看到时间改进。我没有在图像上尝试过这种模式,但我已经完成了处理几个 100 M与串行工作流相比,字节文件和时间节省非常显着。
  • @user3822376 - 欢迎您。我添加了更多关于您可以尝试使用 Parallel For 循环的两种模式。此外,我还找到了一些源代码,可以让您的生活更轻松。祝你好运!
  • @DaveS 首先,感谢您花时间编写此代码!我不熟悉 Visual C++ 语言,也无法解释您的代码。你的意思是我应该改变我的代码?
猜你喜欢
  • 1970-01-01
  • 1970-01-01
  • 2017-12-18
  • 1970-01-01
  • 2017-09-20
  • 2019-04-15
  • 1970-01-01
  • 2015-09-06
  • 2022-01-26
相关资源
最近更新 更多