opencv中的exp32f函数
exp32f
opencv的exp函数和cmath的exp函数在精度上存在一定差异,通过查找源码,发现了这么一段实现。代码如下:
点击查看代码
#define EXPTAB_SCALE 6
#define EXPTAB_MASK ((1 << EXPTAB_SCALE) - 1)
#define EXPPOLY_32F_A0 .9670371139572337719125840413672004409288e-2
static const double expTab[EXPTAB_MASK + 1] = {
1.0 * EXPPOLY_32F_A0,
1.0108892860517004600204097905619 * EXPPOLY_32F_A0,
1.0218971486541166782344801347833 * EXPPOLY_32F_A0,
1.0330248790212284225001082839705 * EXPPOLY_32F_A0,
1.0442737824274138403219664787399 * EXPPOLY_32F_A0,
1.0556451783605571588083413251529 * EXPPOLY_32F_A0,
1.0671404006768236181695211209928 * EXPPOLY_32F_A0,
1.0787607977571197937406800374385 * EXPPOLY_32F_A0,
1.0905077326652576592070106557607 * EXPPOLY_32F_A0,
1.1023825833078409435564142094256 * EXPPOLY_32F_A0,
1.1143867425958925363088129569196 * EXPPOLY_32F_A0,
1.126521618608241899794798643787 * EXPPOLY_32F_A0,
1.1387886347566916537038302838415 * EXPPOLY_32F_A0,
1.151189229952982705817759635202 * EXPPOLY_32F_A0,
1.1637248587775775138135735990922 * EXPPOLY_32F_A0,
1.1763969916502812762846457284838 * EXPPOLY_32F_A0,
1.1892071150027210667174999705605 * EXPPOLY_32F_A0,
1.2021567314527031420963969574978 * EXPPOLY_32F_A0,
1.2152473599804688781165202513388 * EXPPOLY_32F_A0,
1.2284805361068700056940089577928 * EXPPOLY_32F_A0,
1.2418578120734840485936774687266 * EXPPOLY_32F_A0,
1.2553807570246910895793906574423 * EXPPOLY_32F_A0,
1.2690509571917332225544190810323 * EXPPOLY_32F_A0,
1.2828700160787782807266697810215 * EXPPOLY_32F_A0,
1.2968395546510096659337541177925 * EXPPOLY_32F_A0,
1.3109612115247643419229917863308 * EXPPOLY_32F_A0,
1.3252366431597412946295370954987 * EXPPOLY_32F_A0,
1.3396675240533030053600306697244 * EXPPOLY_32F_A0,
1.3542555469368927282980147401407 * EXPPOLY_32F_A0,
1.3690024229745906119296011329822 * EXPPOLY_32F_A0,
1.3839098819638319548726595272652 * EXPPOLY_32F_A0,
1.3989796725383111402095281367152 * EXPPOLY_32F_A0,
1.4142135623730950488016887242097 * EXPPOLY_32F_A0,
1.4296133383919700112350657782751 * EXPPOLY_32F_A0,
1.4451808069770466200370062414717 * EXPPOLY_32F_A0,
1.4609177941806469886513028903106 * EXPPOLY_32F_A0,
1.476826145939499311386907480374 * EXPPOLY_32F_A0,
1.4929077282912648492006435314867 * EXPPOLY_32F_A0,
1.5091644275934227397660195510332 * EXPPOLY_32F_A0,
1.5255981507445383068512536895169 * EXPPOLY_32F_A0,
1.5422108254079408236122918620907 * EXPPOLY_32F_A0,
1.5590044002378369670337280894749 * EXPPOLY_32F_A0,
1.5759808451078864864552701601819 * EXPPOLY_32F_A0,
1.5931421513422668979372486431191 * EXPPOLY_32F_A0,
1.6104903319492543081795206673574 * EXPPOLY_32F_A0,
1.628027421857347766848218522014 * EXPPOLY_32F_A0,
1.6457554781539648445187567247258 * EXPPOLY_32F_A0,
1.6636765803267364350463364569764 * EXPPOLY_32F_A0,
1.6817928305074290860622509524664 * EXPPOLY_32F_A0,
1.7001063537185234695013625734975 * EXPPOLY_32F_A0,
1.7186192981224779156293443764563 * EXPPOLY_32F_A0,
1.7373338352737062489942020818722 * EXPPOLY_32F_A0,
1.7562521603732994831121606193753 * EXPPOLY_32F_A0,
1.7753764925265212525505592001993 * EXPPOLY_32F_A0,
1.7947090750031071864277032421278 * EXPPOLY_32F_A0,
1.8142521755003987562498346003623 * EXPPOLY_32F_A0,
1.8340080864093424634870831895883 * EXPPOLY_32F_A0,
1.8539791250833855683924530703377 * EXPPOLY_32F_A0,
1.8741676341102999013299989499544 * EXPPOLY_32F_A0,
1.8945759815869656413402186534269 * EXPPOLY_32F_A0,
1.9152065613971472938726112702958 * EXPPOLY_32F_A0,
1.9360617934922944505980559045667 * EXPPOLY_32F_A0,
1.9571441241754002690183222516269 * EXPPOLY_32F_A0,
1.9784560263879509682582499181312 * EXPPOLY_32F_A0,
};
static const double exp_prescale = 1.4426950408889634073599246810019 * (1 << EXPTAB_SCALE);
static const double exp_postscale = 1./(1 << EXPTAB_SCALE);
static const double exp_max_val = 3000.*(1 << EXPTAB_SCALE); // log10(DBL_MAX) < 3000
const double* getExpTab64f()
{
return expTab;
}
const float* getExpTab32f()
{
static float expTab_f[EXPTAB_MASK+1];
static std::atomic<bool> expTab_f_initialized(false);
if (!expTab_f_initialized.load())
{
for( int j = 0; j <= EXPTAB_MASK; j++ )
expTab_f[j] = (float)expTab[j];
expTab_f_initialized = true;
}
return expTab_f;
}
void exp32f( const float *_x, float *y, int n )
{
const float* const expTab_f = getExpTab32f();
const float
A4 = (float)(1.000000000000002438532970795181890933776 / EXPPOLY_32F_A0),
A3 = (float)(.6931471805521448196800669615864773144641 / EXPPOLY_32F_A0),
A2 = (float)(.2402265109513301490103372422686535526573 / EXPPOLY_32F_A0),
A1 = (float)(.5550339366753125211915322047004666939128e-1 / EXPPOLY_32F_A0);
int i = 0;
const Cv32suf* x = (const Cv32suf*)_x;
float minval = (float)(-exp_max_val/exp_prescale);
float maxval = (float)(exp_max_val/exp_prescale);
float postscale = (float)exp_postscale;
for( ; i < n; i++ )
{
float x0 = x[i].f;
x0 = std::min(std::max(x0, minval), maxval);
x0 *= (float)exp_prescale;
Cv32suf buf;
int xi = cv::saturate_cast<int>(x0);
x0 = (x0 - xi)*postscale;
int t = (xi >> EXPTAB_SCALE) + 127;
t = !(t & ~255) ? t : t < 0 ? 0 : 255;
buf.i = t << 23;
y[i] = buf.f * expTab_f[xi & EXPTAB_MASK] * ((((x0 + A1)*x0 + A2)*x0 + A3)*x0 + A4);
}
}
这是从opencv4源码中摘出的一段计算\(e^x\)的函数(32位浮点)。初看起来可能有些迷糊,但仔细思考和推导之后,稍微明白了一些。想要理解这段代码,主要有三点需要清楚:第一点,从计算\(e^x\)转换到计算\(2^x\);第二点,使用四阶泰勒展开逼近\(2^x\); 第三点,expTab_f
的作用。
从\(e^x\)转到\(2^x\)。这个比较简单,对输入\(x\)预先乘以\(log_2(e)\)就可以了,exp_prescale
就是起这样的作用(这里预先左移了6位,相当于乘以64,这个先不管)。转换之后对\(x\)进行了整数和小数部分的分离,整数部分为xi
,小数部分为x0
。对于整数部分,按照IEEE754浮点数的表示方式,可以直接移动到解码部分。
int t = (xi >> EXPTAB_SCALE) + 127;
t = !(t & ~255) ? t : t < 0 ? 0 : 255;
buf.i = t << 23;
这个时候整数部分已经计算完成(即buf.f
,这里用了union的存储方式,方便机器数级别的浮点和整数的转换),剩下只要计算小数部分,即\(2^{x0}\)。
y[i] = buf.f * expTab_f[xi & EXPTAB_MASK] * ((((x0 + A1)x0 + A2)x0 + A3)*x0 + A4);
小数部分的计算不是很直接,这里用到了级数近似的方式。
\(2^x\)的多项式展开。对\(2^x\)进行泰勒展开可以得到
\(2^x = 1 + xln2 + \frac{x^2ln^{2}\ 2}{2} + \frac{x^3ln^{3}\ 2}{3!} + \frac{x^4ln^{4}\ 2}{4!} + \cdots\)
\(A1 = \frac{ln^{3}\ 2}{3!A0},\)
\(A2 = \frac{ln^{2}\ 2}{2!A0},\)
\(A3 = \frac{ln2}{A0},\)
\(A4 = \frac{1}{A0}.\)
将以上代入((((x0 + A1)*x0 + A2)*x0 + A3)*x0 + A4)
可以得到\(2^x\)的四阶多项式展开,不过每项系数都多除以了一个A0
。这时候就需要expTab_f
了。
expTab_f。expTab_f
有64项,其作用除了消去\(2^x\)多项式展开中的A0
,还有就是乘上之前忽略的小数部分。一开始,输入x乘以64,相当于小数点向右移动了6位,因而有6位小数移动到了整数部分(按照二进制表示来考虑),不过在计算整数阶码时没有考虑这部分,所以之后需要把这6位小数重新乘上去,根据整数部分低6位的值计算对应的小数部分,这就是expTab_f
所表示的内容。比如整数的低6位为1时,表示小数\(2^{1/64}=1.0108892860517005\),低6位为3时,表示小数\(2^{1/32+1/64}=1.0330248790212284\),低6位为63时(所有位为1),表示小数\(2^{1-1/64}=1.978456026387951\)。
一些疑问
至此,这段代码基本就搞清楚了。对此,还有几个问题:第一,为什么需要预先左移6位,从运算角度考虑并没有起到什么实质作用;第二,为什么\(2^x\)多项式展开的系数都要除以A0
,如果不除会有什么不同。对于第一个问题,我猜可能是精度考虑,移动到整数部分之后,这几位的小数精度可以保证,而且移动的位数越多,近似的精度越高。对于第二个问题,我猜也可能跟精度有关,但并不太清楚是怎样影响的。
opencv中的exp32f函数的更多相关文章
- OpenCV中的绘图函数-OpenCV步步精深
OpenCV 中的绘图函数 画线 首先要为画的线创造出环境,就要生成一个空的黑底图像 img=np.zeros((512,512,3), np.uint8) 这是黑色的底,我们的画布,我把窗口名叫做i ...
- OpenCV中的新函数connectedComponentsWithStats使用
主要内容:对比新旧函数,用于过滤原始图像中轮廓分析后较小的区域,留下较大区域. 关键字 :connectedComponentsWithStats 在以前,常用的方法是"是先调用 cv ...
- 5、opencv中的绘图函数
1.目标 a.学习使用 OpenCV 绘制不同几何图形 b. 你将会学习到这些函数: cv2.line(), cv2.circle(), cv2.rectangle(),cv2.ellipse(),c ...
- OpenCV中的绘图函数
OpenCV可以用来绘制不同的集合图形,包括直线,矩形,圆,椭圆,多边形以及在图片上添加文字.用到的绘图函数包括 cv2.line(),cv2.circle(),cv2.rectangle() ,cv ...
- 【计算机视觉】OpenCV中直方图处理函数简述
计算直方图calcHist 直方图是对数据集合的统计 ,并将统计结果分布于一系列提前定义的bins中.这里的数据不只指的是灰度值 ,统计数据可能是不论什么能有效描写叙述图像的特征. 如果有一个矩阵包括 ...
- Opencv中的阈值函数
OpenCV基础——threshold函数的使用 图像的二值化就是将图像上的像素点的灰度值设置为0或255,这样将使整个图像呈现出明显的黑白效果. 参数原型 参数说明 src:源图像,可以为8位的灰度 ...
- OpenCV中的常用函数
1.cvLoadImage:将图像文件加载至内存: 2.cvNamedWindow:在屏幕上创建一个窗口: 3.cvShowImage:在一个已创建好的窗口中显示图像: 4.cvWaitKey:使程序 ...
- opencv中的缩放函数
图像处理里面缩放操作是比较常见的: 最近邻插值:类似简单映射的处理方式,目标图像w1,h1,原始图像w0,h0,则在目标图像上的点(x,y)的像素点实际对应原始图上(x*w0/w1,y*h0/h1)的 ...
- opencv中自适应阈值函数的实现(c++)
根据<面向飞机蒙皮接缝的线结构光检测技术研究_张卡>论文中的原理,编写了自适应阈值函数 原理: //计算灰度最大最小值 void MaxGrayValue(Mat image,int &a ...
随机推荐
- redhat9 linux 网卡无法激活排障
先查看自己的系统版本! [root@localhost root]# uname -a Linux localhost 2.4.20-8 #1 Thu Mar 13 17:54:28 EST 2003 ...
- 小程序使用 lodash 的问题
import _ from 'lodash' 报错: vendor.js:11874 Uncaught TypeError: Cannot read property 'prototype' of u ...
- 【CSS】模仿迅雷主页的按钮
<!DOCTYPE html> <html lang="en"> <head> <meta charset="UTF-8&quo ...
- 一、自动化监控利器-Zabbix
目录 1. 监控的作用 1.1 为何需要监控系统 1.2 监控系统的实现 1.3 常用的监控软件 2. Zabbix简介 2.1 选择Zabbix的理由 2.2 Zabbix的功能特性 3. Zabb ...
- nRF52832蓝牙iBeacon广播
开发环境 SDK版本:nRF5_SDK_15.0.0 芯片:nRF52832-QFAA 蓝牙iBeacon实现 iBeacon的核心就是广播,不需要进行连接,通过在广播包中插入信息然后广播出去. 广播 ...
- Pytest系列(12)- 测试结果生成HTML报告插件之pytest-html的详细使用
如果你还想从头学起Pytest,可以看看这个系列的文章哦! https://www.cnblogs.com/poloyy/category/1690628.html 环境前提 Python3.6+ 安 ...
- Spring AOP Aspect的简单实现(基于注解)
第1步:声明使用注解 <!-- 配置扫描注解--> 扫描包的位置<context:component-scan base-package="com.zz"/> ...
- AOP联盟通知类型和Spring编写代理半自动
一.cglib功能更强大 二.Spring核心jar包 三.AOP联盟通知 三.代码实现Spring半自动代理 1.环绕通知的切面 2.bean.xml配置 3.创建bean容器,获取bean,即已经 ...
- k8s标签label
1.给节点设置标签 一遍pod部署选择 kubectl label node 节点名 disktype=ssd kubectl label node master1 disktype=ssd 效果 [ ...
- 358 day09字节流、字符流
day09[字节流.字符流] 主要内容 IO流 字节流 字符流 异常处理 Properties 教学目标 [ ] 能够说出IO流的分类和功能 [ ] 能够使用字节输出流写出数据到文件 [ ] 能够使用 ...