Loading [MathJax]/jax/output/CommonHTML/config.js
前往小程序,Get更优阅读体验!
立即前往
首页
学习
活动
专区
圈层
工具
发布
首页
学习
活动
专区
圈层
工具
社区首页 >专栏 >移动端arm cpu优化学习笔记----一步步优化盒子滤波(Box Filter) 顶

移动端arm cpu优化学习笔记----一步步优化盒子滤波(Box Filter) 顶

作者头像
Ldpe2G
发布于 2019-05-14 07:19:29
发布于 2019-05-14 07:19:29
1.2K01
代码可运行
举报
运行总次数:1
代码可运行

            最近一段时间做比较多移动端开发相关的工作,感觉移动端优化相关的对我来说挺有趣的,

以前都是在PC上写代码,写代码的时候对于代码的性能没有过多的思考和感觉。但是在移动端上

写代码明显能察觉到一段代码写的好不好,对于在移动端上运行性能有很大的影响,尤其在一些

比较老旧的机型上测试更能有感觉。

           然后最近刚好在复现一篇论文,要在MXNet中实现类似盒子滤波(box filter)的操作子,其实

就是步长为1的sum pooling,盒子滤波算是很基础和经典的函数,但是在PC上实现的话因为有GPU

借助其强大的算力所以可以很暴力的实现,每个thread计算以某点为中心给定半径下的区域大小的和即可。

然后突发奇想想试试在移动端cpu上试试如何写高效的盒子滤波操作。

              这篇文章就是把我的实践过程记录下来,首先给出最简单的实现然后如何一步步优化,到最后给出

一个性能优化还不错的版本。由于我正式接触移动端优化的时间不长,很多东西理解的不深,所以有哪里

论述不正确的地方请读者指出。

本文的代码:BoxFilter

1.Boxfilter最简单实现

首先来看下Boxfilter最简单最直观的实现:

代码语言:javascript
代码运行次数:0
运行
AI代码解释
复制
void BoxFilter::filter(float *input, int radius, int height, int width, float *output) {
  for (int h = 0; h < height; ++h) {
    int height_sift = h * width;
    for (int w = 0; w < width; ++w) {
      int start_h = std::max(0, h - radius);
      int end_h = std::min(height - 1, h + radius);
      int start_w = std::max(0, w - radius);
      int end_w = std::min(width - 1, w + radius);

      float tmp = 0;
      for (int sh = start_h; sh <= end_h; ++sh) {
        for (int sw = start_w; sw <= end_w; ++ sw) {
          tmp += input[sh * width + sw];
        }
      }

      output[height_sift + w] = tmp;
    }
  }
}

对每个点,计算给定半径下区域的和,需要注意下边界的处理。

其时间复杂度是 O( height x width x (radius x 2 + 1) x (radius x 2 + 1) ),

这个最简单的实现在输入大小固定的情况下,半径越大耗时越大,有很多重复计算的地方,相邻元素

在计算各自区域内和的时候其实是有重叠的。然后第一个优化的思路就是boxfilter的计算是行列可

分离的,具体可参考[4]。

2.Boxfilter优化第一版

代码语言:javascript
代码运行次数:0
运行
AI代码解释
复制
void BoxFilter::fastFilter(float *input, int radius, int height, int width, float *output) {
  float *cachePtr = &(cache[0]);
  // sum horizonal
  for (int h = 0; h < height; ++h) {
    int sift = h * width;
    for (int w = 0; w < width; ++w) {
      int start_w = std::max(0, w - radius);
      int end_w = std::min(width - 1, w + radius);

      float tmp = 0;
      for (int sw = start_w; sw <= end_w; ++ sw) {
        tmp += input[sift + sw];
      }

      cachePtr[sift + w] = tmp;
    }
  }

  // sum vertical
  for (int h = 0; h < height; ++h) {
    int shift = h * width;
    int start_h = std::max(0, h - radius);
    int end_h = std::min(height - 1, h + radius);

    for (int sh = start_h; sh <= end_h; ++sh) {
      int out_shift = sh * width;
      for (int w = 0; w < width; ++w) {
        output[out_shift + w] += cachePtr[shift + w];
      }
    }
  }
}

所谓行列可分离就是,把行列分开计算,从代码里可以看到,对每个元素,首先计算行方向上半径

内的和,然后再计算列半径内的和,

所以这时候的时间复杂度是O(height x width x (radius x 2 + 1) x 2)。

可以看到行列分离之后,时间复杂度减少了不少,尤其半径越大减少的越多,但是还是有重复计

算的地方,而且在固定输入下时间复杂度还是会随半径的变大而变大。那么有没有方法可以使得计算

复杂度不受半径的影响呢,这个优化思路就是比如在算某一行每个点的半径区域内的和时,对于行开头

第一个点,首先计算其半径内和,然后对于接下来的点,不需要重新计算其半径区域内和,而是只需要

把前一个元素半径内的和,按半径窗口偏移之后减去旧的点和加上新加入的点即可。

3.Boxfilter优化第二版

代码语言:javascript
代码运行次数:0
运行
AI代码解释
复制
void BoxFilter::fastFilterV2(float *input, int radius, int height, int width, float *output) {
  float *cachePtr = &(cache[0]);
  // sum horizonal
  for (int h = 0; h < height; ++h) {
    int shift = h * width;

    float tmp = 0;
    for (int w = 0; w < radius; ++w) {
      tmp += input[shift + w];
    }

    for (int w = 0; w <= radius; ++w) {
      tmp += input[shift + w + radius];
      cachePtr[shift + w] = tmp;
    }

    int start = radius + 1;
    int end = width - 1 - radius;
    for (int w = start; w <= end; ++w) {
      tmp += input[shift + w + radius];
      tmp -= input[shift + w - radius - 1];
      cachePtr[shift + w] = tmp;
    }

    start = width - radius;
    for (int w = start; w < width; ++w) {
      tmp -= input[shift + w - radius - 1];
      cachePtr[shift + w] = tmp;
    }
  }

  float *colSumPtr = &(colSum[0]);
  for (int indexW = 0; indexW < width; ++indexW) {
    colSumPtr[indexW] = 0;
  } 
  // sum vertical
  for (int h = 0; h < radius; ++h) {
    int shift = h * width;
    for (int w = 0; w < width; ++w) {
      colSumPtr[w] += cachePtr[shift + w];
    }
  }

  for (int h = 0; h <= radius; ++h) {
    float *addPtr = cachePtr + (h + radius) * width;
    int shift = h * width;
    float *outPtr = output + shift; 
    for (int w = 0; w < width; ++w) {
      colSumPtr[w] += addPtr[w];
      outPtr[w] = colSumPtr[w];
    }
  }

  int start = radius + 1;
  int end = height - 1 - radius;
  for (int h = start; h <= end; ++h) {
    float *addPtr = cachePtr + (h + radius) * width;
    float *subPtr = cachePtr + (h - radius - 1) * width;
    int shift = h * width;
    float *outPtr = output + shift;
    for (int w = 0; w < width; ++w) {
      colSumPtr[w] += addPtr[w];
      colSumPtr[w] -= subPtr[w];
      outPtr[w] = colSumPtr[w];
    }
  }

  start = height - radius;
  for (int h = start; h < height; ++h) {
    float *subPtr = cachePtr + (h - radius - 1) * width;
    int shift = h * width;
    float *outPtr = output + shift; 
    for (int w = 0; w < width; ++w) {
      colSumPtr[w] -= subPtr[w];
      outPtr[w] = colSumPtr[w];
    }
  }
}

这一版时间复杂度大概是O(height x width x 4 ),不算边界只看中间部分的计算就是一次加法和一次减法,

行方向和列方向都一样。这里行方向的部分很好理解,因为边界部分需要特殊处理,比如开始部分只有加,

结尾部分只有减法,所以计算分成了3部分。列方向计算的话按照常规思路,那就是按一列列来处理,可是

我们知道数据一般是按照行来存储的,这样子跳行取数据,会造成很多次cache miss,这样子性能肯定会受

很大的影响,所以这里用了一个大小是width的向量colSum,来存储每一列对应点的半径区域内的和,

然后遍历的时候还是按照行来遍历,如果一下子理解不了这个思路的话,可以想象如果width为1的情况,

那么应该可以更好的理解。

然后我们来看下实验结果,这三版boxfilter在输入是2000x2000的情况下,在不同半径下的运行耗时,

