【问题标题】:optimization issue with splitting an image into channels将图像拆分为通道的优化问题
【发布时间】:2015-09-26 06:01:24
【问题描述】:

这是我的代码,我的“算法”正在尝试获取拜耳图像或 RGB 图像,并将通道 G(即亮度(甚至灰度))分离到颜色的不同通道中,

拜耳模式示例

void Utilities::SeparateChannels(int* channelR, int* channelG, int* channelB, double*& gr, double*& r, double*& b, double*& gb,int _width, int _height, int _colorOrder)
{
    //swith case the color Order
    int counter_R = 0;
    int counter_GR = 0;
    int counter_GB = 0;
    int counter_B = 0;

    switch (_colorOrder)
    {

        //grbg
    case 0: 
        for (int j = 0; j < _width; j++)
        {           
            for (int i = 0; i < _height; i++)
            {
                if (i % 2 == 0 && j % 2 == 0)
                {                    
                    gr[counter_GR] = channelG[i*_width+ j];
                    counter_GR++;
                }
                else if (i % 2 == 0 && j % 2 == 1)
                {
                    r[counter_R] = channelG[i*_width+ j];
                    counter_R++;
                }
                else if (i % 2 == 1 && j % 2 == 0)
                {
                    b[counter_B] =channelG[i*_width+ j];
                    counter_B++;
                }
                else if (i % 2 == 1 && j % 2 == 1)
                {
                    gb[counter_GB] = channelG[i*_width+ j];
                    counter_GB++;
                }
            }
        }

我在 70 张图像上运行了分析器,并附上了我的结果。 你能推荐一种优化我的代码的方法吗?

【问题讨论】:

  • 只是出于好奇,您使用了什么分析器?
  • 你能解释一下代码吗?你是什​​么意思这里的单独频道?您显然只是在“阅读”绿色通道,并将其分成一些无法解释的 4 段输出。类型转换(int -> double)的目的是什么?
  • @Amit 我将一个数据通道分成几个通道
  • 这根本不能回答问题。请使用算法的输入和所需输出的详细信息更新您的帖子。目前尚不清楚该算法实际上做了什么。类型不清楚,变量名称没有多大意义(我假设您正在处理标题所暗示的 RGB 像素)......如果不理解它就不可能“优化”算法。
  • 您仍然没有回答为什么要进行类型转换。这绝对是一项成本高昂的操作。另外,您只使用channelG吗?那为什么其他通道都传入呢?

标签: c++ image-processing optimization


【解决方案1】:

整个算法可以简化为一个内部循环,它将输入数组的一部分解交织成 2 个单独的输出数组。每行的 2 个输出数组都在变化,它们的选择取决于输入类型 (_colorOrder)。

所以..首先将您的算法更改为这样工作:

void Utilities::SeparateChannels(int* channelR, int* channelG, int* channelB, double*& gr, double*& r, double*& b, double*& gb,int _width, int _height, int _colorOrder)
{
    //swith case the color Order
    int counter_R = 0;
    int counter_GR = 0;
    int counter_GB = 0;
    int counter_B = 0;
    double *split1, *split2;

    switch (_colorOrder)
    {

        //grbg
    case 0: 
         for (int i = 0; i < _height; i++)
         {
            if(i % 2 == 0)
            {
                split1 = gr + counter_GR;
                split2 = r + counter_R;
                counter_GR += _width / 2;
                counter_R += _width / 2;
            }
            else
            {
                split1 = b + counter_B;
                split2 = gb + counter_GB;
                counter_B += _width / 2;
                counter_GB += _width / 2;
            }

            int *channel = channelG + (i * _width);

         // deinterleave(channel, split1, split2, _width);
         }

现在您需要做的就是将channel 解交错为split1 和split2 在_width 元素上。在优化的 (ASM?) 内联函数中执行此操作。

【讨论】:

  • 你能解释一下去交错是什么意思吗?我不想去汇编代码。这不是一个实时应用程序。
  • 算法优化有(一般)2种类型:1.降低时间复杂度(需要优化算法背后的“逻辑”)2.优化内循环。由于在这种情况下 1 是不可能的,所以你只剩下 2,这需要在内部循环中尽可能快,如果在这种情况下使用 ASM 完成可能是最好的(它是一个简单的、无逻辑的内部循环循环)。
  • 去交错与交错相反,交错是将多个输入组合成一个输出的过程,其中输出中的项目一次从每个输入中“挑选”一个(您总是可以查看像这样使用互联网的东西)。
  • 最后,如果这不是一个实时应用程序,我猜你真正的意思是你真的不想优化它(实时不是你想要高效的唯一场景代码),你为什么首先问这个问题?
  • 因为我希望有更好的性能,但不想为此使用 ASM。
【解决方案2】:

您在第一个if 中测试i%2==0,然后在第二个if 中再次测试它,然后在第三个if 中测试i%2==1,然后在第四个中再次测试。如果您嵌套了 if 语句,那么您就不必继续测试,如果您知道 i%2 != 0,您可以推断它必须为 1,同样使用 j。

if(i%2==0){
   if(j%2==0){
   }else{
      // j%2 is pretty likely to be 1
   }
}else{
     // i%2 is pretty likely to be 1
}

事实上,您可以走得更远...如果 j 是您的行计数器,它不会在任何行中一直变化,因此您可以在每一行的开头进行一次测试,然后执行不同的测试根据您是在奇数行还是偶数行循环,而不测试每个像素的行索引。

【讨论】:

    【解决方案3】:

    交换循环,首先迭代高度。然后你可以在第二个循环之前计算 i * _width 并计算这 1 次而不是 _width 次。

    【讨论】:

    • 简单地交换循环的顺序将导致不同的输出,除非计数器也被修改,这可能会导致相同的复杂性。
    • @Amit 我认为交换循环的目的不是降低复杂性,而是使代码对缓存更友好。
    • @PaoloM 这种变化将如何使它对缓存更友好?您正在将一个地方的乘法和随机访问与另一个地方的乘法和随机访问交换。都是一样的。
    • @Amit 现在您的内存访问无处不在,因此您的缓存使用率几乎为零。如果您交换循环,您将访问附近的字节,以便缓存减少内存延迟。在这种情况下看到 20 倍的加速,我不会感到惊讶。
    • @NilsPipenbrinck - 首先我没有问这个问题,我只是质疑答案:-)。其次,我的观点是,这种方法用随机写入替换了随机读取,在这两种情况下,另一端都是串行的(随机读取 -> 串行写入与串行读取 -> 随机写入)。不过我想它仍在改进中。
    猜你喜欢
    • 2016-01-13
    • 1970-01-01
    • 2015-08-12
    • 2021-05-27
    • 2021-12-25
    • 1970-01-01
    • 2021-04-29
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多