本系列代码托管于:https://github.com/chintsan-code/opencv4-tutorials

本篇使用的项目为:convolution

#include <opencv2/opencv.hpp>
#include <iostream>

using namespace cv;
using namespace std;

int main(int argc, const char** argv) {
	Mat src = imread("../sample/example.png");
	if (src.empty()) {
		cout << "could not load image..." << endl;
		return -1;
	}
	namedWindow("input", WINDOW_AUTOSIZE);
	imshow("input", src);
	int h = src.rows;
	int w = src.cols;
	Mat result = src.clone();
	// 双层for循环做3*3卷积,实现均值滤波。用指针会更快一点
	for (int row = 1; row < h - 1; row++) {
		for (int col = 1; col < w - 1; col++) {
			// 3x3卷积核
			int sb = src.at<Vec3b>(row, col)[0] + src.at<Vec3b>(row - 1, col - 1)[0] + src.at<Vec3b>(row - 1, col)[0] +
				src.at<Vec3b>(row - 1, col + 1)[0] + src.at<Vec3b>(row, col - 1)[0] + src.at<Vec3b>(row, col + 1)[0] +
				src.at<Vec3b>(row + 1, col - 1)[0] + src.at<Vec3b>(row + 1, col)[0] + src.at<Vec3b>(row + 1, col + 1)[0];

			int sg = src.at<Vec3b>(row, col)[1] + src.at<Vec3b>(row - 1, col - 1)[1] + src.at<Vec3b>(row - 1, col)[1] +
				src.at<Vec3b>(row - 1, col + 1)[1] + src.at<Vec3b>(row, col - 1)[1] + src.at<Vec3b>(row, col + 1)[1] +
				src.at<Vec3b>(row + 1, col - 1)[1] + src.at<Vec3b>(row + 1, col)[1] + src.at<Vec3b>(row + 1, col + 1)[1];

			int sr = src.at<Vec3b>(row, col)[2] + src.at<Vec3b>(row - 1, col - 1)[2] + src.at<Vec3b>(row - 1, col)[2] +
				src.at<Vec3b>(row - 1, col + 1)[2] + src.at<Vec3b>(row, col - 1)[2] + src.at<Vec3b>(row, col + 1)[2] +
				src.at<Vec3b>(row + 1, col - 1)[2] + src.at<Vec3b>(row + 1, col)[2] + src.at<Vec3b>(row + 1, col + 1)[2];
			result.at<Vec3b>(row, col)[0] = sb / 9;
			result.at<Vec3b>(row, col)[1] = sg / 9;
			result.at<Vec3b>(row, col)[2] = sr / 9;
		}
	}
	imshow("conv-demo", result);
	Mat dst;
	// 相当于和下面的卷积核做卷积操作:
	// 1 1 1
	// 1 1 1
	// 1 1 1
	blur(src, dst, Size(3, 3), Point(-1, -1), BORDER_DEFAULT);
	imshow("blur-demo", dst);

	// 边缘填充
	int border = 8;
	Mat border_m;
	copyMakeBorder(src, border_m, border, border, border, border, BORDER_WRAP, Scalar(0, 0, 255));
	imshow("border fill demo", border_m);

	waitKey(0);
	destroyAllWindows();
	return 0;
}

blur:均值滤波

void blur( InputArray src, OutputArray dst, Size ksize, Point anchor = Point(-1,-1), int borderType = BORDER_DEFAULT );
  • src:输入图像,可以为任意通道,当进行滤波时,各通道的处理是独立的。但是深度只能为CV_8U, CV_16U, CV_16S, CV_32F或CV_64F
  • dst:输出图像,与src具有相同的尺寸和类型
  • ksize:卷积核尺寸
  • anchor:锚点,将卷积结果赋值给哪个位置的像素。(-1, -1)代表卷积核中心位置
  • borderType:图像边缘处理方式。注意是在卷积之前进行填充
    • BORDER_CONSTANT:用指定像素值填充。iiiiii|abcdefgh|iiiiiii(用i填充)。在此不可用。
    • BORDER_REPLICATE:用边缘像素填充。aaaaaa|abcdefgh|hhhhhhh
    • BORDER_WRAP:包裹填充。cdefgh|abcdefgh|abcdefg。左填到右,右填到左,上填到下,下填到上
    • BORDER_REFLECT_101。以边界为对称轴反射复制像素。gfedcb|abcdefgh|gfedcba
    • BORDER_DEFAULT,即BORDER_REFLECT_101

关于锚点anchor:

opencv4入门笔记(15):图像卷积-萤火

关于边缘处理方式borderType,以BORDER_REFLECT_101为例:

opencv4入门笔记(15):图像卷积-萤火

copyMakeBorder:边缘填充函数

void copyMakeBorder(InputArray src, OutputArray dst, int top, int bottom, int left, int right, int borderType, const Scalar& value = Scalar() );
  • src:输入图像
  • dst:输出图像。与src具有相同的类型,并且尺寸应该等于Size(src.cols+left+right,src.rows+top+bottom)
  • top:在图像上边缘填充多少个像素
  • bottom:在图像下边缘填充多少个像素
  • left:在图像左边缘填充多少个像素
  • right:在图像右边缘填充多少个像素
  • borderType:图像边缘处理方式
  • value: 填充的像素值。当borderType为BORDER_CONSTANT时可用