OpenCV中封装了很多函数,一般我们只需要调用它提供的API函数即可实现各种图像处理操作,如滤波、边缘检测等,这对我们项目开发来说是非常方便的。但是一个优秀的算法工程师必然也是一位优秀的程序员,因为就算你熟知各种算法的原理,但是不知道怎么实现也是不行的,(举个例子:假如你是一位销售员,你脑袋里面有很多非常棒的点子,可是你不知道怎么将它表述出来,那么到最后你依然还是一位处在销售底层的人。) 只有既熟悉原理又会用代码将大脑中好的想法实现,那才是优秀的算法工程师。当然,要完全靠自己从头实现OpenCV中的各种函数也是不现实的,就算你有那能力但是也不一定会有那么多精力。因此折中一点,至少要让自己能看懂OpenCV中各种算法的实现代码(比如说你做人脸识别,至少也要能完全理解SIFT是怎么实现的吧),这样如果有一天需要自己开发新的或者优化现有算法,那对自己的帮助就很大了。
一、Sobel算子函数介绍
OpenCV中Sobel算子被封装在
CV_EXPORTS_W void Sobel( InputArray src, OutputArray dst, int ddepth,
int dx, int dy, int ksize=3,
double scale=1, double delta=0,
int borderType=BORDER_DEFAULT );
这个函数里面,其中:
- src表示输入原图像;
- dst表示输出目标图像;
- ddepth表示用来度量每一个像素中每一个通道的精度,但它本身与图像的通道数无关!depth数值越大,精度越高。在Opencv中,Mat.depth()得到的是一个0~6的数字,分别代表不同的位数,对应关系如下:
enum{CV_8U=0,CV_8S=1,CV_16U=2,CV_16S=3,CV_32S=4,CV_32F=5,CV_64F=6},ddepth 由于Sobel运算时可能会出现比较大的值,因此取值方式有以下几种:
- 若 ddepth = CV_8U, 取 ddepth = -1 或 CV_16S 或 CV_32F 或 CV_64F
- 若 ddepth = CV_16U, 或 CV_16S, 取 ddepth = -1 或 CV_32F 或 CV_64F
- 若 ddepth = CV_32F, 取 ddepth = -1 或 CV_32F 或 CV_64F
- 若 ddepth = CV_64F, 取 ddepth = -1 或 CV_64F
- 若 ddepth = -1, 代表输出图像与输入图像相同的深度。
- dx表示对X方向进行求导的差分阶数;
- dy表示对Y方向进行求导的差分阶数;(其中, dx=1,dy=0,表示计算X方向的导数,检测出的是垂直方向上的边缘;dx=0,dy=1,表示计算Y方向的导数,检测出的是水平方向上的边缘。)
- ksize 表示卷积核的大小,只能为奇数 1、3、5、7 等,特殊情况:当ksize <= 0 时,采用的模板为3 * 3 的Scharr内核。
- scale 表示缩放尺度,默认为1。
- delta:默认0。(还不知道有什么作用,猜测是用来放大的)
int borderType:滤波时填充的图像边界类型,有如下几种:
enum {
BORDER_REPLICATE=IPL_BORDER_REPLICATE,
BORDER_CONSTANT=IPL_BORDER_CONSTANT,
BORDER_REFLECT=IPL_BORDER_REFLECT,
BORDER_WRAP=IPL_BORDER_WRAP,
BORDER_REFLECT_101=IPL_BORDER_REFLECT_101,
BORDER_REFLECT101=BORDER_REFLECT_101,
BORDER_TRANSPARENT=IPL_BORDER_TRANSPARENT,
BORDER_DEFAULT=BORDER_REFLECT_101,
BORDER_ISOLATED=16 };
默认值为BORDER_DEFAULT,具体边界定义方法参考图像剪切的扩展和高级用法:任意裁剪,边界扩充这篇博客,里面对图像边界的扩充说得很详细。
二、Sobel算子实现流程
三、Sobel算子源码分析
openCV中Sobel函数定义如下:
void cv::Sobel( InputArray _src, OutputArray _dst, int ddepth, int dx, int dy,
int ksize, double scale, double delta, int borderType )
{
Mat src = _src.getMat();
if (ddepth < 0)
ddepth = src.depth();
_dst.create( src.size(), CV_MAKETYPE(ddepth, src.channels()) );
Mat dst = _dst.getMat();
#ifdef HAVE_TEGRA_OPTIMIZATION
if (scale == 1.0 && delta == 0)
{
if (ksize == 3 && tegra::sobel3x3(src, dst, dx, dy, borderType))
return;
if (ksize == -1 && tegra::scharr(src, dst, dx, dy, borderType))
return;
}
#endif
#if defined (HAVE_IPP) && (IPP_VERSION_MAJOR >= 7)
if(dx < 3 && dy < 3 && src.channels() == 1 && borderType == 1)
{
if(IPPDeriv(src, dst, ddepth, dx, dy, ksize,scale))
return;
}
#endif
int ktype = std::max(CV_32F, std: