图像处理之卷积操作(C++)
前言
卷积是图像处理中最基本且重要的概念,使用C++实现图像的卷积运算。
一、原理
步骤:
1.翻转卷积核;
2.对图像边缘进行填充;
3.对卷积核和图像执行相关性计算。(注意:相关和卷积的关系)
二、代码实现
#include <iostream>
#include <opencv.hpp>
using namespace std;
/*基于opencv实现的卷积运算*/
void test()
{
cv::Mat kernel = (cv::Mat_<float>(3, 3) << -1, -2, -1, 0, 0, 0, 1, 2, 1);
cv::Mat src = (cv::Mat_<uchar>(4, 4) << 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16);
cv::Mat dst;
cv::filter2D(src, dst, CV_32F, kernel);
for (int i = 0; i < dst.rows; i++)
for (int j = 0; j < dst.cols; j++)
cout << dst.at<float>(i, j) << endl;
}
/*
* @param cv::Mat src 输入图像
* @param cv::Mat dst 输出图像
* @param cv::Mat kernel 卷积核
* @param int stride 步长
* @breif 自己实现的卷积运算
*/
void myConv(cv::Mat& src, cv::Mat& dst,cv::Mat& kernel,int stride)
{
//1.翻转卷积核180°(OpenCV中卷积核没有翻转)
cv::flip(kernel, kernel, 0);
//2.复制边界填充(OpenCV中采用是 CV_HAL_BORDER_REFLECT_101)
int top, bottom ,right, left;
top = bottom = ((src.rows - 1) * stride + kernel.rows - src.rows)/2; //计算需要padding的值
right=left= ((src.cols - 1) * stride + kernel.cols - src.cols) / 2;
cv::copyMakeBorder(src, src, top, bottom, left, right, CV_HAL_BORDER_REFLECT_101);
//3.执行卷积计算
dst.convertTo(dst, CV_32F);
for(int i=0;i<src.rows-kernel.rows+1;i++)
for (int j = 0; j < src.cols-kernel.cols+1; j++)
{
int sum = 0;
for(int m=0;m<kernel.rows;m++)
for (int n = 0; n < kernel.cols; n++)
{
sum += kernel.at<float>(m, n) * src.at<uchar>(i+m, j+n);
}
cout << sum << endl;
dst.at<float>(i, j) = sum;
}
}
int main()
{
//opencv卷积测试
test();
//自定义卷积测试
cv::Mat kernel = (cv::Mat_<float>(3, 3) << -1, -2, -1, 0, 0, 0, 1, 2, 1);
cv::Mat src = (cv::Mat_<uchar>(4, 4) << 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16);
cv::Mat dst(src.size(),CV_8U);
myConv(src, dst, kernel, 1);
cv::waitKey(0);
return 0;
}
总结
自己手动C++实现了图像卷积的计算,明确了卷积和相关性的关系,探析了opencv内部卷积的实现实际上是相关性计算,没有对卷积核进行翻转,采用的图像填充的方式是CV_HAL_BORDER_REFLECT_101。欢迎大家验证。
因为笔者水平有限,有错误欢迎指出,代码本人均在本地运行实验正确,大家放心使用。