integral函数Opencv源码理解-leetcode动态规划的使用场景

news/2024/7/7 19:36:22

前言

            Opencv有一个integral()函数,也就是积分图算法。有三种积分图类型,求和(sum),求平方和(sqsum),求旋转45°和(titled)。根据名字可知道,前两个是统计输出每个坐标的左上方像素和、左上方像素平方和,旋转45°的积分图则是统计每个像素点左上方45°到右上方45°区域的像素和。

        作为一名合格的算法工程师,肯定都有leetcode的刷题经验,leetcode上的经典动态规划类型题目,可以完美的在integral函数的代码中体现。

        通过本文,你会知道积分图的sum, sqsum, titled的使用和实现细节。看到动态规划中时间复杂度和内存优化的思想是如何被一步步设计的。

integral()函数的介绍

        积分图是数字图像处理中常用的一种方法,通常能够很大程度的加速计算过程。其思想是,当需要统计一张图像中矩形区域的像素和时候,如果我们之前统计过该图像的积分图结果,就可以直接调用积分图数据,做两次加法两次减法就能得到结果了,而不用再遍历这个图像的所有像素点。

        如下图,假设积分图用summ表示, ABCD区域的像素和用S表示,则S=summ[A] + summ[D]-summ[C]-summ[B]。粉色标记区域就是点A的累计像素和区域,分别对应了sum, sqsum积分图和titled积分图。

        另外,使用积分图时候,一般会给一个左上角的点A(x,y, h,w), 其对应的矩形B,C,D的坐标关系,在sum,titled里具有不一样的含义。比如在opencv 文档里的示例,一个正常的矩形 Rect(4,4,3,2) 和一个倾斜45度的矩形Rect(5,1,2,3)。其坐标对应关系如下,请注意stright和titled矩形的P0,P1,P2,P3并非一一对应,只有同一张图里的P0,P1,P2,P3四个点的相对位置是对应的,这里可能会给人造成误解。

        integral积分图有三种构建方式,定义如下。前两种sum,sqsum比如可以用在相似度匹配算法NCC, 第三种titled常用在人脸检测算法的Harr特征提取场景。

 integral的代码实现设计

        由定义可知,sum, sqsum两种积分图除了累加定义不同,其实现思路是一样的。为了描述方便,后面代码设计主要以sum,title两种方式为典型进行分析。

sum模式的代码实现设计

        把一张图按照从左到右,从上到下的顺序遍历,sum是以统计每个像素点的之前的像素值累计之和,那么如果没有优化思想,每个输出像素点都要重新从头开始遍历图像并累加和,那么时间复杂度就是O(H *W *(1+2+3+...+H*W)≈O(H*W)^2,实际上完全没有必要。

        根据定义,我们可以发现当前像素点的积分结果与其左边和上边一个是有关系的!利用这个关系,可以重复利用之前统计过的像素值,而不用重新统计新坐标位置之前的像素和了,实现“看一遍”就出结果的效果。

        如下图,假设原图用src表示, sum表示积分图结果。现在要计算p(x,y)的积分结果,可以有两种实现设计,其时间复杂度都是O(H*W), 只是空间复杂度不同。

设计一(左边):

需要一个额外的空间数组Buf,长度和图像宽w相同。 buf[i]表示该列累计到i的像素和。

sum(x,y)=sum(x-1,y) +Buf(x-1)+src(x,y)

设计二(右边):

需要一个额外空间变量Sx,表示该行累计到x坐标之前的像素和。

sum(x,y)=sum(x,y-1)+Sx+src(x,y)

titled模式的代码实现设计

        同样,我们也可以观察到titled模式的积分统计也是有规律的,当前像素的结果与其周边的几个点也有关系。最终也有两种实现设计方式。

        我们同样用src指代原图,用titled指代旋转45度的积分图结果。

设计一(左边):

需要一个额外的空间数组Buf,长度和图像宽w相同。 buf[i]表示到第i行,其右斜对角的像素和。

titled(x,y)= titled(x-1,y-1)+Buf(x)+Buf(x+1)+src(x,y)

设计二(右边):

titled(x,y)= titled(x-1,y-1)+ titled(x+1,y-1)- titled(x,y-2)+src(x,y)+src(x,y-1)

        另外对于titled模式,请注意,上面示意的只是中间位置像素点的计算,当第一行,或者第一列,最后一列时候的处理关系如下,上面两种设计对这种边界的处理是相同的。

  • titled(x,y)=src(x,y),   if y=0
  • titled(x,y)=titled(x+1,y-1)+src(x,y)+src(x,y-1),  if x=0
  • titled(x,y)=titled(x-1,y-1)+src(x,y)+src(x,y-1),  if x=w-1

小结

        对于sum, titled,opencv的实现分别采用了设计2,设计1。并且为了使计算代码更简洁,在实现时候,sum,titled的内存分配都会比src多一行和一列。最终的结果在去掉这一行一列。

        至此,将integral积分图的代码实现流程已经分析完毕,sum, sqsum的计算比较容易复现,titled模式下,对维护每行斜对角累计像素和的Buf更新,有点不太容易理解。后面如果大家有需求,欢迎留言,再把我写的python实现代码贴一下,今天就到这里啦。

        整体代码实现的设计思路就是用到了动态规划的思想,想想自己之前刷过的题,感觉好亲切啊,有没有?


http://lihuaxi.xjx100.cn/news/170956.html

相关文章

HTML小游戏11 —— 横版恐龙大冒险游戏(附完整源码)

💂 网站推荐:【神级源码资源网】【摸鱼小游戏】🤟 前端学习课程:👉【28个案例趣学前端】【400个JS面试题】💅 想寻找共同学习交流、摸鱼划水的小伙伴,请点击【摸鱼学习交流群】💬 免费且实用的计…

Hive 之拉链表

文章目录什么是拉链表?如何实现拉链?拉链表实现示例什么是拉链表? 一张存储历史数据的表,记录数据由 “生” 到 “死” 的过程,用于处理缓慢变化维。 好处是拉链表可以保存每条数据的所有历史记录,轨迹十…

【C++】string的使用

文章目录一、前言二、标准库中的string类三. string类的常用接口1. 构造函数2. 容量操作3. 访问遍历4. 修改操作5. 其他操作一、前言 C语言中,字符串是以\0结尾的一些字符的集合,为了操作方便,C标准库中提供了一些str系列的库函数&#xff0…

Oracle表空间、用户详解

目录新建连接三者关系表空间创建表空间修改表空间和数据文件修改数据文件容量新增表空间的数据文件重命名数据文件修改表空间状态修改数据文件状态删除表空间查询用户创建删除查询修改新建连接 工具选择: 我们一般会选择一个工具来连接本地的Oracle,而我…

JavaEE——Servlet中的session

之前的博客中提到,cookie是为了浏览器能够在本地保存数据而产生的机制,是在浏览器工作的。而session则是与之对应的,在客户端工作的。一个服务器对应多个客户端,每个客户端都有自己的session,以sessionId为key&#xf…

2.19 emoji符号大全【玩赚小红书】

知乎无法显示全部表情符号,大家可以参考这个网站:🤣 Emoji表情大全,颜文字百科 💌 😃💁表情符号 😀 😃 😄 😁 😆 😅 &…

PySide创建界面关联项目(五) 百篇文章学PyQT

本文章是百篇文章学PyQT的第五篇,本文讲述如何使用PySide创建UI界面,并且关联入PyCharm 新建的项目中成功运行第一个PyQT程序,博主在本篇文章中将遇到和踩过的坑总结出来,可以供大家参考,希望大家安装顺利。包括 安装、…

STM32F407 电机编码器测量

文章目录一、STM32F407 定时器编码器功能1.1 STM32定时器简介1.2 STM32定时器编码器功能二、带编码器的直流电机三、代码与验证3.1 初始化代码3.2 验证一、STM32F407 定时器编码器功能 1.1 STM32定时器简介 STM32的定时器功能非常强大,根据官方手册,定…