opencv学习笔记14(自定义线性滤波)

自定义线性滤波

  • 卷积概念
  • 卷积操作
  • filter2D()函数
  • 常见算子
  • 自定义卷积模糊
  • 结果演示
  • 代码实现

卷积概念

  • 卷积是图像处理中一个操作,是kernel在图像的每个像素上的操作。
  • Kernel本质上一个固定大小的矩阵数组,其中心点称为锚点(anchor point)
    opencv学习笔记14(自定义线性滤波)_第1张图片

卷积操作

  • 把kernel放到像素数组之上,求锚点周围覆盖的像素乘积之和(包括锚点),用来替换锚点覆盖下像素点值称为卷积处理。数学表达如下:
    opencv学习笔记14(自定义线性滤波)_第2张图片
    opencv学习笔记14(自定义线性滤波)_第3张图片
    Sum = 8x1+6x1+6x1+2x1+8x1+6x1+2x1+2x1+8x1
    New pixel = sum / (m*n)
    opencv学习笔记14(自定义线性滤波)_第4张图片

filter2D()函数

参考链接:https://blog.csdn.net/keith_bb/article/details/53103026
此函数利用内核实现对图像的卷积运算

CV_EXPORTS_W void filter2D( InputArray src, OutputArray dst, int ddepth,
                            InputArray kernel, Point anchor=Point(-1,-1),
                            double delta=0, int borderType=BORDER_DEFAULT );

参数解释:

  • InputArray src: 输入图像
  • OutputArray dst: 输出图像,和输入图像具有相同的尺寸和通道数量
  • int ddepth: 目标图像深度,如果没写将生成与原图像深度相同的图像。当depth输入值为-1时,目标图像和原图像深度保持一致。
  • InputArray kernel: 卷积核(或者是相关核),一个单通道浮点型矩阵。如果想在图像不同的通道使用不同的kernel,可以先使用split()函数将图像通道事先分开。
  • Point anchor: 内核的基准点(anchor),其默认值为(-1,-1)说明位于kernel的中心位置。基准点即kernel中与进行处理的像素点重合的点。
  • double delta: 在储存目标图像前可选的添加到像素的值,默认值为0
  • int borderType: 像素向外逼近的方法,默认值是BORDER_DEFAULT,即对全部边界进行计算。

常见算子

  • Robert算子(左侧x方向,右侧y方向)
    opencv学习笔记14(自定义线性滤波)_第5张图片
  • Sobel算子(左侧x方向,右侧y方向)
    opencv学习笔记14(自定义线性滤波)_第6张图片
  • 拉普拉斯算子(当中间值为5时是锐化算子)
    opencv学习笔记14(自定义线性滤波)_第7张图片

自定义卷积模糊

  • filter2D方法filter2D(
    Mat src, //输入图像
    Mat dst, // 模糊图像
    int depth, // 图像深度32/8
    Mat kernel, // 卷积核/模板
    Point anchor, // 锚点位置
    double delta // 计算出来的像素+delta
    )
    其中 kernel是可以自定义的卷积核
    opencv学习笔记14(自定义线性滤波)_第8张图片

结果演示

Robert 算子结果
opencv学习笔记14(自定义线性滤波)_第9张图片
opencv学习笔记14(自定义线性滤波)_第10张图片
sobel 算子结果
opencv学习笔记14(自定义线性滤波)_第11张图片
opencv学习笔记14(自定义线性滤波)_第12张图片
拉普拉斯算子结果
opencv学习笔记14(自定义线性滤波)_第13张图片
自定义模糊结果(卷积核大小为5*5时)
opencv学习笔记14(自定义线性滤波)_第14张图片

代码实现

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

using namespace cv;
using namespace std;

int main(){
	Mat src = imread("G:/OpenCV/opencv笔记所用图片/1.jpg");
	if (src.empty())
	{
		cout << "could not load image..." << endl;
		getchar();
		return -1;
	}

	Mat dst;
	// robert X 方向
	Mat kernel_x = (Mat_<int>(2, 2) << 1, 0, 0, -1);			//定义robert的x方向的算子为卷积核
	filter2D(src, dst, -1, kernel_x, Point(-1, -1), 0.0);		//使用filter2D来对图像进行卷积操作(使用的卷积核已定义)
	imshow("robert X",dst);

	// robert y 方向
	Mat kernel_y = (Mat_<int>(2, 2) << 0, 1, -1, 0);			//定义robert的y方向的算子为卷积核
	filter2D(src, dst, -1, kernel_y, Point(-1, -1), 0.0);
	imshow("robert y", dst);

	//sobel的算子比Robert要差异大一些
	// Sobel X 方向
	 Mat Skernel_x = (Mat_<int>(3, 3) << -1, 0, 1, -2,0,2,-1,0,1);		//定义sobel的x方向的算子为卷积核
	 filter2D(src, dst, -1, Skernel_x, Point(-1, -1), 0.0);
	imshow("Sobel x", dst);

	// Sobel Y 方向
	 Mat yimg;
	 Mat Skernel_y = (Mat_<int>(3, 3) << -1, -2, -1, 0,0,0, 1,2,1);		//定义sobel的y方向的算子为卷积核
	 filter2D(src, yimg, -1, Skernel_y, Point(-1, -1), 0.0);
	 imshow("Sobel y", yimg);

	// 拉普拉斯算子(边缘检测算子,得到的是主要的轮廓,当中间的值是5时是锐化算子)
	Mat Lkernel = (Mat_<int>(3, 3) << 0, -1, 0, -1, 4, -1, 0, -1, 0);
	filter2D(src, dst, -1, Lkernel, Point(-1, -1), 0.0);
	imshow("拉普拉斯", dst);

	//自定义卷积模糊
	int c = 0;
	int index = 0;
	int ksize = 0;
	//使用循环是其模糊程度自动加深
	while (true) {
		c = waitKey(600);
		if ((char)c == 27) {// ESC 
			break;
		}
		ksize = 5 + (index % 8) * 2;
		Mat kernel = Mat::ones(Size(ksize, ksize), CV_32F) / (float)(ksize * ksize);	//定义一个大小变化的卷积核
		filter2D(src, dst, -1, kernel, Point(-1, -1));
		index++;					//进行模糊自增
		imshow("卷积模糊", dst);
	}

	waitKey(0);
	return 0;
}

你可能感兴趣的:(opencv,c++)