测试手机是华为荣耀4C(CHM-TL00),每个函数运行10次取平均为其耗时:

可以看到第二版优化的耗时在不同半径下的表现都很稳定,基本不受影响。然后接下来的优化思路就是在

确定了C++ 的代码之后可以采用arm Neon Intrinsics来加速了,就是利用向量计算指令同时处理多个数据,

把独立的运算同时做,比写汇编要容易。

4.Boxfilter优化第二版 Neon Intrinsics

代码语言:javascript
代码运行次数:0
运行
AI代码解释
复制
  int n = width >> 2;
  int re = width - (n << 2);
  
  int start = radius + 1;
  int end = height - 1 - radius;
  for (int h = start; h <= end; ++h) {
    float *addPtr = cachePtr + (h + radius) * width;
    float *subPtr = cachePtr + (h - radius - 1) * width;
    int shift = h * width;
    float *outPtr = output + shift; 
    int indexW = 0;
    float *tmpOutPtr = outPtr;
    float *tmpColSumPtr = colSumPtr;
    float *tmpAddPtr = addPtr;
    float *tmpSubPtr = subPtr;

    int nn = n;
    int remain = re;
#if __ARM_NEON
    for (; nn > 0; nn--) {
      float32x4_t _add = vld1q_f32(tmpAddPtr);
      float32x4_t _sub = vld1q_f32(tmpSubPtr);
      float32x4_t _colSum = vld1q_f32(tmpColSumPtr);

      float32x4_t _tmp = vaddq_f32(_colSum, _add);
      _tmp = vsubq_f32(_tmp, _sub);

      vst1q_f32(tmpColSumPtr, _tmp);
      vst1q_f32(tmpOutPtr, _tmp);

      tmpAddPtr += 4;
      tmpSubPtr += 4;
      tmpColSumPtr += 4;
      tmpOutPtr += 4;
    }
#endif // __ARM_NEON
    for (; remain > 0; --remain) {
      *tmpColSumPtr += *tmpAddPtr;
      *tmpColSumPtr -= *tmpSubPtr;
      *tmpOutPtr = *tmpColSumPtr;
      tmpAddPtr ++;
      tmpColSumPtr ++;
      tmpOutPtr ++;
      tmpSubPtr ++;
    }
  }

上面的代码是截取列方向中间计算部分来展示如何使用arm Neon Intrinsics函数,

完整代码可以看

https://github.com/Ldpe2G/ArmNeonOptimization/blob/master/boxFilter/src/boxFilter.cpp#L143

而行方向是没办法并行的,因为相邻元素有依赖,而列方向则可以,所以在列方向上做改写。

以上代码其实挺好理解的,vld1q_f32指令就是加载4个浮点数,然后vaddq_f32,为把两个float32x4_t

向量相加,相当于同时计算了4个输出,然后再把结果用vst1q_f32存回去对应的地址,然后所有参与运算

的地址都是每次加4,具体可以参考官网文档

然后来看下这版优化的耗时如何:

可以看到耗时又少了一点,但是收益已经不大了。然后还想尝试进一步优化把Intrinsics部分改写

成内联汇编试试。

4.Boxfilter优化第二版 Neon Assembly

代码语言:javascript
代码运行次数:0
运行
AI代码解释
复制
  int n = width >> 2;
  int re = width - (n << 2);
  
  int start = radius + 1;
  int end = height - 1 - radius;
  for (int h = start; h <= end; ++h) {
    float *addPtr = cachePtr + (h + radius) * width;
    float *subPtr = cachePtr + (h - radius - 1) * width;
    int shift = h * width;
    float *outPtr = output + shift; 
    int indexW = 0;
    float *tmpOutPtr = outPtr;
    float *tmpColSumPtr = colSumPtr;
    float *tmpAddPtr = addPtr;
    float *tmpSubPtr = subPtr;

    int nn = n;
    int remain = re;
#if __ARM_NEON
    asm volatile(
      "0:                       \n"
      "vld1.s32 {d0-d1}, [%0]!  \n"
      "vld1.s32 {d2-d3}, [%1]!  \n"
      "vld1.s32 {d4-d5}, [%2]   \n"
      "vadd.f32 q4, q0, q2      \n"
      "vsub.f32 q3, q4, q1      \n"
      "vst1.s32 {d6-d7}, [%3]!  \n"
      "vst1.s32 {d6-d7}, [%2]!  \n"
      "subs %4, #1              \n"
      "bne  0b                  \n"
      : "=r"(tmpAddPtr), //
      "=r"(tmpSubPtr),
      "=r"(tmpColSumPtr),
      "=r"(tmpOutPtr),
      "=r"(nn)
      : "0"(tmpAddPtr),
      "1"(tmpSubPtr),
      "2"(tmpColSumPtr),
      "3"(tmpOutPtr),
      "4"(nn)
      : "cc", "memory", "q0", "q1", "q2", "q3", "q4"
    );

#endif // __ARM_NEON
    for (; remain > 0; --remain) {
      *tmpColSumPtr += *tmpAddPtr;
      *tmpColSumPtr -= *tmpSubPtr;
      *tmpOutPtr = *tmpColSumPtr;
      tmpAddPtr ++;
      tmpColSumPtr ++;
      tmpOutPtr ++;
      tmpSubPtr ++;
    }
  }

完整版代码:https://github.com/Ldpe2G/ArmNeonOptimization/blob/master/boxFilter/src/boxFilter.cpp#L331

这里我只对列计算中间部分做了改写,neon汇编下面的"cc","memory"之后跟的寄存器,是为了告诉编译器

(主要是q开头的,q和d是一样的,q表示128位向量寄存器(16个),d表示64位(32个),q0 =(d0 + d1)),

这些寄存器会在汇编内被用到,然后编译器在进入这段代码之前,要缓存这些寄存器的内容,然后在离开这段汇编

之后恢复原来的值。一定要记得写上用了哪些向量寄存器。 汇编指令其实和intrinsics函数有对应的具体可参考

官方文档. 然后我们来看下耗时:

什么鬼,竟然还慢了,一定是我使用的方式不对。去查了下资料,看到这篇博客里面提到,指令vld和vst都是需要

消耗两个时钟周期,其他指令基本都是一个时钟周期,但是却不意味着一个时钟周期之后能立刻得到结果。那么

看下来vsub.f32 指令依赖 vadd.f32 的结果,所以白白浪费了不少时钟周期。而且现代的处理器支持双发射流水线

也就意味着CPU可以同时执行两条数据无关指令,那么能否利用这点来更进一步加速呢。

5.Boxfilter优化第二版 Neon Assembly 第二版

代码语言:javascript
代码运行次数:0
运行
AI代码解释
复制
  int start = radius + 1;
  int end = height - 1 - radius;
  for (int h = start; h <= end; ++h) {
    float *addPtr = cachePtr + (h + radius) * width;
    float *subPtr = cachePtr + (h - radius - 1) * width;
    int shift = h * width;
    float *outPtr = output + shift; 
    int indexW = 0;
    float *tmpOutPtr = outPtr;
    float *tmpColSumPtr = colSumPtr;
    float *tmpAddPtr = addPtr;
    float *tmpSubPtr = subPtr;

    int nn = width >> 3;
    int remain = width - (nn << 3);
#if __ARM_NEON
    asm volatile(
      "0:                       \n"
      "pld      [%0, #256]      \n"
      "vld1.s32 {d0-d3}, [%0]!  \n"
      "pld      [%2, #256]      \n"
      "vld1.s32 {d8-d11}, [%2]  \n"

      "vadd.f32 q6, q0, q4      \n"

      "pld      [%1, #256]      \n"
      "vld1.s32 {d4-d7}, [%1]!  \n"
      
      "vadd.f32 q7, q1, q5      \n"
      
      "vsub.f32 q6, q6, q2      \n"
      
      "vsub.f32 q7, q7, q3      \n"
      
      "vst1.s32 {d12-d15}, [%3]!  \n"
      
      "vst1.s32 {d16-d19}, [%2]!  \n"

      "subs %4, #1              \n"
      "bne  0b                  \n"
      : "=r"(tmpAddPtr), //
      "=r"(tmpSubPtr),
      "=r"(tmpColSumPtr),
      "=r"(tmpOutPtr),
      "=r"(nn)
      : "0"(tmpAddPtr),
      "1"(tmpSubPtr),
      "2"(tmpColSumPtr),
      "3"(tmpOutPtr),
      "4"(nn)
      : "cc", "memory", "q0", "q1", "q2", "q3", "q4", "q5", "q6", "q7", "q8", "q9"
    );

#endif // __ARM_NEON
    for (; remain > 0; --remain) {
      *tmpColSumPtr += *tmpAddPtr;
      *tmpColSumPtr -= *tmpSubPtr;
      *tmpOutPtr = *tmpColSumPtr;
      tmpAddPtr ++;
      tmpColSumPtr ++;
      tmpOutPtr ++;
      tmpSubPtr ++;
    }
  }

完整版代码:

https://github.com/Ldpe2G/ArmNeonOptimization/blob/master/boxFilter/src/boxFilter.cpp#L527

可以看到这里的改进思路就是,把两条 vadd.f32 指令放一起,然后跟两条vsub.f32,然后把加载 vsub.f32 要用

到部分数据指令 vld1.s32 放到两个 vadd.f32之间,同时 vld1.s32 指令之前加上 pld 指令,这个指令为什么能加速

我问了下做移动端优化的同事,pld把数据从内存加载到cache然后下一条指令把数据从 cache加载到寄存器,

如果不用pld,数据若不在cache中,那么就是需要直接从内存加载到寄存器,这里会比前者慢很多。

然后我们来看下最终版的耗时:

看表格最终版的耗时比起最原始的实现至少可以加速6~7倍,肯定是还有更好的优化方式,比如如果能对输入做

量化把float类型数据转成8bit整型,那么就可以在单位时间处理更多数据,当然量化到8bit上计算溢出的风险也会

增大许多。有时候炼丹炼久了,学习下优化也挺好玩的,感觉可以很好的锻炼下思维和代码能力,现在深度学习

移动端应用越来越广泛,训出来的模型如果部署到移动端之后运行的效率很低那么也是白费功夫。所以感觉对移动端

优化有一定的了解对于如何设计对移动端更友好的模型还是有帮助的。

相关资料:

[1]小鱼干:ARM NEON 优化

[2] https://azeria-labs.com/writing-arm-assembly-part-1/​azeria-labs.com

[3]http://armneon.blogspot.com/2013/07/neon-tutorial-part-1-simple-function_13.html

[4]解析opencv中Box Filter的实现并提出进一步加速的方案(源码共享)

[5]ARM Information Center

本文参与 腾讯云自媒体同步曝光计划,分享自作者个人站点/博客。
如有侵权请联系 cloudcommunity@tencent.com 删除

本文分享自 作者个人站点/博客 前往查看

如有侵权,请联系 cloudcommunity@tencent.com 删除。

本文参与 腾讯云自媒体同步曝光计划  ,欢迎热爱写作的你一起参与!

评论
登录后参与评论
暂无评论
推荐阅读
编辑精选文章
换一批
【AI移动端算法优化】二,移动端arm cpu优化学习笔记之一步步优化盒子滤波
最近一段时间做比较多移动端开发相关的工作,以前在PC上写代码的时候对于性能没有过多的思考和感觉。但是在移动端上写代码明显能察觉到一段代码写的好不好,对于在移动端上运行性能有很大的影响,尤其在一些比较老旧的机型上测试更有感觉。
BBuf
2020/03/19
9490
一份朴实无华的移动端盒子滤波算法优化笔记
这是我自己做的移动端算法优化笔记的第一篇文章。我入门移动端的时间其实很短,也是今年刚开始接触Neon优化并尝试用Neon来做一些算法加速工作,之前我做过系列的X86上的SSE/AVX算法加速文章分享。但那个系列已经比较久没有更新了,一是因为我日常做的都是和移动端相关的一些算法部署工作,二是因为我变懒了,所以希望新开这个专题重新找到一点分享算法优化文章的热情(笑)。关于盒子滤波这个算法的移动端优化,梁德澎作者已经有分享过一篇很优秀的文章了,即【AI移动端算法优化】二,移动端arm cpu优化学习笔记之一步步优化盒子滤波 ,所以你可能会在我的这篇文章看到很多的优化技巧已经被他讲过了,但这篇文章仍然有我自己大量的思考以及花了大量写出对应的优化代码,我接触了哪些资料或者说学习了哪些知识,我都有列举到,所以对移动端优化感兴趣的小白还是值得看看的。代码开源在https://github.com/BBuf/ArmNeonOptimization 。
BBuf
2020/08/10
1.5K0
基于NCNN的3x3可分离卷积再思考盒子滤波
盒子滤波,并获得了笔者最近关于盒子滤波的优化实验的最快速度,即相对于原始实现有37倍加速,希望对做工程部署或者算法优化的读者有一定启发。代码链接:https://github.com/BBuf/ArmNeonOptimization ❞
BBuf
2020/08/14
9290
在TensorFlow+Keras环境下使用RoI池化一步步实现注意力机制
在本文中,作者解释了感兴趣区域池化(RoI 池化)的基本概念和一般用法,以及如何使用它来实现注意力机制。他一步步给出了在 Keras 和 TensorFlow 环境下使用 RoI 池化的实现。
机器之心
2019/05/15
9700
在TensorFlow+Keras环境下使用RoI池化一步步实现注意力机制
如何阅读一个前向推理框架?以NCNN为例。
CNN从15年的ResNet在ImageNet比赛中大放异彩,到今天各种层出不穷的网络结构被提出以解决生活中碰到的各种问题。然而,在CNN长期发展过程中,也伴随着很多的挑战,比如如何调整算法使得在特定场景或者说数据集上取得最好的精度,如何将学术界出色的算法落地到工业界,如何设计出在边缘端或者有限硬件条件下的定制化CNN等。前两天看到腾讯优图的文章:腾讯优图开源这三年 ,里面提到了NCNN背后的故事,十分感动和佩服,然后我也是白嫖了很多NCNN的算法实现以及一些调优技巧。所以为了让很多不太了解NCNN的人能更好的理解腾讯优图这个"从0到1"的深度学习框架,我将结合我自己擅长的东西来介绍我眼中的NCNN它是什么样的?
BBuf
2020/12/23
2K0
移动端arm cpu优化学习笔记第2弹--常量阶时间复杂度中值滤波
https://github.com/Ldpe2G/ArmNeonOptimization/tree/master/ConstantTimeMedianFilter
Ldpe2G
2019/10/24
1.2K0
移动端arm cpu优化学习笔记第2弹--常量阶时间复杂度中值滤波
gis如何无缝拼接两张图_arcgis多幅影像图拼接
#include “opencv2/features2d/features2d.hpp” #include “opencv2/highgui/highgui.hpp” #include “opencv2/opencv_modules.hpp” #include “opencv2/calib3d/calib3d.hpp”
全栈程序员站长
2022/11/09
6790
移动端arm cpu优化学习笔记第4弹--内联汇编入门
本文主要内容是介绍ARMv7和v8内联汇编的一些基础知识,并且会结合两个具体例子去看下如何用内联汇编来改写原来的代码。
Ldpe2G
2020/05/31
3.1K0
NEON优化
这几个星期在实验室里的任务是对OpenCV源码里某部分代码使用NEON指令集进行优化,在实际操作的过程中对OpenCV环境的配置、NEON指令集、OpenCV源码都有了一定的理解,在这里将所学到的知识分享出来。
ttony0
2022/12/26
2.3K0
NEON优化
一段NEON代码
#include <iostream> #include <arm_neon.h> //需包含的头文件 using namespace std; float sum_array(float *arr, int len) { if (NULL == arr || len < 1) { cout << "input error\n"; return 0; } int dim4 = len >> 2; //
轻舞飞扬SR
2021/09/23
1.1K0
道阻且长_再探矩阵乘法优化
【GiantPandaCV导语】本文记录了笔者最近的一些优化gemm的思路和实现,这些思路大多是公开的方案,例如来自how-to-optimize-gemm工程的一些优化手段,来自ncnn的一些优化手段等。最终,笔者目前实现的版本在armv7a上可以达到50%左右的硬件利用率(这个利用率的确还不高,笔者也是一步步学习和尝试,大佬轻喷),本文记录了这些思路以及核心实现方法。改好的行主序代码(x86+armv7a版本)可以直接访问https://github.com/BBuf/how-to-optimize-gemm获取。
BBuf
2020/12/08
6570
道阻且长_再探矩阵乘法优化
基于粒子滤波的物体跟踪
一直都觉得粒子滤波是个挺牛的东西,每次试图看文献都被复杂的数学符号搞得看不下去。一个偶然的机会发现了Rob Hess(http://web.engr.oregonstate.edu/~hess/)实现的这个粒子滤波。从代码入手,一下子就明白了粒子滤波的原理。根据维基百科上对粒子滤波的介绍(http://en.wikipedia.org/wiki/Particle_filter),粒子滤波其实有很多变种,Rob Hess实现的这种应该是最基本的一种,Sampling Importance Resampling (SIR),根据重要性重采样。下面是我对粒子滤波实现物体跟踪的算法原理的粗浅理解:
MachineLP
2022/05/09
6490
基于粒子滤波的物体跟踪
【STM32H7的DSP教程】第12章 DSP基础函数-相反数,偏移,移位,减法和比例因子
完整版教程下载地址:http://www.armbbs.cn/forum.php?mod=viewthread&tid=94547 第12章       DSP基础函数-相反数,偏移,移位,减法和比例
Simon223
2020/04/22
1.7K0
CVPR论文《100+ Times Faster Weighted Median Filter (WMF)》的实现和解析
【GiantPandaCV导语】由于太硬核,小编已经写不出来导语了。 请直接阅读正文。本文首发于博客园https://www.cnblogs.com/Imageshop/p/9934670.html,然后ImageShop博主授权本公众号以他原创名义发布本篇文章,请勿恶意点举报,谢谢合作。
BBuf
2021/01/08
9910
【STM32H7的DSP教程】第6章 ARM DSP源码和库移植方法(MDK5的AC5和AC6)
完整版教程下载地址:http://www.armbbs.cn/forum.php?mod=viewthread&tid=94547 第6章   ARM DSP源码和库移植方法(MDK5的AC5和AC6
Simon223
2020/04/08
1.7K0
【AI PC端算法优化】四,一步步将Sobel边缘检测加速22倍
继续优化技术的探索,今天以一个的Sobel算子进行边缘检测的算法为例来看看如何使用SSE指令集对其进行优化。
BBuf
2020/04/20
1.5K0
【AI PC端算法优化】四,一步步将Sobel边缘检测加速22倍
【AI PC端算法优化】七,一步步优化RGB和YUV互转算法
继续学习指令集优化的知识,今天来讨论一个图像颜色空间转换经常碰到的一个问题即RGB和YUV图像的颜色空间转换,我们从原理介绍和普通实现开始,然后介绍一些优化方法并引入SSE指令集来优化这个算法的速度。
BBuf
2020/05/21
1.8K0
【AI PC端算法优化】七,一步步优化RGB和YUV互转算法
一步步构建卷积模型
在这个编程练习中,我们将使用numpy实现卷积(CONV)层和池化(POOL)层。
云水木石
2019/07/01
5730
一步步构建卷积模型
GPU编程(三): CPU与GPU的矩阵乘法对比
前言 在上一篇的最后, 我提到了一个矩阵乘法, 这次与CPU进行对比, 从中可以很明显GPU在并行计算上的优势. ---- 计时函数 在贴出代码之前, 来看下我常用的计时函数, 可以精确到微秒级. 首先头文件是#include<sys/time.h>. 结构体为: struct timeval{ long tv_sec; /*秒*/ long tv_usec; /*微秒*/ }; 来看下使用的小栗子: struct timeval start, end; double t
sean_yang
2019/01/28
1.7K0
GPU编程(三): CPU与GPU的矩阵乘法对比
【STM32F407的DSP教程】第7章 ARM DSP源码和库移植方法(IAR8)
完整版教程下载地址:http://www.armbbs.cn/forum.php?mod=viewthread&tid=94547 第7章   ARM DSP源码和库移植方法(IAR8) 本期教程主要
Simon223
2020/04/09
1.3K0
【STM32F407的DSP教程】第7章   ARM DSP源码和库移植方法(IAR8)
推荐阅读
相关推荐
【AI移动端算法优化】二,移动端arm cpu优化学习笔记之一步步优化盒子滤波
更多 >
领券
问题归档专栏文章快讯文章归档关键词归档开发者手册归档开发者手册 Section 归档
查看详情【社区公告】 技术创作特训营有奖征文