您的位置:首页 > 运维架构

opencv算法简化移植

2016-06-14 16:12 429 查看
本文以简化opencv中的库函数laplacian函数为例,来分析如何在不知道算法原理的情况下,仅仅通过对数据流进行跟踪,来实现算法的移植。

这种用途在实际应用中是十分常见的,很多情况是,就算我们知道了算法的原理,用自己的方法实现起来与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中查看指针指向内存的方法为调试->窗口->内存。
内容来自用户分享和网络整理,不保证内容的准确性,如有侵权内容,可联系管理员处理 点击这里给我发消息
标签: