实现一个卷积操作的函数,这里的卷积代指图像卷积,原理如下
1 图像边界扩充
对输⼊的图像根据kernel(卷积核)的size进⾏padding操作,这里笔者选用填0,
2 初始化dst
初始化dst的size和存储元素类型( CV_8UC3 or CV_8UC1 )
3 遍历padding后的图像
此外:
对于多通道的图像,你可以先通过 cv::split() 将其拆分为单通道图像,并对各个单通道图像分别进⾏ 运算,再将结果通过 cv::merge() 合并作为输出。
下面贴上完整代码
#include <opencv2/opencv.hpp>
#include <iostream>
using namespace cv;
using namespace std;
void filter(const cv::Mat &src, cv::Mat &dst, const cv::Mat &kernel)
{
Mat newsrc = cv::Mat::zeros(src.rows, src.cols, CV_8UC1);
//图像边界扩充(按kernel像素计算)
int row_k = (kernel.rows - 1) / 2;
int col_k = (kernel.cols - 1) / 2;
cv::copyMakeBorder(src, newsrc, row_k, row_k, col_k, col_k, BORDER_CONSTANT, 0); //以边缘为轴,对称扩展原图边界
dst = cv::Mat::zeros(src.rows, src.cols, CV_8UC1);
/*/遍历图像,常规法
for (int i = 0; i < src.rows; i++)
{
for (int j = 0; j < src.cols; j++)
{
double cnvSum = 0.0; //卷积和convolution sum
for (int m = 0; m < row_k * 2 + 1; m++)
{
for (int n = 0; n < col_k * 2 + 1; n++)
{
double srcValue = newsrc.at<uchar>(i + m, j + n) * kernel.at<uchar>(m,n);
cnvSum += srcValue;
}
}
dst.at<uchar>(i, j) = cnvSum;
}
}
*/
//遍历图像(矩阵法)
for (int i = 0; i < src.rows; i++)
{
for (int j = 0; j < src.cols; j++)
{
Rect rect(j, i, kernel.cols, kernel.rows); //截取矩阵大小
Mat aa = newsrc(rect);
aa.convertTo(aa, CV_32FC1);
//cout << "kernel =" << kernel << endl;
//cout << "aa =" << aa << endl;
double sum = kernel.dot(aa); //向量点积
//cout << "sum =" << sum << endl;
dst.at<uchar>(i, j) = sum;
}
}
}
int main()
{
//imread()函数载入原始图像
Mat srcImg = imread("tt1.jpg",1);
Mat dst1 = cv::Mat::zeros(Size(srcImg.rows, srcImg.cols),CV_8UC3); //dst1均值滤波
Mat dst2 = cv::Mat::zeros(Size(srcImg.rows, srcImg.cols), CV_8UC3); //dst2我的滤波
//均值滤波
cv::blur(srcImg, dst1, cv::Size(3, 3));
//我的滤波
//滤波算子
//cv::Mat mykernel= (cv::Mat_<float>(3, 3) << 0,-1,0,-1,5,-1,0,-1,0); //若是浮点数,直接输入
cv::Mat mykernel = cv::Mat::ones(5, 5, CV_32FC1) / (5 * 5);
// vector创建三通道Mat,opencv是BGR,
vector<cv::Mat> channels;
//三通道分离函数
cv::split(srcImg, channels);
//蓝色通道单独通道滤波计算
Mat imgBlue = channels.at(0);
filter(imgBlue, channels.at(0), mykernel);
//cv::imshow("blueblur", channels.at(0));
cv::waitKey(0);
//绿色通道单独通道滤波计算
Mat imgGreen = channels.at(1);
filter(imgGreen, channels.at(1), mykernel);
//cv::imshow("Greenblur", channels.at(1));
//cv::waitKey(0);
//红色通道单独通道滤波计算
Mat imgRed = channels.at(2);
filter(imgRed, channels.at(2), mykernel);
//cv::imshow("Redblur", channels.at(2));
//cv::waitKey(0);
//三通道合并函数
cv::merge(channels, dst2);
cv::imshow("raw", srcImg); //原图
cv::imshow("blur",dst1); //OpenCV均值滤波
cv::imshow("myblur", dst2); //自定义滤波函数
cv::waitKey(0);
destroyAllWindows();
return 0;
}
此函数未使用任何OpenCV已有的滤波函数
注意:输入卷积核时若为小数请写1./9.而不要写1/9