Opencv7:卷积核分离优化均值滤波函数
(1)卷积核分离优化思路:
①优化处理1:
将图像与m*n的卷积核卷积的操作,分解为图像先与1*n的卷积核进行水平方向卷积,再与m*1的卷积核进行竖直方向卷积的操作,可减少运算量。
②优化处理2:
当卷积核在水平方向(或竖直方向)移动并处理像素时,可以将每次加权求和理解为“加前减后的操作”。通过上一个像素的处理结果加一个像素值再减去一个像素值代替重新进行一次累加,可减少运算量,当卷积核尺寸大时,优化效果尤其明显。
(2)卷积核分离优化均值滤波函数处理过程:
//卷积核分离优化均值滤波
void MeanImageKernel(Mat& image, Mat& imageResult, Size ksize)
{
//【1】图像边界填充
int halfKheight = (ksize.height-1)/2; //卷积核高度的一半(用于扩展边界)
int halfKwidth = (ksize.width - 1)/2; //卷积核宽度的一半(用于扩展边界)
int Kheight = ksize.height; //卷积核高度
int Kwidth = ksize.width ; //卷积核宽度
Mat MakeBorder;
copyMakeBorder(image, MakeBorder, halfKheight, halfKheight, halfKwidth, halfKwidth, BORDER_REFLECT_101); //图像边界填充
int nr = MakeBorder.rows; //边界填充后图像高度(行数)
int nc = MakeBorder.cols; //边界填充后图像宽度(列数)
//【2】对水平方向做卷积
int Xvalue, Yvalue; //用于存储每个像素处理结果的变量
Mat imageX = Mat::zeros(MakeBorder.size(), CV_16S); //存储水平方向卷积结果的mat
//外层循环:对行循环,内部处理每一行的像素
for (int i = 0;i < nr;i++)
{
//(1)第一个像素的卷积需要额外处理
Xvalue = 0;
for (int j = 0;j < Kwidth;j++)
{
Xvalue = Xvalue + MakeBorder.at<uchar>(i, j);
}
//对存储水平方向卷积结果的mat对应像素进行赋值,此处累加值较大注意数据类型不是uchar
imageX.at<ushort>(i, halfKwidth) = Xvalue;
//(2)处理本行其它像素
for (int j = halfKwidth + 1;j < nc - halfKwidth;j++)
{
//加前减后处理,注意索引
Xvalue = Xvalue + MakeBorder.at<uchar>(i, j + halfKwidth) - MakeBorder.at<uchar>(i, j - halfKwidth-1 );
imageX.at<ushort>(i, j) = Xvalue;
}
//【3】对竖直方向做卷积
imageResult = Mat::zeros(image.size(), image.type());
//外层循环:对列循环,内部处理每一列的像素
for (int i = halfKwidth;i < nc - halfKwidth;i++)
{
//(1)第一个像素的卷积需要额外处理
Yvalue = 0;
for (int j = 0;j < Kheight;j++)
{
Yvalue = Yvalue + imageX.at<ushort>(j, i);
}
//对存储结果的mat对应像素进行赋值,注意累加结果要除以卷积核元素个数
imageResult.at<uchar>(0, i- halfKwidth) = Yvalue/ (Kheight*Kwidth);
//(2)处理其它像素
for (int j = halfKheight + 1;j < nr - halfKheight;j++)
{
//加前减后处理,注意索引
Yvalue = Yvalue + imageX.at<ushort>(j + halfKheight, i) - imageX.at<ushort>(j - halfKheight - 1, i);
imageResult.at<uchar>(j- halfKheight, i - halfKwidth) = Yvalue/ (Kheight*Kwidth);
}
}
}
}
(3)处理结果: