【说明】 看的教程书上是用的opencv基于C的函数,但是在代码实现过程用的是C++的函数。因此,下文中的函数介绍和示例代码会有一些出入,理解效果就好,都是通用的。
一、卷积(convolution)
一个特殊卷积所实现的功能是由其卷积核的形式决定的。这个核本质是一个大小固定、由数值参数构成的数组,数组的参考点(anchor point)通常位于数组的中心。数组的大小成为核支撑(support of the kernel)。
1. opencv函数—cvFilter2D
- 方法名
void cvFilter2D(
const CvArr * src,
CvArr * dst,
const CvMat * kernel,
CvPoint anchor = cvPoint(-1,-1)
);
- 参数说明
- anchor 参考点的位置,默认值(cvPoint(-1,-1))指参考点为核的中心。
- src 和dst 大小应该相同。因为opencv在处理时,在原图像上通过复制边缘像素的方法创建了虚拟边界。因此,dst大小与src保持相同即可。
2. 示例
- 代码
// 加载原始图像
Mat src_door = imread("../source/door.jpg", CV_LOAD_IMAGE_GRAYSCALE);
imshow("源图像", src_door);
// 生成一个新的核
Mat mask = (Mat_<char>(3,3) << 0, -1, 0, -1, 5, -1, 0, -1, 0);
Mat dst;
// 卷积处理图像
filter2D(src_door, dst, src_door.depth(), mask);
imshow("卷积图像", dst);
- 效果
- 二、卷积边界
如上文所述,在使用filter2D时,需要对边界进行预处理,此函数就是用不同方式处理的边界。它可以使特定的图像轻微变大,然后以各种方式自动填充图像边界。
1. opencv函数—cvCopyMakeBorder
- 方法名
void cvCopyMakeBorder(
const CvArr * src,
CvArr * dst,
CvPoint offset,
int bordertype,
CvScalar value = cvScalarAll(0)
);
- 参数说明
- offset 将源图像的副本放到目标图像中的位置
- 如果核为N X N,N为奇数,那么边界在每一侧的宽度都应该是 (N-1)/2,即这幅图像比源图像宽或高 N-1。此时可以把offset设置为 CvPoint((N-1)/2, (N-1)/2 ),使得边界在每一侧都是偶数。
- bordertype
- IPL_BORDER_CONSTANT 固定值填充
- IPL_BORDER_REPLICATE 行列复制
- BORDER_REFLECT
- BORDER_REFLECT_101
- BORDER_WRAP
- -
2. 示例
- 代码
// 加载原始图像
Mat src_door = imread("../source/door.jpg", CV_LOAD_IMAGE_GRAYSCALE);
imshow("源图像", src_door);
Mat mask = (Mat_<char>(3, 3) << 0, -1, 0, -1, 5, -1, 0, -1, 0);
Mat dst_replicate, dst_reflect, dst_reflect101, dst_wrap, dst_constant;
int winsize = 40;
copyMakeBorder(src_door, dst_replicate, winsize, winsize, winsize, winsize,
BORDER_REPLICATE);
copyMakeBorder(src_door, dst_reflect, winsize, winsize, winsize, winsize,
BORDER_REFLECT);
copyMakeBorder(src_door, dst_reflect101, winsize, winsize, winsize, winsize,
BORDER_REFLECT_101);
copyMakeBorder(src_door, dst_wrap, winsize, winsize, winsize, winsize,
BORDER_WRAP);
copyMakeBorder(src_door, dst_constant, winsize, winsize, winsize, winsize,
BORDER_CONSTANT);
imshow("BORDER_REPLICATE", dst_replicate);
imshow("BORDER_REFLECT", dst_reflect);
imshow("BORDER_REFLECT_101", dst_reflect101);
imshow("BORDER_WRAP", dst_wrap);
imshow("BORDER_CONSTANT", dst_constant);
- 效果
三、梯度和Sobel导数
- 一般用作边缘检测
- 一个最重要最基本的卷积就是导数的计算,通常用Sobel微分算子来表达微分;
- Sobel并不是真正意义的导数,只是通过多项式拟合得到的结果;
- 通常人们都用较大的核,因为较大的核有更好的逼近,同时也会使更多像素参与拟合计算。
1. opencv函数—cvSobel
- 方法名
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 输出图像的深度,支持如下src.depth()和ddepth的组合:
若src.depth() = CV_8U, 取ddepth =-1/CV_16S/CV_32F/CV_64F
若src.depth() = CV_16U/CV_16S, 取ddepth =-1/CV_32F/CV_64F
若src.depth() = CV_32F, 取ddepth =-1/CV_32F/CV_64F
若src.depth() = CV_64F, 取ddepth = -1/CV_64F - dx 和 dy是求导的阶数,只能为0, 1, 2
- ksize 是方形滤波器的宽(或高)并且只能是奇数,目前支持1, 3, 5, 7
- scale 是计算导数值时的缩放因子,默认1
- borderType 是边界形式
2. 示例
- 代码
// 加载原始图像
Mat src_door = imread("../source/door.jpg", CV_LOAD_IMAGE_GRAYSCALE);
imshow("源图像", src_door);
Mat dst_gradx, dst_grady;// x和y方向的求导
Mat abs_gradx, abs_grady;
Mat dst_addweight;
// x 导数
Sobel(src_door, dst_gradx, src_door.depth(), 1, 0, 3); // Sobel检测
convertScaleAbs(dst_gradx, abs_gradx); // 线性转换成8位无符号整形
// y 导数
Sobel(src_door, dst_grady, src_door.depth(), 0, 1, 3);
convertScaleAbs(dst_grady, abs_grady);
// 方向加权
addWeighted(abs_gradx, 0.5, abs_grady, 0.5, 0, dst_addweight);
imshow("x方向导数", dst_gradx);
imshow("y方向导数", dst_grady);
imshow("加权结果", dst_addweight);
- 效果
- (未完待续。。。)