opencv算法简化移植
2016-06-14 16:12
429 查看
本文以简化opencv中的库函数laplacian函数为例,来分析如何在不知道算法原理的情况下,仅仅通过对数据流进行跟踪,来实现算法的移植。
这种用途在实际应用中是十分常见的,很多情况是,就算我们知道了算法的原理,用自己的方法实现起来与opencv原版的函数也有不小的差距。
而opencv原版的代码往往错综复杂,代码的安全性以及执行效率都是非常高的,在分析代码时,往往不能理解每一句的含义。
一般的算法步骤分为 初始化,执行两步;
在初始化步骤中,需要完成的任务:
1.根据函数的输入参数判断执行不同的算法;
2.根据函数的输入参数初始化局部变量;
3.创建对象;
在执行步骤中完成对输入数据的处理。
算法的初始化步骤很关键,然而在通常的应用中,我们的输入参数只有一种情况,于是可以将初始化步骤中的很多判断省略,只取其中的一种情况。
然而我们更关心的是算法的执行步骤。
回到上一步中:
模板函数的调用为:
比对函数的声明:
可以得到:
1.输入参数的准备。
2.全局变量的初始化。
3.子函数的实现。
uchar *dst 为变换后的数据。
dst_data = (uchar *)yuv_dst->vir_addr[0];
dststep为图像内存区域的字节宽度 width*4
ptrs.resize(coords.size()); //ptr初始化
1.变量比对
运行到同一状态时的所有变量是一致的。
2.内存比对
同一指针指向的内存的数据应是一致的。在vs2010中查看指针指向内存的方法为调试->窗口->内存。
这种用途在实际应用中是十分常见的,很多情况是,就算我们知道了算法的原理,用自己的方法实现起来与opencv原版的函数也有不小的差距。
而opencv原版的代码往往错综复杂,代码的安全性以及执行效率都是非常高的,在分析代码时,往往不能理解每一句的含义。
1. 流程分析
采用调试的方式对算法流程进行跟踪。一般的算法步骤分为 初始化,执行两步;
在初始化步骤中,需要完成的任务:
1.根据函数的输入参数判断执行不同的算法;
2.根据函数的输入参数初始化局部变量;
3.创建对象;
在执行步骤中完成对输入数据的处理。
算法的初始化步骤很关键,然而在通常的应用中,我们的输入参数只有一种情况,于是可以将初始化步骤中的很多判断省略,只取其中的一种情况。
然而我们更关心的是算法的执行步骤。
2.核心定位
在了解算法的大致执行流程之后,采用的方法是逆向分析,从算法的执行开始入手,分析算法的执行需要哪些条件,哪些参数,各个参数又是怎么来的。2.1核心执行代码:
void operator()(const uchar** src, uchar* dst, int dststep, int count, int width, int cn) { KT _delta = delta; const Point* pt = &coords[0]; const KT* kf = (const KT*)&coeffs[0]; const ST** kp = (const ST**)&ptrs[0]; int i, k, nz = (int)coords.size(); CastOp castOp = castOp0; width *= cn; for( ; count > 0; count--, dst += dststep, src++ ) { DT* D = (DT*)dst; for( k = 0; k < nz; k++ ) kp[k] = (const ST*)src[pt[k].y] + pt[k].x*cn; i = vecOp((const uchar**)kp, dst, width); #if CV_ENABLE_UNROLLED for( ; i <= width - 4; i += 4 ) { KT s0 = _delta, s1 = _delta, s2 = _delta, s3 = _delta; for( k = 0; k < nz; k++ ) { const ST* sptr = kp[k] + i; KT f = kf[k]; s0 += f*sptr[0]; s1 += f*sptr[1]; s2 += f*sptr[2]; s3 += f*sptr[3]; } D[i] = castOp(s0); D[i+1] = castOp(s1); D[i+2] = castOp(s2); D[i+3] = castOp(s3); } #endif for( ; i < width; i++ ) { KT s0 = _delta; for( k = 0; k < nz; k++ ) s0 += kf[k]*kp[k][i]; D[i] = castOp(s0); } } } vector<Point> coords; vector<uchar> coeffs; vector<uchar*> ptrs; KT delta; CastOp castOp0; VecOp vecOp; };
2.2 模板替换
在opencv库函数中使用了大量的模板,在移植过程中要替换为具体的数据类型。回到上一步中:
模板函数的调用为:
if( sdepth == CV_8U && ddepth == CV_32F ) return Ptr<BaseFilter>(new Filter2D<uchar, Cast<float, float>, FilterNoVec>(kernel, anchor, delta));
比对函数的声明:
template<typename ST, class CastOp, class VecOp> struct Filter2D : public BaseFilter
可以得到:
ST 为 uchar; CastOp 为 Cast<float, float>; //这种用法是什么意思? VecOp 为 FilterNoVec;
3. 准备执行
为了能够运行执行函数,需要做哪些准备:1.输入参数的准备。
2.全局变量的初始化。
3.子函数的实现。
3.1 输入参数的准备
const uchar**为原始图像数据构成的二维数组。uchar ** src_data = new uchar*[yuv_src->height]; for(int i=0; i<yuv_src->height; i++) { src_data[i] = (uchar *)yuv_src->vir_addr[0] + i*yuv_src->stride[0]; }
uchar *dst 为变换后的数据。
dst_data = (uchar *)yuv_dst->vir_addr[0];
dststep为图像内存区域的字节宽度 width*4
3.2 全局变量的初始化
coords和coeffs在preprocess2DKernel( _kernel, coords, coeffs );中进行了初始化。ptrs.resize(coords.size()); //ptr初始化
3.3 子函数的实现
该算法中未涉及子函数3.4 数据比对
很多时候,虽然运行不会报错,但是得到的结果却不一样,这是因为某些参数的初始化过程中出现问题,需要比对运行数据,找出导致数据不一样的地方。1.变量比对
运行到同一状态时的所有变量是一致的。
2.内存比对
同一指针指向的内存的数据应是一致的。在vs2010中查看指针指向内存的方法为调试->窗口->内存。
相关文章推荐
- Operating System Point
- linux常用命令加实例大全
- Apache Kafka:下一代分布式消息系统
- linux系统下程序后台运行的方法
- ccah-500 第28题 Which Linux commands help you to identify whether swapping is occurring
- CENTOS Device eth0 does not seem to be present
- linux 下 tree命令用不了怎么回事
- [Learning OpenCV入门 1]OpenCV模块以及基本操作
- Docker-Dockerfile构建镜像
- centos7.2系统基本优化
- nginx+tomcat分布式部署
- 导出数据库到本地为sql格式,linux下执行导入sql数据
- centos6.5安装jira6.3.6详细文档汉化破解
- 【shell】通配符
- Hadoop之——默认布局策略
- Centos 下安装Zabbix Linux 客户端
- Tomcat文件详解
- xshell-linux命令
- Linux cp强制覆盖
- 如何使docker容器不退出