在opencv中如果我们想要自己亲自实现opencv中的一些API,比如说均值滤波、拉普拉斯算子、Sobel 算子等。对于均值滤波、拉普拉斯算子、Sobel算子等,都有一个特点是:卷积核的的数值是固定的。本文的目标是写出一个基础的模板可以方便各种算子使用(仅适用于固定的卷积核)。
我们可能会遇到如何构造卷积核以及如何让卷积核和原图像进行卷积,边界像素卷积时怎么处理等问题?卷积核的构造因为我们已经提前知道了卷积核的权重,可以直接使用一个一维数组来存储权重值。对于如何让卷积核与原图像进行卷积,一个思路是:如果我们使用Mat来存储卷积核的权重,可以直接让两个Mat(卷积核Mat和图像Mat)相乘获得结果。另一个思路是:如果我们使用一维数组来存储权重值,我们就将卷积核权重和原图像素对应相乘然后相加(本文采用的就是这种方法)。
关于使用opencv访问图像中某一点的像素值,请看这里。
Opencv中的图像是以Mat类型存储的,如果我们想取图像中的某一个区域,那么这个区域的类型也应该是Mat。
int main()
{
cv::Mat image = cv::imread("Lena.bmp");
cv::Mat img_Rect = image(cv::Range(0, 256), cv::Range(0, 512)); //第一个参数是row的范围,第二个参数是col的范围
return 0;
}
关于边界像素处理的方法请看(https://blog.csdn.net/qq_41596730/article/details/127738714)。
C++代码如下:
int reflect(int M, int x)
{
if (x < 0)
return -x;
if (x >= M)
return 2 * M - x - 1;
return x;
}
void Convolution(const cv::Mat input, const float *kernel, cv::Mat output, int width, int height, int ksize)
{
int kH = ksize / 2;
int kW = ksize / 2;
for (int h = 0; h < height; h++)
{
for (int w = 0; w < width; w++)
{
float dot = 0;
for (int k_h = -kH; k_h <= kH; k_h++)
{
for (int k_w = -kW; k_w <= kW; k_w++)
{
int cur_h = reflect(height, h + k_h); //边界像素处理
int cur_w = reflect(width, w + k_w);
dot += input.at<uchar>(cur_h, cur_w)*kernel[(k_h + kH)*ksize + (k_w + kW)]; //对应相乘然后相加
}
}
output.at<uchar>(h, w) = (uchar)std::max(0, std::min(255, (int)dot));
}
}
}
main函数如下:
int main()
{
cv::Mat image = cv::imread("Lena.bmp");
//cv::Mat img_Rect = image(cv::Range(0, 256), cv::Range(0, 512));
cv::Mat src;
cv::cvtColor(image, src, CV_BGR2GRAY);
cv::Mat output = cv::Mat::zeros(src.size(), CV_8UC1);
int width = src.cols;
int height = src.rows;
float gx[] = {
-1,0,1,
-2,0,2,
-1,0,1
};
Convolution(src, gx, output, width, height, 3);
return 0;
}